From 89ed2bc9ef9d89eddc21819613a754b14fc6bfdc Mon Sep 17 00:00:00 2001 From: Yasuo Honda Date: Thu, 14 May 2026 08:23:57 +0900 Subject: [PATCH] Skip git source exclusion when lockfile cannot backfill MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The git source exclusion in `find_source_requirements` introduced by ruby/rubygems#9234 relies on `locked_requirements` to backfill the gap for sources used only by --without groups. Without a Gemfile.lock — e.g. an initial `BUNDLE_ONLY=ci bundle install` where a default-group gem from a git source is shifted into the "excluded" set — that fallback is absent, and the source's indirect dependencies fall through to the default rubygems source, causing resolution to fail. Gate the exclusion on `nothing_changed?` so it only applies when the lockfile is guaranteed to cover the excluded sources. Fix ruby/rubygems#9536 Co-Authored-By: Claude Opus 4.7 (1M context) --- bundler/lib/bundler/definition.rb | 10 +++++-- spec/install/git_spec.rb | 48 +++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/bundler/lib/bundler/definition.rb b/bundler/lib/bundler/definition.rb index 6f5ab29b1556..da46319cd2a4 100644 --- a/bundler/lib/bundler/definition.rb +++ b/bundler/lib/bundler/definition.rb @@ -1210,16 +1210,20 @@ def excluded_git_sources def find_source_requirements preload_git_sources + # Only safe to exclude when locked_requirements (merged below) backfills the gap. + nothing_changed = nothing_changed? + excluded = nothing_changed ? excluded_git_sources : [] + # Record the specs available in each gem's source, so that those # specs will be available later when the resolver knows where to # look for that gemspec (or its dependencies) source_requirements = if precompute_source_requirements_for_indirect_dependencies? - all_requirements = source_map.all_requirements(excluded_git_sources) + all_requirements = source_map.all_requirements(excluded) { default: default_source }.merge(all_requirements) else - { default: Source::RubygemsAggregate.new(sources, source_map, excluded_git_sources) }.merge(source_map.direct_requirements) + { default: Source::RubygemsAggregate.new(sources, source_map, excluded) }.merge(source_map.direct_requirements) end - source_requirements.merge!(source_map.locked_requirements) if nothing_changed? + source_requirements.merge!(source_map.locked_requirements) if nothing_changed metadata_dependencies.each do |dep| source_requirements[dep.name] = sources.metadata_source end diff --git a/spec/install/git_spec.rb b/spec/install/git_spec.rb index 78e902cb73fa..1172d661ae69 100644 --- a/spec/install/git_spec.rb +++ b/spec/install/git_spec.rb @@ -317,5 +317,53 @@ expect(the_bundle).to include_gems("production_gem 1.0") expect(the_bundle).not_to include_gems("development_gem 1.0") end + + it "resolves indirect dependencies from a git source not in the requested groups" do + build_lib "activesupport", "1.0", path: lib_path("rails/activesupport") + build_git "activerecord", "1.0", path: lib_path("rails") do |s| + s.add_dependency "activesupport", "= 1.0" + end + + gemfile <<-G + source "https://gem.repo1" + + gem "activerecord", :git => "#{lib_path("rails")}" + + group :ci do + gem "myrack" + end + G + + bundle_config "only ci" + bundle :install + + expect(the_bundle).to include_gems("myrack 1.0.0") + expect(the_bundle).not_to include_gems("activerecord 1.0") + end + + it "resolves indirect dependencies from a git source not in the requested groups (without compact_index dependency API)" do + build_lib "activesupport", "1.0", path: lib_path("rails/activesupport") + build_git "activerecord", "1.0", path: lib_path("rails") do |s| + s.add_dependency "activesupport", "= 1.0" + end + + gemfile <<-G + source "https://gem.repo1" + + gem "activerecord", :git => "#{lib_path("rails")}" + + group :ci do + gem "myrack" + end + G + + # Force the RubygemsAggregate code path in find_source_requirements by + # making the dependency API unavailable. + bundle_config "only ci" + bundle :install, artifice: "endpoint_api_forbidden" + + expect(the_bundle).to include_gems("myrack 1.0.0") + expect(the_bundle).not_to include_gems("activerecord 1.0") + end end end