diff options
Diffstat (limited to 'spec/bundler/install')
34 files changed, 4088 insertions, 1441 deletions
diff --git a/spec/bundler/install/allow_offline_install_spec.rb b/spec/bundler/install/allow_offline_install_spec.rb index 524363fde5..8da94718e0 100644 --- a/spec/bundler/install/allow_offline_install_spec.rb +++ b/spec/bundler/install/allow_offline_install_spec.rb @@ -7,7 +7,7 @@ RSpec.describe "bundle install with :allow_offline_install" do context "with no cached data locally" do it "still installs" do - install_gemfile <<-G, :artifice => "compact_index" + install_gemfile <<-G, artifice: "compact_index" source "http://testgemserver.local" gem "rack-obama" G @@ -15,7 +15,7 @@ RSpec.describe "bundle install with :allow_offline_install" do end it "still fails when the network is down" do - install_gemfile <<-G, :artifice => "fail", :raise_on_error => false + install_gemfile <<-G, artifice: "fail", raise_on_error: false source "http://testgemserver.local" gem "rack-obama" G @@ -26,10 +26,10 @@ RSpec.describe "bundle install with :allow_offline_install" do context "with cached data locally" do it "will install from the compact index" do - system_gems ["rack-1.0.0"], :path => default_bundle_path + system_gems ["rack-1.0.0"], path: default_bundle_path bundle "config set clean false" - install_gemfile <<-G, :artifice => "compact_index" + install_gemfile <<-G, artifice: "compact_index" source "http://testgemserver.local" gem "rack-obama" gem "rack", "< 1.0" @@ -42,7 +42,7 @@ RSpec.describe "bundle install with :allow_offline_install" do gem "rack-obama" G - bundle :update, :artifice => "fail", :all => true + bundle :update, artifice: "fail", all: true expect(last_command.stdboth).to include "Using the cached data for the new index because of a network error" expect(the_bundle).to include_gems("rack-obama 1.0", "rack 1.0.0") @@ -51,9 +51,12 @@ RSpec.describe "bundle install with :allow_offline_install" do def break_git_remote_ops! FileUtils.mkdir_p(tmp("broken_path")) File.open(tmp("broken_path/git"), "w", 0o755) do |f| - f.puts strip_whitespace(<<-RUBY) + f.puts <<~RUBY #!/usr/bin/env ruby - if %w(fetch --force --quiet --tags refs/heads/*:refs/heads/*).-(ARGV).empty? || %w(clone --bare --no-hardlinks --quiet).-(ARGV).empty? + fetch_args = %w(fetch --force --quiet) + clone_args = %w(clone --bare --no-hardlinks --quiet) + + if (fetch_args.-(ARGV).empty? || clone_args.-(ARGV).empty?) && ARGV.any? {|arg| arg.start_with?("file://") } warn "git remote ops have been disabled" exit 1 end @@ -72,14 +75,14 @@ RSpec.describe "bundle install with :allow_offline_install" do it "will install from a cached git repo" do skip "doesn't print errors" if Gem.win_platform? - git = build_git "a", "1.0.0", :path => lib_path("a") - update_git("a", :path => git.path, :branch => "new_branch") + git = build_git "a", "1.0.0", path: lib_path("a") + update_git("a", path: git.path, branch: "new_branch") install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" gem "a", :git => #{git.path.to_s.dump} G - break_git_remote_ops! { bundle :update, :all => true } + break_git_remote_ops! { bundle :update, all: true } expect(err).to include("Using cached git data because of network errors") expect(the_bundle).to be_locked diff --git a/spec/bundler/install/binstubs_spec.rb b/spec/bundler/install/binstubs_spec.rb index 6961171f4f..928ba80b15 100644 --- a/spec/bundler/install/binstubs_spec.rb +++ b/spec/bundler/install/binstubs_spec.rb @@ -2,10 +2,8 @@ RSpec.describe "bundle install" do describe "when system_bindir is set" do - # On OS X, Gem.bindir defaults to /usr/bin, so system_bindir is useful if - # you want to avoid sudo installs for system gems with OS X's default ruby it "overrides Gem.bindir" do - expect(Pathname.new("/usr/bin")).not_to be_writable unless Process.euid == 0 + expect(Pathname.new("/usr/bin")).not_to be_writable gemfile <<-G def Gem.bindir; "/usr/bin"; end source "#{file_uri_for(gem_repo1)}" diff --git a/spec/bundler/install/bundler_spec.rb b/spec/bundler/install/bundler_spec.rb index 963ce82db8..19911f1154 100644 --- a/spec/bundler/install/bundler_spec.rb +++ b/spec/bundler/install/bundler_spec.rb @@ -30,19 +30,18 @@ RSpec.describe "bundle install" do end it "causes a conflict if explicitly requesting a different version of bundler" do - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G, raise_on_error: false source "#{file_uri_for(gem_repo2)}" gem "rails", "3.0" gem "bundler", "0.9.1" G - nice_error = <<-E.strip.gsub(/^ {8}/, "") - Bundler could not find compatible versions for gem "bundler": - In Gemfile: - bundler (= 0.9.1) + nice_error = <<~E.strip + Could not find compatible versions - Current Bundler version: - bundler (#{Bundler::VERSION}) + Because the current Bundler version (#{Bundler::VERSION}) does not satisfy bundler = 0.9.1 + and Gemfile depends on bundler = 0.9.1, + version solving has failed. Your bundle requires a different version of Bundler than the one you're running. Install the necessary version with `gem install bundler:0.9.1` and rerun bundler using `bundle _0.9.1_ install` @@ -51,19 +50,21 @@ RSpec.describe "bundle install" do end it "causes a conflict if explicitly requesting a non matching requirement on bundler" do - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G, raise_on_error: false source "#{file_uri_for(gem_repo2)}" gem "rails", "3.0" gem "bundler", "~> 0.8" G - nice_error = <<-E.strip.gsub(/^ {8}/, "") - Bundler could not find compatible versions for gem "bundler": - In Gemfile: - bundler (~> 0.8) + nice_error = <<~E.strip + Could not find compatible versions - Current Bundler version: - bundler (#{Bundler::VERSION}) + Because rails >= 3.0 depends on bundler >= 0.9.0.pre + and the current Bundler version (#{Bundler::VERSION}) does not satisfy bundler >= 0.9.0.pre, < 1.A, + rails >= 3.0 requires bundler >= 1.A. + So, because Gemfile depends on rails = 3.0 + and Gemfile depends on bundler ~> 0.8, + version solving has failed. Your bundle requires a different version of Bundler than the one you're running. Install the necessary version with `gem install bundler:0.9.1` and rerun bundler using `bundle _0.9.1_ install` @@ -72,19 +73,18 @@ RSpec.describe "bundle install" do end it "causes a conflict if explicitly requesting a version of bundler that doesn't exist" do - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G, raise_on_error: false source "#{file_uri_for(gem_repo2)}" gem "rails", "3.0" gem "bundler", "0.9.2" G - nice_error = <<-E.strip.gsub(/^ {8}/, "") - Bundler could not find compatible versions for gem "bundler": - In Gemfile: - bundler (= 0.9.2) + nice_error = <<~E.strip + Could not find compatible versions - Current Bundler version: - bundler (#{Bundler::VERSION}) + Because the current Bundler version (#{Bundler::VERSION}) does not satisfy bundler = 0.9.2 + and Gemfile depends on bundler = 0.9.2, + version solving has failed. Your bundle requires a different version of Bundler than the one you're running, and that version could not be found. E @@ -143,20 +143,21 @@ RSpec.describe "bundle install" do end end - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G, raise_on_error: false source "#{file_uri_for(gem_repo2)}" gem "activemerchant" gem "rails_pinned_to_old_activesupport" G - nice_error = <<-E.strip.gsub(/^ {8}/, "") - Bundler could not find compatible versions for gem "activesupport": - In Gemfile: - activemerchant was resolved to 1.0, which depends on - activesupport (>= 2.0.0) + nice_error = <<~E.strip + Could not find compatible versions - rails_pinned_to_old_activesupport was resolved to 1.0, which depends on - activesupport (= 1.2.3) + Because every version of rails_pinned_to_old_activesupport depends on activesupport = 1.2.3 + and every version of activemerchant depends on activesupport >= 2.0.0, + every version of rails_pinned_to_old_activesupport is incompatible with activemerchant >= 0. + So, because Gemfile depends on activemerchant >= 0 + and Gemfile depends on rails_pinned_to_old_activesupport >= 0, + version solving has failed. E expect(err).to include(nice_error) end @@ -170,19 +171,20 @@ RSpec.describe "bundle install" do end end - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G, raise_on_error: false source "#{file_uri_for(gem_repo2)}" gem "rails_pinned_to_old_activesupport" gem "activesupport", "2.3.5" G - nice_error = <<-E.strip.gsub(/^ {8}/, "") - Bundler could not find compatible versions for gem "activesupport": - In Gemfile: - activesupport (= 2.3.5) + nice_error = <<~E.strip + Could not find compatible versions - rails_pinned_to_old_activesupport was resolved to 1.0, which depends on - activesupport (= 1.2.3) + Because every version of rails_pinned_to_old_activesupport depends on activesupport = 1.2.3 + and Gemfile depends on rails_pinned_to_old_activesupport >= 0, + activesupport = 1.2.3 is required. + So, because Gemfile depends on activesupport = 2.3.5, + version solving has failed. E expect(err).to include(nice_error) end @@ -208,6 +210,33 @@ RSpec.describe "bundle install" do expect(err).to be_empty end + it "prints the previous version when switching to a previously downloaded gem" do + build_repo4 do + build_gem "rails", "7.0.3" + build_gem "rails", "7.0.4" + end + + bundle "config set path.system true" + + install_gemfile <<-G + source "#{file_uri_for(gem_repo4)}" + gem 'rails', "7.0.4" + G + + install_gemfile <<-G + source "#{file_uri_for(gem_repo4)}" + gem 'rails', "7.0.3" + G + + install_gemfile <<-G + source "#{file_uri_for(gem_repo4)}" + gem 'rails', "7.0.4" + G + + expect(out).to include("Using rails 7.0.4 (was 7.0.3)") + expect(err).to be_empty + end + it "can install dependencies with newer bundler version with system gems" do bundle "config set path.system true" diff --git a/spec/bundler/install/deploy_spec.rb b/spec/bundler/install/deploy_spec.rb index 3bcb6a703e..d89fdea6f1 100644 --- a/spec/bundler/install/deploy_spec.rb +++ b/spec/bundler/install/deploy_spec.rb @@ -8,26 +8,26 @@ RSpec.describe "install in deployment or frozen mode" do G end - context "with CLI flags", :bundler => "< 3" do + context "with CLI flags", bundler: "< 3" do it "fails without a lockfile and says that --deployment requires a lock" do - bundle "install --deployment", :raise_on_error => false - expect(err).to include("The --deployment flag requires a Gemfile.lock") + bundle "install --deployment", raise_on_error: false + expect(err).to include("The --deployment flag requires a lockfile") end it "fails without a lockfile and says that --frozen requires a lock" do - bundle "install --frozen", :raise_on_error => false - expect(err).to include("The --frozen flag requires a Gemfile.lock") + bundle "install --frozen", raise_on_error: false + expect(err).to include("The --frozen flag requires a lockfile") end it "disallows --deployment --system" do - bundle "install --deployment --system", :raise_on_error => false + bundle "install --deployment --system", raise_on_error: false expect(err).to include("You have specified both --deployment") expect(err).to include("Please choose only one option") expect(exitstatus).to eq(15) end it "disallows --deployment --path --system" do - bundle "install --deployment --path . --system", :raise_on_error => false + bundle "install --deployment --path . --system", raise_on_error: false expect(err).to include("You have specified both --path") expect(err).to include("as well as --system") expect(err).to include("Please choose only one option") @@ -35,10 +35,43 @@ RSpec.describe "install in deployment or frozen mode" do end it "doesn't mess up a subsequent `bundle install` after you try to deploy without a lock" do - bundle "install --deployment", :raise_on_error => false + bundle "install --deployment", raise_on_error: false bundle :install expect(the_bundle).to include_gems "rack 1.0" end + + it "installs gems by default to vendor/bundle" do + bundle :lock + bundle "install --deployment" + expect(out).to include("vendor/bundle") + end + + it "installs gems to custom path if specified" do + bundle :lock + bundle "install --path vendor/bundle2 --deployment" + expect(out).to include("vendor/bundle2") + end + + it "works with the --frozen flag" do + bundle :lock + bundle "install --frozen" + end + + it "explodes with the --deployment flag if you make a change and don't check in the lockfile" do + bundle :lock + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack" + gem "rack-obama" + G + + bundle "install --deployment", raise_on_error: false + expect(err).to include("frozen mode") + expect(err).to include("You have added to the Gemfile") + expect(err).to include("* rack-obama") + expect(err).not_to include("You have deleted from the Gemfile") + expect(err).not_to include("You have changed in the Gemfile") + end end it "still works if you are not in the app directory and specify --gemfile" do @@ -46,7 +79,7 @@ RSpec.describe "install in deployment or frozen mode" do simulate_new_machine bundle "config set --local deployment true" bundle "config set --local path vendor/bundle" - bundle "install --gemfile #{tmp}/bundled_app/Gemfile", :dir => tmp + bundle "install --gemfile #{tmp}/bundled_app/Gemfile", dir: tmp expect(the_bundle).to include_gems "rack 1.0" end @@ -70,12 +103,12 @@ RSpec.describe "install in deployment or frozen mode" do bundle :install bundle "config set --local deployment true" bundle :install - bundle "exec bundle check", :env => { "PATH" => path } + bundle "exec bundle check", env: { "PATH" => path } end it "works when using path gems from the same path and the version is specified" do - build_lib "foo", :path => lib_path("nested/foo") - build_lib "bar", :path => lib_path("nested/bar") + build_lib "foo", path: lib_path("nested/foo") + build_lib "bar", path: lib_path("nested/bar") gemfile <<-G source "#{file_uri_for(gem_repo1)}" gem "foo", "1.0", :path => "#{lib_path("nested")}" @@ -88,7 +121,7 @@ RSpec.describe "install in deployment or frozen mode" do end it "works when path gems are specified twice" do - build_lib "foo", :path => lib_path("nested/foo") + build_lib "foo", path: lib_path("nested/foo") gemfile <<-G source "#{file_uri_for(gem_repo1)}" gem "foo", :path => "#{lib_path("nested/foo")}" @@ -101,14 +134,14 @@ RSpec.describe "install in deployment or frozen mode" do end it "works when there are credentials in the source URL" do - install_gemfile(<<-G, :artifice => "endpoint_strict_basic_authentication", :quiet => true) + install_gemfile(<<-G, artifice: "endpoint_strict_basic_authentication", quiet: true) source "http://user:pass@localgemserver.test/" gem "rack-obama", ">= 1.0" G bundle "config set --local deployment true" - bundle :install, :artifice => "endpoint_strict_basic_authentication" + bundle :install, artifice: "endpoint_strict_basic_authentication" end it "works with sources given by a block" do @@ -141,7 +174,7 @@ RSpec.describe "install in deployment or frozen mode" do rack (1.0.0) PLATFORMS - #{local} + #{generic_local_platform} DEPENDENCIES rack @@ -150,50 +183,10 @@ RSpec.describe "install in deployment or frozen mode" do bundle "config set --local deployment true" end - it "prevents the replace by default" do - bundle :install, :raise_on_error => false - - expect(err).to match(/The list of sources changed/) - end - - context "when allow_deployment_source_credential_changes is true" do - before { bundle "config set allow_deployment_source_credential_changes true" } - - it "allows the replace" do - bundle :install - - expect(out).to match(/Bundle complete!/) - end - end - - context "when allow_deployment_source_credential_changes is false" do - before { bundle "config set allow_deployment_source_credential_changes false" } - - it "prevents the replace" do - bundle :install, :raise_on_error => false - - expect(err).to match(/The list of sources changed/) - end - end - - context "when BUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES env var is true" do - before { ENV["BUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES"] = "true" } - - it "allows the replace" do - bundle :install - - expect(out).to match(/Bundle complete!/) - end - end - - context "when BUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES env var is false" do - before { ENV["BUNDLE_ALLOW_DEPLOYMENT_SOURCE_CREDENTIAL_CHANGES"] = "false" } - - it "prevents the replace" do - bundle :install, :raise_on_error => false + it "allows the replace" do + bundle :install - expect(err).to match(/The list of sources changed/) - end + expect(out).to match(/Bundle complete!/) end end @@ -202,29 +195,41 @@ RSpec.describe "install in deployment or frozen mode" do bundle "install" end - it "installs gems by default to vendor/bundle", :bundler => "< 3" do - bundle "install --deployment" + it "installs gems by default to vendor/bundle" do + bundle "config set deployment true" + expect do + bundle "install" + end.not_to change { bundled_app_lock.mtime } expect(out).to include("vendor/bundle") end - it "installs gems to custom path if specified", :bundler => "< 3" do - bundle "install --path vendor/bundle2 --deployment" + it "installs gems to custom path if specified" do + bundle "config set path vendor/bundle2" + bundle "config set deployment true" + bundle "install" expect(out).to include("vendor/bundle2") end - it "works with the --deployment flag if you didn't change anything", :bundler => "< 3" do - bundle "install --deployment" + it "installs gems to custom path if specified, even when configured through ENV" do + bundle "config set deployment true" + bundle "install", env: { "BUNDLE_PATH" => "vendor/bundle2" } + expect(out).to include("vendor/bundle2") end - it "works with the --frozen flag if you didn't change anything", :bundler => "< 3" do - bundle "install --frozen" + it "works with the `frozen` setting" do + bundle "config set frozen true" + expect do + bundle "install" + end.not_to change { bundled_app_lock.mtime } end it "works with BUNDLE_FROZEN if you didn't change anything" do - bundle :install, :env => { "BUNDLE_FROZEN" => "true" } + expect do + bundle :install, env: { "BUNDLE_FROZEN" => "true" } + end.not_to change { bundled_app_lock.mtime } end - it "explodes with the --deployment flag if you make a change and don't check in the lockfile" do + it "explodes with the `deployment` setting if you make a change and don't check in the lockfile" do gemfile <<-G source "#{file_uri_for(gem_repo1)}" gem "rack" @@ -232,8 +237,8 @@ RSpec.describe "install in deployment or frozen mode" do G bundle "config set --local deployment true" - bundle :install, :raise_on_error => false - expect(err).to include("deployment mode") + bundle :install, raise_on_error: false + expect(err).to include("frozen mode") expect(err).to include("You have added to the Gemfile") expect(err).to include("* rack-obama") expect(err).not_to include("You have deleted from the Gemfile") @@ -253,22 +258,60 @@ RSpec.describe "install in deployment or frozen mode" do bundle "config set --local path .bundle" bundle "config set --local without development" bundle "config set --local deployment true" - bundle :install, :env => { "DEBUG" => "1" } + bundle :install, env: { "DEBUG" => "1" } run "puts :WIN" expect(out).to eq("WIN") end - it "works if a gem is missing, but it's on a different platform, and the Gemfile has no global source", :bundler => "< 3" do + it "works if a gem is missing, but it's on a different platform" do + build_repo2 + install_gemfile <<-G + source "#{file_uri_for(gem_repo2)}" + source "#{file_uri_for(gem_repo1)}" do gem "rake", platform: :#{not_local_tag} end G - bundle :install, :env => { "BUNDLE_FROZEN" => "true" } + bundle :install, env: { "BUNDLE_FROZEN" => "true" } expect(last_command).to be_success end + it "shows a good error if a gem is missing from the lockfile" do + build_repo4 do + build_gem "foo" + build_gem "bar" + end + + gemfile <<-G + source "https://gem.repo4" + + gem "foo" + gem "bar" + G + + lockfile <<~L + GEM + remote: https://gem.repo4/ + specs: + foo (1.0) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + foo + bar + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle :install, env: { "BUNDLE_FROZEN" => "true" }, raise_on_error: false, artifice: "compact_index" + expect(err).to include("Your lock file is missing \"bar\", but the lockfile can't be updated because frozen mode is set") + end + it "explodes if a path gem is missing" do build_lib "path_gem" install_gemfile <<-G @@ -281,11 +324,11 @@ RSpec.describe "install in deployment or frozen mode" do bundle "config set --local path .bundle" bundle "config set --local deployment true" - bundle :install, :raise_on_error => false + bundle :install, raise_on_error: false expect(err).to include("The path `#{lib_path("path_gem-1.0")}` does not exist.") end - it "can have --frozen set via an environment variable", :bundler => "< 3" do + it "can have --frozen set via an environment variable" do gemfile <<-G source "#{file_uri_for(gem_repo1)}" gem "rack" @@ -293,8 +336,8 @@ RSpec.describe "install in deployment or frozen mode" do G ENV["BUNDLE_FROZEN"] = "1" - bundle "install", :raise_on_error => false - expect(err).to include("deployment mode") + bundle "install", raise_on_error: false + expect(err).to include("frozen mode") expect(err).to include("You have added to the Gemfile") expect(err).to include("* rack-obama") expect(err).not_to include("You have deleted from the Gemfile") @@ -309,21 +352,21 @@ RSpec.describe "install in deployment or frozen mode" do G ENV["BUNDLE_DEPLOYMENT"] = "true" - bundle "install", :raise_on_error => false - expect(err).to include("deployment mode") + bundle "install", raise_on_error: false + expect(err).to include("frozen mode") expect(err).to include("You have added to the Gemfile") expect(err).to include("* rack-obama") expect(err).not_to include("You have deleted from the Gemfile") expect(err).not_to include("You have changed in the Gemfile") end - it "installs gems by default to vendor/bundle when deployment mode is set via an environment variable", :bundler => "< 3" do + it "installs gems by default to vendor/bundle when deployment mode is set via an environment variable" do ENV["BUNDLE_DEPLOYMENT"] = "true" bundle "install" expect(out).to include("vendor/bundle") end - it "installs gems to custom path when deployment mode is set via an environment variable ", :bundler => "< 3" do + it "installs gems to custom path when deployment mode is set via an environment variable " do ENV["BUNDLE_DEPLOYMENT"] = "true" ENV["BUNDLE_PATH"] = "vendor/bundle2" bundle "install" @@ -340,7 +383,7 @@ RSpec.describe "install in deployment or frozen mode" do ENV["BUNDLE_FROZEN"] = "false" ENV["BUNDLE_DEPLOYMENT"] = "false" bundle "install" - expect(out).not_to include("deployment mode") + expect(out).not_to include("frozen mode") expect(out).not_to include("You have added to the Gemfile") expect(out).not_to include("* rack-obama") end @@ -352,8 +395,8 @@ RSpec.describe "install in deployment or frozen mode" do G bundle "config set --local deployment true" - bundle :install, :raise_on_error => false - expect(err).to include("deployment mode") + bundle :install, raise_on_error: false + expect(err).to include("frozen mode") expect(err).to include("You have added to the Gemfile:\n* activesupport\n\n") expect(err).to include("You have deleted from the Gemfile:\n* rack") expect(err).not_to include("You have changed in the Gemfile") @@ -366,8 +409,8 @@ RSpec.describe "install in deployment or frozen mode" do G bundle "config set --local deployment true" - bundle :install, :raise_on_error => false - expect(err).to include("deployment mode") + bundle :install, raise_on_error: false + expect(err).to include("frozen mode") expect(err).not_to include("You have added to the Gemfile") expect(err).to include("You have changed in the Gemfile:\n* rack from `no specified source` to `git://hubz.com`") end @@ -386,16 +429,16 @@ RSpec.describe "install in deployment or frozen mode" do G bundle "config set --local deployment true" - bundle :install, :raise_on_error => false - expect(err).to include("deployment mode") + bundle :install, raise_on_error: false + expect(err).to include("frozen mode") expect(err).not_to include("You have deleted from the Gemfile") expect(err).not_to include("You have added to the Gemfile") expect(err).to include("You have changed in the Gemfile:\n* rack from `#{lib_path("rack-1.0")}` to `no specified source`") end it "explodes if you change a source" do - build_lib "foo", :path => lib_path("rack/foo") - build_git "rack", :path => lib_path("rack") + build_lib "foo", path: lib_path("rack/foo") + build_git "rack", path: lib_path("rack") install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" @@ -410,8 +453,8 @@ RSpec.describe "install in deployment or frozen mode" do G bundle "config set --local deployment true" - bundle :install, :raise_on_error => false - expect(err).to include("deployment mode") + bundle :install, raise_on_error: false + expect(err).to include("frozen mode") expect(err).to include("You have changed in the Gemfile:\n* rack from `#{lib_path("rack")}` to `no specified source`") expect(err).not_to include("You have added to the Gemfile") expect(err).not_to include("You have deleted from the Gemfile") @@ -428,23 +471,23 @@ RSpec.describe "install in deployment or frozen mode" do gem "rack-obama" G - run "require 'rack'", :raise_on_error => false - expect(err).to include strip_whitespace(<<-E).strip -The dependencies in your gemfile changed + run "require 'rack'", raise_on_error: false + expect(err).to include <<~E.strip + The dependencies in your gemfile changed, but the lockfile can't be updated because frozen mode is set (Bundler::ProductionError) -You have added to the Gemfile: -* rack (= 1.0.0) -* rack-obama + You have added to the Gemfile: + * rack (= 1.0.0) + * rack-obama -You have deleted from the Gemfile: -* rack + You have deleted from the Gemfile: + * rack E end end context "with path in Gemfile and packed" do it "works fine after bundle package and bundle install --local" do - build_lib "foo", :path => lib_path("foo") + build_lib "foo", path: lib_path("foo") install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" gem "foo", :path => "#{lib_path("foo")}" @@ -463,7 +506,7 @@ You have deleted from the Gemfile: simulate_new_machine bundle "config set --local deployment true" bundle "install --verbose" - expect(out).not_to include("You are trying to install in deployment mode after changing your Gemfile") + expect(out).not_to include("but the lockfile can't be updated because frozen mode is set") expect(out).not_to include("You have added to the Gemfile") expect(out).not_to include("You have deleted from the Gemfile") expect(out).to include("vendor/cache/foo") diff --git a/spec/bundler/install/failure_spec.rb b/spec/bundler/install/failure_spec.rb index 4a9c33754f..f972a37bf6 100644 --- a/spec/bundler/install/failure_spec.rb +++ b/spec/bundler/install/failure_spec.rb @@ -14,7 +14,7 @@ RSpec.describe "bundle install" do end end - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G, raise_on_error: false source "#{file_uri_for(gem_repo2)}" gem "rails" G @@ -39,7 +39,7 @@ In Gemfile: end it "removes the downloaded .gem" do - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G, raise_on_error: false source "#{file_uri_for(gem_repo4)}" gem "a" G diff --git a/spec/bundler/install/gemfile/eval_gemfile_spec.rb b/spec/bundler/install/gemfile/eval_gemfile_spec.rb index 02283291b4..cfa66e5986 100644 --- a/spec/bundler/install/gemfile/eval_gemfile_spec.rb +++ b/spec/bundler/install/gemfile/eval_gemfile_spec.rb @@ -2,7 +2,7 @@ RSpec.describe "bundle install with gemfile that uses eval_gemfile" do before do - build_lib("gunks", :path => bundled_app.join("gems/gunks")) do |s| + build_lib("gunks", path: bundled_app.join("gems/gunks")) do |s| s.name = "gunks" s.version = "0.0.1" end @@ -24,7 +24,7 @@ RSpec.describe "bundle install with gemfile that uses eval_gemfile" do expect(out).to include("Resolving dependencies") expect(out).to include("Bundle complete") - expect(the_bundle).to include_gem "gunks 0.0.1", :source => "path@#{bundled_app("gems", "gunks")}" + expect(the_bundle).to include_gem "gunks 0.0.1", source: "path@#{bundled_app("gems", "gunks")}" end end @@ -64,7 +64,7 @@ RSpec.describe "bundle install with gemfile that uses eval_gemfile" do context "eval-ed Gemfile has relative-path gems" do before do - build_lib("a", :path => bundled_app("gems/a")) + build_lib("a", path: bundled_app("gems/a")) create_file bundled_app("nested/Gemfile-nested"), <<-G source "#{file_uri_for(gem_repo1)}" gem "a", :path => "../gems/a" @@ -102,7 +102,7 @@ RSpec.describe "bundle install with gemfile that uses eval_gemfile" do expect(out).to include("Resolving dependencies") expect(out).to include("Bundle complete") - expect(the_bundle).to include_gem "gunks 0.0.1", :source => "path@#{bundled_app("gems", "gunks")}" + expect(the_bundle).to include_gem "gunks 0.0.1", source: "path@#{bundled_app("gems", "gunks")}" end end diff --git a/spec/bundler/install/gemfile/force_ruby_platform_spec.rb b/spec/bundler/install/gemfile/force_ruby_platform_spec.rb new file mode 100644 index 0000000000..a29b79ad62 --- /dev/null +++ b/spec/bundler/install/gemfile/force_ruby_platform_spec.rb @@ -0,0 +1,146 @@ +# frozen_string_literal: true + +RSpec.describe "bundle install with force_ruby_platform DSL option", :jruby do + context "when no transitive deps" do + before do + build_repo4 do + # Build a gem with platform specific versions + build_gem("platform_specific") do |s| + s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0.0 RUBY'" + end + + build_gem("platform_specific") do |s| + s.platform = Bundler.local_platform + s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0.0 #{Bundler.local_platform}'" + end + + # Build the exact same gem with a different name to compare using vs not using the option + build_gem("platform_specific_forced") do |s| + s.write "lib/platform_specific_forced.rb", "PLATFORM_SPECIFIC_FORCED = '1.0.0 RUBY'" + end + + build_gem("platform_specific_forced") do |s| + s.platform = Bundler.local_platform + s.write "lib/platform_specific_forced.rb", "PLATFORM_SPECIFIC_FORCED = '1.0.0 #{Bundler.local_platform}'" + end + end + end + + it "pulls the pure ruby variant of the given gem" do + install_gemfile <<-G + source "#{file_uri_for(gem_repo4)}" + + gem "platform_specific_forced", :force_ruby_platform => true + gem "platform_specific" + G + + expect(the_bundle).to include_gems "platform_specific_forced 1.0.0 RUBY" + expect(the_bundle).to include_gems "platform_specific 1.0.0 #{Bundler.local_platform}" + end + + it "still respects a global `force_ruby_platform` config" do + install_gemfile <<-G, env: { "BUNDLE_FORCE_RUBY_PLATFORM" => "true" } + source "#{file_uri_for(gem_repo4)}" + + gem "platform_specific_forced", :force_ruby_platform => true + gem "platform_specific" + G + + expect(the_bundle).to include_gems "platform_specific_forced 1.0.0 RUBY" + expect(the_bundle).to include_gems "platform_specific 1.0.0 RUBY" + end + end + + context "when also a transitive dependency" do + before do + build_repo4 do + build_gem("depends_on_platform_specific") {|s| s.add_runtime_dependency "platform_specific" } + + build_gem("platform_specific") do |s| + s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0.0 RUBY'" + end + + build_gem("platform_specific") do |s| + s.platform = Bundler.local_platform + s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0.0 #{Bundler.local_platform}'" + end + end + end + + it "still pulls the ruby variant" do + install_gemfile <<-G + source "#{file_uri_for(gem_repo4)}" + + gem "depends_on_platform_specific" + gem "platform_specific", :force_ruby_platform => true + G + + expect(the_bundle).to include_gems "platform_specific 1.0.0 RUBY" + end + end + + context "with transitive dependencies with platform specific versions" do + before do + build_repo4 do + build_gem("depends_on_platform_specific") do |s| + s.add_runtime_dependency "platform_specific" + s.write "lib/depends_on_platform_specific.rb", "DEPENDS_ON_PLATFORM_SPECIFIC = '1.0.0 RUBY'" + end + + build_gem("depends_on_platform_specific") do |s| + s.add_runtime_dependency "platform_specific" + s.platform = Bundler.local_platform + s.write "lib/depends_on_platform_specific.rb", "DEPENDS_ON_PLATFORM_SPECIFIC = '1.0.0 #{Bundler.local_platform}'" + end + + build_gem("platform_specific") do |s| + s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0.0 RUBY'" + end + + build_gem("platform_specific") do |s| + s.platform = Bundler.local_platform + s.write "lib/platform_specific.rb", "PLATFORM_SPECIFIC = '1.0.0 #{Bundler.local_platform}'" + end + end + end + + it "ignores ruby variants for the transitive dependencies" do + install_gemfile <<-G, env: { "DEBUG_RESOLVER" => "true" } + source "#{file_uri_for(gem_repo4)}" + + gem "depends_on_platform_specific", :force_ruby_platform => true + G + + expect(the_bundle).to include_gems "depends_on_platform_specific 1.0.0 RUBY" + expect(the_bundle).to include_gems "platform_specific 1.0.0 #{Bundler.local_platform}" + end + + it "reinstalls the ruby variant when a platform specific variant is already installed, the lockile has only RUBY platform, and :force_ruby_platform is used in the Gemfile" do + lockfile <<-L + GEM + remote: #{file_uri_for(gem_repo4)} + specs: + platform_specific (1.0) + + PLATFORMS + ruby + + DEPENDENCIES + platform_specific + + BUNDLED WITH + #{Bundler::VERSION} + L + + system_gems "platform_specific-1.0-#{Gem::Platform.local}", path: default_bundle_path + + install_gemfile <<-G, env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }, artifice: "compact_index" + source "#{file_uri_for(gem_repo4)}" + + gem "platform_specific", :force_ruby_platform => true + G + + expect(the_bundle).to include_gems "platform_specific 1.0.0 RUBY" + end + end +end diff --git a/spec/bundler/install/gemfile/gemspec_spec.rb b/spec/bundler/install/gemfile/gemspec_spec.rb index 6bcfadab7e..63778567cf 100644 --- a/spec/bundler/install/gemfile/gemspec_spec.rb +++ b/spec/bundler/install/gemfile/gemspec_spec.rb @@ -8,8 +8,38 @@ RSpec.describe "bundle install from an existing gemspec" do end end + let(:x64_mingw_archs) do + if RUBY_PLATFORM == "x64-mingw-ucrt" + if Gem.rubygems_version >= Gem::Version.new("3.2.28") + ["x64-mingw-ucrt", "x64-mingw32"] + else + ["x64-mingw32", "x64-unknown"] + end + else + ["x64-mingw32"] + end + end + + let(:x64_mingw_gems) do + x64_mingw_archs.map {|p| "platform_specific (1.0-#{p})" }.join("\n ") + end + + let(:x64_mingw_platforms) do + x64_mingw_archs.join("\n ") + end + + def x64_mingw_checksums(checksums) + x64_mingw_archs.each do |arch| + if arch == "x64-mingw-ucrt" + checksums.no_checksum "platform_specific", "1.0", arch + else + checksums.checksum gem_repo2, "platform_specific", "1.0", arch + end + end + end + it "should install runtime and development dependencies" do - build_lib("foo", :path => tmp.join("foo")) do |s| + build_lib("foo", path: tmp.join("foo")) do |s| s.write("Gemfile", "source :rubygems\ngemspec") s.add_dependency "bar", "=1.0.0" s.add_development_dependency "bar-dev", "=1.0.0" @@ -20,11 +50,11 @@ RSpec.describe "bundle install from an existing gemspec" do G expect(the_bundle).to include_gems "bar 1.0.0" - expect(the_bundle).to include_gems "bar-dev 1.0.0", :groups => :development + expect(the_bundle).to include_gems "bar-dev 1.0.0", groups: :development end it "that is hidden should install runtime and development dependencies" do - build_lib("foo", :path => tmp.join("foo")) do |s| + build_lib("foo", path: tmp.join("foo")) do |s| s.write("Gemfile", "source :rubygems\ngemspec") s.add_dependency "bar", "=1.0.0" s.add_development_dependency "bar-dev", "=1.0.0" @@ -37,7 +67,7 @@ RSpec.describe "bundle install from an existing gemspec" do G expect(the_bundle).to include_gems "bar 1.0.0" - expect(the_bundle).to include_gems "bar-dev 1.0.0", :groups => :development + expect(the_bundle).to include_gems "bar-dev 1.0.0", groups: :development end it "should handle a list of requirements" do @@ -46,7 +76,7 @@ RSpec.describe "bundle install from an existing gemspec" do build_gem "baz", "1.1" end - build_lib("foo", :path => tmp.join("foo")) do |s| + build_lib("foo", path: tmp.join("foo")) do |s| s.write("Gemfile", "source :rubygems\ngemspec") s.add_dependency "baz", ">= 1.0", "< 1.1" end @@ -59,29 +89,29 @@ RSpec.describe "bundle install from an existing gemspec" do end it "should raise if there are no gemspecs available" do - build_lib("foo", :path => tmp.join("foo"), :gemspec => false) + build_lib("foo", path: tmp.join("foo"), gemspec: false) - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G, raise_on_error: false source "#{file_uri_for(gem_repo2)}" gemspec :path => '#{tmp.join("foo")}' G - expect(err).to match(/There are no gemspecs at #{tmp.join('foo')}/) + expect(err).to match(/There are no gemspecs at #{tmp.join("foo")}/) end it "should raise if there are too many gemspecs available" do - build_lib("foo", :path => tmp.join("foo")) do |s| + build_lib("foo", path: tmp.join("foo")) do |s| s.write("foo2.gemspec", build_spec("foo", "4.0").first.to_ruby) end - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G, raise_on_error: false source "#{file_uri_for(gem_repo2)}" gemspec :path => '#{tmp.join("foo")}' G - expect(err).to match(/There are multiple gemspecs at #{tmp.join('foo')}/) + expect(err).to match(/There are multiple gemspecs at #{tmp.join("foo")}/) end it "should pick a specific gemspec" do - build_lib("foo", :path => tmp.join("foo")) do |s| + build_lib("foo", path: tmp.join("foo")) do |s| s.write("foo2.gemspec", "") s.add_dependency "bar", "=1.0.0" s.add_development_dependency "bar-dev", "=1.0.0" @@ -93,11 +123,11 @@ RSpec.describe "bundle install from an existing gemspec" do G expect(the_bundle).to include_gems "bar 1.0.0" - expect(the_bundle).to include_gems "bar-dev 1.0.0", :groups => :development + expect(the_bundle).to include_gems "bar-dev 1.0.0", groups: :development end it "should use a specific group for development dependencies" do - build_lib("foo", :path => tmp.join("foo")) do |s| + build_lib("foo", path: tmp.join("foo")) do |s| s.write("foo2.gemspec", "") s.add_dependency "bar", "=1.0.0" s.add_development_dependency "bar-dev", "=1.0.0" @@ -109,32 +139,32 @@ RSpec.describe "bundle install from an existing gemspec" do G expect(the_bundle).to include_gems "bar 1.0.0" - expect(the_bundle).not_to include_gems "bar-dev 1.0.0", :groups => :development - expect(the_bundle).to include_gems "bar-dev 1.0.0", :groups => :dev + expect(the_bundle).not_to include_gems "bar-dev 1.0.0", groups: :development + expect(the_bundle).to include_gems "bar-dev 1.0.0", groups: :dev end it "should match a lockfile even if the gemspec defines development dependencies" do - build_lib("foo", :path => tmp.join("foo")) do |s| + build_lib("foo", path: tmp.join("foo")) do |s| s.write("Gemfile", "source '#{file_uri_for(gem_repo1)}'\ngemspec") s.add_dependency "actionpack", "=2.3.2" - s.add_development_dependency "rake", "=13.0.1" + s.add_development_dependency "rake", rake_version end - bundle "install", :dir => tmp.join("foo") + bundle "install", dir: tmp.join("foo") # This should really be able to rely on $stderr, but, it's not written # right, so we can't. In fact, this is a bug negation test, and so it'll # ghost pass in future, and will only catch a regression if the message # doesn't change. Exit codes should be used correctly (they can be more # than just 0 and 1). bundle "config set --local deployment true" - output = bundle("install", :dir => tmp.join("foo")) + output = bundle("install", dir: tmp.join("foo")) expect(output).not_to match(/You have added to the Gemfile/) expect(output).not_to match(/You have deleted from the Gemfile/) - expect(output).not_to match(/install in deployment mode after changing/) + expect(output).not_to match(/the lockfile can't be updated because frozen mode is set/) end it "should match a lockfile without needing to re-resolve" do - build_lib("foo", :path => tmp.join("foo")) do |s| + build_lib("foo", path: tmp.join("foo")) do |s| s.add_dependency "rack" end @@ -143,7 +173,7 @@ RSpec.describe "bundle install from an existing gemspec" do gemspec :path => '#{tmp.join("foo")}' G - bundle "install", :verbose => true + bundle "install", verbose: true message = "Found no changes, using resolution from the lockfile" expect(out.scan(message).size).to eq(1) @@ -152,7 +182,7 @@ RSpec.describe "bundle install from an existing gemspec" do it "should match a lockfile without needing to re-resolve with development dependencies" do simulate_platform java - build_lib("foo", :path => tmp.join("foo")) do |s| + build_lib("foo", path: tmp.join("foo")) do |s| s.add_dependency "rack" s.add_development_dependency "thin" end @@ -162,34 +192,34 @@ RSpec.describe "bundle install from an existing gemspec" do gemspec :path => '#{tmp.join("foo")}' G - bundle "install", :verbose => true + bundle "install", verbose: true message = "Found no changes, using resolution from the lockfile" expect(out.scan(message).size).to eq(1) end - it "should match a lockfile on non-ruby platforms with a transitive platform dependency", :jruby do - build_lib("foo", :path => tmp.join("foo")) do |s| + it "should match a lockfile on non-ruby platforms with a transitive platform dependency", :jruby_only do + build_lib("foo", path: tmp.join("foo")) do |s| s.add_dependency "platform_specific" end - system_gems "platform_specific-1.0-java", :path => default_bundle_path + system_gems "platform_specific-1.0-java", path: default_bundle_path install_gemfile <<-G gemspec :path => '#{tmp.join("foo")}' G - bundle "update --bundler", :artifice => "compact_index", :verbose => true + bundle "update --bundler", artifice: "compact_index", verbose: true expect(the_bundle).to include_gems "foo 1.0", "platform_specific 1.0 JAVA" end it "should evaluate the gemspec in its directory" do - build_lib("foo", :path => tmp.join("foo")) + build_lib("foo", path: tmp.join("foo")) File.open(tmp.join("foo/foo.gemspec"), "w") do |s| s.write "raise 'ahh' unless Dir.pwd == '#{tmp.join("foo")}'" end - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G, raise_on_error: false gemspec :path => '#{tmp.join("foo")}' G expect(last_command.stdboth).not_to include("ahh") @@ -203,7 +233,7 @@ RSpec.describe "bundle install from an existing gemspec" do # so emulate that system_gems %w[rack-1.0.0 rack-0.9.1 rack-obama-1.0] - build_lib("foo", :path => bundled_app) + build_lib("foo", path: bundled_app) gemspec = bundled_app("foo.gemspec").read bundled_app("foo.gemspec").open("w") do |f| f.write "#{gemspec.strip}.tap { gem 'rack-obama'; require 'rack/obama' }" @@ -218,14 +248,14 @@ RSpec.describe "bundle install from an existing gemspec" do end it "allows conflicts" do - build_lib("foo", :path => tmp.join("foo")) do |s| + build_lib("foo", path: tmp.join("foo")) do |s| s.version = "1.0.0" s.add_dependency "bar", "= 1.0.0" end - build_gem "deps", :to_bundle => true do |s| + build_gem "deps", to_bundle: true do |s| s.add_dependency "foo", "= 0.0.1" end - build_gem "foo", "0.0.1", :to_bundle => true + build_gem "foo", "0.0.1", to_bundle: true install_gemfile <<-G source "#{file_uri_for(gem_repo2)}" @@ -237,7 +267,7 @@ RSpec.describe "bundle install from an existing gemspec" do end it "does not break Gem.finish_resolve with conflicts" do - build_lib("foo", :path => tmp.join("foo")) do |s| + build_lib("foo", path: tmp.join("foo")) do |s| s.version = "1.0.0" s.add_dependency "bar", "= 1.0.0" end @@ -261,14 +291,14 @@ RSpec.describe "bundle install from an existing gemspec" do end it "handles downgrades" do - build_lib "omg", "2.0", :path => lib_path("omg") + build_lib "omg", "2.0", path: lib_path("omg") install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" gemspec :path => "#{lib_path("omg")}" G - build_lib "omg", "1.0", :path => lib_path("omg") + build_lib "omg", "1.0", path: lib_path("omg") bundle :install @@ -278,7 +308,7 @@ RSpec.describe "bundle install from an existing gemspec" do context "in deployment mode" do context "when the lockfile was not updated after a change to the gemspec's dependencies" do it "reports that installation failed" do - build_lib "cocoapods", :path => bundled_app do |s| + build_lib "cocoapods", path: bundled_app do |s| s.add_dependency "activesupport", ">= 1" end @@ -289,12 +319,12 @@ RSpec.describe "bundle install from an existing gemspec" do expect(the_bundle).to include_gems("cocoapods 1.0", "activesupport 2.3.5") - build_lib "cocoapods", :path => bundled_app do |s| + build_lib "cocoapods", path: bundled_app do |s| s.add_dependency "activesupport", ">= 1.0.1" end bundle "config set --local deployment true" - bundle :install, :raise_on_error => false + bundle :install, raise_on_error: false expect(err).to include("changed") end @@ -304,13 +334,13 @@ RSpec.describe "bundle install from an existing gemspec" do context "when child gemspecs conflict with a released gemspec" do before do # build the "parent" gem that depends on another gem in the same repo - build_lib "source_conflict", :path => bundled_app do |s| + build_lib "source_conflict", path: bundled_app do |s| s.add_dependency "rack_middleware" end # build the "child" gem that is the same version as a released gem, but # has completely different and conflicting dependency requirements - build_lib "rack_middleware", "1.0", :path => bundled_app("rack_middleware") do |s| + build_lib "rack_middleware", "1.0", path: bundled_app("rack_middleware") do |s| s.add_dependency "rack", "1.0" # anything other than 0.9.1 end end @@ -328,83 +358,69 @@ RSpec.describe "bundle install from an existing gemspec" do context "with a lockfile and some missing dependencies" do let(:source_uri) { "http://localgemserver.test" } - context "previously bundled for Ruby" do - let(:platform) { "ruby" } - - before do - skip "not installing for some reason" if Gem.win_platform? - - build_lib("foo", :path => tmp.join("foo")) do |s| - s.add_dependency "rack", "=1.0.0" - end - - gemfile <<-G - source "#{source_uri}" - gemspec :path => "../foo" - G + before do + build_lib("foo", path: tmp.join("foo")) do |s| + s.add_dependency "rack", "=1.0.0" + end - lockfile <<-L - PATH - remote: ../foo - specs: - foo (1.0) - rack (= 1.0.0) + gemfile <<-G + source "#{source_uri}" + gemspec :path => "../foo" + G - GEM - remote: #{source_uri} - specs: - rack (1.0.0) + checksums = checksums_section_when_existing do |c| + c.no_checksum "foo", "1.0" + end - PLATFORMS - #{generic_local_platform} + lockfile <<-L + PATH + remote: ../foo + specs: + foo (1.0) + rack (= 1.0.0) - DEPENDENCIES - foo! + GEM + remote: #{source_uri} + specs: + rack (1.0.0) - BUNDLED WITH - #{Bundler::VERSION} - L - end + PLATFORMS + #{generic_local_platform} - context "using JRuby with explicit platform", :jruby do - before do - create_file( - tmp.join("foo", "foo-java.gemspec"), - build_spec("foo", "1.0", "java") do - dep "rack", "=1.0.0" - @spec.authors = "authors" - @spec.summary = "summary" - end.first.to_ruby - ) - end + DEPENDENCIES + foo! + #{checksums} + BUNDLED WITH + #{Bundler::VERSION} + L + end - it "should install" do - results = bundle "install", :artifice => "endpoint" - expect(results).to include("Installing rack 1.0.0") - expect(the_bundle).to include_gems "rack 1.0.0" - end + context "using JRuby with explicit platform", :jruby_only do + before do + create_file( + tmp.join("foo", "foo-java.gemspec"), + build_spec("foo", "1.0", "java") do + dep "rack", "=1.0.0" + @spec.authors = "authors" + @spec.summary = "summary" + end.first.to_ruby + ) end - context "using JRuby", :jruby do - it "should install" do - results = bundle "install", :artifice => "endpoint" - expect(results).to include("Installing rack 1.0.0") - expect(the_bundle).to include_gems "rack 1.0.0" - end + it "should install" do + results = bundle "install", artifice: "endpoint" + expect(results).to include("Installing rack 1.0.0") + expect(the_bundle).to include_gems "rack 1.0.0" end + end - context "using Windows" do - it "should install" do - simulate_windows do - results = bundle "install", :artifice => "endpoint" - expect(results).to include("Installing rack 1.0.0") - expect(the_bundle).to include_gems "rack 1.0.0" - end - end - end + it "should install", :jruby do + results = bundle "install", artifice: "endpoint" + expect(results).to include("Installing rack 1.0.0") + expect(the_bundle).to include_gems "rack 1.0.0" end - context "bundled for ruby and jruby" do + context "bundled for multiple platforms" do let(:platform_specific_type) { :runtime } let(:dependency) { "platform_specific" } before do @@ -414,7 +430,7 @@ RSpec.describe "bundle install from an existing gemspec" do end end - build_lib "foo", :path => bundled_app do |s| + build_lib "foo", path: bundled_app do |s| if platform_specific_type == :runtime s.add_runtime_dependency dependency elsif platform_specific_type == :development @@ -434,6 +450,7 @@ RSpec.describe "bundle install from an existing gemspec" do simulate_new_machine simulate_platform("jruby") { bundle "install" } + simulate_platform(x64_mingw32) { bundle "install" } end context "on ruby" do @@ -443,9 +460,17 @@ RSpec.describe "bundle install from an existing gemspec" do end context "as a runtime dependency" do - it "keeps java dependencies in the lockfile" do + it "keeps all platform dependencies in the lockfile" do expect(the_bundle).to include_gems "foo 1.0", "platform_specific 1.0 RUBY" - expect(lockfile).to eq strip_whitespace(<<-L) + + checksums = checksums_section_when_existing do |c| + c.no_checksum "foo", "1.0" + c.checksum gem_repo2, "platform_specific", "1.0" + c.checksum gem_repo2, "platform_specific", "1.0", "java" + x64_mingw_checksums(c) + end + + expect(lockfile).to eq <<~L PATH remote: . specs: @@ -457,14 +482,16 @@ RSpec.describe "bundle install from an existing gemspec" do specs: platform_specific (1.0) platform_specific (1.0-java) + #{x64_mingw_gems} PLATFORMS java ruby + #{x64_mingw_platforms} DEPENDENCIES foo! - + #{checksums} BUNDLED WITH #{Bundler::VERSION} L @@ -474,9 +501,17 @@ RSpec.describe "bundle install from an existing gemspec" do context "as a development dependency" do let(:platform_specific_type) { :development } - it "keeps java dependencies in the lockfile" do + it "keeps all platform dependencies in the lockfile" do expect(the_bundle).to include_gems "foo 1.0", "platform_specific 1.0 RUBY" - expect(lockfile).to eq strip_whitespace(<<-L) + + checksums = checksums_section_when_existing do |c| + c.no_checksum "foo", "1.0" + c.checksum gem_repo2, "platform_specific", "1.0" + c.checksum gem_repo2, "platform_specific", "1.0", "java" + x64_mingw_checksums(c) + end + + expect(lockfile).to eq <<~L PATH remote: . specs: @@ -487,15 +522,17 @@ RSpec.describe "bundle install from an existing gemspec" do specs: platform_specific (1.0) platform_specific (1.0-java) + #{x64_mingw_gems} PLATFORMS java ruby + #{x64_mingw_platforms} DEPENDENCIES foo! platform_specific - + #{checksums} BUNDLED WITH #{Bundler::VERSION} L @@ -506,9 +543,18 @@ RSpec.describe "bundle install from an existing gemspec" do let(:platform_specific_type) { :development } let(:dependency) { "indirect_platform_specific" } - it "keeps java dependencies in the lockfile" do + it "keeps all platform dependencies in the lockfile" do expect(the_bundle).to include_gems "foo 1.0", "indirect_platform_specific 1.0", "platform_specific 1.0 RUBY" - expect(lockfile).to eq strip_whitespace(<<-L) + + checksums = checksums_section_when_existing do |c| + c.no_checksum "foo", "1.0" + c.checksum gem_repo2, "indirect_platform_specific", "1.0" + c.checksum gem_repo2, "platform_specific", "1.0" + c.checksum gem_repo2, "platform_specific", "1.0", "java" + x64_mingw_checksums(c) + end + + expect(lockfile).to eq <<~L PATH remote: . specs: @@ -521,15 +567,17 @@ RSpec.describe "bundle install from an existing gemspec" do platform_specific platform_specific (1.0) platform_specific (1.0-java) + #{x64_mingw_gems} PLATFORMS java ruby + #{x64_mingw_platforms} DEPENDENCIES foo! indirect_platform_specific - + #{checksums} BUNDLED WITH #{Bundler::VERSION} L @@ -541,7 +589,7 @@ RSpec.describe "bundle install from an existing gemspec" do context "with multiple platforms" do before do - build_lib("foo", :path => tmp.join("foo")) do |s| + build_lib("foo", path: tmp.join("foo")) do |s| s.version = "1.0.0" s.add_development_dependency "rack" s.write "foo-universal-java.gemspec", build_spec("foo", "1.0.0", "universal-java") {|sj| sj.runtime "rack", "1.0.0" }.first.to_ruby @@ -575,7 +623,7 @@ RSpec.describe "bundle install from an existing gemspec" do context "with multiple platforms and resolving for more specific platforms" do before do - build_lib("chef", :path => tmp.join("chef")) do |s| + build_lib("chef", path: tmp.join("chef")) do |s| s.version = "17.1.17" s.write "chef-universal-mingw32.gemspec", build_spec("chef", "17.1.17", "universal-mingw32") {|sw| sw.runtime "win32-api", "~> 1.5.3" }.first.to_ruby end @@ -593,6 +641,12 @@ RSpec.describe "bundle install from an existing gemspec" do gemspec :path => "../chef" G + checksums = checksums_section_when_existing do |c| + c.no_checksum "chef", "17.1.17" + c.no_checksum "chef", "17.1.17", "universal-mingw32" + c.checksum gem_repo4, "win32-api", "1.5.3", "universal-mingw32" + end + initial_lockfile = <<~L PATH remote: ../chef @@ -608,12 +662,12 @@ RSpec.describe "bundle install from an existing gemspec" do PLATFORMS ruby - x64-mingw32 + #{x64_mingw_platforms} x86-mingw32 DEPENDENCIES chef! - + #{checksums} BUNDLED WITH #{Bundler::VERSION} L @@ -628,7 +682,7 @@ RSpec.describe "bundle install from an existing gemspec" do context "with multiple locked platforms" do before do - build_lib("activeadmin", :path => tmp.join("activeadmin")) do |s| + build_lib("activeadmin", path: tmp.join("activeadmin")) do |s| s.version = "2.9.0" s.add_dependency "railties", ">= 5.2", "< 6.2" end @@ -651,6 +705,12 @@ RSpec.describe "bundle install from an existing gemspec" do end it "does not remove the platform specific specs from the lockfile when re-resolving due to gemspec changes" do + checksums = checksums_section_when_existing do |c| + c.no_checksum "activeadmin", "2.9.0" + c.no_checksum "jruby-openssl", "0.10.7", "java" + c.checksum gem_repo4, "railties", "6.1.4" + end + expect(lockfile).to eq <<~L PATH remote: ../activeadmin @@ -665,12 +725,12 @@ RSpec.describe "bundle install from an existing gemspec" do railties (6.1.4) PLATFORMS - #{lockfile_platforms_for(["java"] + local_platforms)} + #{lockfile_platforms("java")} DEPENDENCIES activeadmin! jruby-openssl - + #{checksums} BUNDLED WITH #{Bundler::VERSION} L diff --git a/spec/bundler/install/gemfile/git_spec.rb b/spec/bundler/install/gemfile/git_spec.rb index 07995d013b..45ee7b44d1 100644 --- a/spec/bundler/install/gemfile/git_spec.rb +++ b/spec/bundler/install/gemfile/git_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true RSpec.describe "bundle install with git sources" do - describe "when floating on master" do + describe "when floating on main" do before :each do build_git "foo" do |s| s.executables = "foobar" @@ -26,15 +26,22 @@ RSpec.describe "bundle install with git sources" do expect(out).to eq("WIN") end - it "caches the git repo", :bundler => "< 3" do - expect(Dir["#{default_bundle_path}/cache/bundler/git/foo-1.0-*"]).to have_attributes :size => 1 + it "caches the git repo", bundler: "< 3" do + expect(Dir["#{default_bundle_path}/cache/bundler/git/foo-1.0-*"]).to have_attributes size: 1 + end + + it "does not write to cache on bundler/setup" do + cache_path = default_bundle_path.join("cache") + FileUtils.rm_rf(cache_path) + ruby "require 'bundler/setup'" + expect(cache_path).not_to exist end it "caches the git repo globally and properly uses the cached repo on the next invocation" do simulate_new_machine bundle "config set global_gem_cache true" bundle :install - expect(Dir["#{home}/.bundle/cache/git/foo-1.0-*"]).to have_attributes :size => 1 + expect(Dir["#{home}/.bundle/cache/git/foo-1.0-*"]).to have_attributes size: 1 bundle "install --verbose" expect(err).to be_empty @@ -51,9 +58,10 @@ RSpec.describe "bundle install with git sources" do bundle "update foo" - sha = git.ref_for("master", 11) - spec_file = default_bundle_path.join("bundler/gems/foo-1.0-#{sha}/foo.gemspec").to_s - ruby_code = Gem::Specification.load(spec_file).to_ruby + sha = git.ref_for("main", 11) + spec_file = default_bundle_path.join("bundler/gems/foo-1.0-#{sha}/foo.gemspec") + expect(spec_file).to exist + ruby_code = Gem::Specification.load(spec_file.to_s).to_ruby file_code = File.read(spec_file) expect(file_code).to eq(ruby_code) end @@ -61,7 +69,7 @@ RSpec.describe "bundle install with git sources" do it "does not update the git source implicitly" do update_git "foo" - install_gemfile bundled_app2("Gemfile"), <<-G, :dir => bundled_app2 + install_gemfile bundled_app2("Gemfile"), <<-G, dir: bundled_app2 source "#{file_uri_for(gem_repo1)}" git "#{lib_path("foo-1.0")}" do gem 'foo' @@ -84,7 +92,7 @@ RSpec.describe "bundle install with git sources" do it "complains if pinned specs don't exist in the git repo" do build_git "foo" - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G, raise_on_error: false source "#{file_uri_for(gem_repo1)}" gem "foo", "1.1", :git => "#{lib_path("foo-1.0")}" G @@ -92,12 +100,12 @@ RSpec.describe "bundle install with git sources" do expect(err).to include("The source contains the following gems matching 'foo':\n * foo-1.0") end - it "complains with version and platform if pinned specs don't exist in the git repo", :jruby do + it "complains with version and platform if pinned specs don't exist in the git repo", :jruby_only do build_git "only_java" do |s| s.platform = "java" end - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G, raise_on_error: false source "#{file_uri_for(gem_repo1)}" platforms :jruby do gem "only_java", "1.2", :git => "#{lib_path("only_java-1.0-java")}" @@ -107,7 +115,7 @@ RSpec.describe "bundle install with git sources" do expect(err).to include("The source contains the following gems matching 'only_java':\n * only_java-1.0-java") end - it "complains with multiple versions and platforms if pinned specs don't exist in the git repo", :jruby do + it "complains with multiple versions and platforms if pinned specs don't exist in the git repo", :jruby_only do build_git "only_java", "1.0" do |s| s.platform = "java" end @@ -117,7 +125,7 @@ RSpec.describe "bundle install with git sources" do s.write "only_java1-0.gemspec", File.read("#{lib_path("only_java-1.0-java")}/only_java.gemspec") end - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G, raise_on_error: false source "#{file_uri_for(gem_repo1)}" platforms :jruby do gem "only_java", "1.2", :git => "#{lib_path("only_java-1.1-java")}" @@ -133,7 +141,7 @@ RSpec.describe "bundle install with git sources" do FileUtils.mv bundled_app, tmp("bundled_app.bck") - expect(the_bundle).to include_gems "foo 1.0", :dir => tmp("bundled_app.bck") + expect(the_bundle).to include_gems "foo 1.0", dir: tmp("bundled_app.bck") end it "can still install after moving the application directory" do @@ -142,7 +150,7 @@ RSpec.describe "bundle install with git sources" do FileUtils.mv bundled_app, tmp("bundled_app.bck") - update_git "foo", "1.1", :path => lib_path("foo-1.0") + update_git "foo", "1.1", path: lib_path("foo-1.0") gemfile tmp("bundled_app.bck/Gemfile"), <<-G source "#{file_uri_for(gem_repo1)}" @@ -153,9 +161,9 @@ RSpec.describe "bundle install with git sources" do gem "rack", "1.0" G - bundle "update foo", :dir => tmp("bundled_app.bck") + bundle "update foo", dir: tmp("bundled_app.bck") - expect(the_bundle).to include_gems "foo 1.1", "rack 1.0", :dir => tmp("bundled_app.bck") + expect(the_bundle).to include_gems "foo 1.1", "rack 1.0", dir: tmp("bundled_app.bck") end end @@ -192,6 +200,7 @@ RSpec.describe "bundle install with git sources" do gem "foo" end G + expect(err).to be_empty run <<-RUBY require 'foo' @@ -218,16 +227,55 @@ RSpec.describe "bundle install with git sources" do expect(out).to eq("WIN") end + it "works when an abbreviated revision is added after an initial, potentially shallow clone" do + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + git "#{lib_path("foo-1.0")}" do + gem "foo" + end + G + + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + git "#{lib_path("foo-1.0")}", :ref => #{@revision[0..7].inspect} do + gem "foo" + end + G + end + + it "works when a tag that does not look like a commit hash is used as the value of :ref" do + build_git "foo" + @remote = build_git("bar", bare: true) + update_git "foo", remote: file_uri_for(@remote.path) + update_git "foo", push: "main" + + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem 'foo', :git => "#{@remote.path}" + G + + # Create a new tag on the remote that needs fetching + update_git "foo", tag: "v1.0.0" + update_git "foo", push: "v1.0.0" + + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem 'foo', :git => "#{@remote.path}", :ref => "v1.0.0" + G + + expect(err).to be_empty + end + it "works when the revision is a non-head ref" do - # want to ensure we don't fallback to master - update_git "foo", :path => lib_path("foo-1.0") do |s| + # want to ensure we don't fallback to main + update_git "foo", path: lib_path("foo-1.0") do |s| s.write("lib/foo.rb", "raise 'FAIL'") end - sys_exec("git update-ref -m \"Bundler Spec!\" refs/bundler/1 master~1", :dir => lib_path("foo-1.0")) + sys_exec("git update-ref -m \"Bundler Spec!\" refs/bundler/1 main~1", dir: lib_path("foo-1.0")) # want to ensure we don't fallback to HEAD - update_git "foo", :path => lib_path("foo-1.0"), :branch => "rando" do |s| + update_git "foo", path: lib_path("foo-1.0"), branch: "rando" do |s| s.write("lib/foo.rb", "raise 'FAIL_FROM_RANDO'") end @@ -255,15 +303,15 @@ RSpec.describe "bundle install with git sources" do end G - # want to ensure we don't fallback to master - update_git "foo", :path => lib_path("foo-1.0") do |s| + # want to ensure we don't fallback to main + update_git "foo", path: lib_path("foo-1.0") do |s| s.write("lib/foo.rb", "raise 'FAIL'") end - sys_exec("git update-ref -m \"Bundler Spec!\" refs/bundler/1 master~1", :dir => lib_path("foo-1.0")) + sys_exec("git update-ref -m \"Bundler Spec!\" refs/bundler/1 main~1", dir: lib_path("foo-1.0")) # want to ensure we don't fallback to HEAD - update_git "foo", :path => lib_path("foo-1.0"), :branch => "rando" do |s| + update_git "foo", path: lib_path("foo-1.0"), branch: "rando" do |s| s.write("lib/foo.rb", "raise 'FAIL_FROM_RANDO'") end @@ -284,7 +332,7 @@ RSpec.describe "bundle install with git sources" do end it "does not download random non-head refs" do - sys_exec("git update-ref -m \"Bundler Spec!\" refs/bundler/1 master~1", :dir => lib_path("foo-1.0")) + sys_exec("git update-ref -m \"Bundler Spec!\" refs/bundler/1 main~1", dir: lib_path("foo-1.0")) bundle "config set global_gem_cache true" @@ -296,9 +344,9 @@ RSpec.describe "bundle install with git sources" do G # ensure we also git fetch after cloning - bundle :update, :all => true + bundle :update, all: true - sys_exec("git ls-remote .", :dir => Dir[home(".bundle/cache/git/foo-*")].first) + sys_exec("git ls-remote .", dir: Dir[home(".bundle/cache/git/foo-*")].first) expect(out).not_to include("refs/bundler/1") end @@ -309,7 +357,7 @@ RSpec.describe "bundle install with git sources" do let(:repo) { build_git("foo").path } it "works" do - update_git("foo", :path => repo, :branch => branch) + update_git("foo", path: repo, branch: branch) install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" @@ -326,7 +374,7 @@ RSpec.describe "bundle install with git sources" do it "works" do skip "git does not accept this" if Gem.win_platform? - update_git("foo", :path => repo, :branch => branch) + update_git("foo", path: repo, branch: branch) install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" @@ -344,7 +392,7 @@ RSpec.describe "bundle install with git sources" do it "works" do skip "git does not accept this" if Gem.win_platform? - update_git("foo", :path => repo, :branch => branch) + update_git("foo", path: repo, branch: branch) install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" @@ -363,7 +411,7 @@ RSpec.describe "bundle install with git sources" do let(:repo) { build_git("foo").path } it "works" do - update_git("foo", :path => repo, :tag => tag) + update_git("foo", path: repo, tag: tag) install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" @@ -380,7 +428,7 @@ RSpec.describe "bundle install with git sources" do it "works" do skip "git does not accept this" if Gem.win_platform? - update_git("foo", :path => repo, :tag => tag) + update_git("foo", path: repo, tag: tag) install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" @@ -398,7 +446,7 @@ RSpec.describe "bundle install with git sources" do it "works" do skip "git does not accept this" if Gem.win_platform? - update_git("foo", :path => repo, :tag => tag) + update_git("foo", path: repo, tag: tag) install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" @@ -414,13 +462,13 @@ RSpec.describe "bundle install with git sources" do describe "when specifying local override" do it "uses the local repository instead of checking a new one out" do - build_git "rack", "0.8", :path => lib_path("local-rack") do |s| + build_git "rack", "0.8", path: lib_path("local-rack") do |s| s.write "lib/rack.rb", "puts :LOCAL" end gemfile <<-G source "#{file_uri_for(gem_repo1)}" - gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master" + gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "main" G bundle %(config set local.rack #{lib_path("local-rack")}) @@ -435,13 +483,13 @@ RSpec.describe "bundle install with git sources" do FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack")) - update_git "rack", "0.8", :path => lib_path("local-rack") do |s| + update_git "rack", "0.8", path: lib_path("local-rack") do |s| s.write "lib/rack.rb", "puts :LOCAL" end install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" - gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master" + gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "main" G bundle %(config set local.rack #{lib_path("local-rack")}) @@ -454,14 +502,14 @@ RSpec.describe "bundle install with git sources" do FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack")) - update_git "rack", "0.8", :path => lib_path("local-rack") do |s| + update_git "rack", "0.8", path: lib_path("local-rack") do |s| s.write "rack.gemspec", build_spec("rack", "0.8") { runtime "rspec", "> 0" }.first.to_ruby s.write "lib/rack.rb", "puts :LOCAL" end install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" - gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master" + gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "main" G bundle %(config set local.rack #{lib_path("local-rack")}) @@ -477,13 +525,13 @@ RSpec.describe "bundle install with git sources" do install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" - gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master" + gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "main" G lockfile0 = File.read(bundled_app_lock) FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack")) - update_git "rack", "0.8", :path => lib_path("local-rack") do |s| + update_git "rack", "0.8", path: lib_path("local-rack") do |s| s.add_dependency "nokogiri", "1.4.2" end @@ -499,13 +547,13 @@ RSpec.describe "bundle install with git sources" do install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" - gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master" + gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "main" G lockfile0 = File.read(bundled_app_lock) FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack")) - update_git "rack", "0.8", :path => lib_path("local-rack") + update_git "rack", "0.8", path: lib_path("local-rack") bundle %(config set local.rack #{lib_path("local-rack")}) bundle :install @@ -519,12 +567,12 @@ RSpec.describe "bundle install with git sources" do install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" - gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master" + gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "main" G bundle %(config set local.rack #{lib_path("local-rack")}) - bundle :install, :raise_on_error => false - expect(err).to match(/Cannot use local override for rack-0.8 because #{Regexp.escape(lib_path('local-rack').to_s)} does not exist/) + bundle :install, raise_on_error: false + expect(err).to match(/Cannot use local override for rack-0.8 because #{Regexp.escape(lib_path("local-rack").to_s)} does not exist/) solution = "config unset local.rack" expect(err).to match(/Run `bundle #{solution}` to remove the local override/) @@ -545,8 +593,8 @@ RSpec.describe "bundle install with git sources" do G bundle %(config set local.rack #{lib_path("local-rack")}) - bundle :install, :raise_on_error => false - expect(err).to match(/Cannot use local override for rack-0.8 at #{Regexp.escape(lib_path('local-rack').to_s)} because :branch is not specified in Gemfile/) + bundle :install, raise_on_error: false + expect(err).to match(/Cannot use local override for rack-0.8 at #{Regexp.escape(lib_path("local-rack").to_s)} because :branch is not specified in Gemfile/) solution = "config unset local.rack" expect(err).to match(/Specify a branch or run `bundle #{solution}` to remove the local override/) @@ -577,47 +625,47 @@ RSpec.describe "bundle install with git sources" do FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack")) - update_git "rack", "0.8", :path => lib_path("local-rack"), :branch => "another" do |s| + update_git "rack", "0.8", path: lib_path("local-rack"), branch: "another" do |s| s.write "lib/rack.rb", "puts :LOCAL" end install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" - gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master" + gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "main" G bundle %(config set local.rack #{lib_path("local-rack")}) - bundle :install, :raise_on_error => false - expect(err).to match(/is using branch another but Gemfile specifies master/) + bundle :install, raise_on_error: false + expect(err).to match(/is using branch another but Gemfile specifies main/) end it "explodes on invalid revision on install" do build_git "rack", "0.8" - build_git "rack", "0.8", :path => lib_path("local-rack") do |s| + build_git "rack", "0.8", path: lib_path("local-rack") do |s| s.write "lib/rack.rb", "puts :LOCAL" end install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" - gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master" + gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "main" G bundle %(config set local.rack #{lib_path("local-rack")}) - bundle :install, :raise_on_error => false + bundle :install, raise_on_error: false expect(err).to match(/The Gemfile lock is pointing to revision \w+/) end it "does not explode on invalid revision on install" do build_git "rack", "0.8" - build_git "rack", "0.8", :path => lib_path("local-rack") do |s| + build_git "rack", "0.8", path: lib_path("local-rack") do |s| s.write "lib/rack.rb", "puts :LOCAL" end install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" - gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master" + gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "main" G bundle %(config set local.rack #{lib_path("local-rack")}) @@ -658,11 +706,11 @@ RSpec.describe "bundle install with git sources" do it "installs dependencies from git even if a newer gem is available elsewhere" do system_gems "rack-1.0.0" - build_lib "rack", "1.0", :path => lib_path("nested/bar") do |s| + build_lib "rack", "1.0", path: lib_path("nested/bar") do |s| s.write "lib/rack.rb", "puts 'WIN OVERRIDE'" end - build_git "foo", :path => lib_path("nested") do |s| + build_git "foo", path: lib_path("nested") do |s| s.add_dependency "rack", "= 1.0" end @@ -681,7 +729,7 @@ RSpec.describe "bundle install with git sources" do gem "rack", "0.9.1" G - build_git "rack", :path => lib_path("rack") + build_git "rack", path: lib_path("rack") install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" @@ -697,7 +745,7 @@ RSpec.describe "bundle install with git sources" do gem "rack" G - build_git "rack", "1.2", :path => lib_path("rack") + build_git "rack", "1.2", path: lib_path("rack") install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" @@ -710,8 +758,8 @@ RSpec.describe "bundle install with git sources" do describe "block syntax" do it "pulls all gems from a git block" do - build_lib "omg", :path => lib_path("hi2u/omg") - build_lib "hi2u", :path => lib_path("hi2u") + build_lib "omg", path: lib_path("hi2u/omg") + build_lib "hi2u", path: lib_path("hi2u") install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" @@ -759,7 +807,7 @@ RSpec.describe "bundle install with git sources" do end it "runs the gemspec in the context of its parent directory" do - build_lib "bar", :path => lib_path("foo/bar"), :gemspec => false do |s| + build_lib "bar", path: lib_path("foo/bar"), gemspec: false do |s| s.write lib_path("foo/bar/lib/version.rb"), %(BAR_VERSION = '1.0') s.write "bar.gemspec", <<-G $:.unshift Dir.pwd @@ -774,7 +822,7 @@ RSpec.describe "bundle install with git sources" do G end - build_git "foo", :path => lib_path("foo") do |s| + build_git "foo", path: lib_path("foo") do |s| s.write "bin/foo", "" end @@ -789,7 +837,7 @@ RSpec.describe "bundle install with git sources" do end it "installs from git even if a rubygems gem is present" do - build_gem "foo", "1.0", :path => lib_path("fake_foo"), :to_system => true do |s| + build_gem "foo", "1.0", path: lib_path("fake_foo"), to_system: true do |s| s.write "lib/foo.rb", "raise 'FAIL'" end @@ -804,7 +852,7 @@ RSpec.describe "bundle install with git sources" do end it "fakes the gem out if there is no gemspec" do - build_git "foo", :gemspec => false + build_git "foo", gemspec: false install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" @@ -822,7 +870,7 @@ RSpec.describe "bundle install with git sources" do gem "foo", "1.0", :git => "omgomg" G - bundle :install, :raise_on_error => false + bundle :install, raise_on_error: false expect(err).to include("Git error:") expect(err).to include("fatal") @@ -830,7 +878,7 @@ RSpec.describe "bundle install with git sources" do end it "works when the gem path has spaces in it" do - build_git "foo", :path => lib_path("foo space-1.0") + build_git "foo", path: lib_path("foo space-1.0") install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" @@ -855,41 +903,47 @@ RSpec.describe "bundle install with git sources" do s.write "lib/forced.rb", "FORCED = '1.1'" end - bundle "update", :all => true + bundle "update", all: true expect(the_bundle).to include_gems "forced 1.1" - sys_exec("git reset --hard HEAD^", :dir => lib_path("forced-1.0")) + sys_exec("git reset --hard HEAD^", dir: lib_path("forced-1.0")) - bundle "update", :all => true + bundle "update", all: true expect(the_bundle).to include_gems "forced 1.0" end it "ignores submodules if :submodule is not passed" do + # CVE-2022-39253: https://lore.kernel.org/lkml/xmqq4jw1uku5.fsf@gitster.g/ + system(*%W[git config --global protocol.file.allow always]) + build_git "submodule", "1.0" build_git "has_submodule", "1.0" do |s| s.add_dependency "submodule" end - sys_exec "git submodule add #{lib_path("submodule-1.0")} submodule-1.0", :dir => lib_path("has_submodule-1.0") - sys_exec "git commit -m \"submodulator\"", :dir => lib_path("has_submodule-1.0") + sys_exec "git submodule add #{lib_path("submodule-1.0")} submodule-1.0", dir: lib_path("has_submodule-1.0") + sys_exec "git commit -m \"submodulator\"", dir: lib_path("has_submodule-1.0") - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G, raise_on_error: false source "#{file_uri_for(gem_repo1)}" git "#{lib_path("has_submodule-1.0")}" do gem "has_submodule" end G - expect(err).to match(/could not find gem 'submodule/i) + expect(err).to match(%r{submodule >= 0 could not be found in rubygems repository #{file_uri_for(gem_repo1)}/, cached gems or installed locally}) expect(the_bundle).not_to include_gems "has_submodule 1.0" end it "handles repos with submodules" do + # CVE-2022-39253: https://lore.kernel.org/lkml/xmqq4jw1uku5.fsf@gitster.g/ + system(*%W[git config --global protocol.file.allow always]) + build_git "submodule", "1.0" build_git "has_submodule", "1.0" do |s| s.add_dependency "submodule" end - sys_exec "git submodule add #{lib_path("submodule-1.0")} submodule-1.0", :dir => lib_path("has_submodule-1.0") - sys_exec "git commit -m \"submodulator\"", :dir => lib_path("has_submodule-1.0") + sys_exec "git submodule add #{lib_path("submodule-1.0")} submodule-1.0", dir: lib_path("has_submodule-1.0") + sys_exec "git commit -m \"submodulator\"", dir: lib_path("has_submodule-1.0") install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" @@ -902,11 +956,14 @@ RSpec.describe "bundle install with git sources" do end it "does not warn when deiniting submodules" do + # CVE-2022-39253: https://lore.kernel.org/lkml/xmqq4jw1uku5.fsf@gitster.g/ + system(*%W[git config --global protocol.file.allow always]) + build_git "submodule", "1.0" build_git "has_submodule", "1.0" - sys_exec "git submodule add #{lib_path("submodule-1.0")} submodule-1.0", :dir => lib_path("has_submodule-1.0") - sys_exec "git commit -m \"submodulator\"", :dir => lib_path("has_submodule-1.0") + sys_exec "git submodule add #{lib_path("submodule-1.0")} submodule-1.0", dir: lib_path("has_submodule-1.0") + sys_exec "git commit -m \"submodulator\"", dir: lib_path("has_submodule-1.0") install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" @@ -980,7 +1037,7 @@ RSpec.describe "bundle install with git sources" do FileUtils.mkdir_p(default_bundle_path) FileUtils.touch(default_bundle_path("bundler")) - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G, raise_on_error: false source "#{file_uri_for(gem_repo1)}" gem "foo", :git => "#{lib_path("foo-1.0")}" G @@ -992,11 +1049,11 @@ RSpec.describe "bundle install with git sources" do end it "does not duplicate git gem sources" do - build_lib "foo", :path => lib_path("nested/foo") - build_lib "bar", :path => lib_path("nested/bar") + build_lib "foo", path: lib_path("nested/foo") + build_lib "bar", path: lib_path("nested/bar") - build_git "foo", :path => lib_path("nested") - build_git "bar", :path => lib_path("nested") + build_git "foo", path: lib_path("nested") + build_git "bar", path: lib_path("nested") install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" @@ -1009,11 +1066,11 @@ RSpec.describe "bundle install with git sources" do describe "switching sources" do it "doesn't explode when switching Path to Git sources" do - build_gem "foo", "1.0", :to_system => true do |s| + build_gem "foo", "1.0", to_system: true do |s| s.write "lib/foo.rb", "raise 'fail'" end - build_lib "foo", "1.0", :path => lib_path("bar/foo") - build_git "bar", "1.0", :path => lib_path("bar") do |s| + build_lib "foo", "1.0", path: lib_path("bar/foo") + build_git "bar", "1.0", path: lib_path("bar") do |s| s.add_dependency "foo" end @@ -1088,17 +1145,28 @@ RSpec.describe "bundle install with git sources" do G expect(out).to_not match(/Revision.*does not exist/) - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G, raise_on_error: false source "#{file_uri_for(gem_repo1)}" gem "foo", :git => "#{file_uri_for(lib_path("foo-1.0"))}", :ref => "deadbeef" G expect(err).to include("Revision deadbeef does not exist in the repository") end + + it "gives a helpful error message when the remote branch no longer exists" do + build_git "foo" + + install_gemfile <<-G, env: { "LANG" => "en" }, raise_on_error: false + source "#{file_uri_for(gem_repo1)}" + gem "foo", :git => "#{file_uri_for(lib_path("foo-1.0"))}", :branch => "deadbeef" + G + + expect(err).to include("Revision deadbeef does not exist in the repository") + end end describe "bundle install with deployment mode configured and git sources" do it "works" do - build_git "valim", :path => lib_path("valim") + build_git "valim", path: lib_path("valim") install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" @@ -1129,7 +1197,7 @@ RSpec.describe "bundle install with git sources" do end bundle :install, - :requires => [lib_path("install_hooks.rb")] + requires: [lib_path("install_hooks.rb")] expect(err_without_deprecations).to eq("Ran pre-install hook: foo-1.0") end @@ -1149,7 +1217,7 @@ RSpec.describe "bundle install with git sources" do end bundle :install, - :requires => [lib_path("install_hooks.rb")] + requires: [lib_path("install_hooks.rb")] expect(err_without_deprecations).to eq("Ran post-install hook: foo-1.0") end @@ -1168,7 +1236,7 @@ RSpec.describe "bundle install with git sources" do H end - bundle :install, :requires => [lib_path("install_hooks.rb")], :raise_on_error => false + bundle :install, requires: [lib_path("install_hooks.rb")], raise_on_error: false expect(err).to include("failed for foo-1.0") end end @@ -1206,8 +1274,8 @@ RSpec.describe "bundle install with git sources" do expect(out).to include(Pathname.glob(default_bundle_path("bundler/gems/extensions/**/foo-1.0-*")).first.to_s) end - it "does not use old extension after ref changes", :ruby_repo do - git_reader = build_git "foo", :no_default => true do |s| + it "does not use old extension after ref changes" do + git_reader = build_git "foo", no_default: true do |s| s.extensions = ["ext/extconf.rb"] s.write "ext/extconf.rb", <<-RUBY require "mkmf" @@ -1224,7 +1292,7 @@ RSpec.describe "bundle install with git sources" do void Init_foo() { rb_define_global_function("foo", &foo, 0); } C end - sys_exec("git commit -m \"commit for iteration #{i}\" ext/foo.c", :dir => git_reader.path) + sys_exec("git commit -m \"commit for iteration #{i}\" ext/foo.c", dir: git_reader.path) git_commit_sha = git_reader.ref_for("HEAD") @@ -1253,7 +1321,7 @@ RSpec.describe "bundle install with git sources" do RUBY end - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G, raise_on_error: false source "#{file_uri_for(gem_repo1)}" gem "foo", :git => "#{lib_path("foo-1.0")}" G @@ -1379,7 +1447,7 @@ In Gemfile: installed_time = out - update_git("foo", :branch => "branch2") + update_git("foo", branch: "branch2") expect(installed_time).to match(/\A\d+\.\d+\z/) @@ -1430,8 +1498,6 @@ In Gemfile: describe "without git installed" do it "prints a better error message when installing" do - build_git "foo" - gemfile <<-G source "#{file_uri_for(gem_repo1)}" @@ -1460,7 +1526,7 @@ In Gemfile: L with_path_as("") do - bundle "install", :raise_on_error => false + bundle "install", raise_on_error: false end expect(err). to include("You need to install git to be able to use gems from git repositories. For help installing git, please refer to GitHub's tutorial at https://help.github.com/articles/set-up-git") @@ -1477,7 +1543,7 @@ In Gemfile: G with_path_as("") do - bundle "update", :all => true, :raise_on_error => false + bundle "update", all: true, raise_on_error: false end expect(err). to include("You need to install git to be able to use gems from git repositories. For help installing git, please refer to GitHub's tutorial at https://help.github.com/articles/set-up-git") @@ -1496,7 +1562,7 @@ In Gemfile: bundle :cache simulate_new_machine - bundle "install", :env => { "PATH" => "" } + bundle "install", env: { "PATH" => "" } expect(out).to_not include("You need to install git to be able to use gems from git repositories.") end end @@ -1514,11 +1580,11 @@ In Gemfile: end it "installs successfully" do - build_git "foo", "1.0", :path => lib_path("foo") + build_git "foo", "1.0", path: lib_path("foo") gemfile <<-G source "#{file_uri_for(gem_repo1)}" - gem "foo", :git => "#{lib_path("foo")}", :branch => "master" + gem "foo", :git => "#{lib_path("foo")}", :branch => "main" G bundle :install @@ -1532,7 +1598,7 @@ In Gemfile: let(:credentials) { "user1:password1" } it "does not display the password" do - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G, raise_on_error: false source "#{file_uri_for(gem_repo1)}" git "https://#{credentials}@github.com/company/private-repo" do gem "foo" @@ -1548,7 +1614,7 @@ In Gemfile: let(:credentials) { "oauth_token" } it "displays the oauth scheme but not the oauth token" do - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G, raise_on_error: false source "#{file_uri_for(gem_repo1)}" git "https://#{credentials}:x-oauth-basic@github.com/company/private-repo" do gem "foo" diff --git a/spec/bundler/install/gemfile/groups_spec.rb b/spec/bundler/install/gemfile/groups_spec.rb index 734e012e84..f7907a9cad 100644 --- a/spec/bundler/install/gemfile/groups_spec.rb +++ b/spec/bundler/install/gemfile/groups_spec.rb @@ -88,32 +88,32 @@ RSpec.describe "bundle install with groups" do it "installs gems in the default group" do bundle "config set --local without emo" bundle :install - expect(the_bundle).to include_gems "rack 1.0.0", :groups => [:default] + expect(the_bundle).to include_gems "rack 1.0.0", groups: [:default] end it "respects global `without` configuration, but does not save it locally" do bundle "config set --global without emo" bundle :install - expect(the_bundle).to include_gems "rack 1.0.0", :groups => [:default] + expect(the_bundle).to include_gems "rack 1.0.0", groups: [:default] bundle "config list" expect(out).not_to include("Set for your local app (#{bundled_app(".bundle/config")}): [:emo]") expect(out).to include("Set for the current user (#{home(".bundle/config")}): [:emo]") end - it "allows running application where groups where configured by a different user", :bundler => "< 3" do + it "allows running application where groups where configured by a different user", bundler: "< 3" do bundle "config set without emo" bundle :install - bundle "exec ruby -e 'puts 42'", :env => { "BUNDLE_USER_HOME" => tmp("new_home").to_s } + bundle "exec ruby -e 'puts 42'", env: { "BUNDLE_USER_HOME" => tmp("new_home").to_s } expect(out).to include("42") end it "does not install gems from the excluded group" do bundle "config set --local without emo" bundle :install - expect(the_bundle).not_to include_gems "activesupport 2.3.5", :groups => [:default] + expect(the_bundle).not_to include_gems "activesupport 2.3.5", groups: [:default] end - it "remembers previous exclusion with `--without`", :bundler => "< 3" do + it "remembers previous exclusion with `--without`", bundler: "< 3" do bundle "install --without emo" expect(the_bundle).not_to include_gems "activesupport 2.3.5" bundle :install @@ -144,7 +144,7 @@ RSpec.describe "bundle install with groups" do bundle "config set --local without emo" bundle :install - expect(the_bundle).to include_gems "activesupport 2.3.2", :groups => [:default] + expect(the_bundle).to include_gems "activesupport 2.3.2", groups: [:default] end it "still works when BUNDLE_WITHOUT is set" do @@ -153,20 +153,20 @@ RSpec.describe "bundle install with groups" do bundle :install expect(out).not_to include("activesupport") - expect(the_bundle).to include_gems "rack 1.0.0", :groups => [:default] - expect(the_bundle).not_to include_gems "activesupport 2.3.5", :groups => [:default] + expect(the_bundle).to include_gems "rack 1.0.0", groups: [:default] + expect(the_bundle).not_to include_gems "activesupport 2.3.5", groups: [:default] ENV["BUNDLE_WITHOUT"] = nil end - it "clears --without when passed an empty list", :bundler => "< 3" do + it "clears --without when passed an empty list", bundler: "< 3" do bundle "install --without emo" bundle "install --without ''" expect(the_bundle).to include_gems "activesupport 2.3.5" end - it "doesn't clear without when nothing is passed", :bundler => "< 3" do + it "doesn't clear without when nothing is passed", bundler: "< 3" do bundle "install --without emo" bundle :install @@ -184,7 +184,7 @@ RSpec.describe "bundle install with groups" do expect(the_bundle).to include_gems "thin 1.0" end - it "installs gems from the previously requested group", :bundler => "< 3" do + it "installs gems from the previously requested group", bundler: "< 3" do bundle "install --with debugging" expect(the_bundle).to include_gems "thin 1.0" bundle :install @@ -198,26 +198,26 @@ RSpec.describe "bundle install with groups" do ENV["BUNDLE_WITH"] = nil end - it "clears --with when passed an empty list", :bundler => "< 3" do + it "clears --with when passed an empty list", bundler: "< 3" do bundle "install --with debugging" bundle "install --with ''" expect(the_bundle).not_to include_gems "thin 1.0" end - it "removes groups from without when passed at --with", :bundler => "< 3" do + it "removes groups from without when passed at --with", bundler: "< 3" do bundle "config set --local without emo" bundle "install --with emo" expect(the_bundle).to include_gems "activesupport 2.3.5" end - it "removes groups from with when passed at --without", :bundler => "< 3" do + it "removes groups from with when passed at --without", bundler: "< 3" do bundle "config set --local with debugging" - bundle "install --without debugging", :raise_on_error => false + bundle "install --without debugging", raise_on_error: false expect(the_bundle).not_to include_gem "thin 1.0" end - it "errors out when passing a group to with and without via CLI flags", :bundler => "< 3" do - bundle "install --with emo debugging --without emo", :raise_on_error => false + it "errors out when passing a group to with and without via CLI flags", bundler: "< 3" do + bundle "install --with emo debugging --without emo", raise_on_error: false expect(last_command).to be_failure expect(err).to include("The offending groups are: emo") end @@ -235,7 +235,7 @@ RSpec.describe "bundle install with groups" do expect(the_bundle).to include_gem "thin 1.0" end - it "can add and remove a group at the same time", :bundler => "< 3" do + it "can add and remove a group at the same time", bundler: "< 3" do bundle "install --with debugging --without emo" expect(the_bundle).to include_gems "thin 1.0" expect(the_bundle).not_to include_gems "activesupport 2.3.5" @@ -349,7 +349,7 @@ RSpec.describe "bundle install with groups" do G ruby <<-R - require "#{entrypoint}" + require "bundler" Bundler.setup :default Bundler.require :default puts RACK @@ -396,7 +396,7 @@ RSpec.describe "bundle install with groups" do it "does not hit the remote a second time" do FileUtils.rm_rf gem_repo2 bundle "config set --local without rack" - bundle :install, :verbose => true + bundle :install, verbose: true expect(last_command.stdboth).not_to match(/fetching/i) end end diff --git a/spec/bundler/install/gemfile/install_if_spec.rb b/spec/bundler/install/gemfile/install_if_spec.rb index 3d2d15a698..c7640d07e1 100644 --- a/spec/bundler/install/gemfile/install_if_spec.rb +++ b/spec/bundler/install/gemfile/install_if_spec.rb @@ -18,6 +18,13 @@ RSpec.describe "bundle install with install_if conditionals" do expect(the_bundle).not_to include_gems("thin") expect(the_bundle).not_to include_gems("foo") + checksums = checksums_section_when_existing do |c| + c.checksum gem_repo1, "activesupport", "2.3.5" + c.no_checksum "foo", "1.0" + c.checksum gem_repo1, "rack", "1.0.0" + c.no_checksum "thin", "1.0" + end + expect(lockfile).to eq <<~L GEM remote: #{file_uri_for(gem_repo1)}/ @@ -36,7 +43,7 @@ RSpec.describe "bundle install with install_if conditionals" do foo rack thin - + #{checksums} BUNDLED WITH #{Bundler::VERSION} L diff --git a/spec/bundler/install/gemfile/lockfile_spec.rb b/spec/bundler/install/gemfile/lockfile_spec.rb index 313e99d0b8..4601d3e2a8 100644 --- a/spec/bundler/install/gemfile/lockfile_spec.rb +++ b/spec/bundler/install/gemfile/lockfile_spec.rb @@ -11,6 +11,11 @@ RSpec.describe "bundle install with a lockfile present" do install_gemfile(gf) end + it "touches the lockfile on install even when nothing has changed" do + subject + expect { bundle :install }.to change { bundled_app_lock.mtime } + end + context "gemfile evaluation" do let(:gf) { super() + "\n\n File.open('evals', 'a') {|f| f << %(1\n) } unless ENV['BUNDLER_SPEC_NO_APPEND']" } diff --git a/spec/bundler/install/gemfile/path_spec.rb b/spec/bundler/install/gemfile/path_spec.rb index 515901064f..a57b7ee560 100644 --- a/spec/bundler/install/gemfile/path_spec.rb +++ b/spec/bundler/install/gemfile/path_spec.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true RSpec.describe "bundle install with explicit source paths" do - it "fetches gems with a global path source", :bundler => "< 3" do + it "fetches gems with a global path source", bundler: "< 3" do build_lib "foo" install_gemfile <<-G @@ -69,7 +69,7 @@ RSpec.describe "bundle install with explicit source paths" do username = "some_unexisting_user" relative_path = lib_path("foo-1.0").relative_path_from(Pathname.new("/home/#{username}").expand_path) - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G, raise_on_error: false source "#{file_uri_for(gem_repo1)}" gem 'foo', :path => "~#{username}/#{relative_path}" G @@ -78,27 +78,30 @@ RSpec.describe "bundle install with explicit source paths" do end it "expands paths relative to Bundler.root" do - build_lib "foo", :path => bundled_app("foo-1.0") + build_lib "foo", path: bundled_app("foo-1.0") install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" gem 'foo', :path => "./foo-1.0" G - expect(the_bundle).to include_gems("foo 1.0", :dir => bundled_app("subdir").mkpath) + expect(the_bundle).to include_gems("foo 1.0", dir: bundled_app("subdir").mkpath) end it "sorts paths consistently on install and update when they start with ./" do - build_lib "demo", :path => lib_path("demo") - build_lib "aaa", :path => lib_path("demo/aaa") + build_lib "demo", path: lib_path("demo") + build_lib "aaa", path: lib_path("demo/aaa") - gemfile = <<-G + gemfile lib_path("demo/Gemfile"), <<-G source "#{file_uri_for(gem_repo1)}" gemspec gem "aaa", :path => "./aaa" G - File.open(lib_path("demo/Gemfile"), "w") {|f| f.puts gemfile } + checksums = checksums_section_when_existing do |c| + c.no_checksum "aaa", "1.0" + c.no_checksum "demo", "1.0" + end lockfile = <<~L PATH @@ -121,19 +124,19 @@ RSpec.describe "bundle install with explicit source paths" do DEPENDENCIES aaa! demo! - + #{checksums} BUNDLED WITH #{Bundler::VERSION} L - bundle :install, :dir => lib_path("demo") + bundle :install, dir: lib_path("demo") expect(lib_path("demo/Gemfile.lock")).to read_as(lockfile) - bundle :update, :all => true, :dir => lib_path("demo") + bundle :update, all: true, dir: lib_path("demo") expect(lib_path("demo/Gemfile.lock")).to read_as(lockfile) end it "expands paths when comparing locked paths to Gemfile paths" do - build_lib "foo", :path => bundled_app("foo-1.0") + build_lib "foo", path: bundled_app("foo-1.0") install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" @@ -147,11 +150,11 @@ RSpec.describe "bundle install with explicit source paths" do it "installs dependencies from the path even if a newer gem is available elsewhere" do system_gems "rack-1.0.0" - build_lib "rack", "1.0", :path => lib_path("nested/bar") do |s| + build_lib "rack", "1.0", path: lib_path("nested/bar") do |s| s.write "lib/rack.rb", "puts 'WIN OVERRIDE'" end - build_lib "foo", :path => lib_path("nested") do |s| + build_lib "foo", path: lib_path("nested") do |s| s.add_dependency "rack", "= 1.0" end @@ -165,15 +168,15 @@ RSpec.describe "bundle install with explicit source paths" do end it "works" do - build_gem "foo", "1.0.0", :to_system => true do |s| + build_gem "foo", "1.0.0", to_system: true do |s| s.write "lib/foo.rb", "puts 'FAIL'" end - build_lib "omg", "1.0", :path => lib_path("omg") do |s| + build_lib "omg", "1.0", path: lib_path("omg") do |s| s.add_dependency "foo" end - build_lib "foo", "1.0.0", :path => lib_path("omg/foo") + build_lib "foo", "1.0.0", path: lib_path("omg/foo") install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" @@ -184,7 +187,7 @@ RSpec.describe "bundle install with explicit source paths" do end it "works when using prereleases of 0.0.0" do - build_lib "foo", "0.0.0.dev", :path => lib_path("foo") + build_lib "foo", "0.0.0.dev", path: lib_path("foo") gemfile <<~G source "#{file_uri_for(gem_repo1)}" @@ -217,7 +220,7 @@ RSpec.describe "bundle install with explicit source paths" do end it "works when using uppercase prereleases of 0.0.0" do - build_lib "foo", "0.0.0.SNAPSHOT", :path => lib_path("foo") + build_lib "foo", "0.0.0.SNAPSHOT", path: lib_path("foo") gemfile <<~G source "#{file_uri_for(gem_repo1)}" @@ -250,14 +253,14 @@ RSpec.describe "bundle install with explicit source paths" do end it "handles downgrades" do - build_lib "omg", "2.0", :path => lib_path("omg") + build_lib "omg", "2.0", path: lib_path("omg") install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" gem "omg", :path => "#{lib_path("omg")}" G - build_lib "omg", "1.0", :path => lib_path("omg") + build_lib "omg", "1.0", path: lib_path("omg") bundle :install @@ -265,7 +268,7 @@ RSpec.describe "bundle install with explicit source paths" do end it "prefers gemspecs closer to the path root" do - build_lib "premailer", "1.0.0", :path => lib_path("premailer") do |s| + build_lib "premailer", "1.0.0", path: lib_path("premailer") do |s| s.write "gemfiles/ruby187.gemspec", <<-G Gem::Specification.new do |s| s.name = 'premailer' @@ -298,7 +301,7 @@ RSpec.describe "bundle install with explicit source paths" do G end - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G, raise_on_error: false source "#{file_uri_for(gem_repo1)}" gem "foo", :path => "#{lib_path("foo-1.0")}" G @@ -310,24 +313,78 @@ RSpec.describe "bundle install with explicit source paths" do end it "supports gemspec syntax" do - build_lib "foo", "1.0", :path => lib_path("foo") do |s| + build_lib "foo", "1.0", path: lib_path("foo") do |s| s.add_dependency "rack", "1.0" end - gemfile = <<-G + gemfile lib_path("foo/Gemfile"), <<-G source "#{file_uri_for(gem_repo1)}" gemspec G - File.open(lib_path("foo/Gemfile"), "w") {|f| f.puts gemfile } + bundle "install", dir: lib_path("foo") + expect(the_bundle).to include_gems "foo 1.0", dir: lib_path("foo") + expect(the_bundle).to include_gems "rack 1.0", dir: lib_path("foo") + end + + it "does not unlock dependencies of path sources" do + build_repo4 do + build_gem "graphql", "2.0.15" + build_gem "graphql", "2.0.16" + end + + build_lib "foo", "0.1.0", path: lib_path("foo") do |s| + s.add_dependency "graphql", "~> 2.0" + end + + gemfile_path = lib_path("foo/Gemfile") + + gemfile gemfile_path, <<-G + source "#{file_uri_for(gem_repo4)}" + gemspec + G + + lockfile_path = lib_path("foo/Gemfile.lock") + + checksums = checksums_section_when_existing do |c| + c.no_checksum "foo", "0.1.0" + c.checksum gem_repo4, "graphql", "2.0.15" + end + + original_lockfile = <<~L + PATH + remote: . + specs: + foo (0.1.0) + graphql (~> 2.0) + + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + graphql (2.0.15) + + PLATFORMS + #{lockfile_platforms} - bundle "install", :dir => lib_path("foo") - expect(the_bundle).to include_gems "foo 1.0", :dir => lib_path("foo") - expect(the_bundle).to include_gems "rack 1.0", :dir => lib_path("foo") + DEPENDENCIES + foo! + #{checksums} + BUNDLED WITH + #{Bundler::VERSION} + L + + lockfile lockfile_path, original_lockfile + + build_lib "foo", "0.1.1", path: lib_path("foo") do |s| + s.add_dependency "graphql", "~> 2.0" + end + + bundle "install", dir: lib_path("foo") + expect(lockfile_path).to read_as(original_lockfile.gsub("foo (0.1.0)", "foo (0.1.1)")) end it "supports gemspec syntax with an alternative path" do - build_lib "foo", "1.0", :path => lib_path("foo") do |s| + build_lib "foo", "1.0", path: lib_path("foo") do |s| s.add_dependency "rack", "1.0" end @@ -341,48 +398,48 @@ RSpec.describe "bundle install with explicit source paths" do end it "doesn't automatically unlock dependencies when using the gemspec syntax" do - build_lib "foo", "1.0", :path => lib_path("foo") do |s| + build_lib "foo", "1.0", path: lib_path("foo") do |s| s.add_dependency "rack", ">= 1.0" end - install_gemfile lib_path("foo/Gemfile"), <<-G, :dir => lib_path("foo") + install_gemfile lib_path("foo/Gemfile"), <<-G, dir: lib_path("foo") source "#{file_uri_for(gem_repo1)}" gemspec G - build_gem "rack", "1.0.1", :to_system => true + build_gem "rack", "1.0.1", to_system: true - bundle "install", :dir => lib_path("foo") + bundle "install", dir: lib_path("foo") - expect(the_bundle).to include_gems "foo 1.0", :dir => lib_path("foo") - expect(the_bundle).to include_gems "rack 1.0", :dir => lib_path("foo") + expect(the_bundle).to include_gems "foo 1.0", dir: lib_path("foo") + expect(the_bundle).to include_gems "rack 1.0", dir: lib_path("foo") end it "doesn't automatically unlock dependencies when using the gemspec syntax and the gem has development dependencies" do - build_lib "foo", "1.0", :path => lib_path("foo") do |s| + build_lib "foo", "1.0", path: lib_path("foo") do |s| s.add_dependency "rack", ">= 1.0" s.add_development_dependency "activesupport" end - install_gemfile lib_path("foo/Gemfile"), <<-G, :dir => lib_path("foo") + install_gemfile lib_path("foo/Gemfile"), <<-G, dir: lib_path("foo") source "#{file_uri_for(gem_repo1)}" gemspec G - build_gem "rack", "1.0.1", :to_system => true + build_gem "rack", "1.0.1", to_system: true - bundle "install", :dir => lib_path("foo") + bundle "install", dir: lib_path("foo") - expect(the_bundle).to include_gems "foo 1.0", :dir => lib_path("foo") - expect(the_bundle).to include_gems "rack 1.0", :dir => lib_path("foo") + expect(the_bundle).to include_gems "foo 1.0", dir: lib_path("foo") + expect(the_bundle).to include_gems "rack 1.0", dir: lib_path("foo") end it "raises if there are multiple gemspecs" do - build_lib "foo", "1.0", :path => lib_path("foo") do |s| + build_lib "foo", "1.0", path: lib_path("foo") do |s| s.write "bar.gemspec", build_spec("bar", "1.0").first.to_ruby end - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G, raise_on_error: false source "#{file_uri_for(gem_repo1)}" gemspec :path => "#{lib_path("foo")}" G @@ -392,7 +449,7 @@ RSpec.describe "bundle install with explicit source paths" do end it "allows :name to be specified to resolve ambiguity" do - build_lib "foo", "1.0", :path => lib_path("foo") do |s| + build_lib "foo", "1.0", path: lib_path("foo") do |s| s.write "bar.gemspec" end @@ -409,7 +466,7 @@ RSpec.describe "bundle install with explicit source paths" do s.executables = "foobar" end - install_gemfile <<-G, :verbose => true + install_gemfile <<-G, verbose: true source "#{file_uri_for(gem_repo1)}" path "#{lib_path("foo-1.0")}" do gem 'foo' @@ -463,9 +520,9 @@ RSpec.describe "bundle install with explicit source paths" do end it "keeps source pinning" do - build_lib "foo", "1.0", :path => lib_path("foo") - build_lib "omg", "1.0", :path => lib_path("omg") - build_lib "foo", "1.0", :path => lib_path("omg/foo") do |s| + build_lib "foo", "1.0", path: lib_path("foo") + build_lib "omg", "1.0", path: lib_path("omg") + build_lib "foo", "1.0", path: lib_path("omg/foo") do |s| s.write "lib/foo.rb", "puts 'FAIL'" end @@ -479,7 +536,7 @@ RSpec.describe "bundle install with explicit source paths" do end it "works when the path does not have a gemspec" do - build_lib "foo", :gemspec => false + build_lib "foo", gemspec: false gemfile <<-G source "#{file_uri_for(gem_repo1)}" @@ -517,17 +574,17 @@ RSpec.describe "bundle install with explicit source paths" do gem 'net-ssh', '1.0' G - bundle :check, :env => { "DEBUG" => "1" } + bundle :check, env: { "DEBUG" => "1" } expect(out).to match(/using resolution from the lockfile/) expect(the_bundle).to include_gems "rack-obama 1.0", "net-ssh 1.0" end it "source path gems w/deps don't re-resolve without changes" do - build_lib "rack-obama", "1.0", :path => lib_path("omg") do |s| + build_lib "rack-obama", "1.0", path: lib_path("omg") do |s| s.add_dependency "yard" end - build_lib "net-ssh", "1.0", :path => lib_path("omg") do |s| + build_lib "net-ssh", "1.0", path: lib_path("omg") do |s| s.add_dependency "yard" end @@ -537,7 +594,7 @@ RSpec.describe "bundle install with explicit source paths" do gem 'net-ssh', :path => "#{lib_path("omg")}" G - bundle :check, :env => { "DEBUG" => "1" } + bundle :check, env: { "DEBUG" => "1" } expect(out).to match(/using resolution from the lockfile/) expect(the_bundle).to include_gems "rack-obama 1.0", "net-ssh 1.0" end @@ -559,10 +616,10 @@ RSpec.describe "bundle install with explicit source paths" do describe "when the gem version in the path is updated" do before :each do - build_lib "foo", "1.0", :path => lib_path("foo") do |s| + build_lib "foo", "1.0", path: lib_path("foo") do |s| s.add_dependency "bar" end - build_lib "bar", "1.0", :path => lib_path("foo/bar") + build_lib "bar", "1.0", path: lib_path("foo/bar") install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" @@ -571,7 +628,7 @@ RSpec.describe "bundle install with explicit source paths" do end it "unlocks all gems when the top level gem is updated" do - build_lib "foo", "2.0", :path => lib_path("foo") do |s| + build_lib "foo", "2.0", path: lib_path("foo") do |s| s.add_dependency "bar" end @@ -581,7 +638,7 @@ RSpec.describe "bundle install with explicit source paths" do end it "unlocks all gems when a child dependency gem is updated" do - build_lib "bar", "2.0", :path => lib_path("foo/bar") + build_lib "bar", "2.0", path: lib_path("foo/bar") bundle "install" @@ -591,7 +648,7 @@ RSpec.describe "bundle install with explicit source paths" do describe "when dependencies in the path are updated" do before :each do - build_lib "foo", "1.0", :path => lib_path("foo") + build_lib "foo", "1.0", path: lib_path("foo") install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" @@ -600,7 +657,7 @@ RSpec.describe "bundle install with explicit source paths" do end it "gets dependencies that are updated in the path" do - build_lib "foo", "1.0", :path => lib_path("foo") do |s| + build_lib "foo", "1.0", path: lib_path("foo") do |s| s.add_dependency "rack" end @@ -610,7 +667,7 @@ RSpec.describe "bundle install with explicit source paths" do end it "keeps using the same version if it's compatible" do - build_lib "foo", "1.0", :path => lib_path("foo") do |s| + build_lib "foo", "1.0", path: lib_path("foo") do |s| s.add_dependency "rack", "0.9.1" end @@ -618,6 +675,11 @@ RSpec.describe "bundle install with explicit source paths" do expect(the_bundle).to include_gems "rack 0.9.1" + checksums = checksums_section_when_existing do |c| + c.no_checksum "foo", "1.0" + c.checksum gem_repo1, "rack", "0.9.1" + end + expect(lockfile).to eq <<~G PATH remote: #{lib_path("foo")} @@ -635,12 +697,12 @@ RSpec.describe "bundle install with explicit source paths" do DEPENDENCIES foo! - + #{checksums} BUNDLED WITH #{Bundler::VERSION} G - build_lib "foo", "1.0", :path => lib_path("foo") do |s| + build_lib "foo", "1.0", path: lib_path("foo") do |s| s.add_dependency "rack" end @@ -663,7 +725,7 @@ RSpec.describe "bundle install with explicit source paths" do DEPENDENCIES foo! - + #{checksums} BUNDLED WITH #{Bundler::VERSION} G @@ -672,7 +734,7 @@ RSpec.describe "bundle install with explicit source paths" do end it "keeps using the same version even when another dependency is added" do - build_lib "foo", "1.0", :path => lib_path("foo") do |s| + build_lib "foo", "1.0", path: lib_path("foo") do |s| s.add_dependency "rack", "0.9.1" end @@ -680,6 +742,11 @@ RSpec.describe "bundle install with explicit source paths" do expect(the_bundle).to include_gems "rack 0.9.1" + checksums = checksums_section_when_existing do |c| + c.no_checksum "foo", "1.0" + c.checksum gem_repo1, "rack", "0.9.1" + end + expect(lockfile).to eq <<~G PATH remote: #{lib_path("foo")} @@ -697,53 +764,107 @@ RSpec.describe "bundle install with explicit source paths" do DEPENDENCIES foo! - + #{checksums} BUNDLED WITH #{Bundler::VERSION} G - build_lib "foo", "1.0", :path => lib_path("foo") do |s| + build_lib "foo", "1.0", path: lib_path("foo") do |s| s.add_dependency "rack" - s.add_dependency "rake", "13.0.1" + s.add_dependency "rake", rake_version end bundle "install" + checksums.checksum gem_repo1, "rake", rake_version + expect(lockfile).to eq <<~G PATH remote: #{lib_path("foo")} specs: foo (1.0) rack - rake (= 13.0.1) + rake (= #{rake_version}) GEM remote: #{file_uri_for(gem_repo1)}/ specs: rack (0.9.1) - rake (13.0.1) + rake (#{rake_version}) PLATFORMS #{lockfile_platforms} DEPENDENCIES foo! - + #{checksums} BUNDLED WITH #{Bundler::VERSION} G expect(the_bundle).to include_gems "rack 0.9.1" end + + it "does not remove existing ruby platform" do + build_lib "foo", "1.0", path: lib_path("foo") do |s| + s.add_dependency "rack", "0.9.1" + end + + checksums = checksums_section_when_existing do |c| + c.no_checksum "foo", "1.0" + end + + lockfile <<~L + PATH + remote: #{lib_path("foo")} + specs: + foo (1.0) + + PLATFORMS + #{lockfile_platforms("ruby")} + + DEPENDENCIES + foo! + #{checksums} + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "lock" + + checksums.no_checksum "rack", "0.9.1" + + expect(lockfile).to eq <<~G + PATH + remote: #{lib_path("foo")} + specs: + foo (1.0) + rack (= 0.9.1) + + GEM + remote: #{file_uri_for(gem_repo1)}/ + specs: + rack (0.9.1) + + PLATFORMS + #{lockfile_platforms("ruby")} + + DEPENDENCIES + foo! + #{checksums} + BUNDLED WITH + #{Bundler::VERSION} + G + end end describe "switching sources" do it "doesn't switch pinned git sources to rubygems when pinning the parent gem to a path source" do - build_gem "foo", "1.0", :to_system => true do |s| + build_gem "foo", "1.0", to_system: true do |s| s.write "lib/foo.rb", "raise 'fail'" end - build_lib "foo", "1.0", :path => lib_path("bar/foo") - build_git "bar", "1.0", :path => lib_path("bar") do |s| + build_lib "foo", "1.0", path: lib_path("bar/foo") + build_git "bar", "1.0", path: lib_path("bar") do |s| s.add_dependency "foo" end @@ -761,8 +882,8 @@ RSpec.describe "bundle install with explicit source paths" do end it "switches the source when the gem existed in rubygems and the path was already being used for another gem" do - build_lib "foo", "1.0", :path => lib_path("foo") - build_gem "bar", "1.0", :to_bundle => true do |s| + build_lib "foo", "1.0", path: lib_path("foo") + build_gem "bar", "1.0", to_bundle: true do |s| s.write "lib/bar.rb", "raise 'fail'" end @@ -774,7 +895,7 @@ RSpec.describe "bundle install with explicit source paths" do end G - build_lib "bar", "1.0", :path => lib_path("foo/bar") + build_lib "bar", "1.0", path: lib_path("foo/bar") install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" @@ -790,19 +911,17 @@ RSpec.describe "bundle install with explicit source paths" do describe "when there are both a gemspec and remote gems" do it "doesn't query rubygems for local gemspec name" do - build_lib "private_lib", "2.2", :path => lib_path("private_lib") - gemfile = <<-G + build_lib "private_lib", "2.2", path: lib_path("private_lib") + gemfile lib_path("private_lib/Gemfile"), <<-G source "http://localgemserver.test" gemspec gem 'rack' G - File.open(lib_path("private_lib/Gemfile"), "w") {|f| f.puts gemfile } - - bundle :install, :env => { "DEBUG" => "1" }, :artifice => "endpoint", :dir => lib_path("private_lib") + bundle :install, env: { "DEBUG" => "1" }, artifice: "endpoint", dir: lib_path("private_lib") expect(out).to match(%r{^HTTP GET http://localgemserver\.test/api/v1/dependencies\?gems=rack$}) expect(out).not_to match(/^HTTP GET.*private_lib/) - expect(the_bundle).to include_gems "private_lib 2.2", :dir => lib_path("private_lib") - expect(the_bundle).to include_gems "rack 1.0", :dir => lib_path("private_lib") + expect(the_bundle).to include_gems "private_lib 2.2", dir: lib_path("private_lib") + expect(the_bundle).to include_gems "rack 1.0", dir: lib_path("private_lib") end end @@ -823,7 +942,7 @@ RSpec.describe "bundle install with explicit source paths" do end bundle :install, - :requires => [lib_path("install_hooks.rb")] + requires: [lib_path("install_hooks.rb")] expect(err_without_deprecations).to eq("Ran pre-install hook: foo-1.0") end @@ -843,7 +962,7 @@ RSpec.describe "bundle install with explicit source paths" do end bundle :install, - :requires => [lib_path("install_hooks.rb")] + requires: [lib_path("install_hooks.rb")] expect(err_without_deprecations).to eq("Ran post-install hook: foo-1.0") end @@ -862,7 +981,7 @@ RSpec.describe "bundle install with explicit source paths" do H end - bundle :install, :requires => [lib_path("install_hooks.rb")], :raise_on_error => false + bundle :install, requires: [lib_path("install_hooks.rb")], raise_on_error: false expect(err).to include("failed for foo-1.0") end diff --git a/spec/bundler/install/gemfile/platform_spec.rb b/spec/bundler/install/gemfile/platform_spec.rb index cf80844b02..d90dacdc02 100644 --- a/spec/bundler/install/gemfile/platform_spec.rb +++ b/spec/bundler/install/gemfile/platform_spec.rb @@ -50,7 +50,7 @@ RSpec.describe "bundle install across platforms" do expect(the_bundle).to include_gems "platform_specific 1.0 JAVA" end - it "pulls the pure ruby version on jruby if the java platform is not present in the lockfile and bundler is run in frozen mode", :jruby do + it "pulls the pure ruby version on jruby if the java platform is not present in the lockfile and bundler is run in frozen mode", :jruby_only do lockfile <<-G GEM remote: #{file_uri_for(gem_repo1)} @@ -75,6 +75,81 @@ RSpec.describe "bundle install across platforms" do expect(the_bundle).to include_gems "platform_specific 1.0 RUBY" end + context "on universal Rubies" do + before do + build_repo4 do + build_gem "darwin_single_arch" do |s| + s.platform = "ruby" + s.write "lib/darwin_single_arch.rb", "DARWIN_SINGLE_ARCH = '1.0 RUBY'" + end + build_gem "darwin_single_arch" do |s| + s.platform = "arm64-darwin" + s.write "lib/darwin_single_arch.rb", "DARWIN_SINGLE_ARCH = '1.0 arm64-darwin'" + end + build_gem "darwin_single_arch" do |s| + s.platform = "x86_64-darwin" + s.write "lib/darwin_single_arch.rb", "DARWIN_SINGLE_ARCH = '1.0 x86_64-darwin'" + end + end + end + + it "pulls in the correct architecture gem" do + lockfile <<-G + GEM + remote: #{file_uri_for(gem_repo4)} + specs: + darwin_single_arch (1.0) + darwin_single_arch (1.0-arm64-darwin) + darwin_single_arch (1.0-x86_64-darwin) + + PLATFORMS + ruby + + DEPENDENCIES + darwin_single_arch + G + + simulate_platform "universal-darwin-21" + simulate_ruby_platform "universal.x86_64-darwin21" do + install_gemfile <<-G + source "#{file_uri_for(gem_repo4)}" + + gem "darwin_single_arch" + G + + expect(the_bundle).to include_gems "darwin_single_arch 1.0 x86_64-darwin" + end + end + + it "pulls in the correct architecture gem on arm64e macOS Ruby" do + lockfile <<-G + GEM + remote: #{file_uri_for(gem_repo4)} + specs: + darwin_single_arch (1.0) + darwin_single_arch (1.0-arm64-darwin) + darwin_single_arch (1.0-x86_64-darwin) + + PLATFORMS + ruby + + DEPENDENCIES + darwin_single_arch + G + + simulate_platform "universal-darwin-21" + simulate_ruby_platform "universal.arm64e-darwin21" do + install_gemfile <<-G + source "#{file_uri_for(gem_repo4)}" + + gem "darwin_single_arch" + G + + expect(the_bundle).to include_gems "darwin_single_arch 1.0 arm64-darwin" + end + end + end + it "works with gems that have different dependencies" do simulate_platform "java" install_gemfile <<-G @@ -128,6 +203,15 @@ RSpec.describe "bundle install across platforms" do gem "pry" G + checksums = checksums_section_when_existing do |c| + c.checksum gem_repo4, "coderay", "1.1.2" + c.checksum gem_repo4, "empyrean", "0.1.0" + c.checksum gem_repo4, "ffi", "1.9.23", "java" + c.checksum gem_repo4, "method_source", "0.9.0" + c.checksum gem_repo4, "pry", "0.11.3", "java" + c.checksum gem_repo4, "spoon", "0.0.6" + end + expect(lockfile).to eq <<~L GEM remote: #{file_uri_for(gem_repo4)}/ @@ -149,7 +233,7 @@ RSpec.describe "bundle install across platforms" do DEPENDENCIES empyrean (= 0.1.0) pry - + #{checksums} BUNDLED WITH #{Bundler::VERSION} L @@ -181,7 +265,7 @@ RSpec.describe "bundle install across platforms" do DEPENDENCIES empyrean (= 0.1.0) pry - + #{checksums} BUNDLED WITH #{Bundler::VERSION} L @@ -214,30 +298,30 @@ RSpec.describe "bundle install across platforms" do DEPENDENCIES empyrean (= 0.1.0) pry - + #{checksums} BUNDLED WITH - #{Bundler::VERSION} + 1.16.1 L aggregate_failures do lockfile bad_lockfile - bundle :install + bundle :install, env: { "BUNDLER_VERSION" => Bundler::VERSION } expect(lockfile).to eq good_lockfile lockfile bad_lockfile - bundle :update, :all => true + bundle :update, all: true, env: { "BUNDLER_VERSION" => Bundler::VERSION } expect(lockfile).to eq good_lockfile lockfile bad_lockfile - bundle "update ffi" + bundle "update ffi", env: { "BUNDLER_VERSION" => Bundler::VERSION } expect(lockfile).to eq good_lockfile lockfile bad_lockfile - bundle "update empyrean" + bundle "update empyrean", env: { "BUNDLER_VERSION" => Bundler::VERSION } expect(lockfile).to eq good_lockfile lockfile bad_lockfile - bundle :lock + bundle :lock, env: { "BUNDLER_VERSION" => Bundler::VERSION } expect(lockfile).to eq good_lockfile end end @@ -288,6 +372,11 @@ RSpec.describe "bundle install across platforms" do end it "keeps existing platforms when installing with force_ruby_platform" do + checksums = checksums_section do |c| + c.no_checksum "platform_specific", "1.0" + c.no_checksum "platform_specific", "1.0", "java" + end + lockfile <<-G GEM remote: #{file_uri_for(gem_repo1)}/ @@ -299,6 +388,7 @@ RSpec.describe "bundle install across platforms" do DEPENDENCIES platform_specific + #{checksums} G bundle "config set --local force_ruby_platform true" @@ -308,6 +398,8 @@ RSpec.describe "bundle install across platforms" do gem "platform_specific" G + checksums.checksum gem_repo1, "platform_specific", "1.0" + expect(the_bundle).to include_gem "platform_specific 1.0 RUBY" expect(lockfile).to eq <<~G @@ -323,7 +415,7 @@ RSpec.describe "bundle install across platforms" do DEPENDENCIES platform_specific - + #{checksums} BUNDLED WITH #{Bundler::VERSION} G @@ -332,8 +424,6 @@ end RSpec.describe "bundle install with platform conditionals" do it "installs gems tagged w/ the current platforms" do - skip "platform issues" if Gem.win_platform? - install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" @@ -386,7 +476,7 @@ RSpec.describe "bundle install with platform conditionals" do tzinfo (2.0.4) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES activesupport @@ -402,8 +492,6 @@ RSpec.describe "bundle install with platform conditionals" do end it "installs gems tagged w/ the current platforms inline" do - skip "platform issues" if Gem.win_platform? - install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" gem "nokogiri", :platforms => :#{local_tag} @@ -422,8 +510,6 @@ RSpec.describe "bundle install with platform conditionals" do end it "installs gems tagged w/ the current platform inline" do - skip "platform issues" if Gem.win_platform? - install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" gem "nokogiri", :platform => :#{local_tag} @@ -466,11 +552,9 @@ RSpec.describe "bundle install with platform conditionals" do it "does not attempt to install gems from other rubies when using --local" do bundle "config set --local force_ruby_platform true" - other_ruby_version_tag = RUBY_VERSION =~ /^1\.8/ ? :ruby_19 : :ruby_18 - gemfile <<-G source "#{file_uri_for(gem_repo1)}" - gem "some_gem", platform: :#{other_ruby_version_tag} + gem "some_gem", platform: :ruby_22 G bundle "install --local" @@ -483,7 +567,7 @@ RSpec.describe "bundle install with platform conditionals" do gemfile <<-G source "#{file_uri_for(gem_repo1)}" - gem "rack", :platform => [:mingw, :mswin, :x64_mingw, :jruby] + gem "rack", :platform => [:windows, :mswin, :mswin64, :mingw, :x64_mingw, :jruby] G bundle "install" @@ -500,16 +584,36 @@ RSpec.describe "bundle install with platform conditionals" do DEPENDENCIES rack - + #{checksums_section_when_existing} BUNDLED WITH #{Bundler::VERSION} L end + + it "resolves fine when a dependency is unused on a platform different from the current one, but reintroduced transitively" do + bundle "config set --local force_ruby_platform true" + + build_repo4 do + build_gem "listen", "3.7.1" do |s| + s.add_dependency "ffi" + end + + build_gem "ffi", "1.15.5" + end + + install_gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + + gem "listen" + gem "ffi", :platform => :windows + G + expect(err).to be_empty + end end RSpec.describe "when a gem has no architecture" do it "still installs correctly" do - simulate_platform mswin + simulate_platform x86_mswin32 build_repo2 do # The rcov gem is platform mswin32, but has no arch @@ -525,7 +629,7 @@ RSpec.describe "when a gem has no architecture" do gem "rcov" G - bundle :install, :artifice => "windows", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s } + bundle :install, artifice: "windows", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s } expect(the_bundle).to include_gems "rcov 1.0.0" end end diff --git a/spec/bundler/install/gemfile/ruby_spec.rb b/spec/bundler/install/gemfile/ruby_spec.rb index ba250acfdd..b64d633fd3 100644 --- a/spec/bundler/install/gemfile/ruby_spec.rb +++ b/spec/bundler/install/gemfile/ruby_spec.rb @@ -11,13 +11,13 @@ RSpec.describe "ruby requirement" do it "allows adding gems" do install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" - ruby "#{RUBY_VERSION}" + ruby "#{Gem.ruby_version}" gem "rack" G install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" - ruby "#{RUBY_VERSION}" + ruby "#{Gem.ruby_version}" gem "rack" gem "rack-obama" G @@ -28,7 +28,7 @@ RSpec.describe "ruby requirement" do it "allows removing the ruby version requirement" do install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" - ruby "~> #{RUBY_VERSION}" + ruby "~> #{Gem.ruby_version}" gem "rack" G @@ -46,7 +46,7 @@ RSpec.describe "ruby requirement" do it "allows changing the ruby version requirement to something compatible" do install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" - ruby ">= #{RUBY_VERSION[0..2]}.0" + ruby ">= #{current_ruby_minor}" gem "rack" G @@ -55,7 +55,7 @@ RSpec.describe "ruby requirement" do install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" - ruby ">= #{RUBY_VERSION}" + ruby ">= #{Gem.ruby_version}" gem "rack" G @@ -93,7 +93,7 @@ RSpec.describe "ruby requirement" do install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" - ruby ">= #{RUBY_VERSION[0..2]}.0" + ruby ">= #{current_ruby_minor}" gem "rack" G @@ -104,7 +104,7 @@ RSpec.describe "ruby requirement" do it "allows requirements with trailing whitespace" do install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" - ruby "#{RUBY_VERSION}\\n \t\\n" + ruby "#{Gem.ruby_version}\\n \t\\n" gem "rack" G @@ -112,7 +112,7 @@ RSpec.describe "ruby requirement" do end it "fails gracefully with malformed requirements" do - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G, raise_on_error: false source "#{file_uri_for(gem_repo1)}" ruby ">= 0", "-.\\0" gem "rack" @@ -120,4 +120,38 @@ RSpec.describe "ruby requirement" do expect(err).to include("There was an error parsing") # i.e. DSL error, not error template end + + it "allows picking up ruby version from a file" do + create_file ".ruby-version", Gem.ruby_version.to_s + + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + ruby file: ".ruby-version" + gem "rack" + G + + expect(lockfile).to include("RUBY VERSION") + end + + it "reads the ruby version file from the right folder when nested Gemfiles are involved" do + create_file ".ruby-version", Gem.ruby_version.to_s + + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + ruby file: ".ruby-version" + gem "rack" + G + + nested_dir = bundled_app(".ruby-lsp") + + FileUtils.mkdir nested_dir + + create_file ".ruby-lsp/Gemfile", <<-G + eval_gemfile(File.expand_path("../Gemfile", __dir__)) + G + + bundle "install", dir: nested_dir + + expect(bundled_app(".ruby-lsp/Gemfile.lock").read).to include("RUBY VERSION") + end end diff --git a/spec/bundler/install/gemfile/sources_spec.rb b/spec/bundler/install/gemfile/sources_spec.rb index 62cad6800f..a5ba76f4d9 100644 --- a/spec/bundler/install/gemfile/sources_spec.rb +++ b/spec/bundler/install/gemfile/sources_spec.rb @@ -27,18 +27,72 @@ RSpec.describe "bundle install with gems on multiple sources" do G end - it "warns about ambiguous gems, but installs anyway, prioritizing sources last to first", :bundler => "< 3" do - bundle :install, :artifice => "compact_index" + it "refuses to install mismatched checksum because one gem has been tampered with", bundler: "< 3" do + lockfile <<~L + GEM + remote: https://gem.repo3/ + remote: https://gem.repo1/ + specs: + rack (1.0.0) - expect(err).to include("Warning: the gem 'rack' was found in multiple sources.") - expect(err).to include("Installed from: https://gem.repo1") - expect(the_bundle).to include_gems("rack-obama 1.0.0", "rack 1.0.0", :source => "remote1") + PLATFORMS + #{local_platform} + + DEPENDENCIES + depends_on_rack! + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle :install, artifice: "compact_index", raise_on_error: false + + expect(exitstatus).to eq(37) + expect(err).to eq <<~E.strip + [DEPRECATED] Your Gemfile contains multiple global sources. Using `source` more than once without a block is a security risk, and may result in installing unexpected gems. To resolve this warning, use a block to indicate which gems should come from the secondary source. + Bundler found mismatched checksums. This is a potential security risk. + #{checksum_to_lock(gem_repo1, "rack", "1.0.0")} + from the API at https://gem.repo1/ + #{checksum_to_lock(gem_repo3, "rack", "1.0.0")} + from the API at https://gem.repo3/ + + Mismatched checksums each have an authoritative source: + 1. the API at https://gem.repo1/ + 2. the API at https://gem.repo3/ + You may need to alter your Gemfile sources to resolve this issue. + + To ignore checksum security warnings, disable checksum validation with + `bundle config set --local disable_checksum_validation true` + E end - it "fails", :bundler => "3" do - bundle :instal, :artifice => "compact_index", :raise_on_error => false - expect(err).to include("Each source after the first must include a block") - expect(exitstatus).to eq(4) + context "when checksum validation is disabled" do + before do + bundle "config set --local disable_checksum_validation true" + end + + it "warns about ambiguous gems, but installs anyway, prioritizing sources last to first", bundler: "< 3" do + bundle :install, artifice: "compact_index" + + expect(err).to include("Warning: the gem 'rack' was found in multiple sources.") + expect(err).to include("Installed from: https://gem.repo1") + expect(the_bundle).to include_gems("rack-obama 1.0.0", "rack 1.0.0", source: "remote1") + end + + it "does not use the full index unnecessarily", bundler: "< 3" do + bundle :install, artifice: "compact_index", verbose: true + + expect(out).to include("https://gem.repo1/versions") + expect(out).to include("https://gem.repo3/versions") + expect(out).not_to include("https://gem.repo1/quick/Marshal.4.8/") + expect(out).not_to include("https://gem.repo3/quick/Marshal.4.8/") + end + + it "fails", bundler: "3" do + bundle :install, artifice: "compact_index", raise_on_error: false + expect(err).to include("Each source after the first must include a block") + expect(exitstatus).to eq(4) + end end end @@ -54,21 +108,49 @@ RSpec.describe "bundle install with gems on multiple sources" do G end - it "warns about ambiguous gems, but installs anyway", :bundler => "< 3" do - bundle :install, :artifice => "compact_index" + it "warns about ambiguous gems, but installs anyway", bundler: "< 3" do + bundle :install, artifice: "compact_index" expect(err).to include("Warning: the gem 'rack' was found in multiple sources.") expect(err).to include("Installed from: https://gem.repo1") - expect(the_bundle).to include_gems("rack-obama 1.0.0", "rack 1.0.0", :source => "remote1") + expect(the_bundle).to include_gems("rack-obama 1.0.0", "rack 1.0.0", source: "remote1") end - it "fails", :bundler => "3" do - bundle :install, :artifice => "compact_index", :raise_on_error => false + it "fails", bundler: "3" do + bundle :install, artifice: "compact_index", raise_on_error: false expect(err).to include("Each source after the first must include a block") expect(exitstatus).to eq(4) end end end + context "without source affinity, and a stdlib gem present in one of the sources", :ruby_repo do + let(:default_json_version) { ruby "gem 'json'; require 'json'; puts JSON::VERSION" } + + before do + build_repo2 do + build_gem "json", default_json_version + end + + build_repo4 do + build_gem "foo" do |s| + s.add_dependency "json", default_json_version + end + end + + gemfile <<-G + source "https://gem.repo2" + source "https://gem.repo4" + + gem "foo" + G + end + + it "works in standalone mode", bundler: "< 3" do + gem_checksum = checksum_digest(gem_repo4, "foo", "1.0") + bundle "install --standalone", artifice: "compact_index", env: { "BUNDLER_SPEC_FOO_CHECKSUM" => gem_checksum } + end + end + context "with source affinity" do context "with sources given by a block" do before do @@ -95,20 +177,20 @@ RSpec.describe "bundle install with gems on multiple sources" do end it "installs the gems without any warning" do - bundle :install, :artifice => "compact_index" + bundle :install, artifice: "compact_index" expect(err).not_to include("Warning") expect(the_bundle).to include_gems("rack-obama 1.0.0") - expect(the_bundle).to include_gems("rack 1.0.0", :source => "remote1") + expect(the_bundle).to include_gems("rack 1.0.0", source: "remote1") end it "can cache and deploy" do - bundle :cache, :artifice => "compact_index" + bundle :cache, artifice: "compact_index" expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist expect(bundled_app("vendor/cache/rack-obama-1.0.gem")).to exist bundle "config set --local deployment true" - bundle :install, :artifice => "compact_index" + bundle :install, artifice: "compact_index" expect(the_bundle).to include_gems("rack-obama 1.0.0", "rack 1.0.0") end @@ -128,7 +210,7 @@ RSpec.describe "bundle install with gems on multiple sources" do end end - install_gemfile <<-G, :artifice => "compact_index" + install_gemfile <<-G, artifice: "compact_index" source "https://gem.repo3" gem "rack-obama" # should come from repo3! gem "rack", :source => "https://gem.repo1" @@ -168,9 +250,9 @@ RSpec.describe "bundle install with gems on multiple sources" do end it "installs from the same source without any warning" do - bundle :install, :artifice => "compact_index" + bundle :install, artifice: "compact_index" expect(err).not_to include("Warning") - expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", :source => "remote3") + expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", source: "remote3") end end @@ -185,18 +267,18 @@ RSpec.describe "bundle install with gems on multiple sources" do end it "installs from the same source without any warning" do - bundle :install, :artifice => "compact_index" + bundle :install, artifice: "compact_index" expect(err).not_to include("Warning: the gem 'rack' was found in multiple sources.") - expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", :source => "remote3") + expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", source: "remote3") # In https://github.com/bundler/bundler/issues/3585 this failed # when there is already a lock file, and the gems are missing, so try again system_gems [] - bundle :install, :artifice => "compact_index" + bundle :install, artifice: "compact_index" expect(err).not_to include("Warning: the gem 'rack' was found in multiple sources.") - expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", :source => "remote3") + expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", source: "remote3") end end end @@ -218,7 +300,7 @@ RSpec.describe "bundle install with gems on multiple sources" do context "and not in any other sources" do before do - install_gemfile <<-G, :artifice => "compact_index" + install_gemfile <<-G, artifice: "compact_index" source "https://gem.repo2" source "https://gem.repo3" do gem "depends_on_rack" @@ -243,11 +325,63 @@ RSpec.describe "bundle install with gems on multiple sources" do G end - it "installs from the other source and warns about ambiguous gems", :bundler => "< 3" do - bundle :install, :artifice => "compact_index" + it "fails when the two sources don't have the same checksum", bundler: "< 3" do + bundle :install, artifice: "compact_index", raise_on_error: false + + expect(err).to eq(<<~E.strip) + [DEPRECATED] Your Gemfile contains multiple global sources. Using `source` more than once without a block is a security risk, and may result in installing unexpected gems. To resolve this warning, use a block to indicate which gems should come from the secondary source. + Bundler found mismatched checksums. This is a potential security risk. + #{checksum_to_lock(gem_repo2, "rack", "1.0.0")} + from the API at https://gem.repo2/ + #{checksum_to_lock(gem_repo1, "rack", "1.0.0")} + from the API at https://gem.repo1/ + + Mismatched checksums each have an authoritative source: + 1. the API at https://gem.repo2/ + 2. the API at https://gem.repo1/ + You may need to alter your Gemfile sources to resolve this issue. + + To ignore checksum security warnings, disable checksum validation with + `bundle config set --local disable_checksum_validation true` + E + expect(exitstatus).to eq(37) + end + + it "fails when the two sources agree, but the local gem calculates a different checksum", bundler: "< 3" do + rack_checksum = "c0ffee11" * 8 + bundle :install, artifice: "compact_index", env: { "BUNDLER_SPEC_RACK_CHECKSUM" => rack_checksum }, raise_on_error: false + + expect(err).to eq(<<~E.strip) + [DEPRECATED] Your Gemfile contains multiple global sources. Using `source` more than once without a block is a security risk, and may result in installing unexpected gems. To resolve this warning, use a block to indicate which gems should come from the secondary source. + Bundler found mismatched checksums. This is a potential security risk. + rack (1.0.0) sha256=#{rack_checksum} + from the API at https://gem.repo2/ + and the API at https://gem.repo1/ + #{checksum_to_lock(gem_repo2, "rack", "1.0.0")} + from the gem at #{default_bundle_path("cache", "rack-1.0.0.gem")} + + If you trust the API at https://gem.repo2/, to resolve this issue you can: + 1. remove the gem at #{default_bundle_path("cache", "rack-1.0.0.gem")} + 2. run `bundle install` + + To ignore checksum security warnings, disable checksum validation with + `bundle config set --local disable_checksum_validation true` + E + expect(exitstatus).to eq(37) + end + + it "installs from the other source and warns about ambiguous gems when the sources have the same checksum", bundler: "< 3" do + gem_checksum = checksum_digest(gem_repo2, "rack", "1.0.0") + bundle :install, artifice: "compact_index", env: { "BUNDLER_SPEC_RACK_CHECKSUM" => gem_checksum, "DEBUG" => "1" } + expect(err).to include("Warning: the gem 'rack' was found in multiple sources.") expect(err).to include("Installed from: https://gem.repo2") + checksums = checksums_section_when_existing do |c| + c.checksum gem_repo3, "depends_on_rack", "1.0.1" + c.checksum gem_repo2, "rack", "1.0.0" + end + expect(lockfile).to eq <<~L GEM remote: https://gem.repo1/ @@ -262,11 +396,51 @@ RSpec.describe "bundle install with gems on multiple sources" do rack PLATFORMS - #{specific_local_platform} + #{lockfile_platforms} DEPENDENCIES depends_on_rack! + #{checksums} + BUNDLED WITH + #{Bundler::VERSION} + L + + previous_lockfile = lockfile + expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0") + expect(lockfile).to eq(previous_lockfile) + end + it "installs from the other source and warns about ambiguous gems when checksum validation is disabled", bundler: "< 3" do + bundle "config set --local disable_checksum_validation true" + bundle :install, artifice: "compact_index" + + expect(err).to include("Warning: the gem 'rack' was found in multiple sources.") + expect(err).to include("Installed from: https://gem.repo2") + + checksums = checksums_section_when_existing do |c| + c.no_checksum "depends_on_rack", "1.0.1" + c.no_checksum "rack", "1.0.0" + end + + expect(lockfile).to eq <<~L + GEM + remote: https://gem.repo1/ + remote: https://gem.repo2/ + specs: + rack (1.0.0) + + GEM + remote: https://gem.repo3/ + specs: + depends_on_rack (1.0.1) + rack + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + depends_on_rack! + #{checksums} BUNDLED WITH #{Bundler::VERSION} L @@ -276,8 +450,8 @@ RSpec.describe "bundle install with gems on multiple sources" do expect(lockfile).to eq(previous_lockfile) end - it "fails", :bundler => "3" do - bundle :install, :artifice => "compact_index", :raise_on_error => false + it "fails", bundler: "3" do + bundle :install, artifice: "compact_index", raise_on_error: false expect(err).to include("Each source after the first must include a block") expect(exitstatus).to eq(4) end @@ -301,8 +475,8 @@ RSpec.describe "bundle install with gems on multiple sources" do G end - it "installs the dependency from the pinned source without warning", :bundler => "< 3" do - bundle :install, :artifice => "compact_index" + it "installs the dependency from the pinned source without warning", bundler: "< 3" do + bundle :install, artifice: "compact_index" expect(err).not_to include("Warning: the gem 'rack' was found in multiple sources.") expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0") @@ -310,14 +484,14 @@ RSpec.describe "bundle install with gems on multiple sources" do # In https://github.com/rubygems/bundler/issues/3585 this failed # when there is already a lock file, and the gems are missing, so try again system_gems [] - bundle :install, :artifice => "compact_index" + bundle :install, artifice: "compact_index" expect(err).not_to include("Warning: the gem 'rack' was found in multiple sources.") expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0") end - it "fails", :bundler => "3" do - bundle :install, :artifice => "compact_index", :raise_on_error => false + it "fails", bundler: "3" do + bundle :install, artifice: "compact_index", raise_on_error: false expect(err).to include("Each source after the first must include a block") expect(exitstatus).to eq(4) end @@ -345,12 +519,12 @@ RSpec.describe "bundle install with gems on multiple sources" do end it "fails" do - bundle :install, :artifice => "compact_index", :raise_on_error => false - expect(err).to include("Could not find gem 'private_gem_1' in rubygems repository https://gem.repo2/ or installed locally.") + bundle :install, artifice: "compact_index", raise_on_error: false + expect(err).to include("Could not find gem 'private_gem_1' in rubygems repository https://gem.repo2/, cached gems or installed locally.") end end - context "when an indirect dependency can't be found in the aggregate rubygems source", :bundler => "< 3" do + context "when an indirect dependency can't be found in the aggregate rubygems source", bundler: "< 3" do before do build_repo2 @@ -370,8 +544,16 @@ RSpec.describe "bundle install with gems on multiple sources" do end it "fails" do - bundle :install, :artifice => "compact_index", :raise_on_error => false - expect(err).to include("Could not find gem 'missing', which is required by gem 'depends_on_missing', in any of the sources.") + bundle :install, artifice: "compact_index", raise_on_error: false + expect(err).to end_with <<~E.strip + Could not find compatible versions + + Because every version of depends_on_missing depends on missing >= 0 + and missing >= 0 could not be found in any of the sources, + depends_on_missing cannot be used. + So, because Gemfile depends on depends_on_missing >= 0, + version solving has failed. + E end end @@ -406,11 +588,11 @@ RSpec.describe "bundle install with gems on multiple sources" do end it "installs the dependency from the top-level source without warning" do - bundle :install, :artifice => "compact_index" + bundle :install, artifice: "compact_index" expect(err).not_to include("Warning") expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", "unrelated_gem 1.0.0") - expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", :source => "remote2") - expect(the_bundle).to include_gems("unrelated_gem 1.0.0", :source => "remote3") + expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", source: "remote2") + expect(the_bundle).to include_gems("unrelated_gem 1.0.0", source: "remote3") end end @@ -424,10 +606,16 @@ RSpec.describe "bundle install with gems on multiple sources" do end it "does not find the dependency" do - bundle :install, :artifice => "compact_index", :raise_on_error => false - expect(err).to include( - "Could not find gem 'rack', which is required by gem 'depends_on_rack', in rubygems repository https://gem.repo2/ or installed locally." - ) + bundle :install, artifice: "compact_index", raise_on_error: false + expect(err).to end_with <<~E.strip + Could not find compatible versions + + Because every version of depends_on_rack depends on rack >= 0 + and rack >= 0 could not be found in rubygems repository https://gem.repo2/, cached gems or installed locally, + depends_on_rack cannot be used. + So, because Gemfile depends on depends_on_rack >= 0, + version solving has failed. + E end end @@ -445,12 +633,12 @@ RSpec.describe "bundle install with gems on multiple sources" do end it "installs the dependency from the top-level source without warning" do - bundle :install, :artifice => "compact_index" + bundle :install, artifice: "compact_index" expect(err).not_to include("Warning") expect(run("require 'rack'; puts RACK")).to eq("1.0.0") expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", "unrelated_gem 1.0.0") - expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", :source => "remote2") - expect(the_bundle).to include_gems("unrelated_gem 1.0.0", :source => "remote3") + expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", source: "remote2") + expect(the_bundle).to include_gems("unrelated_gem 1.0.0", source: "remote3") end end end @@ -484,10 +672,10 @@ RSpec.describe "bundle install with gems on multiple sources" do end it "installs the dependency from the top-level source" do - bundle :install, :artifice => "compact_index" + bundle :install, artifice: "compact_index" expect(the_bundle).to include_gems("depends_on_depends_on_rack 1.0.1", "depends_on_rack 1.0.1", "rack 1.0.0") - expect(the_bundle).to include_gems("rack 1.0.0", :source => "remote2") - expect(the_bundle).to include_gems("depends_on_depends_on_rack 1.0.1", "depends_on_rack 1.0.1", :source => "remote3") + expect(the_bundle).to include_gems("rack 1.0.0", source: "remote2") + expect(the_bundle).to include_gems("depends_on_depends_on_rack 1.0.1", "depends_on_rack 1.0.1", source: "remote3") end end @@ -501,8 +689,8 @@ RSpec.describe "bundle install with gems on multiple sources" do end it "installs the dependency from the pinned source" do - bundle :install, :artifice => "compact_index" - expect(the_bundle).to include_gems("depends_on_depends_on_rack 1.0.1", "depends_on_rack 1.0.1", "rack 1.0.0", :source => "remote3") + bundle :install, artifice: "compact_index" + expect(the_bundle).to include_gems("depends_on_depends_on_rack 1.0.1", "depends_on_rack 1.0.1", "rack 1.0.0", source: "remote3") end end @@ -520,8 +708,8 @@ RSpec.describe "bundle install with gems on multiple sources" do end it "installs the dependency from the pinned source without warning" do - bundle :install, :artifice => "compact_index" - expect(the_bundle).to include_gems("depends_on_depends_on_rack 1.0.1", "depends_on_rack 1.0.1", "rack 1.0.0", :source => "remote3") + bundle :install, artifice: "compact_index" + expect(the_bundle).to include_gems("depends_on_depends_on_rack 1.0.1", "depends_on_rack 1.0.1", "rack 1.0.0", source: "remote3") end end end @@ -595,6 +783,21 @@ RSpec.describe "bundle install with gems on multiple sources" do end G + @locked_checksums = checksums_section_when_existing do |c| + c.checksum gem_repo2, "activesupport", "6.0.3.4" + c.checksum gem_repo2, "concurrent-ruby", "1.1.8" + c.checksum gem_repo2, "connection_pool", "2.2.3" + c.checksum gem_repo2, "i18n", "1.8.9" + c.checksum gem_repo2, "minitest", "5.14.3" + c.checksum gem_repo2, "rack", "2.2.3" + c.checksum gem_repo2, "redis", "4.2.5" + c.checksum gem_repo2, "sidekiq", "6.1.3" + c.checksum gem_repo3, "sidekiq-pro", "5.2.1" + c.checksum gem_repo2, "thread_safe", "0.3.6" + c.checksum gem_repo2, "tzinfo", "1.2.9" + c.checksum gem_repo2, "zeitwerk", "2.4.2" + end + lockfile <<~L GEM remote: https://gem.repo2/ @@ -626,19 +829,19 @@ RSpec.describe "bundle install with gems on multiple sources" do zeitwerk (2.4.2) PLATFORMS - #{specific_local_platform} + #{lockfile_platforms} DEPENDENCIES activesupport sidekiq-pro! - + #{@locked_checksums} BUNDLED WITH #{Bundler::VERSION} L end it "does not install newer versions but updates the lockfile format when running bundle install in non frozen mode, and doesn't warn" do - bundle :install, :artifice => "compact_index" + bundle :install, artifice: "compact_index" expect(err).to be_empty expect(the_bundle).to include_gems("activesupport 6.0.3.4") @@ -682,22 +885,22 @@ RSpec.describe "bundle install with gems on multiple sources" do sidekiq (>= 6.1.0) PLATFORMS - #{specific_local_platform} + #{lockfile_platforms} DEPENDENCIES activesupport sidekiq-pro! - + #{@locked_checksums} BUNDLED WITH #{Bundler::VERSION} L end - it "does not install newer versions or generate lockfile changes when running bundle install in frozen mode, and warns", :bundler => "< 3" do + it "does not install newer versions or generate lockfile changes when running bundle install in frozen mode, and warns", bundler: "< 3" do initial_lockfile = lockfile bundle "config set --local frozen true" - bundle :install, :artifice => "compact_index" + bundle :install, artifice: "compact_index" expect(err).to include("Your lockfile contains a single rubygems source section with multiple remotes, which is insecure.") @@ -711,11 +914,11 @@ RSpec.describe "bundle install with gems on multiple sources" do expect(lockfile).to eq(initial_lockfile) end - it "fails when running bundle install in frozen mode", :bundler => "3" do + it "fails when running bundle install in frozen mode", bundler: "3" do initial_lockfile = lockfile bundle "config set --local frozen true" - bundle :install, :artifice => "compact_index", :raise_on_error => false + bundle :install, artifice: "compact_index", raise_on_error: false expect(err).to include("Your lockfile contains a single rubygems source section with multiple remotes, which is insecure.") @@ -723,15 +926,21 @@ RSpec.describe "bundle install with gems on multiple sources" do end it "splits sections and upgrades gems when running bundle update, and doesn't warn" do - bundle "update --all", :artifice => "compact_index" + bundle "update --all", artifice: "compact_index" expect(err).to be_empty expect(the_bundle).not_to include_gems("activesupport 6.0.3.4") expect(the_bundle).to include_gems("activesupport 6.1.2.1") + @locked_checksums.checksum gem_repo2, "activesupport", "6.1.2.1" + expect(the_bundle).not_to include_gems("tzinfo 1.2.9") expect(the_bundle).to include_gems("tzinfo 2.0.4") + @locked_checksums.checksum gem_repo2, "tzinfo", "2.0.4" + @locked_checksums.delete "thread_safe" + expect(the_bundle).not_to include_gems("concurrent-ruby 1.1.8") expect(the_bundle).to include_gems("concurrent-ruby 1.1.9") + @locked_checksums.checksum gem_repo2, "concurrent-ruby", "1.1.9" expect(lockfile).to eq <<~L GEM @@ -766,19 +975,19 @@ RSpec.describe "bundle install with gems on multiple sources" do sidekiq (>= 6.1.0) PLATFORMS - #{specific_local_platform} + #{lockfile_platforms} DEPENDENCIES activesupport sidekiq-pro! - + #{@locked_checksums} BUNDLED WITH #{Bundler::VERSION} L end it "upgrades the lockfile format and upgrades the requested gem when running bundle update with an argument" do - bundle "update concurrent-ruby", :artifice => "compact_index" + bundle "update concurrent-ruby", artifice: "compact_index" expect(err).to be_empty expect(the_bundle).to include_gems("activesupport 6.0.3.4") @@ -788,6 +997,8 @@ RSpec.describe "bundle install with gems on multiple sources" do expect(the_bundle).to include_gems("concurrent-ruby 1.1.9") expect(the_bundle).not_to include_gems("concurrent-ruby 1.1.8") + @locked_checksums.checksum gem_repo2, "concurrent-ruby", "1.1.9" + expect(lockfile).to eq <<~L GEM remote: https://gem.repo2/ @@ -822,12 +1033,12 @@ RSpec.describe "bundle install with gems on multiple sources" do sidekiq (>= 6.1.0) PLATFORMS - #{specific_local_platform} + #{lockfile_platforms} DEPENDENCIES activesupport sidekiq-pro! - + #{@locked_checksums} BUNDLED WITH #{Bundler::VERSION} L @@ -836,8 +1047,8 @@ RSpec.describe "bundle install with gems on multiple sources" do context "when a top-level gem has an indirect dependency present in the default source, but with a different version from the one resolved" do before do - build_lib "activesupport", "7.0.0.alpha", :path => lib_path("rails/activesupport") - build_lib "rails", "7.0.0.alpha", :path => lib_path("rails") do |s| + build_lib "activesupport", "7.0.0.alpha", path: lib_path("rails/activesupport") + build_lib "rails", "7.0.0.alpha", path: lib_path("rails") do |s| s.add_dependency "activesupport", "= 7.0.0.alpha" end @@ -859,11 +1070,11 @@ RSpec.describe "bundle install with gems on multiple sources" do end it "installs all gems without warning" do - bundle :install, :artifice => "compact_index" + bundle :install, artifice: "compact_index" expect(err).not_to include("Warning") expect(the_bundle).to include_gems("activesupport 7.0.0.alpha", "rails 7.0.0.alpha") - expect(the_bundle).to include_gems("activesupport 7.0.0.alpha", :source => "path@#{lib_path("rails/activesupport")}") - expect(the_bundle).to include_gems("rails 7.0.0.alpha", :source => "path@#{lib_path("rails")}") + expect(the_bundle).to include_gems("activesupport 7.0.0.alpha", source: "path@#{lib_path("rails/activesupport")}") + expect(the_bundle).to include_gems("rails 7.0.0.alpha", source: "path@#{lib_path("rails")}") end end @@ -895,6 +1106,12 @@ RSpec.describe "bundle install with gems on multiple sources" do end it "installs from the default source without any warnings or errors and generates a proper lockfile" do + checksums = checksums_section_when_existing do |c| + c.checksum gem_repo3, "handsoap", "0.2.5.5" + c.checksum gem_repo2, "nokogiri", "1.11.1" + c.checksum gem_repo2, "racca", "1.5.2" + end + expected_lockfile = <<~L GEM remote: https://gem.repo2/ @@ -910,30 +1127,30 @@ RSpec.describe "bundle install with gems on multiple sources" do nokogiri (>= 1.2.3) PLATFORMS - #{specific_local_platform} + #{lockfile_platforms} DEPENDENCIES handsoap! nokogiri - + #{checksums} BUNDLED WITH #{Bundler::VERSION} L - bundle "install --verbose", :artifice => "compact_index" + bundle "install --verbose", artifice: "compact_index" expect(err).not_to include("Warning") expect(the_bundle).to include_gems("handsoap 0.2.5.5", "nokogiri 1.11.1", "racca 1.5.2") - expect(the_bundle).to include_gems("handsoap 0.2.5.5", :source => "remote3") - expect(the_bundle).to include_gems("nokogiri 1.11.1", "racca 1.5.2", :source => "remote2") + expect(the_bundle).to include_gems("handsoap 0.2.5.5", source: "remote3") + expect(the_bundle).to include_gems("nokogiri 1.11.1", "racca 1.5.2", source: "remote2") expect(lockfile).to eq(expected_lockfile) # Even if the gems are already installed FileUtils.rm bundled_app_lock - bundle "install --verbose", :artifice => "compact_index" + bundle "install --verbose", artifice: "compact_index" expect(err).not_to include("Warning") expect(the_bundle).to include_gems("handsoap 0.2.5.5", "nokogiri 1.11.1", "racca 1.5.2") - expect(the_bundle).to include_gems("handsoap 0.2.5.5", :source => "remote3") - expect(the_bundle).to include_gems("nokogiri 1.11.1", "racca 1.5.2", :source => "remote2") + expect(the_bundle).to include_gems("handsoap 0.2.5.5", source: "remote3") + expect(the_bundle).to include_gems("nokogiri 1.11.1", "racca 1.5.2", source: "remote2") expect(lockfile).to eq(expected_lockfile) end end @@ -944,7 +1161,7 @@ RSpec.describe "bundle install with gems on multiple sources" do build_gem "not_in_repo1", "1.0.0" end - install_gemfile <<-G, :artifice => "compact_index", :raise_on_error => false + install_gemfile <<-G, artifice: "compact_index", raise_on_error: false source "https://gem.repo3" gem "not_in_repo1", :source => "https://gem.repo1" G @@ -957,7 +1174,7 @@ RSpec.describe "bundle install with gems on multiple sources" do context "with an existing lockfile" do before do - system_gems "rack-0.9.1", "rack-1.0.0", :path => default_bundle_path + system_gems "rack-0.9.1", "rack-1.0.0", path: default_bundle_path lockfile <<-L GEM @@ -970,7 +1187,7 @@ RSpec.describe "bundle install with gems on multiple sources" do rack (0.9.1) PLATFORMS - #{specific_local_platform} + #{lockfile_platforms} DEPENDENCIES rack! @@ -1000,11 +1217,11 @@ RSpec.describe "bundle install with gems on multiple sources" do rack (0.9.1) PLATFORMS - #{specific_local_platform} + #{lockfile_platforms} DEPENDENCIES rack! - + #{checksums_section} BUNDLED WITH #{Bundler::VERSION} L @@ -1022,7 +1239,7 @@ RSpec.describe "bundle install with gems on multiple sources" do rack (0.9.1) PLATFORMS - #{specific_local_platform} + #{lockfile_platforms} DEPENDENCIES rack! @@ -1047,20 +1264,48 @@ RSpec.describe "bundle install with gems on multiple sources" do lockfile aggregate_gem_section_lockfile end - it "installs the existing lockfile but prints a warning", :bundler => "< 3" do + it "installs the existing lockfile but prints a warning when checksum validation is disabled", bundler: "< 3" do bundle "config set --local deployment true" + bundle "config set --local disable_checksum_validation true" - bundle "install", :artifice => "compact_index" + bundle "install", artifice: "compact_index" expect(lockfile).to eq(aggregate_gem_section_lockfile) expect(err).to include("Your lockfile contains a single rubygems source section with multiple remotes, which is insecure.") - expect(the_bundle).to include_gems("rack 0.9.1", :source => "remote3") + expect(the_bundle).to include_gems("rack 0.9.1", source: "remote3") + end + + it "prints a checksum warning when the checksums from both sources do not match", bundler: "< 3" do + bundle "config set --local deployment true" + + bundle "install", artifice: "compact_index", raise_on_error: false + + api_checksum1 = checksum_digest(gem_repo1, "rack", "0.9.1") + api_checksum3 = checksum_digest(gem_repo3, "rack", "0.9.1") + + expect(exitstatus).to eq(37) + expect(err).to eq(<<~E.strip) + [DEPRECATED] Your lockfile contains a single rubygems source section with multiple remotes, which is insecure. Make sure you run `bundle install` in non frozen mode and commit the result to make your lockfile secure. + Bundler found mismatched checksums. This is a potential security risk. + rack (0.9.1) sha256=#{api_checksum3} + from the API at https://gem.repo3/ + rack (0.9.1) sha256=#{api_checksum1} + from the API at https://gem.repo1/ + + Mismatched checksums each have an authoritative source: + 1. the API at https://gem.repo3/ + 2. the API at https://gem.repo1/ + You may need to alter your Gemfile sources to resolve this issue. + + To ignore checksum security warnings, disable checksum validation with + `bundle config set --local disable_checksum_validation true` + E end - it "refuses to install the existing lockfile and prints an error", :bundler => "3" do + it "refuses to install the existing lockfile and prints an error", bundler: "3" do bundle "config set --local deployment true" - bundle "install", :artifice => "compact_index", :raise_on_error =>false + bundle "install", artifice: "compact_index", raise_on_error: false expect(lockfile).to eq(aggregate_gem_section_lockfile) expect(err).to include("Your lockfile contains a single rubygems source section with multiple remotes, which is insecure.") @@ -1080,7 +1325,7 @@ RSpec.describe "bundle install with gems on multiple sources" do end it "does not unlock the non-path gem after install" do - bundle :install, :artifice => "compact_index" + bundle :install, artifice: "compact_index" bundle %(exec ruby -e 'puts "OK"') @@ -1093,7 +1338,7 @@ RSpec.describe "bundle install with gems on multiple sources" do before do system_gems "rack-0.9.1" - install_gemfile <<-G, :artifice => "compact_index" + install_gemfile <<-G, artifice: "compact_index" source "https://gem.repo1" gem "rack" # should come from repo1! G @@ -1122,7 +1367,7 @@ RSpec.describe "bundle install with gems on multiple sources" do G bundle "config set --local path ../gems/system" - bundle :install, :artifice => "compact_index" + bundle :install, artifice: "compact_index" # And then we add some new versions... update_repo4 do @@ -1133,7 +1378,7 @@ RSpec.describe "bundle install with gems on multiple sources" do it "allows them to be unlocked separately" do # And install this gemfile, updating only foo. - install_gemfile <<-G, :artifice => "compact_index" + install_gemfile <<-G, artifice: "compact_index" source 'https://gem.repo1' gem 'rack' gem 'foo', '~> 0.2', :source => 'https://gem.repo4' @@ -1157,7 +1402,7 @@ RSpec.describe "bundle install with gems on multiple sources" do build_git "git1" build_git "git2" - install_gemfile <<-G, :artifice => "compact_index" + install_gemfile <<-G, artifice: "compact_index" source "https://gem.repo1" gem "rails" @@ -1173,7 +1418,7 @@ RSpec.describe "bundle install with gems on multiple sources" do end it "does not re-resolve" do - bundle :install, :artifice => "compact_index", :verbose => true + bundle :install, artifice: "compact_index", verbose: true expect(out).to include("using resolution from the lockfile") expect(out).not_to include("re-resolving dependencies") end @@ -1182,7 +1427,7 @@ RSpec.describe "bundle install with gems on multiple sources" do context "when a gem is installed to system gems" do before do - install_gemfile <<-G, :artifice => "compact_index" + install_gemfile <<-G, artifice: "compact_index" source "https://gem.repo1" gem "rack" G @@ -1196,7 +1441,7 @@ RSpec.describe "bundle install with gems on multiple sources" do end # When this gemfile is installed... - install_gemfile <<-G, :artifice => "compact_index" + install_gemfile <<-G, artifice: "compact_index" source "https://gem.repo1" source "https://gem.repo4" do @@ -1218,14 +1463,14 @@ RSpec.describe "bundle install with gems on multiple sources" do G # But we should still be able to find rack 2.0.1.1.forked and install it - bundle :install, :artifice => "compact_index" + bundle :install, artifice: "compact_index" end end end describe "source changed to one containing a higher version of a dependency" do before do - install_gemfile <<-G, :artifice => "compact_index" + install_gemfile <<-G, artifice: "compact_index" source "https://gem.repo1" gem "rack" @@ -1239,11 +1484,11 @@ RSpec.describe "bundle install with gems on multiple sources" do build_gem "bar" end - build_lib("gemspec_test", :path => tmp.join("gemspec_test")) do |s| + build_lib("gemspec_test", path: tmp.join("gemspec_test")) do |s| s.add_dependency "bar", "=1.0.0" end - install_gemfile <<-G, :artifice => "compact_index" + install_gemfile <<-G, artifice: "compact_index" source "https://gem.repo2" gem "rack" gemspec :path => "#{tmp.join("gemspec_test")}" @@ -1255,6 +1500,32 @@ RSpec.describe "bundle install with gems on multiple sources" do end end + context "when Gemfile overrides a gemspec development dependency to change the default source" do + before do + build_repo4 do + build_gem "bar" + end + + build_lib("gemspec_test", path: tmp.join("gemspec_test")) do |s| + s.add_development_dependency "bar" + end + + install_gemfile <<-G, artifice: "compact_index" + source "https://gem.repo1" + + source "https://gem.repo4" do + gem "bar" + end + + gemspec :path => "#{tmp.join("gemspec_test")}" + G + end + + it "does not print warnings" do + expect(err).to be_empty + end + end + it "doesn't update version when a gem uses a source block but a higher version from another source is already installed locally" do build_repo2 do build_gem "example", "0.1.0" @@ -1264,7 +1535,7 @@ RSpec.describe "bundle install with gems on multiple sources" do build_gem "example", "1.0.2" end - install_gemfile <<-G, :artifice => "compact_index" + install_gemfile <<-G, artifice: "compact_index" source "https://gem.repo4" gem "example", :source => "https://gem.repo2" @@ -1273,14 +1544,14 @@ RSpec.describe "bundle install with gems on multiple sources" do bundle "info example" expect(out).to include("example (0.1.0)") - system_gems "example-1.0.2", :path => default_bundle_path, :gem_repo => gem_repo4 + system_gems "example-1.0.2", path: default_bundle_path, gem_repo: gem_repo4 - bundle "update example --verbose", :artifice => "compact_index" + bundle "update example --verbose", artifice: "compact_index" expect(out).not_to include("Using example 1.0.2") expect(out).to include("Using example 0.1.0") end - it "fails inmmediately with a helpful error when a rubygems source does not exist and bundler/setup is required" do + it "fails immediately with a helpful error when a rubygems source does not exist and bundler/setup is required" do gemfile <<-G source "https://gem.repo1" @@ -1289,17 +1560,15 @@ RSpec.describe "bundle install with gems on multiple sources" do end G - simulate_bundler_version_when_missing_prerelease_default_gem_activation do - ruby <<~R, :raise_on_error => false - require 'bundler/setup' - R - end + ruby <<~R, raise_on_error: false + require 'bundler/setup' + R expect(last_command).to be_failure expect(err).to include("Could not find gem 'example' in locally installed gems.") end - it "fails inmmediately with a helpful error when a non retriable network error happens while resolving sources" do + it "fails immediately with a helpful error when a non retriable network error happens while resolving sources" do gemfile <<-G source "https://gem.repo1" @@ -1308,13 +1577,13 @@ RSpec.describe "bundle install with gems on multiple sources" do end G - bundle "install", :artifice => nil, :raise_on_error => false + bundle "install", artifice: nil, raise_on_error: false expect(last_command).to be_failure expect(err).to include("Could not reach host gem.repo4. Check your network connection and try again.") end - context "when an indirect dependency is available from multiple ambiguous sources", :bundler => "< 3" do + context "when an indirect dependency is available from multiple ambiguous sources", bundler: "< 3" do it "succeeds but warns, suggesting a source block" do build_repo4 do build_gem "depends_on_rack" do |s| @@ -1323,7 +1592,7 @@ RSpec.describe "bundle install with gems on multiple sources" do build_gem "rack" end - install_gemfile <<-G, :artifice => "compact_index", :raise_on_error => false + install_gemfile <<-G, artifice: "compact_index", raise_on_error: false source "#{file_uri_for(gem_repo1)}" source "https://gem.repo4" do @@ -1334,7 +1603,7 @@ RSpec.describe "bundle install with gems on multiple sources" do gem "thin" end G - expect(err).to eq strip_whitespace(<<-EOS).strip + expect(err).to eq <<~EOS.strip Warning: The gem 'rack' was found in multiple relevant sources. * rubygems repository https://gem.repo1/ * rubygems repository https://gem.repo4/ @@ -1345,7 +1614,7 @@ RSpec.describe "bundle install with gems on multiple sources" do end end - context "when an indirect dependency is available from multiple ambiguous sources", :bundler => "3" do + context "when an indirect dependency is available from multiple ambiguous sources", bundler: "3" do it "raises, suggesting a source block" do build_repo4 do build_gem "depends_on_rack" do |s| @@ -1354,7 +1623,7 @@ RSpec.describe "bundle install with gems on multiple sources" do build_gem "rack" end - install_gemfile <<-G, :artifice => "compact_index", :raise_on_error => false + install_gemfile <<-G, artifice: "compact_index", raise_on_error: false source "#{file_uri_for(gem_repo1)}" source "https://gem.repo4" do gem "depends_on_rack" @@ -1364,7 +1633,7 @@ RSpec.describe "bundle install with gems on multiple sources" do end G expect(last_command).to be_failure - expect(err).to eq strip_whitespace(<<-EOS).strip + expect(err).to eq <<~EOS.strip The gem 'rack' was found in multiple relevant sources. * rubygems repository https://gem.repo1/ * rubygems repository https://gem.repo4/ @@ -1408,16 +1677,23 @@ RSpec.describe "bundle install with gems on multiple sources" do mime-types (3.3.1) PLATFORMS - #{specific_local_platform} + #{lockfile_platforms} DEPENDENCIES capybara (~> 2.5.0) mime-types (~> 3.0)! + + CHECKSUMS L end it "upgrades the lockfile correctly" do - bundle "lock --update", :artifice => "compact_index" + bundle "lock --update", artifice: "compact_index" + + checksums = checksums_section_when_existing do |c| + c.checksum gem_repo2, "capybara", "2.5.0" + c.checksum gem_repo4, "mime-types", "3.0.0" + end expect(lockfile).to eq <<~L GEM @@ -1432,12 +1708,70 @@ RSpec.describe "bundle install with gems on multiple sources" do mime-types (3.0.0) PLATFORMS - #{specific_local_platform} + #{lockfile_platforms} DEPENDENCIES capybara (~> 2.5.0) mime-types (~> 3.0)! + #{checksums} + BUNDLED WITH + #{Bundler::VERSION} + L + end + end + + context "when default source includes old gems with nil required_ruby_version" do + before do + build_repo2 do + build_gem "ruport", "1.7.0.3" do |s| + s.add_dependency "pdf-writer", "1.1.8" + end + end + build_repo gem_repo4 do + build_gem "pdf-writer", "1.1.8" + end + + path = "#{gem_repo4}/#{Gem::MARSHAL_SPEC_DIR}/pdf-writer-1.1.8.gemspec.rz" + spec = Marshal.load(Bundler.rubygems.inflate(File.binread(path))) + spec.instance_variable_set(:@required_ruby_version, nil) + File.open(path, "wb") do |f| + f.write Gem.deflate(Marshal.dump(spec)) + end + + gemfile <<~G + source "https://localgemserver.test" + + gem "ruport", "= 1.7.0.3", :source => "https://localgemserver.test/extra" + G + end + + it "handles that fine" do + bundle "install", artifice: "compact_index_extra", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } + + checksums = checksums_section_when_existing do |c| + c.checksum gem_repo4, "pdf-writer", "1.1.8" + c.checksum gem_repo2, "ruport", "1.7.0.3" + end + + expect(lockfile).to eq <<~L + GEM + remote: https://localgemserver.test/ + specs: + pdf-writer (1.1.8) + + GEM + remote: https://localgemserver.test/extra/ + specs: + ruport (1.7.0.3) + pdf-writer (= 1.1.8) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + ruport (= 1.7.0.3)! + #{checksums} BUNDLED WITH #{Bundler::VERSION} L @@ -1471,7 +1805,12 @@ RSpec.describe "bundle install with gems on multiple sources" do end it "handles that fine" do - bundle "install", :artifice => "compact_index_extra", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } + bundle "install", artifice: "compact_index_extra", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } + + checksums = checksums_section_when_existing do |c| + c.checksum gem_repo4, "pdf-writer", "1.1.8" + c.checksum gem_repo2, "ruport", "1.7.0.3" + end expect(lockfile).to eq <<~L GEM @@ -1486,11 +1825,11 @@ RSpec.describe "bundle install with gems on multiple sources" do pdf-writer (= 1.1.8) PLATFORMS - #{specific_local_platform} + #{lockfile_platforms} DEPENDENCIES ruport (= 1.7.0.3)! - + #{checksums} BUNDLED WITH #{Bundler::VERSION} L @@ -1518,7 +1857,11 @@ RSpec.describe "bundle install with gems on multiple sources" do end it "handles that fine" do - bundle "install --verbose", :artifice => "endpoint", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } + bundle "install --verbose", artifice: "endpoint", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } + + checksums = checksums_section_when_existing do |c| + c.checksum gem_repo4, "pdf-writer", "1.1.8" + end expect(lockfile).to eq <<~L GEM @@ -1527,14 +1870,53 @@ RSpec.describe "bundle install with gems on multiple sources" do pdf-writer (1.1.8) PLATFORMS - #{specific_local_platform} + #{lockfile_platforms} DEPENDENCIES pdf-writer (= 1.1.8) - + #{checksums} BUNDLED WITH #{Bundler::VERSION} L end end + + context "when mistakenly adding a top level gem already depended on and cached under the wrong source" do + before do + build_repo4 do + build_gem "some_private_gem", "0.1.0" do |s| + s.add_dependency "example", "~> 1.0" + end + end + + build_repo2 do + build_gem "example", "1.0.0" + end + + install_gemfile <<~G, artifice: "compact_index" + source "https://gem.repo2" + + source "https://gem.repo4" do + gem "some_private_gem" + end + G + + gemfile <<~G + source "https://gem.repo2" + + source "https://gem.repo4" do + gem "some_private_gem" + gem "example" # MISTAKE, example is not available at gem.repo4 + end + G + end + + it "shows a proper error message and does not generate a corrupted lockfile" do + expect do + bundle :install, artifice: "compact_index", raise_on_error: false, env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } + end.not_to change { lockfile } + + expect(err).to include("Could not find gem 'example' in rubygems repository https://gem.repo4/") + end + end end diff --git a/spec/bundler/install/gemfile/specific_platform_spec.rb b/spec/bundler/install/gemfile/specific_platform_spec.rb index 113a0a1352..5f1b034bfc 100644 --- a/spec/bundler/install/gemfile/specific_platform_spec.rb +++ b/spec/bundler/install/gemfile/specific_platform_spec.rb @@ -6,29 +6,29 @@ RSpec.describe "bundle install with specific platforms" do gem "google-protobuf" G - context "when on a darwin machine" do - before { simulate_platform "x86_64-darwin-15" } - - it "locks to the specific darwin platform" do + it "locks to the specific darwin platform" do + simulate_platform "x86_64-darwin-15" do setup_multiplatform_gem install_gemfile(google_protobuf) allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile) - expect(the_bundle.locked_gems.platforms).to eq([pl("x86_64-darwin-15")]) + expect(the_bundle.locked_gems.platforms).to include(pl("x86_64-darwin-15")) expect(the_bundle).to include_gem("google-protobuf 3.0.0.alpha.5.0.5.1 universal-darwin") - expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[ - google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin - ]) + expect(the_bundle.locked_gems.specs.map(&:full_name)).to include( + "google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin" + ) end + end - it "understands that a non-platform specific gem in a old lockfile doesn't necessarily mean installing the non-specific variant" do + it "understands that a non-platform specific gem in a old lockfile doesn't necessarily mean installing the non-specific variant" do + simulate_platform "x86_64-darwin-15" do setup_multiplatform_gem system_gems "bundler-2.1.4" # Consistent location to install and look for gems - bundle "config set --local path vendor/bundle", :env => { "BUNDLER_VERSION" => "2.1.4" } + bundle "config set --local path vendor/bundle", env: { "BUNDLER_VERSION" => "2.1.4" } - install_gemfile(google_protobuf, :env => { "BUNDLER_VERSION" => "2.1.4" }) + install_gemfile(google_protobuf, env: { "BUNDLER_VERSION" => "2.1.4" }) # simulate lockfile created with old bundler, which only locks for ruby platform lockfile <<-L @@ -48,22 +48,28 @@ RSpec.describe "bundle install with specific platforms" do L # force strict usage of the lock file by setting frozen mode - bundle "config set --local frozen true", :env => { "BUNDLER_VERSION" => "2.1.4" } + bundle "config set --local frozen true", env: { "BUNDLER_VERSION" => "2.1.4" } # make sure the platform that got actually installed with the old bundler is used expect(the_bundle).to include_gem("google-protobuf 3.0.0.alpha.5.0.5.1 universal-darwin") end + end - it "understands that a non-platform specific gem in a new lockfile locked only to RUBY doesn't necessarily mean installing the non-specific variant" do + it "understands that a non-platform specific gem in a new lockfile locked only to RUBY doesn't necessarily mean installing the non-specific variant" do + simulate_platform "x86_64-darwin-15" do setup_multiplatform_gem system_gems "bundler-2.1.4" # Consistent location to install and look for gems - bundle "config set --local path vendor/bundle", :env => { "BUNDLER_VERSION" => "2.1.4" } + bundle "config set --local path vendor/bundle", env: { "BUNDLER_VERSION" => "2.1.4" } gemfile google_protobuf + checksums = checksums_section_when_existing do |c| + c.checksum gem_repo2, "google-protobuf", "3.0.0.alpha.4.0" + end + # simulate lockfile created with old bundler, which only locks for ruby platform lockfile <<-L GEM @@ -76,12 +82,14 @@ RSpec.describe "bundle install with specific platforms" do DEPENDENCIES google-protobuf - + #{checksums} BUNDLED WITH 2.1.4 L - bundle "update", :env => { "BUNDLER_VERSION" => Bundler::VERSION } + bundle "update", env: { "BUNDLER_VERSION" => Bundler::VERSION } + + checksums.checksum gem_repo2, "google-protobuf", "3.0.0.alpha.5.0.5.1" # make sure the platform that the platform specific dependency is used, since we're only locked to ruby expect(the_bundle).to include_gem("google-protobuf 3.0.0.alpha.5.0.5.1 universal-darwin") @@ -98,13 +106,61 @@ RSpec.describe "bundle install with specific platforms" do DEPENDENCIES google-protobuf + #{checksums} + BUNDLED WITH + #{Bundler::VERSION} + L + end + end + + context "when running on a legacy lockfile locked only to RUBY" do + around do |example| + build_repo4 do + build_gem "nokogiri", "1.3.10" + build_gem "nokogiri", "1.3.10" do |s| + s.platform = "arm64-darwin" + s.required_ruby_version = "< #{Gem.ruby_version}" + end + end + + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + + gem "nokogiri" + G + + lockfile <<-L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + nokogiri (1.3.10) + + PLATFORMS + ruby + + DEPENDENCIES + nokogiri BUNDLED WITH #{Bundler::VERSION} L + + simulate_platform "arm64-darwin-22", &example end - it "doesn't discard previously installed platform specific gem and fall back to ruby on subsequent bundles" do + it "still installs the generic RUBY variant if necessary" do + bundle "install --verbose", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } + expect(out).to include("Installing nokogiri 1.3.10") + end + + it "still installs the generic RUBY variant if necessary, even in frozen mode" do + bundle "install --verbose", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s, "BUNDLE_FROZEN" => "true" } + expect(out).to include("Installing nokogiri 1.3.10") + end + end + + it "doesn't discard previously installed platform specific gem and fall back to ruby on subsequent bundles" do + simulate_platform "x86_64-darwin-15" do build_repo2 do build_gem("libv8", "8.4.255.0") build_gem("libv8", "8.4.255.0") {|s| s.platform = "universal-darwin" } @@ -117,7 +173,7 @@ RSpec.describe "bundle install with specific platforms" do system_gems "bundler-2.1.4" # Consistent location to install and look for gems - bundle "config set --local path vendor/bundle", :env => { "BUNDLER_VERSION" => "2.1.4" } + bundle "config set --local path vendor/bundle", env: { "BUNDLER_VERSION" => "2.1.4" } gemfile <<-G source "https://localgemserver.test" @@ -141,14 +197,50 @@ RSpec.describe "bundle install with specific platforms" do 2.1.4 L - bundle "install --verbose", :artifice => "compact_index", :env => { "BUNDLER_VERSION" => "2.1.4", "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s } + bundle "install --verbose", artifice: "compact_index", env: { "BUNDLER_VERSION" => "2.1.4", "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s } expect(out).to include("Installing libv8 8.4.255.0 (universal-darwin)") - bundle "add mini_racer --verbose", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s } + bundle "add mini_racer --verbose", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s } expect(out).to include("Using libv8 8.4.255.0 (universal-darwin)") end + end - it "caches the universal-darwin gem when --all-platforms is passed and properly picks it up on further bundler invocations" do + it "chooses platform specific gems even when resolving upon materialization and the API returns more specific platforms first" do + simulate_platform "x86_64-darwin-15" do + build_repo4 do + build_gem("grpc", "1.50.0") + build_gem("grpc", "1.50.0") {|s| s.platform = "universal-darwin" } + end + + gemfile <<-G + source "https://localgemserver.test" + gem "grpc" + G + + # simulate lockfile created with old bundler, which only locks for ruby platform + lockfile <<-L + GEM + remote: https://localgemserver.test/ + specs: + grpc (1.50.0) + + PLATFORMS + ruby + + DEPENDENCIES + grpc + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "install --verbose", artifice: "compact_index_precompiled_before", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } + expect(out).to include("Installing grpc 1.50.0 (universal-darwin)") + end + end + + it "caches the universal-darwin gem when --all-platforms is passed and properly picks it up on further bundler invocations" do + simulate_platform "x86_64-darwin-15" do setup_multiplatform_gem gemfile(google_protobuf) bundle "cache --all-platforms" @@ -157,8 +249,10 @@ RSpec.describe "bundle install with specific platforms" do bundle "install --verbose" expect(err).to be_empty end + end - it "caches the universal-darwin gem when cache_all_platforms is configured and properly picks it up on further bundler invocations" do + it "caches the universal-darwin gem when cache_all_platforms is configured and properly picks it up on further bundler invocations" do + simulate_platform "x86_64-darwin-15" do setup_multiplatform_gem gemfile(google_protobuf) bundle "config set --local cache_all_platforms true" @@ -168,44 +262,46 @@ RSpec.describe "bundle install with specific platforms" do bundle "install --verbose" expect(err).to be_empty end + end - it "caches multiplatform git gems with a single gemspec when --all-platforms is passed" do - git = build_git "pg_array_parser", "1.0" + it "caches multiplatform git gems with a single gemspec when --all-platforms is passed" do + git = build_git "pg_array_parser", "1.0" - gemfile <<-G - source "#{file_uri_for(gem_repo1)}" - gem "pg_array_parser", :git => "#{lib_path("pg_array_parser-1.0")}" - G + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "pg_array_parser", :git => "#{lib_path("pg_array_parser-1.0")}" + G - lockfile <<-L - GIT - remote: #{lib_path("pg_array_parser-1.0")} - revision: #{git.ref_for("master")} - specs: - pg_array_parser (1.0-java) - pg_array_parser (1.0) + lockfile <<-L + GIT + remote: #{lib_path("pg_array_parser-1.0")} + revision: #{git.ref_for("main")} + specs: + pg_array_parser (1.0-java) + pg_array_parser (1.0) - GEM - specs: + GEM + specs: - PLATFORMS - java - #{lockfile_platforms} + PLATFORMS + java + #{lockfile_platforms} - DEPENDENCIES - pg_array_parser! + DEPENDENCIES + pg_array_parser! - BUNDLED WITH - #{Bundler::VERSION} - L + BUNDLED WITH + #{Bundler::VERSION} + L - bundle "config set --local cache_all true" - bundle "cache --all-platforms" + bundle "config set --local cache_all true" + bundle "cache --all-platforms" - expect(err).to be_empty - end + expect(err).to be_empty + end - it "uses the platform-specific gem with extra dependencies" do + it "uses the platform-specific gem with extra dependencies" do + simulate_platform "x86_64-darwin-15" do setup_multiplatform_gem_with_different_dependencies_per_platform install_gemfile <<-G source "#{file_uri_for(gem_repo2)}" @@ -213,47 +309,52 @@ RSpec.describe "bundle install with specific platforms" do G allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile) - expect(the_bundle.locked_gems.platforms).to eq([pl("x86_64-darwin-15")]) + expect(the_bundle.locked_gems.platforms).to include(pl("x86_64-darwin-15")) expect(the_bundle).to include_gems("facter 2.4.6 universal-darwin", "CFPropertyList 1.0") - expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(["CFPropertyList-1.0", - "facter-2.4.6-universal-darwin"]) + expect(the_bundle.locked_gems.specs.map(&:full_name)).to include("CFPropertyList-1.0", + "facter-2.4.6-universal-darwin") end + end - context "when adding a platform via lock --add_platform" do - before do - allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile) - end + context "when adding a platform via lock --add_platform" do + before do + allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile) + end - it "adds the foreign platform" do + it "adds the foreign platform" do + simulate_platform "x86_64-darwin-15" do setup_multiplatform_gem install_gemfile(google_protobuf) - bundle "lock --add-platform=#{x64_mingw}" + bundle "lock --add-platform=#{x64_mingw32}" - expect(the_bundle.locked_gems.platforms).to eq([x64_mingw, pl("x86_64-darwin-15")]) - expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[ + expect(the_bundle.locked_gems.platforms).to include(x64_mingw32, pl("x86_64-darwin-15")) + expect(the_bundle.locked_gems.specs.map(&:full_name)).to include(*%w[ google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin google-protobuf-3.0.0.alpha.5.0.5.1-x64-mingw32 ]) end + end - it "falls back on plain ruby when that version doesnt have a platform-specific gem" do + it "falls back on plain ruby when that version doesn't have a platform-specific gem" do + simulate_platform "x86_64-darwin-15" do setup_multiplatform_gem install_gemfile(google_protobuf) bundle "lock --add-platform=#{java}" - expect(the_bundle.locked_gems.platforms).to eq([java, pl("x86_64-darwin-15")]) - expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[ - google-protobuf-3.0.0.alpha.5.0.5.1 - google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin - ]) + expect(the_bundle.locked_gems.platforms).to include(java, pl("x86_64-darwin-15")) + expect(the_bundle.locked_gems.specs.map(&:full_name)).to include( + "google-protobuf-3.0.0.alpha.5.0.5.1", + "google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin" + ) end end end - it "installs sorbet-static, which does not provide a pure ruby variant, just fine on truffleruby", :truffleruby do + it "installs sorbet-static, which does not provide a pure ruby variant, just fine", :truffleruby do + skip "does not apply to Windows" if Gem.win_platform? + build_repo2 do - build_gem("sorbet-static", "0.5.6403") {|s| s.platform = "x86_64-linux" } - build_gem("sorbet-static", "0.5.6403") {|s| s.platform = "universal-darwin-20" } + build_gem("sorbet-static", "0.5.6403") {|s| s.platform = Bundler.local_platform } end gemfile <<~G @@ -266,8 +367,7 @@ RSpec.describe "bundle install with specific platforms" do GEM remote: #{file_uri_for(gem_repo2)}/ specs: - sorbet-static (0.5.6403-universal-darwin-20) - sorbet-static (0.5.6403-x86_64-linux) + sorbet-static (0.5.6403-#{Bundler.local_platform}) PLATFORMS ruby @@ -283,54 +383,1001 @@ RSpec.describe "bundle install with specific platforms" do end it "does not resolve if the current platform does not match any of available platform specific variants for a top level dependency" do - build_repo2 do + build_repo4 do build_gem("sorbet-static", "0.5.6433") {|s| s.platform = "x86_64-linux" } build_gem("sorbet-static", "0.5.6433") {|s| s.platform = "universal-darwin-20" } end gemfile <<~G - source "#{file_uri_for(gem_repo2)}" + source "#{file_uri_for(gem_repo4)}" gem "sorbet-static", "0.5.6433" G - simulate_platform "arm64-darwin-21" do - bundle "install", :raise_on_error => false - end - - expect(err).to include <<~ERROR.rstrip - Could not find gem 'sorbet-static (= 0.5.6433) arm64-darwin-21' in rubygems repository #{file_uri_for(gem_repo2)}/ or installed locally. + error_message = <<~ERROR.strip + Could not find gem 'sorbet-static (= 0.5.6433)' with platform 'arm64-darwin-21' in rubygems repository #{file_uri_for(gem_repo4)}/, cached gems or installed locally. The source contains the following gems matching 'sorbet-static (= 0.5.6433)': * sorbet-static-0.5.6433-universal-darwin-20 * sorbet-static-0.5.6433-x86_64-linux ERROR + + simulate_platform "arm64-darwin-21" do + bundle "lock", raise_on_error: false + end + + expect(err).to include(error_message).once + + # Make sure it doesn't print error twice in verbose mode + + simulate_platform "arm64-darwin-21" do + bundle "lock --verbose", raise_on_error: false + end + + expect(err).to include(error_message).once end it "does not resolve if the current platform does not match any of available platform specific variants for a transitive dependency" do - build_repo2 do + build_repo4 do build_gem("sorbet", "0.5.6433") {|s| s.add_dependency "sorbet-static", "= 0.5.6433" } build_gem("sorbet-static", "0.5.6433") {|s| s.platform = "x86_64-linux" } build_gem("sorbet-static", "0.5.6433") {|s| s.platform = "universal-darwin-20" } end gemfile <<~G - source "#{file_uri_for(gem_repo2)}" + source "#{file_uri_for(gem_repo4)}" gem "sorbet", "0.5.6433" G - simulate_platform "arm64-darwin-21" do - bundle "install", :raise_on_error => false - end + error_message = <<~ERROR.strip + Could not find compatible versions - expect(err).to include <<~ERROR.rstrip - Could not find gem 'sorbet-static (= 0.5.6433) arm64-darwin-21', which is required by gem 'sorbet (= 0.5.6433)', in rubygems repository #{file_uri_for(gem_repo2)}/ or installed locally. + Because every version of sorbet depends on sorbet-static = 0.5.6433 + and sorbet-static = 0.5.6433 could not be found in rubygems repository #{file_uri_for(gem_repo4)}/, cached gems or installed locally for any resolution platforms (arm64-darwin-21), + sorbet cannot be used. + So, because Gemfile depends on sorbet = 0.5.6433, + version solving has failed. The source contains the following gems matching 'sorbet-static (= 0.5.6433)': * sorbet-static-0.5.6433-universal-darwin-20 * sorbet-static-0.5.6433-x86_64-linux ERROR + + simulate_platform "arm64-darwin-21" do + bundle "lock", raise_on_error: false + end + + expect(err).to include(error_message).once + + # Make sure it doesn't print error twice in verbose mode + + simulate_platform "arm64-darwin-21" do + bundle "lock --verbose", raise_on_error: false + end + + expect(err).to include(error_message).once + end + + it "does not generate a lockfile if RUBY platform is forced and some gem has no RUBY variant available" do + build_repo4 do + build_gem("sorbet-static", "0.5.9889") {|s| s.platform = Gem::Platform.local } + end + + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + + gem "sorbet-static", "0.5.9889" + G + + bundle "lock", raise_on_error: false, env: { "BUNDLE_FORCE_RUBY_PLATFORM" => "true" } + + expect(err).to include <<~ERROR.rstrip + Could not find gem 'sorbet-static (= 0.5.9889)' with platform 'ruby' in rubygems repository #{file_uri_for(gem_repo4)}/, cached gems or installed locally. + + The source contains the following gems matching 'sorbet-static (= 0.5.9889)': + * sorbet-static-0.5.9889-#{Gem::Platform.local} + ERROR + end + + it "automatically fixes the lockfile if RUBY platform is locked and some gem has no RUBY variant available" do + build_repo4 do + build_gem("sorbet-static-and-runtime", "0.5.10160") do |s| + s.add_runtime_dependency "sorbet", "= 0.5.10160" + s.add_runtime_dependency "sorbet-runtime", "= 0.5.10160" + end + + build_gem("sorbet", "0.5.10160") do |s| + s.add_runtime_dependency "sorbet-static", "= 0.5.10160" + end + + build_gem("sorbet-runtime", "0.5.10160") + + build_gem("sorbet-static", "0.5.10160") do |s| + s.platform = Gem::Platform.local + end + end + + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + + gem "sorbet-static-and-runtime" + G + + lockfile <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + sorbet (0.5.10160) + sorbet-static (= 0.5.10160) + sorbet-runtime (0.5.10160) + sorbet-static (0.5.10160-#{Gem::Platform.local}) + sorbet-static-and-runtime (0.5.10160) + sorbet (= 0.5.10160) + sorbet-runtime (= 0.5.10160) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + sorbet-static-and-runtime + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "update" + + checksums = checksums_section_when_existing do |c| + c.checksum gem_repo4, "sorbet", "0.5.10160" + c.checksum gem_repo4, "sorbet-runtime", "0.5.10160" + c.checksum gem_repo4, "sorbet-static", "0.5.10160", Gem::Platform.local + c.checksum gem_repo4, "sorbet-static-and-runtime", "0.5.10160" + end + + expect(lockfile).to eq <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + sorbet (0.5.10160) + sorbet-static (= 0.5.10160) + sorbet-runtime (0.5.10160) + sorbet-static (0.5.10160-#{Gem::Platform.local}) + sorbet-static-and-runtime (0.5.10160) + sorbet (= 0.5.10160) + sorbet-runtime (= 0.5.10160) + + PLATFORMS + #{local_platform} + + DEPENDENCIES + sorbet-static-and-runtime + #{checksums} + BUNDLED WITH + #{Bundler::VERSION} + L + end + + it "automatically fixes the lockfile if both RUBY platform and a more specific platform are locked, and some gem has no RUBY variant available" do + build_repo4 do + build_gem "nokogiri", "1.12.0" + build_gem "nokogiri", "1.12.0" do |s| + s.platform = "x86_64-darwin" + end + + build_gem "nokogiri", "1.13.0" + build_gem "nokogiri", "1.13.0" do |s| + s.platform = "x86_64-darwin" + end + + build_gem("sorbet-static", "0.5.10601") do |s| + s.platform = "x86_64-darwin" + end + end + + simulate_platform "x86_64-darwin-22" do + install_gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + + gem "nokogiri" + gem "sorbet-static" + G + end + + checksums = checksums_section_when_existing do |c| + c.no_checksum "nokogiri", "1.13.0", "x86_64-darwin" + c.no_checksum "sorbet-static", "0.5.10601", "x86_64-darwin" + end + + lockfile <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + nokogiri (1.12.0) + nokogiri (1.12.0-x86_64-darwin) + sorbet-static (0.5.10601-x86_64-darwin) + + PLATFORMS + ruby + x86_64-darwin + + DEPENDENCIES + nokogiri + sorbet-static + #{checksums} + BUNDLED WITH + #{Bundler::VERSION} + L + + simulate_platform "x86_64-darwin-22" do + bundle "update --conservative nokogiri" + end + + expect(lockfile).to eq <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + nokogiri (1.13.0-x86_64-darwin) + sorbet-static (0.5.10601-x86_64-darwin) + + PLATFORMS + x86_64-darwin + + DEPENDENCIES + nokogiri + sorbet-static + #{checksums} + BUNDLED WITH + #{Bundler::VERSION} + L + end + + it "automatically fixes the lockfile if only RUBY platform is locked and some gem has no RUBY variant available" do + build_repo4 do + build_gem("sorbet-static-and-runtime", "0.5.10160") do |s| + s.add_runtime_dependency "sorbet", "= 0.5.10160" + s.add_runtime_dependency "sorbet-runtime", "= 0.5.10160" + end + + build_gem("sorbet", "0.5.10160") do |s| + s.add_runtime_dependency "sorbet-static", "= 0.5.10160" + end + + build_gem("sorbet-runtime", "0.5.10160") + + build_gem("sorbet-static", "0.5.10160") do |s| + s.platform = Gem::Platform.local + end + end + + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + + gem "sorbet-static-and-runtime" + G + + lockfile <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + sorbet (0.5.10160) + sorbet-static (= 0.5.10160) + sorbet-runtime (0.5.10160) + sorbet-static (0.5.10160-#{Gem::Platform.local}) + sorbet-static-and-runtime (0.5.10160) + sorbet (= 0.5.10160) + sorbet-runtime (= 0.5.10160) + + PLATFORMS + ruby + + DEPENDENCIES + sorbet-static-and-runtime + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "update" + + checksums = checksums_section_when_existing do |c| + c.checksum gem_repo4, "sorbet", "0.5.10160" + c.checksum gem_repo4, "sorbet-runtime", "0.5.10160" + c.checksum gem_repo4, "sorbet-static", "0.5.10160", Gem::Platform.local + c.checksum gem_repo4, "sorbet-static-and-runtime", "0.5.10160" + end + + expect(lockfile).to eq <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + sorbet (0.5.10160) + sorbet-static (= 0.5.10160) + sorbet-runtime (0.5.10160) + sorbet-static (0.5.10160-#{Gem::Platform.local}) + sorbet-static-and-runtime (0.5.10160) + sorbet (= 0.5.10160) + sorbet-runtime (= 0.5.10160) + + PLATFORMS + #{local_platform} + + DEPENDENCIES + sorbet-static-and-runtime + #{checksums} + BUNDLED WITH + #{Bundler::VERSION} + L + end + + it "automatically fixes the lockfile if multiple platforms locked, but no valid versions of direct dependencies for all of them" 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 + + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + + gem "nokogiri" + gem "sorbet-static" + G + + lockfile <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + nokogiri (1.14.0-arm-linux) + nokogiri (1.14.0-x86_64-linux) + sorbet-static (0.5.10696-x86_64-linux) + + PLATFORMS + aarch64-linux + arm-linux + x86_64-linux + + DEPENDENCIES + nokogiri + sorbet-static + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "update" + + checksums = checksums_section_when_existing do |c| + c.checksum gem_repo4, "nokogiri", "1.14.0", "x86_64-linux" + c.checksum gem_repo4, "sorbet-static", "0.5.10696", "x86_64-linux" + end + + expect(lockfile).to eq <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + nokogiri (1.14.0-x86_64-linux) + sorbet-static (0.5.10696-x86_64-linux) + + PLATFORMS + x86_64-linux + + DEPENDENCIES + nokogiri + sorbet-static + #{checksums} + BUNDLED WITH + #{Bundler::VERSION} + L + end + end + + it "automatically fixes the lockfile without removing other variants if it's missing platform gems, but they are installed locally" do + simulate_platform "x86_64-darwin-21" do + build_repo4 do + build_gem("sorbet-static", "0.5.10549") do |s| + s.platform = "universal-darwin-20" + end + + build_gem("sorbet-static", "0.5.10549") do |s| + s.platform = "universal-darwin-21" + end + end + + # Make sure sorbet-static-0.5.10549-universal-darwin-21 is installed + install_gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + + gem "sorbet-static", "= 0.5.10549" + G + + checksums = checksums_section_when_existing do |c| + c.checksum gem_repo4, "sorbet-static", "0.5.10549", "universal-darwin-20" + c.checksum gem_repo4, "sorbet-static", "0.5.10549", "universal-darwin-21" + end + + # Make sure the lockfile is missing sorbet-static-0.5.10549-universal-darwin-21 + lockfile <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + sorbet-static (0.5.10549-universal-darwin-20) + + PLATFORMS + x86_64-darwin + + DEPENDENCIES + sorbet-static (= 0.5.10549) + #{checksums} + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "install" + + checksums.no_checksum "sorbet-static", "0.5.10549", "universal-darwin-21" + + expect(lockfile).to eq <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + sorbet-static (0.5.10549-universal-darwin-20) + sorbet-static (0.5.10549-universal-darwin-21) + + PLATFORMS + x86_64-darwin + + DEPENDENCIES + sorbet-static (= 0.5.10549) + #{checksums} + BUNDLED WITH + #{Bundler::VERSION} + L + end + end + + it "does not remove ruby if gems for other platforms, and not present in the lockfile, exist in the Gemfile" do + build_repo4 do + build_gem "nokogiri", "1.13.8" + build_gem "nokogiri", "1.13.8" do |s| + s.platform = Gem::Platform.local + end + end + + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + + gem "nokogiri" + + gem "tzinfo", "~> 1.2", platform: :#{not_local_tag} + G + + original_lockfile = <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + nokogiri (1.13.8) + nokogiri (1.13.8-#{Gem::Platform.local}) + + PLATFORMS + #{lockfile_platforms("ruby")} + + DEPENDENCIES + nokogiri + tzinfo (~> 1.2) + + CHECKSUMS + + BUNDLED WITH + #{Bundler::VERSION} + L + + lockfile original_lockfile + + bundle "lock --update" + + checksums = checksums_section_when_existing do |c| + c.no_checksum "nokogiri", "1.13.8" + c.no_checksum "nokogiri", "1.13.8", Gem::Platform.local + end + + updated_lockfile = <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + nokogiri (1.13.8) + nokogiri (1.13.8-#{Gem::Platform.local}) + + PLATFORMS + #{lockfile_platforms("ruby")} + + DEPENDENCIES + nokogiri + tzinfo (~> 1.2) + #{checksums} + BUNDLED WITH + #{Bundler::VERSION} + L + + expect(lockfile).to eq(updated_lockfile) + end + + it "does not remove ruby when adding a new gem to the Gemfile" do + build_repo4 do + build_gem "concurrent-ruby", "1.2.2" + build_gem "rack", "3.0.7" + end + + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + + gem "concurrent-ruby" + gem "rack" + G + + checksums = checksums_section_when_existing do |c| + c.no_checksum "concurrent-ruby", "1.2.2" + c.no_checksum "rack", "3.0.7" + end + + lockfile <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + concurrent-ruby (1.2.2) + + PLATFORMS + ruby + + DEPENDENCIES + concurrent-ruby + #{checksums} + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "lock" + + expect(lockfile).to eq <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + concurrent-ruby (1.2.2) + rack (3.0.7) + + PLATFORMS + #{lockfile_platforms("ruby", generic_local_platform, defaults: [])} + + DEPENDENCIES + concurrent-ruby + rack + #{checksums} + BUNDLED WITH + #{Bundler::VERSION} + L + end + + it "can fallback to a source gem when platform gems are incompatible with current ruby version" do + setup_multiplatform_gem_with_source_gem + + source = file_uri_for(gem_repo2) + + gemfile <<~G + source "#{source}" + + gem "my-precompiled-gem" + G + + # simulate lockfile which includes both a precompiled gem with: + # - Gem the current platform (with incompatible ruby version) + # - A source gem with compatible ruby version + lockfile <<-L + GEM + remote: #{source}/ + specs: + my-precompiled-gem (3.0.0) + my-precompiled-gem (3.0.0-#{Bundler.local_platform}) + + PLATFORMS + ruby + #{Bundler.local_platform} + + DEPENDENCIES + my-precompiled-gem + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle :install + end + + it "automatically fixes the lockfile if the specific platform is locked and we move to a newer ruby version for which a native package is not available" do + # + # Given an existing application using native gems (e.g., nokogiri) + # And a lockfile generated with a stable ruby version + # When want test the application against ruby-head and `bundle install` + # Then bundler should fall back to the generic ruby platform gem + # + simulate_platform "x86_64-linux" do + build_repo4 do + build_gem "nokogiri", "1.14.0" + build_gem "nokogiri", "1.14.0" do |s| + s.platform = "x86_64-linux" + s.required_ruby_version = "< #{Gem.ruby_version}" + end + end + + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + + gem "nokogiri", "1.14.0" + G + + checksums = checksums_section_when_existing do |c| + c.checksum gem_repo4, "nokogiri", "1.14.0", "x86_64-linux" + end + + lockfile <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + nokogiri (1.14.0-x86_64-linux) + + PLATFORMS + x86_64-linux + + DEPENDENCIES + nokogiri (= 1.14.0) + #{checksums} + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle :install + + checksums = checksums_section_when_existing do |c| + c.checksum gem_repo4, "nokogiri", "1.14.0" + end + + expect(lockfile).to eq(<<~L) + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + nokogiri (1.14.0) + + PLATFORMS + x86_64-linux + + DEPENDENCIES + nokogiri (= 1.14.0) + #{checksums} + BUNDLED WITH + #{Bundler::VERSION} + L + end + end + + it "locks specific platforms automatically" do + simulate_platform "x86_64-linux" do + build_repo4 do + build_gem "nokogiri", "1.14.0" + 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 "nokogiri", "1.14.0" do |s| + s.platform = "x64-mingw32" + end + build_gem "nokogiri", "1.14.0" do |s| + s.platform = "java" + end + + build_gem "sorbet-static", "0.5.10696" do |s| + s.platform = "x86_64-linux" + end + build_gem "sorbet-static", "0.5.10696" do |s| + s.platform = "universal-darwin-22" + end + end + + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + + gem "nokogiri" + G + + bundle "lock" + + checksums = checksums_section_when_existing do |c| + c.no_checksum "nokogiri", "1.14.0" + c.no_checksum "nokogiri", "1.14.0", "arm-linux" + c.no_checksum "nokogiri", "1.14.0", "x86_64-linux" + end + + # locks all compatible platforms, excluding Java and Windows + expect(lockfile).to eq(<<~L) + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + nokogiri (1.14.0) + nokogiri (1.14.0-arm-linux) + nokogiri (1.14.0-x86_64-linux) + + PLATFORMS + arm-linux + ruby + x86_64-linux + + DEPENDENCIES + nokogiri + #{checksums} + BUNDLED WITH + #{Bundler::VERSION} + L + + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + + gem "nokogiri" + gem "sorbet-static" + G + + FileUtils.rm bundled_app_lock + + bundle "lock" + + checksums.delete "nokogiri", "arm-linux" + checksums.no_checksum "sorbet-static", "0.5.10696", "universal-darwin-22" + checksums.no_checksum "sorbet-static", "0.5.10696", "x86_64-linux" + + # locks only platforms compatible with all gems in the bundle + expect(lockfile).to eq(<<~L) + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + nokogiri (1.14.0) + nokogiri (1.14.0-x86_64-linux) + sorbet-static (0.5.10696-universal-darwin-22) + sorbet-static (0.5.10696-x86_64-linux) + + PLATFORMS + universal-darwin-22 + x86_64-linux + + DEPENDENCIES + nokogiri + sorbet-static + #{checksums} + BUNDLED WITH + #{Bundler::VERSION} + L + end + end + + it "does not fail when a platform variant is incompatible with the current ruby and another equivalent platform specific variant is part of the resolution", rubygems: ">= 3.3.21" do + build_repo4 do + build_gem "nokogiri", "1.15.5" + + build_gem "nokogiri", "1.15.5" do |s| + s.platform = "x86_64-linux" + s.required_ruby_version = "< #{current_ruby_minor}.dev" + end + + build_gem "sass-embedded", "1.69.5" + + build_gem "sass-embedded", "1.69.5" do |s| + s.platform = "x86_64-linux-gnu" + end + end + + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + + gem "nokogiri" + gem "sass-embedded" + G + + checksums = checksums_section_when_existing do |c| + c.checksum gem_repo4, "nokogiri", "1.15.5" + c.no_checksum "sass-embedded", "1.69.5" + c.checksum gem_repo4, "sass-embedded", "1.69.5", "x86_64-linux-gnu" + end + + simulate_platform "x86_64-linux" do + bundle "install --verbose", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } + + # locks all compatible platforms, excluding Java and Windows + expect(lockfile).to eq(<<~L) + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + nokogiri (1.15.5) + sass-embedded (1.69.5) + sass-embedded (1.69.5-x86_64-linux-gnu) + + PLATFORMS + ruby + x86_64-linux + + DEPENDENCIES + nokogiri + sass-embedded + #{checksums} + BUNDLED WITH + #{Bundler::VERSION} + L + end + end + + it "does not add ruby platform gem if it brings extra dependencies not resolved originally" do + build_repo4 do + build_gem "nokogiri", "1.15.5" do |s| + s.add_dependency "mini_portile2", "~> 2.8.2" + end + + build_gem "nokogiri", "1.15.5" do |s| + s.platform = "x86_64-linux" + end + end + + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + + gem "nokogiri" + G + + checksums = checksums_section_when_existing do |c| + c.checksum gem_repo4, "nokogiri", "1.15.5", "x86_64-linux" + end + + simulate_platform "x86_64-linux" do + bundle "install --verbose", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } + + expect(lockfile).to eq(<<~L) + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + nokogiri (1.15.5-x86_64-linux) + + PLATFORMS + x86_64-linux + + DEPENDENCIES + nokogiri + #{checksums} + BUNDLED WITH + #{Bundler::VERSION} + L + end + end + + ["x86_64-linux", "x86_64-linux-musl"].each do |host_platform| + describe "on host platform #{host_platform}" do + it "adds current musl platform" do + build_repo4 do + build_gem "rcee_precompiled", "0.5.0" do |s| + s.platform = "x86_64-linux" + end + + build_gem "rcee_precompiled", "0.5.0" do |s| + s.platform = "x86_64-linux-musl" + end + end + + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + + gem "rcee_precompiled", "0.5.0" + G + + simulate_platform host_platform do + bundle "lock", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } + + expect(lockfile).to eq(<<~L) + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + rcee_precompiled (0.5.0-x86_64-linux) + rcee_precompiled (0.5.0-x86_64-linux-musl) + + PLATFORMS + x86_64-linux + x86_64-linux-musl + + DEPENDENCIES + rcee_precompiled (= 0.5.0) + + BUNDLED WITH + #{Bundler::VERSION} + L + end + end + end + end + + it "adds current musl platform, when there are also gnu variants", rubygems: ">= 3.3.21" do + build_repo4 do + build_gem "rcee_precompiled", "0.5.0" do |s| + s.platform = "x86_64-linux-gnu" + end + + build_gem "rcee_precompiled", "0.5.0" do |s| + s.platform = "x86_64-linux-musl" + end + end + + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + + gem "rcee_precompiled", "0.5.0" + G + + simulate_platform "x86_64-linux-musl" do + bundle "lock", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } + + expect(lockfile).to eq(<<~L) + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + rcee_precompiled (0.5.0-x86_64-linux-gnu) + rcee_precompiled (0.5.0-x86_64-linux-musl) + + PLATFORMS + x86_64-linux-gnu + x86_64-linux-musl + + DEPENDENCIES + rcee_precompiled (= 0.5.0) + + BUNDLED WITH + #{Bundler::VERSION} + L + end + end + + it "does not add current platform if there's an equivalent less specific platform among the ones resolved" do + build_repo4 do + build_gem "rcee_precompiled", "0.5.0" do |s| + s.platform = "universal-darwin" + end + end + + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + + gem "rcee_precompiled", "0.5.0" + G + + simulate_platform "x86_64-darwin-15" do + bundle "lock", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } + + expect(lockfile).to eq(<<~L) + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + rcee_precompiled (0.5.0-universal-darwin) + + PLATFORMS + universal-darwin + + DEPENDENCIES + rcee_precompiled (= 0.5.0) + + BUNDLED WITH + #{Bundler::VERSION} + L + end end private @@ -363,4 +1410,16 @@ RSpec.describe "bundle install with specific platforms" do build_gem("CFPropertyList") end end + + def setup_multiplatform_gem_with_source_gem + build_repo2 do + build_gem("my-precompiled-gem", "3.0.0") + build_gem("my-precompiled-gem", "3.0.0") do |s| + s.platform = Bundler.local_platform + + # purposely unresolvable + s.required_ruby_version = ">= 1000.0.0" + end + end + end end diff --git a/spec/bundler/install/gemfile_spec.rb b/spec/bundler/install/gemfile_spec.rb index 0f8f1ecfa8..158645d3eb 100644 --- a/spec/bundler/install/gemfile_spec.rb +++ b/spec/bundler/install/gemfile_spec.rb @@ -3,7 +3,7 @@ RSpec.describe "bundle install" do context "with duplicated gems" do it "will display a warning" do - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G, raise_on_error: false source "#{file_uri_for(gem_repo1)}" gem 'rails', '~> 4.0.0' @@ -20,7 +20,7 @@ RSpec.describe "bundle install" do gem 'rack' G - bundle :install, :gemfile => bundled_app("NotGemfile") + bundle :install, gemfile: bundled_app("NotGemfile") # Specify BUNDLE_GEMFILE for `the_bundle` # to retrieve the proper Gemfile @@ -46,8 +46,8 @@ RSpec.describe "bundle install" do end it "uses the gemfile while in a subdirectory" do bundled_app("subdir").mkpath - bundle "install", :dir => bundled_app("subdir") - bundle "list", :dir => bundled_app("subdir") + bundle "install", dir: bundled_app("subdir") + bundle "list", dir: bundled_app("subdir") expect(out).to include("rack (1.0.0)") end @@ -61,12 +61,12 @@ RSpec.describe "bundle install" do gem "rack", :lib => "rack" G - bundle :install, :raise_on_error => false + bundle :install, raise_on_error: false expect(err).to match(/You passed :lib as an option for gem 'rack', but it is invalid/) end end - context "with engine specified in symbol", :jruby do + context "with engine specified in symbol", :jruby_only do it "does not raise any error parsing Gemfile" do install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" diff --git a/spec/bundler/install/gems/compact_index_spec.rb b/spec/bundler/install/gems/compact_index_spec.rb index 72ad40e24f..50add8743b 100644 --- a/spec/bundler/install/gems/compact_index_spec.rb +++ b/spec/bundler/install/gems/compact_index_spec.rb @@ -10,7 +10,7 @@ RSpec.describe "compact index api" do gem "rack" G - bundle :install, :artifice => "compact_index" + bundle :install, artifice: "compact_index" expect(out).to include("Fetching gem metadata from #{source_uri}") expect(the_bundle).to include_gems "rack 1.0.0" end @@ -21,7 +21,7 @@ RSpec.describe "compact index api" do gem " sinatra" G - bundle :install, :artifice => "compact_index", :raise_on_error => false + bundle :install, artifice: "compact_index", raise_on_error: false expect(err).to include("' sinatra' is not a valid gem name because it contains whitespace.") end @@ -31,7 +31,7 @@ RSpec.describe "compact index api" do gem "rails" G - bundle :install, :artifice => "compact_index" + bundle :install, artifice: "compact_index" expect(out).to include("Fetching gem metadata from #{source_uri}") expect(the_bundle).to include_gems( "rails 2.3.2", @@ -44,14 +44,14 @@ RSpec.describe "compact index api" do end it "should handle case sensitivity conflicts" do - build_repo4 do + build_repo4(build_compact_index: false) do build_gem "rack", "1.0" do |s| s.add_runtime_dependency("Rack", "0.1") end build_gem "Rack", "0.1" end - install_gemfile <<-G, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } + install_gemfile <<-G, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } source "#{source_uri}" gem "rack", "1.0" gem "Rack", "0.1" @@ -69,7 +69,7 @@ RSpec.describe "compact index api" do gem "net-sftp" G - bundle :install, :artifice => "compact_index" + bundle :install, artifice: "compact_index" expect(the_bundle).to include_gems "net-sftp 1.1.1" end @@ -78,11 +78,11 @@ RSpec.describe "compact index api" do source "#{source_uri}" gem "rack" G - bundle :install, :artifice => "compact_index" + bundle :install, artifice: "compact_index" bundle "config set --local deployment true" bundle "config set --local path vendor/bundle" - bundle :install, :artifice => "compact_index" + bundle :install, artifice: "compact_index" expect(out).to include("Fetching gem metadata from #{source_uri}") expect(the_bundle).to include_gems "rack 1.0.0" end @@ -100,7 +100,7 @@ RSpec.describe "compact index api" do end G - bundle :install, :artifice => "compact_index" + bundle :install, artifice: "compact_index" expect(the_bundle).to include_gems("rails 2.3.2") end @@ -116,10 +116,10 @@ RSpec.describe "compact index api" do gem 'foo', :git => "#{file_uri_for(lib_path("foo-1.0"))}" G - bundle :install, :artifice => "compact_index" + bundle :install, artifice: "compact_index" bundle "config set --local deployment true" - bundle :install, :artifice => "compact_index" + bundle :install, artifice: "compact_index" expect(the_bundle).to include_gems("rails 2.3.2") end @@ -131,9 +131,9 @@ RSpec.describe "compact index api" do gem 'foo', :git => "#{file_uri_for(lib_path("foo-1.0"))}" G - bundle "install", :artifice => "compact_index" + bundle "install", artifice: "compact_index" bundle "config set --local deployment true" - bundle :install, :artifice => "compact_index" + bundle :install, artifice: "compact_index" expect(the_bundle).to include_gems("foo 1.0") end @@ -144,7 +144,7 @@ RSpec.describe "compact index api" do gem "rack" G - bundle :install, :verbose => true, :artifice => "compact_index_forbidden" + bundle :install, verbose: true, artifice: "compact_index_forbidden" expect(out).to include("Fetching gem metadata from #{source_uri}") expect(the_bundle).to include_gems "rack 1.0.0" end @@ -155,14 +155,34 @@ RSpec.describe "compact index api" do gem "rack" G - bundle :install, :verbose => true, :artifice => "compact_index_checksum_mismatch" + bundle :install, verbose: true, artifice: "compact_index_checksum_mismatch" expect(out).to include("Fetching gem metadata from #{source_uri}") - expect(out).to include <<-'WARN' -The checksum of /versions does not match the checksum provided by the server! Something is wrong (local checksum is "\"d41d8cd98f00b204e9800998ecf8427e\"", was expecting "\"123\""). - WARN + expect(out).to include("The checksum of /versions does not match the checksum provided by the server!") + expect(out).to include('Calculated checksums {"sha-256"=>"8KfZiM/fszVkqhP/m5s9lvE6M9xKu4I1bU4Izddp5Ms="} did not match expected {"sha-256"=>"ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD/YfIAFa0="}') expect(the_bundle).to include_gems "rack 1.0.0" end + it "shows proper path when permission errors happen", :permissions do + gemfile <<-G + source "#{source_uri}" + gem "rack" + G + + versions = Pathname.new(Bundler.rubygems.user_home).join( + ".bundle", "cache", "compact_index", + "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "versions" + ) + versions.dirname.mkpath + versions.write("created_at") + FileUtils.chmod("-r", versions) + + bundle :install, artifice: "compact_index", raise_on_error: false + + expect(err).to include( + "There was an error while trying to read from `#{versions}`. It is likely that you need to grant read permissions for that path." + ) + end + it "falls back when the user's home directory does not exist or is not writable" do ENV["HOME"] = tmp("missing_home").to_s @@ -171,7 +191,7 @@ The checksum of /versions does not match the checksum provided by the server! So gem "rack" G - bundle :install, :artifice => "compact_index" + bundle :install, artifice: "compact_index" expect(out).to include("Fetching gem metadata from #{source_uri}") expect(the_bundle).to include_gems "rack 1.0.0" end @@ -182,11 +202,11 @@ The checksum of /versions does not match the checksum provided by the server! So gem "rack" G - bundle :install, :artifice => "compact_index_host_redirect" + bundle :install, artifice: "compact_index_host_redirect" expect(the_bundle).to include_gems "rack 1.0.0" end - it "handles host redirects without Net::HTTP::Persistent" do + it "handles host redirects without Gem::Net::HTTP::Persistent" do gemfile <<-G source "#{source_uri}" gem "rack" @@ -205,7 +225,7 @@ The checksum of /versions does not match the checksum provided by the server! So H end - bundle :install, :artifice => "compact_index_host_redirect", :requires => [lib_path("disable_net_http_persistent.rb")] + bundle :install, artifice: "compact_index_host_redirect", requires: [lib_path("disable_net_http_persistent.rb")] expect(out).to_not match(/Too many redirects/) expect(the_bundle).to include_gems "rack 1.0.0" end @@ -216,7 +236,7 @@ The checksum of /versions does not match the checksum provided by the server! So gem "rack" G - bundle :install, :artifice => "compact_index_redirects", :raise_on_error => false + bundle :install, artifice: "compact_index_redirects", raise_on_error: false expect(err).to match(/Too many redirects/) end @@ -227,7 +247,7 @@ The checksum of /versions does not match the checksum provided by the server! So gem "rack" G - bundle "install --full-index", :artifice => "compact_index" + bundle "install --full-index", artifice: "compact_index" expect(out).to include("Fetching source index from #{source_uri}") expect(the_bundle).to include_gems "rack 1.0.0" end @@ -238,7 +258,7 @@ The checksum of /versions does not match the checksum provided by the server! So gem "rack" G - bundle "update --full-index", :artifice => "compact_index", :all => true + bundle "update --full-index", artifice: "compact_index", all: true expect(out).to include("Fetching source index from #{source_uri}") expect(the_bundle).to include_gems "rack 1.0.0" end @@ -268,14 +288,14 @@ The checksum of /versions does not match the checksum provided by the server! So end end - system_gems %w[rack-1.0.0 thin-1.0 net_a-1.0], :gem_repo => gem_repo2 + system_gems %w[rack-1.0.0 thin-1.0 net_a-1.0], gem_repo: gem_repo2 bundle "config set --local path.system true" - ENV["BUNDLER_SPEC_ALL_REQUESTS"] = strip_whitespace(<<-EOS).strip + ENV["BUNDLER_SPEC_ALL_REQUESTS"] = <<~EOS.strip #{source_uri}/versions #{source_uri}/info/rack EOS - install_gemfile <<-G, :artifice => "compact_index", :verbose => true, :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s } + install_gemfile <<-G, artifice: "compact_index", verbose: true, env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s } source "#{source_uri}" gem "rack" G @@ -283,7 +303,7 @@ The checksum of /versions does not match the checksum provided by the server! So expect(last_command.stdboth).not_to include "Double checking" end - it "fetches again when more dependencies are found in subsequent sources", :bundler => "< 3" do + it "fetches again when more dependencies are found in subsequent sources", bundler: "< 3" do build_repo2 do build_gem "back_deps" do |s| s.add_dependency "foo" @@ -297,7 +317,7 @@ The checksum of /versions does not match the checksum provided by the server! So gem "back_deps" G - bundle :install, :artifice => "compact_index_extra" + bundle :install, artifice: "compact_index_extra" expect(the_bundle).to include_gems "back_deps 1.0", "foo 1.0" end @@ -309,7 +329,7 @@ The checksum of /versions does not match the checksum provided by the server! So FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")] end - install_gemfile <<-G, :artifice => "compact_index_extra", :verbose => true + install_gemfile <<-G, artifice: "compact_index_extra", verbose: true source "#{source_uri}" source "#{source_uri}/extra" do gem "back_deps" @@ -324,7 +344,7 @@ The checksum of /versions does not match the checksum provided by the server! So source "#{source_uri}" gem "rack", "1.0.0" G - bundle :install, :artifice => "compact_index_extra_api" + bundle :install, artifice: "compact_index_extra_api" expect(the_bundle).to include_gems "rack 1.0.0" build_repo4 do @@ -338,11 +358,11 @@ The checksum of /versions does not match the checksum provided by the server! So source "#{source_uri}/extra" gem "rack", "1.2" G - bundle :install, :artifice => "compact_index_extra_api" + bundle :install, artifice: "compact_index_extra_api" expect(the_bundle).to include_gems "rack 1.2" end - it "considers all possible versions of dependencies from all api gem sources", :bundler => "< 3" do + it "considers all possible versions of dependencies from all api gem sources", bundler: "< 3" do # In this scenario, the gem "somegem" only exists in repo4. It depends on specific version of activesupport that # exists only in repo1. There happens also be a version of activesupport in repo4, but not the one that version 1.0.0 # of somegem wants. This test makes sure that bundler actually finds version 1.2.3 of active support in the other @@ -360,7 +380,7 @@ The checksum of /versions does not match the checksum provided by the server! So gem 'somegem', '1.0.0' G - bundle :install, :artifice => "compact_index_extra_api" + bundle :install, artifice: "compact_index_extra_api" expect(the_bundle).to include_gems "somegem 1.0.0" expect(the_bundle).to include_gems "activesupport 1.2.3" @@ -381,13 +401,13 @@ The checksum of /versions does not match the checksum provided by the server! So end G - bundle :install, :artifice => "compact_index_extra" + bundle :install, artifice: "compact_index_extra" expect(out).to include("Fetching gem metadata from http://localgemserver.test/") expect(out).to include("Fetching source index from http://localgemserver.test/extra") end - it "does not fetch every spec if the index of gems is large when doing back deps" do + it "does not fetch every spec when doing back deps" do build_repo2 do build_gem "back_deps" do |s| s.add_dependency "foo" @@ -397,9 +417,7 @@ The checksum of /versions does not match the checksum provided by the server! So FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")] end - api_request_limit = low_api_request_limit_for(gem_repo2) - - install_gemfile <<-G, :artifice => "compact_index_extra_missing", :requires => [api_request_limit_hack_file], :env => { "BUNDLER_SPEC_API_REQUEST_LIMIT" => api_request_limit.to_s }.merge(env_for_missing_prerelease_default_gem_activation) + install_gemfile <<-G, artifice: "compact_index_extra_missing" source "#{source_uri}" source "#{source_uri}/extra" do gem "back_deps" @@ -409,7 +427,7 @@ The checksum of /versions does not match the checksum provided by the server! So expect(the_bundle).to include_gems "back_deps 1.0" end - it "does not fetch every spec if the index of gems is large when doing back deps & everything is the compact index" do + it "does not fetch every spec when doing back deps & everything is the compact index" do build_repo4 do build_gem "back_deps" do |s| s.add_dependency "foo" @@ -419,9 +437,7 @@ The checksum of /versions does not match the checksum provided by the server! So FileUtils.rm_rf Dir[gem_repo4("gems/foo-*.gem")] end - api_request_limit = low_api_request_limit_for(gem_repo4) - - install_gemfile <<-G, :artifice => "compact_index_extra_api_missing", :requires => [api_request_limit_hack_file], :env => { "BUNDLER_SPEC_API_REQUEST_LIMIT" => api_request_limit.to_s }.merge(env_for_missing_prerelease_default_gem_activation) + install_gemfile <<-G, artifice: "compact_index_extra_api_missing" source "#{source_uri}" source "#{source_uri}/extra" do gem "back_deps" @@ -438,11 +454,11 @@ The checksum of /versions does not match the checksum provided by the server! So gem 'foo' G - bundle :install, :artifice => "compact_index_api_missing" + bundle :install, artifice: "compact_index_api_missing" expect(the_bundle).to include_gems "foo 1.0" end - it "fetches again when more dependencies are found in subsequent sources using deployment mode", :bundler => "< 3" do + it "fetches again when more dependencies are found in subsequent sources using deployment mode", bundler: "< 3" do build_repo2 do build_gem "back_deps" do |s| s.add_dependency "foo" @@ -456,9 +472,9 @@ The checksum of /versions does not match the checksum provided by the server! So gem "back_deps" G - bundle :install, :artifice => "compact_index_extra" + bundle :install, artifice: "compact_index_extra" bundle "config --set local deployment true" - bundle :install, :artifice => "compact_index_extra" + bundle :install, artifice: "compact_index_extra" expect(the_bundle).to include_gems "back_deps 1.0" end @@ -477,9 +493,9 @@ The checksum of /versions does not match the checksum provided by the server! So end G - bundle :install, :artifice => "compact_index_extra" + bundle :install, artifice: "compact_index_extra" bundle "config set --local deployment true" - bundle :install, :artifice => "compact_index_extra" + bundle :install, artifice: "compact_index_extra" expect(the_bundle).to include_gems "back_deps 1.0" end @@ -496,52 +512,40 @@ The checksum of /versions does not match the checksum provided by the server! So gem "bundler_dep" G - bundle :install, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s } + bundle :install, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s } expect(out).to include("Fetching gem metadata from #{source_uri}") end - it "should install when EndpointSpecification has a bin dir owned by root", :sudo => true do - sudo "mkdir -p #{system_gem_path("bin")}" - sudo "chown -R root #{system_gem_path("bin")}" - - gemfile <<-G - source "#{source_uri}" - gem "rails" - G - bundle :install, :artifice => "compact_index" - expect(the_bundle).to include_gems "rails 2.3.2" - end - - it "installs the binstubs", :bundler => "< 3" do + it "installs the binstubs", bundler: "< 3" do gemfile <<-G source "#{source_uri}" gem "rack" G - bundle "install --binstubs", :artifice => "compact_index" + bundle "install --binstubs", artifice: "compact_index" gembin "rackup" expect(out).to eq("1.0.0") end - it "installs the bins when using --path and uses autoclean", :bundler => "< 3" do + it "installs the bins when using --path and uses autoclean", bundler: "< 3" do gemfile <<-G source "#{source_uri}" gem "rack" G - bundle "install --path vendor/bundle", :artifice => "compact_index" + bundle "install --path vendor/bundle", artifice: "compact_index" expect(vendored_gems("bin/rackup")).to exist end - it "installs the bins when using --path and uses bundle clean", :bundler => "< 3" do + it "installs the bins when using --path and uses bundle clean", bundler: "< 3" do gemfile <<-G source "#{source_uri}" gem "rack" G - bundle "install --path vendor/bundle --no-clean", :artifice => "compact_index" + bundle "install --path vendor/bundle --no-clean", artifice: "compact_index" expect(vendored_gems("bin/rackup")).to exist end @@ -552,7 +556,7 @@ The checksum of /versions does not match the checksum provided by the server! So gem 'rack-obama' G - bundle :install, :artifice => "compact_index" + bundle :install, artifice: "compact_index" expect(out).to include("Post-install message from rack:") end @@ -562,7 +566,7 @@ The checksum of /versions does not match the checksum provided by the server! So gem 'rack_middleware' G - bundle :install, :artifice => "compact_index" + bundle :install, artifice: "compact_index" expect(out).to include("Post-install message from rack:") expect(out).to include("Rack's post install message") end @@ -571,7 +575,7 @@ The checksum of /versions does not match the checksum provided by the server! So let(:user) { "user" } let(:password) { "pass" } let(:basic_auth_source_uri) do - uri = Bundler::URI.parse(source_uri) + uri = Gem::URI.parse(source_uri) uri.user = user uri.password = password @@ -584,7 +588,7 @@ The checksum of /versions does not match the checksum provided by the server! So gem "rack" G - bundle :install, :artifice => "compact_index_basic_authentication" + bundle :install, artifice: "compact_index_basic_authentication" expect(out).not_to include("#{user}:#{password}") expect(the_bundle).to include_gems "rack 1.0.0" end @@ -595,19 +599,19 @@ The checksum of /versions does not match the checksum provided by the server! So gem "rack" G - bundle :install, :verbose => true, :artifice => "compact_index_basic_authentication" + bundle :install, verbose: true, artifice: "compact_index_basic_authentication" expect(out).not_to include("#{user}:#{password}") expect(the_bundle).to include_gems "rack 1.0.0" end - it "strips http basic auth creds when warning about ambiguous sources", :bundler => "< 3" do + it "strips http basic auth creds when warning about ambiguous sources", bundler: "< 3" do gemfile <<-G source "#{basic_auth_source_uri}" source "#{file_uri_for(gem_repo1)}" gem "rack" G - bundle :install, :artifice => "compact_index_basic_authentication" + bundle :install, artifice: "compact_index_basic_authentication" expect(err).to include("Warning: the gem 'rack' was found in multiple sources.") expect(err).not_to include("#{user}:#{password}") expect(the_bundle).to include_gems "rack 1.0.0" @@ -619,7 +623,7 @@ The checksum of /versions does not match the checksum provided by the server! So gem "rack" G - bundle :install, :artifice => "compact_index_creds_diff_host" + bundle :install, artifice: "compact_index_creds_diff_host" expect(the_bundle).to include_gems "rack 1.0.0" end @@ -634,7 +638,7 @@ The checksum of /versions does not match the checksum provided by the server! So it "reads authentication details by host name from bundle config" do bundle "config set #{source_hostname} #{user}:#{password}" - bundle :install, :artifice => "compact_index_strict_basic_authentication" + bundle :install, artifice: "compact_index_strict_basic_authentication" expect(out).to include("Fetching gem metadata from #{source_uri}") expect(the_bundle).to include_gems "rack 1.0.0" @@ -644,7 +648,7 @@ The checksum of /versions does not match the checksum provided by the server! So # The trailing slash is necessary here; Fetcher canonicalizes the URI. bundle "config set #{source_uri}/ #{user}:#{password}" - bundle :install, :artifice => "compact_index_strict_basic_authentication" + bundle :install, artifice: "compact_index_strict_basic_authentication" expect(out).to include("Fetching gem metadata from #{source_uri}") expect(the_bundle).to include_gems "rack 1.0.0" @@ -652,7 +656,7 @@ The checksum of /versions does not match the checksum provided by the server! So it "should use the API" do bundle "config set #{source_hostname} #{user}:#{password}" - bundle :install, :artifice => "compact_index_strict_basic_authentication" + bundle :install, artifice: "compact_index_strict_basic_authentication" expect(out).to include("Fetching gem metadata from #{source_uri}") expect(the_bundle).to include_gems "rack 1.0.0" end @@ -665,20 +669,29 @@ The checksum of /versions does not match the checksum provided by the server! So bundle "config set #{source_hostname} otheruser:wrong" - bundle :install, :artifice => "compact_index_strict_basic_authentication" + bundle :install, artifice: "compact_index_strict_basic_authentication" expect(the_bundle).to include_gems "rack 1.0.0" end it "shows instructions if auth is not provided for the source" do - bundle :install, :artifice => "compact_index_strict_basic_authentication", :raise_on_error => false + bundle :install, artifice: "compact_index_strict_basic_authentication", raise_on_error: false expect(err).to include("bundle config set --global #{source_hostname} username:password") end it "fails if authentication has already been provided, but failed" do bundle "config set #{source_hostname} #{user}:wrong" - bundle :install, :artifice => "compact_index_strict_basic_authentication", :raise_on_error => false + bundle :install, artifice: "compact_index_strict_basic_authentication", raise_on_error: false + expect(err).to include("Bad username or password") + end + + it "does not fallback to old dependency API if bad authentication is provided" do + bundle "config set #{source_hostname} #{user}:wrong" + + bundle :install, artifice: "compact_index_strict_basic_authentication", raise_on_error: false, verbose: true expect(err).to include("Bad username or password") + expect(out).to include("HTTP 401 Unauthorized http://user@localgemserver.test/versions") + expect(out).not_to include("HTTP 401 Unauthorized http://user@localgemserver.test/api/v1/dependencies") end end @@ -691,7 +704,7 @@ The checksum of /versions does not match the checksum provided by the server! So gem "rack" G - bundle :install, :artifice => "compact_index_basic_authentication" + bundle :install, artifice: "compact_index_basic_authentication" expect(the_bundle).to include_gems "rack 1.0.0" end end @@ -716,7 +729,7 @@ The checksum of /versions does not match the checksum provided by the server! So gem "rack" G - bundle :install, :env => { "RUBYOPT" => opt_add("-I#{bundled_app("broken_ssl")}", ENV["RUBYOPT"]) }, :raise_on_error => false + bundle :install, env: { "RUBYOPT" => opt_add("-I#{bundled_app("broken_ssl")}", ENV["RUBYOPT"]) }, raise_on_error: false expect(err).to include("OpenSSL") end end @@ -726,7 +739,7 @@ The checksum of /versions does not match the checksum provided by the server! So # Install a monkeypatch that reproduces the effects of openssl raising # a certificate validation error when RubyGems tries to connect. gemfile <<-G - class Net::HTTP + class Gem::Net::HTTP def start raise OpenSSL::SSL::SSLError, "certificate verify failed" end @@ -736,7 +749,7 @@ The checksum of /versions does not match the checksum provided by the server! So gem "rack" G - bundle :install, :raise_on_error => false + bundle :install, raise_on_error: false expect(err).to match(/could not verify the SSL certificate/i) end end @@ -744,7 +757,7 @@ The checksum of /versions does not match the checksum provided by the server! So context ".gemrc with sources is present" do it "uses other sources declared in the Gemfile" do File.open(home(".gemrc"), "w") do |file| - file.puts({ :sources => ["https://rubygems.org"] }.to_yaml) + file.puts({ sources: ["https://rubygems.org"] }.to_yaml) end begin @@ -753,21 +766,30 @@ The checksum of /versions does not match the checksum provided by the server! So gem 'rack' G - bundle :install, :artifice => "compact_index_forbidden" + bundle :install, artifice: "compact_index_forbidden" ensure home(".gemrc").rmtree end end end - it "performs partial update with a non-empty range" do + it "performs update with etag not-modified" do + versions_etag = Pathname.new(Bundler.rubygems.user_home).join( + ".bundle", "cache", "compact_index", + "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "versions.etag" + ) + expect(versions_etag.file?).to eq(false) + gemfile <<-G source "#{source_uri}" gem 'rack', '0.9.1' G - # Initial install creates the cached versions file - bundle :install, :artifice => "compact_index" + # Initial install creates the cached versions file and etag file + bundle :install, artifice: "compact_index" + + expect(versions_etag.file?).to eq(true) + previous_content = versions_etag.binread # Update the Gemfile so we can check subsequent install was successful gemfile <<-G @@ -775,8 +797,59 @@ The checksum of /versions does not match the checksum provided by the server! So gem 'rack', '1.0.0' G - # Second install should make only a partial request to /versions - bundle :install, :artifice => "compact_index_partial_update" + # Second install should match etag + bundle :install, artifice: "compact_index_etag_match" + + expect(versions_etag.binread).to eq(previous_content) + expect(the_bundle).to include_gems "rack 1.0.0" + end + + it "performs full update when range is ignored" do + gemfile <<-G + source "#{source_uri}" + gem 'rack', '0.9.1' + G + + # Initial install creates the cached versions file and etag file + bundle :install, artifice: "compact_index" + + gemfile <<-G + source "#{source_uri}" + gem 'rack', '1.0.0' + G + + versions = Pathname.new(Bundler.rubygems.user_home).join( + ".bundle", "cache", "compact_index", + "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "versions" + ) + # Modify the cached file. The ranged request will be based on this but, + # in this test, the range is ignored so this gets overwritten, allowing install. + versions.write "ruining this file" + + bundle :install, artifice: "compact_index_range_ignored" + + expect(the_bundle).to include_gems "rack 1.0.0" + end + + it "performs partial update with a non-empty range" do + build_repo4 do + build_gem "rack", "0.9.1" + end + + # Initial install creates the cached versions file + install_gemfile <<-G, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } + source "#{source_uri}" + gem 'rack', '0.9.1' + G + + update_repo4 do + build_gem "rack", "1.0.0" + end + + install_gemfile <<-G, artifice: "compact_index_partial_update", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } + source "#{source_uri}" + gem 'rack', '1.0.0' + G expect(the_bundle).to include_gems "rack 1.0.0" end @@ -787,24 +860,48 @@ The checksum of /versions does not match the checksum provided by the server! So gem 'rack' G - # Create an empty file to trigger a partial download - versions = File.join(Bundler.rubygems.user_home, ".bundle", "cache", "compact_index", - "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "versions") - FileUtils.mkdir_p(File.dirname(versions)) - FileUtils.touch(versions) + # Create a partial cache versions file + versions = Pathname.new(Bundler.rubygems.user_home).join( + ".bundle", "cache", "compact_index", + "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "versions" + ) + versions.dirname.mkpath + versions.write("created_at") + + bundle :install, artifice: "compact_index_concurrent_download" + + expect(versions.read).to start_with("created_at") + expect(the_bundle).to include_gems "rack 1.0.0" + end + + it "performs a partial update that fails digest check, then a full update" do + build_repo4 do + build_gem "rack", "0.9.1" + end + + install_gemfile <<-G, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } + source "#{source_uri}" + gem 'rack', '0.9.1' + G + + update_repo4 do + build_gem "rack", "1.0.0" + end - bundle :install, :artifice => "compact_index_concurrent_download" + install_gemfile <<-G, artifice: "compact_index_partial_update_bad_digest", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } + source "#{source_uri}" + gem 'rack', '1.0.0' + G - expect(File.read(versions)).to start_with("created_at") expect(the_bundle).to include_gems "rack 1.0.0" end - it "performs full update if server endpoints serve partial content responses but don't have incremental content and provide no Etag" do + it "performs full update if server endpoints serve partial content responses but don't have incremental content and provide no digest" do build_repo4 do build_gem "rack", "0.9.1" end - install_gemfile <<-G, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } + install_gemfile <<-G, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } source "#{source_uri}" gem 'rack', '0.9.1' G @@ -813,7 +910,7 @@ The checksum of /versions does not match the checksum provided by the server! So build_gem "rack", "1.0.0" end - install_gemfile <<-G, :artifice => "compact_index_partial_update_no_etag_not_incremental", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } + install_gemfile <<-G, artifice: "compact_index_partial_update_no_digest_not_incremental", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } source "#{source_uri}" gem 'rack', '1.0.0' G @@ -827,15 +924,19 @@ The checksum of /versions does not match the checksum provided by the server! So gem 'rack', '0.9.1' G - rake_info_path = File.join(Bundler.rubygems.user_home, ".bundle", "cache", "compact_index", - "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "info", "rack") + bundle :install, artifice: "compact_index" - bundle :install, :artifice => "compact_index" + # We must remove the etag so that we don't ignore the range and get a 304 Not Modified. + rake_info_etag_path = File.join(Bundler.rubygems.user_home, ".bundle", "cache", "compact_index", + "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "info-etags", "rack-11690b09f16021ff06a6857d784a1870") + File.unlink(rake_info_etag_path) if File.exist?(rake_info_etag_path) + rake_info_path = File.join(Bundler.rubygems.user_home, ".bundle", "cache", "compact_index", + "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "info", "rack") expected_rack_info_content = File.read(rake_info_path) - # Modify the cache files. We expect them to be reset to the normal ones when we re-run :install - File.open(rake_info_path, "w") {|f| f << (expected_rack_info_content + "this is different") } + # Modify the cache files to make the range not satisfiable + File.open(rake_info_path, "a") {|f| f << "0.9.2 |checksum:c55b525b421fd833a93171ad3d7f04528ca8e87d99ac273f8933038942a5888c" } # Update the Gemfile so the next install does its normal things gemfile <<-G @@ -845,7 +946,7 @@ The checksum of /versions does not match the checksum provided by the server! So # The cache files now being longer means the requested range is going to be not satisfiable # Bundler must end up requesting the whole file to fix things up. - bundle :install, :artifice => "compact_index_range_not_satisfiable" + bundle :install, artifice: "compact_index_range_not_satisfiable" resulting_rack_info_content = File.read(rake_info_path) @@ -853,7 +954,7 @@ The checksum of /versions does not match the checksum provided by the server! So end it "fails gracefully when the source URI has an invalid scheme" do - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G, raise_on_error: false source "htps://rubygems.org" gem "rack" G @@ -864,38 +965,76 @@ The checksum of /versions does not match the checksum provided by the server! So end describe "checksum validation" do + before do + lockfile <<-L + GEM + remote: #{source_uri} + specs: + rack (1.0.0) + + PLATFORMS + ruby + + DEPENDENCIES + #{checksums_section} + BUNDLED WITH + #{Bundler::VERSION} + L + end + + it "handles checksums from the server in base64" do + api_checksum = checksum_digest(gem_repo1, "rack", "1.0.0") + rack_checksum = [[api_checksum].pack("H*")].pack("m0") + install_gemfile <<-G, artifice: "compact_index", env: { "BUNDLER_SPEC_RACK_CHECKSUM" => rack_checksum } + source "#{source_uri}" + gem "rack" + G + + expect(out).to include("Fetching gem metadata from #{source_uri}") + expect(the_bundle).to include_gems("rack 1.0.0") + end + it "raises when the checksum does not match" do - install_gemfile <<-G, :artifice => "compact_index_wrong_gem_checksum", :raise_on_error => false + install_gemfile <<-G, artifice: "compact_index_wrong_gem_checksum", raise_on_error: false source "#{source_uri}" gem "rack" G - expect(exitstatus).to eq(19) - expect(err). - to include("Bundler cannot continue installing rack (1.0.0)."). - and include("The checksum for the downloaded `rack-1.0.0.gem` does not match the checksum given by the server."). - and include("This means the contents of the downloaded gem is different from what was uploaded to the server, and could be a potential security issue."). - and include("To resolve this issue:"). - and include("1. delete the downloaded gem located at: `#{default_bundle_path}/gems/rack-1.0.0/rack-1.0.0.gem`"). - and include("2. run `bundle install`"). - and include("If you wish to continue installing the downloaded gem, and are certain it does not pose a security issue despite the mismatching checksum, do the following:"). - and include("1. run `bundle config set --local disable_checksum_validation true` to turn off checksum verification"). - and include("2. run `bundle install`"). - and match(/\(More info: The expected SHA256 checksum was "#{"ab" * 22}", but the checksum for the downloaded gem was ".+?"\.\)/) + gem_path = if Bundler.feature_flag.global_gem_cache? + default_cache_path.dirname.join("cache", "gems", "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "rack-1.0.0.gem") + else + default_cache_path.dirname.join("rack-1.0.0.gem") + end + + expect(exitstatus).to eq(37) + expect(err).to eq <<~E.strip + Bundler found mismatched checksums. This is a potential security risk. + rack (1.0.0) sha256=2222222222222222222222222222222222222222222222222222222222222222 + from the API at http://localgemserver.test/ + #{checksum_to_lock(gem_repo1, "rack", "1.0.0")} + from the gem at #{gem_path} + + If you trust the API at http://localgemserver.test/, to resolve this issue you can: + 1. remove the gem at #{gem_path} + 2. run `bundle install` + + To ignore checksum security warnings, disable checksum validation with + `bundle config set --local disable_checksum_validation true` + E end it "raises when the checksum is the wrong length" do - install_gemfile <<-G, :artifice => "compact_index_wrong_gem_checksum", :env => { "BUNDLER_SPEC_RACK_CHECKSUM" => "checksum!", "DEBUG" => "1" }, :verbose => true, :raise_on_error => false + install_gemfile <<-G, artifice: "compact_index_wrong_gem_checksum", env: { "BUNDLER_SPEC_RACK_CHECKSUM" => "checksum!", "DEBUG" => "1" }, verbose: true, raise_on_error: false source "#{source_uri}" gem "rack" G - expect(exitstatus).to eq(5) - expect(err).to include("The given checksum for rack-1.0.0 (\"checksum!\") is not a valid SHA256 hexdigest nor base64digest") + expect(exitstatus).to eq(14) + expect(err).to include('Invalid checksum for rack-0.9.1: "checksum!" is not a valid SHA256 hex or base64 digest') end it "does not raise when disable_checksum_validation is set" do bundle "config set disable_checksum_validation true" - install_gemfile <<-G, :artifice => "compact_index_wrong_gem_checksum" + install_gemfile <<-G, artifice: "compact_index_wrong_gem_checksum" source "#{source_uri}" gem "rack" G @@ -903,7 +1042,7 @@ The checksum of /versions does not match the checksum provided by the server! So end it "works when cache dir is world-writable" do - install_gemfile <<-G, :artifice => "compact_index" + install_gemfile <<-G, artifice: "compact_index" File.umask(0000) source "#{source_uri}" gem "rack" @@ -911,11 +1050,11 @@ The checksum of /versions does not match the checksum provided by the server! So end it "doesn't explode when the API dependencies are wrong" do - install_gemfile <<-G, :artifice => "compact_index_wrong_dependencies", :env => { "DEBUG" => "true" }, :raise_on_error => false + install_gemfile <<-G, artifice: "compact_index_wrong_dependencies", env: { "DEBUG" => "true" }, raise_on_error: false source "#{source_uri}" gem "rails" G - deps = [Gem::Dependency.new("rake", "= 13.0.1"), + deps = [Gem::Dependency.new("rake", "= #{rake_version}"), Gem::Dependency.new("actionpack", "= 2.3.2"), Gem::Dependency.new("activerecord", "= 2.3.2"), Gem::Dependency.new("actionmailer", "= 2.3.2"), @@ -923,12 +1062,12 @@ The checksum of /versions does not match the checksum provided by the server! So expect(out).to include("rails-2.3.2 from rubygems remote at #{source_uri}/ has either corrupted API or lockfile dependencies") expect(err).to include(<<-E.strip) Bundler::APIResponseMismatchError: Downloading rails-2.3.2 revealed dependencies not in the API or the lockfile (#{deps.map(&:to_s).join(", ")}). -Either installing with `--full-index` or running `bundle update rails` should fix the problem. +Running `bundle update rails` should fix the problem. E end it "does not duplicate specs in the lockfile when updating and a dependency is not installed" do - install_gemfile <<-G, :artifice => "compact_index" + install_gemfile <<-G, artifice: "compact_index" source "#{file_uri_for(gem_repo1)}" source "#{source_uri}" do gem "rails" @@ -936,7 +1075,8 @@ Either installing with `--full-index` or running `bundle update rails` should fi end G gem_command "uninstall activemerchant" - bundle "update rails", :artifice => "compact_index" - expect(lockfile.scan(/activemerchant \(/).size).to eq(1) + bundle "update rails", artifice: "compact_index" + count = lockfile.match?("CHECKSUMS") ? 2 : 1 # Once in the specs, and once in CHECKSUMS + expect(lockfile.scan(/activemerchant \(/).size).to eq(count) end end diff --git a/spec/bundler/install/gems/dependency_api_spec.rb b/spec/bundler/install/gems/dependency_api_spec.rb index 79317a7fad..35468b3a1c 100644 --- a/spec/bundler/install/gems/dependency_api_spec.rb +++ b/spec/bundler/install/gems/dependency_api_spec.rb @@ -10,7 +10,7 @@ RSpec.describe "gemcutter's dependency API" do gem "rack" G - bundle :install, :artifice => "endpoint" + bundle :install, artifice: "endpoint" expect(out).to include("Fetching gem metadata from #{source_uri}") expect(the_bundle).to include_gems "rack 1.0.0" end @@ -21,7 +21,7 @@ RSpec.describe "gemcutter's dependency API" do gem " sinatra" G - bundle :install, :artifice => "endpoint", :raise_on_error => false + bundle :install, artifice: "endpoint", raise_on_error: false expect(err).to include("' sinatra' is not a valid gem name because it contains whitespace.") end @@ -31,7 +31,7 @@ RSpec.describe "gemcutter's dependency API" do gem "rails" G - bundle :install, :artifice => "endpoint" + bundle :install, artifice: "endpoint" expect(out).to include("Fetching gem metadata from #{source_uri}/...") expect(the_bundle).to include_gems( "rails 2.3.2", @@ -49,7 +49,7 @@ RSpec.describe "gemcutter's dependency API" do gem "net-sftp" G - bundle :install, :artifice => "endpoint" + bundle :install, artifice: "endpoint" expect(the_bundle).to include_gems "net-sftp 1.1.1" end @@ -58,11 +58,11 @@ RSpec.describe "gemcutter's dependency API" do source "#{source_uri}" gem "rack" G - bundle :install, :artifice => "endpoint" + bundle :install, artifice: "endpoint" bundle "config set --local deployment true" bundle "config set --local path vendor/bundle" - bundle :install, :artifice => "endpoint" + bundle :install, artifice: "endpoint" expect(out).to include("Fetching gem metadata from #{source_uri}") expect(the_bundle).to include_gems "rack 1.0.0" end @@ -80,7 +80,7 @@ RSpec.describe "gemcutter's dependency API" do end G - bundle :install, :artifice => "endpoint" + bundle :install, artifice: "endpoint" expect(the_bundle).to include_gems("rails 2.3.2") end @@ -96,10 +96,10 @@ RSpec.describe "gemcutter's dependency API" do gem 'foo', :git => "#{file_uri_for(lib_path("foo-1.0"))}" G - bundle :install, :artifice => "endpoint" + bundle :install, artifice: "endpoint" bundle "config set --local deployment true" - bundle :install, :artifice => "endpoint" + bundle :install, artifice: "endpoint" expect(the_bundle).to include_gems("rails 2.3.2") end @@ -111,15 +111,15 @@ RSpec.describe "gemcutter's dependency API" do gem 'foo', :git => "#{file_uri_for(lib_path("foo-1.0"))}" G - bundle "install", :artifice => "endpoint" + bundle "install", artifice: "endpoint" bundle "config set --local deployment true" - bundle :install, :artifice => "endpoint" + bundle :install, artifice: "endpoint" expect(the_bundle).to include_gems("foo 1.0") end it "falls back when the API errors out" do - simulate_platform mswin + simulate_platform x86_mswin32 build_repo2 do # The rcov gem is platform mswin32, but has no arch @@ -134,7 +134,7 @@ RSpec.describe "gemcutter's dependency API" do gem "rcov" G - bundle :install, :artifice => "windows", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s } + bundle :install, artifice: "windows", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s } expect(out).to include("Fetching source index from #{source_uri}") expect(the_bundle).to include_gems "rcov 1.0.0" end @@ -150,7 +150,7 @@ RSpec.describe "gemcutter's dependency API" do gem "rack" gem "rails" G - bundle :install, :artifice => "endpoint_fallback" + bundle :install, artifice: "endpoint_fallback" expect(out).to include("Fetching source index from #{source_uri}") expect(the_bundle).to include_gems( @@ -171,7 +171,7 @@ RSpec.describe "gemcutter's dependency API" do gem "rack" G - bundle :install, :verbose => true, :artifice => "endpoint_marshal_fail" + bundle :install, verbose: true, artifice: "endpoint_marshal_fail" expect(out).to include("could not fetch from the dependency API, trying the full index") expect(the_bundle).to include_gems "rack 1.0.0" end @@ -182,7 +182,7 @@ RSpec.describe "gemcutter's dependency API" do gem "rack" G - bundle :install, :verbose => true, :artifice => "endpoint_api_forbidden" + bundle :install, verbose: true, artifice: "endpoint_api_forbidden" expect(out).to include("Fetching source index from #{source_uri}") expect(the_bundle).to include_gems "rack 1.0.0" end @@ -193,11 +193,11 @@ RSpec.describe "gemcutter's dependency API" do gem "rack" G - bundle :install, :artifice => "endpoint_host_redirect" + bundle :install, artifice: "endpoint_host_redirect" expect(the_bundle).to include_gems "rack 1.0.0" end - it "handles host redirects without Net::HTTP::Persistent" do + it "handles host redirects without Gem::Net::HTTP::Persistent" do gemfile <<-G source "#{source_uri}" gem "rack" @@ -216,7 +216,7 @@ RSpec.describe "gemcutter's dependency API" do H end - bundle :install, :artifice => "endpoint_host_redirect", :requires => [lib_path("disable_net_http_persistent.rb")] + bundle :install, artifice: "endpoint_host_redirect", requires: [lib_path("disable_net_http_persistent.rb")] expect(out).to_not match(/Too many redirects/) expect(the_bundle).to include_gems "rack 1.0.0" end @@ -227,7 +227,7 @@ RSpec.describe "gemcutter's dependency API" do gem "rack" G - bundle :install, :artifice => "endpoint_redirect", :raise_on_error => false + bundle :install, artifice: "endpoint_redirect", raise_on_error: false expect(err).to match(/Too many redirects/) end @@ -238,7 +238,7 @@ RSpec.describe "gemcutter's dependency API" do gem "rack" G - bundle "install --full-index", :artifice => "endpoint" + bundle "install --full-index", artifice: "endpoint" expect(out).to include("Fetching source index from #{source_uri}") expect(the_bundle).to include_gems "rack 1.0.0" end @@ -249,13 +249,13 @@ RSpec.describe "gemcutter's dependency API" do gem "rack" G - bundle "update --full-index", :artifice => "endpoint", :all => true + bundle "update --full-index", artifice: "endpoint", all: true expect(out).to include("Fetching source index from #{source_uri}") expect(the_bundle).to include_gems "rack 1.0.0" end end - it "fetches again when more dependencies are found in subsequent sources", :bundler => "< 3" do + it "fetches again when more dependencies are found in subsequent sources", bundler: "< 3" do build_repo2 do build_gem "back_deps" do |s| s.add_dependency "foo" @@ -269,7 +269,7 @@ RSpec.describe "gemcutter's dependency API" do gem "back_deps" G - bundle :install, :artifice => "endpoint_extra" + bundle :install, artifice: "endpoint_extra" expect(the_bundle).to include_gems "back_deps 1.0", "foo 1.0" end @@ -288,7 +288,7 @@ RSpec.describe "gemcutter's dependency API" do end G - bundle :install, :artifice => "endpoint_extra" + bundle :install, artifice: "endpoint_extra" expect(the_bundle).to include_gems "back_deps 1.0", "foo 1.0" end @@ -297,7 +297,7 @@ RSpec.describe "gemcutter's dependency API" do source "#{source_uri}" gem "rack", "1.0.0" G - bundle :install, :artifice => "endpoint_extra_api" + bundle :install, artifice: "endpoint_extra_api" build_repo4 do build_gem "rack", "1.2" do |s| @@ -310,11 +310,11 @@ RSpec.describe "gemcutter's dependency API" do source "#{source_uri}/extra" gem "rack", "1.2" G - bundle :install, :artifice => "endpoint_extra_api" + bundle :install, artifice: "endpoint_extra_api" expect(the_bundle).to include_gems "rack 1.2" end - it "considers all possible versions of dependencies from all api gem sources", :bundler => "< 3" do + it "considers all possible versions of dependencies from all api gem sources", bundler: "< 3" do # In this scenario, the gem "somegem" only exists in repo4. It depends on specific version of activesupport that # exists only in repo1. There happens also be a version of activesupport in repo4, but not the one that version 1.0.0 # of somegem wants. This test makes sure that bundler actually finds version 1.2.3 of active support in the other @@ -332,7 +332,7 @@ RSpec.describe "gemcutter's dependency API" do gem 'somegem', '1.0.0' G - bundle :install, :artifice => "endpoint_extra_api" + bundle :install, artifice: "endpoint_extra_api" expect(the_bundle).to include_gems "somegem 1.0.0" expect(the_bundle).to include_gems "activesupport 1.2.3" @@ -353,13 +353,13 @@ RSpec.describe "gemcutter's dependency API" do end G - bundle :install, :artifice => "endpoint_extra" + bundle :install, artifice: "endpoint_extra" expect(out).to include("Fetching gem metadata from http://localgemserver.test/.") expect(out).to include("Fetching source index from http://localgemserver.test/extra") end - it "does not fetch every spec if the index of gems is large when doing back deps", :bundler => "< 3" do + it "does not fetch every spec when doing back deps", bundler: "< 3" do build_repo2 do build_gem "back_deps" do |s| s.add_dependency "foo" @@ -369,9 +369,7 @@ RSpec.describe "gemcutter's dependency API" do FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")] end - api_request_limit = low_api_request_limit_for(gem_repo2) - - install_gemfile <<-G, :artifice => "endpoint_extra_missing", :requires => [api_request_limit_hack_file], :env => { "BUNDLER_SPEC_API_REQUEST_LIMIT" => api_request_limit.to_s }.merge(env_for_missing_prerelease_default_gem_activation) + install_gemfile <<-G, artifice: "endpoint_extra_missing" source "#{source_uri}" source "#{source_uri}/extra" gem "back_deps" @@ -380,7 +378,7 @@ RSpec.describe "gemcutter's dependency API" do expect(the_bundle).to include_gems "back_deps 1.0" end - it "does not fetch every spec if the index of gems is large when doing back deps using blocks" do + it "does not fetch every spec when doing back deps using blocks" do build_repo2 do build_gem "back_deps" do |s| s.add_dependency "foo" @@ -390,9 +388,7 @@ RSpec.describe "gemcutter's dependency API" do FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")] end - api_request_limit = low_api_request_limit_for(gem_repo2) - - install_gemfile <<-G, :artifice => "endpoint_extra_missing", :requires => [api_request_limit_hack_file], :env => { "BUNDLER_SPEC_API_REQUEST_LIMIT" => api_request_limit.to_s }.merge(env_for_missing_prerelease_default_gem_activation) + install_gemfile <<-G, artifice: "endpoint_extra_missing" source "#{source_uri}" source "#{source_uri}/extra" do gem "back_deps" @@ -402,7 +398,7 @@ RSpec.describe "gemcutter's dependency API" do expect(the_bundle).to include_gems "back_deps 1.0" end - it "fetches again when more dependencies are found in subsequent sources using deployment mode", :bundler => "< 3" do + it "fetches again when more dependencies are found in subsequent sources using deployment mode", bundler: "< 3" do build_repo2 do build_gem "back_deps" do |s| s.add_dependency "foo" @@ -416,9 +412,9 @@ RSpec.describe "gemcutter's dependency API" do gem "back_deps" G - bundle :install, :artifice => "endpoint_extra" + bundle :install, artifice: "endpoint_extra" bundle "config set --local deployment true" - bundle :install, :artifice => "endpoint_extra" + bundle :install, artifice: "endpoint_extra" expect(the_bundle).to include_gems "back_deps 1.0" end @@ -437,12 +433,28 @@ RSpec.describe "gemcutter's dependency API" do end G - bundle :install, :artifice => "endpoint_extra" + bundle :install, artifice: "endpoint_extra" bundle "config set --local deployment true" - bundle "install", :artifice => "endpoint_extra" + bundle "install", artifice: "endpoint_extra" expect(the_bundle).to include_gems "back_deps 1.0" end + it "does not fetch all marshaled specs" do + build_repo2 do + build_gem "foo", "1.0" + build_gem "foo", "2.0" + end + + install_gemfile <<-G, artifice: "endpoint", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }, verbose: true + source "#{source_uri}" + + gem "foo" + G + + expect(out).to include("foo-2.0.gemspec.rz") + expect(out).not_to include("foo-1.0.gemspec.rz") + end + it "does not refetch if the only unmet dependency is bundler" do build_repo2 do build_gem "bundler_dep" do |s| @@ -456,52 +468,40 @@ RSpec.describe "gemcutter's dependency API" do gem "bundler_dep" G - bundle :install, :artifice => "endpoint", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s } + bundle :install, artifice: "endpoint", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s } expect(out).to include("Fetching gem metadata from #{source_uri}") end - it "should install when EndpointSpecification has a bin dir owned by root", :sudo => true do - sudo "mkdir -p #{system_gem_path("bin")}" - sudo "chown -R root #{system_gem_path("bin")}" - - gemfile <<-G - source "#{source_uri}" - gem "rails" - G - bundle :install, :artifice => "endpoint" - expect(the_bundle).to include_gems "rails 2.3.2" - end - - it "installs the binstubs", :bundler => "< 3" do + it "installs the binstubs", bundler: "< 3" do gemfile <<-G source "#{source_uri}" gem "rack" G - bundle "install --binstubs", :artifice => "endpoint" + bundle "install --binstubs", artifice: "endpoint" gembin "rackup" expect(out).to eq("1.0.0") end - it "installs the bins when using --path and uses autoclean", :bundler => "< 3" do + it "installs the bins when using --path and uses autoclean", bundler: "< 3" do gemfile <<-G source "#{source_uri}" gem "rack" G - bundle "install --path vendor/bundle", :artifice => "endpoint" + bundle "install --path vendor/bundle", artifice: "endpoint" expect(vendored_gems("bin/rackup")).to exist end - it "installs the bins when using --path and uses bundle clean", :bundler => "< 3" do + it "installs the bins when using --path and uses bundle clean", bundler: "< 3" do gemfile <<-G source "#{source_uri}" gem "rack" G - bundle "install --path vendor/bundle --no-clean", :artifice => "endpoint" + bundle "install --path vendor/bundle --no-clean", artifice: "endpoint" expect(vendored_gems("bin/rackup")).to exist end @@ -512,7 +512,7 @@ RSpec.describe "gemcutter's dependency API" do gem 'rack-obama' G - bundle :install, :artifice => "endpoint" + bundle :install, artifice: "endpoint" expect(out).to include("Post-install message from rack:") end @@ -522,7 +522,7 @@ RSpec.describe "gemcutter's dependency API" do gem 'rack_middleware' G - bundle :install, :artifice => "endpoint" + bundle :install, artifice: "endpoint" expect(out).to include("Post-install message from rack:") expect(out).to include("Rack's post install message") end @@ -531,7 +531,7 @@ RSpec.describe "gemcutter's dependency API" do let(:user) { "user" } let(:password) { "pass" } let(:basic_auth_source_uri) do - uri = Bundler::URI.parse(source_uri) + uri = Gem::URI.parse(source_uri) uri.user = user uri.password = password @@ -544,7 +544,7 @@ RSpec.describe "gemcutter's dependency API" do gem "rack" G - bundle :install, :artifice => "endpoint_basic_authentication" + bundle :install, artifice: "endpoint_basic_authentication" expect(out).not_to include("#{user}:#{password}") expect(the_bundle).to include_gems "rack 1.0.0" end @@ -555,7 +555,7 @@ RSpec.describe "gemcutter's dependency API" do gem "rack" G - bundle :install, :verbose => true, :artifice => "endpoint_basic_authentication" + bundle :install, verbose: true, artifice: "endpoint_basic_authentication" expect(out).not_to include("#{user}:#{password}") expect(the_bundle).to include_gems "rack 1.0.0" end @@ -566,7 +566,7 @@ RSpec.describe "gemcutter's dependency API" do gem "rack" G - bundle :install, :artifice => "endpoint_marshal_fail_basic_authentication" + bundle :install, artifice: "endpoint_marshal_fail_basic_authentication" expect(out).not_to include("#{user}:#{password}") expect(the_bundle).to include_gems "rack 1.0.0" end @@ -577,18 +577,18 @@ RSpec.describe "gemcutter's dependency API" do gem "rack" G - bundle :install, :artifice => "endpoint_500", :raise_on_error => false + bundle :install, artifice: "endpoint_500", raise_on_error: false expect(out).not_to include("#{user}:#{password}") end - it "strips http basic auth creds when warning about ambiguous sources", :bundler => "< 3" do + it "strips http basic auth creds when warning about ambiguous sources", bundler: "< 3" do gemfile <<-G source "#{basic_auth_source_uri}" source "#{file_uri_for(gem_repo1)}" gem "rack" G - bundle :install, :artifice => "endpoint_basic_authentication" + bundle :install, artifice: "endpoint_basic_authentication" expect(err).to include("Warning: the gem 'rack' was found in multiple sources.") expect(err).not_to include("#{user}:#{password}") expect(the_bundle).to include_gems "rack 1.0.0" @@ -600,7 +600,7 @@ RSpec.describe "gemcutter's dependency API" do gem "rack" G - bundle :install, :artifice => "endpoint_creds_diff_host" + bundle :install, artifice: "endpoint_creds_diff_host" expect(the_bundle).to include_gems "rack 1.0.0" end @@ -613,7 +613,7 @@ RSpec.describe "gemcutter's dependency API" do end it "reads authentication details from a valid ENV variable" do - bundle :install, :artifice => "endpoint_strict_basic_authentication", :env => { "BUNDLE_LOCAL___GEMSERVER__TEST" => "#{user}:#{password}" } + bundle :install, artifice: "endpoint_strict_basic_authentication", env: { "BUNDLE_LOCAL___GEMSERVER__TEST" => "#{user}:#{password}" } expect(out).to include("Fetching gem metadata from http://local-gemserver.test") expect(the_bundle).to include_gems "rack 1.0.0" @@ -631,7 +631,7 @@ RSpec.describe "gemcutter's dependency API" do it "reads authentication details by host name from bundle config" do bundle "config set #{source_hostname} #{user}:#{password}" - bundle :install, :artifice => "endpoint_strict_basic_authentication" + bundle :install, artifice: "endpoint_strict_basic_authentication" expect(out).to include("Fetching gem metadata from #{source_uri}") expect(the_bundle).to include_gems "rack 1.0.0" @@ -641,7 +641,7 @@ RSpec.describe "gemcutter's dependency API" do # The trailing slash is necessary here; Fetcher canonicalizes the URI. bundle "config set #{source_uri}/ #{user}:#{password}" - bundle :install, :artifice => "endpoint_strict_basic_authentication" + bundle :install, artifice: "endpoint_strict_basic_authentication" expect(out).to include("Fetching gem metadata from #{source_uri}") expect(the_bundle).to include_gems "rack 1.0.0" @@ -649,7 +649,7 @@ RSpec.describe "gemcutter's dependency API" do it "should use the API" do bundle "config set #{source_hostname} #{user}:#{password}" - bundle :install, :artifice => "endpoint_strict_basic_authentication" + bundle :install, artifice: "endpoint_strict_basic_authentication" expect(out).to include("Fetching gem metadata from #{source_uri}") expect(the_bundle).to include_gems "rack 1.0.0" end @@ -662,19 +662,19 @@ RSpec.describe "gemcutter's dependency API" do bundle "config set #{source_hostname} otheruser:wrong" - bundle :install, :artifice => "endpoint_strict_basic_authentication" + bundle :install, artifice: "endpoint_strict_basic_authentication" expect(the_bundle).to include_gems "rack 1.0.0" end it "shows instructions if auth is not provided for the source" do - bundle :install, :artifice => "endpoint_strict_basic_authentication", :raise_on_error => false + bundle :install, artifice: "endpoint_strict_basic_authentication", raise_on_error: false expect(err).to include("bundle config set --global #{source_hostname} username:password") end it "fails if authentication has already been provided, but failed" do bundle "config set #{source_hostname} #{user}:wrong" - bundle :install, :artifice => "endpoint_strict_basic_authentication", :raise_on_error => false + bundle :install, artifice: "endpoint_strict_basic_authentication", raise_on_error: false expect(err).to include("Bad username or password") end end @@ -688,7 +688,7 @@ RSpec.describe "gemcutter's dependency API" do gem "rack" G - bundle :install, :artifice => "endpoint_basic_authentication" + bundle :install, artifice: "endpoint_basic_authentication" expect(the_bundle).to include_gems "rack 1.0.0" end end @@ -713,7 +713,7 @@ RSpec.describe "gemcutter's dependency API" do gem "rack" G - bundle :install, :env => { "RUBYOPT" => opt_add("-I#{bundled_app("broken_ssl")}", ENV["RUBYOPT"]) }, :raise_on_error => false + bundle :install, env: { "RUBYOPT" => opt_add("-I#{bundled_app("broken_ssl")}", ENV["RUBYOPT"]) }, raise_on_error: false expect(err).to include("OpenSSL") end end @@ -723,7 +723,7 @@ RSpec.describe "gemcutter's dependency API" do # Install a monkeypatch that reproduces the effects of openssl raising # a certificate validation error when RubyGems tries to connect. gemfile <<-G - class Net::HTTP + class Gem::Net::HTTP def start raise OpenSSL::SSL::SSLError, "certificate verify failed" end @@ -733,7 +733,7 @@ RSpec.describe "gemcutter's dependency API" do gem "rack" G - bundle :install, :raise_on_error => false + bundle :install, raise_on_error: false expect(err).to match(/could not verify the SSL certificate/i) end end @@ -741,7 +741,7 @@ RSpec.describe "gemcutter's dependency API" do context ".gemrc with sources is present" do it "uses other sources declared in the Gemfile" do File.open(home(".gemrc"), "w") do |file| - file.puts({ :sources => ["https://rubygems.org"] }.to_yaml) + file.puts({ sources: ["https://rubygems.org"] }.to_yaml) end begin @@ -750,7 +750,7 @@ RSpec.describe "gemcutter's dependency API" do gem 'rack' G - bundle "install", :artifice => "endpoint_marshal_fail" + bundle "install", artifice: "endpoint_marshal_fail" ensure home(".gemrc").rmtree end diff --git a/spec/bundler/install/gems/flex_spec.rb b/spec/bundler/install/gems/flex_spec.rb index f9b374cf01..8ef3984975 100644 --- a/spec/bundler/install/gems/flex_spec.rb +++ b/spec/bundler/install/gems/flex_spec.rb @@ -156,7 +156,7 @@ RSpec.describe "bundle flex_install" do end end - describe "when Gemfile conflicts with lockfile" do + describe "when running bundle install and Gemfile conflicts with lockfile" do before(:each) do build_repo2 install_gemfile <<-G @@ -183,34 +183,70 @@ RSpec.describe "bundle flex_install" do end it "does not install gems whose dependencies are not met" do - bundle :install, :raise_on_error => false - ruby <<-RUBY, :raise_on_error => false + bundle :install, raise_on_error: false + ruby <<-RUBY, raise_on_error: false require 'bundler/setup' RUBY expect(err).to match(/could not find gem 'rack-obama/i) end - it "suggests bundle update when the Gemfile requires different versions than the lock" do + it "discards the locked gems when the Gemfile requires different versions than the lock" do bundle "config set force_ruby_platform true" - nice_error = <<-E.strip.gsub(/^ {8}/, "") - Bundler could not find compatible versions for gem "rack": - In snapshot (Gemfile.lock): - rack (= 0.9.1) + nice_error = <<~E.strip + Could not find compatible versions - In Gemfile: - rack-obama (= 2.0) was resolved to 2.0, which depends on - rack (= 1.2) + Because rack-obama >= 2.0 depends on rack = 1.2 + and rack = 1.2 could not be found in rubygems repository #{file_uri_for(gem_repo2)}/, cached gems or installed locally, + rack-obama >= 2.0 cannot be used. + So, because Gemfile depends on rack-obama = 2.0, + version solving has failed. + E + + bundle :install, retry: 0, raise_on_error: false + expect(err).to end_with(nice_error) + end - rack_middleware was resolved to 1.0, which depends on - rack (= 0.9.1) + it "does not include conflicts with a single requirement tree, because that can't possibly be a conflict" do + bundle "config set force_ruby_platform true" - Running `bundle update` will rebuild your snapshot from scratch, using only - the gems in your Gemfile, which may resolve the conflict. + bad_error = <<~E.strip + Bundler could not find compatible versions for gem "rack-obama": + In Gemfile: + rack-obama (= 2.0) E - bundle :install, :retry => 0, :raise_on_error => false - expect(err).to end_with(nice_error) + bundle "update rack_middleware", retry: 0, raise_on_error: false + expect(err).not_to end_with(bad_error) + end + end + + describe "when running bundle update and Gemfile conflicts with lockfile" do + before(:each) do + build_repo4 do + build_gem "jekyll-feed", "0.16.0" + build_gem "jekyll-feed", "0.15.1" + + build_gem "github-pages", "226" do |s| + s.add_dependency "jekyll-feed", "0.15.1" + end + end + + install_gemfile <<-G + source "#{file_uri_for(gem_repo4)}" + gem "jekyll-feed", "~> 0.12" + G + + gemfile <<-G + source "#{file_uri_for(gem_repo4)}" + gem "github-pages", "~> 226" + gem "jekyll-feed", "~> 0.12" + G + end + + it "discards the conflicting lockfile information and resolves properly" do + bundle :update, raise_on_error: false, all: true + expect(err).to be_empty end end @@ -232,6 +268,11 @@ RSpec.describe "bundle flex_install" do it "should work when you install" do bundle "install" + checksums = checksums_section_when_existing do |c| + c.checksum gem_repo1, "rack", "0.9.1" + c.checksum gem_repo1, "rack-obama", "1.0" + end + expect(lockfile).to eq <<~L GEM remote: #{file_uri_for(gem_repo1)}/ @@ -246,7 +287,7 @@ RSpec.describe "bundle flex_install" do DEPENDENCIES rack (= 0.9.1) rack-obama - + #{checksums} BUNDLED WITH #{Bundler::VERSION} L @@ -272,6 +313,10 @@ RSpec.describe "bundle flex_install" do gem "rack" G + checksums = checksums_section_when_existing do |c| + c.checksum gem_repo1, "rack", "1.0.0" + end + expect(lockfile).to eq <<~L GEM remote: #{file_uri_for(gem_repo1)}/ @@ -287,7 +332,7 @@ RSpec.describe "bundle flex_install" do DEPENDENCIES rack - + #{checksums} BUNDLED WITH #{Bundler::VERSION} L @@ -314,7 +359,7 @@ RSpec.describe "bundle flex_install" do end end - it "prints the correct error message" do + it "resolves them" do # install Rails 3.0.0.rc install_gemfile <<-G source "#{file_uri_for(gem_repo2)}" @@ -323,13 +368,12 @@ RSpec.describe "bundle flex_install" do G # upgrade Rails to 3.0.0 and then install again - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G source "#{file_uri_for(gem_repo2)}" gem "rails", "3.0.0" gem "capybara", "0.3.9" G - - expect(err).to include("Gemfile.lock") + expect(err).to be_empty end end end diff --git a/spec/bundler/install/gems/fund_spec.rb b/spec/bundler/install/gems/fund_spec.rb index f521b0296f..9aadc9ed25 100644 --- a/spec/bundler/install/gems/fund_spec.rb +++ b/spec/bundler/install/gems/fund_spec.rb @@ -6,20 +6,20 @@ RSpec.describe "bundle install" do build_repo2 do build_gem "has_funding_and_other_metadata" do |s| s.metadata = { - "bug_tracker_uri" => "https://example.com/user/bestgemever/issues", - "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md", + "bug_tracker_uri" => "https://example.com/user/bestgemever/issues", + "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md", "documentation_uri" => "https://www.example.info/gems/bestgemever/0.0.1", - "homepage_uri" => "https://bestgemever.example.io", - "mailing_list_uri" => "https://groups.example.com/bestgemever", - "funding_uri" => "https://example.com/has_funding_and_other_metadata/funding", - "source_code_uri" => "https://example.com/user/bestgemever", - "wiki_uri" => "https://example.com/user/bestgemever/wiki", + "homepage_uri" => "https://bestgemever.example.io", + "mailing_list_uri" => "https://groups.example.com/bestgemever", + "funding_uri" => "https://example.com/has_funding_and_other_metadata/funding", + "source_code_uri" => "https://example.com/user/bestgemever", + "wiki_uri" => "https://example.com/user/bestgemever/wiki", } end build_gem "has_funding", "1.2.3" do |s| s.metadata = { - "funding_uri" => "https://example.com/has_funding/funding", + "funding_uri" => "https://example.com/has_funding/funding", } end @@ -52,6 +52,33 @@ RSpec.describe "bundle install" do end end + context "when gems include a fund URI but `ignore_funding_requests` is configured" do + before do + bundle "config set ignore_funding_requests true" + end + + it "does not display the plural fund message after installing" do + install_gemfile <<-G + source "#{file_uri_for(gem_repo2)}" + gem 'has_funding_and_other_metadata' + gem 'has_funding' + gem 'rack-obama' + G + + expect(out).not_to include("2 installed gems you directly depend on are looking for funding.") + end + + it "does not display the singular fund message after installing" do + install_gemfile <<-G + source "#{file_uri_for(gem_repo2)}" + gem 'has_funding' + gem 'rack-obama' + G + + expect(out).not_to include("1 installed gem you directly depend on is looking for funding.") + end + end + context "when gems do not include fund messages" do it "does not display any fund messages" do install_gemfile <<-G diff --git a/spec/bundler/install/gems/native_extensions_spec.rb b/spec/bundler/install/gems/native_extensions_spec.rb index d5cafcfc2c..907778a384 100644 --- a/spec/bundler/install/gems/native_extensions_spec.rb +++ b/spec/bundler/install/gems/native_extensions_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.describe "installing a gem with native extensions", :ruby_repo do +RSpec.describe "installing a gem with native extensions" do it "installs" do build_repo2 do build_gem "c_extension" do |s| @@ -91,7 +91,7 @@ RSpec.describe "installing a gem with native extensions", :ruby_repo do it "installs correctly from git when multiple gems with extensions share one repository" do build_repo2 do ["one", "two"].each do |n| - build_lib "c_extension_#{n}", "1.0", :path => lib_path("gems/c_extension_#{n}") do |s| + build_lib "c_extension_#{n}", "1.0", path: lib_path("gems/c_extension_#{n}") do |s| s.extensions = ["ext/extconf.rb"] s.write "ext/extconf.rb", <<-E require "mkmf" @@ -119,7 +119,7 @@ RSpec.describe "installing a gem with native extensions", :ruby_repo do C end end - build_git "gems", :path => lib_path("gems"), :gemspec => false + build_git "gems", path: lib_path("gems"), gemspec: false end bundle "config set build.c_extension_one --with-c_extension_one=one" diff --git a/spec/bundler/install/gems/resolving_spec.rb b/spec/bundler/install/gems/resolving_spec.rb index 469ecd412f..c5f9c4a3d3 100644 --- a/spec/bundler/install/gems/resolving_spec.rb +++ b/spec/bundler/install/gems/resolving_spec.rb @@ -106,7 +106,7 @@ RSpec.describe "bundle install with install-time dependencies" do 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, "wb") do |f| f.write Gem.deflate(Marshal.dump(spec)) @@ -131,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 @@ -140,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" @@ -157,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 @@ -171,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 @@ -185,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 @@ -214,13 +208,13 @@ 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 @@ -235,51 +229,304 @@ RSpec.describe "bundle install with install-time dependencies" do end end - install_gemfile <<-G, :artifice => "endpoint", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s } - ruby "#{RUBY_VERSION}" + 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(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 "gives a meaningful error if there's a lockfile using the newer incompatible version" do - build_repo2 do - build_gem "parallel_tests", "3.7.0" do |s| - s.required_ruby_version = ">= #{current_ruby_minor}" + 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 - build_gem "parallel_tests", "3.8.0" do |s| - s.required_ruby_version = ">= #{next_ruby_minor}" + 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 - gemfile <<-G - source "http://localgemserver.test/" - gem 'parallel_tests' - G + 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 } - lockfile <<~L - GEM - remote: http://localgemserver.test/ - specs: - parallel_tests (3.8.0) + 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}" - PLATFORMS - #{lockfile_platforms} + s.add_dependency "rubocop-ast", ">= 1.17.0", "< 2.0" + end - DEPENDENCIES - parallel_tests + build_gem "rubocop", "1.35.0" do |s| + s.required_ruby_version = ">= #{next_ruby_minor}" - BUNDLED WITH - #{Bundler::VERSION} - L + s.add_dependency "rubocop-ast", ">= 1.20.1", "< 2.0" + end - bundle "install --verbose", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s }, :raise_on_error => false - 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.") + 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 @@ -291,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 @@ -308,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 @@ -337,33 +584,32 @@ RSpec.describe "bundle install with install-time dependencies" do end end - let(:ruby_requirement) { %("#{RUBY_VERSION}") } - let(:error_message_requirement) { "= #{RUBY_VERSION}" } + 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 + 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 = strip_whitespace(<<-E).strip - Bundler found conflicting requirements for the Ruby\0 version: - In Gemfile: - require_ruby was resolved to 1.0, which depends on - Ruby\0 (> 9000) - - Current Ruby\0 version: - Ruby\0 (#{error_message_requirement}) + 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 - 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' @@ -371,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: - require_ruby was resolved to 1.0, which depends on - Ruby\0 (> 9000) - - Current Ruby\0 version: - Ruby\0 (#{error_message_requirement}) + 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 @@ -410,21 +655,18 @@ 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") - nice_error = strip_whitespace(<<-E).strip - Bundler found conflicting requirements for the RubyGems\0 version: - In Gemfile: - require_rubygems was resolved to 1.0, which depends on - RubyGems\0 (> 9000) - - Current RubyGems\0 version: - RubyGems\0 (= #{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 diff --git a/spec/bundler/install/gems/standalone_spec.rb b/spec/bundler/install/gems/standalone_spec.rb index 5cbb484c68..46cab2dfeb 100644 --- a/spec/bundler/install/gems/standalone_spec.rb +++ b/spec/bundler/install/gems/standalone_spec.rb @@ -32,6 +32,21 @@ RSpec.shared_examples "bundle install --standalone" do expect(out).to eq(expected_gems.values.join("\n")) end + it "makes the gems available without bundler nor rubygems" do + testrb = String.new <<-RUBY + $:.unshift File.expand_path("bundle") + require "bundler/setup" + + RUBY + expected_gems.each do |k, _| + testrb << "\nrequire \"#{k}\"" + testrb << "\nputs #{k.upcase}" + end + sys_exec %(#{Gem.ruby} --disable-gems -w -e #{testrb.shellescape}) + + expect(out).to eq(expected_gems.values.join("\n")) + end + it "makes the gems available without bundler via Kernel.require" do testrb = String.new <<-RUBY $:.unshift File.expand_path("bundle") @@ -82,7 +97,23 @@ RSpec.shared_examples "bundle install --standalone" do testrb << "\nrequire \"#{k}\"" testrb << "\nputs #{k.upcase}" end - ruby testrb, :dir => "#{bundled_app}2" + ruby testrb, dir: "#{bundled_app}2" + + expect(out).to eq(expected_gems.values.join("\n")) + end + + it "skips activating gems" do + testrb = String.new <<-RUBY + $:.unshift File.expand_path("bundle") + require "bundler/setup" + + gem "do_not_activate_me" + RUBY + expected_gems.each do |k, _| + testrb << "\nrequire \"#{k}\"" + testrb << "\nputs #{k.upcase}" + end + sys_exec %(#{Gem.ruby} -w -e #{testrb.shellescape}) expect(out).to eq(expected_gems.values.join("\n")) end @@ -95,7 +126,7 @@ RSpec.shared_examples "bundle install --standalone" do gem "rails" G bundle "config set --local path #{bundled_app("bundle")}" - bundle :install, :standalone => true, :dir => cwd + bundle :install, standalone: true, dir: cwd end let(:expected_gems) do @@ -110,26 +141,18 @@ RSpec.shared_examples "bundle install --standalone" do describe "with default gems and a lockfile", :ruby_repo do before do - skip "does not work on rubygems versions where `--install_dir` doesn't respect --default" unless Gem::Installer.for_spec(loaded_gemspec, :install_dir => "/foo").default_spec_file == "/foo/specifications/default/bundler-#{Bundler::VERSION}.gemspec" # Since rubygems 3.2.0.rc.2 - skip "does not work on old rubies because the realworld gems that need to be installed don't support them" if RUBY_VERSION < "2.7.0" - - if Gem.win_platform? && RUBY_VERSION < "3.1.0" - default_fiddle_version = ruby "require 'fiddle'; puts Gem.loaded_specs['fiddle'].version" - realworld_system_gems "fiddle --version #{default_fiddle_version}" - end - realworld_system_gems "tsort --version 0.1.0" - necessary_system_gems = ["optparse --version 0.1.1", "psych --version 3.3.2", "logger --version 1.4.3", "etc --version 1.2.0", "stringio --version 3.0.1"] + necessary_system_gems = ["optparse --version 0.1.1", "psych --version 3.3.2", "logger --version 1.4.3", "etc --version 1.2.0", "stringio --version 3.1.0"] necessary_system_gems += ["shellwords --version 0.1.0", "base64 --version 0.1.0", "resolv --version 0.2.1"] if Gem.rubygems_version < Gem::Version.new("3.3.a") necessary_system_gems += ["yaml --version 0.1.1"] if Gem.rubygems_version < Gem::Version.new("3.4.a") - realworld_system_gems(*necessary_system_gems, :path => scoped_gem_path(bundled_app("bundle"))) + realworld_system_gems(*necessary_system_gems, path: scoped_gem_path(bundled_app("bundle"))) - build_gem "foo", "1.0.0", :to_system => true, :default => true do |s| + build_gem "foo", "1.0.0", to_system: true, default: true do |s| s.add_dependency "bar" end - build_gem "bar", "1.0.0", :to_system => true, :default => true + build_gem "bar", "1.0.0", to_system: true, default: true build_repo4 do build_gem "foo", "1.0.0" do |s| @@ -144,18 +167,25 @@ RSpec.shared_examples "bundle install --standalone" do gem "foo" G - bundle "lock", :dir => cwd, :artifice => "compact_index" + bundle "lock", dir: cwd, artifice: "compact_index" end - it "works" do + it "works and points to the vendored copies, not to the default copies", :realworld do bundle "config set --local path #{bundled_app("bundle")}" - bundle :install, :standalone => true, :dir => cwd, :artifice => "compact_index", :env => { "BUNDLER_GEM_DEFAULT_DIR" => system_gem_path.to_s } + bundle :install, standalone: true, dir: cwd, artifice: "compact_index", env: { "BUNDLER_GEM_DEFAULT_DIR" => system_gem_path.to_s } + + load_path_lines = bundled_app("bundle/bundler/setup.rb").read.split("\n").select {|line| line.start_with?("$:.unshift") } + + expect(load_path_lines).to eq [ + '$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/bar-1.0.0/lib")', + '$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/foo-1.0.0/lib")', + ] end end - describe "with Gemfiles using path sources and resulting bundle moved to a folder hierarchy with different nesting" do + describe "with Gemfiles using absolute path sources and resulting bundle moved to a folder hierarchy with different nesting" do before do - build_lib "minitest", "1.0.0", :path => lib_path("minitest") + build_lib "minitest", "1.0.0", path: lib_path("minitest") Dir.mkdir bundled_app("app") @@ -164,14 +194,14 @@ RSpec.shared_examples "bundle install --standalone" do gem "minitest", :path => "#{lib_path("minitest")}" G - bundle "install", :standalone => true, :dir => bundled_app("app") + bundle "install", standalone: true, dir: bundled_app("app") Dir.mkdir tmp("one_more_level") FileUtils.mv bundled_app, tmp("one_more_level") end it "also works" do - ruby <<-RUBY, :dir => tmp("one_more_level/bundled_app/app") + ruby <<-RUBY, dir: tmp("one_more_level/bundled_app/app") require "./bundle/bundler/setup" require "minitest" @@ -183,10 +213,39 @@ RSpec.shared_examples "bundle install --standalone" do end end - describe "with gems with native extension", :ruby_repo do + describe "with Gemfiles using relative path sources and app moved to a different root" do + before do + FileUtils.mkdir_p bundled_app("app/vendor") + + build_lib "minitest", "1.0.0", path: bundled_app("app/vendor/minitest") + + gemfile bundled_app("app/Gemfile"), <<-G + source "#{file_uri_for(gem_repo1)}" + gem "minitest", :path => "vendor/minitest" + G + + bundle "install", standalone: true, dir: bundled_app("app") + + FileUtils.mv(bundled_app("app"), bundled_app2("app")) + end + + it "also works" do + ruby <<-RUBY, dir: bundled_app2("app") + require "./bundle/bundler/setup" + + require "minitest" + puts MINITEST + RUBY + + expect(out).to eq("1.0.0") + expect(err).to be_empty + end + end + + describe "with gems with native extension" do before do bundle "config set --local path #{bundled_app("bundle")}" - install_gemfile <<-G, :standalone => true, :dir => cwd + install_gemfile <<-G, standalone: true, dir: cwd source "#{file_uri_for(gem_repo1)}" gem "very_simple_binary" G @@ -194,15 +253,19 @@ RSpec.shared_examples "bundle install --standalone" do it "generates a bundle/bundler/setup.rb with the proper paths" do expected_path = bundled_app("bundle/bundler/setup.rb") - extension_line = File.read(expected_path).each_line.find {|line| line.include? "/extensions/" }.strip - expect(extension_line).to start_with '$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{RbConfig::CONFIG["ruby_version"]}/extensions/' - expect(extension_line).to end_with '/very_simple_binary-1.0")' + script_content = File.read(expected_path) + expect(script_content).to include("def self.ruby_api_version") + expect(script_content).to include("def self.extension_api_version") + extension_line = script_content.each_line.find {|line| line.include? "/extensions/" }.strip + platform = Gem::Platform.local + expect(extension_line).to start_with '$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/extensions/' + expect(extension_line).to end_with platform.to_s + '/#{Gem.extension_api_version}/very_simple_binary-1.0")' end end describe "with gem that has an invalid gemspec" do before do - build_git "bar", :gemspec => false do |s| + build_git "bar", gemspec: false do |s| s.write "lib/bar/version.rb", %(BAR_VERSION = '1.0') s.write "bar.gemspec", <<-G lib = File.expand_path('lib/', __dir__) @@ -220,7 +283,7 @@ RSpec.shared_examples "bundle install --standalone" do G end bundle "config set --local path #{bundled_app("bundle")}" - install_gemfile <<-G, :standalone => true, :dir => cwd, :raise_on_error => false + install_gemfile <<-G, standalone: true, dir: cwd, raise_on_error: false source "#{file_uri_for(gem_repo1)}" gem "bar", :git => "#{lib_path("bar-1.0")}" G @@ -242,7 +305,7 @@ RSpec.shared_examples "bundle install --standalone" do gem "devise", :git => "#{lib_path("devise-1.0")}" G bundle "config set --local path #{bundled_app("bundle")}" - bundle :install, :standalone => true, :dir => cwd + bundle :install, standalone: true, dir: cwd end let(:expected_gems) do @@ -270,7 +333,7 @@ RSpec.shared_examples "bundle install --standalone" do end G bundle "config set --local path #{bundled_app("bundle")}" - bundle :install, :standalone => true, :dir => cwd + bundle :install, standalone: true, dir: cwd end let(:expected_gems) do @@ -284,7 +347,7 @@ RSpec.shared_examples "bundle install --standalone" do it "allows creating a standalone file with limited groups" do bundle "config set --local path #{bundled_app("bundle")}" - bundle :install, :standalone => "default", :dir => cwd + bundle :install, standalone: "default", dir: cwd load_error_ruby <<-RUBY, "spec" $:.unshift File.expand_path("bundle") @@ -302,7 +365,7 @@ RSpec.shared_examples "bundle install --standalone" do it "allows `without` configuration to limit the groups used in a standalone" do bundle "config set --local path #{bundled_app("bundle")}" bundle "config set --local without test" - bundle :install, :standalone => true, :dir => cwd + bundle :install, standalone: true, dir: cwd load_error_ruby <<-RUBY, "spec" $:.unshift File.expand_path("bundle") @@ -319,7 +382,7 @@ RSpec.shared_examples "bundle install --standalone" do it "allows `path` configuration to change the location of the standalone bundle" do bundle "config set --local path path/to/bundle" - bundle "install", :standalone => true, :dir => cwd + bundle "install", standalone: true, dir: cwd ruby <<-RUBY $:.unshift File.expand_path("path/to/bundle") @@ -334,9 +397,9 @@ RSpec.shared_examples "bundle install --standalone" do it "allows `without` to limit the groups used in a standalone" do bundle "config set --local without test" - bundle :install, :dir => cwd + bundle :install, dir: cwd bundle "config set --local path #{bundled_app("bundle")}" - bundle :install, :standalone => true, :dir => cwd + bundle :install, standalone: true, dir: cwd load_error_ruby <<-RUBY, "spec" $:.unshift File.expand_path("bundle") @@ -362,7 +425,7 @@ RSpec.shared_examples "bundle install --standalone" do gem "rails" G bundle "config set --local path #{bundled_app("bundle")}" - bundle :install, :standalone => true, :artifice => "endpoint", :dir => cwd + bundle :install, standalone: true, artifice: "endpoint", dir: cwd end let(:expected_gems) do @@ -376,14 +439,14 @@ RSpec.shared_examples "bundle install --standalone" do end end - describe "with --binstubs", :bundler => "< 3" do + describe "with --binstubs", bundler: "< 3" do before do gemfile <<-G source "#{file_uri_for(gem_repo1)}" gem "rails" G bundle "config set --local path #{bundled_app("bundle")}" - bundle :install, :standalone => true, :binstubs => true, :dir => cwd + bundle :install, standalone: true, binstubs: true, dir: cwd end let(:expected_gems) do @@ -401,7 +464,7 @@ RSpec.shared_examples "bundle install --standalone" do it "creates stubs that can be executed from anywhere" do require "tmpdir" - sys_exec(%(#{bundled_app("bin/rails")} -v), :dir => Dir.tmpdir) + sys_exec(%(#{bundled_app("bin/rails")} -v), dir: Dir.tmpdir) expect(out).to eq("2.3.2") end @@ -435,3 +498,31 @@ RSpec.describe "bundle install --standalone run in a subdirectory" do include_examples("bundle install --standalone") end + +RSpec.describe "bundle install --standalone --local" do + before do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack" + G + + system_gems "rack-1.0.0", path: default_bundle_path + end + + it "generates script pointing to system gems" do + bundle "install --standalone --local --verbose" + + expect(out).to include("Using rack 1.0.0") + + load_error_ruby <<-RUBY, "spec" + require "./bundler/setup" + + require "rack" + puts RACK + require "spec" + RUBY + + expect(out).to eq("1.0.0") + expect(err).to eq("ZOMG LOAD ERROR") + end +end diff --git a/spec/bundler/install/gems/sudo_spec.rb b/spec/bundler/install/gems/sudo_spec.rb deleted file mode 100644 index 41b241da25..0000000000 --- a/spec/bundler/install/gems/sudo_spec.rb +++ /dev/null @@ -1,205 +0,0 @@ -# frozen_string_literal: true - -RSpec.describe "when using sudo", :sudo => true do - describe "and BUNDLE_PATH is writable" do - context "but BUNDLE_PATH/build_info is not writable" do - let(:subdir) do - system_gem_path("cache") - end - - before do - bundle "config set path.system true" - subdir.mkpath - sudo "chmod u-w #{subdir}" - end - - after do - sudo "chmod u+w #{subdir}" - end - - it "installs" do - install_gemfile <<-G - source "#{file_uri_for(gem_repo1)}" - gem "rack" - G - - expect(out).to_not match(/an error occurred/i) - expect(system_gem_path("cache/rack-1.0.0.gem")).to exist - expect(the_bundle).to include_gems "rack 1.0" - end - end - end - - describe "and GEM_HOME is owned by root" do - before :each do - bundle "config set path.system true" - chown_system_gems_to_root - end - - it "installs" do - install_gemfile <<-G - source "#{file_uri_for(gem_repo1)}" - gem "rack", '1.0' - gem "thin" - G - - expect(system_gem_path("gems/rack-1.0.0")).to exist - expect(system_gem_path("gems/rack-1.0.0").stat.uid).to eq(0) - expect(the_bundle).to include_gems "rack 1.0" - end - - it "installs rake and a gem dependent on rake in the same session" do - build_repo2 do - build_gem "another_implicit_rake_dep" do |s| - s.extensions << "Rakefile" - s.write "Rakefile", <<-RUBY - task :default do - 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'" - end - end - RUBY - end - end - - gemfile <<-G - source "#{file_uri_for(gem_repo2)}" - gem "rake" - gem "another_implicit_rake_dep" - G - bundle "install" - expect(system_gem_path("gems/another_implicit_rake_dep-1.0")).to exist - end - - it "installs when BUNDLE_PATH is owned by root" do - bundle_path = tmp("owned_by_root") - FileUtils.mkdir_p bundle_path - sudo "chown -R root #{bundle_path}" - - ENV["BUNDLE_PATH"] = bundle_path.to_s - install_gemfile <<-G - source "#{file_uri_for(gem_repo1)}" - gem "rack", '1.0' - G - - expect(bundle_path.join(Bundler.ruby_scope, "gems/rack-1.0.0")).to exist - expect(bundle_path.join(Bundler.ruby_scope, "gems/rack-1.0.0").stat.uid).to eq(0) - expect(the_bundle).to include_gems "rack 1.0" - end - - it "installs when BUNDLE_PATH does not exist" do - root_path = tmp("owned_by_root") - FileUtils.mkdir_p root_path - sudo "chown -R root #{root_path}" - bundle_path = root_path.join("does_not_exist") - - ENV["BUNDLE_PATH"] = bundle_path.to_s - install_gemfile <<-G - source "#{file_uri_for(gem_repo1)}" - gem "rack", '1.0' - G - - expect(bundle_path.join(Bundler.ruby_scope, "gems/rack-1.0.0")).to exist - expect(bundle_path.join(Bundler.ruby_scope, "gems/rack-1.0.0").stat.uid).to eq(0) - expect(the_bundle).to include_gems "rack 1.0" - end - - it "installs extensions/" do - install_gemfile <<-G - source "#{file_uri_for(gem_repo1)}" - gem "very_simple_binary" - G - - expect(system_gem_path("gems/very_simple_binary-1.0")).to exist - binary_glob = system_gem_path("extensions/*/*/very_simple_binary-1.0") - expect(Dir.glob(binary_glob).first).to be - end - end - - describe "and BUNDLE_PATH is not writable" do - before do - bundle "config set --local path .bundle" - sudo "chmod ugo-w .bundle" - end - - after do - sudo "chmod ugo+w .bundle" - end - - it "installs" do - install_gemfile <<-G - source "#{file_uri_for(gem_repo1)}" - gem "rack", '1.0' - G - - expect(local_gem_path("gems/rack-1.0.0")).to exist - expect(the_bundle).to include_gems "rack 1.0" - end - - it "cleans up the tmpdirs generated" do - require "tmpdir" - Dir.glob("#{Dir.tmpdir}/bundler*").each do |tmpdir| - FileUtils.remove_entry_secure(tmpdir) - end - - install_gemfile <<-G - source "#{file_uri_for(gem_repo1)}" - gem "rack" - G - tmpdirs = Dir.glob("#{Dir.tmpdir}/bundler*") - - expect(tmpdirs).to be_empty - end - end - - describe "and GEM_HOME is not writable" do - it "installs" do - bundle "config set path.system true" - gem_home = tmp("sudo_gem_home") - sudo "mkdir -p #{gem_home}" - sudo "chmod ugo-w #{gem_home}" - - system_gems :bundler, :path => gem_home - - gemfile <<-G - source "#{file_uri_for(gem_repo1)}" - gem "rack", '1.0' - G - - bundle :install, :env => { "GEM_HOME" => gem_home.to_s, "GEM_PATH" => nil } - expect(gem_home.join("bin/rackup")).to exist - expect(the_bundle).to include_gems "rack 1.0", :env => { "GEM_HOME" => gem_home.to_s, "GEM_PATH" => nil } - - sudo "rm -rf #{tmp("sudo_gem_home")}" - end - end - - describe "and root runs install" do - let(:warning) { "Don't run Bundler as root." } - - before do - gemfile %(source "#{file_uri_for(gem_repo1)}") - end - - it "warns against that" do - bundle :install, :sudo => :preserve_env - expect(err).to include(warning) - end - - context "when ENV['BUNDLE_SILENCE_ROOT_WARNING'] is set" do - it "skips the warning" do - bundle :install, :sudo => :preserve_env, :env => { "BUNDLE_SILENCE_ROOT_WARNING" => "true" } - expect(err).to_not include(warning) - end - end - - context "when silence_root_warning = false" do - it "warns against that" do - bundle :install, :sudo => :preserve_env, :env => { "BUNDLE_SILENCE_ROOT_WARNING" => "false" } - expect(err).to include(warning) - end - end - end -end diff --git a/spec/bundler/install/gemspecs_spec.rb b/spec/bundler/install/gemspecs_spec.rb index 3684d8749d..51aa0ed14f 100644 --- a/spec/bundler/install/gemspecs_spec.rb +++ b/spec/bundler/install/gemspecs_spec.rb @@ -4,7 +4,7 @@ RSpec.describe "bundle install" do describe "when a gem has a YAML gemspec" do before :each do build_repo2 do - build_gem "yaml_spec", :gemspec => :yaml + build_gem "yaml_spec", gemspec: :yaml end end @@ -18,7 +18,7 @@ RSpec.describe "bundle install" do end it "still installs correctly when using path" do - build_lib "yaml_spec", :gemspec => :yaml + build_lib "yaml_spec", gemspec: :yaml install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" @@ -34,7 +34,7 @@ RSpec.describe "bundle install" do gem 'rack' G - system_gems "rack-1.0.0", :path => default_bundle_path + system_gems "rack-1.0.0", path: default_bundle_path FileUtils.mkdir_p "#{default_bundle_path}/specifications" File.open("#{default_bundle_path}/specifications/rack-1.0.0.gemspec", "w+") do |f| @@ -45,7 +45,7 @@ RSpec.describe "bundle install" do end f.write spec.to_ruby end - bundle :install, :artifice => "endpoint_marshal_fail" # force gemspec load + bundle :install, artifice: "endpoint_marshal_fail" # force gemspec load expect(the_bundle).to include_gems "rack 1.0.0", "activesupport 2.3.2" end @@ -59,7 +59,7 @@ RSpec.describe "bundle install" do end G - install_gemfile <<-G, :env => { "LANG" => "C" } + install_gemfile <<-G, env: { "LANG" => "C" } source "#{file_uri_for(gem_repo1)}" gemspec G @@ -94,8 +94,9 @@ RSpec.describe "bundle install" do end context "when ruby version is specified in gemspec and gemfile" do - it "installs when patch level is not specified and the version matches" do - build_lib("foo", :path => bundled_app) do |s| + it "installs when patch level is not specified and the version matches", + if: RUBY_PATCHLEVEL >= 0 do + build_lib("foo", path: bundled_app) do |s| s.required_ruby_version = "~> #{RUBY_VERSION}.0" end @@ -108,12 +109,12 @@ RSpec.describe "bundle install" do end it "installs when patch level is specified and the version still matches the current version", - :if => RUBY_PATCHLEVEL >= 0 do - build_lib("foo", :path => bundled_app) do |s| + if: RUBY_PATCHLEVEL >= 0 do + build_lib("foo", path: bundled_app) do |s| s.required_ruby_version = "#{RUBY_VERSION}.#{RUBY_PATCHLEVEL}" end - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G, raise_on_error: false ruby '#{RUBY_VERSION}', :engine_version => '#{RUBY_VERSION}', :engine => 'ruby', :patchlevel => '#{RUBY_PATCHLEVEL}' source "#{file_uri_for(gem_repo1)}" gemspec @@ -122,13 +123,13 @@ RSpec.describe "bundle install" do end it "fails and complains about patchlevel on patchlevel mismatch", - :if => RUBY_PATCHLEVEL >= 0 do + if: RUBY_PATCHLEVEL >= 0 do patchlevel = RUBY_PATCHLEVEL.to_i + 1 - build_lib("foo", :path => bundled_app) do |s| + build_lib("foo", path: bundled_app) do |s| s.required_ruby_version = "#{RUBY_VERSION}.#{patchlevel}" end - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G, raise_on_error: false ruby '#{RUBY_VERSION}', :engine_version => '#{RUBY_VERSION}', :engine => 'ruby', :patchlevel => '#{patchlevel}' source "#{file_uri_for(gem_repo1)}" gemspec @@ -142,11 +143,11 @@ RSpec.describe "bundle install" do it "fails and complains about version on version mismatch" do version = Gem::Requirement.create(RUBY_VERSION).requirements.first.last.bump.version - build_lib("foo", :path => bundled_app) do |s| + build_lib("foo", path: bundled_app) do |s| s.required_ruby_version = version end - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G, raise_on_error: false ruby '#{version}', :engine_version => '#{version}', :engine => 'ruby' source "#{file_uri_for(gem_repo1)}" gemspec diff --git a/spec/bundler/install/git_spec.rb b/spec/bundler/install/git_spec.rb index d43aacee7e..c8d574baf3 100644 --- a/spec/bundler/install/git_spec.rb +++ b/spec/bundler/install/git_spec.rb @@ -3,51 +3,51 @@ RSpec.describe "bundle install" do context "git sources" do it "displays the revision hash of the gem repository" do - build_git "foo", "1.0", :path => lib_path("foo") + build_git "foo", "1.0", path: lib_path("foo") - install_gemfile <<-G, :verbose => true + install_gemfile <<-G, verbose: true source "#{file_uri_for(gem_repo1)}" gem "foo", :git => "#{file_uri_for(lib_path("foo"))}" G - expect(out).to include("Using foo 1.0 from #{file_uri_for(lib_path("foo"))} (at master@#{revision_for(lib_path("foo"))[0..6]})") - expect(the_bundle).to include_gems "foo 1.0", :source => "git@#{lib_path("foo")}" + expect(out).to include("Using foo 1.0 from #{file_uri_for(lib_path("foo"))} (at main@#{revision_for(lib_path("foo"))[0..6]})") + expect(the_bundle).to include_gems "foo 1.0", source: "git@#{lib_path("foo")}" end - it "displays the correct default branch", :git => ">= 2.28.0" do - build_git "foo", "1.0", :path => lib_path("foo"), :default_branch => "main" + it "displays the correct default branch", git: ">= 2.28.0" do + build_git "foo", "1.0", path: lib_path("foo"), default_branch: "main" - install_gemfile <<-G, :verbose => true + install_gemfile <<-G, verbose: true source "#{file_uri_for(gem_repo1)}" gem "foo", :git => "#{file_uri_for(lib_path("foo"))}" G expect(out).to include("Using foo 1.0 from #{file_uri_for(lib_path("foo"))} (at main@#{revision_for(lib_path("foo"))[0..6]})") - expect(the_bundle).to include_gems "foo 1.0", :source => "git@#{lib_path("foo")}" + expect(the_bundle).to include_gems "foo 1.0", source: "git@#{lib_path("foo")}" end it "displays the ref of the gem repository when using branch~num as a ref" do skip "maybe branch~num notation doesn't work on Windows' git" if Gem.win_platform? - build_git "foo", "1.0", :path => lib_path("foo") + build_git "foo", "1.0", path: lib_path("foo") rev = revision_for(lib_path("foo"))[0..6] - update_git "foo", "2.0", :path => lib_path("foo"), :gemspec => true + update_git "foo", "2.0", path: lib_path("foo"), gemspec: true rev2 = revision_for(lib_path("foo"))[0..6] - update_git "foo", "3.0", :path => lib_path("foo"), :gemspec => true + update_git "foo", "3.0", path: lib_path("foo"), gemspec: true - install_gemfile <<-G, :verbose => true + install_gemfile <<-G, verbose: true source "#{file_uri_for(gem_repo1)}" - gem "foo", :git => "#{file_uri_for(lib_path("foo"))}", :ref => "master~2" + gem "foo", :git => "#{file_uri_for(lib_path("foo"))}", :ref => "main~2" G - expect(out).to include("Using foo 1.0 from #{file_uri_for(lib_path("foo"))} (at master~2@#{rev})") - expect(the_bundle).to include_gems "foo 1.0", :source => "git@#{lib_path("foo")}" + expect(out).to include("Using foo 1.0 from #{file_uri_for(lib_path("foo"))} (at main~2@#{rev})") + expect(the_bundle).to include_gems "foo 1.0", source: "git@#{lib_path("foo")}" - update_git "foo", "4.0", :path => lib_path("foo"), :gemspec => true + update_git "foo", "4.0", path: lib_path("foo"), gemspec: true - bundle :update, :all => true, :verbose => true - expect(out).to include("Using foo 2.0 (was 1.0) from #{file_uri_for(lib_path("foo"))} (at master~2@#{rev2})") - expect(the_bundle).to include_gems "foo 2.0", :source => "git@#{lib_path("foo")}" + bundle :update, all: true, verbose: true + expect(out).to include("Using foo 2.0 (was 1.0) from #{file_uri_for(lib_path("foo"))} (at main~2@#{rev2})") + expect(the_bundle).to include_gems "foo 2.0", source: "git@#{lib_path("foo")}" end it "should allows git repos that are missing but not being installed" do @@ -81,9 +81,9 @@ RSpec.describe "bundle install" do it "allows multiple gems from the same git source" do build_repo2 do - build_lib "foo", "1.0", :path => lib_path("gems/foo") - build_lib "zebra", "2.0", :path => lib_path("gems/zebra") - build_git "gems", :path => lib_path("gems"), :gemspec => false + build_lib "foo", "1.0", path: lib_path("gems/foo") + build_lib "zebra", "2.0", path: lib_path("gems/zebra") + build_git "gems", path: lib_path("gems"), gemspec: false end install_gemfile <<-G @@ -98,5 +98,108 @@ RSpec.describe "bundle install" do bundle "info zebra" expect(out).to include("* zebra (2.0 #{revision_for(lib_path("gems"))[0..6]})") end + + it "should always sort dependencies in the same order" do + # This Gemfile + lockfile had a problem where the first + # `bundle install` would change the order, but the second would + # change it back. + + # NOTE: both gems MUST have the same path! It has to be two gems in one repo. + + test = build_git "test", "1.0.0", path: lib_path("test-and-other") + other = build_git "other", "1.0.0", path: lib_path("test-and-other") + test_ref = test.ref_for("HEAD") + other_ref = other.ref_for("HEAD") + + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + + gem "test", git: #{test.path.to_s.inspect} + gem "other", ref: #{other_ref.inspect}, git: #{other.path.to_s.inspect} + G + + lockfile <<-L + GIT + remote: #{test.path} + revision: #{test_ref} + specs: + test (1.0.0) + + GIT + remote: #{other.path} + revision: #{other_ref} + ref: #{other_ref} + specs: + other (1.0.0) + + GEM + remote: #{file_uri_for(gem_repo1)}/ + specs: + + PLATFORMS + ruby + + DEPENDENCIES + other! + test! + + BUNDLED WITH + #{Bundler::VERSION} + L + + # If GH#6743 is present, the first `bundle install` will change the + # lockfile, by flipping the order (`other` would be moved to the top). + # + # The second `bundle install` would then change the lockfile back + # to the original. + # + # The fix makes it so it may change it once, but it will not change + # it a second time. + # + # So, we run `bundle install` once, and store the value of the + # modified lockfile. + bundle :install + modified_lockfile = lockfile + + # If GH#6743 is present, the second `bundle install` would change the + # lockfile back to what it was originally. + # + # This `expect` makes sure it doesn't change a second time. + bundle :install + expect(lockfile).to eq(modified_lockfile) + + expect(out).to include("Bundle complete!") + end + + it "allows older revisions of git source when clean true" do + build_git "foo", "1.0", path: lib_path("foo") + rev = revision_for(lib_path("foo")) + + bundle "config set path vendor/bundle" + bundle "config set clean true" + install_gemfile <<-G, verbose: true + source "#{file_uri_for(gem_repo1)}" + gem "foo", :git => "#{file_uri_for(lib_path("foo"))}" + G + + expect(out).to include("Using foo 1.0 from #{file_uri_for(lib_path("foo"))} (at main@#{rev[0..6]})") + expect(the_bundle).to include_gems "foo 1.0", source: "git@#{lib_path("foo")}" + + old_lockfile = lockfile + + update_git "foo", "2.0", path: lib_path("foo"), gemspec: true + rev2 = revision_for(lib_path("foo")) + + bundle :update, all: true, verbose: true + expect(out).to include("Using foo 2.0 (was 1.0) from #{file_uri_for(lib_path("foo"))} (at main@#{rev2[0..6]})") + expect(out).to include("Removing foo (#{rev[0..11]})") + expect(the_bundle).to include_gems "foo 2.0", source: "git@#{lib_path("foo")}" + + lockfile(old_lockfile) + + bundle :install, verbose: true + expect(out).to include("Using foo 1.0 from #{file_uri_for(lib_path("foo"))} (at main@#{rev[0..6]})") + expect(the_bundle).to include_gems "foo 1.0", source: "git@#{lib_path("foo")}" + end end end diff --git a/spec/bundler/install/global_cache_spec.rb b/spec/bundler/install/global_cache_spec.rb index afa0ff76c1..0da4de05b2 100644 --- a/spec/bundler/install/global_cache_spec.rb +++ b/spec/bundler/install/global_cache_spec.rb @@ -16,7 +16,7 @@ RSpec.describe "global gem caching" do end it "caches gems into the global cache on download" do - install_gemfile <<-G, :artifice => "compact_index" + install_gemfile <<-G, artifice: "compact_index" source "#{source}" gem "rack" G @@ -29,7 +29,7 @@ RSpec.describe "global gem caching" do source_global_cache.mkpath FileUtils.cp(gem_repo1("gems/rack-1.0.0.gem"), source_global_cache("rack-1.0.0.gem")) - install_gemfile <<-G, :artifice => "compact_index_no_gem" + install_gemfile <<-G, artifice: "compact_index_no_gem" source "#{source}" gem "rack" G @@ -41,7 +41,7 @@ RSpec.describe "global gem caching" do source_global_cache.mkpath FileUtils.touch(source_global_cache("rack-1.0.0.gem")) - install_gemfile <<-G, :artifice => "compact_index_no_gem", :raise_on_error => false + install_gemfile <<-G, artifice: "compact_index_no_gem", raise_on_error: false source "#{source}" gem "rack" G @@ -51,7 +51,7 @@ RSpec.describe "global gem caching" do describe "when the same gem from different sources is installed" do it "should use the appropriate one from the global cache" do - install_gemfile <<-G, :artifice => "compact_index" + install_gemfile <<-G, artifice: "compact_index" source "#{source}" gem "rack" G @@ -61,7 +61,7 @@ RSpec.describe "global gem caching" do expect(source_global_cache("rack-1.0.0.gem")).to exist # rack 1.0.0 is not installed and it is in the global cache - install_gemfile <<-G, :artifice => "compact_index" + install_gemfile <<-G, artifice: "compact_index" source "#{source2}" gem "rack", "0.9.1" G @@ -76,7 +76,7 @@ RSpec.describe "global gem caching" do gem "rack", "1.0.0" G - bundle :install, :artifice => "compact_index_no_gem" + bundle :install, artifice: "compact_index_no_gem" # rack 1.0.0 is installed and rack 0.9.1 is not expect(the_bundle).to include_gems "rack 1.0.0" expect(the_bundle).not_to include_gems "rack 0.9.1" @@ -87,7 +87,7 @@ RSpec.describe "global gem caching" do gem "rack", "0.9.1" G - bundle :install, :artifice => "compact_index_no_gem" + bundle :install, artifice: "compact_index_no_gem" # rack 0.9.1 is installed and rack 1.0.0 is not expect(the_bundle).to include_gems "rack 0.9.1" expect(the_bundle).not_to include_gems "rack 1.0.0" @@ -99,7 +99,7 @@ RSpec.describe "global gem caching" do gem "rack" G - bundle :install, :artifice => "compact_index" + bundle :install, artifice: "compact_index" simulate_new_machine expect(the_bundle).not_to include_gems "rack 1.0.0" expect(source_global_cache("rack-1.0.0.gem")).to exist @@ -110,7 +110,7 @@ RSpec.describe "global gem caching" do gem "rack", "0.9.1" G - bundle :install, :artifice => "compact_index" + bundle :install, artifice: "compact_index" simulate_new_machine expect(the_bundle).not_to include_gems "rack 0.9.1" expect(source2_global_cache("rack-0.9.1.gem")).to exist @@ -123,7 +123,7 @@ RSpec.describe "global gem caching" do expect(source_global_cache("rack-1.0.0.gem")).to exist expect(source2_global_cache("rack-0.9.1.gem")).to exist - bundle :install, :artifice => "compact_index_no_gem", :raise_on_error => false + bundle :install, artifice: "compact_index_no_gem", raise_on_error: false expect(err).to include("Internal Server Error 500") expect(err).not_to include("ERROR REPORT TEMPLATE") @@ -138,7 +138,7 @@ RSpec.describe "global gem caching" do expect(source_global_cache("rack-1.0.0.gem")).to exist expect(source2_global_cache("rack-0.9.1.gem")).to exist - bundle :install, :artifice => "compact_index_no_gem", :raise_on_error => false + bundle :install, artifice: "compact_index_no_gem", raise_on_error: false expect(err).to include("Internal Server Error 500") expect(err).not_to include("ERROR REPORT TEMPLATE") @@ -150,7 +150,7 @@ RSpec.describe "global gem caching" do describe "when installing gems from a different directory" do it "uses the global cache as a source" do - install_gemfile <<-G, :artifice => "compact_index" + install_gemfile <<-G, artifice: "compact_index" source "#{source}" gem "rack" gem "activesupport" @@ -166,7 +166,7 @@ RSpec.describe "global gem caching" do expect(the_bundle).not_to include_gems "rack 1.0.0" expect(the_bundle).not_to include_gems "activesupport 2.3.5" - install_gemfile <<-G, :artifice => "compact_index_no_gem" + install_gemfile <<-G, artifice: "compact_index_no_gem" source "#{source}" gem "rack" G @@ -183,20 +183,18 @@ RSpec.describe "global gem caching" do G # Neither gem is installed and both are in the global cache - expect(the_bundle).not_to include_gems "rack 1.0.0", :dir => bundled_app2 - expect(the_bundle).not_to include_gems "activesupport 2.3.5", :dir => bundled_app2 + expect(the_bundle).not_to include_gems "rack 1.0.0", dir: bundled_app2 + expect(the_bundle).not_to include_gems "activesupport 2.3.5", dir: bundled_app2 expect(source_global_cache("rack-1.0.0.gem")).to exist expect(source_global_cache("activesupport-2.3.5.gem")).to exist # Install using the global cache instead of by downloading the .gem # from the server - bundle :install, :artifice => "compact_index_no_gem", :dir => bundled_app2 + bundle :install, artifice: "compact_index_no_gem", dir: bundled_app2 # activesupport is installed and both are in the global cache - simulate_bundler_version_when_missing_prerelease_default_gem_activation do - expect(the_bundle).not_to include_gems "rack 1.0.0", :dir => bundled_app2 - expect(the_bundle).to include_gems "activesupport 2.3.5", :dir => bundled_app2 - end + expect(the_bundle).not_to include_gems "rack 1.0.0", dir: bundled_app2 + expect(the_bundle).to include_gems "activesupport 2.3.5", dir: bundled_app2 expect(source_global_cache("rack-1.0.0.gem")).to exist expect(source_global_cache("activesupport-2.3.5.gem")).to exist @@ -205,7 +203,7 @@ RSpec.describe "global gem caching" do end describe "extension caching" do - it "works", :ruby_repo do + it "works" do skip "gets incorrect ref in path" if Gem.win_platform? build_git "very_simple_git_binary", &:add_c_extension @@ -220,9 +218,9 @@ RSpec.describe "global gem caching" do gem "very_simple_path_binary", :path => "#{lib_path("very_simple_path_binary-1.0")}" G - gem_binary_cache = home(".bundle", "cache", "extensions", specific_local_platform.to_s, Bundler.ruby_scope, + gem_binary_cache = home(".bundle", "cache", "extensions", local_platform.to_s, Bundler.ruby_scope, Digest(:MD5).hexdigest("#{gem_repo1}/"), "very_simple_binary-1.0") - git_binary_cache = home(".bundle", "cache", "extensions", specific_local_platform.to_s, Bundler.ruby_scope, + git_binary_cache = home(".bundle", "cache", "extensions", local_platform.to_s, Bundler.ruby_scope, "very_simple_git_binary-1.0-#{revision}", "very_simple_git_binary-1.0") cached_extensions = Pathname.glob(home(".bundle", "cache", "extensions", "*", "*", "*", "*", "*")).sort @@ -234,7 +232,7 @@ RSpec.describe "global gem caching" do R expect(out).to eq "VERY_SIMPLE_BINARY_IN_C\nVERY_SIMPLE_GIT_BINARY_IN_C" - FileUtils.rm Dir[home(".bundle", "cache", "extensions", "**", "*binary_c*")] + FileUtils.rm_rf Dir[home(".bundle", "cache", "extensions", "**", "*binary_c*")] gem_binary_cache.join("very_simple_binary_c.rb").open("w") {|f| f << "puts File.basename(__FILE__)" } git_binary_cache.join("very_simple_git_binary_c.rb").open("w") {|f| f << "puts File.basename(__FILE__)" } diff --git a/spec/bundler/install/path_spec.rb b/spec/bundler/install/path_spec.rb index b0392c4ed2..0a30e402b7 100644 --- a/spec/bundler/install/path_spec.rb +++ b/spec/bundler/install/path_spec.rb @@ -3,7 +3,7 @@ RSpec.describe "bundle install" do describe "with path configured" do before :each do - build_gem "rack", "1.0.0", :to_system => true do |s| + build_gem "rack", "1.0.0", to_system: true do |s| s.write "lib/rack.rb", "puts 'FAIL'" end @@ -23,7 +23,7 @@ RSpec.describe "bundle install" do bundle "config set --local path.system true" bundle "config set --global path vendor/bundle" bundle :install - run "require 'rack'", :raise_on_error => false + run "require 'rack'", raise_on_error: false expect(out).to include("FAIL") end @@ -32,7 +32,7 @@ RSpec.describe "bundle install" do dir.mkpath bundle "config set --local path #{dir.join("vendor/bundle")}" - bundle :install, :dir => dir + bundle :install, dir: dir expect(out).to include("installed into `./vendor/bundle`") dir.rmtree @@ -44,13 +44,13 @@ RSpec.describe "bundle install" do expect(out).to include("gems are installed into `./vendor/bundle`") end - it "disallows --path vendor/bundle --system", :bundler => "< 3" do - bundle "install --path vendor/bundle --system", :raise_on_error => false + it "disallows --path vendor/bundle --system", bundler: "< 3" do + bundle "install --path vendor/bundle --system", raise_on_error: false expect(err).to include("Please choose only one option.") expect(exitstatus).to eq(15) end - it "remembers to disable system gems after the first time with bundle --path vendor/bundle", :bundler => "< 3" do + it "remembers to disable system gems after the first time with bundle --path vendor/bundle", bundler: "< 3" do bundle "install --path vendor/bundle" FileUtils.rm_rf bundled_app("vendor") bundle "install" @@ -62,22 +62,22 @@ RSpec.describe "bundle install" do context "with path_relative_to_cwd set to true" do before { bundle "config set path_relative_to_cwd true" } - it "installs the bundle relatively to current working directory", :bundler => "< 3" do - bundle "install --gemfile='#{bundled_app}/Gemfile' --path vendor/bundle", :dir => bundled_app.parent + it "installs the bundle relatively to current working directory", bundler: "< 3" do + bundle "install --gemfile='#{bundled_app}/Gemfile' --path vendor/bundle", dir: bundled_app.parent expect(out).to include("installed into `./vendor/bundle`") expect(bundled_app("../vendor/bundle")).to be_directory expect(the_bundle).to include_gems "rack 1.0.0" end it "installs the standalone bundle relative to the cwd" do - bundle :install, :gemfile => bundled_app_gemfile, :standalone => true, :dir => bundled_app.parent + bundle :install, gemfile: bundled_app_gemfile, standalone: true, dir: bundled_app.parent expect(out).to include("installed into `./bundled_app/bundle`") expect(bundled_app("bundle")).to be_directory expect(bundled_app("bundle/ruby")).to be_directory bundle "config unset path" - bundle :install, :gemfile => bundled_app_gemfile, :standalone => true, :dir => bundled_app("subdir").tap(&:mkpath) + bundle :install, gemfile: bundled_app_gemfile, standalone: true, dir: bundled_app("subdir").tap(&:mkpath) expect(out).to include("installed into `../bundle`") expect(bundled_app("bundle")).to be_directory expect(bundled_app("bundle/ruby")).to be_directory @@ -87,7 +87,7 @@ RSpec.describe "bundle install" do describe "when BUNDLE_PATH or the global path config is set" do before :each do - build_lib "rack", "1.0.0", :to_system => true do |s| + build_lib "rack", "1.0.0", to_system: true do |s| s.write "lib/rack.rb", "raise 'FAIL'" end @@ -141,7 +141,7 @@ RSpec.describe "bundle install" do set_bundle_path(type, "vendor") FileUtils.mkdir_p bundled_app("lol") - bundle :install, :dir => bundled_app("lol") + bundle :install, dir: bundled_app("lol") expect(bundled_app("vendor", Bundler.ruby_scope, "gems/rack-1.0.0")).to be_directory expect(the_bundle).to include_gems "rack 1.0.0" @@ -168,7 +168,7 @@ RSpec.describe "bundle install" do it "disables system gems when passing a path to install" do # This is so that vendored gems can be distributed to others - build_gem "rack", "1.1.0", :to_system => true + build_gem "rack", "1.1.0", to_system: true bundle "config set --local path ./vendor/bundle" bundle :install @@ -176,8 +176,8 @@ RSpec.describe "bundle install" do expect(the_bundle).to include_gems "rack 1.0.0" end - it "re-installs gems whose extensions have been deleted", :ruby_repo do - build_lib "very_simple_binary", "1.0.0", :to_system => true do |s| + it "re-installs gems whose extensions have been deleted" do + build_lib "very_simple_binary", "1.0.0", to_system: true do |s| s.write "lib/very_simple_binary.rb", "raise 'FAIL'" end @@ -191,11 +191,11 @@ RSpec.describe "bundle install" do expect(vendored_gems("gems/very_simple_binary-1.0")).to be_directory expect(vendored_gems("extensions")).to be_directory - expect(the_bundle).to include_gems "very_simple_binary 1.0", :source => "remote1" + expect(the_bundle).to include_gems "very_simple_binary 1.0", source: "remote1" vendored_gems("extensions").rmtree - run "require 'very_simple_binary_c'", :raise_on_error => false + run "require 'very_simple_binary_c'", raise_on_error: false expect(err).to include("Bundler::GemNotFound") bundle "config set --local path ./vendor/bundle" @@ -203,7 +203,7 @@ RSpec.describe "bundle install" do expect(vendored_gems("gems/very_simple_binary-1.0")).to be_directory expect(vendored_gems("extensions")).to be_directory - expect(the_bundle).to include_gems "very_simple_binary 1.0", :source => "remote1" + expect(the_bundle).to include_gems "very_simple_binary 1.0", source: "remote1" end end @@ -219,7 +219,7 @@ RSpec.describe "bundle install" do G bundle "config set --local path bundle" - bundle :install, :raise_on_error => false + bundle :install, raise_on_error: false expect(err).to include("file already exists") end end diff --git a/spec/bundler/install/process_lock_spec.rb b/spec/bundler/install/process_lock_spec.rb index dac0d34bc4..1f8c62f26e 100644 --- a/spec/bundler/install/process_lock_spec.rb +++ b/spec/bundler/install/process_lock_spec.rb @@ -31,5 +31,27 @@ RSpec.describe "process lock spec" do expect(processed).to eq true end end + + context "when creating a lock raises Errno::EPERM" do + before { allow(File).to receive(:open).and_raise(Errno::EPERM) } + + it "skips creating the lock file and yields" do + processed = false + Bundler::ProcessLock.lock(default_bundle_path) { processed = true } + + expect(processed).to eq true + end + end + + context "when creating a lock raises Errno::EROFS" do + before { allow(File).to receive(:open).and_raise(Errno::EROFS) } + + it "skips creating the lock file and yields" do + processed = false + Bundler::ProcessLock.lock(default_bundle_path) { processed = true } + + expect(processed).to eq true + end + end end end diff --git a/spec/bundler/install/redownload_spec.rb b/spec/bundler/install/redownload_spec.rb index a936b2b536..3a72c356d9 100644 --- a/spec/bundler/install/redownload_spec.rb +++ b/spec/bundler/install/redownload_spec.rb @@ -57,7 +57,7 @@ RSpec.describe "bundle install" do end end - describe "with --force", :bundler => 2 do + describe "with --force", bundler: 2 do it_behaves_like "an option to force redownloading gems" do let(:flag) { "force" } end diff --git a/spec/bundler/install/security_policy_spec.rb b/spec/bundler/install/security_policy_spec.rb index 43c3069c4e..befeb81da5 100644 --- a/spec/bundler/install/security_policy_spec.rb +++ b/spec/bundler/install/security_policy_spec.rb @@ -16,23 +16,23 @@ RSpec.describe "policies with unsigned gems" do end it "will work after you try to deploy without a lock" do - bundle "install --deployment", :raise_on_error => false + bundle "install --deployment", raise_on_error: false bundle :install expect(the_bundle).to include_gems "rack 1.0", "signed_gem 1.0" end it "will fail when given invalid security policy" do - bundle "install --trust-policy=InvalidPolicyName", :raise_on_error => false + bundle "install --trust-policy=InvalidPolicyName", raise_on_error: false expect(err).to include("RubyGems doesn't know about trust policy") end it "will fail with High Security setting due to presence of unsigned gem" do - bundle "install --trust-policy=HighSecurity", :raise_on_error => false + bundle "install --trust-policy=HighSecurity", raise_on_error: false expect(err).to include("security policy didn't allow") end it "will fail with Medium Security setting due to presence of unsigned gem" do - bundle "install --trust-policy=MediumSecurity", :raise_on_error => false + bundle "install --trust-policy=MediumSecurity", raise_on_error: false expect(err).to include("security policy didn't allow") end @@ -51,12 +51,12 @@ RSpec.describe "policies with signed gems and no CA" do end it "will fail with High Security setting, gem is self-signed" do - bundle "install --trust-policy=HighSecurity", :raise_on_error => false + bundle "install --trust-policy=HighSecurity", raise_on_error: false expect(err).to include("security policy didn't allow") end it "will fail with Medium Security setting, gem is self-signed" do - bundle "install --trust-policy=MediumSecurity", :raise_on_error => false + bundle "install --trust-policy=MediumSecurity", raise_on_error: false expect(err).to include("security policy didn't allow") end diff --git a/spec/bundler/install/yanked_spec.rb b/spec/bundler/install/yanked_spec.rb index b53c15be69..7408c24327 100644 --- a/spec/bundler/install/yanked_spec.rb +++ b/spec/bundler/install/yanked_spec.rb @@ -15,14 +15,14 @@ RSpec.context "when installing a bundle that includes yanked gems" do foo (10.0.0) PLATFORMS - ruby + #{lockfile_platforms} DEPENDENCIES foo (= 10.0.0) L - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G, raise_on_error: false source "#{file_uri_for(gem_repo4)}" gem "foo", "10.0.0" G @@ -30,10 +30,76 @@ RSpec.context "when installing a bundle that includes yanked gems" do expect(err).to include("Your bundle is locked to foo (10.0.0)") end + context "when a re-resolve is necessary, and a yanked version is considered by the resolver" do + before do + skip "Materialization on Windows is not yet strict, so the example does not detect the gem has been yanked" if Gem.win_platform? + + build_repo4 do + build_gem "foo", "1.0.0", "1.0.1" + build_gem "actiontext", "6.1.7" do |s| + s.add_dependency "nokogiri", ">= 1.8" + end + build_gem "actiontext", "6.1.6" do |s| + s.add_dependency "nokogiri", ">= 1.8" + end + build_gem "actiontext", "6.1.7" do |s| + s.add_dependency "nokogiri", ">= 1.8" + end + build_gem "nokogiri", "1.13.8" + end + + gemfile <<~G + source "#{source_uri}" + gem "foo", "1.0.1" + gem "actiontext", "6.1.6" + G + + lockfile <<~L + GEM + remote: #{source_uri}/ + specs: + actiontext (6.1.6) + nokogiri (>= 1.8) + foo (1.0.0) + nokogiri (1.13.8-#{Bundler.local_platform}) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + actiontext (= 6.1.6) + foo (= 1.0.0) + + BUNDLED WITH + #{Bundler::VERSION} + L + end + + context "and the old index is used" do + let(:source_uri) { file_uri_for(gem_repo4) } + + it "reports the yanked gem properly" do + bundle "install", raise_on_error: false + + expect(err).to include("Your bundle is locked to nokogiri (1.13.8-#{Bundler.local_platform})") + end + end + + context "and the compact index API is used" do + let(:source_uri) { "https://gem.repo4" } + + it "reports the yanked gem properly" do + bundle "install", artifice: "compact_index", raise_on_error: false + + expect(err).to include("Your bundle is locked to nokogiri (1.13.8-#{Bundler.local_platform})") + end + end + end + it "throws the original error when only the Gemfile specifies a gem version that doesn't exist" do bundle "config set force_ruby_platform true" - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G, raise_on_error: false source "#{file_uri_for(gem_repo4)}" gem "foo", "10.0.0" G @@ -43,6 +109,63 @@ RSpec.context "when installing a bundle that includes yanked gems" do end end +RSpec.context "when resolving a bundle that includes yanked gems, but unlocking an unrelated gem" do + before(:each) do + build_repo4 do + build_gem "foo", "10.0.0" + + build_gem "bar", "1.0.0" + build_gem "bar", "2.0.0" + end + + lockfile <<-L + GEM + remote: #{file_uri_for(gem_repo4)} + specs: + foo (9.0.0) + bar (1.0.0) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + foo + bar + + BUNDLED WITH + #{Bundler::VERSION} + L + + gemfile <<-G + source "#{file_uri_for(gem_repo4)}" + gem "foo" + gem "bar" + G + end + + it "does not update the yanked gem" do + bundle "lock --update bar" + + expect(lockfile).to eq <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + bar (2.0.0) + foo (9.0.0) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + bar + foo + + BUNDLED WITH + #{Bundler::VERSION} + L + end +end + RSpec.context "when using gem before installing" do it "does not suggest the author has yanked the gem" do gemfile <<-G @@ -57,18 +180,24 @@ RSpec.context "when using gem before installing" do rack (0.9.1) PLATFORMS - ruby + #{lockfile_platforms} DEPENDENCIES rack (= 0.9.1) L - bundle :list, :raise_on_error => false + bundle :list, raise_on_error: false - expect(err).to include("Could not find rack-0.9.1 in any of the sources") + expect(err).to include("Could not find rack-0.9.1 in cached gems or installed locally") expect(err).to_not include("Your bundle is locked to rack (0.9.1) from") expect(err).to_not include("If you haven't changed sources, that means the author of rack (0.9.1) has removed it.") expect(err).to_not include("You'll need to update your bundle to a different version of rack (0.9.1) that hasn't been removed in order to install.") + + # Check error message is still correct when multiple platforms are locked + lockfile lockfile.gsub(/PLATFORMS\n #{lockfile_platforms}/m, "PLATFORMS\n #{lockfile_platforms("ruby")}") + + bundle :list, raise_on_error: false + expect(err).to include("Could not find rack-0.9.1 in cached gems or installed locally") end it "does not suggest the author has yanked the gem when using more than one gem, but shows all gems that couldn't be found in the source" do @@ -86,16 +215,16 @@ RSpec.context "when using gem before installing" do rack_middleware (1.0) PLATFORMS - ruby + #{lockfile_platforms} DEPENDENCIES rack (= 0.9.1) rack_middleware (1.0) L - bundle :list, :raise_on_error => false + bundle :list, raise_on_error: false - expect(err).to include("Could not find rack-0.9.1, rack_middleware-1.0 in any of the sources") + expect(err).to include("Could not find rack-0.9.1, rack_middleware-1.0 in cached gems or installed locally") expect(err).to include("Install missing gems with `bundle install`.") expect(err).to_not include("Your bundle is locked to rack (0.9.1) from") expect(err).to_not include("If you haven't changed sources, that means the author of rack (0.9.1) has removed it.") |