[rubygems/rubygems] Make sure to force latest resolvable version explicitly

To make sure we can always update to the latest resolvable version for
each gem explicitly requested for update, we first run a full update,
and then add explicit exact requirements to the resolved versions. This
may lead into conflicts, but our resolver already automatically parses
those and unlocks additional gems to fix them.

https://github.com/rubygems/rubygems/commit/01c0bf34f0
This commit is contained in:
David Rodriguez 2024-03-27 17:20:52 +01:00 committed by git
parent 491195af02
commit 435f449b4e
2 changed files with 148 additions and 2 deletions

View File

@ -570,7 +570,9 @@ module Bundler
last_resolve = converge_locked_specs
remove_invalid_platforms!(current_dependencies)
packages = Resolver::Base.new(source_requirements, expanded_dependencies, last_resolve, @platforms, locked_specs: @originally_locked_specs, unlock: @gems_to_unlock, prerelease: gem_version_promoter.pre?)
additional_base_requirements_for_resolve(packages, last_resolve)
packages = additional_base_requirements_to_prevent_downgrades(packages, last_resolve)
packages = additional_base_requirements_to_force_updates(packages)
packages
end
end
@ -1015,7 +1017,7 @@ module Bundler
current == proposed
end
def additional_base_requirements_for_resolve(resolution_packages, last_resolve)
def additional_base_requirements_to_prevent_downgrades(resolution_packages, last_resolve)
return resolution_packages unless @locked_gems && !sources.expired_sources?(@locked_gems.sources)
converge_specs(@originally_locked_specs - last_resolve).each do |locked_spec|
next if locked_spec.source.is_a?(Source::Path)
@ -1024,6 +1026,28 @@ module Bundler
resolution_packages
end
def additional_base_requirements_to_force_updates(resolution_packages)
return resolution_packages if @explicit_unlocks.empty?
full_update = dup_for_full_unlock.resolve
@explicit_unlocks.each do |name|
version = full_update[name].first&.version
resolution_packages.base_requirements[name] = Gem::Requirement.new("= #{version}") if version
end
resolution_packages
end
def dup_for_full_unlock
unlocked_definition = self.class.new(@lockfile, @dependencies, @sources, true, @ruby_version, @optional_groups, @gemfiles)
unlocked_definition.resolution_mode = { "local" => !@remote }
unlocked_definition.setup_sources_for_resolve
unlocked_definition.gem_version_promoter.tap do |gvp|
gvp.level = gem_version_promoter.level
gvp.strict = gem_version_promoter.strict
gvp.pre = gem_version_promoter.pre
end
unlocked_definition
end
def remove_invalid_platforms!(dependencies)
return if Bundler.frozen_bundle?

View File

@ -252,6 +252,128 @@ RSpec.describe "bundle lock" do
expect(read_lockfile).to eq(remove_checksums_from_lockfile(@lockfile, "(2.3.2)", "(#{rake_version})"))
end
it "updates specific gems using --update, even if that requires unlocking other top level gems" do
build_repo4 do
build_gem "prism", "0.15.1"
build_gem "prism", "0.24.0"
build_gem "ruby-lsp", "0.12.0" do |s|
s.add_dependency "prism", "< 0.24.0"
end
build_gem "ruby-lsp", "0.16.1" do |s|
s.add_dependency "prism", ">= 0.24.0"
end
build_gem "tapioca", "0.11.10" do |s|
s.add_dependency "prism", "< 0.24.0"
end
build_gem "tapioca", "0.13.1" do |s|
s.add_dependency "prism", ">= 0.24.0"
end
end
gemfile <<~G
source "#{file_uri_for(gem_repo4)}"
gem "tapioca"
gem "ruby-lsp"
G
lockfile <<~L
GEM
remote: #{file_uri_for(gem_repo4)}
specs:
prism (0.15.1)
ruby-lsp (0.12.0)
prism (< 0.24.0)
tapioca (0.11.10)
prism (< 0.24.0)
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
ruby-lsp
tapioca
BUNDLED WITH
#{Bundler::VERSION}
L
bundle "lock --update tapioca --verbose"
expect(lockfile).to include("tapioca (0.13.1)")
end
it "updates specific gems using --update, even if that requires unlocking other top level gems, but only as few as possible" do
build_repo4 do
build_gem "prism", "0.15.1"
build_gem "prism", "0.24.0"
build_gem "ruby-lsp", "0.12.0" do |s|
s.add_dependency "prism", "< 0.24.0"
end
build_gem "ruby-lsp", "0.16.1" do |s|
s.add_dependency "prism", ">= 0.24.0"
end
build_gem "tapioca", "0.11.10" do |s|
s.add_dependency "prism", "< 0.24.0"
end
build_gem "tapioca", "0.13.1" do |s|
s.add_dependency "prism", ">= 0.24.0"
end
build_gem "other-prism-dependent", "1.0.0" do |s|
s.add_dependency "prism", ">= 0.15.1"
end
build_gem "other-prism-dependent", "1.1.0" do |s|
s.add_dependency "prism", ">= 0.15.1"
end
end
gemfile <<~G
source "#{file_uri_for(gem_repo4)}"
gem "tapioca"
gem "ruby-lsp"
gem "other-prism-dependent"
G
lockfile <<~L
GEM
remote: #{file_uri_for(gem_repo4)}
specs:
other-prism-dependent (1.0.0)
prism (>= 0.15.1)
prism (0.15.1)
ruby-lsp (0.12.0)
prism (< 0.24.0)
tapioca (0.11.10)
prism (< 0.24.0)
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
ruby-lsp
tapioca
BUNDLED WITH
#{Bundler::VERSION}
L
bundle "lock --update tapioca"
expect(lockfile).to include("tapioca (0.13.1)")
expect(lockfile).to include("other-prism-dependent (1.0.0)")
end
it "preserves unknown checksum algorithms" do
lockfile @lockfile.gsub(/(sha256=[a-f0-9]+)$/, "constant=true,\\1,xyz=123")