Skip to content

Commit bcc287f

Browse files
deivid-rodriguezhsbt
authored andcommitted
[rubygems/rubygems] Fix bundle update foo not upgrading foo to latest in a specific case
If upgrading `foo` needs an indirect dependency to be downgraded, Bundler would not be able to upgrade foo. This is because when calculating the latest resolvable version of foo, Bundler was still adding lower bound requirements on the locked versions of all dependencies to avoid downgrades, effectively pinning foo to a version older than the latest. To fix this, instead of creating a second "unlocked" definition to figure out the latest resolvable version, create a second unlocked resolver, and DO NOT add lower bound requirements to it. ruby/rubygems@00cc0ecc69
1 parent 8136039 commit bcc287f

File tree

2 files changed

+63
-17
lines changed

2 files changed

+63
-17
lines changed

lib/bundler/definition.rb

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -492,8 +492,6 @@ def unlocking?
492492
@unlocking
493493
end
494494

495-
attr_writer :source_requirements
496-
497495
def add_checksums
498496
@locked_checksums = true
499497

@@ -614,7 +612,7 @@ def write_lock(file, preserve_unknown_sections)
614612
end
615613

616614
def resolver
617-
@resolver ||= Resolver.new(resolution_base, gem_version_promoter, @most_specific_locked_platform)
615+
@resolver ||= new_resolver(resolution_base)
618616
end
619617

620618
def expanded_dependencies
@@ -632,8 +630,7 @@ def resolution_base
632630
@resolution_base ||= begin
633631
last_resolve = converge_locked_specs
634632
remove_invalid_platforms!
635-
new_resolution_platforms = @current_platform_missing ? @new_platforms + [Bundler.local_platform] : @new_platforms
636-
base = Resolver::Base.new(source_requirements, expanded_dependencies, last_resolve, @platforms, locked_specs: @originally_locked_specs, unlock: @unlocking_all || @gems_to_unlock, prerelease: gem_version_promoter.pre?, prefer_local: @prefer_local, new_platforms: new_resolution_platforms)
633+
base = new_resolution_base(last_resolve: last_resolve, unlock: @unlocking_all || @gems_to_unlock)
637634
base = additional_base_requirements_to_prevent_downgrades(base)
638635
base = additional_base_requirements_to_force_updates(base)
639636
base
@@ -1136,25 +1133,14 @@ def additional_base_requirements_to_prevent_downgrades(resolution_base)
11361133

11371134
def additional_base_requirements_to_force_updates(resolution_base)
11381135
return resolution_base if @explicit_unlocks.empty?
1139-
full_update = dup_for_full_unlock.resolve
1136+
full_update = SpecSet.new(new_resolver_for_full_update.start)
11401137
@explicit_unlocks.each do |name|
11411138
version = full_update.version_for(name)
11421139
resolution_base.base_requirements[name] = Gem::Requirement.new("= #{version}") if version
11431140
end
11441141
resolution_base
11451142
end
11461143

1147-
def dup_for_full_unlock
1148-
unlocked_definition = self.class.new(@lockfile, @dependencies, @sources, true, @ruby_version, @optional_groups, @gemfiles)
1149-
unlocked_definition.source_requirements = source_requirements
1150-
unlocked_definition.gem_version_promoter.tap do |gvp|
1151-
gvp.level = gem_version_promoter.level
1152-
gvp.strict = gem_version_promoter.strict
1153-
gvp.pre = gem_version_promoter.pre
1154-
end
1155-
unlocked_definition
1156-
end
1157-
11581144
def remove_invalid_platforms!
11591145
return if Bundler.frozen_bundle?
11601146

@@ -1173,5 +1159,22 @@ def remove_invalid_platforms!
11731159
def source_map
11741160
@source_map ||= SourceMap.new(sources, dependencies, @locked_specs)
11751161
end
1162+
1163+
def new_resolver_for_full_update
1164+
new_resolver(unlocked_resolution_base)
1165+
end
1166+
1167+
def unlocked_resolution_base
1168+
new_resolution_base(last_resolve: SpecSet.new([]), unlock: true)
1169+
end
1170+
1171+
def new_resolution_base(last_resolve:, unlock:)
1172+
new_resolution_platforms = @current_platform_missing ? @new_platforms + [Bundler.local_platform] : @new_platforms
1173+
Resolver::Base.new(source_requirements, expanded_dependencies, last_resolve, @platforms, locked_specs: @originally_locked_specs, unlock: unlock, prerelease: gem_version_promoter.pre?, prefer_local: @prefer_local, new_platforms: new_resolution_platforms)
1174+
end
1175+
1176+
def new_resolver(base)
1177+
Resolver.new(base, gem_version_promoter, @most_specific_locked_platform)
1178+
end
11761179
end
11771180
end

spec/bundler/commands/update_spec.rb

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,49 @@
445445
expect(out).to include("Installing sneakers 2.11.0").and include("Installing rake 13.0.6")
446446
end
447447

448+
it "downgrades indirect dependencies if required to fulfill an explicit upgrade request" do
449+
build_repo4 do
450+
build_gem "rbs", "3.6.1"
451+
build_gem "rbs", "3.9.4"
452+
453+
build_gem "solargraph", "0.56.0" do |s|
454+
s.add_dependency "rbs", "~> 3.3"
455+
end
456+
457+
build_gem "solargraph", "0.56.2" do |s|
458+
s.add_dependency "rbs", "~> 3.6.1"
459+
end
460+
end
461+
462+
gemfile <<~G
463+
source "https://gem.repo4"
464+
465+
gem 'solargraph', '~> 0.56.0'
466+
G
467+
468+
lockfile <<~L
469+
GEM
470+
remote: https://gem.repo4/
471+
specs:
472+
rbs (3.9.4)
473+
solargraph (0.56.0)
474+
rbs (~> 3.3)
475+
476+
PLATFORMS
477+
#{lockfile_platforms}
478+
479+
DEPENDENCIES
480+
solargraph (~> 0.56.0)
481+
482+
BUNDLED WITH
483+
#{Bundler::VERSION}
484+
L
485+
486+
bundle "lock --update solargraph"
487+
488+
expect(lockfile).to include("solargraph (0.56.2)")
489+
end
490+
448491
it "does not downgrade direct dependencies unnecessarily" do
449492
build_repo4 do
450493
build_gem "redis", "4.8.1"

0 commit comments

Comments
 (0)