diff options
| author | Hiroshi SHIBATA <hsbt@ruby-lang.org> | 2026-05-08 11:56:53 +0900 |
|---|---|---|
| committer | git <svn-admin@ruby-lang.org> | 2026-05-08 06:43:48 +0000 |
| commit | 95ce1c5228a86fffdce7535a374008e7cbcd076c (patch) | |
| tree | 8bcd87d7fc0074463d623b42cc90877d4cc872cf | |
| parent | 5970638803840297c2daaca1635c9a98453d740c (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.rb | 16 | ||||
| -rw-r--r-- | spec/bundler/bundler/spec_set_spec.rb | 52 |
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 |
