From 8598f8c2dc78c6d1ae87cb6ae19c34ba2cb29241 Mon Sep 17 00:00:00 2001 From: hsbt Date: Fri, 8 Sep 2017 08:45:41 +0000 Subject: Merge bundler to standard libraries. rubygems 2.7.x depends bundler-1.15.x. This is preparation for rubygems and bundler migration. * lib/bundler.rb, lib/bundler/*: files of bundler-1.15.4 * spec/bundler/*: rspec examples of bundler-1.15.4. I applied patches. * https://github.com/bundler/bundler/pull/6007 * Exclude not working examples on ruby repository. * Fake ruby interpriter instead of installed ruby. * Makefile.in: Added test task named `test-bundler`. This task is only working macOS/linux yet. I'm going to support Windows environment later. * tool/sync_default_gems.rb: Added sync task for bundler. [Feature #12733][ruby-core:77172] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@59779 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- spec/bundler/runtime/executable_spec.rb | 149 ++++ spec/bundler/runtime/gem_tasks_spec.rb | 43 + spec/bundler/runtime/inline_spec.rb | 268 ++++++ spec/bundler/runtime/load_spec.rb | 115 +++ spec/bundler/runtime/platform_spec.rb | 123 +++ spec/bundler/runtime/require_spec.rb | 442 +++++++++ spec/bundler/runtime/setup_spec.rb | 1289 +++++++++++++++++++++++++++ spec/bundler/runtime/with_clean_env_spec.rb | 135 +++ 8 files changed, 2564 insertions(+) create mode 100644 spec/bundler/runtime/executable_spec.rb create mode 100644 spec/bundler/runtime/gem_tasks_spec.rb create mode 100644 spec/bundler/runtime/inline_spec.rb create mode 100644 spec/bundler/runtime/load_spec.rb create mode 100644 spec/bundler/runtime/platform_spec.rb create mode 100644 spec/bundler/runtime/require_spec.rb create mode 100644 spec/bundler/runtime/setup_spec.rb create mode 100644 spec/bundler/runtime/with_clean_env_spec.rb (limited to 'spec/bundler/runtime') diff --git a/spec/bundler/runtime/executable_spec.rb b/spec/bundler/runtime/executable_spec.rb new file mode 100644 index 0000000000..ff27d0b415 --- /dev/null +++ b/spec/bundler/runtime/executable_spec.rb @@ -0,0 +1,149 @@ +# frozen_string_literal: true +require "spec_helper" + +RSpec.describe "Running bin/* commands" do + before :each do + gemfile <<-G + source "file://#{gem_repo1}" + gem "rack" + G + end + + it "runs the bundled command when in the bundle" do + bundle "install --binstubs" + + build_gem "rack", "2.0", :to_system => true do |s| + s.executables = "rackup" + end + + gembin "rackup" + expect(out).to eq("1.0.0") + end + + it "allows the location of the gem stubs to be specified" do + bundle "install --binstubs gbin" + + expect(bundled_app("bin")).not_to exist + expect(bundled_app("gbin/rackup")).to exist + + gembin bundled_app("gbin/rackup") + expect(out).to eq("1.0.0") + end + + it "allows absolute paths as a specification of where to install bin stubs" do + bundle "install --binstubs #{tmp}/bin" + + gembin tmp("bin/rackup") + expect(out).to eq("1.0.0") + end + + it "uses the default ruby install name when shebang is not specified" do + bundle "install --binstubs" + expect(File.open("bin/rackup").gets).to eq("#!/usr/bin/env #{RbConfig::CONFIG["ruby_install_name"]}\n") + end + + it "allows the name of the shebang executable to be specified" do + bundle "install --binstubs --shebang ruby-foo" + expect(File.open("bin/rackup").gets).to eq("#!/usr/bin/env ruby-foo\n") + end + + it "runs the bundled command when out of the bundle" do + bundle "install --binstubs" + + build_gem "rack", "2.0", :to_system => true do |s| + s.executables = "rackup" + end + + Dir.chdir(tmp) do + gembin "rackup" + expect(out).to eq("1.0.0") + end + end + + it "works with gems in path" do + build_lib "rack", :path => lib_path("rack") do |s| + s.executables = "rackup" + end + + gemfile <<-G + gem "rack", :path => "#{lib_path("rack")}" + G + + bundle "install --binstubs" + + build_gem "rack", "2.0", :to_system => true do |s| + s.executables = "rackup" + end + + gembin "rackup" + expect(out).to eq("1.0") + end + + it "don't bundle da bundla" do + build_gem "bundler", Bundler::VERSION, :to_system => true do |s| + s.executables = "bundle" + end + + gemfile <<-G + source "file://#{gem_repo1}" + gem "bundler" + G + + bundle "install --binstubs" + + expect(bundled_app("bin/bundle")).not_to exist + end + + it "does not generate bin stubs if the option was not specified" do + bundle "install" + + expect(bundled_app("bin/rackup")).not_to exist + end + + it "allows you to stop installing binstubs" do + bundle "install --binstubs bin/" + bundled_app("bin/rackup").rmtree + bundle "install --binstubs \"\"" + + expect(bundled_app("bin/rackup")).not_to exist + + bundle "config bin" + expect(out).to include("You have not configured a value for `bin`") + end + + it "remembers that the option was specified" do + gemfile <<-G + source "file://#{gem_repo1}" + gem "activesupport" + G + + bundle "install --binstubs" + + gemfile <<-G + source "file://#{gem_repo1}" + gem "activesupport" + gem "rack" + G + + bundle "install" + + expect(bundled_app("bin/rackup")).to exist + end + + it "rewrites bins on --binstubs (to maintain backwards compatibility)" do + gemfile <<-G + source "file://#{gem_repo1}" + gem "rack" + G + + bundle "install --binstubs bin/" + + File.open(bundled_app("bin/rackup"), "wb") do |file| + file.print "OMG" + end + + bundle "install" + + expect(bundled_app("bin/rackup").read).to_not eq("OMG") + end +end diff --git a/spec/bundler/runtime/gem_tasks_spec.rb b/spec/bundler/runtime/gem_tasks_spec.rb new file mode 100644 index 0000000000..7cb0f32c0c --- /dev/null +++ b/spec/bundler/runtime/gem_tasks_spec.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true +require "spec_helper" + +RSpec.describe "require 'bundler/gem_tasks'" do + before :each do + bundled_app("foo.gemspec").open("w") do |f| + f.write <<-GEMSPEC + Gem::Specification.new do |s| + s.name = "foo" + end + GEMSPEC + end + bundled_app("Rakefile").open("w") do |f| + f.write <<-RAKEFILE + $:.unshift("#{bundler_path}") + require "bundler/gem_tasks" + RAKEFILE + end + end + + it "includes the relevant tasks" do + with_gem_path_as(Spec::Path.base_system_gems.to_s) do + sys_exec "#{rake} -T" + end + + expect(err).to eq("") + expected_tasks = [ + "rake build", + "rake clean", + "rake clobber", + "rake install", + "rake release[remote]", + ] + tasks = out.lines.to_a.map {|s| s.split("#").first.strip } + expect(tasks & expected_tasks).to eq(expected_tasks) + expect(exitstatus).to eq(0) if exitstatus + end + + it "adds 'pkg' to rake/clean's CLOBBER" do + require "bundler/gem_tasks" + expect(CLOBBER).to include("pkg") + end +end diff --git a/spec/bundler/runtime/inline_spec.rb b/spec/bundler/runtime/inline_spec.rb new file mode 100644 index 0000000000..e816799d08 --- /dev/null +++ b/spec/bundler/runtime/inline_spec.rb @@ -0,0 +1,268 @@ +# frozen_string_literal: true +require "spec_helper" + +RSpec.describe "bundler/inline#gemfile" do + def script(code, options = {}) + requires = ["bundler/inline"] + requires.unshift File.expand_path("../../support/artifice/" + options.delete(:artifice) + ".rb", __FILE__) if options.key?(:artifice) + requires = requires.map {|r| "require '#{r}'" }.join("\n") + @out = ruby("#{requires}\n\n" + code, options) + end + + before :each do + build_lib "one", "1.0.0" do |s| + s.write "lib/baz.rb", "puts 'baz'" + s.write "lib/qux.rb", "puts 'qux'" + end + + build_lib "two", "1.0.0" do |s| + s.write "lib/two.rb", "puts 'two'" + s.add_dependency "three", "= 1.0.0" + end + + build_lib "three", "1.0.0" do |s| + s.write "lib/three.rb", "puts 'three'" + s.add_dependency "seven", "= 1.0.0" + end + + build_lib "four", "1.0.0" do |s| + s.write "lib/four.rb", "puts 'four'" + end + + build_lib "five", "1.0.0", :no_default => true do |s| + s.write "lib/mofive.rb", "puts 'five'" + end + + build_lib "six", "1.0.0" do |s| + s.write "lib/six.rb", "puts 'six'" + end + + build_lib "seven", "1.0.0" do |s| + s.write "lib/seven.rb", "puts 'seven'" + end + + build_lib "eight", "1.0.0" do |s| + s.write "lib/eight.rb", "puts 'eight'" + end + + build_lib "four", "1.0.0" do |s| + s.write "lib/four.rb", "puts 'four'" + end + end + + it "requires the gems" do + script <<-RUBY + gemfile do + path "#{lib_path}" + gem "two" + end + RUBY + + expect(out).to eq("two") + expect(exitstatus).to be_zero if exitstatus + + script <<-RUBY + gemfile do + path "#{lib_path}" + gem "eleven" + end + + puts "success" + RUBY + + expect(err).to include "Could not find gem 'eleven'" + expect(out).not_to include "success" + + script <<-RUBY + gemfile(true) do + source "file://#{gem_repo1}" + gem "rack" + end + RUBY + + expect(out).to include("Rack's post install message") + expect(exitstatus).to be_zero if exitstatus + + script <<-RUBY, :artifice => "endpoint" + gemfile(true) do + source "https://notaserver.com" + gem "activesupport", :require => true + end + RUBY + + expect(out).to include("Installing activesupport") + err.gsub! %r{.*lib/sinatra/base\.rb:\d+: warning: constant ::Fixnum is deprecated$}, "" + err.strip! + expect(err).to lack_errors + expect(exitstatus).to be_zero if exitstatus + end + + it "lets me use my own ui object" do + script <<-RUBY, :artifice => "endpoint" + require 'bundler' + class MyBundlerUI < Bundler::UI::Silent + def confirm(msg, newline = nil) + puts "CONFIRMED!" + end + end + gemfile(true, :ui => MyBundlerUI.new) do + source "https://notaserver.com" + gem "activesupport", :require => true + end + RUBY + + expect(out).to eq("CONFIRMED!\nCONFIRMED!") + expect(exitstatus).to be_zero if exitstatus + end + + it "raises an exception if passed unknown arguments" do + script <<-RUBY + gemfile(true, :arglebargle => true) do + path "#{lib_path}" + gem "two" + end + + puts "success" + RUBY + expect(err).to include "Unknown options: arglebargle" + expect(out).not_to include "success" + end + + it "does not mutate the option argument" do + script <<-RUBY + require 'bundler' + options = { :ui => Bundler::UI::Shell.new } + gemfile(false, options) do + path "#{lib_path}" + gem "two" + end + puts "OKAY" if options.key?(:ui) + RUBY + + expect(out).to match("OKAY") + expect(exitstatus).to be_zero if exitstatus + end + + it "installs quietly if necessary when the install option is not set" do + script <<-RUBY + gemfile do + source "file://#{gem_repo1}" + gem "rack" + end + + puts RACK + RUBY + + expect(out).to eq("1.0.0") + expect(err).to be_empty + expect(exitstatus).to be_zero if exitstatus + end + + it "installs quietly from git if necessary when the install option is not set" do + build_git "foo", "1.0.0" + baz_ref = build_git("baz", "2.0.0").ref_for("HEAD") + script <<-RUBY + gemfile do + gem "foo", :git => #{lib_path("foo-1.0.0").to_s.dump} + gem "baz", :git => #{lib_path("baz-2.0.0").to_s.dump}, :ref => #{baz_ref.dump} + end + + puts FOO + puts BAZ + RUBY + + expect(out).to eq("1.0.0\n2.0.0") + expect(err).to be_empty + expect(exitstatus).to be_zero if exitstatus + end + + it "allows calling gemfile twice" do + script <<-RUBY + gemfile do + path "#{lib_path}" do + gem "two" + end + end + + gemfile do + path "#{lib_path}" do + gem "four" + end + end + RUBY + + expect(out).to eq("two\nfour") + expect(err).to be_empty + expect(exitstatus).to be_zero if exitstatus + end + + it "installs inline gems when a Gemfile.lock is present" do + gemfile <<-G + source "https://notaserver.com" + gem "rake" + G + + lockfile <<-G + GEM + remote: https://rubygems.org/ + specs: + rake (11.3.0) + + PLATFORMS + ruby + + DEPENDENCIES + rake + + BUNDLED WITH + 1.13.6 + G + + in_app_root do + script <<-RUBY + gemfile do + source "file://#{gem_repo1}" + gem "rack" + end + + puts RACK + RUBY + end + + expect(err).to be_empty + expect(exitstatus).to be_zero if exitstatus + end + + it "installs inline gems when BUNDLE_GEMFILE is set to an empty string" do + ENV["BUNDLE_GEMFILE"] = "" + + in_app_root do + script <<-RUBY + gemfile do + source "file://#{gem_repo1}" + gem "rack" + end + + puts RACK + RUBY + end + + expect(err).to be_empty + expect(exitstatus).to be_zero if exitstatus + end + + it "installs inline gems when BUNDLE_BIN is set" do + ENV["BUNDLE_BIN"] = "/usr/local/bundle/bin" + + script <<-RUBY + gemfile do + source "file://#{gem_repo1}" + gem "rack" # has the rackup executable + end + + puts RACK + RUBY + expect(exitstatus).to eq(0) if exitstatus + expect(out).to eq "1.0.0" + end +end diff --git a/spec/bundler/runtime/load_spec.rb b/spec/bundler/runtime/load_spec.rb new file mode 100644 index 0000000000..d0e308ed3e --- /dev/null +++ b/spec/bundler/runtime/load_spec.rb @@ -0,0 +1,115 @@ +# frozen_string_literal: true +require "spec_helper" + +RSpec.describe "Bundler.load" do + before :each do + system_gems "rack-1.0.0" + end + + describe "with a gemfile" do + before(:each) do + gemfile <<-G + source "file://#{gem_repo1}" + gem "rack" + G + end + + it "provides a list of the env dependencies" do + expect(Bundler.load.dependencies).to have_dep("rack", ">= 0") + end + + it "provides a list of the resolved gems" do + expect(Bundler.load.gems).to have_gem("rack-1.0.0", "bundler-#{Bundler::VERSION}") + end + + it "ignores blank BUNDLE_GEMFILEs" do + expect do + ENV["BUNDLE_GEMFILE"] = "" + Bundler.load + end.not_to raise_error + end + end + + describe "with a gems.rb file" do + before(:each) do + create_file "gems.rb", <<-G + source "file://#{gem_repo1}" + gem "rack" + G + end + + it "provides a list of the env dependencies" do + expect(Bundler.load.dependencies).to have_dep("rack", ">= 0") + end + + it "provides a list of the resolved gems" do + expect(Bundler.load.gems).to have_gem("rack-1.0.0", "bundler-#{Bundler::VERSION}") + end + end + + describe "without a gemfile" do + it "raises an exception if the default gemfile is not found" do + expect do + Bundler.load + end.to raise_error(Bundler::GemfileNotFound, /could not locate gemfile/i) + end + + it "raises an exception if a specified gemfile is not found" do + expect do + ENV["BUNDLE_GEMFILE"] = "omg.rb" + Bundler.load + end.to raise_error(Bundler::GemfileNotFound, /omg\.rb/) + end + + it "does not find a Gemfile above the testing directory" do + bundler_gemfile = tmp.join("../Gemfile") + unless File.exist?(bundler_gemfile) + FileUtils.touch(bundler_gemfile) + @remove_bundler_gemfile = true + end + begin + expect { Bundler.load }.to raise_error(Bundler::GemfileNotFound) + ensure + bundler_gemfile.rmtree if @remove_bundler_gemfile + end + end + end + + describe "when called twice" do + it "doesn't try to load the runtime twice" do + system_gems "rack-1.0.0", "activesupport-2.3.5" + gemfile <<-G + gem "rack" + gem "activesupport", :group => :test + G + + ruby <<-RUBY + require "bundler" + Bundler.setup :default + Bundler.require :default + puts RACK + begin + require "activesupport" + rescue LoadError + puts "no activesupport" + end + RUBY + + expect(out.split("\n")).to eq(["1.0.0", "no activesupport"]) + end + end + + describe "not hurting brittle rubygems" do + it "does not inject #source into the generated YAML of the gem specs" do + system_gems "activerecord-2.3.2", "activesupport-2.3.2" + gemfile <<-G + gem "activerecord" + G + + Bundler.load.specs.each do |spec| + expect(spec.to_yaml).not_to match(/^\s+source:/) + expect(spec.to_yaml).not_to match(/^\s+groups:/) + end + end + end +end diff --git a/spec/bundler/runtime/platform_spec.rb b/spec/bundler/runtime/platform_spec.rb new file mode 100644 index 0000000000..4df934e71f --- /dev/null +++ b/spec/bundler/runtime/platform_spec.rb @@ -0,0 +1,123 @@ +# frozen_string_literal: true +require "spec_helper" + +RSpec.describe "Bundler.setup with multi platform stuff" do + it "raises a friendly error when gems are missing locally" do + gemfile <<-G + source "file://#{gem_repo1}" + gem "rack" + G + + lockfile <<-G + GEM + remote: file:#{gem_repo1}/ + specs: + rack (1.0) + + PLATFORMS + #{local_tag} + + DEPENDENCIES + rack + G + + ruby <<-R + begin + require 'bundler' + Bundler.setup + rescue Bundler::GemNotFound => e + puts "WIN" + end + R + + expect(out).to eq("WIN") + end + + it "will resolve correctly on the current platform when the lockfile was targetted for a different one" do + lockfile <<-G + GEM + remote: file:#{gem_repo1}/ + specs: + nokogiri (1.4.2-java) + weakling (= 0.0.3) + weakling (0.0.3) + + PLATFORMS + java + + DEPENDENCIES + nokogiri + G + + system_gems "nokogiri-1.4.2" + + simulate_platform "x86-darwin-10" + gemfile <<-G + source "file://#{gem_repo1}" + gem "nokogiri" + G + + expect(the_bundle).to include_gems "nokogiri 1.4.2" + end + + it "will add the resolve for the current platform" do + lockfile <<-G + GEM + remote: file:#{gem_repo1}/ + specs: + nokogiri (1.4.2-java) + weakling (= 0.0.3) + weakling (0.0.3) + + PLATFORMS + java + + DEPENDENCIES + nokogiri + G + + simulate_platform "x86-darwin-100" + + system_gems "nokogiri-1.4.2", "platform_specific-1.0-x86-darwin-100" + + gemfile <<-G + source "file://#{gem_repo1}" + gem "nokogiri" + gem "platform_specific" + G + + expect(the_bundle).to include_gems "nokogiri 1.4.2", "platform_specific 1.0 x86-darwin-100" + end + + it "allows specifying only-ruby-platform" do + simulate_platform "java" + + install_gemfile! <<-G + source "file://#{gem_repo1}" + gem "nokogiri" + gem "platform_specific" + G + + bundle! "config force_ruby_platform true" + + bundle! "install" + + expect(the_bundle).to include_gems "nokogiri 1.4.2", "platform_specific 1.0 RUBY" + end + + it "allows specifying only-ruby-platform on windows with dependency platforms" do + simulate_windows do + install_gemfile! <<-G + source "file://#{gem_repo1}" + gem "nokogiri", :platforms => [:mingw, :mswin, :x64_mingw, :jruby] + gem "platform_specific" + G + + bundle! "config force_ruby_platform true" + + bundle! "install" + + expect(the_bundle).to include_gems "platform_specific 1.0 RUBY" + end + end +end diff --git a/spec/bundler/runtime/require_spec.rb b/spec/bundler/runtime/require_spec.rb new file mode 100644 index 0000000000..b68313726b --- /dev/null +++ b/spec/bundler/runtime/require_spec.rb @@ -0,0 +1,442 @@ +# frozen_string_literal: true +require "spec_helper" + +RSpec.describe "Bundler.require" do + before :each do + build_lib "one", "1.0.0" do |s| + s.write "lib/baz.rb", "puts 'baz'" + s.write "lib/qux.rb", "puts 'qux'" + end + + build_lib "two", "1.0.0" do |s| + s.write "lib/two.rb", "puts 'two'" + s.add_dependency "three", "= 1.0.0" + end + + build_lib "three", "1.0.0" do |s| + s.write "lib/three.rb", "puts 'three'" + s.add_dependency "seven", "= 1.0.0" + end + + build_lib "four", "1.0.0" do |s| + s.write "lib/four.rb", "puts 'four'" + end + + build_lib "five", "1.0.0", :no_default => true do |s| + s.write "lib/mofive.rb", "puts 'five'" + end + + build_lib "six", "1.0.0" do |s| + s.write "lib/six.rb", "puts 'six'" + end + + build_lib "seven", "1.0.0" do |s| + s.write "lib/seven.rb", "puts 'seven'" + end + + build_lib "eight", "1.0.0" do |s| + s.write "lib/eight.rb", "puts 'eight'" + end + + build_lib "nine", "1.0.0" do |s| + s.write "lib/nine.rb", "puts 'nine'" + end + + build_lib "ten", "1.0.0" do |s| + s.write "lib/ten.rb", "puts 'ten'" + end + + gemfile <<-G + path "#{lib_path}" + gem "one", :group => :bar, :require => %w[baz qux] + gem "two" + gem "three", :group => :not + gem "four", :require => false + gem "five" + gem "six", :group => "string" + gem "seven", :group => :not + gem "eight", :require => true, :group => :require_true + env "BUNDLER_TEST" => "nine" do + gem "nine", :require => true + end + gem "ten", :install_if => lambda { ENV["BUNDLER_TEST"] == "ten" } + G + end + + it "requires the gems" do + # default group + run "Bundler.require" + expect(out).to eq("two") + + # specific group + run "Bundler.require(:bar)" + expect(out).to eq("baz\nqux") + + # default and specific group + run "Bundler.require(:default, :bar)" + expect(out).to eq("baz\nqux\ntwo") + + # specific group given as a string + run "Bundler.require('bar')" + expect(out).to eq("baz\nqux") + + # specific group declared as a string + run "Bundler.require(:string)" + expect(out).to eq("six") + + # required in resolver order instead of gemfile order + run("Bundler.require(:not)") + expect(out.split("\n").sort).to eq(%w(seven three)) + + # test require: true + run "Bundler.require(:require_true)" + expect(out).to eq("eight") + end + + it "allows requiring gems with non standard names explicitly" do + run "Bundler.require ; require 'mofive'" + expect(out).to eq("two\nfive") + end + + it "allows requiring gems which are scoped by env" do + ENV["BUNDLER_TEST"] = "nine" + run "Bundler.require" + expect(out).to eq("two\nnine") + end + + it "allows requiring gems which are scoped by install_if" do + ENV["BUNDLER_TEST"] = "ten" + run "Bundler.require" + expect(out).to eq("two\nten") + end + + it "raises an exception if a require is specified but the file does not exist" do + gemfile <<-G + path "#{lib_path}" + gem "two", :require => 'fail' + G + + load_error_run <<-R, "fail" + Bundler.require + R + + expect(err).to eq_err("ZOMG LOAD ERROR") + end + + it "displays a helpful message if the required gem throws an error" do + build_lib "faulty", "1.0.0" do |s| + s.write "lib/faulty.rb", "raise RuntimeError.new(\"Gem Internal Error Message\")" + end + + gemfile <<-G + path "#{lib_path}" + gem "faulty" + G + + run "Bundler.require" + expect(err).to match("error while trying to load the gem 'faulty'") + expect(err).to match("Gem Internal Error Message") + end + + it "doesn't swallow the error when the library has an unrelated error" do + build_lib "loadfuuu", "1.0.0" do |s| + s.write "lib/loadfuuu.rb", "raise LoadError.new(\"cannot load such file -- load-bar\")" + end + + gemfile <<-G + path "#{lib_path}" + gem "loadfuuu" + G + + cmd = <<-RUBY + begin + Bundler.require + rescue LoadError => e + $stderr.puts "ZOMG LOAD ERROR: \#{e.message}" + end + RUBY + run(cmd) + + expect(err).to eq_err("ZOMG LOAD ERROR: cannot load such file -- load-bar") + end + + describe "with namespaced gems" do + before :each do + build_lib "jquery-rails", "1.0.0" do |s| + s.write "lib/jquery/rails.rb", "puts 'jquery/rails'" + end + lib_path("jquery-rails-1.0.0/lib/jquery-rails.rb").rmtree + end + + it "requires gem names that are namespaced" do + gemfile <<-G + path '#{lib_path}' + gem 'jquery-rails' + G + + run "Bundler.require" + expect(out).to eq("jquery/rails") + end + + it "silently passes if the require fails" do + build_lib "bcrypt-ruby", "1.0.0", :no_default => true do |s| + s.write "lib/brcrypt.rb", "BCrypt = '1.0.0'" + end + gemfile <<-G + path "#{lib_path}" + gem "bcrypt-ruby" + G + + cmd = <<-RUBY + require 'bundler' + Bundler.require + RUBY + ruby(cmd) + + expect(err).to lack_errors + end + + it "does not mangle explicitly given requires" do + gemfile <<-G + path "#{lib_path}" + gem 'jquery-rails', :require => 'jquery-rails' + G + + load_error_run <<-R, "jquery-rails" + Bundler.require + R + expect(err).to eq_err("ZOMG LOAD ERROR") + end + + it "handles the case where regex fails" do + build_lib "load-fuuu", "1.0.0" do |s| + s.write "lib/load-fuuu.rb", "raise LoadError.new(\"Could not open library 'libfuuu-1.0': libfuuu-1.0: cannot open shared object file: No such file or directory.\")" + end + + gemfile <<-G + path "#{lib_path}" + gem "load-fuuu" + G + + cmd = <<-RUBY + begin + Bundler.require + rescue LoadError => e + $stderr.puts "ZOMG LOAD ERROR" if e.message.include?("Could not open library 'libfuuu-1.0'") + end + RUBY + run(cmd) + + expect(err).to eq_err("ZOMG LOAD ERROR") + end + + it "doesn't swallow the error when the library has an unrelated error" do + build_lib "load-fuuu", "1.0.0" do |s| + s.write "lib/load/fuuu.rb", "raise LoadError.new(\"cannot load such file -- load-bar\")" + end + lib_path("load-fuuu-1.0.0/lib/load-fuuu.rb").rmtree + + gemfile <<-G + path "#{lib_path}" + gem "load-fuuu" + G + + cmd = <<-RUBY + begin + Bundler.require + rescue LoadError => e + $stderr.puts "ZOMG LOAD ERROR: \#{e.message}" + end + RUBY + run(cmd) + + expect(err).to eq_err("ZOMG LOAD ERROR: cannot load such file -- load-bar") + end + end + + describe "using bundle exec" do + it "requires the locked gems" do + bundle "exec ruby -e 'Bundler.require'" + expect(out).to eq("two") + + bundle "exec ruby -e 'Bundler.require(:bar)'" + expect(out).to eq("baz\nqux") + + bundle "exec ruby -e 'Bundler.require(:default, :bar)'" + expect(out).to eq("baz\nqux\ntwo") + end + end + + describe "order" do + before(:each) do + build_lib "one", "1.0.0" do |s| + s.write "lib/one.rb", <<-ONE + if defined?(Two) + Two.two + else + puts "two_not_loaded" + end + puts 'one' + ONE + end + + build_lib "two", "1.0.0" do |s| + s.write "lib/two.rb", <<-TWO + module Two + def self.two + puts 'module_two' + end + end + puts 'two' + TWO + end + end + + it "works when the gems are in the Gemfile in the correct order" do + gemfile <<-G + path "#{lib_path}" + gem "two" + gem "one" + G + + run "Bundler.require" + expect(out).to eq("two\nmodule_two\none") + end + + describe "a gem with different requires for different envs" do + before(:each) do + build_gem "multi_gem", :to_system => true do |s| + s.write "lib/one.rb", "puts 'ONE'" + s.write "lib/two.rb", "puts 'TWO'" + end + + install_gemfile <<-G + gem "multi_gem", :require => "one", :group => :one + gem "multi_gem", :require => "two", :group => :two + G + end + + it "requires both with Bundler.require(both)" do + run "Bundler.require(:one, :two)" + expect(out).to eq("ONE\nTWO") + end + + it "requires one with Bundler.require(:one)" do + run "Bundler.require(:one)" + expect(out).to eq("ONE") + end + + it "requires :two with Bundler.require(:two)" do + run "Bundler.require(:two)" + expect(out).to eq("TWO") + end + end + + it "fails when the gems are in the Gemfile in the wrong order" do + gemfile <<-G + path "#{lib_path}" + gem "one" + gem "two" + G + + run "Bundler.require" + expect(out).to eq("two_not_loaded\none\ntwo") + end + + describe "with busted gems" do + it "should be busted" do + build_gem "busted_require", :to_system => true do |s| + s.write "lib/busted_require.rb", "require 'no_such_file_omg'" + end + + install_gemfile <<-G + gem "busted_require" + G + + load_error_run <<-R, "no_such_file_omg" + Bundler.require + R + expect(err).to eq_err("ZOMG LOAD ERROR") + end + end + end + + it "does not load rubygems gemspecs that are used", :rubygems => ">= 2.5.2" do + install_gemfile! <<-G + source "file://#{gem_repo1}" + gem "rack" + G + + run! <<-R + path = File.join(Gem.dir, "specifications", "rack-1.0.0.gemspec") + contents = File.read(path) + contents = contents.lines.to_a.insert(-2, "\n raise 'broken gemspec'\n").join + File.open(path, "w") do |f| + f.write contents + end + R + + run! <<-R + Bundler.require + puts "WIN" + R + + expect(out).to eq("WIN") + end + + it "does not load git gemspecs that are used", :rubygems => ">= 2.5.2" do + build_git "foo" + + install_gemfile! <<-G + gem "foo", :git => "#{lib_path("foo-1.0")}" + G + + run! <<-R + path = Gem.loaded_specs["foo"].loaded_from + contents = File.read(path) + contents = contents.lines.to_a.insert(-2, "\n raise 'broken gemspec'\n").join + File.open(path, "w") do |f| + f.write contents + end + R + + run! <<-R + Bundler.require + puts "WIN" + R + + expect(out).to eq("WIN") + end +end + +RSpec.describe "Bundler.require with platform specific dependencies" do + it "does not require the gems that are pinned to other platforms" do + install_gemfile <<-G + source "file://#{gem_repo1}" + + platforms :#{not_local_tag} do + gem "fail", :require => "omgomg" + end + + gem "rack", "1.0.0" + G + + run "Bundler.require" + expect(err).to lack_errors + end + + it "requires gems pinned to multiple platforms, including the current one" do + install_gemfile <<-G + source "file://#{gem_repo1}" + + platforms :#{not_local_tag}, :#{local_tag} do + gem "rack", :require => "rack" + end + G + + run "Bundler.require; puts RACK" + + expect(out).to eq("1.0.0") + expect(err).to lack_errors + end +end diff --git a/spec/bundler/runtime/setup_spec.rb b/spec/bundler/runtime/setup_spec.rb new file mode 100644 index 0000000000..dc7af07188 --- /dev/null +++ b/spec/bundler/runtime/setup_spec.rb @@ -0,0 +1,1289 @@ +# frozen_string_literal: true +require "spec_helper" + +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 + G + + ruby <<-RUBY + require 'rubygems' + require 'bundler' + Bundler.setup + + require 'rack' + puts RACK + RUBY + expect(err).to lack_errors + expect(out).to eq("1.0.0") + end + end + + describe "when called with groups" do + before(:each) do + install_gemfile <<-G + source "file://#{gem_repo1}" + gem "yard" + gem "rack", :group => :test + G + end + + it "doesn't make all groups available" do + ruby <<-RUBY + require 'rubygems' + require 'bundler' + Bundler.setup(:default) + + begin + require 'rack' + rescue LoadError + puts "WIN" + end + RUBY + expect(err).to lack_errors + 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 + RUBY + expect(err).to lack_errors + 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 + RUBY + expect(err).to lack_errors + 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) + + begin + require 'yard' + puts "WIN" + rescue LoadError + puts "FAIL" + end + RUBY + expect(err).to lack_errors + expect(out).to match("WIN") + end + + it "handles multiple non-additive invocations" do + ruby <<-RUBY + 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 = 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}/, "") } + end + + it "puts loaded gems after -I and RUBYLIB", :ruby_repo do + install_gemfile <<-G + source "file://#{gem_repo1}" + gem "rack" + G + + 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") } + + 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 + end + + it "orders the load path correctly when there are dependencies", :ruby_repo do + install_gemfile <<-G + source "file://#{gem_repo1}" + gem "rails" + G + + ruby! <<-RUBY + require 'rubygems' + 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-10.0.2/lib" + ) + end + + it "falls back to order the load path alphabetically for backwards compatibility" do + install_gemfile! <<-G + source "file://#{gem_repo1}" + gem "weakling" + gem "duradura" + gem "terranova" + G + + ruby! <<-RUBY + require 'rubygems' + 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://#{gem_repo1}" + gem "rack" + G + + ruby <<-R + require 'rubygems' + require 'bundler' + + 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://#{gem_repo1}" + gem "rack" + G + + ruby <<-R + require 'rubygems' + require 'bundler' + + Bundler.setup + R + + expect(bundled_app("Gemfile.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" + G + + lockfile = File.read(bundled_app("Gemfile.lock")) + + gemfile <<-G + source "file://#{gem_repo1}" + gem "rack" + gem "nosuchgem", "10.0" + G + + ruby <<-R + require 'rubygems' + require 'bundler' + + Bundler.setup + R + + expect(File.read(bundled_app("Gemfile.lock"))).to eq(lockfile) + end + + it "makes a Gemfile.lock if setup succeeds" do + install_gemfile <<-G + source "file://#{gem_repo1}" + gem "rack" + G + + File.read(bundled_app("Gemfile.lock")) + + FileUtils.rm(bundled_app("Gemfile.lock")) + + run "1" + expect(bundled_app("Gemfile.lock")).to exist + end + + it "uses BUNDLE_GEMFILE to locate the gemfile if present" do + gemfile <<-G + source "file://#{gem_repo1}" + gem "rack" + G + + gemfile bundled_app("4realz"), <<-G + source "file://#{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 + + 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" + 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://#{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 lack_errors + 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 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}" + 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 + path "#{lib_path("rack-1.0.0")}" + source "file://#{gem_repo1}" + gem "rack" + 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 + 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" + 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 'rubygems' + 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("Gemfile.lock")) + + break_git! + + ruby <<-R + require "rubygems" + require "bundler" + + begin + Bundler.setup + puts "FAIL" + rescue Bundler::GitError => e + puts e.message + end + R + + run "puts 'FAIL'" + + 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" + end + + it "does not randomly change the path when specifying --path and the bundle directory becomes read only" do + bundle "install --path vendor/bundle" + + with_read_only("**/*") 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 "install" + + with_read_only("#{Bundler.bundle_path}/**/*") 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://#{gem_repo1}" + gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master" + G + + bundle %(config local.rack #{lib_path("local-rack")}) + 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/) + 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://#{gem_repo1}" + gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master" + G + + bundle %(config local.rack #{lib_path("local-rack")}) + 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")}" + G + + run "require 'rack'" + 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://#{gem_repo1}" + gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master" + G + + bundle %(config local.rack #{lib_path("local-rack")}) + 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" + G + + run "require 'rack'" + 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://#{gem_repo1}" + gem "rack", :git => "#{lib_path("rack-0.8")}", :ref => "master", :branch => "master" + G + + gemfile <<-G + source "file://#{gem_repo1}" + gem "rack", :git => "#{lib_path("rack-0.8")}", :ref => "master", :branch => "nonexistant" + G + + bundle %(config local.rack #{lib_path("local-rack")}) + run "require 'rack'" + 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 + install_gemfile <<-G, :without => :rails + source "file://#{gem_repo1}" + gem "activesupport" + + group :rails do + gem "rails", "2.3.2" + end + G + + install_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 + install_gemfile <<-G, :without => :rails + source "file://#{gem_repo1}" + gem "activesupport" + + group :rails do + gem "rails", "2.3.2" + end + G + + install_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" + + 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 + + # 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" + end + + gemfile <<-G + gem "thin", "1.0" + G + + ruby <<-R + require 'rubygems' + gem "thin" + require 'bundler' + begin + Bundler.setup + puts "FAIL" + rescue Gem::LoadError => e + puts e.message + end + R + + 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 "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 + + gemfile <<-G + gem "thin", "1.0" + G + + ruby <<-R + require 'rubygems' + gem "thin" + require 'bundler' + begin + Bundler.setup + puts "FAIL" + rescue Gem::LoadError => e + puts e.message + end + R + + expect(err).to lack_errors + end + 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://#{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", :rubygems => ">= 2.3" do + install_gemfile! <<-G + source "file://#{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://#{gem_repo1}" + gem "rack" + G + + ENV["GEM_HOME"] = "" + bundle %(exec ruby -e "require 'set'") + + expect(err).to lack_errors + 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://#{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 "extenstions", full_gem_name) + + install_gem full_gem_name + + install_gemfile <<-G + source "file://#{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}' + + 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 + + it "stubs out Gem.refresh so it does not reveal system gems" do + system_gems "rack-1.0.0" + + install_gemfile <<-G + source "file://#{gem_repo1}" + gem "activesupport" + G + + run <<-R + puts Bundler.rubygems.find_name("rack").inspect + Gem.refresh + puts Bundler.rubygems.find_name("rack").inspect + R + + expect(out).to eq("[]\n[]") + end + + describe "when a vendored gem specification uses the :path option" do + 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 + 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 + 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") + 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 + 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 + + expect(err).to lack_errors + end + end + + describe "with git gems that don't have gemspecs" do + before :each do + build_git "no-gemspec", :gemspec => false + + install_gemfile <<-G + 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 + 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://#{gem_repo1}" + + gem "activesupport", "2.3.5" + G + end + + it "does not pull in system gems" do + run <<-R + require 'rubygems' + + 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 + system_gems "rack-1.0.0" + + install_gemfile <<-G + source "file://#{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) + 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| + 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' + + 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 + 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 + ref = update_git "bar", :gemspec => false do |s| + s.write "bar.gemspec", "require 'foobarbaz'" + end.ref_for("HEAD") + bundle :install + + expect(out.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'" + ) + end + + it "evals each gemspec with a binding from the top level" do + bundle "install" + + ruby <<-RUBY + require 'bundler' + def Bundler.require(path) + raise "LOSE" + end + Bundler.load + RUBY + + expect(err).to lack_errors + expect(out).to eq("") + end + end + + describe "when Bundler is bundled" do + it "doesn't blow up" do + install_gemfile <<-G + gem "bundler", :path => "#{File.expand_path("..", lib)}" + G + + bundle %(exec ruby -e "require 'bundler'; Bundler.setup") + expect(err).to lack_errors + end + end + + describe "when BUNDLED WITH" do + def lock_with(bundler_version = nil) + lock = <<-L + GEM + remote: file:#{gem_repo1}/ + specs: + rack (1.0.0) + + PLATFORMS + #{generic_local_platform} + + DEPENDENCIES + rack + L + + if bundler_version + lock += "\n BUNDLED WITH\n #{bundler_version}\n" + end + + lock + end + + before do + install_gemfile <<-G + source "file://#{gem_repo1}" + gem "rack" + G + end + + context "is not present" do + it "does not change the lock" do + lockfile lock_with(nil) + ruby "require 'bundler/setup'" + lockfile_should_be 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 eq("") + expect(err).to eq("") + lockfile_should_be lock_with(Bundler::VERSION.succ) + end + end + + context "is older" do + it "does not change the lock" do + lockfile lock_with("1.10.1") + ruby "require 'bundler/setup'" + lockfile_should_be 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:#{gem_repo1}/ + specs: + rack (1.0.0) + + PLATFORMS + #{generic_local_platform} + + DEPENDENCIES + rack + L + + if ruby_version + lock += "\n RUBY 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:#{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 eq("") + expect(err).to eq("") + 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 Psych", :ruby => "~> 2.2" do + gemfile "" + ruby <<-RUBY + require 'bundler/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! "" + 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", :ruby_repo 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" + 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 + end + end + + require "bundler/setup" + 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! "" + ruby!(code) + expect(err).to eq("") + expect(out).to eq("{}") + end + + it "activates no gems with bundle exec" do + install_gemfile! "" + create_file("script.rb", code) + bundle! "exec ruby ./script.rb" + expect(err).to eq("") + 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" + + install_gemfile! "" + 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("") + 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 + build_repo4 do + default_gems.each do |g| + build_gem g, "999999" + end + end + + install_gemfile! <<-G + source "file:#{gem_repo4}" + #{default_gems}.each do |g| + gem g, "999999" + end + G + + expect(the_bundle).to include_gems(*default_gems.map {|g| "#{g} 999999" }) + end + + it "activates older versions of default gems" do + build_repo4 do + default_gems.each do |g| + build_gem g, "0.0.0.a" + end + end + + default_gems.reject! {|g| exemptions.include?(g) } + + install_gemfile! <<-G + source "file:#{gem_repo4}" + #{default_gems}.each do |g| + gem g, "0.0.0.a" + end + G + + expect(the_bundle).to include_gems(*default_gems.map {|g| "#{g} 0.0.0.a" }) + end + end + end + + describe "after setup" do + it "allows calling #gem on random objects" do + install_gemfile <<-G + source "file:#{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 + end +end diff --git a/spec/bundler/runtime/with_clean_env_spec.rb b/spec/bundler/runtime/with_clean_env_spec.rb new file mode 100644 index 0000000000..d18a0de485 --- /dev/null +++ b/spec/bundler/runtime/with_clean_env_spec.rb @@ -0,0 +1,135 @@ +# frozen_string_literal: true +require "spec_helper" + +RSpec.describe "Bundler.with_env helpers" do + describe "Bundler.original_env" do + before do + gemfile "" + bundle "install --path vendor/bundle" + end + + it "should return the PATH present before bundle was activated", :ruby_repo do + code = "print Bundler.original_env['PATH']" + path = `getconf PATH`.strip + "#{File::PATH_SEPARATOR}/foo" + with_path_as(path) do + result = bundle("exec ruby -e #{code.dump}") + expect(result).to eq(path) + end + end + + it "should return the GEM_PATH present before bundle was activated" do + code = "print Bundler.original_env['GEM_PATH']" + gem_path = ENV["GEM_PATH"] + ":/foo" + with_gem_path_as(gem_path) do + result = bundle("exec ruby -e #{code.inspect}") + expect(result).to eq(gem_path) + end + end + + it "works with nested bundle exec invocations", :ruby_repo do + create_file("exe.rb", <<-'RB') + count = ARGV.first.to_i + exit if count < 0 + STDERR.puts "#{count} #{ENV["PATH"].end_with?(":/foo")}" + if count == 2 + ENV["PATH"] = "#{ENV["PATH"]}:/foo" + end + exec("ruby", __FILE__, (count - 1).to_s) + RB + path = `getconf PATH`.strip + File::PATH_SEPARATOR + File.dirname(Gem.ruby) + with_path_as(path) do + bundle!("exec ruby #{bundled_app("exe.rb")} 2") + end + expect(err).to eq <<-EOS.strip +2 false +1 true +0 true + EOS + end + end + + describe "Bundler.clean_env" do + before do + gemfile "" + bundle "install --path vendor/bundle" + end + + it "should delete BUNDLE_PATH" do + code = "print Bundler.clean_env.has_key?('BUNDLE_PATH')" + ENV["BUNDLE_PATH"] = "./foo" + result = bundle("exec ruby -e #{code.inspect}") + expect(result).to eq("false") + end + + it "should remove '-rbundler/setup' from RUBYOPT" do + code = "print Bundler.clean_env['RUBYOPT']" + ENV["RUBYOPT"] = "-W2 -rbundler/setup" + result = bundle("exec ruby -e #{code.inspect}") + expect(result).not_to include("-rbundler/setup") + end + + it "should clean up RUBYLIB", :ruby_repo do + code = "print Bundler.clean_env['RUBYLIB']" + ENV["RUBYLIB"] = root.join("lib").to_s + File::PATH_SEPARATOR + "/foo" + result = bundle("exec ruby -e #{code.inspect}") + expect(result).to eq("/foo") + end + + it "should restore the original MANPATH" do + code = "print Bundler.clean_env['MANPATH']" + ENV["MANPATH"] = "/foo" + ENV["BUNDLER_ORIG_MANPATH"] = "/foo-original" + result = bundle("exec ruby -e #{code.inspect}") + expect(result).to eq("/foo-original") + end + end + + describe "Bundler.with_original_env" do + it "should set ENV to original_env in the block" do + expected = Bundler.original_env + actual = Bundler.with_original_env { ENV.to_hash } + expect(actual).to eq(expected) + end + + it "should restore the environment after execution" do + Bundler.with_original_env do + ENV["FOO"] = "hello" + end + + expect(ENV).not_to have_key("FOO") + end + end + + describe "Bundler.with_clean_env" do + it "should set ENV to clean_env in the block" do + expected = Bundler.clean_env + actual = Bundler.with_clean_env { ENV.to_hash } + expect(actual).to eq(expected) + end + + it "should restore the environment after execution" do + Bundler.with_clean_env do + ENV["FOO"] = "hello" + end + + expect(ENV).not_to have_key("FOO") + end + end + + describe "Bundler.clean_system", :ruby => ">= 1.9" do + it "runs system inside with_clean_env" do + Bundler.clean_system(%(echo 'if [ "$BUNDLE_PATH" = "" ]; then exit 42; else exit 1; fi' | /bin/sh)) + expect($?.exitstatus).to eq(42) + end + end + + describe "Bundler.clean_exec", :ruby => ">= 1.9" do + it "runs exec inside with_clean_env" do + pid = Kernel.fork do + Bundler.clean_exec(%(echo 'if [ "$BUNDLE_PATH" = "" ]; then exit 42; else exit 1; fi' | /bin/sh)) + end + Process.wait(pid) + expect($?.exitstatus).to eq(42) + end + end +end -- cgit v1.2.3