bundler 4.0.12 → 4.0.14
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 +4 -4
- data/CHANGELOG.md +23 -0
- data/lib/bundler/build_metadata.rb +1 -1
- data/lib/bundler/cli/add.rb +3 -0
- data/lib/bundler/cli/common.rb +6 -0
- data/lib/bundler/cli/install.rb +3 -0
- data/lib/bundler/cli/outdated.rb +42 -2
- data/lib/bundler/cli/update.rb +2 -0
- data/lib/bundler/cli.rb +4 -0
- data/lib/bundler/dsl.rb +6 -2
- data/lib/bundler/endpoint_specification.rb +11 -1
- data/lib/bundler/installer.rb +5 -0
- data/lib/bundler/man/bundle-add.1 +4 -1
- data/lib/bundler/man/bundle-add.1.ronn +6 -1
- data/lib/bundler/man/bundle-config.1 +120 -156
- data/lib/bundler/man/bundle-config.1.ronn +30 -0
- data/lib/bundler/man/bundle-install.1 +4 -1
- data/lib/bundler/man/bundle-install.1.ronn +9 -1
- data/lib/bundler/man/bundle-outdated.1 +16 -13
- data/lib/bundler/man/bundle-outdated.1.ronn +19 -12
- data/lib/bundler/man/bundle-update.1 +4 -1
- data/lib/bundler/man/bundle-update.1.ronn +8 -0
- data/lib/bundler/remote_specification.rb +1 -1
- data/lib/bundler/resolver.rb +58 -1
- data/lib/bundler/rubygems_ext.rb +22 -0
- data/lib/bundler/rubygems_gem_installer.rb +1 -1
- data/lib/bundler/settings.rb +1 -0
- data/lib/bundler/source/git/git_proxy.rb +7 -2
- data/lib/bundler/source/rubygems/remote.rb +12 -2
- data/lib/bundler/source/rubygems.rb +52 -4
- data/lib/bundler/source_list.rb +10 -2
- data/lib/bundler/version.rb +1 -1
- metadata +2 -2
data/lib/bundler/resolver.rb
CHANGED
|
@@ -184,6 +184,9 @@ module Bundler
|
|
|
184
184
|
|
|
185
185
|
platforms_explanation = specs_matching_other_platforms.any? ? " for any resolution platforms (#{package.platforms.join(", ")})" : ""
|
|
186
186
|
custom_explanation = "#{constraint} could not be found in #{repository_for(package)}#{platforms_explanation}"
|
|
187
|
+
if hint = cooldown_hint(specs_matching_other_platforms)
|
|
188
|
+
custom_explanation += " (#{hint})"
|
|
189
|
+
end
|
|
187
190
|
|
|
188
191
|
label = "#{name} (#{constraint_string})"
|
|
189
192
|
extended_explanation = other_specs_matching_message(specs_matching_other_platforms, label) if specs_matching_other_platforms.any?
|
|
@@ -353,6 +356,10 @@ module Bundler
|
|
|
353
356
|
message << "\n#{other_specs_matching_message(specs, matching_part)}"
|
|
354
357
|
end
|
|
355
358
|
|
|
359
|
+
if hint = cooldown_hint(specs_matching_requirement)
|
|
360
|
+
message << "\n\n#{hint}."
|
|
361
|
+
end
|
|
362
|
+
|
|
356
363
|
if specs_matching_requirement.any? && (hint = platform_mismatch_hint)
|
|
357
364
|
message << "\n\n#{hint}"
|
|
358
365
|
end
|
|
@@ -396,7 +403,7 @@ module Bundler
|
|
|
396
403
|
end
|
|
397
404
|
|
|
398
405
|
def filter_specs(specs, package)
|
|
399
|
-
filter_remote_specs(filter_prereleases(specs, package), package)
|
|
406
|
+
filter_remote_specs(filter_cooldown(filter_prereleases(specs, package)), package)
|
|
400
407
|
end
|
|
401
408
|
|
|
402
409
|
def filter_prereleases(specs, package)
|
|
@@ -405,6 +412,56 @@ module Bundler
|
|
|
405
412
|
specs.reject {|s| s.version.prerelease? }
|
|
406
413
|
end
|
|
407
414
|
|
|
415
|
+
def filter_cooldown(specs)
|
|
416
|
+
return specs if specs.empty?
|
|
417
|
+
excluded_versions = cooldown_excluded_versions(specs)
|
|
418
|
+
return specs if excluded_versions.empty?
|
|
419
|
+
specs.reject {|s| excluded_versions.include?([s.name, s.version]) }
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
def cooldown_excluded_versions(specs)
|
|
423
|
+
excluded = {}
|
|
424
|
+
specs.each do |spec|
|
|
425
|
+
next unless cooldown_excluded?(spec)
|
|
426
|
+
excluded[[spec.name, spec.version]] = true
|
|
427
|
+
end
|
|
428
|
+
excluded
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
def cooldown_hint(specs)
|
|
432
|
+
excluded_versions = cooldown_excluded_versions(specs)
|
|
433
|
+
return nil if excluded_versions.empty?
|
|
434
|
+
"#{excluded_versions.size} version#{"s" if excluded_versions.size > 1} excluded by the cooldown setting; pass `--cooldown 0` to bypass"
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
def cooldown_excluded?(spec)
|
|
438
|
+
return false unless spec.respond_to?(:created_at) && spec.created_at
|
|
439
|
+
return false unless spec.respond_to?(:remote) && spec.remote
|
|
440
|
+
return false if pinned_by_lockfile_floor?(spec)
|
|
441
|
+
days = spec.remote.effective_cooldown
|
|
442
|
+
return false if days.nil? || days <= 0
|
|
443
|
+
(cooldown_now - spec.created_at) < (days * 86_400)
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
# A spec sitting exactly at a `>= locked_version` prevent-downgrade floor is
|
|
447
|
+
# the version the lockfile currently pins. `bundle update` and `bundle
|
|
448
|
+
# outdated` install that floor so resolution never moves a gem backwards.
|
|
449
|
+
# Filtering it out for cooldown would then make resolution impossible
|
|
450
|
+
# whenever the locked version is itself inside the cooldown window, which is
|
|
451
|
+
# exactly what happens to a lockfile written before cooldown was enabled.
|
|
452
|
+
# Keep it eligible; gems being explicitly updated carry an exact `=`
|
|
453
|
+
# requirement instead and stay subject to the cooldown filter.
|
|
454
|
+
def pinned_by_lockfile_floor?(spec)
|
|
455
|
+
return false unless defined?(@base) && @base
|
|
456
|
+
requirement = base_requirements[spec.name]
|
|
457
|
+
return false unless requirement && !requirement.exact?
|
|
458
|
+
requirement.requirements.any? {|op, version| op == ">=" && version == spec.version }
|
|
459
|
+
end
|
|
460
|
+
|
|
461
|
+
def cooldown_now
|
|
462
|
+
@cooldown_now ||= Time.now
|
|
463
|
+
end
|
|
464
|
+
|
|
408
465
|
def filter_remote_specs(specs, package)
|
|
409
466
|
if package.prefer_local?
|
|
410
467
|
local_specs = specs.select {|s| s.is_a?(StubSpecification) }
|
data/lib/bundler/rubygems_ext.rb
CHANGED
|
@@ -465,6 +465,28 @@ module Gem
|
|
|
465
465
|
Resolver::APISet::GemParser.prepend(UnfreezeCompactIndexParsedResponse)
|
|
466
466
|
end
|
|
467
467
|
|
|
468
|
+
# RubyGems before 4.0.13 split compact index dependency/requirement entries
|
|
469
|
+
# on every colon, which mangles metadata values that contain colons such as
|
|
470
|
+
# the `created_at` timestamps the cooldown feature relies on. Split only on
|
|
471
|
+
# the first colon so those values survive on older RubyGems.
|
|
472
|
+
#
|
|
473
|
+
# The module is defined unconditionally so it stays testable on any RubyGems,
|
|
474
|
+
# but only prepended when the host RubyGems still has the buggy behavior.
|
|
475
|
+
module SplitCompactIndexEntryOnFirstColon
|
|
476
|
+
private
|
|
477
|
+
|
|
478
|
+
def parse_dependency(string)
|
|
479
|
+
dependency = string.split(":", 2)
|
|
480
|
+
dependency[-1] = dependency[-1].split("&") if dependency.size > 1
|
|
481
|
+
dependency[0] = -dependency[0]
|
|
482
|
+
dependency
|
|
483
|
+
end
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
unless Gem.rubygems_version >= Gem::Version.new("4.0.13")
|
|
487
|
+
Resolver::APISet::GemParser.prepend(SplitCompactIndexEntryOnFirstColon)
|
|
488
|
+
end
|
|
489
|
+
|
|
468
490
|
if Gem.rubygems_version < Gem::Version.new("3.6.0")
|
|
469
491
|
class Package; end
|
|
470
492
|
require "rubygems/package/tar_reader"
|
data/lib/bundler/settings.rb
CHANGED
|
@@ -432,9 +432,14 @@ module Bundler
|
|
|
432
432
|
end
|
|
433
433
|
|
|
434
434
|
def capture3_args_for(cmd, dir)
|
|
435
|
-
|
|
435
|
+
# Disable automatic maintenance so a background commit-graph write in
|
|
436
|
+
# the source repo can't race the hardlinking local clone and fail with
|
|
437
|
+
# "hardlink different from source".
|
|
438
|
+
opts = ["-c", "gc.auto=0", "-c", "maintenance.auto=false"]
|
|
436
439
|
|
|
437
|
-
["git",
|
|
440
|
+
return ["git", *opts, *cmd] unless dir
|
|
441
|
+
|
|
442
|
+
["git", "-C", dir.to_s, *opts, *cmd]
|
|
438
443
|
end
|
|
439
444
|
|
|
440
445
|
def extra_clone_args
|
|
@@ -4,9 +4,9 @@ module Bundler
|
|
|
4
4
|
class Source
|
|
5
5
|
class Rubygems
|
|
6
6
|
class Remote
|
|
7
|
-
attr_reader :uri, :anonymized_uri, :original_uri
|
|
7
|
+
attr_reader :uri, :anonymized_uri, :original_uri, :cooldown
|
|
8
8
|
|
|
9
|
-
def initialize(uri)
|
|
9
|
+
def initialize(uri, cooldown: nil)
|
|
10
10
|
orig_uri = uri
|
|
11
11
|
uri = Bundler.settings.mirror_for(uri)
|
|
12
12
|
@original_uri = orig_uri if orig_uri != uri
|
|
@@ -14,6 +14,16 @@ module Bundler
|
|
|
14
14
|
|
|
15
15
|
@uri = apply_auth(uri, fallback_auth).freeze
|
|
16
16
|
@anonymized_uri = remove_auth(@uri).freeze
|
|
17
|
+
@cooldown = cooldown
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Returns the cooldown days that apply to this remote, resolving the
|
|
21
|
+
# precedence CLI > config > Gemfile per-source. Returns nil if no
|
|
22
|
+
# cooldown applies.
|
|
23
|
+
def effective_cooldown
|
|
24
|
+
override = Bundler.settings[:cooldown]
|
|
25
|
+
return override if override
|
|
26
|
+
@cooldown
|
|
17
27
|
end
|
|
18
28
|
|
|
19
29
|
MAX_CACHE_SLUG_HOST_SIZE = 255 - 1 - 32 # 255 minus dot minus MD5 length
|
|
@@ -11,11 +11,12 @@ module Bundler
|
|
|
11
11
|
API_REQUEST_SIZE = 100
|
|
12
12
|
REQUIRE_MUTEX = Mutex.new
|
|
13
13
|
|
|
14
|
-
attr_accessor :remotes
|
|
14
|
+
attr_accessor :remotes, :remote_cooldowns
|
|
15
15
|
|
|
16
16
|
def initialize(options = {})
|
|
17
17
|
@options = options
|
|
18
18
|
@remotes = []
|
|
19
|
+
@remote_cooldowns = {}
|
|
19
20
|
@dependency_names = []
|
|
20
21
|
@allow_remote = false
|
|
21
22
|
@allow_cached = false
|
|
@@ -25,7 +26,8 @@ module Bundler
|
|
|
25
26
|
@gem_installers = {}
|
|
26
27
|
@gem_installers_mutex = Mutex.new
|
|
27
28
|
|
|
28
|
-
|
|
29
|
+
cooldown = options["cooldown"]
|
|
30
|
+
Array(options["remotes"]).reverse_each {|r| add_remote(r, cooldown: cooldown) }
|
|
29
31
|
|
|
30
32
|
@lockfile_remotes = @remotes if options["from_lockfile"]
|
|
31
33
|
end
|
|
@@ -148,6 +150,13 @@ module Bundler
|
|
|
148
150
|
# sources, and large_idx.merge! small_idx is way faster than
|
|
149
151
|
# small_idx.merge! large_idx.
|
|
150
152
|
index = @allow_remote ? remote_specs.dup : Index.new
|
|
153
|
+
|
|
154
|
+
# Snapshot per-version `created_at` from the remote info before installed
|
|
155
|
+
# / cached specs overwrite the EndpointSpecification objects that carry
|
|
156
|
+
# it. The cooldown filter consults `created_at` on every candidate, so
|
|
157
|
+
# local stubs need the published date back-filled to participate.
|
|
158
|
+
remote_created_at = collect_remote_created_at(index)
|
|
159
|
+
|
|
151
160
|
index.merge!(cached_specs) if @allow_cached
|
|
152
161
|
index.merge!(installed_specs) if @allow_local
|
|
153
162
|
|
|
@@ -161,6 +170,8 @@ module Bundler
|
|
|
161
170
|
end
|
|
162
171
|
end
|
|
163
172
|
|
|
173
|
+
backfill_created_at(index, remote_created_at) unless remote_created_at.empty?
|
|
174
|
+
|
|
164
175
|
index
|
|
165
176
|
end
|
|
166
177
|
end
|
|
@@ -243,9 +254,14 @@ module Bundler
|
|
|
243
254
|
cached_path
|
|
244
255
|
end
|
|
245
256
|
|
|
246
|
-
def add_remote(source)
|
|
257
|
+
def add_remote(source, cooldown: nil)
|
|
247
258
|
uri = normalize_uri(source)
|
|
248
259
|
@remotes.unshift(uri) unless @remotes.include?(uri)
|
|
260
|
+
@remote_cooldowns[uri] = cooldown if cooldown
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def cooldown_for(uri)
|
|
264
|
+
@remote_cooldowns[uri]
|
|
249
265
|
end
|
|
250
266
|
|
|
251
267
|
def spec_names
|
|
@@ -266,7 +282,7 @@ module Bundler
|
|
|
266
282
|
|
|
267
283
|
def remote_fetchers
|
|
268
284
|
@remote_fetchers ||= remotes.to_h do |uri|
|
|
269
|
-
remote = Source::Rubygems::Remote.new(uri)
|
|
285
|
+
remote = Source::Rubygems::Remote.new(uri, cooldown: cooldown_for(uri))
|
|
270
286
|
[remote, Bundler::Fetcher.new(remote)]
|
|
271
287
|
end.freeze
|
|
272
288
|
end
|
|
@@ -314,6 +330,13 @@ module Bundler
|
|
|
314
330
|
@allow_remote && api_fetchers.any?
|
|
315
331
|
end
|
|
316
332
|
|
|
333
|
+
def clear_cache
|
|
334
|
+
@specs = nil
|
|
335
|
+
@installed_specs = nil
|
|
336
|
+
@default_specs = nil
|
|
337
|
+
@cached_specs = nil
|
|
338
|
+
end
|
|
339
|
+
|
|
317
340
|
protected
|
|
318
341
|
|
|
319
342
|
def remote_names
|
|
@@ -456,6 +479,31 @@ module Bundler
|
|
|
456
479
|
|
|
457
480
|
private
|
|
458
481
|
|
|
482
|
+
def collect_remote_created_at(index)
|
|
483
|
+
return {} unless @allow_remote
|
|
484
|
+
|
|
485
|
+
snapshot = {}
|
|
486
|
+
index.each do |spec|
|
|
487
|
+
next unless spec.respond_to?(:created_at) && spec.created_at
|
|
488
|
+
# Remember the remote that supplied the date too: when a source has
|
|
489
|
+
# several remotes with different per-URI cooldown settings we must
|
|
490
|
+
# restore the same one during backfill so `effective_cooldown` agrees.
|
|
491
|
+
snapshot[[spec.name, spec.version]] = [spec.created_at, spec.remote]
|
|
492
|
+
end
|
|
493
|
+
snapshot
|
|
494
|
+
end
|
|
495
|
+
|
|
496
|
+
def backfill_created_at(index, snapshot)
|
|
497
|
+
index.each do |spec|
|
|
498
|
+
next unless spec.respond_to?(:created_at=)
|
|
499
|
+
next if spec.created_at
|
|
500
|
+
remote_created_at, remote = snapshot[[spec.name, spec.version]]
|
|
501
|
+
next unless remote_created_at
|
|
502
|
+
spec.created_at = remote_created_at
|
|
503
|
+
spec.remote ||= remote if remote && spec.respond_to?(:remote=)
|
|
504
|
+
end
|
|
505
|
+
end
|
|
506
|
+
|
|
459
507
|
def lockfile_remotes
|
|
460
508
|
@lockfile_remotes || credless_remotes
|
|
461
509
|
end
|
data/lib/bundler/source_list.rb
CHANGED
|
@@ -59,8 +59,8 @@ module Bundler
|
|
|
59
59
|
add_source_to_list Plugin.source(source).new(options), @plugin_sources
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
-
def add_global_rubygems_remote(uri)
|
|
63
|
-
global_rubygems_source.add_remote(uri)
|
|
62
|
+
def add_global_rubygems_remote(uri, cooldown: nil)
|
|
63
|
+
global_rubygems_source.add_remote(uri, cooldown: cooldown)
|
|
64
64
|
global_rubygems_source
|
|
65
65
|
end
|
|
66
66
|
|
|
@@ -136,6 +136,10 @@ module Bundler
|
|
|
136
136
|
all_sources.each(&:remote!)
|
|
137
137
|
end
|
|
138
138
|
|
|
139
|
+
def clear_cache
|
|
140
|
+
rubygems_sources.each(&:clear_cache)
|
|
141
|
+
end
|
|
142
|
+
|
|
139
143
|
private
|
|
140
144
|
|
|
141
145
|
def map_sources(replacement_sources)
|
|
@@ -165,6 +169,10 @@ module Bundler
|
|
|
165
169
|
# locked sources never include credentials so always prefer remotes from the gemfile
|
|
166
170
|
replacement_source.remotes = gemfile_source.remotes
|
|
167
171
|
|
|
172
|
+
# cooldowns are only ever declared in the Gemfile, so carry them over
|
|
173
|
+
# along with the remotes they apply to
|
|
174
|
+
replacement_source.remote_cooldowns = gemfile_source.remote_cooldowns
|
|
175
|
+
|
|
168
176
|
yield replacement_source if block_given?
|
|
169
177
|
|
|
170
178
|
replacement_source
|
data/lib/bundler/version.rb
CHANGED
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: 4.0.
|
|
4
|
+
version: 4.0.14
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- André Arko
|
|
@@ -402,7 +402,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
402
402
|
- !ruby/object:Gem::Version
|
|
403
403
|
version: 3.4.1
|
|
404
404
|
requirements: []
|
|
405
|
-
rubygems_version: 4.0.
|
|
405
|
+
rubygems_version: 4.0.13
|
|
406
406
|
specification_version: 4
|
|
407
407
|
summary: The best way to manage your application's dependencies
|
|
408
408
|
test_files: []
|