diff options
Diffstat (limited to 'spec/bundler/commands/exec_spec.rb')
| -rw-r--r-- | spec/bundler/commands/exec_spec.rb | 638 |
1 files changed, 359 insertions, 279 deletions
diff --git a/spec/bundler/commands/exec_spec.rb b/spec/bundler/commands/exec_spec.rb index c6648f0a7a..aa35685be8 100644 --- a/spec/bundler/commands/exec_spec.rb +++ b/spec/bundler/commands/exec_spec.rb @@ -1,142 +1,126 @@ # frozen_string_literal: true RSpec.describe "bundle exec" do - let(:system_gems_to_install) { %w[rack-1.0.0 rack-0.9.1] } - before :each do - system_gems(system_gems_to_install, :path => default_bundle_path) - end - it "works with --gemfile flag" do - create_file "CustomGemfile", <<-G - source "#{file_uri_for(gem_repo1)}" - gem "rack", "1.0.0" + system_gems(%w[myrack-1.0.0 myrack-0.9.1], path: default_bundle_path) + + gemfile "CustomGemfile", <<-G + source "https://gem.repo1" + gem "myrack", "1.0.0" G - bundle "exec --gemfile CustomGemfile rackup" + bundle "exec --gemfile CustomGemfile myrackup" expect(out).to eq("1.0.0") end it "activates the correct gem" do + system_gems(%w[myrack-1.0.0 myrack-0.9.1], path: default_bundle_path) + gemfile <<-G - source "#{file_uri_for(gem_repo1)}" - gem "rack", "0.9.1" + source "https://gem.repo1" + gem "myrack", "0.9.1" G - bundle "exec rackup" + bundle "exec myrackup" expect(out).to eq("0.9.1") end it "works and prints no warnings when HOME is not writable" do + system_gems(%w[myrack-1.0.0 myrack-0.9.1], path: default_bundle_path) + gemfile <<-G - source "#{file_uri_for(gem_repo1)}" - gem "rack", "0.9.1" + source "https://gem.repo1" + gem "myrack", "0.9.1" G - bundle "exec rackup", :env => { "HOME" => "/" } + bundle "exec myrackup", env: { "HOME" => "/" } expect(out).to eq("0.9.1") expect(err).to be_empty end it "works when the bins are in ~/.bundle" do install_gemfile <<-G - source "#{file_uri_for(gem_repo1)}" - gem "rack" + source "https://gem.repo1" + gem "myrack" G - bundle "exec rackup" + bundle "exec myrackup" expect(out).to eq("1.0.0") end it "works when running from a random directory" do install_gemfile <<-G - source "#{file_uri_for(gem_repo1)}" - gem "rack" + source "https://gem.repo1" + gem "myrack" G - bundle "exec 'cd #{tmp("gems")} && rackup'" + bundle "exec 'cd #{tmp("gems")} && myrackup'" expect(out).to eq("1.0.0") end it "works when exec'ing something else" do - install_gemfile "source \"#{file_uri_for(gem_repo1)}\"; gem \"rack\"" + install_gemfile "source \"https://gem.repo1\"; gem \"myrack\"" bundle "exec echo exec" expect(out).to eq("exec") end it "works when exec'ing to ruby" do - install_gemfile "source \"#{file_uri_for(gem_repo1)}\"; gem \"rack\"" + install_gemfile "source \"https://gem.repo1\"; gem \"myrack\"" bundle "exec ruby -e 'puts %{hi}'" expect(out).to eq("hi") end it "works when exec'ing to rubygems" do - install_gemfile "source \"#{file_uri_for(gem_repo1)}\"; gem \"rack\"" + install_gemfile "source \"https://gem.repo1\"; gem \"myrack\"" bundle "exec #{gem_cmd} --version" expect(out).to eq(Gem::VERSION) end it "works when exec'ing to rubygems through sh -c" do - install_gemfile "source \"#{file_uri_for(gem_repo1)}\"; gem \"rack\"" + install_gemfile "source \"https://gem.repo1\"; gem \"myrack\"" bundle "exec sh -c '#{gem_cmd} --version'" expect(out).to eq(Gem::VERSION) end - it "works when exec'ing back to bundler with a lockfile that doesn't include the current platform" do + it "works when exec'ing back to bundler to run a remote resolve" do install_gemfile <<-G - source "#{file_uri_for(gem_repo1)}" - gem "rack", "0.9.1" + source "https://gem.repo1" + gem "myrack", "0.9.1" G - # simulate lockfile generated with old version not including specific platform - lockfile <<-L - GEM - remote: #{file_uri_for(gem_repo1)}/ - specs: - rack (0.9.1) - - PLATFORMS - RUBY - - DEPENDENCIES - rack (= 0.9.1) + bundle "exec bundle lock", env: { "BUNDLER_VERSION" => Bundler::VERSION } - BUNDLED WITH - 2.1.4 - L - - bundle "exec bundle cache", :env => { "BUNDLER_VERSION" => Bundler::VERSION } - - expect(out).to include("Updating files in vendor/cache") + expect(out).to include("Writing lockfile") end it "respects custom process title when loading through ruby" do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? + skip "https://github.com/ruby/rubygems/issues/3351" if Gem.win_platform? script_that_changes_its_own_title_and_checks_if_picked_up_by_ps_unix_utility = <<~'RUBY' Process.setproctitle("1-2-3-4-5-6-7") puts `ps -ocommand= -p#{$$}` RUBY - create_file "Gemfile", "source \"#{file_uri_for(gem_repo1)}\"" + gemfile "Gemfile", "source \"https://gem.repo1\"" create_file "a.rb", script_that_changes_its_own_title_and_checks_if_picked_up_by_ps_unix_utility bundle "exec ruby a.rb" expect(out).to eq("1-2-3-4-5-6-7") end it "accepts --verbose" do - install_gemfile "source \"#{file_uri_for(gem_repo1)}\"; gem \"rack\"" + install_gemfile "source \"https://gem.repo1\"; gem \"myrack\"" bundle "exec --verbose echo foobar" expect(out).to eq("foobar") end it "passes --verbose to command if it is given after the command" do - install_gemfile "source \"#{file_uri_for(gem_repo1)}\"; gem \"rack\"" + install_gemfile "source \"https://gem.repo1\"; gem \"myrack\"" bundle "exec echo --verbose" expect(out).to eq("--verbose") end it "handles --keep-file-descriptors" do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? + skip "https://github.com/ruby/rubygems/issues/3351" if Gem.win_platform? require "tempfile" @@ -154,24 +138,24 @@ RSpec.describe "bundle exec" do end G - install_gemfile "source \"#{file_uri_for(gem_repo1)}\"" - sys_exec "#{Gem.ruby} #{command.path}" + install_gemfile "source \"https://gem.repo1\"" + in_bundled_app "#{Gem.ruby} #{command.path}" expect(out).to be_empty expect(err).to be_empty end it "accepts --keep-file-descriptors" do - install_gemfile "source \"#{file_uri_for(gem_repo1)}\"" + install_gemfile "source \"https://gem.repo1\"" bundle "exec --keep-file-descriptors echo foobar" expect(err).to be_empty end it "can run a command named --verbose" do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? + skip "https://github.com/ruby/rubygems/issues/3351" if Gem.win_platform? - install_gemfile "source \"#{file_uri_for(gem_repo1)}\"; gem \"rack\"" + install_gemfile "source \"https://gem.repo1\"; gem \"myrack\"" File.open(bundled_app("--verbose"), "w") do |f| f.puts "#!/bin/sh" f.puts "echo foobar" @@ -185,194 +169,185 @@ RSpec.describe "bundle exec" do it "handles different versions in different bundles" do build_repo2 do - build_gem "rack_two", "1.0.0" do |s| - s.executables = "rackup" + build_gem "myrack_two", "1.0.0" do |s| + s.executables = "myrackup" end end install_gemfile <<-G - source "#{file_uri_for(gem_repo1)}" - gem "rack", "0.9.1" + source "https://gem.repo1" + gem "myrack", "0.9.1" G - install_gemfile bundled_app2("Gemfile"), <<-G, :dir => bundled_app2 - source "#{file_uri_for(gem_repo2)}" - gem "rack_two", "1.0.0" + install_gemfile bundled_app2("Gemfile"), <<-G, dir: bundled_app2 + source "https://gem.repo2" + gem "myrack_two", "1.0.0" G - bundle "exec rackup" + bundle "exec myrackup" expect(out).to eq("0.9.1") - bundle "exec rackup", :dir => bundled_app2 + bundle "exec myrackup", dir: bundled_app2 expect(out).to eq("1.0.0") end context "with default gems" do - let(:system_gems_to_install) { [] } - - let(:default_irb_version) { ruby "gem 'irb', '< 999999'; require 'irb'; puts IRB::VERSION", :raise_on_error => false } + # TODO: Switch to ERB::VERSION once Ruby 3.4 support is dropped, so all + # supported rubies include an `erb` gem version where `ERB::VERSION` is + # public + let(:default_erb_version) { ruby "require 'erb/version'; puts ERB.const_get(:VERSION)" } context "when not specified in Gemfile" do before do - skip "irb isn't a default gem" if default_irb_version.empty? - - install_gemfile "source \"#{file_uri_for(gem_repo1)}\"" + install_gemfile "source \"https://gem.repo1\"" end it "uses version provided by ruby" do - bundle "exec irb --version" + bundle "exec erb --version" - expect(out).to include(default_irb_version) + expect(stdboth).to eq(default_erb_version) end end context "when specified in Gemfile directly" do - let(:specified_irb_version) { "0.9.6" } + let(:specified_erb_version) { "2.0.0" } before do - skip "irb isn't a default gem" if default_irb_version.empty? - build_repo2 do - build_gem "irb", specified_irb_version do |s| - s.executables = "irb" + build_gem "erb", specified_erb_version do |s| + s.executables = "erb" end end install_gemfile <<-G - source "#{file_uri_for(gem_repo2)}" - gem "irb", "#{specified_irb_version}" + source "https://gem.repo2" + gem "erb", "#{specified_erb_version}" G end it "uses version specified" do - bundle "exec irb --version" + bundle "exec erb --version" - expect(out).to eq(specified_irb_version) - expect(err).to be_empty + expect(stdboth).to eq(specified_erb_version) end end context "when specified in Gemfile indirectly" do - let(:indirect_irb_version) { "0.9.6" } + let(:indirect_erb_version) { "2.0.0" } before do - skip "irb isn't a default gem" if default_irb_version.empty? - build_repo2 do - build_gem "irb", indirect_irb_version do |s| - s.executables = "irb" + build_gem "erb", indirect_erb_version do |s| + s.executables = "erb" end - build_gem "gem_depending_on_old_irb" do |s| - s.add_dependency "irb", indirect_irb_version + build_gem "gem_depending_on_old_erb" do |s| + s.add_dependency "erb", indirect_erb_version end end install_gemfile <<-G - source "#{file_uri_for(gem_repo2)}" - gem "gem_depending_on_old_irb" + source "https://gem.repo2" + gem "gem_depending_on_old_erb" G - - bundle "exec irb --version" end it "uses resolved version" do - expect(out).to eq(indirect_irb_version) - expect(err).to be_empty + bundle "exec erb --version" + + expect(stdboth).to eq(indirect_erb_version) end end end it "warns about executable conflicts" do build_repo2 do - build_gem "rack_two", "1.0.0" do |s| - s.executables = "rackup" + build_gem "myrack_two", "1.0.0" do |s| + s.executables = "myrackup" end end - bundle "config set path.system true" + bundle_config_global "path.system true" install_gemfile <<-G - source "#{file_uri_for(gem_repo1)}" - gem "rack", "0.9.1" + source "https://gem.repo1" + gem "myrack", "0.9.1" G - install_gemfile bundled_app2("Gemfile"), <<-G, :dir => bundled_app2 - source "#{file_uri_for(gem_repo2)}" - gem "rack_two", "1.0.0" + install_gemfile bundled_app2("Gemfile"), <<-G, dir: bundled_app2 + source "https://gem.repo2" + gem "myrack_two", "1.0.0" G - bundle "exec rackup" + bundle "exec myrackup" expect(last_command.stderr).to eq( - "Bundler is using a binstub that was created for a different gem (rack).\n" \ - "You should run `bundle binstub rack_two` to work around a system/bundle conflict." + "Bundler is using a binstub that was created for a different gem (myrack).\n" \ + "You should run `bundle binstub myrack_two` to work around a system/bundle conflict." ) end it "handles gems installed with --without" do - bundle "config set --local without middleware" + bundle_config "without middleware" install_gemfile <<-G - source "#{file_uri_for(gem_repo1)}" - gem "rack" # rack 0.9.1 and 1.0 exist + source "https://gem.repo1" + gem "myrack" # myrack 0.9.1 and 1.0 exist group :middleware do - gem "rack_middleware" # rack_middleware depends on rack 0.9.1 + gem "myrack_middleware" # myrack_middleware depends on myrack 0.9.1 end G - bundle "exec rackup" + bundle "exec myrackup" expect(out).to eq("0.9.1") - expect(the_bundle).not_to include_gems "rack_middleware 1.0" + expect(the_bundle).not_to include_gems "myrack_middleware 1.0" end it "does not duplicate already exec'ed RUBYOPT" do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? - + create_file("echoopt", "#!/usr/bin/env ruby\nprint ENV['RUBYOPT']") install_gemfile <<-G - source "#{file_uri_for(gem_repo1)}" - gem "rack" + source "https://gem.repo1" + gem "myrack" G bundler_setup_opt = "-r#{lib_dir}/bundler/setup" rubyopt = opt_add(bundler_setup_opt, ENV["RUBYOPT"]) - bundle "exec 'echo $RUBYOPT'" + bundle "exec echoopt" expect(out.split(" ").count(bundler_setup_opt)).to eq(1) - bundle "exec 'echo $RUBYOPT'", :env => { "RUBYOPT" => rubyopt } + bundle "exec echoopt", env: { "RUBYOPT" => rubyopt } expect(out.split(" ").count(bundler_setup_opt)).to eq(1) end it "does not duplicate already exec'ed RUBYLIB" do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? - + create_file("echolib", "#!/usr/bin/env ruby\nprint ENV['RUBYLIB']") install_gemfile <<-G - source "#{file_uri_for(gem_repo1)}" - gem "rack" + source "https://gem.repo1" + gem "myrack" G rubylib = ENV["RUBYLIB"] rubylib = rubylib.to_s.split(File::PATH_SEPARATOR).unshift lib_dir.to_s rubylib = rubylib.uniq.join(File::PATH_SEPARATOR) - bundle "exec 'echo $RUBYLIB'" + bundle "exec echolib" expect(out).to include(rubylib) - bundle "exec 'echo $RUBYLIB'", :env => { "RUBYLIB" => rubylib } + bundle "exec echolib", env: { "RUBYLIB" => rubylib } expect(out).to include(rubylib) end it "errors nicely when the argument doesn't exist" do install_gemfile <<-G - source "#{file_uri_for(gem_repo1)}" - gem "rack" + source "https://gem.repo1" + gem "myrack" G - bundle "exec foobarbaz", :raise_on_error => false + bundle "exec foobarbaz", raise_on_error: false expect(exitstatus).to eq(127) expect(err).to include("bundler: command not found: foobarbaz") expect(err).to include("Install missing gem executables with `bundle install`") @@ -380,37 +355,39 @@ RSpec.describe "bundle exec" do it "errors nicely when the argument is not executable" do install_gemfile <<-G - source "#{file_uri_for(gem_repo1)}" - gem "rack" + source "https://gem.repo1" + gem "myrack" G - bundle "exec touch foo" - bundle "exec ./foo", :raise_on_error => false + bundled_app("foo").write("") + bundle "exec ./foo", raise_on_error: false expect(exitstatus).to eq(126) expect(err).to include("bundler: not executable: ./foo") end it "errors nicely when no arguments are passed" do install_gemfile <<-G - source "#{file_uri_for(gem_repo1)}" - gem "rack" + source "https://gem.repo1" + gem "myrack" G - bundle "exec", :raise_on_error => false + bundle "exec", raise_on_error: false expect(exitstatus).to eq(128) expect(err).to include("bundler: exec needs a command to run") end it "raises a helpful error when exec'ing to something outside of the bundle" do - bundle "config set clean false" # want to keep the rackup binstub + system_gems(%w[myrack-1.0.0 myrack-0.9.1], path: default_bundle_path) + + bundle_config "clean false" # want to keep the myrackup binstub install_gemfile <<-G - source "#{file_uri_for(gem_repo1)}" + source "https://gem.repo1" gem "foo" G [true, false].each do |l| - bundle "config set disable_exec_load #{l}" - bundle "exec rackup", :raise_on_error => false - expect(err).to include "can't find executable rackup for gem rack. rack is not currently included in the bundle, perhaps you meant to add it to your Gemfile?" + bundle_config "disable_exec_load #{l}" + bundle "exec myrackup", raise_on_error: false + expect(err).to include "can't find executable myrackup for gem myrack. myrack is not currently included in the bundle, perhaps you meant to add it to your Gemfile?" end end @@ -421,18 +398,15 @@ RSpec.describe "bundle exec" do each_prefix.call("exec") do |exec| describe "when #{exec} is used" do before(:each) do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? - install_gemfile <<-G - source "#{file_uri_for(gem_repo1)}" - gem "rack" + source "https://gem.repo1" + gem "myrack" G create_file("print_args", <<-'RUBY') #!/usr/bin/env ruby puts "args: #{ARGV.inspect}" RUBY - bundled_app("print_args").chmod(0o755) end it "shows executable's man page when --help is after the executable" do @@ -453,6 +427,7 @@ RSpec.describe "bundle exec" do it "shows executable's man page when the executable has a -" do FileUtils.mv(bundled_app("print_args"), bundled_app("docker-template")) + FileUtils.mv(bundled_app("print_args.bat"), bundled_app("docker-template.bat")) if Gem.win_platform? bundle "#{exec} docker-template build discourse --help" expect(out).to eq('args: ["build", "discourse", "--help"]') end @@ -469,7 +444,7 @@ RSpec.describe "bundle exec" do it "shows bundle-exec's man page when --help is between exec and the executable" do with_fake_man do - bundle "#{exec} --help cat" + bundle "#{exec} --help echo" end expect(out).to include(%(["#{man_dir}/bundle-exec.1"])) end @@ -509,31 +484,31 @@ RSpec.describe "bundle exec" do describe "run from a random directory" do before(:each) do install_gemfile <<-G - source "#{file_uri_for(gem_repo1)}" - gem "rack" + source "https://gem.repo1" + gem "myrack" G end it "works when unlocked" do - bundle "exec 'cd #{tmp("gems")} && rackup'" + bundle "exec 'cd #{tmp("gems")} && myrackup'" expect(out).to eq("1.0.0") end it "works when locked" do expect(the_bundle).to be_locked - bundle "exec 'cd #{tmp("gems")} && rackup'" + bundle "exec 'cd #{tmp("gems")} && myrackup'" expect(out).to eq("1.0.0") end end describe "from gems bundled via :path" do before(:each) do - build_lib "fizz", :path => home("fizz") do |s| + build_lib "fizz", path: home("fizz") do |s| s.executables = "fizz" end install_gemfile <<-G - source "#{file_uri_for(gem_repo1)}" + source "https://gem.repo1" gem "fizz", :path => "#{File.expand_path(home("fizz"))}" G end @@ -558,7 +533,7 @@ RSpec.describe "bundle exec" do end install_gemfile <<-G - source "#{file_uri_for(gem_repo1)}" + source "https://gem.repo1" gem "fizz_git", :git => "#{lib_path("fizz_git-1.0")}" G end @@ -577,12 +552,12 @@ RSpec.describe "bundle exec" do describe "from gems bundled via :git with no gemspec" do before(:each) do - build_git "fizz_no_gemspec", :gemspec => false do |s| + build_git "fizz_no_gemspec", gemspec: false do |s| s.executables = "fizz_no_gemspec" end install_gemfile <<-G - source "#{file_uri_for(gem_repo1)}" + source "https://gem.repo1" gem "fizz_no_gemspec", "1.0", :git => "#{lib_path("fizz_no_gemspec-1.0")}" G end @@ -602,16 +577,59 @@ RSpec.describe "bundle exec" do it "performs an automatic bundle install" do gemfile <<-G - source "#{file_uri_for(gem_repo1)}" - gem "rack", "0.9.1" + source "https://gem.repo1" + gem "myrack", "0.9.1" gem "foo" G - bundle "config set auto_install 1" - bundle "exec rackup" + bundle_config "auto_install 1" + bundle "exec myrackup", artifice: "compact_index" expect(out).to include("Installing foo 1.0") end + it "performs an automatic bundle install with git gems" do + build_git "foo" do |s| + s.executables = "foo" + end + gemfile <<-G + source "https://gem.repo1" + gem "myrack", "0.9.1" + gem "foo", :git => "#{lib_path("foo-1.0")}" + G + + bundle_config "auto_install 1" + bundle "exec foo", artifice: "compact_index" + expect(out).to include("Fetching myrack 0.9.1") + expect(out).to include("Fetching #{lib_path("foo-1.0")}") + expect(out.lines).to end_with("1.0") + end + + it "loads the correct optparse when `auto_install` is set, and optparse is a dependency" do + build_repo4 do + build_gem "fastlane", "2.192.0" do |s| + s.executables = "fastlane" + s.add_dependency "optparse", "~> 999.999.999" + end + + build_gem "optparse", "999.999.998" + build_gem "optparse", "999.999.999" + end + + system_gems "optparse-999.999.998", gem_repo: gem_repo4 + + bundle_config "auto_install 1" + bundle_config "path vendor/bundle" + + gemfile <<~G + source "https://gem.repo4" + gem "fastlane" + G + + bundle "exec fastlane", artifice: "compact_index" + expect(out).to include("Installing optparse 999.999.999") + expect(out).to include("2.192.0") + end + describe "with gems bundled via :path with invalid gemspecs" do it "outputs the gemspec validation errors" do build_lib "foo" @@ -624,28 +642,27 @@ RSpec.describe "bundle exec" do s.version = '1.0' s.summary = 'TODO: Add summary' s.authors = 'Me' + s.rubygems_version = nil end G end gemfile <<-G - source "#{file_uri_for(gem_repo1)}" + source "https://gem.repo1" gem "foo", :path => "#{lib_path("foo-1.0")}" G - bundle "exec irb", :raise_on_error => false + bundle "exec erb", raise_on_error: false expect(err).to match("The gemspec at #{lib_path("foo-1.0").join("foo.gemspec")} is not valid") - expect(err).to match('"TODO" is not a summary') + expect(err).to match(/missing value for attribute rubygems_version|rubygems_version must not be nil/) end end describe "with gems bundled for deployment" do it "works when calling bundler from another script" do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? - gemfile <<-G - source "#{file_uri_for(gem_repo1)}" + source "https://gem.repo1" module Monkey def bin_path(a,b,c) @@ -654,54 +671,97 @@ RSpec.describe "bundle exec" do end Bundler.rubygems.extend(Monkey) G - bundle "config set path.system true" + bundle_config "path.system true" bundle "install" - bundle "exec ruby -e '`bundle -v`; puts $?.success?'", :env => { "BUNDLER_VERSION" => Bundler::VERSION } + bundle "exec ruby -e '`bundle -v`; puts $?.success?'", env: { "BUNDLER_VERSION" => Bundler::VERSION } expect(out).to match("true") end end + describe "bundle exec gem uninstall" do + before do + build_repo4 do + build_gem "foo" + end + + install_gemfile <<-G + source "https://gem.repo4" + + gem "foo" + G + end + + it "works" do + bundle "exec #{gem_cmd} uninstall foo" + expect(out).to eq("Successfully uninstalled foo-1.0") + end + end + + describe "running gem commands in presence of rubygems plugins" do + before do + build_repo4 do + build_gem "foo" do |s| + s.write "lib/rubygems_plugin.rb", "puts 'FAIL'" + end + end + + system_gems "foo-1.0", path: default_bundle_path, gem_repo: gem_repo4 + + install_gemfile <<-G + source "https://gem.repo4" + G + end + + it "does not load plugins outside of the bundle" do + bundle "exec #{gem_cmd} -v" + expect(out).not_to include("FAIL") + end + end + context "`load`ing a ruby file instead of `exec`ing" do let(:path) { bundled_app("ruby_executable") } let(:shebang) { "#!/usr/bin/env ruby" } - let(:executable) { <<-RUBY.gsub(/^ */, "").strip } + let(:executable) { <<~RUBY.strip } #{shebang} - require "rack" + require "myrack" puts "EXEC: \#{caller.grep(/load/).empty? ? 'exec' : 'load'}" puts "ARGS: \#{$0} \#{ARGV.join(' ')}" - puts "RACK: \#{RACK}" - process_title = `ps -o args -p \#{Process.pid}`.split("\n", 2).last.strip + puts "MYRACK: \#{MYRACK}" + if Gem.win_platform? + process_title = "ruby" + else + process_title = `ps -o args -p \#{Process.pid}`.split("\n", 2).last.strip + end puts "PROCESS: \#{process_title}" RUBY before do - bundled_app(path).open("w") {|f| f << executable } - bundled_app(path).chmod(0o755) + create_file(bundled_app(path), executable) install_gemfile <<-G - source "#{file_uri_for(gem_repo1)}" - gem "rack" + source "https://gem.repo1" + gem "myrack" G end let(:exec) { "EXEC: load" } let(:args) { "ARGS: #{path} arg1 arg2" } - let(:rack) { "RACK: 1.0.0" } + let(:myrack) { "MYRACK: 1.0.0" } let(:process) do - title = "PROCESS: #{path}" - title += " arg1 arg2" - title + if Gem.win_platform? + "PROCESS: ruby" + else + "PROCESS: #{path} arg1 arg2" + end end let(:exit_code) { 0 } - let(:expected) { [exec, args, rack, process].join("\n") } + let(:expected) { [exec, args, myrack, process].join("\n") } let(:expected_err) { "" } - subject { bundle "exec #{path} arg1 arg2", :raise_on_error => false } + subject { bundle "exec #{path} arg1 arg2", raise_on_error: false } it "runs" do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? - subject expect(exitstatus).to eq(exit_code) expect(err).to eq(expected_err) @@ -713,8 +773,6 @@ RSpec.describe "bundle exec" do context "with exit 0" do it "runs" do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? - subject expect(exitstatus).to eq(exit_code) expect(err).to eq(expected_err) @@ -726,8 +784,6 @@ RSpec.describe "bundle exec" do let(:exit_code) { 99 } it "runs" do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? - subject expect(exitstatus).to eq(exit_code) expect(err).to eq(expected_err) @@ -745,13 +801,11 @@ RSpec.describe "bundle exec" do end let(:expected_err) { "" } let(:exit_code) do - # signal mask 128 + plus signal 15 -> TERM - # this is specified by C99 - 128 + 15 + exit_status_for_signal(Signal.list["TERM"]) end it "runs" do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? + skip "https://github.com/ruby/rubygems/issues/3351" if Gem.win_platform? subject expect(exitstatus).to eq(exit_code) @@ -768,7 +822,12 @@ RSpec.describe "bundle exec" do let(:expected) { "" } it "runs" do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? + # it's empty, so `create_file` won't add executable permission and bat scripts on Windows + bundled_app(path).chmod(0o755) + path.sub_ext(".bat").write <<~SCRIPT if Gem.win_platform? + @ECHO OFF + @"ruby.exe" "%~dpn0" %* + SCRIPT subject expect(exitstatus).to eq(exit_code) @@ -781,16 +840,13 @@ RSpec.describe "bundle exec" do let(:executable) { super() << "\nraise 'ERROR'" } let(:exit_code) { 1 } let(:expected_err) do - "bundler: failed to load command: #{path} (#{path})" \ - "\n#{path}:10:in `<top (required)>': ERROR (RuntimeError)" + /\Abundler: failed to load command: #{Regexp.quote(path.to_s)} \(#{Regexp.quote(path.to_s)}\)\n#{Regexp.quote(path.to_s)}:[0-9]+:in [`']<top \(required\)>': ERROR \(RuntimeError\)/ end it "runs like a normally executed executable" do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? - subject expect(exitstatus).to eq(exit_code) - expect(err).to start_with(expected_err) + expect(err).to match(expected_err) expect(out).to eq(expected) end end @@ -802,8 +858,6 @@ RSpec.describe "bundle exec" do let(:expected) { super() } it "runs" do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? - subject expect(exitstatus).to eq(exit_code) expect(err).to eq(expected_err) @@ -815,8 +869,6 @@ RSpec.describe "bundle exec" do let(:shebang) { "#!#{Gem.ruby}" } it "runs" do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? - subject expect(exitstatus).to eq(exit_code) expect(err).to eq(expected_err) @@ -824,26 +876,29 @@ RSpec.describe "bundle exec" do end end - context "when Bundler.setup fails", :bundler => "< 3" do + context "when Bundler.setup fails" do before do + system_gems(%w[myrack-1.0.0 myrack-0.9.1], path: default_bundle_path) + gemfile <<-G - source "#{file_uri_for(gem_repo1)}" - gem 'rack', '2' + source "https://gem.repo1" + gem 'myrack', '2' G ENV["BUNDLER_FORCE_TTY"] = "true" end let(:exit_code) { Bundler::GemNotFound.new.status_code } let(:expected) { "" } - let(:expected_err) { <<-EOS.strip } -Could not find gem 'rack (= 2)' in locally installed gems. -The source contains the following versions of 'rack': 0.9.1, 1.0.0 -Run `bundle install` to install missing gems. + let(:expected_err) { <<~EOS.strip } + Could not find gem 'myrack (= 2)' in locally installed gems. + + The source contains the following gems matching 'myrack': + * myrack-0.9.1 + * myrack-1.0.0 + Run `bundle install` to install missing gems. EOS it "runs" do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? - subject expect(exitstatus).to eq(exit_code) expect(err).to eq(expected_err) @@ -851,29 +906,24 @@ Run `bundle install` to install missing gems. end end - context "when Bundler.setup fails", :bundler => "3" do + context "when Bundler.setup fails and Gemfile is not the default" do before do - gemfile <<-G - source "#{file_uri_for(gem_repo1)}" - gem 'rack', '2' + gemfile "CustomGemfile", <<-G + source "https://gem.repo1" + gem 'myrack', '2' G ENV["BUNDLER_FORCE_TTY"] = "true" + ENV["BUNDLE_GEMFILE"] = "CustomGemfile" + ENV["BUNDLER_ORIG_BUNDLE_GEMFILE"] = nil end let(:exit_code) { Bundler::GemNotFound.new.status_code } let(:expected) { "" } - let(:expected_err) { <<-EOS.strip } -Could not find gem 'rack (= 2)' in locally installed gems. -The source contains the following versions of 'rack': 1.0.0 -Run `bundle install` to install missing gems. - EOS - - it "runs" do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? + it "prints proper suggestion" do subject expect(exitstatus).to eq(exit_code) - expect(err).to eq(expected_err) + expect(err).to include("Run `bundle install --gemfile CustomGemfile` to install missing gems.") expect(out).to eq(expected) end end @@ -883,8 +933,6 @@ Run `bundle install` to install missing gems. let(:exit_code) { 1 } it "runs" do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? - subject expect(exitstatus).to eq(exit_code) expect(err).to eq(expected_err) @@ -894,15 +942,19 @@ Run `bundle install` to install missing gems. context "when disable_exec_load is set" do let(:exec) { "EXEC: exec" } - let(:process) { "PROCESS: ruby #{path} arg1 arg2" } + let(:process) do + if Gem.win_platform? + "PROCESS: ruby" + else + "PROCESS: ruby #{path} arg1 arg2" + end + end before do - bundle "config set disable_exec_load true" + bundle_config "disable_exec_load true" end it "runs" do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? - subject expect(exitstatus).to eq(exit_code) expect(err).to eq(expected_err) @@ -917,27 +969,30 @@ Run `bundle install` to install missing gems. puts "__FILE__: #{__FILE__.inspect}" RUBY - let(:expected) { super() + <<-EOS.chomp } + context "when the path is absolute" do + let(:expected) { super() + <<~EOS.chomp } -$0: #{path.to_s.inspect} -__FILE__: #{path.to_s.inspect} - EOS + $0: #{path.to_s.inspect} + __FILE__: #{path.to_s.inspect} + EOS - it "runs" do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? - - subject - expect(exitstatus).to eq(exit_code) - expect(err).to eq(expected_err) - expect(out).to eq(expected) + it "runs" do + subject + expect(exitstatus).to eq(exit_code) + expect(err).to eq(expected_err) + expect(out).to eq(expected) + end end context "when the path is relative" do let(:path) { super().relative_path_from(bundled_app) } + let(:expected) { super() + <<~EOS.chomp } - it "runs" do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? + $0: #{path.to_s.inspect} + __FILE__: #{path.to_s.inspect} + EOS + it "runs" do subject expect(exitstatus).to eq(exit_code) expect(err).to eq(expected_err) @@ -947,29 +1002,39 @@ __FILE__: #{path.to_s.inspect} context "when the path is relative with a leading ./" do let(:path) { Pathname.new("./#{super().relative_path_from(bundled_app)}") } + let(:expected) { super() + <<~EOS.chomp } - pending "relative paths with ./ have absolute __FILE__" + $0: #{path.to_s.inspect} + __FILE__: #{File.expand_path(path, bundled_app).inspect} + EOS + + it "runs" do + subject + expect(exitstatus).to eq(exit_code) + expect(err).to eq(expected_err) + expect(out).to eq(expected) + end end end context "signal handling" do let(:test_signals) do open3_reserved_signals = %w[CHLD CLD PIPE] - reserved_signals = %w[SEGV BUS ILL FPE VTALRM KILL STOP EXIT] + reserved_signals = %w[SEGV BUS ILL FPE ABRT IOT VTALRM KILL STOP EXIT] bundler_signals = %w[INT] Signal.list.keys - (bundler_signals + reserved_signals + open3_reserved_signals) end context "signals being trapped by bundler" do - let(:executable) { strip_whitespace <<-RUBY } + let(:executable) { <<~RUBY } #{shebang} begin Thread.new do puts 'Started' # For process sync STDOUT.flush sleep 1 # ignore quality_spec - raise "Didn't receive INT at all" + raise RuntimeError, "Didn't receive expected INT" end.join rescue Interrupt puts "foo" @@ -977,7 +1042,7 @@ __FILE__: #{path.to_s.inspect} RUBY it "receives the signal" do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? + skip "https://github.com/ruby/rubygems/issues/3351" if Gem.win_platform? bundle("exec #{path}") do |_, o, thr| o.gets # Consumes 'Started' and ensures that thread has started @@ -989,7 +1054,7 @@ __FILE__: #{path.to_s.inspect} end context "signals not being trapped by bunder" do - let(:executable) { strip_whitespace <<-RUBY } + let(:executable) { <<~RUBY } #{shebang} signals = #{test_signals.inspect} @@ -1000,7 +1065,7 @@ __FILE__: #{path.to_s.inspect} RUBY it "makes sure no unexpected signals are restored to DEFAULT" do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? + skip "https://github.com/ruby/rubygems/issues/3351" if Gem.win_platform? test_signals.each do |n| Signal.trap(n, "IGNORE") @@ -1017,13 +1082,13 @@ __FILE__: #{path.to_s.inspect} context "nested bundle exec" do context "when bundle in a local path" do before do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? + skip "https://github.com/ruby/rubygems/issues/3351" if Gem.win_platform? gemfile <<-G - source "#{file_uri_for(gem_repo1)}" - gem "rack" + source "https://gem.repo1" + gem "myrack" G - bundle "config set path vendor/bundler" + bundle_config "path vendor/bundler" bundle :install end @@ -1034,16 +1099,16 @@ __FILE__: #{path.to_s.inspect} puts `bundle exec echo foo` RUBY file.chmod(0o777) - bundle "exec #{file}", :env => { "PATH" => path } + bundle "exec #{file}", env: { "PATH" => path } expect(out).to eq("foo") end end context "when Kernel.require uses extra monkeypatches" do before do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? + skip "https://github.com/ruby/rubygems/issues/3351" if Gem.win_platform? - install_gemfile "source \"#{file_uri_for(gem_repo1)}\"" + install_gemfile "source \"https://gem.repo1\"" end it "does not undo the monkeypatches" do @@ -1084,27 +1149,25 @@ __FILE__: #{path.to_s.inspect} context "when gemfile and path are configured", :ruby_repo do before do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? - build_repo2 do build_gem "rails", "6.1.0" do |s| s.executables = "rails" end end - bundle "config set path vendor/bundle" - bundle "config set gemfile gemfiles/rack_6_1.gemfile" + bundle_config "path vendor/bundle" + bundle_config "gemfile gemfiles/myrack_6_1.gemfile" - create_file(bundled_app("gemfiles/rack_6_1.gemfile"), <<~RUBY) - source "#{file_uri_for(gem_repo2)}" + gemfile(bundled_app("gemfiles/myrack_6_1.gemfile"), <<~RUBY) + source "https://gem.repo2" gem "rails", "6.1.0" RUBY # A Gemfile needs to be in the root to trick bundler's root resolution - create_file(bundled_app("Gemfile"), "source \"#{file_uri_for(gem_repo1)}\"") + gemfile "source 'https://gem.repo1'" - bundle "install" + bundle "install", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo2.to_s } end it "can still find gems after a nested subprocess" do @@ -1123,27 +1186,44 @@ __FILE__: #{path.to_s.inspect} expect(err).to be_empty expect(out).to eq("6.1.0") end + + it "can still find gems after a nested subprocess when using bundler (with a final r) executable" do + script = bundled_app("bin/myscript") + + create_file(script, <<~RUBY) + #!#{Gem.ruby} + + puts `bundler exec rails` + RUBY + + script.chmod(0o777) + + bundle "exec #{script}" + + expect(err).to be_empty + expect(out).to eq("6.1.0") + end end context "with a system gem that shadows a default gem" do let(:openssl_version) { "99.9.9" } - let(:expected) { ruby "gem 'openssl', '< 999999'; require 'openssl'; puts OpenSSL::VERSION", :artifice => nil, :raise_on_error => false } it "only leaves the default gem in the stdlib available" do - skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? - skip "openssl isn't a default gem" if expected.empty? + default_openssl_version = ruby "require 'openssl'; puts OpenSSL::VERSION" + + skip "https://github.com/ruby/rubygems/issues/3351" if Gem.win_platform? - install_gemfile "source \"#{file_uri_for(gem_repo1)}\"" # must happen before installing the broken system gem + install_gemfile "source \"https://gem.repo1\"" # must happen before installing the broken system gem build_repo4 do build_gem "openssl", openssl_version do |s| s.write("lib/openssl.rb", <<-RUBY) - raise "custom openssl should not be loaded, it's not in the gemfile!" + raise ArgumentError, "custom openssl should not be loaded" RUBY end end - system_gems("openssl-#{openssl_version}", :gem_repo => gem_repo4) + system_gems("openssl-#{openssl_version}", gem_repo: gem_repo4) file = bundled_app("require_openssl.rb") create_file(file, <<-RUBY) @@ -1156,15 +1236,15 @@ __FILE__: #{path.to_s.inspect} env = { "PATH" => path } aggregate_failures do - expect(bundle("exec #{file}", :artifice => nil, :env => env)).to eq(expected) - expect(bundle("exec bundle exec #{file}", :artifice => nil, :env => env)).to eq(expected) - expect(bundle("exec ruby #{file}", :artifice => nil, :env => env)).to eq(expected) - expect(run(file.read, :artifice => nil, :env => env)).to eq(expected) + expect(bundle("exec #{file}", env: env)).to eq(default_openssl_version) + expect(bundle("exec bundle exec #{file}", env: env)).to eq(default_openssl_version) + expect(bundle("exec ruby #{file}", env: env)).to eq(default_openssl_version) + expect(run(file.read, artifice: nil, env: env)).to eq(default_openssl_version) end skip "ruby_core has openssl and rubygems in the same folder, and this test needs rubygems require but default openssl not in a directly added entry in $LOAD_PATH" if ruby_core? # sanity check that we get the newer, custom version without bundler - sys_exec "#{Gem.ruby} #{file}", :env => env, :raise_on_error => false + sys_exec "#{Gem.ruby} #{file}", env: env, raise_on_error: false expect(err).to include("custom openssl should not be loaded") end end @@ -1172,9 +1252,9 @@ __FILE__: #{path.to_s.inspect} context "with a git gem that includes extensions", :ruby_repo do before do build_git "simple_git_binary", &:add_c_extension - bundle "config set --local path .bundle" + bundle_config "path .bundle" install_gemfile <<-G - source "#{file_uri_for(gem_repo1)}" + source "https://gem.repo1" gem "simple_git_binary", :git => '#{lib_path("simple_git_binary-1.0")}' G end @@ -1184,7 +1264,7 @@ __FILE__: #{path.to_s.inspect} end it "allows calling bundle install after removing gem.build_complete" do - FileUtils.rm_rf Dir[bundled_app(".bundle/**/gem.build_complete")] + FileUtils.rm_r Dir[bundled_app(".bundle/**/gem.build_complete")] bundle "exec #{Gem.ruby} -S bundle install" end end |
