Re-resolve when lockfile is invalid
Move the check for unmet dependencies in lockfile just in time to be able to re-resolve if unmet dependencies are found.
This commit is contained in:
parent
7e51cadc2e
commit
cbf2e133c1
@ -149,7 +149,7 @@ module Bundler
|
|||||||
@dependency_changes = converge_dependencies
|
@dependency_changes = converge_dependencies
|
||||||
@local_changes = converge_locals
|
@local_changes = converge_locals
|
||||||
|
|
||||||
@missing_lockfile_dep = check_missing_lockfile_dep
|
check_lockfile
|
||||||
end
|
end
|
||||||
|
|
||||||
def gem_version_promoter
|
def gem_version_promoter
|
||||||
@ -478,7 +478,7 @@ module Bundler
|
|||||||
private :sources
|
private :sources
|
||||||
|
|
||||||
def nothing_changed?
|
def nothing_changed?
|
||||||
!@source_changes && !@dependency_changes && !@new_platform && !@path_changes && !@local_changes && !@missing_lockfile_dep && !@unlocking_bundler
|
!@source_changes && !@dependency_changes && !@new_platform && !@path_changes && !@local_changes && !@missing_lockfile_dep && !@unlocking_bundler && !@invalid_lockfile_dep
|
||||||
end
|
end
|
||||||
|
|
||||||
def no_resolve_needed?
|
def no_resolve_needed?
|
||||||
@ -630,6 +630,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 lock file is missing \"#{@missing_lockfile_dep}\""],
|
[@missing_lockfile_dep, "your lock file 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"],
|
||||||
|
[@invalid_lockfile_dep, "your lock file has an invalid dependency \"#{@invalid_lockfile_dep}\""],
|
||||||
].select(&:first).map(&:last).join(", ")
|
].select(&:first).map(&:last).join(", ")
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -684,26 +685,40 @@ module Bundler
|
|||||||
!sources_with_changes.each {|source| @unlock[:sources] << source.name }.empty?
|
!sources_with_changes.each {|source| @unlock[:sources] << source.name }.empty?
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_missing_lockfile_dep
|
def check_lockfile
|
||||||
all_locked_specs = @locked_specs.map(&:name) << "bundler"
|
@invalid_lockfile_dep = nil
|
||||||
|
@missing_lockfile_dep = nil
|
||||||
|
|
||||||
missing = @locked_specs.select do |s|
|
locked_names = @locked_specs.map(&:name)
|
||||||
s.dependencies.any? {|dep| !all_locked_specs.include?(dep.name) }
|
missing = []
|
||||||
|
invalid = []
|
||||||
|
|
||||||
|
@locked_specs.each do |s|
|
||||||
|
s.dependencies.each do |dep|
|
||||||
|
next if dep.name == "bundler"
|
||||||
|
|
||||||
|
missing << s unless locked_names.include?(dep.name)
|
||||||
|
invalid << s if @locked_specs.none? {|spec| dep.matches_spec?(spec) }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if missing.any?
|
if missing.any?
|
||||||
@locked_specs.delete(missing)
|
@locked_specs.delete(missing)
|
||||||
|
|
||||||
return missing.first.name
|
@missing_lockfile_dep = missing.first.name
|
||||||
end
|
elsif !@dependency_changes
|
||||||
|
@missing_lockfile_dep = current_dependencies.find do |d|
|
||||||
return if @dependency_changes
|
|
||||||
|
|
||||||
current_dependencies.find do |d|
|
|
||||||
@locked_specs[d.name].empty? && d.name != "bundler"
|
@locked_specs[d.name].empty? && d.name != "bundler"
|
||||||
end&.name
|
end&.name
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if invalid.any?
|
||||||
|
@locked_specs.delete(invalid)
|
||||||
|
|
||||||
|
@invalid_lockfile_dep = invalid.first.name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def converge_paths
|
def converge_paths
|
||||||
sources.path_sources.any? do |source|
|
sources.path_sources.any? do |source|
|
||||||
specs_changed?(source)
|
specs_changed?(source)
|
||||||
|
@ -91,38 +91,12 @@ module Bundler
|
|||||||
install_serially
|
install_serially
|
||||||
end
|
end
|
||||||
|
|
||||||
check_for_unmet_dependencies
|
|
||||||
|
|
||||||
handle_error if failed_specs.any?
|
handle_error if failed_specs.any?
|
||||||
@specs
|
@specs
|
||||||
ensure
|
ensure
|
||||||
worker_pool&.stop
|
worker_pool&.stop
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_for_unmet_dependencies
|
|
||||||
unmet_dependencies = @specs.map do |s|
|
|
||||||
[
|
|
||||||
s,
|
|
||||||
s.dependencies.reject {|dep| @specs.any? {|spec| dep.matches_spec?(spec.spec) } },
|
|
||||||
]
|
|
||||||
end.reject {|a| a.last.empty? }
|
|
||||||
return if unmet_dependencies.empty?
|
|
||||||
|
|
||||||
warning = []
|
|
||||||
warning << "Your lockfile doesn't include a valid resolution."
|
|
||||||
warning << "You can fix this by regenerating your lockfile or manually editing the bad locked gems to a version that satisfies all dependencies."
|
|
||||||
warning << "The unmet dependencies are:"
|
|
||||||
|
|
||||||
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) }
|
|
||||||
warning << "* #{unmet_spec_dependency}, dependency of #{spec.full_name}, unsatisfied by #{found.full_name}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
Bundler.ui.warn(warning.join("\n"))
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def failed_specs
|
def failed_specs
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require "bundler/installer/parallel_installer"
|
|
||||||
|
|
||||||
RSpec.describe Bundler::ParallelInstaller do
|
|
||||||
let(:installer) { instance_double("Installer") }
|
|
||||||
let(:all_specs) { [] }
|
|
||||||
let(:size) { 1 }
|
|
||||||
let(:standalone) { false }
|
|
||||||
let(:force) { false }
|
|
||||||
|
|
||||||
subject { described_class.new(installer, all_specs, size, standalone, force) }
|
|
||||||
|
|
||||||
context "when the spec set is not a valid resolution" do
|
|
||||||
let(:all_specs) do
|
|
||||||
[
|
|
||||||
build_spec("cucumber", "4.1.0") {|s| s.runtime "diff-lcs", "< 1.4" },
|
|
||||||
build_spec("diff-lcs", "1.4.4"),
|
|
||||||
].flatten
|
|
||||||
end
|
|
||||||
|
|
||||||
it "prints a warning" do
|
|
||||||
expect(Bundler.ui).to receive(:warn).with(<<-W.strip)
|
|
||||||
Your lockfile doesn't include a valid resolution.
|
|
||||||
You can fix this by regenerating your lockfile or manually editing the bad locked gems to a version that satisfies all dependencies.
|
|
||||||
The unmet dependencies are:
|
|
||||||
* diff-lcs (< 1.4), dependency of cucumber-4.1.0, unsatisfied by diff-lcs-1.4.4
|
|
||||||
W
|
|
||||||
subject.check_for_unmet_dependencies
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when the spec set is a valid resolution" do
|
|
||||||
let(:all_specs) do
|
|
||||||
[
|
|
||||||
build_spec("cucumber", "4.1.0") {|s| s.runtime "diff-lcs", "< 1.4" },
|
|
||||||
build_spec("diff-lcs", "1.3"),
|
|
||||||
].flatten
|
|
||||||
end
|
|
||||||
|
|
||||||
it "doesn't print a warning" do
|
|
||||||
expect(Bundler.ui).not_to receive(:warn)
|
|
||||||
subject.check_for_unmet_dependencies
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
@ -1101,4 +1101,47 @@ RSpec.describe "bundle install with gem sources" do
|
|||||||
expect(err).to include("Could not find compatible versions")
|
expect(err).to include("Could not find compatible versions")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "when a lockfile has unmet dependencies, and the Gemfile has no resolution" do
|
||||||
|
before do
|
||||||
|
build_repo4 do
|
||||||
|
build_gem "aaa", "0.2.0" do |s|
|
||||||
|
s.add_dependency "zzz", "< 0.2.0"
|
||||||
|
end
|
||||||
|
|
||||||
|
build_gem "zzz", "0.2.0"
|
||||||
|
end
|
||||||
|
|
||||||
|
gemfile <<~G
|
||||||
|
source "#{file_uri_for(gem_repo4)}"
|
||||||
|
|
||||||
|
gem "aaa"
|
||||||
|
gem "zzz"
|
||||||
|
G
|
||||||
|
|
||||||
|
lockfile <<~L
|
||||||
|
GEM
|
||||||
|
remote: #{file_uri_for(gem_repo4)}/
|
||||||
|
specs:
|
||||||
|
aaa (0.2.0)
|
||||||
|
zzz (< 0.2.0)
|
||||||
|
zzz (0.2.0)
|
||||||
|
|
||||||
|
PLATFORMS
|
||||||
|
#{lockfile_platforms}
|
||||||
|
|
||||||
|
DEPENDENCIES
|
||||||
|
aaa!
|
||||||
|
zzz!
|
||||||
|
|
||||||
|
BUNDLED WITH
|
||||||
|
#{Bundler::VERSION}
|
||||||
|
L
|
||||||
|
end
|
||||||
|
|
||||||
|
it "does not install, but raises a resolution error" do
|
||||||
|
bundle "install", :raise_on_error => false
|
||||||
|
expect(err).to include("Could not find compatible versions")
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user