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 parent spec
containing the missing dependency as incomplete.
  • Loading branch information
composerinteralia committed Feb 22, 2023
1 parent 451ac56 commit 486ecb8
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 20 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
8 changes: 6 additions & 2 deletions bundler/lib/bundler/spec_set.rb
Expand Up @@ -24,6 +24,7 @@ def for(dependencies, check = false, platforms = [nil])

name = dep[0].name
platform = dep[1]
incomplete = false

key = [name, platform]
next if handled.key?(key)
Expand All @@ -36,11 +37,14 @@ def for(dependencies, check = false, platforms = [nil])

specs_for_dep.first.dependencies.each do |d|
next if d.type == :development
incomplete = true if d.name != "bundler" && lookup[d.name].empty?
deps << [d, dep[1]]
end
elsif check
@incomplete_specs += lookup[name]
else
incomplete = true
end

@incomplete_specs += lookup[name] if incomplete && check
end

specs
Expand Down
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 486ecb8

Please sign in to comment.