Skip to content

Commit

Permalink
Auto-heal on corrupted lockfile with missing deps
Browse files Browse the repository at this point in the history
Following up on rubygems#6355, which
turned a crash into a nicer error message, this commit auto-heals the
corrupt lockfile instead.

In this particular case (a corrupt Gemfile.lock with missing
dependencies) the LazySpecification will not have accurate dependency
information, we have to materialize the SpecSet to determine there are
missing dependencies. We've already got a way to handle this, via
`SpecSet#incomplete_specs`, but it wasn't quite working for this case
because we'd get to `@incomplete_specs += lookup[name]` and
`lookup[name]` would be empty for the dependency.

With this commit we catch it a bit earlier, marking the spec containing
the missing dependency as incomplete.
  • Loading branch information
composerinteralia committed Feb 22, 2023
1 parent 75dac08 commit 0234ac7
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 19 deletions.
15 changes: 1 addition & 14 deletions bundler/lib/bundler/installer/parallel_installer.rb
Expand Up @@ -47,13 +47,6 @@ def dependencies_installed?(all_specs)
dependencies.all? {|d| installed_specs.include? d.name }
end

# Check whether spec's dependencies are missing, which can indicate a
# corrupted lockfile
def dependencies_missing?(all_specs)
spec_names = all_specs.map(&:name)
dependencies.any? {|d| !spec_names.include? d.name }
end

# Represents only the non-development dependencies, the ones that are
# itself and are in the total list.
def dependencies
Expand Down Expand Up @@ -123,11 +116,7 @@ def check_for_unmet_dependencies
unmet_dependencies.each do |spec, unmet_spec_dependencies|
unmet_spec_dependencies.each do |unmet_spec_dependency|
found = @specs.find {|s| s.name == unmet_spec_dependency.name && !unmet_spec_dependency.matches_spec?(s.spec) }
if found
warning << "* #{unmet_spec_dependency}, dependency of #{spec.full_name}, unsatisfied by #{found.full_name}"
else
warning << "* #{unmet_spec_dependency}, dependency of #{spec.full_name} but missing from lockfile"
end
warning << "* #{unmet_spec_dependency}, dependency of #{spec.full_name}, unsatisfied by #{found.full_name}"
end
end

Expand Down Expand Up @@ -224,8 +213,6 @@ def enqueue_specs
if spec.dependencies_installed? @specs
spec.state = :enqueued
worker_pool.enq spec
elsif spec.dependencies_missing? @specs
spec.state = :failed
end
end
end
Expand Down
6 changes: 5 additions & 1 deletion bundler/lib/bundler/spec_set.rb
Expand Up @@ -31,7 +31,7 @@ def for(dependencies, check = false, platforms = [nil])
handled[key] = true

specs_for_dep = specs_for_dependency(*dep)
if specs_for_dep.any?
if specs_for_dep.any? && dependencies_available?(specs_for_dep.first)
specs.concat(specs_for_dep)

specs_for_dep.first.dependencies.each do |d|
Expand Down Expand Up @@ -202,5 +202,9 @@ def tsort_each_child(s)
lookup[d.name].each {|s2| yield s2 }
end
end

def dependencies_available?(spec)
spec.dependencies.all? {|d| d.type == :development || d.name == "bundler" || lookup[d.name].any? }
end
end
end
23 changes: 19 additions & 4 deletions bundler/spec/lock/lockfile_spec.rb
Expand Up @@ -1221,7 +1221,7 @@
and include("Either installing with `--full-index` or running `bundle update rack_middleware` should fix the problem.")
end

it "errors gracefully on a corrupt lockfile" do
it "auto-heals when the lockfile is missing dependent specs" do
build_repo4 do
build_gem "minitest-bisect", "1.6.0" do |s|
s.add_dependency "path_expander", "~> 1.1"
Expand Down Expand Up @@ -1253,10 +1253,25 @@
L

cache_gems "minitest-bisect-1.6.0", "path_expander-1.1.1", :gem_repo => gem_repo4
bundle :install, :raise_on_error => false
bundle :install

expect(err).not_to include("ERROR REPORT TEMPLATE")
expect(err).to include("path_expander (~> 1.1), dependency of minitest-bisect-1.6.0 but missing from lockfile")
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

it "auto-heals when the lockfile is missing specs" do
Expand Down

0 comments on commit 0234ac7

Please sign in to comment.