Skip to content

Skip git source exclusion when lockfile cannot backfill#9544

Merged
hsbt merged 1 commit into
ruby:masterfrom
yahonda:fix-gh-9536
May 14, 2026
Merged

Skip git source exclusion when lockfile cannot backfill#9544
hsbt merged 1 commit into
ruby:masterfrom
yahonda:fix-gh-9536

Conversation

@yahonda
Copy link
Copy Markdown
Contributor

@yahonda yahonda commented May 14, 2026

What was the end-user or developer problem that led to this PR?

#9536: starting with Bundler 4.0.7, BUNDLE_ONLY=<group> bundle install fails with "Could not find compatible versions" when the Gemfile has a default-group gem fetched from a git source and no Gemfile.lock is present. Reproducer from the issue:

# Gemfile
source "https://rubygems.org"
gem "activerecord", git: "https://github.com/rails/rails.git", branch: "main"
group :ci do
  gem "rubocop"
end
$ rm -f Gemfile.lock
$ BUNDLE_ONLY=ci bundle install
Resolving dependencies...
Could not find compatible versions

Because every version of activerecord depends on activesupport = 8.2.0.alpha
  and activesupport = 8.2.0.alpha could not be found in rubygems repository https://rubygems.org/ or installed locally,
  activerecord cannot be used.
So, because Gemfile depends on activerecord >= 0,
  version solving has failed.

Bundler 4.0.6 succeeds; 4.0.7 — 4.0.11 fail. The regression is between those tags.

What is your fix for the problem, implemented in this PR?

Diagnosis

The regression was introduced by #9234, which added needed_git_sources / excluded_git_sources and threaded excluded_git_sources into Bundler::Definition#find_source_requirements:

source_requirements = if precompute_source_requirements_for_indirect_dependencies?
  all_requirements = source_map.all_requirements(excluded_git_sources)
  { default: default_source }.merge(all_requirements)
else
  { default: Source::RubygemsAggregate.new(sources, source_map, excluded_git_sources) }.merge(source_map.direct_requirements)
end
source_requirements.merge!(source_map.locked_requirements) if nothing_changed?

With BUNDLE_ONLY=ci:

  • requested_groups = [:ci]
  • dependencies_for([:ci]) only contains rubocop (a rubygems source)
  • needed_git_sources returns [], so the rails-git source (used by the default-group activerecord) is treated as excluded
  • source_map.all_requirements then skips that source when filling indirect-dependency mappings
  • The indirect dep activesupport (a sibling spec in the rails git tree) is not mapped to rails-git, so the resolver looks for it in the default rubygems source, doesn't find = 8.2.0.alpha, and gives up

The #9234 spec only passed because it pre-created a complete lockfile; with nothing_changed? true, source_map.locked_requirements (merged a few lines down) backfilled the gap. Without a Gemfile.lock that fallback is absent.

Fix

Gate the exclusion on nothing_changed? so it only applies when the lockfile is guaranteed to cover the excluded sources:

nothing_changed = nothing_changed?
excluded = nothing_changed ? excluded_git_sources : []

The needed_git_sources filter in preload_git_sources is left in place, so #9234's parallel-preload optimization for --without-only git sources is preserved.

Alternatives considered

End-to-end verification

Verified on Fedora with the exact reproducer from #9536, using Gemfile without Gemfile.lock:

Bundler BUNDLE_ONLY=ci bundle install
4.0.6 ✅ Bundle complete!
4.0.7 — 4.0.11 (current master) ❌ Could not find compatible versions
this PR ✅ Bundle complete!

Make sure the following tasks are checked

Two regression specs cover both branches of find_source_requirements:

  • source_map.all_requirements(excluded) (compact_index dependency API, the default path)
  • Source::RubygemsAggregate.new(sources, source_map, excluded) (via the endpoint_api_forbidden artifice)

The existing #9234 spec ("works if you exclude a group with a git gem") is unchanged and still passes.

@yahonda yahonda changed the title [Bundler] Skip git source exclusion when lockfile cannot backfill Skip git source exclusion when lockfile cannot backfill May 14, 2026
Comment thread spec/install/git_spec.rb Outdated
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", ruby: ">= 3.3" do
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this Ruby 3.3 guard is unnecessary for this and next examples. Can you try to remove them?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review. Removed ruby: ">= 3.3" guard from both specs.

The git source exclusion in `find_source_requirements` introduced by
ruby#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#9536

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@hsbt hsbt merged commit 41e5474 into ruby:master May 14, 2026
96 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BUNDLE_ONLY=<name> bundle install raises "Could not find compatible versions"

2 participants