Skip to content

bundler: use replaces_base credential for gemspec-only deps#14348

Merged
jeffwidman merged 2 commits intomainfrom
fix/bundler-replaces-base-gemspec-deps
Mar 3, 2026
Merged

bundler: use replaces_base credential for gemspec-only deps#14348
jeffwidman merged 2 commits intomainfrom
fix/bundler-replaces-base-gemspec-deps

Conversation

@jeffwidman
Copy link
Member

@jeffwidman jeffwidman commented Mar 3, 2026

Summary

When a replaces_base rubygems_server credential is configured, gems declared only in a gemspec (via add_runtime_dependency) were bypassing the private registry and querying rubygems.org directly during version checks. Gems declared in the Gemfile correctly routed through the private registry.

Root cause

Two code paths determine the registry URL for version checks, and neither checked for replaces_base? credentials:

  1. PackageDetailsFetcher#rubygems_versionsget_url_from_dependency() returns nil for gemspec deps (no :source in requirements), falling back to https://rubygems.org.
  2. LatestVersionFinder::DependencySource#dependency_rubygems_uri — hardcoded to https://rubygems.org.

Meanwhile, MetadataFinder#base_url in the bundler ecosystem already correctly handles replaces_base?, and other ecosystems (npm, python, maven) also check it in their version-checking code.

Fix

Both code paths now check for a replaces_base? rubygems_server credential before falling back to rubygems.org, using the same pattern as MetadataFinder:

credential = credentials.find { |cred| cred["type"] == "rubygems_server" && cred.replaces_base? }

Tests are added for both code paths covering:

  • replaces_base credential present → uses private registry
  • Explicit source in requirements → takes precedence over replaces_base
  • No replaces_base credential → falls back to rubygems.org
  • Non-replaces_base rubygems_server credential → falls back to rubygems.org

Design note on duplication

The core credential lookup (credentials.find { ... replaces_base? }) now appears in 3 places across the bundler ecosystem: MetadataFinder#base_url, PackageDetailsFetcher#replaces_base_registry_url, and DependencySource#replaces_base_host. Each caller shapes the result differently (full URL with trailing slash handling, URL without trailing slash, raw host string), and the 3 classes live in different inheritance hierarchies with no natural shared ancestor. This mirrors how other ecosystems (npm, maven) also inline this pattern rather than sharing it. If more callsites appear in the future, extracting a shared helper returning the raw host string would be a reasonable next step.

Fixes #14342

Copilot AI review requested due to automatic review settings March 3, 2026 08:14
@jeffwidman jeffwidman requested a review from a team as a code owner March 3, 2026 08:14
@github-actions github-actions bot added the L: ruby:bundler RubyGems via bundler label Mar 3, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adjusts Bundler’s RubyGems version-checking logic so that when a rubygems_server credential is configured with replaces-base: true, gemspec-only dependencies no longer fall back to querying rubygems.org, aligning behavior with Gemfile-declared dependencies and existing Bundler MetadataFinder behavior.

Changes:

  • Update LatestVersionFinder::DependencySource to build the versions API URL using a replaces-base RubyGems host when present.
  • Update PackageDetailsFetcher to prefer a replaces-base registry URL when dependency requirements do not specify a source.
  • Add/extend RSpec coverage for both code paths and the expected fallback/precedence behavior.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
bundler/lib/dependabot/bundler/update_checker/latest_version_finder/dependency_source.rb Uses replaces-base credential host when constructing the RubyGems versions endpoint URL.
bundler/lib/dependabot/bundler/package/package_details_fetcher.rb Falls back to replaces-base registry URL before rubygems.org when requirements don’t specify a source.
bundler/spec/dependabot/bundler/update_checker/latest_version_finder/dependency_source_spec.rb Adds specs for RubyGems versions URI behavior with/without replaces-base.
bundler/spec/dependabot/bundler/package/package_details_fetcher_spec.rb Adds specs ensuring replaces-base registry is used (and explicit source takes precedence).

When a replaces_base rubygems_server credential is configured, gems
declared only in a gemspec (via add_runtime_dependency) were bypassing
the private registry and querying rubygems.org directly during version
checks. Gems in the Gemfile correctly used the private registry.

This fixes both code paths that determine the registry URL:

1. PackageDetailsFetcher#rubygems_versions - now checks for a
   replaces_base credential before falling back to rubygems.org
2. LatestVersionFinder::DependencySource#dependency_rubygems_uri -
   same fix for the version finder's registry URL

Both follow the existing pattern already used by MetadataFinder.

Fixes #14342
Guard against nil or empty host values in replaces_base credential
lookups to avoid constructing invalid URLs like 'https://'.
@jeffwidman jeffwidman force-pushed the fix/bundler-replaces-base-gemspec-deps branch from cb319e7 to b254a56 Compare March 3, 2026 16:35
@jeffwidman jeffwidman merged commit cdaf413 into main Mar 3, 2026
93 checks passed
@jeffwidman jeffwidman deleted the fix/bundler-replaces-base-gemspec-deps branch March 3, 2026 17:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

L: ruby:bundler RubyGems via bundler

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bundler: replaces-base credential not used for gemspec-only dependencies during version checks

3 participants