bundler 2.4.19 → 2.4.20

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a9e1ac32a1ea746c717048bdfc433edc9d61cd2184f6d07b24e6addb565c1693
4
- data.tar.gz: 95ab89a536022f9c642dd02683640819134441441dd0c4ae7b1cb0e9dd561a68
3
+ metadata.gz: 6276990d50143a594e7e8625034add1d1168df511587dd82c74f7b93a7e66bdc
4
+ data.tar.gz: cca71ac6a7840814e8a7178ca795ed379684658b25c320e8bb67f2c022d3f2e8
5
5
  SHA512:
6
- metadata.gz: 1cda1a2dfbdf88aaf7627304dc8fc740d3e4343b6d4f0e97a6473dc168b3b78c1128973251625beeee479a0418c25972f7dedb0cbcc9e107666fcdad32c991c8
7
- data.tar.gz: e4589afe5650d8a937a2c56196221db8ce72d8cb91539ca0fceb9ad742d542ab4415b5253e6ffa3c8b03ccaac3dc6e8fe3b25d61cb17cd9155cc50d16dc44d4c
6
+ metadata.gz: 5162fc140937170d6c3a58e7f9097cbffbcda5fa8edc96a22a14fa5c1ed548cebb8d45fc9ae9901078f4dd1ff99e6f9892f832c2b6a1f598cd34f5163e80b282
7
+ data.tar.gz: 0aea59def565fa9dc8172659891fe2e6ce7b20a033f5197e1679536c91d1102a170a3784d7a05da0255662d6d342b2d1972a437fcad919756f50321b0a6801df
data/CHANGELOG.md CHANGED
@@ -1,3 +1,32 @@
1
+ # 2.4.20 (September 27, 2023)
2
+
3
+ ## Enhancements:
4
+
5
+ - Bump actions/checkout to v4 in bundler gem template [#6966](https://github.com/rubygems/rubygems/pull/6966)
6
+ - Add support for the `ruby-3.2.2` format in the `ruby file:` Gemfile directive, and explicitly test the `3.2.2@gemset` format as rejected [#6954](https://github.com/rubygems/rubygems/pull/6954)
7
+ - Support `ruby file: ".tool-versions"` in Gemfile [#6898](https://github.com/rubygems/rubygems/pull/6898)
8
+ - Unify LockfileParser loading of SPECS section [#6933](https://github.com/rubygems/rubygems/pull/6933)
9
+ - Only check circular deps when dependency api is available, not on full index sources [#6919](https://github.com/rubygems/rubygems/pull/6919)
10
+
11
+ ## Bug fixes:
12
+
13
+ - Allow standalone mode to work on a Windows edge case [#6989](https://github.com/rubygems/rubygems/pull/6989)
14
+ - Fix `bundle outdated` crashing when both `ref` and `branch` specified for a git gem in Gemfile [#6959](https://github.com/rubygems/rubygems/pull/6959)
15
+ - Fix `bundle update --redownload` [#6924](https://github.com/rubygems/rubygems/pull/6924)
16
+ - Fixed malformed bundler version in lockfile making Bundler crash [#6920](https://github.com/rubygems/rubygems/pull/6920)
17
+ - Fix standalone install crashing when using legacy gemfiles with multiple global sources [#6918](https://github.com/rubygems/rubygems/pull/6918)
18
+ - Resolve ruby version file relative to bundle root [#6892](https://github.com/rubygems/rubygems/pull/6892)
19
+
20
+ ## Performance:
21
+
22
+ - Lazily construct fetcher debug messages [#6973](https://github.com/rubygems/rubygems/pull/6973)
23
+ - Avoid allocating empty hashes in Index [#6962](https://github.com/rubygems/rubygems/pull/6962)
24
+ - Stop allocating the same settings keys repeatedly [#6963](https://github.com/rubygems/rubygems/pull/6963)
25
+ - Improve `Bundler::Index` efficiency by removing unnecessary creation and dups [#6931](https://github.com/rubygems/rubygems/pull/6931)
26
+ - (Further) Improve Bundler::Settings#[] performance and memory usage [#6923](https://github.com/rubygems/rubygems/pull/6923)
27
+ - Don't use full indexes unnecessarily on legacy Gemfiles [#6916](https://github.com/rubygems/rubygems/pull/6916)
28
+ - Improve memory usage in Bundler::Settings, and thus improve boot time [#6884](https://github.com/rubygems/rubygems/pull/6884)
29
+
1
30
  # 2.4.19 (August 17, 2023)
2
31
 
3
32
  ## Enhancements:
@@ -4,8 +4,8 @@ module Bundler
4
4
  # Represents metadata from when the Bundler gem was built.
5
5
  module BuildMetadata
6
6
  # begin ivars
7
- @built_at = "2023-08-17".freeze
8
- @git_commit_sha = "86f98098e3".freeze
7
+ @built_at = "2023-09-27".freeze
8
+ @git_commit_sha = "de20c7e7b".freeze
9
9
  @release = true
10
10
  # end ivars
11
11
 
@@ -33,7 +33,7 @@ module Bundler
33
33
  def default_gem_spec(gem_name)
34
34
  return unless Gem::Specification.respond_to?(:find_all_by_name)
35
35
  gem_spec = Gem::Specification.find_all_by_name(gem_name).last
36
- return gem_spec if gem_spec&.default_gem?
36
+ gem_spec if gem_spec&.default_gem?
37
37
  end
38
38
 
39
39
  def spec_not_found(gem_name)
@@ -63,6 +63,7 @@ module Bundler
63
63
  opts = options.dup
64
64
  opts["update"] = true
65
65
  opts["local"] = options[:local]
66
+ opts["force"] = options[:redownload]
66
67
 
67
68
  Bundler.settings.set_command_option_if_given :jobs, opts["jobs"]
68
69
 
@@ -38,9 +38,9 @@ module Bundler
38
38
 
39
39
  private
40
40
 
41
- def log_specs(debug_msg)
41
+ def log_specs(&block)
42
42
  if Bundler.ui.debug?
43
- Bundler.ui.debug debug_msg
43
+ Bundler.ui.debug yield
44
44
  else
45
45
  Bundler.ui.info ".", false
46
46
  end
@@ -35,7 +35,7 @@ module Bundler
35
35
  remaining_gems = gem_names.dup
36
36
 
37
37
  until remaining_gems.empty?
38
- log_specs "Looking up gems #{remaining_gems.inspect}"
38
+ log_specs { "Looking up gems #{remaining_gems.inspect}" }
39
39
 
40
40
  deps = begin
41
41
  parallel_compact_index_client.dependencies(remaining_gems)
@@ -60,10 +60,6 @@ module Bundler
60
60
  Bundler.ui.debug("FIPS mode is enabled, bundler can't use the CompactIndex API")
61
61
  return nil
62
62
  end
63
- if fetch_uri.scheme == "file"
64
- Bundler.ui.debug("Using a local server, bundler won't use the CompactIndex API")
65
- return false
66
- end
67
63
  # Read info file checksums out of /versions, so we can know if gems are up to date
68
64
  compact_index_client.update_and_parse_checksums!
69
65
  rescue CompactIndexClient::Updater::MisMatchedChecksumError => e
@@ -24,7 +24,7 @@ module Bundler
24
24
  def specs(gem_names, full_dependency_list = [], last_spec_list = [])
25
25
  query_list = gem_names.uniq - full_dependency_list
26
26
 
27
- log_specs "Query List: #{query_list.inspect}"
27
+ log_specs { "Query List: #{query_list.inspect}" }
28
28
 
29
29
  return last_spec_list if query_list.empty?
30
30
 
@@ -9,6 +9,7 @@ require "rubygems/request"
9
9
  module Bundler
10
10
  # Handles all the fetching with the rubygems server
11
11
  class Fetcher
12
+ autoload :Base, File.expand_path("fetcher/base", __dir__)
12
13
  autoload :CompactIndex, File.expand_path("fetcher/compact_index", __dir__)
13
14
  autoload :Downloader, File.expand_path("fetcher/downloader", __dir__)
14
15
  autoload :Dependency, File.expand_path("fetcher/dependency", __dir__)
@@ -134,18 +135,7 @@ module Bundler
134
135
  def specs(gem_names, source)
135
136
  index = Bundler::Index.new
136
137
 
137
- if Bundler::Fetcher.disable_endpoint
138
- @use_api = false
139
- specs = fetchers.last.specs(gem_names)
140
- else
141
- specs = []
142
- @fetchers = fetchers.drop_while do |f|
143
- !f.available? || (f.api_fetcher? && !gem_names) || !specs = f.specs(gem_names)
144
- end
145
- @use_api = false if fetchers.none?(&:api_fetcher?)
146
- end
147
-
148
- specs.each do |name, version, platform, dependencies, metadata|
138
+ fetch_specs(gem_names).each do |name, version, platform, dependencies, metadata|
149
139
  spec = if dependencies
150
140
  EndpointSpecification.new(name, version, platform, self, dependencies, metadata)
151
141
  else
@@ -158,22 +148,10 @@ module Bundler
158
148
 
159
149
  index
160
150
  rescue CertificateFailureError
161
- Bundler.ui.info "" if gem_names && use_api # newline after dots
151
+ Bundler.ui.info "" if gem_names && api_fetcher? # newline after dots
162
152
  raise
163
153
  end
164
154
 
165
- def use_api
166
- return @use_api if defined?(@use_api)
167
-
168
- fetchers.shift until fetchers.first.available?
169
-
170
- @use_api = if remote_uri.scheme == "file" || Bundler::Fetcher.disable_endpoint
171
- false
172
- else
173
- fetchers.first.api_fetcher?
174
- end
175
- end
176
-
177
155
  def user_agent
178
156
  @user_agent ||= begin
179
157
  ruby = Bundler::RubyVersion.system
@@ -209,10 +187,6 @@ module Bundler
209
187
  end
210
188
  end
211
189
 
212
- def fetchers
213
- @fetchers ||= FETCHERS.map {|f| f.new(downloader, @remote, uri) }
214
- end
215
-
216
190
  def http_proxy
217
191
  return unless uri = connection.proxy_uri
218
192
  uri.to_s
@@ -222,9 +196,36 @@ module Bundler
222
196
  "#<#{self.class}:0x#{object_id} uri=#{uri}>"
223
197
  end
224
198
 
199
+ def api_fetcher?
200
+ fetchers.first.api_fetcher?
201
+ end
202
+
225
203
  private
226
204
 
227
- FETCHERS = [CompactIndex, Dependency, Index].freeze
205
+ def available_fetchers
206
+ if Bundler::Fetcher.disable_endpoint
207
+ [Index]
208
+ elsif remote_uri.scheme == "file"
209
+ Bundler.ui.debug("Using a local server, bundler won't use the CompactIndex API")
210
+ [Index]
211
+ else
212
+ [CompactIndex, Dependency, Index]
213
+ end
214
+ end
215
+
216
+ def fetchers
217
+ @fetchers ||= available_fetchers.map {|f| f.new(downloader, @remote, uri) }.drop_while {|f| !f.available? }
218
+ end
219
+
220
+ def fetch_specs(gem_names)
221
+ fetchers.reject!(&:api_fetcher?) unless gem_names
222
+ fetchers.reject! do |f|
223
+ specs = f.specs(gem_names)
224
+ return specs if specs
225
+ true
226
+ end
227
+ []
228
+ end
228
229
 
229
230
  def cis
230
231
  env_cis = {
data/lib/bundler/index.rb CHANGED
@@ -10,8 +10,8 @@ module Bundler
10
10
  i
11
11
  end
12
12
 
13
- attr_reader :specs, :all_specs, :sources
14
- protected :specs, :all_specs
13
+ attr_reader :specs, :duplicates, :sources
14
+ protected :specs, :duplicates
15
15
 
16
16
  RUBY = "ruby"
17
17
  NULL = "\0"
@@ -19,21 +19,21 @@ module Bundler
19
19
  def initialize
20
20
  @sources = []
21
21
  @cache = {}
22
- @specs = Hash.new {|h, k| h[k] = {} }
23
- @all_specs = Hash.new {|h, k| h[k] = EMPTY_SEARCH }
22
+ @specs = {}
23
+ @duplicates = {}
24
24
  end
25
25
 
26
26
  def initialize_copy(o)
27
27
  @sources = o.sources.dup
28
28
  @cache = {}
29
- @specs = Hash.new {|h, k| h[k] = {} }
30
- @all_specs = Hash.new {|h, k| h[k] = EMPTY_SEARCH }
29
+ @specs = {}
30
+ @duplicates = {}
31
31
 
32
32
  o.specs.each do |name, hash|
33
33
  @specs[name] = hash.dup
34
34
  end
35
- o.all_specs.each do |name, array|
36
- @all_specs[name] = array.dup
35
+ o.duplicates.each do |name, array|
36
+ @duplicates[name] = array.dup
37
37
  end
38
38
  end
39
39
 
@@ -46,12 +46,11 @@ module Bundler
46
46
  true
47
47
  end
48
48
 
49
- def search_all(name)
50
- all_matches = local_search(name) + @all_specs[name]
51
- @sources.each do |source|
52
- all_matches.concat(source.search_all(name))
53
- end
54
- all_matches
49
+ def search_all(name, &blk)
50
+ return enum_for(:search_all, name) unless blk
51
+ specs_by_name(name).each(&blk)
52
+ @duplicates[name]&.each(&blk)
53
+ @sources.each {|source| source.search_all(name, &blk) }
55
54
  end
56
55
 
57
56
  # Search this index's specs, and any source indexes that this index knows
@@ -61,11 +60,14 @@ module Bundler
61
60
  return results unless @sources.any?
62
61
 
63
62
  @sources.each do |source|
64
- results.concat(source.search(query))
63
+ results = safe_concat(results, source.search(query))
65
64
  end
66
- results.uniq(&:full_name)
65
+ results.uniq!(&:full_name) unless results.empty? # avoid modifying frozen EMPTY_SEARCH
66
+ results
67
67
  end
68
68
 
69
+ alias_method :[], :search
70
+
69
71
  def local_search(query)
70
72
  case query
71
73
  when Gem::Specification, RemoteSpecification, LazySpecification, EndpointSpecification then search_by_spec(query)
@@ -76,12 +78,10 @@ module Bundler
76
78
  end
77
79
  end
78
80
 
79
- alias_method :[], :search
80
-
81
- def <<(spec)
82
- @specs[spec.name][spec.full_name] = spec
83
- spec
81
+ def add(spec)
82
+ (@specs[spec.name] ||= {}).store(spec.full_name, spec)
84
83
  end
84
+ alias_method :<<, :add
85
85
 
86
86
  def each(&blk)
87
87
  return enum_for(:each) unless blk
@@ -115,15 +115,25 @@ module Bundler
115
115
  names.uniq
116
116
  end
117
117
 
118
- def use(other, override_dupes = false)
118
+ # Combines indexes proritizing existing specs, like `Hash#reverse_merge!`
119
+ # Duplicate specs found in `other` are stored in `@duplicates`.
120
+ def use(other)
119
121
  return unless other
120
- other.each do |s|
121
- if (dupes = search_by_spec(s)) && !dupes.empty?
122
- # safe to << since it's a new array when it has contents
123
- @all_specs[s.name] = dupes << s
124
- next unless override_dupes
122
+ other.each do |spec|
123
+ exist?(spec) ? add_duplicate(spec) : add(spec)
124
+ end
125
+ self
126
+ end
127
+
128
+ # Combines indexes proritizing specs from `other`, like `Hash#merge!`
129
+ # Duplicate specs found in `self` are saved in `@duplicates`.
130
+ def merge!(other)
131
+ return unless other
132
+ other.each do |spec|
133
+ if existing = find_by_spec(spec)
134
+ add_duplicate(existing)
125
135
  end
126
- self << s
136
+ add spec
127
137
  end
128
138
  self
129
139
  end
@@ -157,19 +167,40 @@ module Bundler
157
167
 
158
168
  private
159
169
 
170
+ def safe_concat(a, b)
171
+ return a if b.empty?
172
+ return b if a.empty?
173
+ a.concat(b)
174
+ end
175
+
176
+ def add_duplicate(spec)
177
+ (@duplicates[spec.name] ||= []) << spec
178
+ end
179
+
160
180
  def specs_by_name_and_version(name, version)
161
- specs_by_name(name).select {|spec| spec.version == version }
181
+ results = @specs[name]&.values
182
+ return EMPTY_SEARCH unless results
183
+ results.select! {|spec| spec.version == version }
184
+ results
162
185
  end
163
186
 
164
187
  def specs_by_name(name)
165
- @specs[name].values
188
+ @specs[name]&.values || EMPTY_SEARCH
166
189
  end
167
190
 
168
191
  EMPTY_SEARCH = [].freeze
169
192
 
170
193
  def search_by_spec(spec)
171
- spec = @specs[spec.name][spec.full_name]
194
+ spec = find_by_spec(spec)
172
195
  spec ? [spec] : EMPTY_SEARCH
173
196
  end
197
+
198
+ def find_by_spec(spec)
199
+ @specs[spec.name]&.fetch(spec.full_name, nil)
200
+ end
201
+
202
+ def exist?(spec)
203
+ @specs[spec.name]&.key?(spec.full_name)
204
+ end
174
205
  end
175
206
  end
@@ -55,13 +55,20 @@ module Bundler
55
55
  if spec.source.instance_of?(Source::Path) && spec.source.path.absolute?
56
56
  full_path
57
57
  else
58
- Pathname.new(full_path).relative_path_from(Bundler.root.join(bundler_path)).to_s
58
+ relative_path_from(Bundler.root.join(bundler_path), :to => full_path) || full_path
59
59
  end
60
60
  rescue TypeError
61
61
  error_message = "#{spec.name} #{spec.version} has an invalid gemspec"
62
62
  raise Gem::InvalidSpecificationException.new(error_message)
63
63
  end
64
64
 
65
+ def relative_path_from(source, to:)
66
+ Pathname.new(to).relative_path_from(source).to_s
67
+ rescue ArgumentError
68
+ # on Windows, if source and destination are on different drivers, there's no relative path from one to the other
69
+ nil
70
+ end
71
+
65
72
  def define_path_helpers
66
73
  <<~'END'
67
74
  unless defined?(Gem)
@@ -110,21 +110,9 @@ module Bundler
110
110
  def parse_source(line)
111
111
  case line
112
112
  when SPECS
113
- case @type
114
- when PATH
115
- @current_source = TYPES[@type].from_lock(@opts)
116
- @sources << @current_source
117
- when GIT
118
- @current_source = TYPES[@type].from_lock(@opts)
119
- @sources << @current_source
120
- when GEM
121
- @opts["remotes"] = Array(@opts.delete("remote")).reverse
122
- @current_source = TYPES[@type].from_lock(@opts)
123
- @sources << @current_source
124
- when PLUGIN
125
- @current_source = Plugin.source_from_lock(@opts)
126
- @sources << @current_source
127
- end
113
+ return unless TYPES.key?(@type)
114
+ @current_source = TYPES[@type].from_lock(@opts)
115
+ @sources << @current_source
128
116
  when OPTIONS
129
117
  value = $2
130
118
  value = true if value == "true"
@@ -98,6 +98,17 @@ ruby file: "\.ruby\-version"
98
98
  .
99
99
  .IP "" 0
100
100
  .
101
+ .P
102
+ The version file should conform to any of the following formats:
103
+ .
104
+ .IP "\(bu" 4
105
+ \fB3\.1\.2\fR (\.ruby\-version)
106
+ .
107
+ .IP "\(bu" 4
108
+ \fBruby 3\.1\.2\fR (\.tool\-versions, read: https://asdf\-vm\.com/manage/configuration\.html#tool\-versions)
109
+ .
110
+ .IP "" 0
111
+ .
101
112
  .SS "ENGINE"
102
113
  Each application \fImay\fR specify a Ruby engine\. If an engine is specified, an engine version \fImust\fR also be specified\.
103
114
  .
@@ -74,6 +74,11 @@ you can use the `file` option instead.
74
74
 
75
75
  ruby file: ".ruby-version"
76
76
 
77
+ The version file should conform to any of the following formats:
78
+
79
+ - `3.1.2` (.ruby-version)
80
+ - `ruby 3.1.2` (.tool-versions, read: https://asdf-vm.com/manage/configuration.html#tool-versions)
81
+
77
82
  ### ENGINE
78
83
 
79
84
  Each application _may_ specify a Ruby engine. If an engine is specified, an
@@ -197,7 +197,7 @@ module Bundler
197
197
  # @param [Hash] The options that are present in the lock file
198
198
  # @return [API::Source] the instance of the class that handles the source
199
199
  # type passed in locked_opts
200
- def source_from_lock(locked_opts)
200
+ def from_lock(locked_opts)
201
201
  src = source(locked_opts["type"])
202
202
 
203
203
  src.new(locked_opts.merge("uri" => locked_opts["remote"]))
@@ -37,9 +37,17 @@ module Bundler
37
37
  root_version = Resolver::Candidate.new(0)
38
38
 
39
39
  @all_specs = Hash.new do |specs, name|
40
- specs[name] = source_for(name).specs.search(name).reject do |s|
41
- s.dependencies.any? {|d| d.name == name && !d.requirement.satisfied_by?(s.version) } # ignore versions that depend on themselves incorrectly
42
- end.sort_by {|s| [s.version, s.platform.to_s] }
40
+ source = source_for(name)
41
+ matches = source.specs.search(name)
42
+
43
+ # Don't bother to check for circular deps when no dependency API are
44
+ # available, since it's too slow to be usable. That edge case won't work
45
+ # but resolution other than that should work fine and reasonably fast.
46
+ if source.respond_to?(:dependency_api_available?) && source.dependency_api_available?
47
+ matches = filter_invalid_self_dependencies(matches, name)
48
+ end
49
+
50
+ specs[name] = matches.sort_by {|s| [s.version, s.platform.to_s] }
43
51
  end
44
52
 
45
53
  @sorted_versions = Hash.new do |candidates, package|
@@ -318,6 +326,13 @@ module Bundler
318
326
  specs.reject {|s| s.version.prerelease? }
319
327
  end
320
328
 
329
+ # Ignore versions that depend on themselves incorrectly
330
+ def filter_invalid_self_dependencies(specs, name)
331
+ specs.reject do |s|
332
+ s.dependencies.any? {|d| d.name == name && !d.requirement.satisfied_by?(s.version) }
333
+ end
334
+ end
335
+
321
336
  def requirement_satisfied_by?(requirement, spec)
322
337
  requirement.satisfied_by?(spec.version) || spec.source.is_a?(Source::Gemspec)
323
338
  end
data/lib/bundler/retry.rb CHANGED
@@ -56,7 +56,7 @@ module Bundler
56
56
  def keep_trying?
57
57
  return true if current_run.zero?
58
58
  return false if last_attempt?
59
- return true if @failed
59
+ true if @failed
60
60
  end
61
61
 
62
62
  def last_attempt?
@@ -10,8 +10,8 @@ module Bundler
10
10
  raise GemfileError, "Please define :engine" if options[:engine_version] && options[:engine].nil?
11
11
 
12
12
  if options[:file]
13
- raise GemfileError, "Cannot specify version when using the file option" if ruby_version.any?
14
- ruby_version << Bundler.read_file(options[:file]).strip
13
+ raise GemfileError, "Do not pass version argument when using :file option" unless ruby_version.empty?
14
+ ruby_version << normalize_ruby_file(options[:file])
15
15
  end
16
16
 
17
17
  if options[:engine] == "ruby" && options[:engine_version] &&
@@ -20,5 +20,26 @@ module Bundler
20
20
  end
21
21
  @ruby_version = RubyVersion.new(ruby_version, options[:patchlevel], options[:engine], options[:engine_version])
22
22
  end
23
+
24
+ # Support the various file formats found in .ruby-version files.
25
+ #
26
+ # 3.2.2
27
+ # ruby-3.2.2
28
+ #
29
+ # Also supports .tool-versions files for asdf. Lines not starting with "ruby" are ignored.
30
+ #
31
+ # ruby 2.5.1 # comment is ignored
32
+ # ruby 2.5.1# close comment and extra spaces doesn't confuse
33
+ #
34
+ # Intentionally does not support `3.2.1@gemset` since rvm recommends using .ruby-gemset instead
35
+ def normalize_ruby_file(filename)
36
+ file_content = Bundler.read_file(Bundler.root.join(filename))
37
+ # match "ruby-3.2.2" or "ruby 3.2.2" capturing version string up to the first space or comment
38
+ if /^ruby(-|\s+)([^\s#]+)/.match(file_content)
39
+ $2
40
+ else
41
+ file_content.strip
42
+ end
43
+ end
23
44
  end
24
45
  end
@@ -163,6 +163,8 @@ module Bundler
163
163
 
164
164
  parsed_version = Bundler::LockfileParser.bundled_with
165
165
  @lockfile_version = parsed_version ? Gem::Version.new(parsed_version) : nil
166
+ rescue ArgumentError
167
+ @lockfile_version = nil
166
168
  end
167
169
  end
168
170
  end
@@ -88,14 +88,24 @@ module Bundler
88
88
  def initialize(root = nil)
89
89
  @root = root
90
90
  @local_config = load_config(local_config_file)
91
- @env_config = ENV.to_h.select {|key, _value| key =~ /\ABUNDLE_.+/ }
91
+
92
+ @env_config = ENV.to_h
93
+ @env_config.select! {|key, _value| key.start_with?("BUNDLE_") }
94
+ @env_config.delete("BUNDLE_")
95
+
92
96
  @global_config = load_config(global_config_file)
93
97
  @temporary = {}
94
98
  end
95
99
 
96
100
  def [](name)
97
101
  key = key_for(name)
98
- value = configs.values.map {|config| config[key] }.compact.first
102
+
103
+ value = nil
104
+ configs.each do |_, config|
105
+ value = config[key]
106
+ next if value.nil?
107
+ break
108
+ end
99
109
 
100
110
  converted_value(value, name)
101
111
  end
@@ -138,17 +148,22 @@ module Bundler
138
148
  end
139
149
 
140
150
  def all
141
- keys = @temporary.keys | @global_config.keys | @local_config.keys | @env_config.keys
151
+ keys = @temporary.keys.union(@global_config.keys, @local_config.keys, @env_config.keys)
142
152
 
143
- keys.map do |key|
144
- key.sub(/^BUNDLE_/, "").gsub(/___/, "-").gsub(/__/, ".").downcase
145
- end.sort
153
+ keys.map! do |key|
154
+ key = key.delete_prefix("BUNDLE_")
155
+ key.gsub!("___", "-")
156
+ key.gsub!("__", ".")
157
+ key.downcase!
158
+ key
159
+ end.sort!
160
+ keys
146
161
  end
147
162
 
148
163
  def local_overrides
149
164
  repos = {}
150
165
  all.each do |k|
151
- repos[$'] = self[k] if k =~ /^local\./
166
+ repos[k.delete_prefix("local.")] = self[k] if k.start_with?("local.")
152
167
  end
153
168
  repos
154
169
  end
@@ -301,7 +316,7 @@ module Bundler
301
316
  private
302
317
 
303
318
  def configs
304
- {
319
+ @configs ||= {
305
320
  :temporary => @temporary,
306
321
  :local => @local_config,
307
322
  :env => @env_config,
@@ -327,16 +342,20 @@ module Bundler
327
342
  end
328
343
 
329
344
  def is_bool(name)
330
- BOOL_KEYS.include?(name.to_s) || BOOL_KEYS.include?(parent_setting_for(name.to_s))
345
+ name = name.to_s
346
+ BOOL_KEYS.include?(name) || BOOL_KEYS.include?(parent_setting_for(name))
331
347
  end
332
348
 
333
349
  def is_string(name)
334
- STRING_KEYS.include?(name.to_s) || name.to_s.start_with?("local.") || name.to_s.start_with?("mirror.") || name.to_s.start_with?("build.")
350
+ name = name.to_s
351
+ STRING_KEYS.include?(name) || name.start_with?("local.") || name.start_with?("mirror.") || name.start_with?("build.")
335
352
  end
336
353
 
337
354
  def to_bool(value)
338
355
  case value
339
- when nil, /\A(false|f|no|n|0|)\z/i, false
356
+ when String
357
+ value.match?(/\A(false|f|no|n|0|)\z/i) ? false : true
358
+ when nil, false
340
359
  false
341
360
  else
342
361
  true
@@ -392,6 +411,8 @@ module Bundler
392
411
  end
393
412
 
394
413
  def converted_value(value, key)
414
+ key = key.to_s
415
+
395
416
  if is_array(key)
396
417
  to_array(value)
397
418
  elsif value.nil?
@@ -482,8 +503,11 @@ module Bundler
482
503
 
483
504
  def self.key_for(key)
484
505
  key = normalize_uri(key).to_s if key.is_a?(String) && key.start_with?("http", "mirror.http")
485
- key = key.to_s.gsub(".", "__").gsub("-", "___").upcase
486
- "BUNDLE_#{key}"
506
+ key = key.to_s.gsub(".", "__")
507
+ key.gsub!("-", "___")
508
+ key.upcase!
509
+
510
+ key.prepend("BUNDLE_")
487
511
  end
488
512
 
489
513
  # TODO: duplicates Rubygems#normalize_uri
@@ -43,6 +43,13 @@ module Bundler
43
43
  end
44
44
  end
45
45
 
46
+ class AmbiguousGitReference < GitError
47
+ def initialize(options)
48
+ msg = "Specification of branch or ref with tag is ambiguous. You specified #{options.inspect}"
49
+ super msg
50
+ end
51
+ end
52
+
46
53
  # The GitProxy is responsible to interact with git repositories.
47
54
  # All actions required by the Git source is encapsulated in this
48
55
  # object.
@@ -53,10 +60,15 @@ module Bundler
53
60
  def initialize(path, uri, options = {}, revision = nil, git = nil)
54
61
  @path = path
55
62
  @uri = uri
56
- @branch = options["branch"]
57
63
  @tag = options["tag"]
64
+ @branch = options["branch"]
58
65
  @ref = options["ref"]
59
- @explicit_ref = branch || tag || ref
66
+ if @tag
67
+ raise AmbiguousGitReference.new(options) if @branch || @ref
68
+ @explicit_ref = @tag
69
+ else
70
+ @explicit_ref = @ref || @branch
71
+ end
60
72
  @revision = revision
61
73
  @git = git
62
74
  @commit_ref = nil
@@ -88,6 +88,7 @@ module Bundler
88
88
  end
89
89
 
90
90
  def self.from_lock(options)
91
+ options["remotes"] = Array(options.delete("remote")).reverse
91
92
  new(options)
92
93
  end
93
94
 
@@ -128,12 +129,12 @@ module Bundler
128
129
  def specs
129
130
  @specs ||= begin
130
131
  # remote_specs usually generates a way larger Index than the other
131
- # sources, and large_idx.use small_idx is way faster than
132
- # small_idx.use large_idx.
133
- idx = @allow_remote ? remote_specs.dup : Index.new
134
- idx.use(cached_specs, :override_dupes) if @allow_cached || @allow_remote
135
- idx.use(installed_specs, :override_dupes) if @allow_local
136
- idx
132
+ # sources, and large_idx.merge! small_idx is way faster than
133
+ # small_idx.merge! large_idx.
134
+ index = @allow_remote ? remote_specs.dup : Index.new
135
+ index.merge!(cached_specs) if @allow_cached || @allow_remote
136
+ index.merge!(installed_specs) if @allow_local
137
+ index
137
138
  end
138
139
  end
139
140
 
@@ -237,7 +238,7 @@ module Bundler
237
238
  end
238
239
 
239
240
  def spec_names
240
- if @allow_remote && dependency_api_available?
241
+ if dependency_api_available?
241
242
  remote_specs.spec_names
242
243
  else
243
244
  []
@@ -245,7 +246,7 @@ module Bundler
245
246
  end
246
247
 
247
248
  def unmet_deps
248
- if @allow_remote && dependency_api_available?
249
+ if dependency_api_available?
249
250
  remote_specs.unmet_dependency_names
250
251
  else
251
252
  []
@@ -260,7 +261,6 @@ module Bundler
260
261
  end
261
262
 
262
263
  def double_check_for(unmet_dependency_names)
263
- return unless @allow_remote
264
264
  return unless dependency_api_available?
265
265
 
266
266
  unmet_dependency_names = unmet_dependency_names.call
@@ -275,7 +275,9 @@ module Bundler
275
275
 
276
276
  Bundler.ui.debug "Double checking for #{unmet_dependency_names || "all specs (due to the size of the request)"} in #{self}"
277
277
 
278
- fetch_names(api_fetchers, unmet_dependency_names, specs, false)
278
+ fetch_names(api_fetchers, unmet_dependency_names, remote_specs)
279
+
280
+ specs.use remote_specs
279
281
  end
280
282
 
281
283
  def dependency_names_to_double_check
@@ -379,7 +381,7 @@ module Bundler
379
381
 
380
382
  def cached_specs
381
383
  @cached_specs ||= begin
382
- idx = @allow_local ? installed_specs.dup : Index.new
384
+ idx = Index.new
383
385
 
384
386
  Dir["#{cache_path}/*.gem"].each do |gemfile|
385
387
  s ||= Bundler.rubygems.spec_from_gem(gemfile)
@@ -392,35 +394,30 @@ module Bundler
392
394
  end
393
395
 
394
396
  def api_fetchers
395
- fetchers.select {|f| f.use_api && f.fetchers.first.api_fetcher? }
397
+ fetchers.select(&:api_fetcher?)
396
398
  end
397
399
 
398
400
  def remote_specs
399
401
  @remote_specs ||= Index.build do |idx|
400
402
  index_fetchers = fetchers - api_fetchers
401
403
 
402
- # gather lists from non-api sites
403
- fetch_names(index_fetchers, nil, idx, false)
404
-
405
- # legacy multi-remote sources need special logic to figure out
406
- # dependency names and that logic can be very costly if one remote
407
- # uses the dependency API but others don't. So use full indexes
408
- # consistently in that particular case.
409
- allow_api = !multiple_remotes?
410
-
411
- fetch_names(api_fetchers, allow_api && dependency_names, idx, false)
404
+ if index_fetchers.empty?
405
+ fetch_names(api_fetchers, dependency_names, idx)
406
+ else
407
+ fetch_names(fetchers, nil, idx)
408
+ end
412
409
  end
413
410
  end
414
411
 
415
- def fetch_names(fetchers, dependency_names, index, override_dupes)
412
+ def fetch_names(fetchers, dependency_names, index)
416
413
  fetchers.each do |f|
417
414
  if dependency_names
418
415
  Bundler.ui.info "Fetching gem metadata from #{URICredentialsFilter.credential_filtered_uri(f.uri)}", Bundler.ui.debug?
419
- index.use f.specs_with_retry(dependency_names, self), override_dupes
416
+ index.use f.specs_with_retry(dependency_names, self)
420
417
  Bundler.ui.info "" unless Bundler.ui.debug? # new line now that the dots are over
421
418
  else
422
419
  Bundler.ui.info "Fetching source index from #{URICredentialsFilter.credential_filtered_uri(f.uri)}"
423
- index.use f.specs_with_retry(nil, self), override_dupes
420
+ index.use f.specs_with_retry(nil, self)
424
421
  end
425
422
  end
426
423
  end
@@ -17,7 +17,7 @@ jobs:
17
17
  - '<%= RUBY_VERSION %>'
18
18
 
19
19
  steps:
20
- - uses: actions/checkout@v3
20
+ - uses: actions/checkout@v4
21
21
  <%- if config[:ext] == 'rust' -%>
22
22
  - name: Set up Ruby & Rust
23
23
  uses: oxidize-rb/actions/setup-ruby-and-rust@v1
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: false
2
2
 
3
3
  module Bundler
4
- VERSION = "2.4.19".freeze
4
+ VERSION = "2.4.20".freeze
5
5
 
6
6
  def self.bundler_major_version
7
7
  @bundler_major_version ||= VERSION.split(".").first.to_i
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bundler
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.4.19
4
+ version: 2.4.20
5
5
  platform: ruby
6
6
  authors:
7
7
  - André Arko
@@ -22,7 +22,7 @@ authors:
22
22
  autorequire:
23
23
  bindir: exe
24
24
  cert_chain: []
25
- date: 2023-08-17 00:00:00.000000000 Z
25
+ date: 2023-09-27 00:00:00.000000000 Z
26
26
  dependencies: []
27
27
  description: Bundler manages an application's dependencies through its entire life,
28
28
  across many machines, systematically and repeatably
@@ -381,7 +381,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
381
381
  - !ruby/object:Gem::Version
382
382
  version: 3.0.1
383
383
  requirements: []
384
- rubygems_version: 3.4.19
384
+ rubygems_version: 3.4.20
385
385
  signing_key:
386
386
  specification_version: 4
387
387
  summary: The best way to manage your application's dependencies