[rubygems/rubygems] Add checksum of gems hosted on private servers:

- ### Problem

  Running `bundle lock --add-checksums` doesn't add the checksum of
  gems hosted on server that don't implement the compact index API.

  This result in a lockfile which is unusable in production as
  some checksums will be missing and Bundler raising an error.
  Users can work around this problem by running:

  `BUNDLE_LOCKFILE_CHECKSUMS=true bundle install --force`

  But this means redownloading and installing all gems which isn't
  great and slow on large apps.

  ### Context

  Bundler uses the Compact Index API to get the checksum of gems,
  but most private gem servers don't implement the compact index API
  (such as cloudsmith or packagecloud). This results in a soft failure
  on bundler side, and bundler leaving out blank checksum for those
  gems.

  ### Solution

  For gems that are hosted on private servers that don't send back
  the checksum of the gem, I'd like to fallback to the
  `bundle install` mechanism, which don't rely on an external API but
  instead compute the checksum of the package installed on disk.

  This patch goes through the spec that didn't return a checksum,
  and compute one if the package exists on disk.
  This solution makes the  `bundle lock --add-checksums` command
  actually usable in real world scenarios while keeping the
  `bundle lock` command fast enough.

https://github.com/rubygems/rubygems/commit/8e9abb5472
This commit is contained in:
Edouard CHIN 2025-10-09 17:23:43 +02:00 committed by git
parent 51b2c5a4cd
commit dce202d6d6
2 changed files with 103 additions and 1 deletions

View File

@ -540,7 +540,18 @@ module Bundler
setup_domain!(add_checksums: true)
specs # force materialization to real specifications, so that checksums are fetched
# force materialization to real specifications, so that checksums are fetched
specs.each do |spec|
next unless spec.source.is_a?(Bundler::Source::Rubygems)
# Checksum was fetched from the compact index API.
next if !spec.source.checksum_store.missing?(spec) && !spec.source.checksum_store.empty?(spec)
# The gem isn't installed, can't compute the checksum.
next unless spec.loaded_from
package = Gem::Package.new(spec.source.cached_built_in_gem(spec))
checksum = Checksum.from_gem_package(package)
spec.source.checksum_store.register(spec, checksum)
end
end
private

View File

@ -2174,6 +2174,97 @@ RSpec.describe "bundle lock" do
L
end
it "add checksums for gems installed on disk" do
build_repo4 do
build_gem "warning", "18.0.0"
end
bundle "config lockfile_checksums false"
simulate_platform "x86_64-linux" do
install_gemfile(<<-G, artifice: "endpoint")
source "https://gem.repo4"
gem "warning"
G
bundle "config --delete lockfile_checksums"
bundle("lock --add-checksums", artifice: "endpoint")
end
checksums = checksums_section do |c|
c.checksum gem_repo4, "warning", "18.0.0"
end
expect(lockfile).to eq <<~L
GEM
remote: https://gem.repo4/
specs:
warning (18.0.0)
PLATFORMS
ruby
x86_64-linux
DEPENDENCIES
warning
#{checksums}
BUNDLED WITH
#{Bundler::VERSION}
L
end
it "doesn't add checksum for gems not installed on disk" do
lockfile(<<~L)
GEM
remote: https://gem.repo4/
specs:
warning (18.0.0)
PLATFORMS
#{local_platform}
DEPENDENCIES
warning
BUNDLED WITH
#{Bundler::VERSION}
L
gemfile(<<~G)
source "https://gem.repo4"
gem "warning"
G
build_repo4 do
build_gem "warning", "18.0.0"
end
FileUtils.rm_rf("#{gem_repo4}/gems")
bundle("lock --add-checksums", artifice: "endpoint")
expect(lockfile).to eq <<~L
GEM
remote: https://gem.repo4/
specs:
warning (18.0.0)
PLATFORMS
#{local_platform}
DEPENDENCIES
warning
CHECKSUMS
warning (18.0.0)
BUNDLED WITH
#{Bundler::VERSION}
L
end
context "when re-resolving to include prereleases" do
before do
build_repo4 do