Skip to content

Commit

Permalink
Make sure to force latest resolvable version explicitly
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
deivid-rodriguez committed Apr 26, 2024
1 parent 3b46bf3 commit c0a65f5
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 2 deletions.
26 changes: 24 additions & 2 deletions bundler/lib/bundler/definition.rb
Expand Up @@ -568,7 +568,9 @@ def resolution_packages
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

Expand Down Expand Up @@ -1015,7 +1017,7 @@ def lockfiles_equal?(current, proposed, preserve_unknown_sections)
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)
Expand All @@ -1024,6 +1026,26 @@ def additional_base_requirements_for_resolve(resolution_packages, last_resolve)
resolution_packages
end

def additional_base_requirements_to_force_updates(resolution_packages)
return resolution_packages if @explicit_unlocks.empty?

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
full_update = unlocked_definition.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 remove_invalid_platforms!(dependencies)
return if Bundler.frozen_bundle?

Expand Down
122 changes: 122 additions & 0 deletions bundler/spec/commands/lock_spec.rb
Expand Up @@ -252,6 +252,128 @@
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")

Expand Down

0 comments on commit c0a65f5

Please sign in to comment.