[rubygems/rubygems] Fix crash when lockfile is missing dependencies

We have a check for a corrupt lockfile right before installing. However,
the check accounted for locked specs not satisfying locked dependencies,
but not for locked specs missing for some locked dependencies.

Instead of fixing this check, I decided to remove it in favor of
automatically detecting the situation and re-resolve to automatically
fix the lockfile rather than printing a warning but leave the problem
there.

4a7a584252
This commit is contained in:
David Rodríguez 2022-12-15 12:14:40 +01:00 committed by Hiroshi SHIBATA
parent 16b36a5b0c
commit 3fd33590f6
Notes: git 2022-12-20 04:15:36 +00:00
4 changed files with 65 additions and 66 deletions

View file

@ -144,6 +144,8 @@ module Bundler
@dependency_changes = converge_dependencies
@local_changes = converge_locals
@incomplete_lockfile = check_missing_lockfile_specs
end
def gem_version_promoter
@ -458,7 +460,7 @@ module Bundler
private :sources
def nothing_changed?
!@source_changes && !@dependency_changes && !@new_platform && !@path_changes && !@local_changes
!@source_changes && !@dependency_changes && !@new_platform && !@path_changes && !@local_changes && !@incomplete_lockfile
end
def unlocking?
@ -603,6 +605,7 @@ module Bundler
[@new_platform, "you added a new platform to your gemfile"],
[@path_changes, "the gemspecs for path gems changed"],
[@local_changes, "the gemspecs for git local gems changed"],
[@incomplete_lockfile, "your lock file is missing some gems"],
].select(&:first).map(&:last).join(", ")
end
@ -657,6 +660,14 @@ module Bundler
!sources_with_changes.each {|source| @unlock[:sources] << source.name }.empty?
end
def check_missing_lockfile_specs
all_locked_specs = @locked_specs.map(&:name) << "bundler"
@locked_specs.any? do |s|
s.dependencies.any? {|dep| !all_locked_specs.include?(dep.name) }
end
end
def converge_paths
sources.path_sources.any? do |source|
specs_changed?(source)

View file

@ -53,10 +53,6 @@ module Bundler
@dependencies ||= all_dependencies.reject {|dep| ignorable_dependency? dep }
end
def missing_lockfile_dependencies(all_spec_names)
dependencies.reject {|dep| all_spec_names.include? dep.name }
end
# Represents all dependencies
def all_dependencies
@spec.dependencies
@ -84,8 +80,6 @@ module Bundler
end
def call
check_for_corrupt_lockfile
if @rake
do_install(@rake, 0)
Gem::Specification.reset
@ -128,31 +122,6 @@ module Bundler
Bundler.ui.warn(warning.join("\n"))
end
def check_for_corrupt_lockfile
missing_dependencies = @specs.map do |s|
[
s,
s.missing_lockfile_dependencies(@specs.map(&:name)),
]
end.reject {|a| a.last.empty? }
return if missing_dependencies.empty?
warning = []
warning << "Your lockfile was created by an old Bundler that left some things out."
if @size != 1
warning << "Because of the missing DEPENDENCIES, we can only install gems one at a time, instead of installing #{@size} at a time."
@size = 1
end
warning << "You can fix this by adding the missing gems to your Gemfile, running bundle install, and then removing the gems from your Gemfile."
warning << "The missing gems are:"
missing_dependencies.each do |spec, missing|
warning << "* #{missing.map(&:name).join(", ")} depended upon by #{spec.name}"
end
Bundler.ui.warn(warning.join("\n"))
end
private
def failed_specs

View file

@ -11,40 +11,6 @@ RSpec.describe Bundler::ParallelInstaller do
subject { described_class.new(installer, all_specs, size, standalone, force) }
context "when dependencies that are not on the overall installation list are the only ones not installed" do
let(:all_specs) do
[
build_spec("alpha", "1.0") {|s| s.runtime "a", "1" },
].flatten
end
it "prints a warning" do
expect(Bundler.ui).to receive(:warn).with(<<-W.strip)
Your lockfile was created by an old Bundler that left some things out.
You can fix this by adding the missing gems to your Gemfile, running bundle install, and then removing the gems from your Gemfile.
The missing gems are:
* a depended upon by alpha
W
subject.check_for_corrupt_lockfile
end
context "when size > 1" do
let(:size) { 500 }
it "prints a warning and sets size to 1" do
expect(Bundler.ui).to receive(:warn).with(<<-W.strip)
Your lockfile was created by an old Bundler that left some things out.
Because of the missing DEPENDENCIES, we can only install gems one at a time, instead of installing 500 at a time.
You can fix this by adding the missing gems to your Gemfile, running bundle install, and then removing the gems from your Gemfile.
The missing gems are:
* a depended upon by alpha
W
subject.check_for_corrupt_lockfile
expect(subject.size).to eq(1)
end
end
end
context "when the spec set is not a valid resolution" do
let(:all_specs) do
[

View file

@ -1221,6 +1221,59 @@ RSpec.describe "the lockfile format" do
and include("Either installing with `--full-index` or running `bundle update rack_middleware` should fix the problem.")
end
it "auto-heals when the lockfile is missing specs" do
build_repo4 do
build_gem "minitest-bisect", "1.6.0" do |s|
s.add_dependency "path_expander", "~> 1.1"
end
build_gem "path_expander", "1.1.1"
end
gemfile <<~G
source "#{file_uri_for(gem_repo4)}"
gem "minitest-bisect"
G
lockfile <<~L
GEM
remote: #{file_uri_for(gem_repo4)}/
specs:
minitest-bisect (1.6.0)
path_expander (~> 1.1)
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
minitest-bisect
BUNDLED WITH
#{Bundler::VERSION}
L
bundle "install --verbose"
expect(out).to include("re-resolving dependencies because your lock file is missing some gems")
expect(lockfile).to eq <<~L
GEM
remote: #{file_uri_for(gem_repo4)}/
specs:
minitest-bisect (1.6.0)
path_expander (~> 1.1)
path_expander (1.1.1)
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
minitest-bisect
BUNDLED WITH
#{Bundler::VERSION}
L
end
describe "a line ending" do
def set_lockfile_mtime_to_known_value
time = Time.local(2000, 1, 1, 0, 0, 0)