[rubygems/rubygems] Automatically fix lockfile when the API reveals missing deps
https://github.com/rubygems/rubygems/commit/92196ccfdb
This commit is contained in:
parent
a4938e5bc0
commit
54f6b787e3
@ -587,7 +587,13 @@ module Bundler
|
||||
end
|
||||
|
||||
def materialize(dependencies)
|
||||
specs = resolve.materialize(dependencies)
|
||||
specs = begin
|
||||
resolve.materialize(dependencies)
|
||||
rescue IncorrectLockfileDependencies => e
|
||||
reresolve_without([e.spec])
|
||||
retry
|
||||
end
|
||||
|
||||
missing_specs = specs.missing_specs
|
||||
|
||||
if missing_specs.any?
|
||||
@ -613,8 +619,7 @@ module Bundler
|
||||
|
||||
Bundler.ui.debug("The lockfile does not have all gems needed for the current platform though, Bundler will still re-resolve dependencies")
|
||||
sources.remote!
|
||||
resolution_packages.delete(incomplete_specs)
|
||||
@resolve = start_resolution
|
||||
reresolve_without(incomplete_specs)
|
||||
specs = resolve.materialize(dependencies)
|
||||
|
||||
still_incomplete_specs = specs.incomplete_specs
|
||||
@ -633,6 +638,11 @@ module Bundler
|
||||
specs
|
||||
end
|
||||
|
||||
def reresolve_without(incomplete_specs)
|
||||
resolution_packages.delete(incomplete_specs)
|
||||
@resolve = start_resolution
|
||||
end
|
||||
|
||||
def start_resolution
|
||||
local_platform_needed_for_resolvability = @most_specific_non_local_locked_ruby_platform && !@platforms.include?(local_platform)
|
||||
@platforms << local_platform if local_platform_needed_for_resolvability
|
||||
|
@ -246,4 +246,14 @@ module Bundler
|
||||
end
|
||||
|
||||
class InvalidArgumentError < BundlerError; status_code(40); end
|
||||
|
||||
class IncorrectLockfileDependencies < BundlerError
|
||||
attr_reader :spec
|
||||
|
||||
def initialize(spec)
|
||||
@spec = spec
|
||||
end
|
||||
|
||||
status_code(41)
|
||||
end
|
||||
end
|
||||
|
@ -136,8 +136,12 @@ module Bundler
|
||||
end
|
||||
if search.nil? && fallback_to_non_installable
|
||||
search = candidates.last
|
||||
elsif search && search.full_name == full_name && (search.is_a?(RemoteSpecification) || search.instance_of?(EndpointSpecification))
|
||||
search.dependencies = dependencies
|
||||
elsif search && search.full_name == full_name
|
||||
if search.is_a?(StubSpecification)
|
||||
search.dependencies = dependencies
|
||||
elsif !source.is_a?(Source::Path) && search.runtime_dependencies.sort != dependencies.sort
|
||||
raise IncorrectLockfileDependencies.new(self)
|
||||
end
|
||||
end
|
||||
search
|
||||
end
|
||||
|
@ -1546,7 +1546,7 @@ RSpec.describe "the lockfile format" do
|
||||
G
|
||||
end
|
||||
|
||||
it "raises a helpful error message when the lockfile is missing deps" do
|
||||
it "automatically fixes the lockfile when it's missing deps" do
|
||||
lockfile <<-L
|
||||
GEM
|
||||
remote: https://gem.repo2/
|
||||
@ -1558,15 +1558,203 @@ RSpec.describe "the lockfile format" do
|
||||
|
||||
DEPENDENCIES
|
||||
myrack_middleware
|
||||
|
||||
BUNDLED WITH
|
||||
#{Bundler::VERSION}
|
||||
L
|
||||
|
||||
install_gemfile <<-G, raise_on_error: false
|
||||
install_gemfile <<-G
|
||||
source "https://gem.repo2"
|
||||
gem "myrack_middleware"
|
||||
G
|
||||
|
||||
expect(err).to include("Downloading myrack_middleware-1.0 revealed dependencies not in the API or the lockfile (#{Gem::Dependency.new("myrack", "= 0.9.1")}).").
|
||||
and include("Running `bundle update myrack_middleware` should fix the problem.")
|
||||
expect(lockfile).to eq <<~L
|
||||
GEM
|
||||
remote: https://gem.repo2/
|
||||
specs:
|
||||
myrack (0.9.1)
|
||||
myrack_middleware (1.0)
|
||||
myrack (= 0.9.1)
|
||||
|
||||
PLATFORMS
|
||||
#{lockfile_platforms}
|
||||
|
||||
DEPENDENCIES
|
||||
myrack_middleware
|
||||
|
||||
BUNDLED WITH
|
||||
#{Bundler::VERSION}
|
||||
L
|
||||
end
|
||||
|
||||
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_gem "other_dep", "0.9"
|
||||
build_gem "other_dep", "1.0"
|
||||
|
||||
build_gem "myrack", "0.9.1"
|
||||
|
||||
build_gem "myrack_middleware", "1.0" do |s|
|
||||
s.add_dependency "myrack", "= 0.9.1"
|
||||
s.add_dependency "other_dep", "= 0.9"
|
||||
end
|
||||
end
|
||||
|
||||
lockfile <<~L
|
||||
GEM
|
||||
remote: https://gem.repo4/
|
||||
specs:
|
||||
myrack_middleware (1.0)
|
||||
other_dep (1.0)
|
||||
|
||||
PLATFORMS
|
||||
#{lockfile_platforms}
|
||||
|
||||
DEPENDENCIES
|
||||
myrack_middleware
|
||||
other_dep
|
||||
|
||||
BUNDLED WITH
|
||||
#{Bundler::VERSION}
|
||||
L
|
||||
|
||||
install_gemfile <<-G
|
||||
source "https://gem.repo4"
|
||||
gem "myrack_middleware"
|
||||
gem "other_dep"
|
||||
G
|
||||
|
||||
expect(lockfile).to eq <<~L
|
||||
GEM
|
||||
remote: https://gem.repo4/
|
||||
specs:
|
||||
myrack (0.9.1)
|
||||
myrack_middleware (1.0)
|
||||
myrack (= 0.9.1)
|
||||
other_dep (= 0.9)
|
||||
other_dep (0.9)
|
||||
|
||||
PLATFORMS
|
||||
#{lockfile_platforms}
|
||||
|
||||
DEPENDENCIES
|
||||
myrack_middleware
|
||||
other_dep
|
||||
|
||||
BUNDLED WITH
|
||||
#{Bundler::VERSION}
|
||||
L
|
||||
end
|
||||
|
||||
it "automatically fixes the lockfile when it's missing multiple deps, they conflict with other locked deps, but conflicts are fixable" do
|
||||
build_repo4 do
|
||||
build_gem "other_dep", "0.9"
|
||||
build_gem "other_dep", "1.0"
|
||||
|
||||
build_gem "myrack", "0.9.1"
|
||||
|
||||
build_gem "myrack_middleware", "1.0" do |s|
|
||||
s.add_dependency "myrack", "= 0.9.1"
|
||||
end
|
||||
|
||||
build_gem "another_dep_middleware", "1.0" do |s|
|
||||
s.add_dependency "other_dep", "= 0.9"
|
||||
end
|
||||
end
|
||||
|
||||
lockfile <<~L
|
||||
GEM
|
||||
remote: https://gem.repo4/
|
||||
specs:
|
||||
myrack_middleware (1.0)
|
||||
another_dep_middleware (1.0)
|
||||
|
||||
PLATFORMS
|
||||
#{lockfile_platforms}
|
||||
|
||||
DEPENDENCIES
|
||||
myrack_middleware
|
||||
another_dep_middleware
|
||||
|
||||
BUNDLED WITH
|
||||
#{Bundler::VERSION}
|
||||
L
|
||||
|
||||
install_gemfile <<-G
|
||||
source "https://gem.repo4"
|
||||
gem "myrack_middleware"
|
||||
gem "another_dep_middleware"
|
||||
G
|
||||
|
||||
expect(lockfile).to eq <<~L
|
||||
GEM
|
||||
remote: https://gem.repo4/
|
||||
specs:
|
||||
another_dep_middleware (1.0)
|
||||
other_dep (= 0.9)
|
||||
myrack (0.9.1)
|
||||
myrack_middleware (1.0)
|
||||
myrack (= 0.9.1)
|
||||
other_dep (0.9)
|
||||
|
||||
PLATFORMS
|
||||
#{lockfile_platforms}
|
||||
|
||||
DEPENDENCIES
|
||||
another_dep_middleware
|
||||
myrack_middleware
|
||||
|
||||
BUNDLED WITH
|
||||
#{Bundler::VERSION}
|
||||
L
|
||||
end
|
||||
|
||||
it "raises a resolution error when lockfile is missing deps, they conflict with other locked deps, and conflicts are not fixable" do
|
||||
build_repo4 do
|
||||
build_gem "other_dep", "0.9"
|
||||
build_gem "other_dep", "1.0"
|
||||
|
||||
build_gem "myrack", "0.9.1"
|
||||
|
||||
build_gem "myrack_middleware", "1.0" do |s|
|
||||
s.add_dependency "myrack", "= 0.9.1"
|
||||
s.add_dependency "other_dep", "= 0.9"
|
||||
end
|
||||
end
|
||||
|
||||
lockfile <<~L
|
||||
GEM
|
||||
remote: https://gem.repo4/
|
||||
specs:
|
||||
myrack_middleware (1.0)
|
||||
other_dep (1.0)
|
||||
|
||||
PLATFORMS
|
||||
#{lockfile_platforms}
|
||||
|
||||
DEPENDENCIES
|
||||
myrack_middleware
|
||||
other_dep
|
||||
|
||||
BUNDLED WITH
|
||||
#{Bundler::VERSION}
|
||||
L
|
||||
|
||||
install_gemfile <<-G, raise_on_error: false
|
||||
source "https://gem.repo4"
|
||||
gem "myrack_middleware"
|
||||
gem "other_dep", "1.0"
|
||||
G
|
||||
|
||||
expect(err).to eq <<~ERROR.strip
|
||||
Could not find compatible versions
|
||||
|
||||
Because every version of myrack_middleware depends on other_dep = 0.9
|
||||
and Gemfile depends on myrack_middleware >= 0,
|
||||
other_dep = 0.9 is required.
|
||||
So, because Gemfile depends on other_dep = 1.0,
|
||||
version solving has failed.
|
||||
ERROR
|
||||
end
|
||||
|
||||
it "regenerates a lockfile with no specs" do
|
||||
@ -1620,6 +1808,47 @@ RSpec.describe "the lockfile format" do
|
||||
G
|
||||
end
|
||||
|
||||
it "automatically fixes the lockfile when it's missing deps and the full index is in use" do
|
||||
lockfile <<-L
|
||||
GEM
|
||||
remote: https://gem.repo2/
|
||||
specs:
|
||||
myrack_middleware (1.0)
|
||||
|
||||
PLATFORMS
|
||||
#{lockfile_platforms}
|
||||
|
||||
DEPENDENCIES
|
||||
myrack_middleware
|
||||
|
||||
BUNDLED WITH
|
||||
#{Bundler::VERSION}
|
||||
L
|
||||
|
||||
install_gemfile <<-G
|
||||
source "#{file_uri_for(gem_repo2)}"
|
||||
gem "myrack_middleware"
|
||||
G
|
||||
|
||||
expect(lockfile).to eq <<~L
|
||||
GEM
|
||||
remote: #{file_uri_for(gem_repo2)}/
|
||||
specs:
|
||||
myrack (0.9.1)
|
||||
myrack_middleware (1.0)
|
||||
myrack (= 0.9.1)
|
||||
|
||||
PLATFORMS
|
||||
#{lockfile_platforms}
|
||||
|
||||
DEPENDENCIES
|
||||
myrack_middleware
|
||||
|
||||
BUNDLED WITH
|
||||
#{Bundler::VERSION}
|
||||
L
|
||||
end
|
||||
|
||||
shared_examples_for "a lockfile missing dependent specs" do
|
||||
it "auto-heals" do
|
||||
build_repo4 do
|
||||
|
Loading…
x
Reference in New Issue
Block a user