From 140d8318dbafe2c7c112651389da0b3ef4fd8424 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Rodr=C3=ADguez?= Date: Thu, 4 Jul 2024 18:22:03 +0200 Subject: [PATCH] [rubygems/rubygems] Fix generic platform gems getting incorrectly removed when locked for a specific platform If they are already in the lockfile as the most specific variant for a platform, we shouldn't change that unless explicitly unlocking. https://github.com/rubygems/rubygems/commit/a901660498 --- lib/bundler/gem_helpers.rb | 7 +- lib/bundler/resolver.rb | 2 +- lib/bundler/resolver/package.rb | 4 + .../install/gemfile/specific_platform_spec.rb | 96 +++++++++++++++++++ 4 files changed, 107 insertions(+), 2 deletions(-) diff --git a/lib/bundler/gem_helpers.rb b/lib/bundler/gem_helpers.rb index c27ceb360a..70ccceb491 100644 --- a/lib/bundler/gem_helpers.rb +++ b/lib/bundler/gem_helpers.rb @@ -46,13 +46,18 @@ module Bundler end module_function :platform_specificity_match - def select_best_platform_match(specs, platform, force_ruby: false) + def select_best_platform_match(specs, platform, force_ruby: false, prefer_locked: false) matching = if force_ruby specs.select {|spec| spec.match_platform(Gem::Platform::RUBY) && spec.force_ruby_platform! } else specs.select {|spec| spec.match_platform(platform) } end + if prefer_locked + locked_originally = matching.select {|spec| spec.is_a?(LazySpecification) } + return locked_originally if locked_originally.any? + end + sort_best_platform_match(matching, platform) end module_function :select_best_platform_match diff --git a/lib/bundler/resolver.rb b/lib/bundler/resolver.rb index b640eaf81b..2be7af94a2 100644 --- a/lib/bundler/resolver.rb +++ b/lib/bundler/resolver.rb @@ -255,7 +255,7 @@ module Bundler results = filter_matching_specs(results, locked_requirement) if locked_requirement results.group_by(&:version).reduce([]) do |groups, (version, specs)| - platform_specs = package.platforms.map {|platform| select_best_platform_match(specs, platform) } + platform_specs = package.platform_specs(specs) # If package is a top-level dependency, # candidate is only valid if there are matching versions for all resolution platforms. diff --git a/lib/bundler/resolver/package.rb b/lib/bundler/resolver/package.rb index 0461328683..16c43d05af 100644 --- a/lib/bundler/resolver/package.rb +++ b/lib/bundler/resolver/package.rb @@ -25,6 +25,10 @@ module Bundler @prerelease = @dependency.prerelease? || @locked_version&.prerelease? || prerelease ? :consider_first : :ignore end + def platform_specs(specs) + platforms.map {|platform| GemHelpers.select_best_platform_match(specs, platform, prefer_locked: !unlock?) } + end + def to_s @name.delete("\0") end diff --git a/spec/bundler/install/gemfile/specific_platform_spec.rb b/spec/bundler/install/gemfile/specific_platform_spec.rb index 9d4d5b6e8f..ffeedad197 100644 --- a/spec/bundler/install/gemfile/specific_platform_spec.rb +++ b/spec/bundler/install/gemfile/specific_platform_spec.rb @@ -1521,6 +1521,102 @@ RSpec.describe "bundle install with specific platforms" do end end + it "does not remove generic platform gems locked for a specific platform from lockfile when unlocking an unrelated gem" do + build_repo4 do + build_gem "ffi" + + build_gem "ffi" do |s| + s.platform = "x86_64-linux" + end + + build_gem "nokogiri" + end + + gemfile <<~G + source "https://gem.repo4" + + gem "ffi" + gem "nokogiri" + G + + original_lockfile = <<~L + GEM + remote: https://gem.repo4/ + specs: + ffi (1.0) + nokogiri (1.0) + + PLATFORMS + x86_64-linux + + DEPENDENCIES + ffi + nokogiri + + BUNDLED WITH + #{Bundler::VERSION} + L + + lockfile original_lockfile + + simulate_platform "x86_64-linux" do + bundle "lock --update nokogiri" + + expect(lockfile).to eq(original_lockfile) + end + end + + it "does not remove generic platform gems locked for a specific platform from lockfile when unlocking an unrelated gem, and variants for other platform also locked" do + build_repo4 do + build_gem "ffi" + + build_gem "ffi" do |s| + s.platform = "x86_64-linux" + end + + build_gem "ffi" do |s| + s.platform = "java" + end + + build_gem "nokogiri" + end + + gemfile <<~G + source "https://gem.repo4" + + gem "ffi" + gem "nokogiri" + G + + original_lockfile = <<~L + GEM + remote: https://gem.repo4/ + specs: + ffi (1.0) + ffi (1.0-java) + nokogiri (1.0) + + PLATFORMS + java + x86_64-linux + + DEPENDENCIES + ffi + nokogiri + + BUNDLED WITH + #{Bundler::VERSION} + L + + lockfile original_lockfile + + simulate_platform "x86_64-linux" do + bundle "lock --update nokogiri" + + expect(lockfile).to eq(original_lockfile) + end + end + private def setup_multiplatform_gem