[rubygems/rubygems] Raise an error in frozen mode if CHECKSUMS entries are missing

https://github.com/rubygems/rubygems/commit/054a0cd76c
This commit is contained in:
David Rodríguez 2025-03-12 19:19:13 +01:00 committed by Hiroshi SHIBATA
parent 60d00da083
commit 55930987e1
Notes: git 2025-03-24 04:25:24 +00:00
7 changed files with 90 additions and 24 deletions

View File

@ -201,9 +201,11 @@ module Bundler
end
end
def register(spec, checksum)
return unless checksum
def missing?(spec)
@store[spec.lock_name].nil?
end
def register(spec, checksum)
register_checksum(spec.lock_name, checksum)
end
@ -218,7 +220,7 @@ module Bundler
def to_lock(spec)
lock_name = spec.lock_name
checksums = @store[lock_name]
if checksums
if checksums&.any?
"#{lock_name} #{checksums.values.map(&:to_lock).sort.join(",")}"
else
lock_name
@ -229,11 +231,15 @@ module Bundler
def register_checksum(lock_name, checksum)
@store_mutex.synchronize do
existing = fetch_checksum(lock_name, checksum.algo)
if existing
merge_checksum(lock_name, checksum, existing)
if checksum
existing = fetch_checksum(lock_name, checksum.algo)
if existing
merge_checksum(lock_name, checksum, existing)
else
store_checksum(lock_name, checksum)
end
else
store_checksum(lock_name, checksum)
init_checksum(lock_name)
end
end
end
@ -243,7 +249,11 @@ module Bundler
end
def store_checksum(lock_name, checksum)
(@store[lock_name] ||= {})[checksum.algo] = checksum
init_checksum(lock_name)[checksum.algo] = checksum
end
def init_checksum(lock_name)
@store[lock_name] ||= {}
end
def fetch_checksum(lock_name, algo)

View File

@ -147,8 +147,8 @@ module Bundler
@current_platform_missing = add_current_platform unless Bundler.frozen_bundle?
@path_changes = converge_paths
@source_changes = converge_sources
@path_changes = converge_paths
if conservative
@gems_to_unlock = @explicit_unlocks.any? ? @explicit_unlocks : @dependencies.map(&:name)
@ -563,6 +563,7 @@ module Bundler
@local_changes ||
@missing_lockfile_dep ||
@unlocking_bundler ||
@locked_spec_with_missing_checksums ||
@locked_spec_with_missing_deps ||
@locked_spec_with_invalid_deps
end
@ -815,6 +816,7 @@ module Bundler
[@local_changes, "the gemspecs for git local gems changed"],
[@missing_lockfile_dep, "your lockfile is missing \"#{@missing_lockfile_dep}\""],
[@unlocking_bundler, "an update to the version of Bundler itself was requested"],
[@locked_spec_with_missing_checksums, "your lockfile is missing a CHECKSUMS entry for \"#{@locked_spec_with_missing_checksums}\""],
[@locked_spec_with_missing_deps, "your lockfile includes \"#{@locked_spec_with_missing_deps}\" but not some of its dependencies"],
[@locked_spec_with_invalid_deps, "your lockfile does not satisfy dependencies of \"#{@locked_spec_with_invalid_deps}\""],
].select(&:first).map(&:last).join(", ")
@ -873,21 +875,27 @@ module Bundler
def check_lockfile
@locked_spec_with_invalid_deps = nil
@locked_spec_with_missing_deps = nil
@locked_spec_with_missing_checksums = nil
missing = []
missing_deps = []
missing_checksums = []
invalid = []
@locked_specs.each do |s|
missing_checksums << s if @locked_checksums && s.source.checksum_store.missing?(s)
validation = @locked_specs.validate_deps(s)
missing << s if validation == :missing
missing_deps << s if validation == :missing
invalid << s if validation == :invalid
end
if missing.any?
@locked_specs.delete(missing)
@locked_spec_with_missing_checksums = missing_checksums.first.name if missing_checksums.any?
@locked_spec_with_missing_deps = missing.first.name
if missing_deps.any?
@locked_specs.delete(missing_deps)
@locked_spec_with_missing_deps = missing_deps.first.name
end
if invalid.any?

View File

@ -239,7 +239,6 @@ module Bundler
spaces = $1
return unless spaces.size == 2
checksums = $6
return unless checksums
name = $2
version = $3
platform = $4
@ -249,10 +248,14 @@ module Bundler
full_name = Gem::NameTuple.new(name, version, platform).full_name
return unless spec = @specs[full_name]
checksums.split(",") do |lock_checksum|
column = line.index(lock_checksum) + 1
checksum = Checksum.from_lock(lock_checksum, "#{@lockfile_path}:#{@pos.line}:#{column}")
spec.source.checksum_store.register(spec, checksum)
if checksums
checksums.split(",") do |lock_checksum|
column = line.index(lock_checksum) + 1
checksum = Checksum.from_lock(lock_checksum, "#{@lockfile_path}:#{@pos.line}:#{column}")
spec.source.checksum_store.register(spec, checksum)
end
else
spec.source.checksum_store.register(spec, nil)
end
end

View File

@ -4,6 +4,7 @@ module Bundler
class Source
class Gemspec < Path
attr_reader :gemspec
attr_writer :checksum_store
def initialize(options)
super

View File

@ -183,9 +183,7 @@ module Bundler
end
path = @path_sources.map do |source|
next source if source.is_a?(Source::Gemspec)
replace_source(replacement_sources, source)
replace_path_source(replacement_sources, source)
end
[rubygems, path, git, plugin]
@ -201,6 +199,8 @@ module Bundler
replacement_source.remotes = gemfile_source.remotes
yield replacement_source if block_given?
replacement_source
end
end
@ -208,11 +208,22 @@ module Bundler
replacement_source = replacement_sources.find {|s| s == gemfile_source }
return gemfile_source unless replacement_source
yield replacement_source if block_given?
replacement_source = yield(replacement_source) if block_given?
replacement_source
end
def replace_path_source(replacement_sources, gemfile_source)
replace_source(replacement_sources, gemfile_source) do |replacement_source|
if gemfile_source.is_a?(Source::Gemspec)
gemfile_source.checksum_store = replacement_source.checksum_store
gemfile_source
else
replacement_source
end
end
end
def different_sources?(lock_sources, replacement_sources)
!equivalent_sources?(lock_sources, replacement_sources)
end

View File

@ -1221,7 +1221,7 @@ RSpec.describe "bundle install with gems on multiple sources" do
DEPENDENCIES
myrack!
#{checksums_section}
BUNDLED WITH
#{Bundler::VERSION}
L

View File

@ -1613,6 +1613,39 @@ RSpec.describe "the lockfile format" do
expect(the_bundle).not_to include_gems "myrack_middleware 1.0"
end
it "raises a clear error when frozen mode is set and lockfile is missing entries in CHECKSUMS section, and does not install any gems" do
lockfile <<-L
GEM
remote: https://gem.repo2/
specs:
myrack_middleware (1.0)
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
myrack_middleware
CHECKSUMS
BUNDLED WITH
#{Bundler::VERSION}
L
install_gemfile <<-G, env: { "BUNDLE_FROZEN" => "true" }, raise_on_error: false
source "https://gem.repo2"
gem "myrack_middleware"
G
expect(err).to eq <<~L.strip
Your lockfile is missing a checksums entry for \"myrack_middleware\", but can't be updated because frozen mode is set
Run `bundle install` elsewhere and add the updated Gemfile.lock to version control.
L
expect(the_bundle).not_to include_gems "myrack_middleware 1.0"
end
it "automatically fixes the lockfile when it's missing deps, they conflict with other locked deps, but conflicts are fixable" do
build_repo4 do
build_gem "other_dep", "0.9"