diff options
Diffstat (limited to 'spec/bundler/runtime')
-rw-r--r-- | spec/bundler/runtime/executable_spec.rb | 22 | ||||
-rw-r--r-- | spec/bundler/runtime/gem_tasks_spec.rb | 16 | ||||
-rw-r--r-- | spec/bundler/runtime/inline_spec.rb | 299 | ||||
-rw-r--r-- | spec/bundler/runtime/load_spec.rb | 2 | ||||
-rw-r--r-- | spec/bundler/runtime/platform_spec.rb | 196 | ||||
-rw-r--r-- | spec/bundler/runtime/require_spec.rb | 14 | ||||
-rw-r--r-- | spec/bundler/runtime/requiring_spec.rb | 15 | ||||
-rw-r--r-- | spec/bundler/runtime/self_management_spec.rb | 159 | ||||
-rw-r--r-- | spec/bundler/runtime/setup_spec.rb | 322 | ||||
-rw-r--r-- | spec/bundler/runtime/with_unbundled_env_spec.rb | 22 |
10 files changed, 868 insertions, 199 deletions
diff --git a/spec/bundler/runtime/executable_spec.rb b/spec/bundler/runtime/executable_spec.rb index a11f547648..36ce6dcf67 100644 --- a/spec/bundler/runtime/executable_spec.rb +++ b/spec/bundler/runtime/executable_spec.rb @@ -11,7 +11,7 @@ RSpec.describe "Running bin/* commands" do it "runs the bundled command when in the bundle" do bundle "binstubs rack" - build_gem "rack", "2.0", :to_system => true do |s| + build_gem "rack", "2.0", to_system: true do |s| s.executables = "rackup" end @@ -20,7 +20,7 @@ RSpec.describe "Running bin/* commands" do end it "allows the location of the gem stubs to be specified" do - bundle "binstubs rack", :path => "gbin" + bundle "binstubs rack", path: "gbin" expect(bundled_app("bin")).not_to exist expect(bundled_app("gbin/rackup")).to exist @@ -30,7 +30,7 @@ RSpec.describe "Running bin/* commands" do end it "allows absolute paths as a specification of where to install bin stubs" do - bundle "binstubs rack", :path => tmp("bin") + bundle "binstubs rack", path: tmp("bin") gembin tmp("bin/rackup") expect(out).to eq("1.0.0") @@ -42,23 +42,23 @@ RSpec.describe "Running bin/* commands" do end it "allows the name of the shebang executable to be specified" do - bundle "binstubs rack", :shebang => "ruby-foo" + bundle "binstubs rack", shebang: "ruby-foo" expect(File.readlines(bundled_app("bin/rackup")).first).to eq("#!/usr/bin/env ruby-foo\n") end it "runs the bundled command when out of the bundle" do bundle "binstubs rack" - build_gem "rack", "2.0", :to_system => true do |s| + build_gem "rack", "2.0", to_system: true do |s| s.executables = "rackup" end - gembin "rackup", :dir => tmp + gembin "rackup", dir: tmp expect(out).to eq("1.0.0") end it "works with gems in path" do - build_lib "rack", :path => lib_path("rack") do |s| + build_lib "rack", path: lib_path("rack") do |s| s.executables = "rackup" end @@ -69,7 +69,7 @@ RSpec.describe "Running bin/* commands" do bundle "binstubs rack" - build_gem "rack", "2.0", :to_system => true do |s| + build_gem "rack", "2.0", to_system: true do |s| s.executables = "rackup" end @@ -94,7 +94,7 @@ RSpec.describe "Running bin/* commands" do expect(bundled_app("bin/rackup")).not_to exist end - it "allows you to stop installing binstubs", :bundler => "< 3" do + it "allows you to stop installing binstubs", bundler: "< 3" do skip "delete permission error" if Gem.win_platform? bundle "install --binstubs bin/" @@ -107,13 +107,13 @@ RSpec.describe "Running bin/* commands" do expect(out).to include("You have not configured a value for `bin`") end - it "remembers that the option was specified", :bundler => "< 3" do + it "remembers that the option was specified", bundler: "< 3" do gemfile <<-G source "#{file_uri_for(gem_repo1)}" gem "activesupport" G - bundle :install, :binstubs => "bin" + bundle :install, binstubs: "bin" gemfile <<-G source "#{file_uri_for(gem_repo1)}" diff --git a/spec/bundler/runtime/gem_tasks_spec.rb b/spec/bundler/runtime/gem_tasks_spec.rb index b0ef0cc144..f7afc0eb92 100644 --- a/spec/bundler/runtime/gem_tasks_spec.rb +++ b/spec/bundler/runtime/gem_tasks_spec.rb @@ -27,8 +27,8 @@ RSpec.describe "require 'bundler/gem_tasks'" do end it "includes the relevant tasks" do - with_gem_path_as(base_system_gems.to_s) do - sys_exec "#{rake} -T", :env => { "GEM_HOME" => system_gem_path.to_s } + with_gem_path_as(base_system_gem_path.to_s) do + sys_exec "#{rake} -T", env: { "GEM_HOME" => system_gem_path.to_s } end expect(err).to be_empty @@ -44,8 +44,8 @@ RSpec.describe "require 'bundler/gem_tasks'" do end it "defines a working `rake install` task", :ruby_repo do - with_gem_path_as(base_system_gems.to_s) do - sys_exec "#{rake} install", :env => { "GEM_HOME" => system_gem_path.to_s } + with_gem_path_as(base_system_gem_path.to_s) do + sys_exec "#{rake} install", env: { "GEM_HOME" => system_gem_path.to_s } end expect(err).to be_empty @@ -59,7 +59,7 @@ RSpec.describe "require 'bundler/gem_tasks'" do before do spaced_bundled_app = tmp.join("bundled app") FileUtils.cp_r bundled_app, spaced_bundled_app - bundle "exec rake build", :dir => spaced_bundled_app + bundle "exec rake build", dir: spaced_bundled_app end it "still runs successfully" do @@ -71,7 +71,7 @@ RSpec.describe "require 'bundler/gem_tasks'" do before do bracketed_bundled_app = tmp.join("bundled[app") FileUtils.cp_r bundled_app, bracketed_bundled_app - bundle "exec rake build", :dir => bracketed_bundled_app + bundle "exec rake build", dir: bracketed_bundled_app end it "still runs successfully" do @@ -98,8 +98,8 @@ RSpec.describe "require 'bundler/gem_tasks'" do end it "adds 'pkg' to rake/clean's CLOBBER" do - with_gem_path_as(base_system_gems.to_s) do - sys_exec %(#{rake} -e 'load "Rakefile"; puts CLOBBER.inspect'), :env => { "GEM_HOME" => system_gem_path.to_s } + with_gem_path_as(base_system_gem_path.to_s) do + sys_exec %(#{rake} -e 'load "Rakefile"; puts CLOBBER.inspect'), env: { "GEM_HOME" => system_gem_path.to_s } end expect(out).to eq '["pkg"]' end diff --git a/spec/bundler/runtime/inline_spec.rb b/spec/bundler/runtime/inline_spec.rb index 2ffadebf55..50a5258dc7 100644 --- a/spec/bundler/runtime/inline_spec.rb +++ b/spec/bundler/runtime/inline_spec.rb @@ -2,7 +2,7 @@ RSpec.describe "bundler/inline#gemfile" do def script(code, options = {}) - requires = ["#{entrypoint}/inline"] + requires = ["bundler/inline"] requires.unshift "#{spec_dir}/support/artifice/" + options.delete(:artifice) if options.key?(:artifice) requires = requires.map {|r| "require '#{r}'" }.join("\n") ruby("#{requires}\n\n" + code, options) @@ -28,7 +28,7 @@ RSpec.describe "bundler/inline#gemfile" do s.write "lib/four.rb", "puts 'four'" end - build_lib "five", "1.0.0", :no_default => true do |s| + build_lib "five", "1.0.0", no_default: true do |s| s.write "lib/mofive.rb", "puts 'five'" end @@ -57,7 +57,7 @@ RSpec.describe "bundler/inline#gemfile" do expect(out).to eq("two") - script <<-RUBY, :raise_on_error => false + script <<-RUBY, raise_on_error: false gemfile do source "#{file_uri_for(gem_repo1)}" path "#{lib_path}" do @@ -80,7 +80,7 @@ RSpec.describe "bundler/inline#gemfile" do expect(out).to include("Rack's post install message") - script <<-RUBY, :artifice => "endpoint" + script <<-RUBY, artifice: "endpoint" gemfile(true) do source "https://notaserver.com" gem "activesupport", :require => true @@ -89,19 +89,21 @@ RSpec.describe "bundler/inline#gemfile" do expect(out).to include("Installing activesupport") err_lines = err.split("\n") - err_lines.reject!{|line| line =~ /\.rb:\d+: warning: / } unless RUBY_VERSION < "2.7" + err_lines.reject! {|line| line =~ /\.rb:\d+: warning: / } expect(err_lines).to be_empty end it "lets me use my own ui object" do - script <<-RUBY, :artifice => "endpoint" - require '#{entrypoint}' - class MyBundlerUI < Bundler::UI::Silent + script <<-RUBY, artifice: "endpoint" + require 'bundler' + class MyBundlerUI < Bundler::UI::Shell def confirm(msg, newline = nil) puts "CONFIRMED!" end end - gemfile(true, :ui => MyBundlerUI.new) do + my_ui = MyBundlerUI.new + my_ui.level = "confirm" + gemfile(true, :ui => my_ui) do source "https://notaserver.com" gem "activesupport", :require => true end @@ -111,8 +113,8 @@ RSpec.describe "bundler/inline#gemfile" do end it "has an option for quiet installation" do - script <<-RUBY, :artifice => "endpoint" - require '#{entrypoint}/inline' + script <<-RUBY, artifice: "endpoint" + require 'bundler/inline' gemfile(true, :quiet => true) do source "https://notaserver.com" @@ -124,7 +126,7 @@ RSpec.describe "bundler/inline#gemfile" do end it "raises an exception if passed unknown arguments" do - script <<-RUBY, :raise_on_error => false + script <<-RUBY, raise_on_error: false gemfile(true, :arglebargle => true) do path "#{lib_path}" gem "two" @@ -138,7 +140,7 @@ RSpec.describe "bundler/inline#gemfile" do it "does not mutate the option argument" do script <<-RUBY - require '#{entrypoint}' + require 'bundler' options = { :ui => Bundler::UI::Shell.new } gemfile(false, options) do source "#{file_uri_for(gem_repo1)}" @@ -166,6 +168,54 @@ RSpec.describe "bundler/inline#gemfile" do expect(err).to be_empty end + it "installs subdependencies quietly if necessary when the install option is not set" do + build_repo4 do + build_gem "rack" do |s| + s.add_dependency "rackdep" + end + + build_gem "rackdep", "1.0.0" + end + + script <<-RUBY + gemfile do + source "#{file_uri_for(gem_repo4)}" + gem "rack" + end + + require "rackdep" + puts RACKDEP + RUBY + + expect(out).to eq("1.0.0") + expect(err).to be_empty + end + + it "installs subdependencies quietly if necessary when the install option is not set, and multiple sources used" do + build_repo4 do + build_gem "rack" do |s| + s.add_dependency "rackdep" + end + + build_gem "rackdep", "1.0.0" + end + + script <<-RUBY + gemfile do + source "#{file_uri_for(gem_repo1)}" + source "#{file_uri_for(gem_repo4)}" do + gem "rack" + end + end + + require "rackdep" + puts RACKDEP + RUBY + + expect(out).to eq("1.0.0") + expect(err).to be_empty + 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") @@ -205,6 +255,113 @@ RSpec.describe "bundler/inline#gemfile" do expect(err).to be_empty end + it "doesn't reinstall already installed gems" do + system_gems "rack-1.0.0" + + script <<-RUBY + require 'bundler' + ui = Bundler::UI::Shell.new + ui.level = "confirm" + + gemfile(true, ui: ui) do + source "#{file_uri_for(gem_repo1)}" + gem "activesupport" + gem "rack" + end + RUBY + + expect(out).to include("Installing activesupport") + expect(out).not_to include("Installing rack") + expect(err).to be_empty + end + + it "installs gems in later gemfile calls" do + system_gems "rack-1.0.0" + + script <<-RUBY + require 'bundler' + ui = Bundler::UI::Shell.new + ui.level = "confirm" + gemfile(true, ui: ui) do + source "#{file_uri_for(gem_repo1)}" + gem "rack" + end + + gemfile(true, ui: ui) do + source "#{file_uri_for(gem_repo1)}" + gem "activesupport" + end + RUBY + + expect(out).to include("Installing activesupport") + expect(out).not_to include("Installing rack") + expect(err).to be_empty + end + + it "doesn't reinstall already installed gems in later gemfile calls" do + system_gems "rack-1.0.0" + + script <<-RUBY + require 'bundler' + ui = Bundler::UI::Shell.new + ui.level = "confirm" + gemfile(true, ui: ui) do + source "#{file_uri_for(gem_repo1)}" + gem "activesupport" + end + + gemfile(true, ui: ui) do + source "#{file_uri_for(gem_repo1)}" + gem "rack" + end + RUBY + + expect(out).to include("Installing activesupport") + expect(out).not_to include("Installing rack") + expect(err).to be_empty + end + + it "installs gems with native extensions in later gemfile calls" do + system_gems "rack-1.0.0" + + build_git "foo" do |s| + s.add_dependency "rake" + s.extensions << "Rakefile" + s.write "Rakefile", <<-RUBY + task :default do + path = File.expand_path("lib", __dir__) + FileUtils.mkdir_p(path) + File.open("\#{path}/foo.rb", "w") do |f| + f.puts "FOO = 'YES'" + end + end + RUBY + end + + script <<-RUBY + require 'bundler' + ui = Bundler::UI::Shell.new + ui.level = "confirm" + gemfile(true, ui: ui) do + source "#{file_uri_for(gem_repo1)}" + gem "rack" + end + + gemfile(true, ui: ui) do + source "#{file_uri_for(gem_repo1)}" + gem "foo", :git => "#{lib_path("foo-1.0")}" + end + + require 'foo' + puts FOO + puts $:.grep(/ext/) + RUBY + + expect(out).to include("YES") + expect(out).to include(Pathname.glob(default_bundle_path("bundler/gems/extensions/**/foo-1.0-*")).first.to_s) + expect(err).to be_empty + end + it "installs inline gems when a Gemfile.lock is present" do gemfile <<-G source "https://notaserver.com" @@ -239,8 +396,42 @@ RSpec.describe "bundler/inline#gemfile" do expect(err).to be_empty end + it "does not leak Gemfile.lock versions to the installation output" 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 + #{Bundler::VERSION} + G + + script <<-RUBY + gemfile(true) do + source "#{file_uri_for(gem_repo1)}" + gem "rake", "#{rake_version}" + end + RUBY + + expect(out).to include("Installing rake #{rake_version}") + expect(out).not_to include("was 11.3.0") + expect(err).to be_empty + end + it "installs inline gems when frozen is set" do - script <<-RUBY, :env => { "BUNDLE_FROZEN" => "true" } + script <<-RUBY, env: { "BUNDLE_FROZEN" => "true" } gemfile do source "#{file_uri_for(gem_repo1)}" gem "rack" @@ -253,7 +444,7 @@ RSpec.describe "bundler/inline#gemfile" do end it "installs inline gems when deployment is set" do - script <<-RUBY, :env => { "BUNDLE_DEPLOYMENT" => "true" } + script <<-RUBY, env: { "BUNDLE_DEPLOYMENT" => "true" } gemfile do source "#{file_uri_for(gem_repo1)}" gem "rack" @@ -297,7 +488,7 @@ RSpec.describe "bundler/inline#gemfile" do context "when BUNDLE_PATH is set" do it "installs inline gems to the system path regardless" do - script <<-RUBY, :env => { "BUNDLE_PATH" => "./vendor/inline" } + script <<-RUBY, env: { "BUNDLE_PATH" => "./vendor/inline" } gemfile(true) do source "#{file_uri_for(gem_repo1)}" gem "rack" @@ -309,7 +500,7 @@ RSpec.describe "bundler/inline#gemfile" do end it "skips platform warnings" do - simulate_platform "ruby" + bundle "config set --local force_ruby_platform true" script <<-RUBY gemfile(true) do @@ -321,6 +512,20 @@ RSpec.describe "bundler/inline#gemfile" do expect(err).to be_empty end + it "still installs if the application has `bundle package` no_install config set" do + bundle "config set --local no_install true" + + script <<-RUBY + gemfile do + source "#{file_uri_for(gem_repo1)}" + gem "rack" + end + RUBY + + expect(last_command).to be_success + expect(system_gem_path("gems/rack-1.0.0")).to exist + end + it "preserves previous BUNDLE_GEMFILE value" do ENV["BUNDLE_GEMFILE"] = "" script <<-RUBY @@ -371,7 +576,7 @@ RSpec.describe "bundler/inline#gemfile" do s.write "lib/foo.rb", foo_code end - script <<-RUBY, :dir => tmp("path_without_gemfile") + script <<-RUBY, dir: tmp("path_without_gemfile") gemfile do source "#{file_uri_for(gem_repo2)}" path "#{lib_path}" do @@ -386,26 +591,17 @@ RSpec.describe "bundler/inline#gemfile" do expect(err).to be_empty end - it "when requiring fileutils after does not show redefinition warnings" do - dependency_installer_loads_fileutils = ruby "require 'rubygems/dependency_installer'; puts $LOADED_FEATURES.grep(/fileutils/)", :raise_on_error => false - skip "does not work if rubygems/dependency_installer loads fileutils, which happens until rubygems 3.2.0" unless dependency_installer_loads_fileutils.empty? - - skip "pathname does not install cleanly on this ruby" if RUBY_VERSION < "2.7.0" - + it "when requiring fileutils after does not show redefinition warnings", :realworld do Dir.mkdir tmp("path_without_gemfile") - default_fileutils_version = ruby "gem 'fileutils', '< 999999'; require 'fileutils'; puts FileUtils::VERSION", :raise_on_error => false + default_fileutils_version = ruby "gem 'fileutils', '< 999999'; require 'fileutils'; puts FileUtils::VERSION", raise_on_error: false skip "fileutils isn't a default gem" if default_fileutils_version.empty? realworld_system_gems "fileutils --version 1.4.1" realworld_system_gems "pathname --version 0.2.0" - realworld_system_gems "fiddle" # not sure why, but this is needed on Windows to boot rubygems successfully - - realworld_system_gems "timeout uri" # this spec uses net/http which requires these default gems - - script <<-RUBY, :dir => tmp("path_without_gemfile"), :env => { "BUNDLER_GEM_DEFAULT_DIR" => system_gem_path.to_s } + script <<-RUBY, dir: tmp("path_without_gemfile"), env: { "BUNDLER_GEM_DEFAULT_DIR" => system_gem_path.to_s } require "bundler/inline" gemfile(true) do @@ -417,4 +613,47 @@ RSpec.describe "bundler/inline#gemfile" do expect(err).to eq("The Gemfile specifies no dependencies") end + + it "does not load default timeout" do + default_timeout_version = ruby "gem 'timeout', '< 999999'; require 'timeout'; puts Timeout::VERSION", raise_on_error: false + skip "timeout isn't a default gem" if default_timeout_version.empty? + + # This only works on RubyGems 3.5.0 or higher + ruby "require 'rubygems/timeout'", raise_on_error: false + skip "rubygems under test does not yet vendor timeout" unless last_command.success? + + build_repo4 do + build_gem "timeout", "999" + end + + script <<-RUBY + require "bundler/inline" + + gemfile(true) do + source "#{file_uri_for(gem_repo4)}" + + gem "timeout" + end + RUBY + + expect(out).to include("Installing timeout 999") + end + + it "does not upcase ENV" do + script <<-RUBY + require 'bundler/inline' + + ENV['Test_Variable'] = 'value string' + puts("before: \#{ENV.each_key.select { |key| key.match?(/test_variable/i) }}") + + gemfile do + source "#{file_uri_for(gem_repo1)}" + end + + puts("after: \#{ENV.each_key.select { |key| key.match?(/test_variable/i) }}") + RUBY + + expect(out).to include("before: [\"Test_Variable\"]") + expect(out).to include("after: [\"Test_Variable\"]") + end end diff --git a/spec/bundler/runtime/load_spec.rb b/spec/bundler/runtime/load_spec.rb index 96a22a46cc..f28ffd9460 100644 --- a/spec/bundler/runtime/load_spec.rb +++ b/spec/bundler/runtime/load_spec.rb @@ -82,7 +82,7 @@ RSpec.describe "Bundler.load" do G ruby <<-RUBY - require "#{entrypoint}" + require "bundler" Bundler.setup :default Bundler.require :default puts RACK diff --git a/spec/bundler/runtime/platform_spec.rb b/spec/bundler/runtime/platform_spec.rb index 433396d106..1925e9bf2e 100644 --- a/spec/bundler/runtime/platform_spec.rb +++ b/spec/bundler/runtime/platform_spec.rb @@ -22,7 +22,7 @@ RSpec.describe "Bundler.setup with multi platform stuff" do ruby <<-R begin - require '#{entrypoint}' + require 'bundler' Bundler.ui.silence { Bundler.setup } rescue Bundler::GemNotFound => e puts "WIN" @@ -61,16 +61,23 @@ RSpec.describe "Bundler.setup with multi platform stuff" do build_repo4 do build_gem "nokogiri", "1.11.1" do |s| s.add_dependency "mini_portile2", "~> 2.5.0" - s.add_dependency "racc", "~> 1.5.2" + s.add_dependency "racca", "~> 1.5.2" end build_gem "nokogiri", "1.11.1" do |s| s.platform = Bundler.local_platform - s.add_dependency "racc", "~> 1.4" + s.add_dependency "racca", "~> 1.4" end build_gem "mini_portile2", "2.5.0" - build_gem "racc", "1.5.2" + build_gem "racca", "1.5.2" + end + + checksums = checksums_section do |c| + c.checksum gem_repo4, "mini_portile2", "2.5.0" + c.checksum gem_repo4, "nokogiri", "1.11.1" + c.checksum gem_repo4, "nokogiri", "1.11.1", Bundler.local_platform + c.checksum gem_repo4, "racca", "1.5.2" end good_lockfile = <<~L @@ -80,17 +87,17 @@ RSpec.describe "Bundler.setup with multi platform stuff" do mini_portile2 (2.5.0) nokogiri (1.11.1) mini_portile2 (~> 2.5.0) - racc (~> 1.5.2) + racca (~> 1.5.2) nokogiri (1.11.1-#{Bundler.local_platform}) - racc (~> 1.4) - racc (1.5.2) + racca (~> 1.4) + racca (1.5.2) PLATFORMS - #{lockfile_platforms_for(["ruby"] + local_platforms)} + #{lockfile_platforms("ruby")} DEPENDENCIES nokogiri (~> 1.11) - + #{checksums} BUNDLED WITH #{Bundler::VERSION} L @@ -138,14 +145,14 @@ RSpec.describe "Bundler.setup with multi platform stuff" do #{Bundler::VERSION} L - bundle "install", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } + bundle "install", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } expect(out).to include("Fetching nokogiri 1.11.1") expect(the_bundle).to include_gems "nokogiri 1.11.1" expect(the_bundle).not_to include_gems "nokogiri 1.11.1 #{Bundler.local_platform}" end - it "will use the java platform if both generic java and generic ruby platforms are locked", :jruby do + it "will use the java platform if both generic java and generic ruby platforms are locked", :jruby_only do gemfile <<-G source "#{file_uri_for(gem_repo1)}" gem "nokogiri" @@ -168,7 +175,7 @@ RSpec.describe "Bundler.setup with multi platform stuff" do nokogiri BUNDLED WITH - 2.1.4 + #{Bundler::VERSION} G bundle "install" @@ -204,7 +211,7 @@ RSpec.describe "Bundler.setup with multi platform stuff" do expect(the_bundle).to include_gems "nokogiri 1.4.2", "platform_specific 1.0 x86-darwin-100" end - it "allows specifying only-ruby-platform on jruby", :jruby do + it "allows specifying only-ruby-platform on jruby", :jruby_only do install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" gem "nokogiri" @@ -232,7 +239,21 @@ RSpec.describe "Bundler.setup with multi platform stuff" do expect(the_bundle).to include_gems "nokogiri 1.4.2", "platform_specific 1.0 RUBY" end - it "doesn't pull platform specific gems on truffleruby", :truffleruby do + it "allows specifying only-ruby-platform even if the lockfile is locked to a specific compatible platform" do + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "nokogiri" + gem "platform_specific" + G + + bundle "config set force_ruby_platform true" + + bundle "install" + + expect(the_bundle).to include_gems "nokogiri 1.4.2", "platform_specific 1.0 RUBY" + end + + it "doesn't pull platform specific gems on truffleruby", :truffleruby_only do install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" gem "platform_specific" @@ -241,11 +262,117 @@ RSpec.describe "Bundler.setup with multi platform stuff" do expect(the_bundle).to include_gems "platform_specific 1.0 RUBY" end + it "doesn't pull platform specific gems on truffleruby (except when whitelisted) even if lockfile was generated with an older version that declared RUBY as platform", :truffleruby_only do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "platform_specific" + G + + lockfile <<-L + GEM + remote: #{file_uri_for(gem_repo1)}/ + specs: + platform_specific (1.0) + + PLATFORMS + ruby + + DEPENDENCIES + platform_specific + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "install" + + expect(the_bundle).to include_gems "platform_specific 1.0 RUBY" + + simulate_platform "x86_64-linux" do + build_repo4 do + build_gem "libv8" + + build_gem "libv8" do |s| + s.platform = "x86_64-linux" + end + end + + gemfile <<-G + source "#{file_uri_for(gem_repo4)}" + gem "libv8" + G + + lockfile <<-L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + libv8 (1.0) + + PLATFORMS + ruby + + DEPENDENCIES + libv8 + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "install" + + expect(the_bundle).to include_gems "libv8 1.0 x86_64-linux" + end + end + + it "doesn't pull platform specific gems on truffleruby, even if lockfile only includes those", :truffleruby_only do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "platform_specific" + G + + lockfile <<-L + GEM + remote: #{file_uri_for(gem_repo1)}/ + specs: + platform_specific (1.0-x86-darwin-100) + + PLATFORMS + x86-darwin-100 + + DEPENDENCIES + platform_specific + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "install" + + expect(the_bundle).to include_gems "platform_specific 1.0 RUBY" + end + + it "pulls platform specific gems correctly on musl" do + build_repo4 do + build_gem "nokogiri", "1.13.8" do |s| + s.platform = "aarch64-linux" + end + end + + simulate_platform "aarch64-linux-musl" do + install_gemfile <<-G, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }, verbose: true + source "https://gems.repo4" + gem "nokogiri" + G + end + + expect(out).to include("Fetching nokogiri 1.13.8 (aarch64-linux)") + end + it "allows specifying only-ruby-platform on windows with dependency platforms" do simulate_windows do install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" - gem "nokogiri", :platforms => [:mingw, :mswin, :x64_mingw, :jruby] + gem "nokogiri", :platforms => [:windows, :mswin, :mswin64, :mingw, :x64_mingw, :jruby] gem "platform_specific" G @@ -259,7 +386,7 @@ RSpec.describe "Bundler.setup with multi platform stuff" do end it "allows specifying only-ruby-platform on windows with gemspec dependency" do - build_lib("foo", "1.0", :path => bundled_app) do |s| + build_lib("foo", "1.0", path: bundled_app) do |s| s.add_dependency "rack" end @@ -283,7 +410,7 @@ RSpec.describe "Bundler.setup with multi platform stuff" do s.add_dependency "platform_specific" end end - simulate_windows x64_mingw do + simulate_windows x64_mingw32 do lockfile <<-L GEM remote: #{file_uri_for(gem_repo2)}/ @@ -300,12 +427,45 @@ RSpec.describe "Bundler.setup with multi platform stuff" do requires_platform_specific L - install_gemfile <<-G, :verbose => true + install_gemfile <<-G, verbose: true source "#{file_uri_for(gem_repo2)}" gem "requires_platform_specific" G + expect(out).to include("lockfile does not have all gems needed for the current platform") expect(the_bundle).to include_gem "platform_specific 1.0 x64-mingw32" end end + + %w[x86-mswin32 x64-mswin64 x86-mingw32 x64-mingw32 x64-mingw-ucrt].each do |arch| + it "allows specifying platform windows on #{arch} arch" do + platform = send(arch.tr("-", "_")) + + simulate_windows platform do + lockfile <<-L + GEM + remote: #{file_uri_for(gem_repo1)}/ + specs: + platform_specific (1.0-#{platform}) + requires_platform_specific (1.0) + platform_specific + + PLATFORMS + #{platform} + + DEPENDENCIES + requires_platform_specific + L + + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "platform_specific", :platforms => [:windows] + G + + bundle "install" + + expect(the_bundle).to include_gems "platform_specific 1.0 #{platform}" + end + end + end end diff --git a/spec/bundler/runtime/require_spec.rb b/spec/bundler/runtime/require_spec.rb index d91b5f8666..76271a5593 100644 --- a/spec/bundler/runtime/require_spec.rb +++ b/spec/bundler/runtime/require_spec.rb @@ -21,7 +21,7 @@ RSpec.describe "Bundler.require" do s.write "lib/four.rb", "puts 'four'" end - build_lib "five", "1.0.0", :no_default => true do |s| + build_lib "five", "1.0.0", no_default: true do |s| s.write "lib/mofive.rb", "puts 'five'" end @@ -138,7 +138,7 @@ RSpec.describe "Bundler.require" do end G - run "Bundler.require", :raise_on_error => false + run "Bundler.require", raise_on_error: false expect(err).to match("error while trying to load the gem 'faulty'") expect(err).to match("Gem Internal Error Message") end @@ -187,7 +187,7 @@ RSpec.describe "Bundler.require" do end it "silently passes if the require fails" do - build_lib "bcrypt-ruby", "1.0.0", :no_default => true do |s| + build_lib "bcrypt-ruby", "1.0.0", no_default: true do |s| s.write "lib/brcrypt.rb", "BCrypt = '1.0.0'" end gemfile <<-G @@ -199,7 +199,7 @@ RSpec.describe "Bundler.require" do G cmd = <<-RUBY - require '#{entrypoint}' + require 'bundler' Bundler.require RUBY ruby(cmd) @@ -323,7 +323,7 @@ RSpec.describe "Bundler.require" do describe "a gem with different requires for different envs" do before(:each) do - build_gem "multi_gem", :to_bundle => true do |s| + build_gem "multi_gem", to_bundle: true do |s| s.write "lib/one.rb", "puts 'ONE'" s.write "lib/two.rb", "puts 'TWO'" end @@ -366,7 +366,7 @@ RSpec.describe "Bundler.require" do describe "with busted gems" do it "should be busted" do - build_gem "busted_require", :to_bundle => true do |s| + build_gem "busted_require", to_bundle: true do |s| s.write "lib/busted_require.rb", "require 'no_such_file_omg'" end @@ -449,8 +449,6 @@ RSpec.describe "Bundler.require with platform specific dependencies" do end it "requires gems pinned to multiple platforms, including the current one" do - skip "platform issues" if Gem.win_platform? - install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" diff --git a/spec/bundler/runtime/requiring_spec.rb b/spec/bundler/runtime/requiring_spec.rb new file mode 100644 index 0000000000..1f32269622 --- /dev/null +++ b/spec/bundler/runtime/requiring_spec.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +RSpec.describe "Requiring bundler" do + it "takes care of requiring rubygems when entrypoint is bundler/setup" do + sys_exec("#{Gem.ruby} -I#{lib_dir} -rbundler/setup -e'puts true'", env: { "RUBYOPT" => opt_add("--disable=gems", ENV["RUBYOPT"]) }) + + expect(last_command.stdboth).to eq("true") + end + + it "takes care of requiring rubygems when requiring just bundler" do + sys_exec("#{Gem.ruby} -I#{lib_dir} -rbundler -e'puts true'", env: { "RUBYOPT" => opt_add("--disable=gems", ENV["RUBYOPT"]) }) + + expect(last_command.stdboth).to eq("true") + end +end diff --git a/spec/bundler/runtime/self_management_spec.rb b/spec/bundler/runtime/self_management_spec.rb new file mode 100644 index 0000000000..d15ca3189e --- /dev/null +++ b/spec/bundler/runtime/self_management_spec.rb @@ -0,0 +1,159 @@ +# frozen_string_literal: true + +RSpec.describe "Self management", rubygems: ">= 3.3.0.dev", realworld: true do + describe "auto switching" do + let(:previous_minor) do + "2.3.0" + end + + let(:current_version) do + "2.4.0" + end + + before do + build_repo2 + + gemfile <<-G + source "#{file_uri_for(gem_repo2)}" + + gem "rack" + G + end + + it "installs locked version when using system path and uses it" do + lockfile_bundled_with(previous_minor) + + bundle "config set --local path.system true" + bundle "install", artifice: "vcr" + expect(out).to include("Bundler #{Bundler::VERSION} is running, but your lockfile was generated with #{previous_minor}. Installing Bundler #{previous_minor} and restarting using that version.") + + # It uninstalls the older system bundler + bundle "clean --force", artifice: nil + expect(out).to eq("Removing bundler (#{Bundler::VERSION})") + + # App now uses locked version + bundle "-v", artifice: nil + expect(out).to end_with(previous_minor[0] == "2" ? "Bundler version #{previous_minor}" : previous_minor) + + # Subsequent installs use the locked version without reinstalling + bundle "install --verbose", artifice: nil + expect(out).to include("Using bundler #{previous_minor}") + expect(out).not_to include("Bundler #{Bundler::VERSION} is running, but your lockfile was generated with #{previous_minor}. Installing Bundler #{previous_minor} and restarting using that version.") + end + + it "installs locked version when using local path and uses it" do + lockfile_bundled_with(previous_minor) + + bundle "config set --local path vendor/bundle" + bundle "install", artifice: "vcr" + expect(out).to include("Bundler #{Bundler::VERSION} is running, but your lockfile was generated with #{previous_minor}. Installing Bundler #{previous_minor} and restarting using that version.") + expect(vendored_gems("gems/bundler-#{previous_minor}")).to exist + + # It does not uninstall the locked bundler + bundle "clean" + expect(out).to be_empty + + # App now uses locked version + bundle "-v" + expect(out).to end_with(previous_minor[0] == "2" ? "Bundler version #{previous_minor}" : previous_minor) + + # Subsequent installs use the locked version without reinstalling + bundle "install --verbose" + expect(out).to include("Using bundler #{previous_minor}") + expect(out).not_to include("Bundler #{Bundler::VERSION} is running, but your lockfile was generated with #{previous_minor}. Installing Bundler #{previous_minor} and restarting using that version.") + end + + it "installs locked version when using deployment option and uses it" do + lockfile_bundled_with(previous_minor) + + bundle "config set --local deployment true" + bundle "install", artifice: "vcr" + expect(out).to include("Bundler #{Bundler::VERSION} is running, but your lockfile was generated with #{previous_minor}. Installing Bundler #{previous_minor} and restarting using that version.") + expect(vendored_gems("gems/bundler-#{previous_minor}")).to exist + + # It does not uninstall the locked bundler + bundle "clean" + expect(out).to be_empty + + # App now uses locked version + bundle "-v" + expect(out).to end_with(previous_minor[0] == "2" ? "Bundler version #{previous_minor}" : previous_minor) + + # Subsequent installs use the locked version without reinstalling + bundle "install --verbose" + expect(out).to include("Using bundler #{previous_minor}") + expect(out).not_to include("Bundler #{Bundler::VERSION} is running, but your lockfile was generated with #{previous_minor}. Installing Bundler #{previous_minor} and restarting using that version.") + end + + it "does not try to install a development version" do + lockfile_bundled_with("#{previous_minor}.dev") + + bundle "install --verbose" + expect(out).not_to match(/restarting using that version/) + + bundle "-v" + expect(out).to eq(Bundler::VERSION[0] == "2" ? "Bundler version #{Bundler::VERSION}" : Bundler::VERSION) + end + + it "shows a discrete message if locked bundler does not exist" do + missing_minor = "#{Bundler::VERSION[0]}.999.999" + + lockfile_bundled_with(missing_minor) + + bundle "install", artifice: "vcr" + expect(err).to eq("Your lockfile is locked to a version of bundler (#{missing_minor}) that doesn't exist at https://rubygems.org/. Going on using #{Bundler::VERSION}") + + bundle "-v" + expect(out).to eq(Bundler::VERSION[0] == "2" ? "Bundler version #{Bundler::VERSION}" : Bundler::VERSION) + end + + it "installs BUNDLE_VERSION version when using bundle config version x.y.z" do + lockfile_bundled_with(current_version) + + bundle "config set --local version #{previous_minor}" + bundle "install", artifice: "vcr" + expect(out).to include("Bundler #{Bundler::VERSION} is running, but your configuration was #{previous_minor}. Installing Bundler #{previous_minor} and restarting using that version.") + + bundle "-v" + expect(out).to eq(previous_minor[0] == "2" ? "Bundler version #{previous_minor}" : previous_minor) + end + + it "does not try to install when using bundle config version global" do + lockfile_bundled_with(previous_minor) + + bundle "config set version system" + bundle "install", artifice: "vcr" + expect(out).not_to match(/restarting using that version/) + + bundle "-v" + expect(out).to eq(Bundler::VERSION[0] == "2" ? "Bundler version #{Bundler::VERSION}" : Bundler::VERSION) + end + + it "ignores malformed lockfile version" do + lockfile_bundled_with("2.3.") + + bundle "install --verbose" + expect(out).to include("Using bundler #{Bundler::VERSION}") + end + + private + + def lockfile_bundled_with(version) + lockfile <<~L + GEM + remote: #{file_uri_for(gem_repo2)}/ + specs: + rack (1.0.0) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + rack + + BUNDLED WITH + #{version} + L + end + end +end diff --git a/spec/bundler/runtime/setup_spec.rb b/spec/bundler/runtime/setup_spec.rb index 804e29c3c1..0344d24223 100644 --- a/spec/bundler/runtime/setup_spec.rb +++ b/spec/bundler/runtime/setup_spec.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true require "tmpdir" -require "tempfile" RSpec.describe "Bundler.setup" do describe "with no arguments" do @@ -90,7 +89,7 @@ RSpec.describe "Bundler.setup" do end it "handles multiple non-additive invocations" do - ruby <<-RUBY, :raise_on_error => false + ruby <<-RUBY, raise_on_error: false require 'bundler' Bundler.setup(:default, :test) Bundler.setup(:default) @@ -145,6 +144,7 @@ RSpec.describe "Bundler.setup" do ruby <<-RUBY require 'bundler' + gem "bundler", "#{Bundler::VERSION}" if #{ruby_core?} Bundler.setup puts $LOAD_PATH RUBY @@ -158,7 +158,7 @@ RSpec.describe "Bundler.setup" do "/gems/actionpack-2.3.2/lib", "/gems/actionmailer-2.3.2/lib", "/gems/activesupport-2.3.2/lib", - "/gems/rake-13.0.1/lib" + "/gems/rake-#{rake_version}/lib" ) end @@ -194,7 +194,7 @@ RSpec.describe "Bundler.setup" do G ruby <<-R - require '#{entrypoint}' + require 'bundler' begin Bundler.setup @@ -213,7 +213,7 @@ RSpec.describe "Bundler.setup" do gem "rack" G - ruby <<-R, :raise_on_error => false + ruby <<-R, raise_on_error: false require 'bundler' Bundler.setup @@ -236,7 +236,7 @@ RSpec.describe "Bundler.setup" do gem "nosuchgem", "10.0" G - ruby <<-R, :raise_on_error => false + ruby <<-R, raise_on_error: false require 'bundler' Bundler.setup @@ -312,7 +312,7 @@ RSpec.describe "Bundler.setup" do gem "rack", "1.0.0" G - build_gem "rack", "1.0", :to_system => true do |s| + build_gem "rack", "1.0", to_system: true do |s| s.write "lib/rack.rb", "RACK = 'FAIL'" end @@ -341,19 +341,6 @@ RSpec.describe "Bundler.setup" do expect(out).to eq("WIN") end - it "version_requirement is now deprecated in rubygems 1.4.0+ when gem is missing" do - run <<-R - begin - gem "activesupport" - puts "FAIL" - rescue LoadError - puts "WIN" - end - R - - expect(err).to be_empty - end - it "replaces #gem but raises when the version is wrong" do run <<-R begin @@ -366,19 +353,6 @@ RSpec.describe "Bundler.setup" do expect(out).to eq("WIN") end - - it "version_requirement is now deprecated in rubygems 1.4.0+ when the version is wrong" do - run <<-R - begin - gem "rack", "1.0.0" - puts "FAIL" - rescue LoadError - puts "WIN" - end - R - - expect(err).to be_empty - end end describe "by hiding system gems" do @@ -397,8 +371,8 @@ RSpec.describe "Bundler.setup" do context "when the ruby stdlib is a substring of Gem.path" do it "does not reject the stdlib from $LOAD_PATH" do - substring = "/" + $LOAD_PATH.find {|p| p =~ /vendor_ruby/ }.split("/")[2] - run "puts 'worked!'", :env => { "GEM_PATH" => substring } + substring = "/" + $LOAD_PATH.find {|p| p.include?("vendor_ruby") }.split("/")[2] + run "puts 'worked!'", env: { "GEM_PATH" => substring } expect(out).to eq("worked!") end end @@ -436,8 +410,8 @@ RSpec.describe "Bundler.setup" do end it "provides a useful exception when the git repo is not checked out yet" do - run "1", :raise_on_error => false - expect(err).to match(/the git source #{lib_path('rack-1.0.0')} is not yet checked out. Please run `bundle install`/i) + run "1", raise_on_error: false + expect(err).to match(/the git source #{lib_path("rack-1.0.0")} is not yet checked out. Please run `bundle install`/i) end it "does not hit the git binary if the lockfile is available and up to date" do @@ -467,7 +441,7 @@ RSpec.describe "Bundler.setup" do break_git! ruby <<-R - require "#{entrypoint}" + require "bundler" begin Bundler.setup @@ -477,7 +451,7 @@ RSpec.describe "Bundler.setup" do end R - run "puts 'FAIL'", :raise_on_error => false + run "puts 'FAIL'", raise_on_error: false expect(err).not_to include "This is not the git you are looking for" end @@ -516,15 +490,15 @@ RSpec.describe "Bundler.setup" do gemfile <<-G source "#{file_uri_for(gem_repo1)}" - gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master" + gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "main" G bundle %(config set local.rack #{lib_path("local-rack")}) bundle :install FileUtils.rm_rf(lib_path("local-rack")) - run "require 'rack'", :raise_on_error => false - expect(err).to match(/Cannot use local override for rack-0.8 because #{Regexp.escape(lib_path('local-rack').to_s)} does not exist/) + run "require 'rack'", raise_on_error: false + expect(err).to match(/Cannot use local override for rack-0.8 because #{Regexp.escape(lib_path("local-rack").to_s)} does not exist/) end it "explodes if branch is not given on runtime" do @@ -534,7 +508,7 @@ RSpec.describe "Bundler.setup" do gemfile <<-G source "#{file_uri_for(gem_repo1)}" - gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master" + gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "main" G bundle %(config set local.rack #{lib_path("local-rack")}) @@ -545,7 +519,7 @@ RSpec.describe "Bundler.setup" do gem "rack", :git => "#{lib_path("rack-0.8")}" G - run "require 'rack'", :raise_on_error => false + run "require 'rack'", raise_on_error: false expect(err).to match(/because :branch is not specified in Gemfile/) end @@ -556,7 +530,7 @@ RSpec.describe "Bundler.setup" do gemfile <<-G source "#{file_uri_for(gem_repo1)}" - gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master" + gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "main" G bundle %(config set local.rack #{lib_path("local-rack")}) @@ -567,8 +541,8 @@ RSpec.describe "Bundler.setup" do gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "changed" G - run "require 'rack'", :raise_on_error => false - expect(err).to match(/is using branch master but Gemfile specifies changed/) + run "require 'rack'", raise_on_error: false + expect(err).to match(/is using branch main but Gemfile specifies changed/) end it "explodes on refs with different branches on runtime" do @@ -578,17 +552,17 @@ RSpec.describe "Bundler.setup" do install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" - gem "rack", :git => "#{lib_path("rack-0.8")}", :ref => "master", :branch => "master" + gem "rack", :git => "#{lib_path("rack-0.8")}", :ref => "main", :branch => "main" G gemfile <<-G source "#{file_uri_for(gem_repo1)}" - gem "rack", :git => "#{lib_path("rack-0.8")}", :ref => "master", :branch => "nonexistant" + gem "rack", :git => "#{lib_path("rack-0.8")}", :ref => "main", :branch => "nonexistent" G bundle %(config set local.rack #{lib_path("local-rack")}) - run "require 'rack'", :raise_on_error => false - expect(err).to match(/is using branch master but Gemfile specifies nonexistant/) + run "require 'rack'", raise_on_error: false + expect(err).to match(/is using branch main but Gemfile specifies nonexistent/) end end @@ -606,7 +580,7 @@ RSpec.describe "Bundler.setup" do system_gems "activesupport-2.3.5" - expect(the_bundle).to include_gems "activesupport 2.3.2", :groups => :default + expect(the_bundle).to include_gems "activesupport 2.3.2", groups: :default end it "remembers --without and does not bail on bare Bundler.setup" do @@ -629,7 +603,7 @@ RSpec.describe "Bundler.setup" do bundle "config set --local without development" path = bundled_app(File.join("vendor", "foo")) - build_lib "foo", :path => path + build_lib "foo", path: path install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" @@ -639,7 +613,7 @@ RSpec.describe "Bundler.setup" do FileUtils.rm_rf(path) - ruby "require 'bundler'; Bundler.setup", :env => { "DEBUG" => "1" } + ruby "require 'bundler'; Bundler.setup", env: { "DEBUG" => "1" } expect(out).to include("Assuming that source at `vendor/foo` has not changed since fetching its specs errored") expect(out).to include("Found no changes, using resolution from the lockfile") expect(err).to be_empty @@ -659,8 +633,35 @@ RSpec.describe "Bundler.setup" do gem "depends_on_bundler" G - ruby "require '#{system_gem_path("gems/bundler-9.99.9.beta1/lib/bundler.rb")}'; Bundler.setup", :env => { "DEBUG" => "1" } + ruby "require '#{system_gem_path("gems/bundler-9.99.9.beta1/lib/bundler.rb")}'; Bundler.setup", env: { "DEBUG" => "1" } expect(out).to include("Found no changes, using resolution from the lockfile") + expect(out).not_to include("lockfile does not have all gems needed for the current platform") + expect(err).to be_empty + end + + it "doesn't fail in frozen mode when bundler is a Gemfile dependency" do + install_gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + gem "bundler" + G + + bundle "install --verbose", env: { "BUNDLE_FROZEN" => "true" } + expect(err).to be_empty + end + + it "doesn't re-resolve when deleting dependencies" do + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack" + gem "actionpack" + G + + install_gemfile <<-G, verbose: true + source "#{file_uri_for(gem_repo1)}" + gem "rack" + G + + expect(out).to include("Some dependencies were deleted, using a subset of the resolution from the lockfile") expect(err).to be_empty end @@ -679,15 +680,15 @@ RSpec.describe "Bundler.setup" do 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 + expect(the_bundle).not_to include_gems "activesupport 2.3.2", groups: :rack + expect(the_bundle).to include_gems "rack 1.0.0", groups: :rack end end # RubyGems returns loaded_from as a string it "has loaded_from as a string on all specs" do build_git "foo" - build_git "no-gemspec", :gemspec => false + build_git "no-gemspec", gemspec: false install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" @@ -727,6 +728,27 @@ end R run <<-R + File.open(File.join(Gem.dir, "specifications", "broken-ext.gemspec"), "w") do |f| + f.write <<-RUBY +# -*- encoding: utf-8 -*- +# stub: broken-ext 1.0.0 ruby lib +# stub: a.ext\\0b.ext + +Gem::Specification.new do |s| + s.name = "broken-ext" + s.version = "1.0.0" + raise "BROKEN GEMSPEC EXT" +end + RUBY + end + # Need to write the gem.build_complete file, + # otherwise the full spec is loaded to check the installed_by_version + extensions_dir = Gem.default_ext_dir_for(Gem.dir) || File.join(Gem.dir, "extensions", Gem::Platform.local.to_s, Gem.extension_api_version) + Bundler::FileUtils.mkdir_p(File.join(extensions_dir, "broken-ext-1.0.0")) + File.open(File.join(extensions_dir, "broken-ext-1.0.0", "gem.build_complete"), "w") {} + R + + run <<-R puts "WIN" R @@ -800,7 +822,7 @@ end run <<~RUBY puts ENV['MANPATH'] require "open3" - puts Open3.capture2e("man", "ls")[0] + puts Open3.capture2e({ "LC_ALL" => "C" }, "man", "ls")[0] RUBY lines = out.split("\n") @@ -828,7 +850,7 @@ end expect(out).to eq("yay") end - it "should clean $LOAD_PATH properly", :ruby_repo do + it "should clean $LOAD_PATH properly" do gem_name = "very_simple_binary" full_gem_name = gem_name + "-1.0" ext_dir = File.join(tmp("extensions", full_gem_name)) @@ -861,23 +883,21 @@ end context "with bundler is located in symlinked GEM_HOME" do let(:gem_home) { Dir.mktmpdir } - let(:symlinked_gem_home) { Tempfile.new("gem_home").path } + let(:symlinked_gem_home) { tmp("gem_home-symlink").to_s } let(:full_name) { "bundler-#{Bundler::VERSION}" } before do - skip "symlink destination exists" if Gem.win_platform? - - FileUtils.ln_sf(gem_home, symlinked_gem_home) + File.symlink(gem_home, symlinked_gem_home) gems_dir = File.join(gem_home, "gems") specifications_dir = File.join(gem_home, "specifications") Dir.mkdir(gems_dir) Dir.mkdir(specifications_dir) - FileUtils.ln_s(source_root, File.join(gems_dir, full_name)) + File.symlink(source_root, File.join(gems_dir, full_name)) gemspec_content = File.binread(gemspec). sub("Bundler::VERSION", %("#{Bundler::VERSION}")). - lines.reject {|line| line =~ %r{lib/bundler/version} }.join + lines.reject {|line| line.include?("lib/bundler/version") }.join File.open(File.join(specifications_dir, "#{full_name}.gemspec"), "wb") do |f| f.write(gemspec_content) @@ -887,7 +907,7 @@ end it "should not remove itself from the LOAD_PATH and require a different copy of 'bundler/setup'" do install_gemfile "source \"#{file_uri_for(gem_repo1)}\"" - ruby <<-R, :env => { "GEM_PATH" => symlinked_gem_home } + ruby <<-R, env: { "GEM_PATH" => symlinked_gem_home } TracePoint.trace(:class) do |tp| if tp.path.include?("bundler") && !tp.path.start_with?("#{source_root}") puts "OMG. Defining a class from another bundler at \#{tp.path}:\#{tp.lineno}" @@ -927,7 +947,7 @@ end it "should resolve paths relative to the Gemfile" do path = bundled_app(File.join("vendor", "foo")) - build_lib "foo", :path => path + build_lib "foo", path: path # If the .gemspec exists, then Bundler handles the path differently. # See Source::Path.load_spec_files for details. @@ -938,7 +958,7 @@ end gem 'foo', '1.2.3', :path => 'vendor/foo' G - run <<-R, :env => { "BUNDLE_GEMFILE" => bundled_app_gemfile.to_s }, :dir => bundled_app.parent + run <<-R, env: { "BUNDLE_GEMFILE" => bundled_app_gemfile.to_s }, dir: bundled_app.parent require 'foo' R expect(err).to be_empty @@ -948,7 +968,7 @@ end relative_path = File.join("vendor", Dir.pwd.gsub(/^#{filesystem_root}/, "")) absolute_path = bundled_app(relative_path) FileUtils.mkdir_p(absolute_path) - build_lib "foo", :path => absolute_path + build_lib "foo", path: absolute_path # If the .gemspec exists, then Bundler handles the path differently. # See Source::Path.load_spec_files for details. @@ -961,7 +981,7 @@ end bundle :install - run <<-R, :env => { "BUNDLE_GEMFILE" => bundled_app_gemfile.to_s }, :dir => bundled_app.parent + run <<-R, env: { "BUNDLE_GEMFILE" => bundled_app_gemfile.to_s }, dir: bundled_app.parent require 'foo' R @@ -971,7 +991,7 @@ end describe "with git gems that don't have gemspecs" do before :each do - build_git "no_gemspec", :gemspec => false + build_git "no_gemspec", gemspec: false install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" @@ -1061,7 +1081,7 @@ end describe "with a gemspec that requires other files" do before :each do - build_git "bar", :gemspec => false do |s| + build_git "bar", gemspec: false do |s| s.write "lib/bar/version.rb", %(BAR_VERSION = '1.0') s.write "bar.gemspec", <<-G require_relative 'lib/bar/version' @@ -1091,10 +1111,10 @@ end it "error intelligently if the gemspec has a LoadError" do skip "whitespace issue?" if Gem.win_platform? - ref = update_git "bar", :gemspec => false do |s| + ref = update_git "bar", gemspec: false do |s| s.write "bar.gemspec", "require 'foobarbaz'" end.ref_for("HEAD") - bundle :install, :raise_on_error => false + bundle :install, raise_on_error: false expect(err.lines.map(&:chomp)).to include( a_string_starting_with("[!] There was an error while loading `bar.gemspec`:"), @@ -1167,7 +1187,7 @@ end context "is not present" do it "does not change the lock" do lockfile lock_with(nil) - ruby "require '#{entrypoint}/setup'" + ruby "require 'bundler/setup'" expect(lockfile).to eq lock_with(nil) end end @@ -1186,7 +1206,7 @@ end it "does not change the lock" do system_gems "bundler-1.10.1" lockfile lock_with("1.10.1") - ruby "require '#{entrypoint}/setup'" + ruby "require 'bundler/setup'" expect(lockfile).to eq lock_with("1.10.1") end end @@ -1196,6 +1216,10 @@ end let(:ruby_version) { nil } def lock_with(ruby_version = nil) + checksums = checksums_section do |c| + c.checksum gem_repo1, "rack", "1.0.0" + end + lock = <<~L GEM remote: #{file_uri_for(gem_repo1)}/ @@ -1207,6 +1231,7 @@ end DEPENDENCIES rack + #{checksums} L if ruby_version @@ -1256,9 +1281,7 @@ end describe "with gemified standard libraries" do it "does not load Digest", :ruby_repo do - skip "Only for Ruby 3.0+" unless RUBY_VERSION >= "3.0" - - build_git "bar", :gemspec => false do |s| + build_git "bar", gemspec: false do |s| s.write "lib/bar/version.rb", %(BAR_VERSION = '1.0') s.write "bar.gemspec", <<-G require_relative 'lib/bar/version' @@ -1283,7 +1306,7 @@ end bundle :install ruby <<-RUBY - require '#{entrypoint}/setup' + require 'bundler/setup' puts defined?(::Digest) ? "Digest defined" : "Digest undefined" require 'digest' RUBY @@ -1293,7 +1316,7 @@ end it "does not load Psych" do gemfile "source \"#{file_uri_for(gem_repo1)}\"" ruby <<-RUBY - require '#{entrypoint}/setup' + require 'bundler/setup' puts defined?(Psych::VERSION) ? Psych::VERSION : "undefined" require 'psych' puts Psych::VERSION @@ -1314,24 +1337,46 @@ end expect(out).to eq("undefined\nconstant") end + it "does not load uri while reading gemspecs", rubygems: ">= 3.6.0.dev" do + Dir.mkdir bundled_app("test") + + create_file(bundled_app("test/test.gemspec"), <<-G) + Gem::Specification.new do |s| + s.name = "test" + s.version = "1.0.0" + s.summary = "test" + s.authors = ['John Doe'] + s.homepage = 'https://example.com' + end + G + + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "test", path: "#{bundled_app("test")}" + G + + ruby <<-RUBY + require "bundler/setup" + puts defined?(URI) || "undefined" + require "uri" + puts defined?(URI) || "undefined" + RUBY + expect(out).to eq("undefined\nconstant") + end + describe "default gem activation" do let(:exemptions) do - exempts = if Gem.rubygems_version >= Gem::Version.new("2.7") - %w[did_you_mean] - else - %w[io-console openssl] - end << "bundler" - exempts << "fiddle" if Gem.win_platform? && Gem.rubygems_version >= Gem::Version.new("2.7") - exempts << "uri" if Gem.ruby_version >= Gem::Version.new("2.7") - exempts << "pathname" if Gem.ruby_version >= Gem::Version.new("3.0") + exempts = %w[did_you_mean bundler uri pathname] + exempts << "etc" if Gem.ruby_version < Gem::Version.new("3.2") && Gem.win_platform? exempts << "set" unless Gem.rubygems_version >= Gem::Version.new("3.2.6") exempts << "tsort" unless Gem.rubygems_version >= Gem::Version.new("3.2.31") exempts << "error_highlight" # added in Ruby 3.1 as a default gem exempts << "ruby2_keywords" # added in Ruby 3.1 as a default gem + exempts << "syntax_suggest" # added in Ruby 3.2 as a default gem exempts end - let(:activation_warning_hack) { strip_whitespace(<<-RUBY) } + let(:activation_warning_hack) { <<~RUBY } require #{spec_dir.join("support/hax").to_s.dump} Gem::Specification.send(:alias_method, :bundler_spec_activate, :activate) @@ -1351,7 +1396,7 @@ end "-r#{bundled_app("activation_warning_hack.rb")} #{ENV["RUBYOPT"]}" end - let(:code) { strip_whitespace(<<-RUBY) } + let(:code) { <<~RUBY } require "pp" loaded_specs = Gem.loaded_specs.dup #{exemptions.inspect}.each {|s| loaded_specs.delete(s) } @@ -1366,14 +1411,14 @@ end it "activates no gems with -rbundler/setup" do install_gemfile "source \"#{file_uri_for(gem_repo1)}\"" - ruby code, :env => { "RUBYOPT" => activation_warning_hack_rubyopt + " -rbundler/setup" } + ruby code, env: { "RUBYOPT" => activation_warning_hack_rubyopt + " -rbundler/setup" } expect(out).to eq("{}") end it "activates no gems with bundle exec" do install_gemfile "source \"#{file_uri_for(gem_repo1)}\"" create_file("script.rb", code) - bundle "exec ruby ./script.rb", :env => { "RUBYOPT" => activation_warning_hack_rubyopt } + bundle "exec ruby ./script.rb", env: { "RUBYOPT" => activation_warning_hack_rubyopt } expect(out).to eq("{}") end @@ -1383,7 +1428,7 @@ end install_gemfile "source \"#{file_uri_for(gem_repo1)}\"" create_file("script.rb", "#!/usr/bin/env ruby\n\n#{code}") FileUtils.chmod(0o777, bundled_app("script.rb")) - bundle "exec ./script.rb", :artifice => nil, :env => { "RUBYOPT" => activation_warning_hack_rubyopt } + bundle "exec ./script.rb", artifice: nil, env: { "RUBYOPT" => activation_warning_hack_rubyopt } expect(out).to eq("{}") end @@ -1392,7 +1437,7 @@ end build_gem "net-http-pipeline", "1.0.1" end - system_gems "net-http-pipeline-1.0.1", :gem_repo => gem_repo4 + system_gems "net-http-pipeline-1.0.1", gem_repo: gem_repo4 gemfile <<-G source "#{file_uri_for(gem_repo4)}" @@ -1421,7 +1466,7 @@ end gem "#{g}", "999999" G - expect(the_bundle).to include_gem("#{g} 999999", :env => { "RUBYOPT" => activation_warning_hack_rubyopt }) + expect(the_bundle).to include_gem("#{g} 999999", env: { "RUBYOPT" => activation_warning_hack_rubyopt }) end it "activates older versions of #{g}", :ruby_repo do @@ -1436,14 +1481,14 @@ end gem "#{g}", "0.0.0.a" G - expect(the_bundle).to include_gem("#{g} 0.0.0.a", :env => { "RUBYOPT" => activation_warning_hack_rubyopt }) + expect(the_bundle).to include_gem("#{g} 0.0.0.a", env: { "RUBYOPT" => activation_warning_hack_rubyopt }) end end end end describe "after setup" do - it "allows calling #gem on random objects", :bundler => "< 3" do + it "allows calling #gem on random objects", bundler: "< 3" do install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" gem "rack" @@ -1458,13 +1503,13 @@ end expect(out).to eq("rack-1.0.0") end - it "keeps Kernel#gem private", :bundler => "3" do + it "keeps Kernel#gem private", bundler: "3" do install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" gem "rack" G - ruby <<-RUBY, :raise_on_error => false + ruby <<-RUBY, raise_on_error: false require "bundler/setup" Object.new.gem "rack" puts "FAIL" @@ -1480,20 +1525,14 @@ end gem "rack" G - ruby <<-RUBY, :raise_on_error => false + ruby <<-RUBY, raise_on_error: false require "bundler/setup" Object.new.require "rack" puts "FAIL" RUBY expect(last_command.stdboth).not_to include "FAIL" - expect(err).to include "private method `require'" - end - - it "takes care of requiring rubygems" do - sys_exec("#{Gem.ruby} -I#{lib_dir} -rbundler/setup -e'puts true'", :env => { "RUBYOPT" => opt_add("--disable=gems", ENV["RUBYOPT"]) }) - - expect(last_command.stdboth).to eq("true") + expect(err).to match(/private method [`']require'/) end it "memoizes initial set of specs when requiring bundler/setup, so that even if further code mutates dependencies, Bundler.definition.specs is not affected" do @@ -1503,7 +1542,7 @@ end gem "rack", :group => :test G - ruby <<-RUBY, :raise_on_error => false + ruby <<-RUBY, raise_on_error: false require "bundler/setup" Bundler.require(:test).select! {|d| (d.groups & [:test]).any? } puts Bundler.definition.specs.map(&:name).join(", ") @@ -1511,5 +1550,68 @@ end expect(out).to include("rack, yard") end + + it "does not cause double loads when higher versions of default gems are activated before bundler" do + build_repo2 do + build_gem "json", "999.999.999" do |s| + s.write "lib/json.rb", <<~RUBY + module JSON + VERSION = "999.999.999" + end + RUBY + end + end + + system_gems "json-999.999.999", gem_repo: gem_repo2 + + install_gemfile "source \"#{file_uri_for(gem_repo1)}\"" + ruby <<-RUBY + require "json" + require "bundler/setup" + require "json" + RUBY + + expect(err).to be_empty + end + end + + it "does not undo the Kernel.require decorations", rubygems: ">= 3.4.6" do + install_gemfile "source \"#{file_uri_for(gem_repo1)}\"" + script = bundled_app("bin/script") + create_file(script, <<~RUBY) + module Kernel + module_function + + alias_method :require_before_extra_monkeypatches, :require + + def require(path) + puts "requiring \#{path} used the monkeypatch" + + require_before_extra_monkeypatches(path) + end + end + + require "bundler/setup" + + require "foo" + RUBY + + sys_exec "#{Gem.ruby} #{script}", raise_on_error: false + expect(out).to include("requiring foo used the monkeypatch") + end + + it "performs an automatic bundle install" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack", :group => :test + G + + bundle "config set auto_install 1" + + ruby <<-RUBY + require 'bundler/setup' + RUBY + expect(err).to be_empty + expect(out).to include("Installing rack 1.0.0") end end diff --git a/spec/bundler/runtime/with_unbundled_env_spec.rb b/spec/bundler/runtime/with_unbundled_env_spec.rb index 731a9921a2..135c71b0af 100644 --- a/spec/bundler/runtime/with_unbundled_env_spec.rb +++ b/spec/bundler/runtime/with_unbundled_env_spec.rb @@ -90,9 +90,7 @@ RSpec.describe "Bundler.with_env helpers" do RUBY setup_require = "-r#{lib_dir}/bundler/setup" ENV["BUNDLER_ORIG_RUBYOPT"] = "-W2 #{setup_require} #{ENV["RUBYOPT"]}" - simulate_bundler_version_when_missing_prerelease_default_gem_activation do - bundle_exec_ruby bundled_app("source.rb") - end + bundle_exec_ruby bundled_app("source.rb") expect(last_command.stdboth).not_to include(setup_require) end @@ -101,9 +99,7 @@ RSpec.describe "Bundler.with_env helpers" do print #{modified_env}['RUBYOPT'] RUBY ENV["BUNDLER_ORIG_RUBYOPT"] = "-W2 -rbundler/setup #{ENV["RUBYOPT"]}" - simulate_bundler_version_when_missing_prerelease_default_gem_activation do - bundle_exec_ruby bundled_app("source.rb") - end + bundle_exec_ruby bundled_app("source.rb") expect(last_command.stdboth).not_to include("-rbundler/setup") end @@ -134,7 +130,7 @@ RSpec.describe "Bundler.with_env helpers" do it_behaves_like "an unbundling helper" end - describe "Bundler.clean_env", :bundler => 2 do + describe "Bundler.clean_env", bundler: 2 do let(:modified_env) { "Bundler.clean_env" } it_behaves_like "an unbundling helper" @@ -143,7 +139,7 @@ RSpec.describe "Bundler.with_env helpers" do 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 { Bundler::EnvironmentPreserver.env_to_hash(ENV) } + actual = Bundler.with_original_env { ENV.to_hash } expect(actual).to eq(expected) end @@ -156,12 +152,12 @@ RSpec.describe "Bundler.with_env helpers" do end end - describe "Bundler.with_clean_env", :bundler => 2 do + describe "Bundler.with_clean_env", bundler: 2 do it "should set ENV to unbundled_env in the block" do expected = Bundler.unbundled_env actual = Bundler.ui.silence do - Bundler.with_clean_env { Bundler::EnvironmentPreserver.env_to_hash(ENV) } + Bundler.with_clean_env { ENV.to_hash } end expect(actual).to eq(expected) @@ -179,7 +175,7 @@ RSpec.describe "Bundler.with_env helpers" do describe "Bundler.with_unbundled_env" do it "should set ENV to unbundled_env in the block" do expected = Bundler.unbundled_env - actual = Bundler.with_unbundled_env { Bundler::EnvironmentPreserver.env_to_hash(ENV) } + actual = Bundler.with_unbundled_env { ENV.to_hash } expect(actual).to eq(expected) end @@ -207,7 +203,7 @@ RSpec.describe "Bundler.with_env helpers" do end end - describe "Bundler.clean_system", :bundler => 2 do + describe "Bundler.clean_system", bundler: 2 do before do create_file("source.rb", <<-'RUBY') Bundler.ui.silence { Bundler.clean_system("ruby", "-e", "exit(42) unless ENV['BUNDLE_FOO'] == 'bar'") } @@ -258,7 +254,7 @@ RSpec.describe "Bundler.with_env helpers" do end end - describe "Bundler.clean_exec", :bundler => 2 do + describe "Bundler.clean_exec", bundler: 2 do before do create_file("source.rb", <<-'RUBY') Process.fork do |