1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
ruby--ruby/spec/bundler/runtime/setup_spec.rb
David Rodríguez 526c9359ca [rubygems/rubygems] Don't cleanup paths from gems already activated from $LOAD_PATH
This way, if some default gem has been required before bundler, and
rubygems has enhanced the `$LOAD_PATH` to use the latest version in the
system, further requires of that default gem after bundler has been
activated will use the same version and don't cause redefinition
warnings or worse problems derived from the fact of mixing up two
different versions. That, unless the gem is a `Gemfile` dependency. In
that case, we'll get a mismatch error anyways as we do now.

This fix doesn't mean that all default gems internally used by
bundler/rubygems are now supported inside `Gemfile`'s. That should be
handled case by case, but it will now bite people only when they try to
add the gem to their `Gemfile`, not before.

https://github.com/rubygems/rubygems/commit/7325530547
2021-12-07 01:53:39 +09:00

1538 lines
40 KiB
Ruby

# frozen_string_literal: true
require "tmpdir"
require "tempfile"
RSpec.describe "Bundler.setup" do
describe "with no arguments" do
it "makes all groups available" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack", :group => :test
G
ruby <<-RUBY
require 'bundler'
Bundler.setup
require 'rack'
puts RACK
RUBY
expect(err).to be_empty
expect(out).to eq("1.0.0")
end
end
describe "when called with groups" do
before(:each) do
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "yard"
gem "rack", :group => :test
G
end
it "doesn't make all groups available" do
ruby <<-RUBY
require 'bundler'
Bundler.setup(:default)
begin
require 'rack'
rescue LoadError
puts "WIN"
end
RUBY
expect(err).to be_empty
expect(out).to eq("WIN")
end
it "accepts string for group name" do
ruby <<-RUBY
require 'bundler'
Bundler.setup(:default, 'test')
require 'rack'
puts RACK
RUBY
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 'bundler'
Bundler.setup
Bundler.setup(:default)
require 'rack'
puts RACK
RUBY
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 'bundler'
Bundler.setup(:default)
Bundler.setup(:default, :test)
begin
require 'yard'
puts "WIN"
rescue LoadError
puts "FAIL"
end
RUBY
expect(err).to be_empty
expect(out).to match("WIN")
end
it "handles multiple non-additive invocations" do
ruby <<-RUBY, :raise_on_error => false
require 'bundler'
Bundler.setup(:default, :test)
Bundler.setup(:default)
require 'rack'
puts "FAIL"
RUBY
expect(err).to match("rack")
expect(err).to match("LoadError")
expect(out).not_to match("FAIL")
end
end
context "load order" do
def clean_load_path(lp)
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_uri_for(gem_repo1)}"
gem "rack"
G
ENV["RUBYOPT"] = "#{ENV["RUBYOPT"]} -Idash_i_dir"
ENV["RUBYLIB"] = "rubylib_dir"
ruby <<-RUBY
require 'bundler'
Bundler.setup
puts $LOAD_PATH
RUBY
load_path = out.split("\n")
rack_load_order = load_path.index {|path| path.include?("rack") }
expect(err).to be_empty
expect(load_path).to include(a_string_ending_with("dash_i_dir"), "rubylib_dir")
expect(rack_load_order).to be > 0
end
it "orders the load path correctly when there are dependencies" do
bundle "config set path.system true"
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rails"
G
ruby <<-RUBY
require 'bundler'
Bundler.setup
puts $LOAD_PATH
RUBY
load_path = clean_load_path(out.split("\n"))
expect(load_path).to start_with(
"/gems/rails-2.3.2/lib",
"/gems/activeresource-2.3.2/lib",
"/gems/activerecord-2.3.2/lib",
"/gems/actionpack-2.3.2/lib",
"/gems/actionmailer-2.3.2/lib",
"/gems/activesupport-2.3.2/lib",
"/gems/rake-13.0.1/lib"
)
end
it "falls back to order the load path alphabetically for backwards compatibility" do
bundle "config set path.system true"
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "weakling"
gem "duradura"
gem "terranova"
G
ruby <<-RUBY
require 'bundler/setup'
puts $LOAD_PATH
RUBY
load_path = clean_load_path(out.split("\n"))
expect(load_path).to start_with(
"/gems/weakling-0.0.3/lib",
"/gems/terranova-8/lib",
"/gems/duradura-7.0/lib"
)
end
end
it "raises if the Gemfile was not yet installed" do
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack"
G
ruby <<-R
require '#{entrypoint}'
begin
Bundler.setup
puts "FAIL"
rescue Bundler::GemNotFound
puts "WIN"
end
R
expect(out).to eq("WIN")
end
it "doesn't create a Gemfile.lock if the setup fails" do
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack"
G
ruby <<-R, :raise_on_error => false
require 'bundler'
Bundler.setup
R
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_uri_for(gem_repo1)}"
gem "rack"
G
lockfile = File.read(bundled_app_lock)
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack"
gem "nosuchgem", "10.0"
G
ruby <<-R, :raise_on_error => false
require 'bundler'
Bundler.setup
R
expect(File.read(bundled_app_lock)).to eq(lockfile)
end
it "makes a Gemfile.lock if setup succeeds" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack"
G
File.read(bundled_app_lock)
FileUtils.rm(bundled_app_lock)
run "1"
expect(bundled_app_lock).to exist
end
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 "#{file_uri_for(gem_repo1)}"
gem "rack"
G
gemfile bundled_app("4realz"), <<-G
source "#{file_uri_for(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 "#{file_uri_for(gem_repo1)}"
G
bundle "install"
bundle "config set --local deployment true"
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
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_uri_for(gem_repo1)}"
gem "rack", "1.0.0"
G
build_gem "rack", "1.0", :to_system => true do |s|
s.write "lib/rack.rb", "RACK = 'FAIL'"
end
expect(the_bundle).to include_gems "rack 1.0.0"
end
describe "integrate with rubygems" do
describe "by replacing #gem" do
before :each do
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack", "0.9.1"
G
end
it "replaces #gem but raises when the gem is missing" do
run <<-R
begin
gem "activesupport"
puts "FAIL"
rescue LoadError
puts "WIN"
end
R
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 be_empty
end
it "replaces #gem but raises when the version is wrong" do
run <<-R
begin
gem "rack", "1.0.0"
puts "FAIL"
rescue LoadError
puts "WIN"
end
R
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 be_empty
end
end
describe "by hiding system gems" do
before :each do
system_gems "activesupport-2.3.5"
install_gemfile <<-G
source "#{file_uri_for(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")
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 }
expect(out).to eq("worked!")
end
end
end
end
describe "with paths" do
it "activates the gems in the path source" do
system_gems "rack-1.0.0"
build_lib "rack", "1.0.0" do |s|
s.write "lib/rack.rb", "puts 'WIN'"
end
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
path "#{lib_path("rack-1.0.0")}" do
gem "rack"
end
G
run "require 'rack'"
expect(out).to eq("WIN")
end
end
describe "with git" do
before do
build_git "rack", "1.0.0"
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack", :git => "#{lib_path("rack-1.0.0")}"
G
end
it "provides a useful exception when the git repo is not checked out yet" do
run "1", :raise_on_error => false
expect(err).to match(/the git source #{lib_path('rack-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
bundle "install"
break_git!
ruby <<-R
require 'bundler'
begin
Bundler.setup
puts "WIN"
rescue Exception => e
puts "FAIL"
end
R
expect(out).to eq("WIN")
end
it "provides a good exception if the lockfile is unavailable" do
bundle "install"
FileUtils.rm(bundled_app_lock)
break_git!
ruby <<-R
require "#{entrypoint}"
begin
Bundler.setup
puts "FAIL"
rescue Bundler::GitError => e
puts e.message
end
R
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 "config set --local path vendor/bundle"
bundle :install
FileUtils.rm_rf vendored_gems("cache")
expect(the_bundle).to include_gems "rack 1.0.0"
end
it "does not randomly change the path when specifying --path and the bundle directory becomes read only" do
bundle "config set --local path vendor/bundle"
bundle :install
with_read_only("#{bundled_app}/**/*") do
expect(the_bundle).to include_gems "rack 1.0.0"
end
end
it "finds git gem when default bundle path becomes read only" do
bundle "config set --local path .bundle"
bundle "install"
with_read_only("#{bundled_app(".bundle")}/**/*") do
expect(the_bundle).to include_gems "rack 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"
FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
G
bundle %(config set local.rack #{lib_path("local-rack")})
bundle :install
FileUtils.rm_rf(lib_path("local-rack"))
run "require 'rack'", :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/)
end
it "explodes if branch is not given on runtime" do
build_git "rack", "0.8"
FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
G
bundle %(config set local.rack #{lib_path("local-rack")})
bundle :install
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack", :git => "#{lib_path("rack-0.8")}"
G
run "require 'rack'", :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"
FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
G
bundle %(config set local.rack #{lib_path("local-rack")})
bundle :install
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "changed"
G
run "require 'rack'", :raise_on_error => false
expect(err).to match(/is using branch master but Gemfile specifies changed/)
end
it "explodes on refs with different branches on runtime" do
build_git "rack", "0.8"
FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack", :git => "#{lib_path("rack-0.8")}", :ref => "master", :branch => "master"
G
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack", :git => "#{lib_path("rack-0.8")}", :ref => "master", :branch => "nonexistant"
G
bundle %(config set local.rack #{lib_path("local-rack")})
run "require 'rack'", :raise_on_error => false
expect(err).to match(/is using branch master but Gemfile specifies nonexistant/)
end
end
describe "when excluding groups" do
it "doesn't change the resolve if --without is used" do
bundle "config set --local without rails"
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "activesupport"
group :rails do
gem "rails", "2.3.2"
end
G
system_gems "activesupport-2.3.5"
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
bundle "config set --local without rails"
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "activesupport"
group :rails do
gem "rails", "2.3.2"
end
G
system_gems "activesupport-2.3.5"
expect(the_bundle).to include_gems "activesupport 2.3.2"
end
it "remembers --without and does not bail on bare Bundler.setup, even in the case of path gems no longer available" do
bundle "config set --local without development"
path = bundled_app(File.join("vendor", "foo"))
build_lib "foo", :path => path
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "activesupport", "2.3.2"
gem 'foo', :path => 'vendor/foo', :group => :development
G
FileUtils.rm_rf(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
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
install_gemfile <<~G
source "#{file_uri_for(gem_repo4)}"
gem "depends_on_bundler"
G
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(err).to be_empty
end
it "remembers --without and does not include groups passed to Bundler.setup" do
bundle "config set --local without rails"
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "activesupport"
group :rack do
gem "rack"
end
group :rails do
gem "rails", "2.3.2"
end
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
end
end
# 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
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack"
gem "foo", :git => "#{lib_path("foo-1.0")}"
gem "no-gemspec", "1.0", :git => "#{lib_path("no-gemspec-1.0")}"
G
run <<-R
Gem.loaded_specs.each do |n, s|
puts "FAIL" unless s.loaded_from.is_a?(String)
end
R
expect(out).to be_empty
end
it "does not load all gemspecs" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack"
G
run <<-R
File.open(File.join(Gem.dir, "specifications", "broken.gemspec"), "w") do |f|
f.write <<-RUBY
# -*- encoding: utf-8 -*-
# stub: broken 1.0.0 ruby lib
Gem::Specification.new do |s|
s.name = "broken"
s.version = "1.0.0"
raise "BROKEN GEMSPEC"
end
RUBY
end
R
run <<-R
puts "WIN"
R
expect(out).to eq("WIN")
end
it "ignores empty gem paths" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack"
G
ENV["GEM_HOME"] = ""
bundle %(exec ruby -e "require 'set'")
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 "#{file_uri_for(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 "#{file_uri_for(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 "#{file_uri_for(gem_repo4)}"
gem "with_man_overriding_system_man"
G
run <<~RUBY
puts ENV['MANPATH']
require "open3"
puts Open3.capture2e("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
update_repo2 do
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]
end
end
install_gemfile <<-G
source "#{file_uri_for(gem_repo2)}"
gem "requirepaths", :require => nil
G
run "require 'rq'"
expect(out).to eq("yay")
end
it "should clean $LOAD_PATH properly", :ruby_repo do
gem_name = "very_simple_binary"
full_gem_name = gem_name + "-1.0"
ext_dir = File.join(tmp("extensions", full_gem_name))
system_gems full_gem_name
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
G
ruby <<-R
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 }
require 'bundler'
gem '#{gem_name}'
puts $LOAD_PATH.count {|path| path =~ /#{gem_name}/} >= 2
Bundler.setup
puts $LOAD_PATH.count {|path| path =~ /#{gem_name}/} == 0
R
expect(out).to eq("true\ntrue")
end
context "with bundler is located in symlinked GEM_HOME" do
let(:gem_home) { Dir.mktmpdir }
let(:symlinked_gem_home) { Tempfile.new("gem_home").path }
let(:full_name) { "bundler-#{Bundler::VERSION}" }
before do
skip "symlink destination exists" if Gem.win_platform?
FileUtils.ln_sf(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)
FileUtils.ln_s(source_root, File.join(gems_dir, full_name))
gemspec_content = File.binread(gemspec).
sub("Bundler::VERSION", %("#{Bundler::VERSION}")).
lines.reject {|line| line =~ %r{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 \"#{file_uri_for(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 "rack-1.0.0"
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "activesupport"
G
run <<-R
puts Bundler.rubygems.all_specs.map(&:name)
Gem.refresh
puts Bundler.rubygems.all_specs.map(&:name)
R
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
# 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 "#{file_uri_for(gem_repo1)}"
gem 'foo', '1.2.3', :path => 'vendor/foo'
G
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.gsub(/^#{filesystem_root}/, ""))
absolute_path = bundled_app(relative_path)
FileUtils.mkdir_p(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 "#{file_uri_for(gem_repo1)}"
gem 'foo', '1.2.3', :path => '#{relative_path}'
G
bundle :install
run <<-R, :env => { "BUNDLE_GEMFILE" => bundled_app_gemfile.to_s }, :dir => bundled_app.parent
require 'foo'
R
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
install_gemfile <<-G
source "#{file_uri_for(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 NO_GEMSPEC
R
expect(out).to eq("1.0")
end
end
describe "with bundled and system gems" do
before :each do
system_gems "rack-1.0.0"
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "activesupport", "2.3.5"
G
end
it "does not pull in system gems" do
run <<-R
begin;
require 'rack'
rescue LoadError
puts 'WIN'
end
R
expect(out).to eq("WIN")
end
it "provides a gem method" do
run <<-R
gem 'activesupport'
require 'activesupport'
puts ACTIVESUPPORT
R
expect(out).to eq("2.3.5")
end
it "raises an exception if gem is used to invoke a system gem not in the bundle" do
run <<-R
begin
gem 'rack'
rescue LoadError => e
puts e.message
end
R
expect(out).to eq("rack is not part of the bundle. Add it to your Gemfile.")
end
it "sets GEM_HOME appropriately" do
run "puts ENV['GEM_HOME']"
expect(out).to eq(default_bundle_path.to_s)
end
end
describe "with system gems in the bundle" do
before :each do
bundle "config set path.system true"
system_gems "rack-1.0.0"
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack", "1.0.0"
gem "activesupport", "2.3.5"
G
end
it "sets GEM_PATH appropriately" do
run "puts Gem.path"
paths = out.split("\n")
expect(paths).to include(system_gem_path.to_s)
end
end
describe "with a gemspec that requires other files" do
before :each 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'
end
G
end
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "bar", :git => "#{lib_path("bar-1.0")}"
G
end
it "evals each gemspec in the context of its parent directory" do
bundle :install
run "require 'bar'; puts BAR"
expect(out).to eq("1.0")
end
it "error intelligently if the gemspec has a LoadError" do
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, :raise_on_error => false
expect(err.lines.map(&:chomp)).to include(
a_string_starting_with("[!] There was an error while loading `bar.gemspec`:"),
" # from #{default_bundle_path "bundler", "gems", "bar-1.0-#{ref[0, 12]}", "bar.gemspec"}:1",
" > require 'foobarbaz'"
)
end
it "evals each gemspec with a binding from the top level" do
bundle "install"
ruby <<-RUBY
require 'bundler'
bundler_module = class << Bundler; self; end
bundler_module.send(:remove_method, :require)
def Bundler.require(path)
raise "LOSE"
end
Bundler.load
RUBY
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
source "#{file_uri_for(gem_repo1)}"
gem "bundler", :path => "#{root}"
G
bundle %(exec ruby -e "require 'bundler'; Bundler.setup")
expect(err).to be_empty
end
end
describe "when BUNDLED WITH" do
def lock_with(bundler_version = nil)
lock = <<~L
GEM
remote: #{file_uri_for(gem_repo1)}/
specs:
rack (1.0.0)
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
rack
L
if bundler_version
lock += "\nBUNDLED WITH\n #{bundler_version}\n"
end
lock
end
before do
bundle "config set --local path.system true"
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack"
G
end
context "is not present" do
it "does not change the lock" do
lockfile lock_with(nil)
ruby "require '#{entrypoint}/setup'"
expect(lockfile).to eq lock_with(nil)
end
end
context "is newer" do
it "does not change the lock or warn" do
lockfile lock_with(Bundler::VERSION.succ)
ruby "require 'bundler/setup'"
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 '#{entrypoint}/setup'"
expect(lockfile).to eq lock_with("1.10.1")
end
end
end
describe "when RUBY VERSION" do
let(:ruby_version) { nil }
def lock_with(ruby_version = nil)
lock = <<~L
GEM
remote: #{file_uri_for(gem_repo1)}/
specs:
rack (1.0.0)
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
rack
L
if ruby_version
lock += "\nRUBY VERSION\n ruby #{ruby_version}\n"
end
lock += <<~L
BUNDLED WITH
#{Bundler::VERSION}
L
lock
end
before do
install_gemfile <<-G
ruby ">= 0"
source "#{file_uri_for(gem_repo1)}"
gem "rack"
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 }
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 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 }
end
end
end
describe "with gemified standard libraries" do
it "does not load Digest", :ruby_repo do
skip "Only for Ruby 3.0+" unless RUBY_VERSION >= "3.0"
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_runtime_dependency 'digest'
end
G
end
gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "bar", :git => "#{lib_path("bar-1.0")}"
G
bundle :install
ruby <<-RUBY
require '#{entrypoint}/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 \"#{file_uri_for(gem_repo1)}\""
ruby <<-RUBY
require '#{entrypoint}/setup'
puts defined?(Psych::VERSION) ? Psych::VERSION : "undefined"
require 'psych'
puts Psych::VERSION
RUBY
pre_bundler, post_bundler = out.split("\n")
expect(pre_bundler).to eq("undefined")
expect(post_bundler).to match(/\d+\.\d+\.\d+/)
end
it "does not load openssl" do
install_gemfile "source \"#{file_uri_for(gem_repo1)}\""
ruby <<-RUBY
require "bundler/setup"
puts defined?(OpenSSL) || "undefined"
require "openssl"
puts defined?(OpenSSL) || "undefined"
RUBY
expect(out).to eq("undefined\nconstant")
end
describe "default gem activation" do
let(:exemptions) do
exempts = if Gem.rubygems_version >= Gem::Version.new("2.7")
%w[did_you_mean]
else
%w[io-console openssl]
end << "bundler"
exempts << "fiddle" if Gem.win_platform? && Gem.rubygems_version >= Gem::Version.new("2.7")
exempts << "uri" if Gem.ruby_version >= Gem::Version.new("2.7")
exempts << "pathname" if Gem.ruby_version >= Gem::Version.new("3.0")
exempts << "set" unless Gem.rubygems_version >= Gem::Version.new("3.2.6")
exempts << "tsort" unless Gem.rubygems_version >= Gem::Version.new("3.2.31")
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
end
let(:activation_warning_hack) { strip_whitespace(<<-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
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) { strip_whitespace(<<-RUBY) }
require "pp"
loaded_specs = Gem.loaded_specs.dup
#{exemptions.inspect}.each {|s| loaded_specs.delete(s) }
pp loaded_specs
# not a default gem, but harmful to have loaded
open_uri = $LOADED_FEATURES.grep(/open.uri/)
unless open_uri.empty?
warn "open_uri: \#{open_uri}"
end
RUBY
it "activates no gems with -rbundler/setup" do
install_gemfile "source \"#{file_uri_for(gem_repo1)}\""
ruby code, :env => { "RUBYOPT" => activation_warning_hack_rubyopt + " -rbundler/setup" }
expect(out).to eq("{}")
end
it "activates no gems with bundle exec" do
install_gemfile "source \"#{file_uri_for(gem_repo1)}\""
create_file("script.rb", code)
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
skip "not executable" if Gem.win_platform?
install_gemfile "source \"#{file_uri_for(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, :env => { "RUBYOPT" => activation_warning_hack_rubyopt }
expect(out).to eq("{}")
end
it "does not load net-http-pipeline too early" do
build_repo4 do
build_gem "net-http-pipeline", "1.0.1"
end
system_gems "net-http-pipeline-1.0.1", :gem_repo => gem_repo4
gemfile <<-G
source "#{file_uri_for(gem_repo4)}"
gem "net-http-pipeline", "1.0.1"
G
bundle "config set --local path vendor/bundle"
bundle :install
bundle :check
expect(out).to eq("The Gemfile's dependencies are satisfied")
end
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 "#{file_uri_for(gem_repo4)}"
gem "#{g}", "999999"
G
expect(the_bundle).to include_gem("#{g} 999999", :env => { "RUBYOPT" => activation_warning_hack_rubyopt })
end
it "activates older versions of #{g}", :ruby_repo do
skip if exemptions.include?(g)
build_repo4 do
build_gem g, "0.0.0.a"
end
install_gemfile <<-G
source "#{file_uri_for(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 })
end
end
end
end
describe "after setup" do
it "allows calling #gem on random objects", :bundler => "< 3" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack"
G
ruby <<-RUBY
require "bundler/setup"
Object.new.gem "rack"
puts Gem.loaded_specs["rack"].full_name
RUBY
expect(out).to eq("rack-1.0.0")
end
it "keeps Kernel#gem private", :bundler => "3" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack"
G
ruby <<-RUBY, :raise_on_error => false
require "bundler/setup"
Object.new.gem "rack"
puts "FAIL"
RUBY
expect(last_command.stdboth).not_to include "FAIL"
expect(err).to include "private method `gem'"
end
it "keeps Kernel#require private" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo1)}"
gem "rack"
G
ruby <<-RUBY, :raise_on_error => false
require "bundler/setup"
Object.new.require "rack"
puts "FAIL"
RUBY
expect(last_command.stdboth).not_to include "FAIL"
expect(err).to include "private method `require'"
end
it "takes care of requiring rubygems" do
sys_exec("#{Gem.ruby} -I#{lib_dir} -rbundler/setup -e'puts true'", :env => { "RUBYOPT" => opt_add("--disable=gems", ENV["RUBYOPT"]) })
expect(last_command.stdboth).to eq("true")
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 "#{file_uri_for(gem_repo1)}"
gem "yard"
gem "rack", :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("rack, 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 \"#{file_uri_for(gem_repo1)}\""
ruby <<-RUBY
require "json"
require "bundler/setup"
require "json"
RUBY
expect(err).to be_empty
end
end
end