diff options
Diffstat (limited to 'spec/bundler/runtime/setup_spec.rb')
| -rw-r--r-- | spec/bundler/runtime/setup_spec.rb | 1279 |
1 files changed, 850 insertions, 429 deletions
diff --git a/spec/bundler/runtime/setup_spec.rb b/spec/bundler/runtime/setup_spec.rb index dc7af07188..ceb6fcf66a 100644 --- a/spec/bundler/runtime/setup_spec.rb +++ b/spec/bundler/runtime/setup_spec.rb @@ -1,23 +1,23 @@ # frozen_string_literal: true -require "spec_helper" + +require "tmpdir" RSpec.describe "Bundler.setup" do describe "with no arguments" do it "makes all groups available" do install_gemfile <<-G - source "file://#{gem_repo1}" - gem "rack", :group => :test + source "https://gem.repo1" + gem "myrack", :group => :test G ruby <<-RUBY - require 'rubygems' require 'bundler' Bundler.setup - require 'rack' - puts RACK + require 'myrack' + puts MYRACK RUBY - expect(err).to lack_errors + expect(err).to be_empty expect(out).to eq("1.0.0") end end @@ -25,58 +25,54 @@ RSpec.describe "Bundler.setup" do describe "when called with groups" do before(:each) do install_gemfile <<-G - source "file://#{gem_repo1}" + source "https://gem.repo1" gem "yard" - gem "rack", :group => :test + gem "myrack", :group => :test G end it "doesn't make all groups available" do ruby <<-RUBY - require 'rubygems' require 'bundler' Bundler.setup(:default) begin - require 'rack' + require 'myrack' rescue LoadError puts "WIN" end RUBY - expect(err).to lack_errors + expect(err).to be_empty expect(out).to eq("WIN") end it "accepts string for group name" do ruby <<-RUBY - require 'rubygems' require 'bundler' Bundler.setup(:default, 'test') - require 'rack' - puts RACK + require 'myrack' + puts MYRACK RUBY - expect(err).to lack_errors + expect(err).to be_empty expect(out).to eq("1.0.0") end it "leaves all groups available if they were already" do ruby <<-RUBY - require 'rubygems' require 'bundler' Bundler.setup Bundler.setup(:default) - require 'rack' - puts RACK + require 'myrack' + puts MYRACK RUBY - expect(err).to lack_errors + expect(err).to be_empty expect(out).to eq("1.0.0") end it "leaves :default available if setup is called twice" do ruby <<-RUBY - require 'rubygems' require 'bundler' Bundler.setup(:default) Bundler.setup(:default, :test) @@ -88,21 +84,21 @@ RSpec.describe "Bundler.setup" do puts "FAIL" end RUBY - expect(err).to lack_errors + expect(err).to be_empty expect(out).to match("WIN") end it "handles multiple non-additive invocations" do - ruby <<-RUBY + ruby <<-RUBY, raise_on_error: false require 'bundler' Bundler.setup(:default, :test) Bundler.setup(:default) - require 'rack' + require 'myrack' puts "FAIL" RUBY - expect(err).to match("rack") + expect(err).to match("myrack") expect(err).to match("LoadError") expect(out).not_to match("FAIL") end @@ -110,50 +106,45 @@ RSpec.describe "Bundler.setup" do context "load order" do def clean_load_path(lp) - without_bundler_load_path = ruby!("puts $LOAD_PATH").split("\n") - lp = lp - [ - bundler_path.to_s, - bundler_path.join("gems/bundler-#{Bundler::VERSION}/lib").to_s, - tmp("rubygems/lib").to_s, - root.join("../lib").expand_path.to_s, - ] - without_bundler_load_path - lp.map! {|p| p.sub(/^#{system_gem_path}/, "") } + without_bundler_load_path = ruby("puts $LOAD_PATH").split("\n") + lp -= [*without_bundler_load_path, lib_dir.to_s] + lp.map! {|p| p.sub(system_gem_path.to_s, "") } end it "puts loaded gems after -I and RUBYLIB", :ruby_repo do install_gemfile <<-G - source "file://#{gem_repo1}" - gem "rack" + source "https://gem.repo1" + gem "myrack" G - ENV["RUBYOPT"] = "-Idash_i_dir" + ENV["RUBYOPT"] = "#{ENV["RUBYOPT"]} -Idash_i_dir" ENV["RUBYLIB"] = "rubylib_dir" ruby <<-RUBY - require 'rubygems' require 'bundler' Bundler.setup puts $LOAD_PATH RUBY load_path = out.split("\n") - rack_load_order = load_path.index {|path| path.include?("rack") } + myrack_load_order = load_path.index {|path| path.include?("myrack") } - expect(err).to eq("") - expect(load_path[1]).to include "dash_i_dir" - expect(load_path[2]).to include "rubylib_dir" - expect(rack_load_order).to be > 0 + expect(err).to be_empty + expect(load_path).to include(a_string_ending_with("dash_i_dir"), "rubylib_dir") + expect(myrack_load_order).to be > 0 end - it "orders the load path correctly when there are dependencies", :ruby_repo do + it "orders the load path correctly when there are dependencies" do + bundle_config "path.system true" + install_gemfile <<-G - source "file://#{gem_repo1}" + source "https://gem.repo1" gem "rails" G - ruby! <<-RUBY - require 'rubygems' + ruby <<-RUBY require 'bundler' + gem "bundler", "#{Bundler::VERSION}" if #{ruby_core?} Bundler.setup puts $LOAD_PATH RUBY @@ -167,20 +158,21 @@ RSpec.describe "Bundler.setup" do "/gems/actionpack-2.3.2/lib", "/gems/actionmailer-2.3.2/lib", "/gems/activesupport-2.3.2/lib", - "/gems/rake-10.0.2/lib" + "/gems/rake-#{rake_version}/lib" ) end it "falls back to order the load path alphabetically for backwards compatibility" do - install_gemfile! <<-G - source "file://#{gem_repo1}" + bundle_config "path.system true" + + install_gemfile <<-G + source "https://gem.repo1" gem "weakling" gem "duradura" gem "terranova" G - ruby! <<-RUBY - require 'rubygems' + ruby <<-RUBY require 'bundler/setup' puts $LOAD_PATH RUBY @@ -197,12 +189,11 @@ RSpec.describe "Bundler.setup" do it "raises if the Gemfile was not yet installed" do gemfile <<-G - source "file://#{gem_repo1}" - gem "rack" + source "https://gem.repo1" + gem "myrack" G ruby <<-R - require 'rubygems' require 'bundler' begin @@ -218,95 +209,148 @@ RSpec.describe "Bundler.setup" do it "doesn't create a Gemfile.lock if the setup fails" do gemfile <<-G - source "file://#{gem_repo1}" - gem "rack" + source "https://gem.repo1" + gem "myrack" G - ruby <<-R - require 'rubygems' + ruby <<-R, raise_on_error: false require 'bundler' Bundler.setup R - expect(bundled_app("Gemfile.lock")).not_to exist + expect(bundled_app_lock).not_to exist end it "doesn't change the Gemfile.lock if the setup fails" do install_gemfile <<-G - source "file://#{gem_repo1}" - gem "rack" + source "https://gem.repo1" + gem "myrack" G - lockfile = File.read(bundled_app("Gemfile.lock")) + lockfile = File.read(bundled_app_lock) gemfile <<-G - source "file://#{gem_repo1}" - gem "rack" + source "https://gem.repo1" + gem "myrack" gem "nosuchgem", "10.0" G - ruby <<-R - require 'rubygems' + ruby <<-R, raise_on_error: false require 'bundler' Bundler.setup R - expect(File.read(bundled_app("Gemfile.lock"))).to eq(lockfile) + expect(File.read(bundled_app_lock)).to eq(lockfile) end it "makes a Gemfile.lock if setup succeeds" do install_gemfile <<-G - source "file://#{gem_repo1}" - gem "rack" + source "https://gem.repo1" + gem "myrack" G - File.read(bundled_app("Gemfile.lock")) + File.read(bundled_app_lock) - FileUtils.rm(bundled_app("Gemfile.lock")) + FileUtils.rm(bundled_app_lock) run "1" - expect(bundled_app("Gemfile.lock")).to exist + expect(bundled_app_lock).to exist end - it "uses BUNDLE_GEMFILE to locate the gemfile if present" do - gemfile <<-G - source "file://#{gem_repo1}" - gem "rack" - G + describe "$BUNDLE_GEMFILE" do + context "user provides an absolute path" do + it "uses BUNDLE_GEMFILE to locate the gemfile if present" do + gemfile <<-G + source "https://gem.repo1" + gem "myrack" + G - gemfile bundled_app("4realz"), <<-G - source "file://#{gem_repo1}" - gem "activesupport", "2.3.5" - G + gemfile bundled_app("4realz"), <<-G + source "https://gem.repo1" + gem "activesupport", "2.3.5" + G + + ENV["BUNDLE_GEMFILE"] = bundled_app("4realz").to_s + bundle :install + + expect(the_bundle).to include_gems "activesupport 2.3.5" + end + end + + context "an absolute path is not provided" do + it "uses BUNDLE_GEMFILE to locate the gemfile if present and doesn't fail in deployment mode" do + gemfile <<-G + source "https://gem.repo1" + G + + bundle "install" + bundle_config "deployment true" - ENV["BUNDLE_GEMFILE"] = bundled_app("4realz").to_s - bundle :install + ENV["BUNDLE_GEMFILE"] = "Gemfile" + ruby <<-R + require 'bundler' + + begin + Bundler.setup + puts "WIN" + rescue ArgumentError => e + puts "FAIL" + end + R + + expect(out).to eq("WIN") + end + end + + context "user sets it via `config set --local gemfile`" do + it "uses the value in the config" do + gemfile <<-G + source "https://gem.repo1" + gem "myrack" + G + + gemfile bundled_app("CustomGemfile"), <<-G + source "https://gem.repo1" + gem "activesupport", "2.3.5" + G + + bundle_config "gemfile #{bundled_app("CustomGemfile")}" + bundle "install" + + ruby <<-R + require 'bundler' + Bundler.setup + require 'activesupport' + puts ACTIVESUPPORT + R - expect(the_bundle).to include_gems "activesupport 2.3.5" + expect(out).to eq("2.3.5") + end + end end it "prioritizes gems in BUNDLE_PATH over gems in GEM_HOME" do ENV["BUNDLE_PATH"] = bundled_app(".bundle").to_s install_gemfile <<-G - source "file://#{gem_repo1}" - gem "rack", "1.0.0" + source "https://gem.repo1" + gem "myrack", "1.0.0" G - build_gem "rack", "1.0", :to_system => true do |s| - s.write "lib/rack.rb", "RACK = 'FAIL'" + build_gem "myrack", "1.0", to_system: true do |s| + s.write "lib/myrack.rb", "MYRACK = 'FAIL'" end - expect(the_bundle).to include_gems "rack 1.0.0" + expect(the_bundle).to include_gems "myrack 1.0.0" end describe "integrate with rubygems" do describe "by replacing #gem" do before :each do install_gemfile <<-G - source "file://#{gem_repo1}" - gem "rack", "0.9.1" + source "https://gem.repo1" + gem "myrack", "0.9.1" G end @@ -323,23 +367,10 @@ RSpec.describe "Bundler.setup" do expect(out).to eq("WIN") end - it "version_requirement is now deprecated in rubygems 1.4.0+ when gem is missing" do - run <<-R - begin - gem "activesupport" - puts "FAIL" - rescue LoadError - puts "WIN" - end - R - - expect(err).to lack_errors - end - it "replaces #gem but raises when the version is wrong" do run <<-R begin - gem "rack", "1.0.0" + gem "myrack", "1.0.0" puts "FAIL" rescue LoadError puts "WIN" @@ -348,39 +379,27 @@ RSpec.describe "Bundler.setup" do expect(out).to eq("WIN") end - - it "version_requirement is now deprecated in rubygems 1.4.0+ when the version is wrong" do - run <<-R - begin - gem "rack", "1.0.0" - puts "FAIL" - rescue LoadError - puts "WIN" - end - R - - expect(err).to lack_errors - end end describe "by hiding system gems" do before :each do system_gems "activesupport-2.3.5" install_gemfile <<-G - source "file://#{gem_repo1}" + source "https://gem.repo1" gem "yard" G end it "removes system gems from Gem.source_index" do run "require 'yard'" - expect(out).to eq("bundler-#{Bundler::VERSION}\nyard-1.0") + expect(out).to include("bundler-#{Bundler::VERSION}").and include("yard-1.0") + expect(out).not_to include("activesupport-2.3.5") end context "when the ruby stdlib is a substring of Gem.path" do it "does not reject the stdlib from $LOAD_PATH" do - substring = "/" + $LOAD_PATH.find {|p| p =~ /vendor_ruby/ }.split("/")[2] - run "puts 'worked!'", :env => { "GEM_PATH" => substring } + substring = "/" + $LOAD_PATH.find {|p| p.include?("vendor_ruby") }.split("/")[2] + run "puts 'worked!'", env: { "GEM_PATH" => substring } expect(out).to eq("worked!") end end @@ -389,35 +408,37 @@ RSpec.describe "Bundler.setup" do describe "with paths" do it "activates the gems in the path source" do - system_gems "rack-1.0.0" + system_gems "myrack-1.0.0" - build_lib "rack", "1.0.0" do |s| - s.write "lib/rack.rb", "puts 'WIN'" + build_lib "myrack", "1.0.0" do |s| + s.write "lib/myrack.rb", "puts 'WIN'" end gemfile <<-G - path "#{lib_path("rack-1.0.0")}" - source "file://#{gem_repo1}" - gem "rack" + source "https://gem.repo1" + path "#{lib_path("myrack-1.0.0")}" do + gem "myrack" + end G - run "require 'rack'" + run "require 'myrack'" expect(out).to eq("WIN") end end describe "with git" do before do - build_git "rack", "1.0.0" + build_git "myrack", "1.0.0" gemfile <<-G - gem "rack", :git => "#{lib_path("rack-1.0.0")}" + source "https://gem.repo1" + gem "myrack", :git => "#{lib_path("myrack-1.0.0")}" G end it "provides a useful exception when the git repo is not checked out yet" do - run "1" - expect(err).to match(/the git source #{lib_path('rack-1.0.0')} is not yet checked out. Please run `bundle install`/i) + run "1", raise_on_error: false + expect(err).to match(/the git source #{lib_path("myrack-1.0.0")} is not yet checked out. Please run `bundle install`/i) end it "does not hit the git binary if the lockfile is available and up to date" do @@ -426,7 +447,6 @@ RSpec.describe "Bundler.setup" do break_git! ruby <<-R - require 'rubygems' require 'bundler' begin @@ -443,12 +463,11 @@ RSpec.describe "Bundler.setup" do it "provides a good exception if the lockfile is unavailable" do bundle "install" - FileUtils.rm(bundled_app("Gemfile.lock")) + FileUtils.rm(bundled_app_lock) break_git! ruby <<-R - require "rubygems" require "bundler" begin @@ -459,125 +478,125 @@ RSpec.describe "Bundler.setup" do end R - run "puts 'FAIL'" + run "puts 'FAIL'", raise_on_error: false expect(err).not_to include "This is not the git you are looking for" end it "works even when the cache directory has been deleted" do - bundle "install --path vendor/bundle" - FileUtils.rm_rf vendored_gems("cache") - expect(the_bundle).to include_gems "rack 1.0.0" + bundle :install + FileUtils.rm_r default_cache_path + expect(the_bundle).to include_gems "myrack 1.0.0" end it "does not randomly change the path when specifying --path and the bundle directory becomes read only" do - bundle "install --path vendor/bundle" + bundle_config "path vendor/bundle" + bundle :install - with_read_only("**/*") do - expect(the_bundle).to include_gems "rack 1.0.0" + with_read_only("#{bundled_app}/**/*") do + expect(the_bundle).to include_gems "myrack 1.0.0" end end it "finds git gem when default bundle path becomes read only" do + bundle_config "path .bundle" bundle "install" - with_read_only("#{Bundler.bundle_path}/**/*") do - expect(the_bundle).to include_gems "rack 1.0.0" + with_read_only("#{bundled_app(".bundle")}/**/*") do + expect(the_bundle).to include_gems "myrack 1.0.0" end end end describe "when specifying local override" do it "explodes if given path does not exist on runtime" do - build_git "rack", "0.8" + build_git "myrack", "0.8" - FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack")) + FileUtils.cp_r("#{lib_path("myrack-0.8")}/.", lib_path("local-myrack")) gemfile <<-G - source "file://#{gem_repo1}" - gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master" + source "https://gem.repo1" + gem "myrack", :git => "#{lib_path("myrack-0.8")}", :branch => "main" G - bundle %(config local.rack #{lib_path("local-rack")}) + bundle %(config set local.myrack #{lib_path("local-myrack")}) bundle :install - expect(out).to match(/at #{lib_path('local-rack')}/) - FileUtils.rm_rf(lib_path("local-rack")) - run "require 'rack'" - expect(err).to match(/Cannot use local override for rack-0.8 because #{Regexp.escape(lib_path('local-rack').to_s)} does not exist/) + FileUtils.rm_r(lib_path("local-myrack")) + run "require 'myrack'", raise_on_error: false + expect(err).to match(/Cannot use local override for myrack-0.8 because #{Regexp.escape(lib_path("local-myrack").to_s)} does not exist/) end it "explodes if branch is not given on runtime" do - build_git "rack", "0.8" + build_git "myrack", "0.8" - FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack")) + FileUtils.cp_r("#{lib_path("myrack-0.8")}/.", lib_path("local-myrack")) gemfile <<-G - source "file://#{gem_repo1}" - gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master" + source "https://gem.repo1" + gem "myrack", :git => "#{lib_path("myrack-0.8")}", :branch => "main" G - bundle %(config local.rack #{lib_path("local-rack")}) + bundle %(config set local.myrack #{lib_path("local-myrack")}) bundle :install - expect(out).to match(/at #{lib_path('local-rack')}/) gemfile <<-G - source "file://#{gem_repo1}" - gem "rack", :git => "#{lib_path("rack-0.8")}" + source "https://gem.repo1" + gem "myrack", :git => "#{lib_path("myrack-0.8")}" G - run "require 'rack'" + run "require 'myrack'", raise_on_error: false expect(err).to match(/because :branch is not specified in Gemfile/) end it "explodes on different branches on runtime" do - build_git "rack", "0.8" + build_git "myrack", "0.8" - FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack")) + FileUtils.cp_r("#{lib_path("myrack-0.8")}/.", lib_path("local-myrack")) gemfile <<-G - source "file://#{gem_repo1}" - gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master" + source "https://gem.repo1" + gem "myrack", :git => "#{lib_path("myrack-0.8")}", :branch => "main" G - bundle %(config local.rack #{lib_path("local-rack")}) + bundle %(config set local.myrack #{lib_path("local-myrack")}) bundle :install - expect(out).to match(/at #{lib_path('local-rack')}/) gemfile <<-G - source "file://#{gem_repo1}" - gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "changed" + source "https://gem.repo1" + gem "myrack", :git => "#{lib_path("myrack-0.8")}", :branch => "changed" G - run "require 'rack'" - expect(err).to match(/is using branch master but Gemfile specifies changed/) + run "require 'myrack'", raise_on_error: false + expect(err).to match(/is using branch main but Gemfile specifies changed/) end it "explodes on refs with different branches on runtime" do - build_git "rack", "0.8" + build_git "myrack", "0.8" - FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack")) + FileUtils.cp_r("#{lib_path("myrack-0.8")}/.", lib_path("local-myrack")) install_gemfile <<-G - source "file://#{gem_repo1}" - gem "rack", :git => "#{lib_path("rack-0.8")}", :ref => "master", :branch => "master" + source "https://gem.repo1" + gem "myrack", :git => "#{lib_path("myrack-0.8")}", :ref => "main", :branch => "main" G gemfile <<-G - source "file://#{gem_repo1}" - gem "rack", :git => "#{lib_path("rack-0.8")}", :ref => "master", :branch => "nonexistant" + source "https://gem.repo1" + gem "myrack", :git => "#{lib_path("myrack-0.8")}", :ref => "main", :branch => "nonexistent" G - bundle %(config local.rack #{lib_path("local-rack")}) - run "require 'rack'" - expect(err).to match(/is using branch master but Gemfile specifies nonexistant/) + bundle %(config set local.myrack #{lib_path("local-myrack")}) + run "require 'myrack'", raise_on_error: false + expect(err).to match(/is using branch main but Gemfile specifies nonexistent/) end end describe "when excluding groups" do it "doesn't change the resolve if --without is used" do - install_gemfile <<-G, :without => :rails - source "file://#{gem_repo1}" + bundle_config "without rails" + install_gemfile <<-G + source "https://gem.repo1" gem "activesupport" group :rails do @@ -585,14 +604,15 @@ RSpec.describe "Bundler.setup" do end G - install_gems "activesupport-2.3.5" + system_gems "activesupport-2.3.5" - 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 "remembers --without and does not bail on bare Bundler.setup" do - install_gemfile <<-G, :without => :rails - source "file://#{gem_repo1}" + bundle_config "without rails" + install_gemfile <<-G + source "https://gem.repo1" gem "activesupport" group :rails do @@ -600,94 +620,105 @@ RSpec.describe "Bundler.setup" do end G - install_gems "activesupport-2.3.5" + system_gems "activesupport-2.3.5" expect(the_bundle).to include_gems "activesupport 2.3.2" end - it "remembers --without and does not include groups passed to Bundler.setup" do - install_gemfile <<-G, :without => :rails - source "file://#{gem_repo1}" - gem "activesupport" + it "remembers --without and does not bail on bare Bundler.setup, even in the case of path gems no longer available" do + bundle_config "without development" - group :rack do - gem "rack" - end + path = bundled_app(File.join("vendor", "foo")) + build_lib "foo", path: path - group :rails do - gem "rails", "2.3.2" - end + install_gemfile <<-G + source "https://gem.repo1" + gem "activesupport", "2.3.2" + gem 'foo', :path => 'vendor/foo', :group => :development G - expect(the_bundle).not_to include_gems "activesupport 2.3.2", :groups => :rack - expect(the_bundle).to include_gems "rack 1.0.0", :groups => :rack + FileUtils.rm_r(path) + + ruby "require 'bundler'; Bundler.setup", env: { "DEBUG" => "1" } + expect(out).to include("Assuming that source at `vendor/foo` has not changed since fetching its specs errored") + expect(out).to include("Found no changes, using resolution from the lockfile") + expect(err).to be_empty end - end - # Unfortunately, gem_prelude does not record the information about - # activated gems, so this test cannot work on 1.9 :( - if RUBY_VERSION < "1.9" - describe "preactivated gems" do - it "raises an exception if a pre activated gem conflicts with the bundle" do - system_gems "thin-1.0", "rack-1.0.0" - build_gem "thin", "1.1", :to_system => true do |s| - s.add_dependency "rack" + it "doesn't re-resolve when a pre-release bundler is used and a dependency includes a dependency on bundler" do + system_gems "bundler-9.99.9.beta1" + + build_repo4 do + build_gem "depends_on_bundler", "1.0" do |s| + s.add_dependency "bundler", ">= 1.5.0" end + end - gemfile <<-G - gem "thin", "1.0" - G + install_gemfile <<~G + source "https://gem.repo4" + gem "depends_on_bundler" + G - ruby <<-R - require 'rubygems' - gem "thin" - require 'bundler' - begin - Bundler.setup - puts "FAIL" - rescue Gem::LoadError => e - puts e.message - end - R + ruby "require '#{system_gem_path("gems/bundler-9.99.9.beta1/lib/bundler.rb")}'; Bundler.setup", env: { "DEBUG" => "1" } + expect(out).to include("Found no changes, using resolution from the lockfile") + expect(out).not_to include("lockfile does not have all gems needed for the current platform") + expect(err).to be_empty + end - expect(out).to eq("You have already activated thin 1.1, but your Gemfile requires thin 1.0. Prepending `bundle exec` to your command may solve this.") - end + it "doesn't fail in frozen mode when bundler is a Gemfile dependency" do + install_gemfile <<~G + source "https://gem.repo4" + gem "bundler" + G - it "version_requirement is now deprecated in rubygems 1.4.0+" do - system_gems "thin-1.0", "rack-1.0.0" - build_gem "thin", "1.1", :to_system => true do |s| - s.add_dependency "rack" - end + bundle "install --verbose", env: { "BUNDLE_FROZEN" => "true" } + expect(err).to be_empty + end - gemfile <<-G - gem "thin", "1.0" - G + it "doesn't re-resolve when deleting dependencies" do + install_gemfile <<-G + source "https://gem.repo1" + gem "myrack" + gem "actionpack" + G - ruby <<-R - require 'rubygems' - gem "thin" - require 'bundler' - begin - Bundler.setup - puts "FAIL" - rescue Gem::LoadError => e - puts e.message - end - R + install_gemfile <<-G, verbose: true + source "https://gem.repo1" + gem "myrack" + G - expect(err).to lack_errors - end + expect(out).to include("Some dependencies were deleted, using a subset of the resolution from the lockfile") + expect(err).to be_empty + end + + it "remembers --without and does not include groups passed to Bundler.setup" do + bundle_config "without rails" + install_gemfile <<-G + source "https://gem.repo1" + gem "activesupport" + + group :myrack do + gem "myrack" + end + + group :rails do + gem "rails", "2.3.2" + end + G + + expect(the_bundle).not_to include_gems "activesupport 2.3.2", groups: :myrack + expect(the_bundle).to include_gems "myrack 1.0.0", groups: :myrack end end - # Rubygems returns loaded_from as a string + # RubyGems returns loaded_from as a string it "has loaded_from as a string on all specs" do build_git "foo" - build_git "no-gemspec", :gemspec => false + build_git "no-gemspec", gemspec: false install_gemfile <<-G - source "file://#{gem_repo1}" - gem "rack" + source "https://gem.repo1" + gem "myrack" gem "foo", :git => "#{lib_path("foo-1.0")}" gem "no-gemspec", "1.0", :git => "#{lib_path("no-gemspec-1.0")}" G @@ -701,44 +732,163 @@ RSpec.describe "Bundler.setup" do expect(out).to be_empty end - it "does not load all gemspecs", :rubygems => ">= 2.3" do - install_gemfile! <<-G - source "file://#{gem_repo1}" - gem "rack" + it "has gem_dir pointing to local repo" do + build_lib "foo", "1.0", path: bundled_app + + install_gemfile <<-G + source "https://gem.repo1" + gemspec + G + + run <<-R + puts Gem.loaded_specs['foo'].gem_dir + R + + expect(out).to eq(bundled_app.to_s) + end + + it "does not load all gemspecs" do + install_gemfile <<-G + source "https://gem.repo1" + gem "myrack" G - run! <<-R - File.open(File.join(Gem.dir, "specifications", "broken.gemspec"), "w") do |f| + run <<-R + File.open(File.join(Gem.dir, "specifications", "invalid.gemspec"), "w") do |f| f.write <<-RUBY # -*- encoding: utf-8 -*- -# stub: broken 1.0.0 ruby lib +# stub: invalid 1.0.0 ruby lib Gem::Specification.new do |s| - s.name = "broken" + s.name = "invalid" s.version = "1.0.0" - raise "BROKEN GEMSPEC" + s.authors = ["Invalid Author"] + s.files = ["lib/invalid.rb"] + s.add_dependency "nonexistent-gem", "~> 999.999.999" + s.validate! end RUBY end R - run! <<-R - puts "WIN" + run <<-R + File.open(File.join(Gem.dir, "specifications", "invalid-ext.gemspec"), "w") do |f| + f.write <<-RUBY +# -*- encoding: utf-8 -*- +# stub: invalid-ext 1.0.0 ruby lib +# stub: a.ext\\0b.ext + +Gem::Specification.new do |s| + s.name = "invalid-ext" + s.version = "1.0.0" + s.authors = ["Invalid Author"] + s.files = ["lib/invalid.rb"] + s.required_ruby_version = "~> 0.8.0" + s.validate! +end + RUBY + end + # Need to write the gem.build_complete file, + # otherwise the full spec is loaded to check the installed_by_version + extensions_dir = Gem.default_ext_dir_for(Gem.dir) || File.join(Gem.dir, "extensions", Gem::Platform.local.to_s, Gem.extension_api_version) + Bundler::FileUtils.mkdir_p(File.join(extensions_dir, "invalid-ext-1.0.0")) + File.open(File.join(extensions_dir, "invalid-ext-1.0.0", "gem.build_complete"), "w") {} R - expect(out).to eq("WIN") + run <<-R + puts "Success" + R + + expect(out).to eq("Success") end it "ignores empty gem paths" do install_gemfile <<-G - source "file://#{gem_repo1}" - gem "rack" + source "https://gem.repo1" + gem "myrack" G ENV["GEM_HOME"] = "" bundle %(exec ruby -e "require 'set'") - expect(err).to lack_errors + expect(err).to be_empty + end + + it "can require rubygems without warnings, when using a local cache", :truffleruby do + install_gemfile <<-G + source "https://gem.repo1" + gem "myrack" + G + + bundle "package" + bundle %(exec ruby -w -e "require 'rubygems'") + + expect(err).to be_empty + end + + context "when the user has `MANPATH` set", :man do + before { ENV["MANPATH"] = "/foo#{File::PATH_SEPARATOR}" } + + it "adds the gem's man dir to the MANPATH" do + build_repo4 do + build_gem "with_man" do |s| + s.write("man/man1/page.1", "MANPAGE") + end + end + + install_gemfile <<-G + source "https://gem.repo4" + gem "with_man" + G + + run "puts ENV['MANPATH']" + expect(out).to eq("#{default_bundle_path("gems/with_man-1.0/man")}#{File::PATH_SEPARATOR}/foo") + end + end + + context "when the user does not have `MANPATH` set", :man do + before { ENV.delete("MANPATH") } + + it "adds the gem's man dir to the MANPATH, leaving : in the end so that system man pages still work" do + build_repo4 do + build_gem "with_man" do |s| + s.write("man/man1/page.1", "MANPAGE") + end + + build_gem "with_man_overriding_system_man" do |s| + s.write("man/man1/ls.1", "LS MANPAGE") + end + end + + install_gemfile <<-G + source "https://gem.repo4" + gem "with_man" + G + + run <<~RUBY + puts ENV['MANPATH'] + require "open3" + puts Open3.capture2e("man", "ls")[1].success? + RUBY + + expect(out).to eq("#{default_bundle_path("gems/with_man-1.0/man")}#{File::PATH_SEPARATOR}\ntrue") + + install_gemfile <<-G + source "https://gem.repo4" + gem "with_man_overriding_system_man" + G + + run <<~RUBY + puts ENV['MANPATH'] + require "open3" + puts Open3.capture2e({ "LC_ALL" => "C" }, "man", "ls")[0] + RUBY + + lines = out.split("\n") + + expect(lines).to include("#{default_bundle_path("gems/with_man_overriding_system_man-1.0/man")}#{File::PATH_SEPARATOR}") + expect(lines).to include("LS MANPAGE") + end end it "should prepend gemspec require paths to $LOAD_PATH in order" do @@ -746,12 +896,12 @@ end build_gem("requirepaths") do |s| s.write("lib/rq.rb", "puts 'yay'") s.write("src/rq.rb", "puts 'nooo'") - s.require_paths = %w(lib src) + s.require_paths = %w[lib src] end end install_gemfile <<-G - source "file://#{gem_repo2}" + source "https://gem.repo2" gem "requirepaths", :require => nil G @@ -759,26 +909,17 @@ end expect(out).to eq("yay") end - it "should clean $LOAD_PATH properly", :ruby_repo do + it "should clean $LOAD_PATH properly" do gem_name = "very_simple_binary" full_gem_name = gem_name + "-1.0" - ext_dir = File.join(tmp "extenstions", full_gem_name) - install_gem full_gem_name + system_gems full_gem_name install_gemfile <<-G - source "file://#{gem_repo1}" + source "https://gem.repo1" G ruby <<-R - if Gem::Specification.method_defined? :extension_dir - s = Gem::Specification.find_by_name '#{gem_name}' - s.extension_dir = '#{ext_dir}' - - # Don't build extensions. - s.class.send(:define_method, :build_extensions) { nil } - end - require 'bundler' gem '#{gem_name}' @@ -792,83 +933,128 @@ end expect(out).to eq("true\ntrue") end - it "stubs out Gem.refresh so it does not reveal system gems" do - system_gems "rack-1.0.0" + context "with bundler is located in symlinked GEM_HOME" do + let(:gem_home) { Dir.mktmpdir } + let(:symlinked_gem_home) { tmp("gem_home-symlink").to_s } + let(:full_name) { "bundler-#{Bundler::VERSION}" } + + before do + File.symlink(gem_home, symlinked_gem_home) + gems_dir = File.join(gem_home, "gems") + specifications_dir = File.join(gem_home, "specifications") + Dir.mkdir(gems_dir) + Dir.mkdir(specifications_dir) + + File.symlink(source_root, File.join(gems_dir, full_name)) + + gemspec_content = File.binread(gemspec). + sub("Bundler::VERSION", %("#{Bundler::VERSION}")). + lines.reject {|line| line.include?("lib/bundler/version") }.join + + File.open(File.join(specifications_dir, "#{full_name}.gemspec"), "wb") do |f| + f.write(gemspec_content) + end + end + + it "should not remove itself from the LOAD_PATH and require a different copy of 'bundler/setup'" do + install_gemfile "source 'https://gem.repo1'" + + ruby <<-R, env: { "GEM_PATH" => symlinked_gem_home } + TracePoint.trace(:class) do |tp| + if tp.path.include?("bundler") && !tp.path.start_with?("#{source_root}") + puts "OMG. Defining a class from another bundler at \#{tp.path}:\#{tp.lineno}" + end + end + gem 'bundler', '#{Bundler::VERSION}' + require 'bundler/setup' + R + + expect(out).to be_empty + end + end + + it "does not reveal system gems even when Gem.refresh is called" do + system_gems "myrack-1.0.0" install_gemfile <<-G - source "file://#{gem_repo1}" + source "https://gem.repo1" gem "activesupport" G run <<-R - puts Bundler.rubygems.find_name("rack").inspect + puts Bundler.rubygems.installed_specs.map(&:name) Gem.refresh - puts Bundler.rubygems.find_name("rack").inspect + puts Bundler.rubygems.installed_specs.map(&:name) R - expect(out).to eq("[]\n[]") + expect(out).to eq("activesupport\nbundler\nactivesupport\nbundler") end describe "when a vendored gem specification uses the :path option" do + let(:filesystem_root) do + current = Pathname.new(Dir.pwd) + current = current.parent until current == current.parent + current + end + it "should resolve paths relative to the Gemfile" do path = bundled_app(File.join("vendor", "foo")) - build_lib "foo", :path => path + build_lib "foo", path: path # If the .gemspec exists, then Bundler handles the path differently. # See Source::Path.load_spec_files for details. FileUtils.rm(File.join(path, "foo.gemspec")) install_gemfile <<-G + source "https://gem.repo1" gem 'foo', '1.2.3', :path => 'vendor/foo' G - Dir.chdir(bundled_app.parent) do - run <<-R, :env => { "BUNDLE_GEMFILE" => bundled_app("Gemfile") } - require 'foo' - R - end - expect(err).to lack_errors + run <<-R, env: { "BUNDLE_GEMFILE" => bundled_app_gemfile.to_s }, dir: bundled_app.parent + require 'foo' + R + expect(err).to be_empty end it "should make sure the Bundler.root is really included in the path relative to the Gemfile" do - relative_path = File.join("vendor", Dir.pwd[1..-1], "foo") + relative_path = File.join("vendor", Dir.pwd.gsub(/^#{filesystem_root}/, "")) absolute_path = bundled_app(relative_path) FileUtils.mkdir_p(absolute_path) - build_lib "foo", :path => absolute_path + build_lib "foo", path: absolute_path # If the .gemspec exists, then Bundler handles the path differently. # See Source::Path.load_spec_files for details. FileUtils.rm(File.join(absolute_path, "foo.gemspec")) gemfile <<-G + source "https://gem.repo1" gem 'foo', '1.2.3', :path => '#{relative_path}' G bundle :install - Dir.chdir(bundled_app.parent) do - run <<-R, :env => { "BUNDLE_GEMFILE" => bundled_app("Gemfile") } - require 'foo' - R - end + run <<-R, env: { "BUNDLE_GEMFILE" => bundled_app_gemfile.to_s }, dir: bundled_app.parent + require 'foo' + R - expect(err).to lack_errors + expect(err).to be_empty end end describe "with git gems that don't have gemspecs" do before :each do - build_git "no-gemspec", :gemspec => false + build_git "no_gemspec", gemspec: false install_gemfile <<-G - gem "no-gemspec", "1.0", :git => "#{lib_path("no-gemspec-1.0")}" + source "https://gem.repo1" + gem "no_gemspec", "1.0", :git => "#{lib_path("no_gemspec-1.0")}" G end it "loads the library via a virtual spec" do run <<-R - require 'no-gemspec' - puts NOGEMSPEC + require 'no_gemspec' + puts NO_GEMSPEC R expect(out).to eq("1.0") @@ -877,10 +1063,10 @@ end describe "with bundled and system gems" do before :each do - system_gems "rack-1.0.0" + system_gems "myrack-1.0.0" install_gemfile <<-G - source "file://#{gem_repo1}" + source "https://gem.repo1" gem "activesupport", "2.3.5" G @@ -888,10 +1074,8 @@ end it "does not pull in system gems" do run <<-R - require 'rubygems' - begin; - require 'rack' + require 'myrack' rescue LoadError puts 'WIN' end @@ -913,13 +1097,13 @@ end it "raises an exception if gem is used to invoke a system gem not in the bundle" do run <<-R begin - gem 'rack' + gem 'myrack' rescue LoadError => e puts e.message end R - expect(out).to eq("rack is not part of the bundle. Add it to your Gemfile.") + expect(out).to eq("myrack is not part of the bundle. Add it to your Gemfile.") end it "sets GEM_HOME appropriately" do @@ -930,11 +1114,12 @@ end describe "with system gems in the bundle" do before :each do - system_gems "rack-1.0.0" + bundle_config "path.system true" + system_gems "myrack-1.0.0" install_gemfile <<-G - source "file://#{gem_repo1}" - gem "rack", "1.0.0" + source "https://gem.repo1" + gem "myrack", "1.0.0" gem "activesupport", "2.3.5" G end @@ -943,18 +1128,15 @@ end run "puts Gem.path" paths = out.split("\n") expect(paths).to include(system_gem_path.to_s) - expect(paths).to include(default_bundle_path.to_s) end end describe "with a gemspec that requires other files" do before :each 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/', __FILE__) - $:.unshift lib unless $:.include?(lib) - require 'bar/version' + require_relative 'lib/bar/version' Gem::Specification.new do |s| s.name = 'bar' @@ -967,6 +1149,7 @@ end end gemfile <<-G + source "https://gem.repo1" gem "bar", :git => "#{lib_path("bar-1.0")}" G end @@ -978,14 +1161,15 @@ end end it "error intelligently if the gemspec has a LoadError" do - ref = update_git "bar", :gemspec => false do |s| + skip "whitespace issue?" if Gem.win_platform? + + ref = update_git "bar", gemspec: false do |s| s.write "bar.gemspec", "require 'foobarbaz'" end.ref_for("HEAD") - bundle :install + bundle :install, raise_on_error: false - expect(out.lines.map(&:chomp)).to include( + expect(err.lines.map(&:chomp)).to include( a_string_starting_with("[!] There was an error while loading `bar.gemspec`:"), - RUBY_VERSION >= "1.9" ? a_string_starting_with("Does it try to require a relative path? That's been removed in Ruby 1.9.") : "", " # from #{default_bundle_path "bundler", "gems", "bar-1.0-#{ref[0, 12]}", "bar.gemspec"}:1", " > require 'foobarbaz'" ) @@ -996,54 +1180,59 @@ end ruby <<-RUBY require 'bundler' + bundler_module = class << Bundler; self; end + bundler_module.send(:remove_method, :require) def Bundler.require(path) - raise "LOSE" + raise StandardError, "didn't use binding from top level" end Bundler.load RUBY - expect(err).to lack_errors - expect(out).to eq("") + expect(err).to be_empty + expect(out).to be_empty end end describe "when Bundler is bundled" do it "doesn't blow up" do install_gemfile <<-G - gem "bundler", :path => "#{File.expand_path("..", lib)}" + source "https://gem.repo1" + gem "bundler", :path => "#{root}" G bundle %(exec ruby -e "require 'bundler'; Bundler.setup") - expect(err).to lack_errors + expect(err).to be_empty end end describe "when BUNDLED WITH" do def lock_with(bundler_version = nil) - lock = <<-L + lock = <<~L GEM - remote: file:#{gem_repo1}/ + remote: https://gem.repo1/ specs: - rack (1.0.0) + myrack (1.0.0) PLATFORMS - #{generic_local_platform} + #{lockfile_platforms} DEPENDENCIES - rack + myrack L if bundler_version - lock += "\n BUNDLED WITH\n #{bundler_version}\n" + lock += "\nBUNDLED WITH\n #{bundler_version}\n" end lock end before do + bundle_config "path.system true" + install_gemfile <<-G - source "file://#{gem_repo1}" - gem "rack" + source "https://gem.repo1" + gem "myrack" G end @@ -1051,7 +1240,7 @@ end it "does not change the lock" do lockfile lock_with(nil) ruby "require 'bundler/setup'" - lockfile_should_be lock_with(nil) + expect(lockfile).to eq lock_with(nil) end end @@ -1059,17 +1248,18 @@ end it "does not change the lock or warn" do lockfile lock_with(Bundler::VERSION.succ) ruby "require 'bundler/setup'" - expect(out).to eq("") - expect(err).to eq("") - lockfile_should_be lock_with(Bundler::VERSION.succ) + expect(out).to be_empty + expect(err).to be_empty + expect(lockfile).to eq lock_with(Bundler::VERSION.succ) end end context "is older" do it "does not change the lock" do + system_gems "bundler-1.10.1" lockfile lock_with("1.10.1") ruby "require 'bundler/setup'" - lockfile_should_be lock_with("1.10.1") + expect(lockfile).to eq lock_with("1.10.1") end end end @@ -1078,27 +1268,32 @@ end let(:ruby_version) { nil } def lock_with(ruby_version = nil) - lock = <<-L + checksums = checksums_section do |c| + c.checksum gem_repo1, "myrack", "1.0.0" + end + + lock = <<~L GEM - remote: file:#{gem_repo1}/ + remote: https://gem.repo1/ specs: - rack (1.0.0) + myrack (1.0.0) PLATFORMS - #{generic_local_platform} + #{lockfile_platforms} DEPENDENCIES - rack + myrack + #{checksums} L if ruby_version - lock += "\n RUBY VERSION\n ruby #{ruby_version}\n" + lock += "\nRUBY VERSION\n ruby #{ruby_version}\n" end - lock += <<-L + lock += <<~L BUNDLED WITH - #{Bundler::VERSION} + #{Bundler::VERSION} L lock @@ -1107,38 +1302,76 @@ end before do install_gemfile <<-G ruby ">= 0" - source "file:#{gem_repo1}" - gem "rack" + source "https://gem.repo1" + gem "myrack" G lockfile lock_with(ruby_version) end context "is not present" do - it "does not change the lock" do - expect { ruby! "require 'bundler/setup'" }.not_to change { lockfile } + # Skipped on ruby-core because `ruby "require 'bundler/setup'"` does not + # activate bundler as a gem there, so Source::Metadata falls back to a + # synthetic spec whose cache_file does not exist on disk and + # LockfileGenerator#bundler_checksum drops the bundler checksum, while + # the on-disk lockfile still has it. + it "does not change the lock", :ruby_repo do + expect { ruby "require 'bundler/setup'" }.not_to change { lockfile } end end context "is newer" do let(:ruby_version) { "5.5.5" } it "does not change the lock or warn" do - expect { ruby! "require 'bundler/setup'" }.not_to change { lockfile } - expect(out).to eq("") - expect(err).to eq("") + expect { ruby "require 'bundler/setup'" }.not_to change { lockfile } + expect(out).to be_empty + expect(err).to be_empty end end context "is older" do let(:ruby_version) { "1.0.0" } it "does not change the lock" do - expect { ruby! "require 'bundler/setup'" }.not_to change { lockfile } + expect { ruby "require 'bundler/setup'" }.not_to change { lockfile } end end end describe "with gemified standard libraries" do - it "does not load Psych", :ruby => "~> 2.2" do - gemfile "" + it "does not load Digest", :ruby_repo do + build_git "bar", gemspec: false do |s| + s.write "lib/bar/version.rb", %(BAR_VERSION = '1.0') + s.write "bar.gemspec", <<-G + require_relative 'lib/bar/version' + + Gem::Specification.new do |s| + s.name = 'bar' + s.version = BAR_VERSION + s.summary = 'Bar' + s.files = Dir["lib/**/*.rb"] + s.author = 'no one' + + s.add_dependency 'digest' + end + G + end + + gemfile <<-G + source "https://gem.repo1" + gem "bar", :git => "#{lib_path("bar-1.0")}" + G + + bundle :install, env: { "BUNDLE_LOCKFILE_CHECKSUMS" => "false" } + + ruby <<-RUBY, artifice: nil + require 'bundler/setup' + puts defined?(::Digest) ? "Digest defined" : "Digest undefined" + require 'digest' + RUBY + expect(out).to eq("Digest undefined") + end + + it "does not load Psych" do + gemfile "source 'https://gem.repo1'" ruby <<-RUBY require 'bundler/setup' puts defined?(Psych::VERSION) ? Psych::VERSION : "undefined" @@ -1151,8 +1384,8 @@ end end it "does not load openssl" do - install_gemfile! "" - ruby! <<-RUBY + install_gemfile "source 'https://gem.repo1'" + ruby <<-RUBY, artifice: nil require "bundler/setup" puts defined?(OpenSSL) || "undefined" require "openssl" @@ -1161,32 +1394,82 @@ end expect(out).to eq("undefined\nconstant") end - describe "default gem activation", :ruby_repo do + it "does not load uri while reading gemspecs", rubygems: ">= 3.6.0.dev" do + Dir.mkdir bundled_app("test") + + create_file(bundled_app("test/test.gemspec"), <<-G) + Gem::Specification.new do |s| + s.name = "test" + s.version = "1.0.0" + s.summary = "test" + s.authors = ['John Doe'] + s.homepage = 'https://example.com' + end + G + + install_gemfile <<-G + source "https://gem.repo1" + gem "test", path: "#{bundled_app("test")}" + G + + ruby <<-RUBY, artifice: nil + require "bundler/setup" + puts defined?(URI) || "undefined" + require "uri" + puts defined?(URI) || "undefined" + RUBY + expect(out).to eq("undefined\nconstant") + end + + it "activates default gems when they are part of the bundle, but not installed explicitly", :ruby_repo do + default_delegate_version = ruby "gem 'delegate'; require 'delegate'; puts Delegator::VERSION" + + build_repo2 do + build_gem "delegate", default_delegate_version + end + + gemfile "source \"https://gem.repo2\"; gem 'delegate'" + + ruby <<-RUBY + require "bundler/setup" + require "delegate" + puts defined?(::Delegator) ? "Delegator defined" : "Delegator undefined" + RUBY + + expect(out).to eq("Delegator defined") + expect(err).to be_empty + end + + describe "default gem activation" do let(:exemptions) do - if Gem::Version.new(Gem::VERSION) >= Gem::Version.new("2.7") || ENV["RGV"] == "master" - [] - else - %w(io-console openssl) - end << "bundler" + exempts = %w[did_you_mean bundler uri pathname] + exempts << "error_highlight" # added in Ruby 3.1 as a default gem + exempts << "ruby2_keywords" # added in Ruby 3.1 as a default gem + exempts << "syntax_suggest" # added in Ruby 3.2 as a default gem + exempts end - let(:code) { strip_whitespace(<<-RUBY) } - require "rubygems" - - if Gem::Specification.instance_methods.map(&:to_sym).include?(:activate) - Gem::Specification.send(:alias_method, :bundler_spec_activate, :activate) - Gem::Specification.send(:define_method, :activate) do - unless #{exemptions.inspect}.include?(name) - warn '-' * 80 - warn "activating \#{full_name}" - warn *caller - warn '*' * 80 - end - bundler_spec_activate + let(:activation_warning_hack) { <<~RUBY } + require #{spec_dir.join("support/hax").to_s.dump} + + Gem::Specification.send(:alias_method, :bundler_spec_activate, :activate) + Gem::Specification.send(:define_method, :activate) do + unless #{exemptions.inspect}.include?(name) + warn '-' * 80 + warn "activating \#{full_name}" + warn(*caller) + warn '*' * 80 end + bundler_spec_activate end + RUBY - require "bundler/setup" + let(:activation_warning_hack_rubyopt) do + create_file("activation_warning_hack.rb", activation_warning_hack) + "-r#{bundled_app("activation_warning_hack.rb")} #{ENV["RUBYOPT"]}" + end + + let(:code) { <<~RUBY } require "pp" loaded_specs = Gem.loaded_specs.dup #{exemptions.inspect}.each {|s| loaded_specs.delete(s) } @@ -1200,90 +1483,228 @@ end RUBY it "activates no gems with -rbundler/setup" do - install_gemfile! "" - ruby!(code) - expect(err).to eq("") + install_gemfile "source 'https://gem.repo1'" + ruby code, env: { "RUBYOPT" => activation_warning_hack_rubyopt + " -rbundler/setup" }, artifice: nil expect(out).to eq("{}") end it "activates no gems with bundle exec" do - install_gemfile! "" + install_gemfile "source 'https://gem.repo1'" create_file("script.rb", code) - bundle! "exec ruby ./script.rb" - expect(err).to eq("") + bundle "exec ruby ./script.rb", env: { "RUBYOPT" => activation_warning_hack_rubyopt } expect(out).to eq("{}") end it "activates no gems with bundle exec that is loaded" do - # TODO: remove once https://github.com/erikhuda/thor/pull/539 is released - exemptions << "io-console" + skip "not executable" if Gem.win_platform? - install_gemfile! "" + install_gemfile "source 'https://gem.repo1'" create_file("script.rb", "#!/usr/bin/env ruby\n\n#{code}") FileUtils.chmod(0o777, bundled_app("script.rb")) - bundle! "exec ./script.rb", :artifice => nil - expect(err).to eq("") + bundle "exec ./script.rb", env: { "RUBYOPT" => activation_warning_hack_rubyopt } expect(out).to eq("{}") end - let(:default_gems) do - ruby!(<<-RUBY).split("\n") - if Gem::Specification.is_a?(Enumerable) - puts Gem::Specification.select(&:default_gem?).map(&:name) - end - RUBY - end - - it "activates newer versions of default gems" do + it "does not load net-http-pipeline too early" do build_repo4 do - default_gems.each do |g| - build_gem g, "999999" - end + build_gem "net-http-pipeline", "1.0.1" end - install_gemfile! <<-G - source "file:#{gem_repo4}" - #{default_gems}.each do |g| - gem g, "999999" - end + system_gems "net-http-pipeline-1.0.1", gem_repo: gem_repo4 + + gemfile <<-G + source "https://gem.repo4" + gem "net-http-pipeline", "1.0.1" G - expect(the_bundle).to include_gems(*default_gems.map {|g| "#{g} 999999" }) + bundle_config "path vendor/bundle" + + bundle :install + + bundle :check + + expect(out).to eq("The Gemfile's dependencies are satisfied") end - it "activates older versions of default gems" do - build_repo4 do - default_gems.each do |g| - build_gem g, "0.0.0.a" + Gem::Specification.select(&:default_gem?).map(&:name).each do |g| + it "activates newer versions of #{g}", :ruby_repo do + skip if exemptions.include?(g) + + build_repo4 do + build_gem g, "999999" end + + install_gemfile <<-G + source "https://gem.repo4" + gem "#{g}", "999999" + G + + expect(the_bundle).to include_gem("#{g} 999999", env: { "RUBYOPT" => activation_warning_hack_rubyopt }, artifice: nil) end - default_gems.reject! {|g| exemptions.include?(g) } + it "activates older versions of #{g}", :ruby_repo do + skip if exemptions.include?(g) - install_gemfile! <<-G - source "file:#{gem_repo4}" - #{default_gems}.each do |g| - gem g, "0.0.0.a" + build_repo4 do + build_gem g, "0.0.0.a" end - G - expect(the_bundle).to include_gems(*default_gems.map {|g| "#{g} 0.0.0.a" }) + install_gemfile <<-G + source "https://gem.repo4" + gem "#{g}", "0.0.0.a" + G + + expect(the_bundle).to include_gem("#{g} 0.0.0.a", env: { "RUBYOPT" => activation_warning_hack_rubyopt }, artifice: nil) + end end end end describe "after setup" do - it "allows calling #gem on random objects" do + it "keeps Kernel#gem private" do + install_gemfile <<-G + source "https://gem.repo1" + gem "myrack" + G + + ruby <<-RUBY, raise_on_error: false + require "bundler/setup" + Object.new.gem "myrack" + puts "FAIL" + RUBY + + expect(stdboth).not_to include "FAIL" + expect(err).to match(/private method [`']gem'/) + end + + it "keeps Kernel#require private" do install_gemfile <<-G - source "file:#{gem_repo1}" - gem "rack" + source "https://gem.repo1" + gem "myrack" G - ruby! <<-RUBY + + ruby <<-RUBY, raise_on_error: false require "bundler/setup" - Object.new.gem "rack" - puts Gem.loaded_specs["rack"].full_name + Object.new.require "myrack" + puts "FAIL" RUBY - expect(out).to eq("rack-1.0.0") + + expect(stdboth).not_to include "FAIL" + expect(err).to match(/private method [`']require'/) + end + + it "memoizes initial set of specs when requiring bundler/setup, so that even if further code mutates dependencies, Bundler.definition.specs is not affected" do + install_gemfile <<~G + source "https://gem.repo1" + gem "yard" + gem "myrack", :group => :test + G + + ruby <<-RUBY, raise_on_error: false + require "bundler/setup" + Bundler.require(:test).select! {|d| (d.groups & [:test]).any? } + puts Bundler.definition.specs.map(&:name).join(", ") + RUBY + + expect(out).to include("myrack, yard") + end + + it "does not cause double loads when higher versions of default gems are activated before bundler" do + build_repo2 do + build_gem "json", "999.999.999" do |s| + s.write "lib/json.rb", <<~RUBY + module JSON + VERSION = "999.999.999" + end + RUBY + end + end + + system_gems "json-999.999.999", gem_repo: gem_repo2 + + install_gemfile "source 'https://gem.repo1'" + ruby <<-RUBY + require "json" + require "bundler/setup" + require "json" + RUBY + + expect(err).to be_empty + end + end + + it "does not undo the Kernel.require decorations", rubygems: ">= 3.4.6" do + install_gemfile "source 'https://gem.repo1'" + script = bundled_app("bin/script") + create_file(script, <<~RUBY) + module Kernel + module_function + + alias_method :require_before_extra_monkeypatches, :require + + def require(path) + puts "requiring \#{path} used the monkeypatch" + + require_before_extra_monkeypatches(path) + end + end + + require "bundler/setup" + + require "foo" + RUBY + + sys_exec "#{Gem.ruby} #{script}", raise_on_error: false + expect(out).to include("requiring foo used the monkeypatch") + end + + it "performs an automatic bundle install" do + build_repo4 do + build_gem "myrack", "1.0.0" + end + + gemfile <<-G + source "https://gem.repo1" + gem "myrack", :group => :test + G + + bundle_config "auto_install 1" + + ruby <<-RUBY, artifice: "compact_index" + require 'bundler/setup' + RUBY + expect(err).to be_empty + expect(out).to include("Installing myrack 1.0.0") + end + + context "in a read-only filesystem" do + before do + gemfile <<-G + source "https://gem.repo4" + G + + lockfile <<-L + GEM + remote: https://gem.repo4/ + + PLATFORMS + x86_64-darwin-19 + + DEPENDENCIES + + BUNDLED WITH + #{Bundler::VERSION} + L + end + + it "should fail loudly if the lockfile platforms don't include the current platform" do + simulate_platform "x86_64-linux" do + ruby <<-RUBY, raise_on_error: false, env: { "BUNDLER_SPEC_READ_ONLY" => "true", "BUNDLER_FORCE_TTY" => "true" } + require "bundler/setup" + RUBY + end + + expect(err).to include("Your lockfile is missing the current platform, but can't be updated because file system is read-only") end end end |
