From 5ba3464e1564170d0aaadb66fd44712495154141 Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Fri, 17 May 2024 14:45:53 -0600 Subject: [PATCH] fix syncing a gem update that changes platforms --- lib/bundler/multilock.rb | 5 ++--- lib/bundler/multilock/cache.rb | 20 ++++++++++++++++++-- lib/bundler/multilock/check.rb | 2 +- spec/bundler/multilock_spec.rb | 29 +++++++++++++++++++++++++++++ 4 files changed, 50 insertions(+), 6 deletions(-) diff --git a/lib/bundler/multilock.rb b/lib/bundler/multilock.rb index 40f689d..1b5ce8e 100644 --- a/lib/bundler/multilock.rb +++ b/lib/bundler/multilock.rb @@ -217,7 +217,7 @@ def after_install_all(install: true) end # add a source for the current gem - gem_spec = parent_specs[[File.basename(Bundler.root), "ruby"]] + gem_spec = parent_specs.dig(File.basename(Bundler.root), "ruby") if gem_spec adjusted_parent_lockfile_contents += <<~TEXT @@ -257,9 +257,8 @@ def after_install_all(install: true) # replace any duplicate specs with what's in the parent lockfile lockfile.specs.map! do |spec| - parent_spec = parent_specs[[spec.name, spec.platform]] + parent_spec = cache.find_matching_spec(parent_specs, spec) next spec unless parent_spec - next spec if check_precedence.call(spec, parent_spec) == :self dependency_changes ||= spec != parent_spec diff --git a/lib/bundler/multilock/cache.rb b/lib/bundler/multilock/cache.rb index cf8665b..0d3f856 100644 --- a/lib/bundler/multilock/cache.rb +++ b/lib/bundler/multilock/cache.rb @@ -57,11 +57,27 @@ def parser(lockfile_name) end def specs(lockfile_name) - @specs[lockfile_name] ||= parser(lockfile_name).specs.to_h do |spec| - [[spec.name, spec.platform], spec] + @specs[lockfile_name] ||= begin + specs = {} + parser(lockfile_name).specs.each do |spec| + (specs[spec.name] ||= {})[spec.platform] = spec + end + specs end end + # sometimes a gem changes platforms with a new version, such as from aarch64-linux + # to aarch64-linux-gnu. we need to still sync it + def find_matching_spec(specs, spec) + specs = self.specs(specs) unless specs.is_a?(Hash) + platform_specs = specs[spec.name] + return unless platform_specs + + parent_spec = platform_specs[spec.platform] + parent_spec ||= platform_specs.find { |platform, _| platform =~ spec.platform }&.last + parent_spec || platform_specs.find { |platform, _| platform == "ruby" }&.last + end + # @param lockfile_name [Pathname] # @return [Hash>] hash of gem name to set of gem names that depend on it def reverse_dependencies(lockfile_name) diff --git a/lib/bundler/multilock/check.rb b/lib/bundler/multilock/check.rb index 2bc7b86..0180556 100644 --- a/lib/bundler/multilock/check.rb +++ b/lib/bundler/multilock/check.rb @@ -120,7 +120,7 @@ def deep_check(lockfile_definition) # check for conflicting requirements (and build list of pins, in the same loop) parser.specs.each do |spec| - parent_spec = @cache.specs(parent_lockfile_name)[[spec.name, spec.platform]] + parent_spec = @cache.find_matching_spec(parent_lockfile_name, spec) if lockfile_definition[:enforce_pinned_additional_dependencies] # look through what this spec depends on, and keep track of all pinned requirements diff --git a/spec/bundler/multilock_spec.rb b/spec/bundler/multilock_spec.rb index 1d104b1..e60bfe4 100644 --- a/spec/bundler/multilock_spec.rb +++ b/spec/bundler/multilock_spec.rb @@ -863,6 +863,35 @@ end end + it "syncs gems whose platforms changed slightly" do + if RUBY_VERSION < "3.0" + skip "The test case that triggers this requires Ruby 3.0+; " \ + "just rely on this test running on other ruby versions" + end + + with_gemfile(<<~RUBY) do + gem "sqlite3", "~> 1.7" + + lockfile("all") {} + RUBY + invoke_bundler("install") + + write_gemfile(<<~RUBY) + gem "sqlite3" + + lockfile("all") {} + RUBY + invoke_bundler("install") + + expect(invoke_bundler("info sqlite3")).to include("1.7.3") + expect(invoke_bundler("info sqlite3", env: { "BUNDLE_LOCKFILE" => "all" })).to include("1.7.3") + + invoke_bundler("update sqlite3") + expect(invoke_bundler("info sqlite3")).not_to include("1.7.3") + expect(invoke_bundler("info sqlite3", env: { "BUNDLE_LOCKFILE" => "all" })).not_to include("1.7.3") + end + end + it "syncs ruby version" do with_gemfile(<<~RUBY) do gem "concurrent-ruby", "1.2.2"