summaryrefslogtreecommitdiff
path: root/spec/bundler/install/gems/resolving_spec.rb
diff options
context:
space:
mode:
Diffstat (limited to 'spec/bundler/install/gems/resolving_spec.rb')
-rw-r--r--spec/bundler/install/gems/resolving_spec.rb429
1 files changed, 377 insertions, 52 deletions
diff --git a/spec/bundler/install/gems/resolving_spec.rb b/spec/bundler/install/gems/resolving_spec.rb
index 94fac0052c..c5f9c4a3d3 100644
--- a/spec/bundler/install/gems/resolving_spec.rb
+++ b/spec/bundler/install/gems/resolving_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe "bundle install with install-time dependencies" do
s.extensions << "Rakefile"
s.write "Rakefile", <<-RUBY
task :default do
- path = File.expand_path("../lib", __FILE__)
+ path = File.expand_path("lib", __dir__)
FileUtils.mkdir_p(path)
File.open("\#{path}/implicit_rake_dep.rb", "w") do |f|
f.puts "IMPLICIT_RAKE_DEP = 'YES'"
@@ -20,7 +20,7 @@ RSpec.describe "bundle install with install-time dependencies" do
s.extensions << "Rakefile"
s.write "Rakefile", <<-RUBY
task :default do
- path = File.expand_path("../lib", __FILE__)
+ path = File.expand_path("lib", __dir__)
FileUtils.mkdir_p(path)
File.open("\#{path}/another_implicit_rake_dep.rb", "w") do |f|
f.puts "ANOTHER_IMPLICIT_RAKE_DEP = 'YES'"
@@ -42,7 +42,7 @@ RSpec.describe "bundle install with install-time dependencies" do
s.extensions << "Rakefile"
s.write "Rakefile", <<-RUBY
task :default do
- path = File.expand_path("../lib", __FILE__)
+ path = File.expand_path("lib", __dir__)
FileUtils.mkdir_p(path)
File.open("\#{path}/net_build_extensions.rb", "w") do |f|
f.puts "NET_BUILD_EXTENSIONS = 'YES'"
@@ -101,16 +101,14 @@ RSpec.describe "bundle install with install-time dependencies" do
end
it "installs gems with a dependency with no type" do
- skip "incorrect data check error" if Gem.win_platform?
-
build_repo2
path = "#{gem_repo2}/#{Gem::MARSHAL_SPEC_DIR}/actionpack-2.3.2.gemspec.rz"
spec = Marshal.load(Bundler.rubygems.inflate(File.binread(path)))
spec.dependencies.each do |d|
- d.instance_variable_set(:@type, :fail)
+ d.instance_variable_set(:@type, "fail")
end
- File.open(path, "w") do |f|
+ File.open(path, "wb") do |f|
f.write Gem.deflate(Marshal.dump(spec))
end
@@ -133,7 +131,7 @@ RSpec.describe "bundle install with install-time dependencies" do
end
it "installs plugins depended on by other plugins" do
- install_gemfile <<-G, :env => { "DEBUG" => "1" }
+ install_gemfile <<-G, env: { "DEBUG" => "1" }
source "#{file_uri_for(gem_repo2)}"
gem "net_a"
G
@@ -142,7 +140,7 @@ RSpec.describe "bundle install with install-time dependencies" do
end
it "installs multiple levels of dependencies" do
- install_gemfile <<-G, :env => { "DEBUG" => "1" }
+ install_gemfile <<-G, env: { "DEBUG" => "1" }
source "#{file_uri_for(gem_repo2)}"
gem "net_c"
gem "net_e"
@@ -159,9 +157,9 @@ RSpec.describe "bundle install with install-time dependencies" do
gem "net_e"
G
- bundle :install, :env => { "BUNDLER_DEBUG_RESOLVER" => "1", "DEBUG" => "1" }
+ bundle :install, env: { "BUNDLER_DEBUG_RESOLVER" => "1", "DEBUG" => "1" }
- expect(out).to include("BUNDLER: Starting resolution")
+ expect(out).to include("Resolving dependencies...")
end
end
@@ -173,9 +171,9 @@ RSpec.describe "bundle install with install-time dependencies" do
gem "net_e"
G
- bundle :install, :env => { "DEBUG_RESOLVER" => "1", "DEBUG" => "1" }
+ bundle :install, env: { "DEBUG_RESOLVER" => "1", "DEBUG" => "1" }
- expect(out).to include("BUNDLER: Starting resolution")
+ expect(out).to include("Resolving dependencies...")
end
end
@@ -187,18 +185,12 @@ RSpec.describe "bundle install with install-time dependencies" do
gem "net_e"
G
- bundle :install, :env => { "DEBUG_RESOLVER_TREE" => "1", "DEBUG" => "1" }
-
- activated_groups = if local_platforms.any?
- "net_b (1.0) (ruby), net_b (1.0) (#{local_platforms.join(", ")})"
- else
- "net_b (1.0) (ruby)"
- end
+ bundle :install, env: { "DEBUG_RESOLVER_TREE" => "1", "DEBUG" => "1" }
expect(out).to include(" net_b").
- and include("BUNDLER: Starting resolution").
- and include("BUNDLER: Finished resolution").
- and include("Attempting to activate [#{activated_groups}]")
+ and include("Resolving dependencies...").
+ and include("Solution found after 1 attempts:").
+ and include("selected net_b 1.0")
end
end
end
@@ -216,16 +208,327 @@ RSpec.describe "bundle install with install-time dependencies" do
end
end
- install_gemfile <<-G, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
- ruby "#{RUBY_VERSION}"
+ install_gemfile <<-G, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
+ ruby "#{Gem.ruby_version}"
source "http://localgemserver.test/"
gem 'rack'
G
- expect(out).to_not include("rack-9001.0.0 requires ruby version > 9000")
+ expect(err).to_not include("rack-9001.0.0 requires ruby version > 9000")
expect(the_bundle).to include_gems("rack 1.2")
end
+ it "installs the older version when using servers not implementing the compact index API" do
+ build_repo2 do
+ build_gem "rack", "1.2" do |s|
+ s.executables = "rackup"
+ end
+
+ build_gem "rack", "9001.0.0" do |s|
+ s.required_ruby_version = "> 9000"
+ end
+ end
+
+ install_gemfile <<-G, artifice: "endpoint", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
+ ruby "#{Gem.ruby_version}"
+ source "http://localgemserver.test/"
+ gem 'rack'
+ G
+
+ expect(err).to_not include("rack-9001.0.0 requires ruby version > 9000")
+ expect(the_bundle).to include_gems("rack 1.2")
+ end
+
+ context "when there is a lockfile using the newer incompatible version" do
+ before do
+ build_repo2 do
+ build_gem "parallel_tests", "3.7.0" do |s|
+ s.required_ruby_version = ">= #{current_ruby_minor}"
+ end
+
+ build_gem "parallel_tests", "3.8.0" do |s|
+ s.required_ruby_version = ">= #{next_ruby_minor}"
+ end
+ end
+
+ gemfile <<-G
+ source "http://localgemserver.test/"
+ gem 'parallel_tests'
+ G
+
+ checksums = checksums_section do |c|
+ c.checksum gem_repo2, "parallel_tests", "3.8.0"
+ end
+
+ lockfile <<~L
+ GEM
+ remote: http://localgemserver.test/
+ specs:
+ parallel_tests (3.8.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ parallel_tests
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "automatically updates lockfile to use the older version" do
+ bundle "install --verbose", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
+
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo2, "parallel_tests", "3.7.0"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: http://localgemserver.test/
+ specs:
+ parallel_tests (3.7.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ parallel_tests
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "gives a meaningful error if we're in frozen mode" do
+ expect do
+ bundle "install --verbose", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s, "BUNDLE_FROZEN" => "true" }, raise_on_error: false
+ end.not_to change { lockfile }
+
+ expect(err).to include("parallel_tests-3.8.0 requires ruby version >= #{next_ruby_minor}")
+ expect(err).not_to include("That means the author of parallel_tests (3.8.0) has removed it.")
+ end
+ end
+
+ context "with transitive dependencies in a lockfile" do
+ before do
+ build_repo2 do
+ build_gem "rubocop", "1.28.2" do |s|
+ s.required_ruby_version = ">= #{current_ruby_minor}"
+
+ s.add_dependency "rubocop-ast", ">= 1.17.0", "< 2.0"
+ end
+
+ build_gem "rubocop", "1.35.0" do |s|
+ s.required_ruby_version = ">= #{next_ruby_minor}"
+
+ s.add_dependency "rubocop-ast", ">= 1.20.1", "< 2.0"
+ end
+
+ build_gem "rubocop-ast", "1.17.0" do |s|
+ s.required_ruby_version = ">= #{current_ruby_minor}"
+ end
+
+ build_gem "rubocop-ast", "1.21.0" do |s|
+ s.required_ruby_version = ">= #{next_ruby_minor}"
+ end
+ end
+
+ gemfile <<-G
+ source "http://localgemserver.test/"
+ gem 'rubocop'
+ G
+
+ checksums = checksums_section do |c|
+ c.checksum gem_repo2, "rubocop", "1.35.0"
+ c.checksum gem_repo2, "rubocop-ast", "1.21.0"
+ end
+
+ lockfile <<~L
+ GEM
+ remote: http://localgemserver.test/
+ specs:
+ rubocop (1.35.0)
+ rubocop-ast (>= 1.20.1, < 2.0)
+ rubocop-ast (1.21.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ parallel_tests
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "automatically updates lockfile to use the older compatible versions" do
+ bundle "install --verbose", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }
+
+ checksums = checksums_section_when_existing do |c|
+ c.checksum gem_repo2, "rubocop", "1.28.2"
+ c.checksum gem_repo2, "rubocop-ast", "1.17.0"
+ end
+
+ expect(lockfile).to eq <<~L
+ GEM
+ remote: http://localgemserver.test/
+ specs:
+ rubocop (1.28.2)
+ rubocop-ast (>= 1.17.0, < 2.0)
+ rubocop-ast (1.17.0)
+
+ PLATFORMS
+ #{lockfile_platforms}
+
+ DEPENDENCIES
+ rubocop
+ #{checksums}
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ context "with a Gemfile and lock file that don't resolve under the current platform" do
+ before do
+ build_repo4 do
+ build_gem "sorbet", "0.5.10554" do |s|
+ s.add_dependency "sorbet-static", "0.5.10554"
+ end
+
+ build_gem "sorbet-static", "0.5.10554" do |s|
+ s.platform = "universal-darwin-21"
+ end
+ end
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+ gem 'sorbet', '= 0.5.10554'
+ G
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ sorbet (0.5.10554)
+ sorbet-static (= 0.5.10554)
+ sorbet-static (0.5.10554-universal-darwin-21)
+
+ PLATFORMS
+ arm64-darwin-21
+
+ DEPENDENCIES
+ sorbet (= 0.5.10554)
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ it "raises a proper error" do
+ simulate_platform "aarch64-linux" do
+ bundle "install", raise_on_error: false
+ end
+
+ nice_error = <<~E.strip
+ Could not find gems matching 'sorbet-static (= 0.5.10554)' valid for all resolution platforms (arm64-darwin-21, aarch64-linux) in rubygems repository #{file_uri_for(gem_repo4)}/, cached gems or installed locally.
+
+ The source contains the following gems matching 'sorbet-static (= 0.5.10554)':
+ * sorbet-static-0.5.10554-universal-darwin-21
+ E
+ expect(err).to end_with(nice_error)
+ end
+ end
+
+ context "when adding a new gem that does not resolve under all locked platforms" do
+ before do
+ simulate_platform "x86_64-linux" do
+ build_repo4 do
+ build_gem "nokogiri", "1.14.0" do |s|
+ s.platform = "x86_64-linux"
+ end
+ build_gem "nokogiri", "1.14.0" do |s|
+ s.platform = "arm-linux"
+ end
+
+ build_gem "sorbet-static", "0.5.10696" do |s|
+ s.platform = "x86_64-linux"
+ end
+ end
+
+ lockfile <<~L
+ GEM
+ remote: #{file_uri_for(gem_repo4)}/
+ specs:
+ nokogiri (1.14.0-arm-linux)
+ nokogiri (1.14.0-x86_64-linux)
+
+ PLATFORMS
+ arm-linux
+ x86_64-linux
+
+ DEPENDENCIES
+ nokogiri
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+
+ gemfile <<~G
+ source "#{file_uri_for(gem_repo4)}"
+
+ gem "nokogiri"
+ gem "sorbet-static"
+ G
+
+ bundle "lock", raise_on_error: false
+ end
+ end
+
+ it "raises a proper error" do
+ nice_error = <<~E.strip
+ Could not find gems matching 'sorbet-static' valid for all resolution platforms (arm-linux, x86_64-linux) in rubygems repository #{file_uri_for(gem_repo4)}/, cached gems or installed locally.
+
+ The source contains the following gems matching 'sorbet-static':
+ * sorbet-static-0.5.10696-x86_64-linux
+ E
+ expect(err).to end_with(nice_error)
+ end
+ end
+
+ it "gives a meaningful error on ruby version mismatches between dependencies" do
+ build_repo4 do
+ build_gem "requires-old-ruby" do |s|
+ s.required_ruby_version = "< #{Gem.ruby_version}"
+ end
+ end
+
+ build_lib("foo", path: bundled_app) do |s|
+ s.required_ruby_version = ">= #{Gem.ruby_version}"
+
+ s.add_dependency "requires-old-ruby"
+ end
+
+ install_gemfile <<-G, raise_on_error: false
+ source "#{file_uri_for(gem_repo4)}"
+ gemspec
+ G
+
+ expect(err).to end_with <<~E.strip
+ Could not find compatible versions
+
+ Because every version of foo depends on requires-old-ruby >= 0
+ and every version of requires-old-ruby depends on Ruby < #{Gem.ruby_version},
+ every version of foo requires Ruby < #{Gem.ruby_version}.
+ So, because Gemfile depends on foo >= 0
+ and current Ruby version is = #{Gem.ruby_version},
+ version solving has failed.
+ E
+ end
+
it "installs the older version under rate limiting conditions" do
build_repo4 do
build_gem "rack", "9001.0.0" do |s|
@@ -235,14 +538,14 @@ RSpec.describe "bundle install with install-time dependencies" do
build_gem "foo1", "1.0"
end
- install_gemfile <<-G, :artifice => "compact_index_rate_limited", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
- ruby "#{RUBY_VERSION}"
+ install_gemfile <<-G, artifice: "compact_index_rate_limited", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ ruby "#{Gem.ruby_version}"
source "http://localgemserver.test/"
gem 'rack'
gem 'foo1'
G
- expect(out).to_not include("rack-9001.0.0 requires ruby version > 9000")
+ expect(err).to_not include("rack-9001.0.0 requires ruby version > 9000")
expect(the_bundle).to include_gems("rack 1.2")
end
@@ -252,22 +555,22 @@ RSpec.describe "bundle install with install-time dependencies" do
s.required_ruby_version = "> 9000"
end
build_gem "rack", "1.2" do |s|
- s.platform = mingw
+ s.platform = x86_mingw32
s.required_ruby_version = "> 9000"
end
build_gem "rack", "1.2"
end
- simulate_platform mingw do
- install_gemfile <<-G, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
- ruby "#{RUBY_VERSION}"
+ simulate_platform x86_mingw32 do
+ install_gemfile <<-G, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }
+ ruby "#{Gem.ruby_version}"
source "http://localgemserver.test/"
gem 'rack'
G
end
- expect(out).to_not include("rack-9001.0.0 requires ruby version > 9000")
- expect(out).to_not include("rack-1.2-#{Bundler.local_platform} requires ruby version > 9000")
+ expect(err).to_not include("rack-9001.0.0 requires ruby version > 9000")
+ expect(err).to_not include("rack-1.2-#{Bundler.local_platform} requires ruby version > 9000")
expect(the_bundle).to include_gems("rack 1.2")
end
end
@@ -281,14 +584,32 @@ RSpec.describe "bundle install with install-time dependencies" do
end
end
- let(:ruby_requirement) { %("#{RUBY_VERSION}") }
- let(:error_message_requirement) { "~> #{RUBY_VERSION}.0" }
+ let(:ruby_requirement) { %("#{Gem.ruby_version}") }
+ let(:error_message_requirement) { "= #{Gem.ruby_version}" }
+
+ it "raises a proper error that mentions the current Ruby version during resolution" do
+ install_gemfile <<-G, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }, raise_on_error: false
+ source "http://localgemserver.test/"
+ gem 'require_ruby'
+ G
+
+ expect(out).to_not include("Gem::InstallError: require_ruby requires Ruby version > 9000")
+
+ nice_error = <<~E.strip
+ Could not find compatible versions
+
+ Because every version of require_ruby depends on Ruby > 9000
+ and Gemfile depends on require_ruby >= 0,
+ Ruby > 9000 is required.
+ So, because current Ruby version is #{error_message_requirement},
+ version solving has failed.
+ E
+ expect(err).to end_with(nice_error)
+ end
shared_examples_for "ruby version conflicts" do
it "raises an error during resolution" do
- skip "ruby requirement includes platform and it shouldn't" if Gem.win_platform?
-
- install_gemfile <<-G, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }, :raise_on_error => false
+ install_gemfile <<-G, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }, raise_on_error: false
source "http://localgemserver.test/"
ruby #{ruby_requirement}
gem 'require_ruby'
@@ -296,15 +617,14 @@ RSpec.describe "bundle install with install-time dependencies" do
expect(out).to_not include("Gem::InstallError: require_ruby requires Ruby version > 9000")
- nice_error = strip_whitespace(<<-E).strip
- Bundler found conflicting requirements for the Ruby\0 version:
- In Gemfile:
- Ruby\0 (#{error_message_requirement})
-
- require_ruby was resolved to 1.0, which depends on
- Ruby\0 (> 9000)
+ nice_error = <<~E.strip
+ Could not find compatible versions
- Ruby\0 (> 9000), which is required by gem 'require_ruby', is not available in the local ruby installation
+ Because every version of require_ruby depends on Ruby > 9000
+ and Gemfile depends on require_ruby >= 0,
+ Ruby > 9000 is required.
+ So, because current Ruby version is #{error_message_requirement},
+ version solving has failed.
E
expect(err).to end_with(nice_error)
end
@@ -314,7 +634,6 @@ RSpec.describe "bundle install with install-time dependencies" do
describe "with a < requirement" do
let(:ruby_requirement) { %("< 5000") }
- let(:error_message_requirement) { "< 5000" }
it_behaves_like "ruby version conflicts"
end
@@ -322,7 +641,6 @@ RSpec.describe "bundle install with install-time dependencies" do
describe "with a compound requirement" do
let(:reqs) { ["> 0.1", "< 5000"] }
let(:ruby_requirement) { reqs.map(&:dump).join(", ") }
- let(:error_message_requirement) { Gem::Requirement.new(reqs).to_s }
it_behaves_like "ruby version conflicts"
end
@@ -337,13 +655,20 @@ RSpec.describe "bundle install with install-time dependencies" do
end
end
- install_gemfile <<-G, :raise_on_error => false
+ install_gemfile <<-G, raise_on_error: false
source "#{file_uri_for(gem_repo2)}"
gem 'require_rubygems'
G
expect(err).to_not include("Gem::InstallError: require_rubygems requires RubyGems version > 9000")
- expect(err).to include("require_rubygems-1.0 requires rubygems version > 9000, which is incompatible with the current version, #{Gem::VERSION}")
+ nice_error = <<~E.strip
+ Because every version of require_rubygems depends on RubyGems > 9000
+ and Gemfile depends on require_rubygems >= 0,
+ RubyGems > 9000 is required.
+ So, because current RubyGems version is = #{Gem::VERSION},
+ version solving has failed.
+ E
+ expect(err).to end_with(nice_error)
end
end
end