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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1613,6 +1613,39 @@ RSpec.describe "the lockfile format" do
expect(the_bundle).not_to include_gems "myrack_middleware 1.0" expect(the_bundle).not_to include_gems "myrack_middleware 1.0"
end 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 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_repo4 do
build_gem "other_dep", "0.9" build_gem "other_dep", "0.9"