1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
|
# frozen_string_literal: true
module Bundler
#
# This class handles installing and switching to the version of bundler needed
# by an application.
#
class SelfManager
def restart_with_locked_bundler_if_needed
return unless needs_switching? && installed?
restart_with(restart_version)
end
def install_locked_bundler_and_restart_with_it_if_needed
return unless needs_switching?
if restart_version == lockfile_version
Bundler.ui.info \
"Bundler #{current_version} is running, but your lockfile was generated with #{lockfile_version}. " \
"Installing Bundler #{lockfile_version} and restarting using that version."
else
Bundler.ui.info \
"Bundler #{current_version} is running, but your configuration was #{restart_version}. " \
"Installing Bundler #{restart_version} and restarting using that version."
end
install_and_restart_with(restart_version)
end
def update_bundler_and_restart_with_it_if_needed(target)
return unless autoswitching_applies?
spec = resolve_update_version_from(target)
return unless spec
version = spec.version
Bundler.ui.info "Updating bundler to #{version}."
install(spec)
restart_with(version)
end
private
def install_and_restart_with(version)
requirement = Gem::Requirement.new(version)
spec = find_latest_matching_spec(requirement)
if spec.nil?
Bundler.ui.warn "Your lockfile is locked to a version of bundler (#{lockfile_version}) that doesn't exist at https://rubygems.org/. Going on using #{current_version}"
return
end
install(spec)
rescue StandardError => e
Bundler.ui.trace e
Bundler.ui.warn "There was an error installing the locked bundler version (#{lockfile_version}), rerun with the `--verbose` flag for more details. Going on using bundler #{current_version}."
else
restart_with(version)
end
def install(spec)
spec.source.install(spec)
end
def restart_with(version)
configured_gem_home = ENV["GEM_HOME"]
configured_gem_path = ENV["GEM_PATH"]
# Bundler specs need some stuff to be required before Bundler starts
# running, for example, for faking the compact index API. However, these
# flags are lost when we reexec to a different version of Bundler. In the
# future, we may be able to properly reconstruct the original Ruby
# invocation (see https://bugs.ruby-lang.org/issues/6648), but for now
# there's no way to do it, so we need to be explicit about how to re-exec.
# This may be a feature end users request at some point, but maybe by that
# time, we have builtin tools to do. So for now, we use an undocumented
# ENV variable only for our specs.
bundler_spec_original_cmd = ENV["BUNDLER_SPEC_ORIGINAL_CMD"]
if bundler_spec_original_cmd
require "shellwords"
cmd = [*Shellwords.shellsplit(bundler_spec_original_cmd), *ARGV]
else
argv0 = File.exist?($PROGRAM_NAME) ? $PROGRAM_NAME : Process.argv0
cmd = [argv0, *ARGV]
cmd.unshift(Gem.ruby) unless File.executable?(argv0)
end
Bundler.with_original_env do
Kernel.exec(
{ "GEM_HOME" => configured_gem_home, "GEM_PATH" => configured_gem_path, "BUNDLER_VERSION" => version.to_s },
*cmd
)
end
end
def needs_switching?
autoswitching_applies? &&
Bundler.settings[:version] != "system" &&
released?(restart_version) &&
!running?(restart_version) &&
!updating?
end
def autoswitching_applies?
ENV["BUNDLER_VERSION"].nil? &&
ruby_can_restart_with_same_arguments? &&
SharedHelpers.in_bundle? &&
lockfile_version
end
def resolve_update_version_from(target)
requirement = Gem::Requirement.new(target)
update_candidate = find_latest_matching_spec(requirement)
if update_candidate.nil?
raise InvalidOption, "The `bundle update --bundler` target version (#{target}) does not exist"
end
resolved_version = update_candidate.version
needs_update = requirement.specific? ? !running?(resolved_version) : running_older_than?(resolved_version)
return unless needs_update
update_candidate
end
def local_specs
@local_specs ||= Bundler::Source::Rubygems.new("allow_local" => true).specs.select {|spec| spec.name == "bundler" }
end
def remote_specs
@remote_specs ||= begin
source = Bundler::Source::Rubygems.new("remotes" => "https://rubygems.org")
source.remote!
source.add_dependency_names("bundler")
source.specs.select(&:matches_current_metadata?)
end
end
def find_latest_matching_spec(requirement)
local_result = find_latest_matching_spec_from_collection(local_specs, requirement)
return local_result if local_result && requirement.specific?
remote_result = find_latest_matching_spec_from_collection(remote_specs, requirement)
return remote_result if local_result.nil?
[local_result, remote_result].max
end
def find_latest_matching_spec_from_collection(specs, requirement)
specs.sort.reverse_each.find {|spec| requirement.satisfied_by?(spec.version) }
end
def running?(version)
version == current_version
end
def running_older_than?(version)
current_version < version
end
def released?(version)
!version.to_s.end_with?(".dev")
end
def ruby_can_restart_with_same_arguments?
$PROGRAM_NAME != "-e"
end
def updating?
"update".start_with?(ARGV.first || " ") && ARGV[1..-1].any? {|a| a.start_with?("--bundler") }
end
def installed?
Bundler.configure
Bundler.rubygems.find_bundler(restart_version.to_s)
end
def current_version
@current_version ||= Gem::Version.new(Bundler::VERSION)
end
def lockfile_version
return @lockfile_version if defined?(@lockfile_version)
parsed_version = Bundler::LockfileParser.bundled_with
@lockfile_version = parsed_version ? Gem::Version.new(parsed_version) : nil
rescue ArgumentError
@lockfile_version = nil
end
def restart_version
return @restart_version if defined?(@restart_version)
# BUNDLE_VERSION=x.y.z
@restart_version = Gem::Version.new(Bundler.settings[:version])
rescue ArgumentError
# BUNDLE_VERSION=lockfile
@restart_version = lockfile_version
end
end
end
|