bundler 2.4.19 → 2.4.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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