summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHiroshi SHIBATA <hsbt@ruby-lang.org>2026-05-08 11:56:53 +0900
committergit <svn-admin@ruby-lang.org>2026-05-08 06:43:48 +0000
commit95ce1c5228a86fffdce7535a374008e7cbcd076c (patch)
tree8bcd87d7fc0074463d623b42cc90877d4cc872cf
parent5970638803840297c2daaca1635c9a98453d740c (diff)
[ruby/rubygems] Honor LazySpec overrides in SpecSet#complete_platform
complete_platform validates platform-specific candidates returned by spec.source.specs.search, which are remote specs that do not carry the override list. Borrow the override list from the LazySpec exemplar already in scope so platform-variant validation uses the same effective metadata as the install/resolve path. Also propagate the overrides onto the synthesized LazySpec built from platform_spec. Without this, the next complete_platform call could pick the synthesized variant as its exemplar (it is now in the set returned by lookup) and fall back to strict matching, dropping platforms that the user's override would otherwise allow. https://github.com/ruby/rubygems/commit/205955c5b3 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
-rw-r--r--lib/bundler/spec_set.rb16
-rw-r--r--spec/bundler/bundler/spec_set_spec.rb52
2 files changed, 66 insertions, 2 deletions
diff --git a/lib/bundler/spec_set.rb b/lib/bundler/spec_set.rb
index e8d990d207..ae5e5cbaa9 100644
--- a/lib/bundler/spec_set.rb
+++ b/lib/bundler/spec_set.rb
@@ -274,13 +274,25 @@ module Bundler
valid_platform = lookup.all? do |_, specs|
spec = specs.first
+ # The matching candidates returned by source.specs.search are remote
+ # specs that do not carry the override list themselves. Borrow it from
+ # the LazySpec we are validating so platform-variant validation honors
+ # the same overrides the install/resolve path already applies.
+ overrides = spec.is_a?(LazySpecification) ? Array(spec.overrides) : []
matching_specs = spec.source.specs.search([spec.name, spec.version])
platform_spec = MatchPlatform.select_best_platform_match(matching_specs, platform).find do |s|
- valid?(s)
+ s.matches_current_metadata_with_overrides?(overrides) && valid_dependencies?(s)
end
if platform_spec
- new_specs << LazySpecification.from_spec(platform_spec) unless specs.include?(platform_spec)
+ unless specs.include?(platform_spec)
+ new_lazy = LazySpecification.from_spec(platform_spec)
+ # Carry the overrides forward so a follow-up complete_platform
+ # call that picks this synthesized variant as its exemplar still
+ # honors the user's override list.
+ new_lazy.overrides = overrides if overrides.any?
+ new_specs << new_lazy
+ end
true
else
false
diff --git a/spec/bundler/bundler/spec_set_spec.rb b/spec/bundler/bundler/spec_set_spec.rb
index 3e630555e6..1e1ceadf26 100644
--- a/spec/bundler/bundler/spec_set_spec.rb
+++ b/spec/bundler/bundler/spec_set_spec.rb
@@ -93,4 +93,56 @@ RSpec.describe Bundler::SpecSet do
end
end
+ describe "#complete_platform" do
+ let(:platform) { Gem::Platform.new("x86_64-linux") }
+
+ let(:platform_variant) do
+ build_spec("needs_old_ruby", "1.0", platform).first.tap do |s|
+ s.required_ruby_version = Gem::Requirement.new("< #{Gem.ruby_version}")
+ end
+ end
+
+ let(:lazy_spec) do
+ lazy = Bundler::LazySpecification.new("needs_old_ruby", Gem::Version.new("1.0"), Gem::Platform::RUBY)
+ lazy.required_ruby_version = Gem::Requirement.new("< #{Gem.ruby_version}")
+ source = double("source")
+ source_specs = double("source_specs")
+ allow(source).to receive(:specs).and_return(source_specs)
+ allow(source_specs).to receive(:search).
+ with(["needs_old_ruby", Gem::Version.new("1.0")]).and_return([platform_variant])
+ lazy.source = source
+ lazy
+ end
+
+ it "rejects a platform variant whose strict metadata is incompatible when no override is attached" do
+ set = described_class.new([lazy_spec])
+ expect(set.send(:complete_platform, platform)).to be(false)
+ end
+
+ it "accepts a platform variant when the LazySpec carries an override that allows it" do
+ lazy_spec.overrides = [Bundler::Override.new("needs_old_ruby", :required_ruby_version, :ignore_upper)]
+ set = described_class.new([lazy_spec])
+ expect(set.send(:complete_platform, platform)).to be(true)
+ end
+
+ it "carries overrides onto a synthesized LazySpec so a follow-up complete_platform still honors them" do
+ override = Bundler::Override.new("needs_old_ruby", :required_ruby_version, :ignore_upper)
+ lazy_spec.overrides = [override]
+ second_platform = Gem::Platform.new("aarch64-linux")
+ second_variant = build_spec("needs_old_ruby", "1.0", second_platform).first.tap do |s|
+ s.required_ruby_version = Gem::Requirement.new("< #{Gem.ruby_version}")
+ end
+ allow(lazy_spec.source.specs).to receive(:search).
+ with(["needs_old_ruby", Gem::Version.new("1.0")]).and_return([platform_variant, second_variant])
+
+ set = described_class.new([lazy_spec])
+ expect(set.send(:complete_platform, platform)).to be(true)
+ # The synthesized x86_64-linux variant is now in the set. If lookup
+ # picks it as exemplar for the next platform check, the override list
+ # must still be reachable via its overrides accessor.
+ synthesized = set.to_a.find {|s| s.platform == platform }
+ expect(synthesized.overrides).to eq([override])
+ expect(set.send(:complete_platform, second_platform)).to be(true)
+ end
+ end
end