diff options
Diffstat (limited to 'spec/bundler/commands')
28 files changed, 4916 insertions, 837 deletions
diff --git a/spec/bundler/commands/add_spec.rb b/spec/bundler/commands/add_spec.rb index 4c533652ca..36e286793b 100644 --- a/spec/bundler/commands/add_spec.rb +++ b/spec/bundler/commands/add_spec.rb @@ -9,6 +9,7 @@ RSpec.describe "bundle add" do build_gem "bar", "0.12.3" build_gem "cat", "0.12.3.pre" build_gem "dog", "1.1.3.pre" + build_gem "lemur", "3.1.1.pre.2023.1.1" end build_git "foo", "2.0" @@ -21,7 +22,7 @@ RSpec.describe "bundle add" do context "when no gems are specified" do it "shows error" do - bundle "add", :raise_on_error => false + bundle "add", raise_on_error: false expect(err).to include("Please specify gems to add") end @@ -51,6 +52,13 @@ RSpec.describe "bundle add" do expect(bundled_app_gemfile.read).to match(/gem "dog", "~> 1.1.pre"/) expect(the_bundle).to include_gems "dog 1.1.3.pre" end + + it "version requirement becomes ~> major.minor.pre.tail when resolved version has a very long tail pre version" do + bundle "add 'lemur'" + # the trailing pre purposely matches the release version to ensure that subbing the release doesn't change the pre.version" + expect(bundled_app_gemfile.read).to match(/gem "lemur", "~> 3.1.pre.2023.1.1"/) + expect(the_bundle).to include_gems "lemur 3.1.1.pre.2023.1.1" + end end describe "with --version" do @@ -63,11 +71,23 @@ RSpec.describe "bundle add" do it "adds multiple version constraints when specified" do requirements = ["< 3.0", "> 1.0"] bundle "add 'foo' --version='#{requirements.join(", ")}'" - expect(bundled_app_gemfile.read).to match(/gem "foo", #{Gem::Requirement.new(requirements).as_list.map(&:dump).join(', ')}/) + expect(bundled_app_gemfile.read).to match(/gem "foo", #{Gem::Requirement.new(requirements).as_list.map(&:dump).join(", ")}/) expect(the_bundle).to include_gems "foo 2.0" end end + describe "with --require" do + it "adds the require param for the gem" do + bundle "add 'foo' --require=foo/engine" + expect(bundled_app_gemfile.read).to match(%r{gem "foo",(?: .*,) :require => "foo\/engine"}) + end + + it "converts false to a boolean" do + bundle "add 'foo' --require=false" + expect(bundled_app_gemfile.read).to match(/gem "foo",(?: .*,) :require => false/) + end + end + describe "with --group" do it "adds dependency for the specified group" do bundle "add 'foo' --group='development'" @@ -91,8 +111,17 @@ RSpec.describe "bundle add" do end end + describe "with --path" do + it "adds dependency with specified path" do + bundle "add 'foo' --path='#{lib_path("foo-2.0")}'" + + expect(bundled_app_gemfile.read).to match(/gem "foo", "~> 2.0", :path => "#{lib_path("foo-2.0")}"/) + expect(the_bundle).to include_gems "foo 2.0" + end + end + describe "with --git" do - it "adds dependency with specified github source" do + it "adds dependency with specified git source" do bundle "add foo --git=#{lib_path("foo-2.0")}" expect(bundled_app_gemfile.read).to match(/gem "foo", "~> 2.0", :git => "#{lib_path("foo-2.0")}"/) @@ -102,10 +131,10 @@ RSpec.describe "bundle add" do describe "with --git and --branch" do before do - update_git "foo", "2.0", :branch => "test" + update_git "foo", "2.0", branch: "test" end - it "adds dependency with specified github source and branch" do + it "adds dependency with specified git source and branch" do bundle "add foo --git=#{lib_path("foo-2.0")} --branch=test" expect(bundled_app_gemfile.read).to match(/gem "foo", "~> 2.0", :git => "#{lib_path("foo-2.0")}", :branch => "test"/) @@ -113,6 +142,94 @@ RSpec.describe "bundle add" do end end + describe "with --git and --ref" do + it "adds dependency with specified git source and branch" do + bundle "add foo --git=#{lib_path("foo-2.0")} --ref=#{revision_for(lib_path("foo-2.0"))}" + + expect(bundled_app_gemfile.read).to match(/gem "foo", "~> 2\.0", :git => "#{lib_path("foo-2.0")}", :ref => "#{revision_for(lib_path("foo-2.0"))}"/) + expect(the_bundle).to include_gems "foo 2.0" + end + end + + describe "with --github" do + it "adds dependency with specified github source", :realworld do + bundle "add rake --github=ruby/rake" + + expect(bundled_app_gemfile.read).to match(%r{gem "rake", "~> 13\.\d+", :github => "ruby\/rake"}) + end + end + + describe "with --github and --branch" do + it "adds dependency with specified github source and branch", :realworld do + bundle "add rake --github=ruby/rake --branch=master" + + expect(bundled_app_gemfile.read).to match(%r{gem "rake", "~> 13\.\d+", :github => "ruby\/rake", :branch => "master"}) + end + end + + describe "with --github and --ref" do + it "adds dependency with specified github source and ref", :realworld do + bundle "add rake --github=ruby/rake --ref=5c60da8" + + expect(bundled_app_gemfile.read).to match(%r{gem "rake", "~> 13\.\d+", :github => "ruby\/rake", :ref => "5c60da8"}) + end + end + + describe "with --git and --glob" do + it "adds dependency with specified git source" do + bundle "add foo --git=#{lib_path("foo-2.0")} --glob='./*.gemspec'" + + expect(bundled_app_gemfile.read).to match(%r{gem "foo", "~> 2.0", :git => "#{lib_path("foo-2.0")}", :glob => "\./\*\.gemspec"}) + expect(the_bundle).to include_gems "foo 2.0" + end + end + + describe "with --git and --branch and --glob" do + before do + update_git "foo", "2.0", branch: "test" + end + + it "adds dependency with specified git source and branch" do + bundle "add foo --git=#{lib_path("foo-2.0")} --branch=test --glob='./*.gemspec'" + + expect(bundled_app_gemfile.read).to match(%r{gem "foo", "~> 2.0", :git => "#{lib_path("foo-2.0")}", :branch => "test", :glob => "\./\*\.gemspec"}) + expect(the_bundle).to include_gems "foo 2.0" + end + end + + describe "with --git and --ref and --glob" do + it "adds dependency with specified git source and branch" do + bundle "add foo --git=#{lib_path("foo-2.0")} --ref=#{revision_for(lib_path("foo-2.0"))} --glob='./*.gemspec'" + + expect(bundled_app_gemfile.read).to match(%r{gem "foo", "~> 2\.0", :git => "#{lib_path("foo-2.0")}", :ref => "#{revision_for(lib_path("foo-2.0"))}", :glob => "\./\*\.gemspec"}) + expect(the_bundle).to include_gems "foo 2.0" + end + end + + describe "with --github and --glob" do + it "adds dependency with specified github source", :realworld do + bundle "add rake --github=ruby/rake --glob='./*.gemspec'" + + expect(bundled_app_gemfile.read).to match(%r{gem "rake", "~> 13\.\d+", :github => "ruby\/rake", :glob => "\.\/\*\.gemspec"}) + end + end + + describe "with --github and --branch --and glob" do + it "adds dependency with specified github source and branch", :realworld do + bundle "add rake --github=ruby/rake --branch=master --glob='./*.gemspec'" + + expect(bundled_app_gemfile.read).to match(%r{gem "rake", "~> 13\.\d+", :github => "ruby\/rake", :branch => "master", :glob => "\.\/\*\.gemspec"}) + end + end + + describe "with --github and --ref and --glob" do + it "adds dependency with specified github source and ref", :realworld do + bundle "add rake --github=ruby/rake --ref=5c60da8 --glob='./*.gemspec'" + + expect(bundled_app_gemfile.read).to match(%r{gem "rake", "~> 13\.\d+", :github => "ruby\/rake", :ref => "5c60da8", :glob => "\.\/\*\.gemspec"}) + end + end + describe "with --skip-install" do it "adds gem to Gemfile but is not installed" do bundle "add foo --skip-install --version=2.0" @@ -129,24 +246,24 @@ RSpec.describe "bundle add" do end it "shows error message when version is not formatted correctly" do - bundle "add 'foo' -v='~>1 . 0'", :raise_on_error => false + bundle "add 'foo' -v='~>1 . 0'", raise_on_error: false expect(err).to match("Invalid gem requirement pattern '~>1 . 0'") end it "shows error message when gem cannot be found" do bundle "config set force_ruby_platform true" - bundle "add 'werk_it'", :raise_on_error => false + bundle "add 'werk_it'", raise_on_error: false expect(err).to match("Could not find gem 'werk_it' in") - bundle "add 'werk_it' -s='#{file_uri_for(gem_repo2)}'", :raise_on_error => false + bundle "add 'werk_it' -s='#{file_uri_for(gem_repo2)}'", raise_on_error: false expect(err).to match("Could not find gem 'werk_it' in rubygems repository") end it "shows error message when source cannot be reached" do - bundle "add 'baz' --source='http://badhostasdf'", :raise_on_error => false + bundle "add 'baz' --source='http://badhostasdf'", raise_on_error: false expect(err).to include("Could not reach host badhostasdf. Check your network connection and try again.") - bundle "add 'baz' --source='file://does/not/exist'", :raise_on_error => false + bundle "add 'baz' --source='file://does/not/exist'", raise_on_error: false expect(err).to include("Could not fetch specs from file://does/not/exist/") end @@ -176,7 +293,7 @@ RSpec.describe "bundle add" do describe "with --optimistic and --strict" do it "throws error" do - bundle "add 'foo' --strict --optimistic", :raise_on_error => false + bundle "add 'foo' --strict --optimistic", raise_on_error: false expect(err).to include("You can not specify `--strict` and `--optimistic` at the same time") end @@ -191,7 +308,7 @@ RSpec.describe "bundle add" do end it "throws error if any of the specified gems are present in the gemfile with different version" do - bundle "add weakling bar", :raise_on_error => false + bundle "add weakling bar", raise_on_error: false expect(err).to include("You cannot specify the same gem twice with different version requirements") expect(err).to include("You specified: weakling (~> 0.0.1) and weakling (>= 0).") @@ -205,7 +322,7 @@ RSpec.describe "bundle add" do gem "rack", "1.0" G - bundle "add 'rack' --version=1.1", :raise_on_error => false + bundle "add 'rack' --version=1.1", raise_on_error: false expect(err).to include("You cannot specify the same gem twice with different version requirements") expect(err).to include("If you want to update the gem version, run `bundle update rack`. You may also need to change the version requirement specified in the Gemfile if it's too restrictive") @@ -217,7 +334,7 @@ RSpec.describe "bundle add" do gem "rack", "1.0" G - bundle "add 'rack'", :raise_on_error => false + bundle "add 'rack'", raise_on_error: false expect(err).to include("Gem already added.") expect(err).to include("You cannot specify the same gem twice with different version requirements") @@ -232,7 +349,7 @@ RSpec.describe "bundle add" do gem "rack" G - bundle "add 'rack' --version=1.1", :raise_on_error => false + bundle "add 'rack' --version=1.1", raise_on_error: false expect(err).to include("You cannot specify the same gem twice with different version requirements") expect(err).to include("If you want to update the gem version, run `bundle update rack`.") diff --git a/spec/bundler/commands/binstubs_spec.rb b/spec/bundler/commands/binstubs_spec.rb index 1cd0e16d95..6c3dc7bb2d 100644 --- a/spec/bundler/commands/binstubs_spec.rb +++ b/spec/bundler/commands/binstubs_spec.rb @@ -45,7 +45,7 @@ RSpec.describe "bundle binstubs <gem>" do gem "rails" G - bundle :binstubs, :all => true + bundle :binstubs, all: true expect(bundled_app("bin/rails")).to exist expect(bundled_app("bin/rake")).to exist @@ -69,7 +69,7 @@ RSpec.describe "bundle binstubs <gem>" do gem "rack" G - bundle "binstubs", :raise_on_error => false + bundle "binstubs", raise_on_error: false expect(exitstatus).to eq(1) expect(err).to include("`bundle binstubs` needs at least one gem to run.") end @@ -80,7 +80,7 @@ RSpec.describe "bundle binstubs <gem>" do gem "rack" G - bundle "binstubs rack", :all => true, :raise_on_error => false + bundle "binstubs rack", all: true, raise_on_error: false expect(last_command).to be_failure expect(err).to include("Cannot specify --all with specific gems") end @@ -98,7 +98,7 @@ RSpec.describe "bundle binstubs <gem>" do file.print "OMG" end - sys_exec "bin/rackup", :raise_on_error => false + sys_exec "bin/rackup", raise_on_error: false expect(err).to include("was not generated by Bundler") end @@ -132,13 +132,13 @@ RSpec.describe "bundle binstubs <gem>" do let(:system_bundler_version) { Bundler::VERSION } it "runs bundler" do - sys_exec "bin/bundle install", :env => { "DEBUG" => "1" } + sys_exec "bin/bundle install", env: { "DEBUG" => "1" } expect(out).to include %(Using bundler #{system_bundler_version}\n) end context "when BUNDLER_VERSION is set" do it "runs the correct version of bundler" do - sys_exec "bin/bundle install", :env => { "BUNDLER_VERSION" => "999.999.999" }, :raise_on_error => false + sys_exec "bin/bundle install", env: { "BUNDLER_VERSION" => "999.999.999" }, raise_on_error: false expect(exitstatus).to eq(42) expect(err).to include("Activating bundler (999.999.999) failed:"). and include("To install the version of bundler this project requires, run `gem install bundler -v '999.999.999'`") @@ -147,7 +147,7 @@ RSpec.describe "bundle binstubs <gem>" do it "runs the correct version of bundler even if a higher version is installed" do system_gems "bundler-999.999.998", "bundler-999.999.999" - sys_exec "bin/bundle install", :env => { "BUNDLER_VERSION" => "999.999.998", "DEBUG" => "1" }, :raise_on_error => false + sys_exec "bin/bundle install", env: { "BUNDLER_VERSION" => "999.999.998", "DEBUG" => "1" }, raise_on_error: false expect(out).to include %(Using bundler 999.999.998\n) end end @@ -159,7 +159,22 @@ RSpec.describe "bundle binstubs <gem>" do end it "runs the correct version of bundler" do - sys_exec "bin/bundle install", :raise_on_error => false + sys_exec "bin/bundle install", raise_on_error: false + expect(exitstatus).to eq(42) + expect(err).to include("Activating bundler (~> 999.999) failed:"). + and include("To install the version of bundler this project requires, run `gem install bundler -v '~> 999.999'`") + end + end + + context "and the version is newer when given `gems.rb` and `gems.locked`" do + before do + gemfile bundled_app("gems.rb"), gemfile + lockfile bundled_app("gems.locked"), lockfile.gsub(system_bundler_version, "999.999") + end + + it "runs the correct version of bundler" do + sys_exec "bin/bundle install", env: { "BUNDLE_GEMFILE" => "gems.rb" }, raise_on_error: false + expect(exitstatus).to eq(42) expect(err).to include("Activating bundler (~> 999.999) failed:"). and include("To install the version of bundler this project requires, run `gem install bundler -v '~> 999.999'`") @@ -174,7 +189,23 @@ RSpec.describe "bundle binstubs <gem>" do end it "runs the correct version of bundler" do - sys_exec "bin/bundle install", :raise_on_error => false + sys_exec "bin/bundle install", raise_on_error: false + expect(exitstatus).to eq(42) + expect(err).to include("Activating bundler (~> 44.0) failed:"). + and include("To install the version of bundler this project requires, run `gem install bundler -v '~> 44.0'`") + end + end + + context "and the version is older and a different major when given `gems.rb` and `gems.locked`" do + let(:system_bundler_version) { "55" } + + before do + gemfile bundled_app("gems.rb"), gemfile + lockfile bundled_app("gems.locked"), lockfile.gsub(/BUNDLED WITH\n .*$/m, "BUNDLED WITH\n 44.0") + end + + it "runs the correct version of bundler" do + sys_exec "bin/bundle install", env: { "BUNDLE_GEMFILE" => "gems.rb" }, raise_on_error: false expect(exitstatus).to eq(42) expect(err).to include("Activating bundler (~> 44.0) failed:"). and include("To install the version of bundler this project requires, run `gem install bundler -v '~> 44.0'`") @@ -182,16 +213,26 @@ RSpec.describe "bundle binstubs <gem>" do end context "and the version is older and the same major" do - let(:system_bundler_version) { "55.1" } + let(:system_bundler_version) { "2.999.999" } before do - lockfile lockfile.gsub(/BUNDLED WITH\n .*$/m, "BUNDLED WITH\n 55.0") + lockfile lockfile.gsub(/BUNDLED WITH\n .*$/m, "BUNDLED WITH\n 2.3.0") + end + + it "installs and runs the exact version of bundler", rubygems: ">= 3.3.0.dev", realworld: true do + sys_exec "bin/bundle install --verbose", artifice: "vcr" + expect(exitstatus).not_to eq(42) + expect(out).to include("Bundler 2.999.999 is running, but your lockfile was generated with 2.3.0. Installing Bundler 2.3.0 and restarting using that version.") + expect(out).to include("Using bundler 2.3.0") + expect(err).not_to include("Activating bundler (~> 2.3.0) failed:") end - it "runs the available version of bundler when the version is older and the same major" do - sys_exec "bin/bundle install" + it "runs the available version of bundler", rubygems: "< 3.3.0.dev" do + sys_exec "bin/bundle install --verbose" expect(exitstatus).not_to eq(42) - expect(err).not_to include("Activating bundler (~> 55.0) failed:") + expect(out).not_to include("Bundler 2.999.999 is running, but your lockfile was generated with 2.3.0. Installing Bundler 2.3.0 and restarting using that version.") + expect(out).to include("Using bundler 2.999.999") + expect(err).not_to include("Activating bundler (~> 2.3.0) failed:") end end @@ -203,7 +244,7 @@ RSpec.describe "bundle binstubs <gem>" do end it "runs the correct version of bundler when the version is a pre-release" do - sys_exec "bin/bundle install", :raise_on_error => false + sys_exec "bin/bundle install", raise_on_error: false expect(exitstatus).to eq(42) expect(err).to include("Activating bundler (~> 2.12.a) failed:"). and include("To install the version of bundler this project requires, run `gem install bundler -v '~> 2.12.a'`") @@ -214,13 +255,16 @@ RSpec.describe "bundle binstubs <gem>" do context "when update --bundler is called" do before { lockfile.gsub(system_bundler_version, "1.1.1") } - it "calls through to the latest bundler version" do - sys_exec "bin/bundle update --bundler", :env => { "DEBUG" => "1" } - expect(out).to include %(Using bundler #{system_bundler_version}\n) + it "calls through to the latest bundler version", :realworld do + sys_exec "bin/bundle update --bundler", env: { "DEBUG" => "1" } + using_bundler_line = /Using bundler ([\w\.]+)\n/.match(out) + expect(using_bundler_line).to_not be_nil + latest_version = using_bundler_line[1] + expect(Gem::Version.new(latest_version)).to be >= Gem::Version.new(system_bundler_version) end it "calls through to the explicit bundler version" do - sys_exec "bin/bundle update --bundler=999.999.999", :raise_on_error => false + sys_exec "bin/bundle update --bundler=999.999.999", raise_on_error: false expect(exitstatus).to eq(42) expect(err).to include("Activating bundler (999.999.999) failed:"). and include("To install the version of bundler this project requires, run `gem install bundler -v '999.999.999'`") @@ -230,7 +274,7 @@ RSpec.describe "bundle binstubs <gem>" do context "without a lockfile" do it "falls back to the latest installed bundler" do FileUtils.rm bundled_app_lock - sys_exec "bin/bundle install", :env => { "DEBUG" => "1" } + sys_exec "bin/bundle install", env: { "DEBUG" => "1" } expect(out).to include "Using bundler #{system_bundler_version}\n" end end @@ -245,7 +289,7 @@ RSpec.describe "bundle binstubs <gem>" do before { lockfile lockfile.gsub(Bundler::VERSION, "999.999.999") } it "attempts to load that version" do - sys_exec bundled_app("bin/rackup").to_s, :raise_on_error => false + sys_exec bundled_app("bin/rackup").to_s, raise_on_error: false expect(exitstatus).to eq(42) expect(err).to include("Activating bundler (~> 999.999) failed:"). and include("To install the version of bundler this project requires, run `gem install bundler -v '~> 999.999'`") @@ -257,7 +301,7 @@ RSpec.describe "bundle binstubs <gem>" do it "installs binstubs from git gems" do FileUtils.mkdir_p(lib_path("foo/bin")) FileUtils.touch(lib_path("foo/bin/foo")) - build_git "foo", "1.0", :path => lib_path("foo") do |s| + build_git "foo", "1.0", path: lib_path("foo") do |s| s.executables = %w[foo] end install_gemfile <<-G @@ -273,7 +317,7 @@ RSpec.describe "bundle binstubs <gem>" do it "installs binstubs from path gems" do FileUtils.mkdir_p(lib_path("foo/bin")) FileUtils.touch(lib_path("foo/bin/foo")) - build_lib "foo", "1.0", :path => lib_path("foo") do |s| + build_lib "foo", "1.0", path: lib_path("foo") do |s| s.executables = %w[foo] end install_gemfile <<-G @@ -318,7 +362,7 @@ RSpec.describe "bundle binstubs <gem>" do source "#{file_uri_for(gem_repo1)}" G - bundle "binstubs doesnt_exist", :raise_on_error => false + bundle "binstubs doesnt_exist", raise_on_error: false expect(exitstatus).to eq(7) expect(err).to include("Could not find gem 'doesnt_exist'.") @@ -337,14 +381,14 @@ RSpec.describe "bundle binstubs <gem>" do expect(bundled_app("exec/rackup")).to exist end - it "setting is saved for bundle install", :bundler => "< 3" do + it "setting is saved for bundle install", bundler: "< 3" do install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" gem "rack" gem "rails" G - bundle "binstubs rack", :path => "exec" + bundle "binstubs rack", path: "exec" bundle :install expect(bundled_app("exec/rails")).to exist @@ -356,6 +400,7 @@ RSpec.describe "bundle binstubs <gem>" do install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" gem "rack" + gem "rails" G end @@ -383,6 +428,26 @@ RSpec.describe "bundle binstubs <gem>" do expect(bundled_app("bin/rackup.cmd")).to exist end end + + context "when the gem is bundler" do + it "warns without generating a standalone binstub" do + bundle "binstubs bundler --standalone" + expect(bundled_app("bin/bundle")).not_to exist + expect(bundled_app("bin/bundler")).not_to exist + expect(err).to include("Sorry, Bundler can only be run via RubyGems.") + end + end + + context "when specified --all option" do + it "generates standalone binstubs for all gems except bundler" do + bundle "binstubs --standalone --all" + expect(bundled_app("bin/rackup")).to exist + expect(bundled_app("bin/rails")).to exist + expect(bundled_app("bin/bundle")).not_to exist + expect(bundled_app("bin/bundler")).not_to exist + expect(err).not_to include("Sorry, Bundler can only be run via RubyGems.") + end + end end context "when the bin already exists" do @@ -484,7 +549,7 @@ RSpec.describe "bundle binstubs <gem>" do G bundle "config set auto_install 1" - bundle "binstubs rack", :env => { "BUNDLE_INSTALL" => "1" } + bundle "binstubs rack", env: { "BUNDLE_INSTALL" => "1" } expect(out).not_to include("Installing rack 1.0.0") end end diff --git a/spec/bundler/commands/cache_spec.rb b/spec/bundler/commands/cache_spec.rb index 4f9c1c26c4..70e2c84961 100644 --- a/spec/bundler/commands/cache_spec.rb +++ b/spec/bundler/commands/cache_spec.rb @@ -158,7 +158,7 @@ RSpec.describe "bundle cache" do end end - context "with --path", :bundler => "< 3" do + context "with --path", bundler: "< 3" do it "sets root directory for gems" do gemfile <<-D source "#{file_uri_for(gem_repo1)}" @@ -211,7 +211,17 @@ RSpec.describe "bundle cache" do end context "with --all-platforms" do - it "puts the gems in vendor/cache even for other rubies" do + it "puts the gems in vendor/cache even for other rubies", bundler: ">= 2.4.0" do + gemfile <<-D + source "#{file_uri_for(gem_repo1)}" + gem 'rack', :platforms => [:ruby_20, :windows_20] + D + + bundle "cache --all-platforms" + expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist + end + + it "puts the gems in vendor/cache even for legacy windows rubies", bundler: ">= 2.4.0" do gemfile <<-D source "#{file_uri_for(gem_repo1)}" gem 'rack', :platforms => [:ruby_20, :x64_mingw_20] @@ -262,7 +272,7 @@ RSpec.describe "bundle cache" do end G - bundle :lock, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo1.to_s } + bundle :lock, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo1.to_s } bundle :cache, "all-platforms" => true, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo1.to_s } expect(bundled_app("vendor/cache/weakling-0.0.3.gem")).to exist end @@ -279,7 +289,7 @@ RSpec.describe "bundle cache" do subject do bundle "config set --local frozen true" - bundle :cache, :raise_on_error => false + bundle :cache, raise_on_error: false end it "tries to install with frozen" do @@ -291,7 +301,7 @@ RSpec.describe "bundle cache" do G subject expect(exitstatus).to eq(16) - expect(err).to include("deployment mode") + expect(err).to include("frozen mode") expect(err).to include("You have added to the Gemfile") expect(err).to include("* rack-obama") bundle "env" @@ -383,7 +393,7 @@ RSpec.describe "bundle install with gem sources" do G bundle :cache - build_gem "rack", "1.0.0", :path => bundled_app("vendor/cache") do |s| + build_gem "rack", "1.0.0", path: bundled_app("vendor/cache") do |s| s.write "lib/rack.rb", "raise 'omg'" end @@ -403,14 +413,14 @@ RSpec.describe "bundle install with gem sources" do simulate_new_machine - simulate_platform "ruby" do - install_gemfile <<-G - source "#{file_uri_for(gem_repo1)}" - gem "platform_specific" - G - run "require 'platform_specific' ; puts PLATFORM_SPECIFIC" - expect(out).to eq("1.0.0 RUBY") - end + bundle "config set --local force_ruby_platform true" + + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "platform_specific" + G + run "require 'platform_specific' ; puts PLATFORM_SPECIFIC" + expect(out).to eq("1.0.0 RUBY") end it "does not update the cache if --no-cache is passed" do diff --git a/spec/bundler/commands/check_spec.rb b/spec/bundler/commands/check_spec.rb index 1fa35136eb..02f9bb5b7a 100644 --- a/spec/bundler/commands/check_spec.rb +++ b/spec/bundler/commands/check_spec.rb @@ -17,7 +17,7 @@ RSpec.describe "bundle check" do gem "rails" G - bundle "check --gemfile bundled_app/Gemfile", :dir => tmp + bundle "check --gemfile bundled_app/Gemfile", dir: tmp expect(out).to include("The Gemfile's dependencies are satisfied") end @@ -55,7 +55,7 @@ RSpec.describe "bundle check" do gem "rails" G - bundle :check, :raise_on_error => false + bundle :check, raise_on_error: false expect(err).to include("Bundler can't satisfy your Gemfile's dependencies.") end @@ -65,7 +65,7 @@ RSpec.describe "bundle check" do gem "rails" G - bundle :check, :raise_on_error => false + bundle :check, raise_on_error: false expect(exitstatus).to be > 0 expect(err).to include("Bundler can't satisfy your Gemfile's dependencies.") end @@ -88,11 +88,11 @@ RSpec.describe "bundle check" do gem "rails_pinned_to_old_activesupport" G - bundle :check, :raise_on_error => false + bundle :check, raise_on_error: false expect(err).to include("Bundler can't satisfy your Gemfile's dependencies.") end - it "remembers --without option from install", :bundler => "< 3" do + it "remembers --without option from install", bundler: "< 3" do gemfile <<-G source "#{file_uri_for(gem_repo1)}" group :foo do @@ -132,7 +132,7 @@ RSpec.describe "bundle check" do gem "rack" G - bundle "check", :raise_on_error => false + bundle "check", raise_on_error: false expect(err).to include("* rack (1.0.0)") expect(exitstatus).to eq(1) end @@ -146,9 +146,9 @@ RSpec.describe "bundle check" do bundle "config set --local path vendor/bundle" bundle :cache - gem_command "uninstall rack", :env => { "GEM_HOME" => vendored_gems.to_s } + gem_command "uninstall rack", env: { "GEM_HOME" => vendored_gems.to_s } - bundle "check", :raise_on_error => false + bundle "check", raise_on_error: false expect(err).to include("* rack (1.0.0)") expect(exitstatus).to eq(1) end @@ -162,7 +162,7 @@ RSpec.describe "bundle check" do end G - system_gems "rack-1.0.0", :path => default_bundle_path + system_gems "rack-1.0.0", path: default_bundle_path lockfile <<-G GEM @@ -172,7 +172,7 @@ RSpec.describe "bundle check" do rack (1.0.0) PLATFORMS - #{local} + #{generic_local_platform} #{not_local} DEPENDENCIES @@ -193,7 +193,7 @@ RSpec.describe "bundle check" do end G - system_gems "rack-1.0.0", :path => default_bundle_path + system_gems "rack-1.0.0", path: default_bundle_path lockfile <<-G GEM @@ -203,7 +203,7 @@ RSpec.describe "bundle check" do rack (1.0.0) PLATFORMS - #{local} + #{generic_local_platform} #{not_local} DEPENDENCIES @@ -216,13 +216,13 @@ RSpec.describe "bundle check" do end it "outputs an error when the default Gemfile is not found" do - bundle :check, :raise_on_error => false + bundle :check, raise_on_error: false expect(exitstatus).to eq(10) expect(err).to include("Could not locate Gemfile") end it "does not output fatal error message" do - bundle :check, :raise_on_error => false + bundle :check, raise_on_error: false expect(exitstatus).to eq(10) expect(err).not_to include("Unfortunately, a fatal error has occurred. ") end @@ -237,11 +237,11 @@ RSpec.describe "bundle check" do bundle "install" FileUtils.rm(bundled_app_lock) - bundle :check, :raise_on_error => false + bundle :check, raise_on_error: false expect(last_command).to be_failure end - context "--path", :bundler => "< 3" do + context "--path", bundler: "< 3" do context "after installing gems in the proper directory" do before do gemfile <<-G @@ -271,7 +271,7 @@ RSpec.describe "bundle check" do gem "rails" G - bundle "check --path vendor/bundle", :raise_on_error => false + bundle "check --path vendor/bundle", raise_on_error: false end it "returns false" do @@ -298,7 +298,7 @@ RSpec.describe "bundle check" do it "shows what is missing with the current Gemfile if it is not satisfied" do simulate_new_machine - bundle :check, :raise_on_error => false + bundle :check, raise_on_error: false expect(err).to match(/The following gems are missing/) expect(err).to include("* rack (1.0") end @@ -326,7 +326,7 @@ RSpec.describe "bundle check" do end it "shows what is missing with the current Gemfile without duplications" do - bundle :check, :raise_on_error => false + bundle :check, raise_on_error: false expect(err).to match(/The following gems are missing/) expect(err).to include("* rack (1.0").once end @@ -351,7 +351,7 @@ RSpec.describe "bundle check" do PLATFORMS ruby - #{specific_local_platform} + #{local_platform} DEPENDENCIES rack @@ -362,7 +362,7 @@ RSpec.describe "bundle check" do end it "shows what is missing with the current Gemfile without duplications" do - bundle :check, :raise_on_error => false + bundle :check, raise_on_error: false expect(err).to match(/The following gems are missing/) expect(err).to include("* rack (1.0").once end @@ -379,7 +379,7 @@ RSpec.describe "bundle check" do end it "returns success when the Gemfile is satisfied" do - system_gems "rack-1.0.0", :path => default_bundle_path + system_gems "rack-1.0.0", path: default_bundle_path bundle :check expect(out).to include("The Gemfile's dependencies are satisfied") end @@ -404,8 +404,14 @@ RSpec.describe "bundle check" do end it "returns success when the Gemfile is satisfied and generates a correct lockfile" do - system_gems "depends_on_rack-1.0", "rack-1.0", :gem_repo => gem_repo4, :path => default_bundle_path + system_gems "depends_on_rack-1.0", "rack-1.0", gem_repo: gem_repo4, path: default_bundle_path bundle :check + + checksums = checksums_section_when_existing do |c| + c.no_checksum "depends_on_rack", "1.0" + c.no_checksum "rack", "1.0" + end + expect(out).to include("The Gemfile's dependencies are satisfied") expect(lockfile).to eq <<~L GEM @@ -424,7 +430,76 @@ RSpec.describe "bundle check" do DEPENDENCIES depends_on_rack! + #{checksums} + BUNDLED WITH + #{Bundler::VERSION} + L + end + end + + context "with gemspec directive and scoped sources" do + before do + build_repo4 do + build_gem "awesome_print" + end + + build_repo2 do + build_gem "dex-dispatch-engine" + end + + build_lib("bundle-check-issue", path: tmp.join("bundle-check-issue")) do |s| + s.write "Gemfile", <<-G + source "https://localgemserver.test" + + gemspec + + source "https://localgemserver.test/extra" do + gem "dex-dispatch-engine" + end + G + + s.add_dependency "awesome_print" + end + + bundle "install", artifice: "compact_index_extra", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s }, dir: tmp.join("bundle-check-issue") + end + + it "does not corrupt lockfile when changing version" do + version_file = tmp.join("bundle-check-issue/bundle-check-issue.gemspec") + File.write(version_file, File.read(version_file).gsub(/s\.version = .+/, "s.version = '9999'")) + + bundle "check --verbose", dir: tmp.join("bundle-check-issue") + checksums = checksums_section_when_existing do |c| + c.checksum gem_repo4, "awesome_print", "1.0" + c.no_checksum "bundle-check-issue", "9999" + c.checksum gem_repo2, "dex-dispatch-engine", "1.0" + end + + expect(File.read(tmp.join("bundle-check-issue/Gemfile.lock"))).to eq <<~L + PATH + remote: . + specs: + bundle-check-issue (9999) + awesome_print + + GEM + remote: https://localgemserver.test/ + specs: + awesome_print (1.0) + + GEM + remote: https://localgemserver.test/extra/ + specs: + dex-dispatch-engine (1.0) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + bundle-check-issue! + dex-dispatch-engine! + #{checksums} BUNDLED WITH #{Bundler::VERSION} L @@ -471,10 +546,10 @@ RSpec.describe "bundle check" do end context "is newer" do - it "does not change the lock but warns" do + it "does not change the lock and does not warn" do lockfile lock_with(Bundler::VERSION.succ) bundle :check - expect(err).to include("the running version of Bundler (#{Bundler::VERSION}) is older than the version that created the lockfile (#{Bundler::VERSION.succ})") + expect(err).to be_empty expect(lockfile).to eq lock_with(Bundler::VERSION.succ) end end diff --git a/spec/bundler/commands/clean_spec.rb b/spec/bundler/commands/clean_spec.rb index ffaf22dbb3..0b559a87c8 100644 --- a/spec/bundler/commands/clean_spec.rb +++ b/spec/bundler/commands/clean_spec.rb @@ -156,7 +156,7 @@ RSpec.describe "bundle clean" do end it "removes unused git gems" do - build_git "foo", :path => lib_path("foo") + build_git "foo", path: lib_path("foo") git_path = lib_path("foo") revision = revision_for(git_path) @@ -194,7 +194,7 @@ RSpec.describe "bundle clean" do end it "keeps used git gems even if installed to a symlinked location" do - build_git "foo", :path => lib_path("foo") + build_git "foo", path: lib_path("foo") git_path = lib_path("foo") revision = revision_for(git_path) @@ -208,7 +208,7 @@ RSpec.describe "bundle clean" do G FileUtils.mkdir_p(bundled_app("real-path")) - FileUtils.ln_sf(bundled_app("real-path"), bundled_app("symlink-path")) + File.symlink(bundled_app("real-path"), bundled_app("symlink-path")) bundle "config set path #{bundled_app("symlink-path")}" bundle "install" @@ -221,7 +221,7 @@ RSpec.describe "bundle clean" do end it "removes old git gems" do - build_git "foo-bar", :path => lib_path("foo-bar") + build_git "foo-bar", path: lib_path("foo-bar") revision = revision_for(lib_path("foo-bar")) gemfile <<-G @@ -236,10 +236,10 @@ RSpec.describe "bundle clean" do bundle "config set path vendor/bundle" bundle "install" - update_git "foo-bar", :path => lib_path("foo-bar") + update_git "foo-bar", path: lib_path("foo-bar") revision2 = revision_for(lib_path("foo-bar")) - bundle "update", :all => true + bundle "update", all: true bundle :clean expect(out).to include("Removing foo-bar (#{revision[0..11]})") @@ -254,8 +254,8 @@ RSpec.describe "bundle clean" do end it "does not remove nested gems in a git repo" do - build_lib "activesupport", "3.0", :path => lib_path("rails/activesupport") - build_git "rails", "3.0", :path => lib_path("rails") do |s| + build_lib "activesupport", "3.0", path: lib_path("rails/activesupport") + build_git "rails", "3.0", path: lib_path("rails") do |s| s.add_dependency "activesupport", "= 3.0" end revision = revision_for(lib_path("rails")) @@ -274,7 +274,7 @@ RSpec.describe "bundle clean" do end it "does not remove git sources that are in without groups" do - build_git "foo", :path => lib_path("foo") + build_git "foo", path: lib_path("foo") git_path = lib_path("foo") revision = revision_for(git_path) @@ -326,7 +326,7 @@ RSpec.describe "bundle clean" do gem "rack", "1.0.0" G - bundle :clean, :raise_on_error => false + bundle :clean, raise_on_error: false expect(exitstatus).to eq(15) expect(err).to include("--force") @@ -383,7 +383,7 @@ RSpec.describe "bundle clean" do expect(out).to include("rack (1.0.0)").and include("thin (1.0)") end - it "--clean should override the bundle setting on install", :bundler => "< 3" do + it "--clean should override the bundle setting on install", bundler: "< 3" do gemfile <<-G source "#{file_uri_for(gem_repo1)}" @@ -405,7 +405,7 @@ RSpec.describe "bundle clean" do should_not_have_gems "thin-1.0" end - it "--clean should override the bundle setting on update", :bundler => "< 3" do + it "--clean should override the bundle setting on update", bundler: "< 3" do build_repo2 gemfile <<-G @@ -421,13 +421,13 @@ RSpec.describe "bundle clean" do build_gem "foo", "1.0.1" end - bundle "update", :all => true + bundle "update", all: true should_have_gems "foo-1.0.1" should_not_have_gems "foo-1.0" end - it "automatically cleans when path has not been set", :bundler => "3" do + it "automatically cleans when path has not been set", bundler: "3" do build_repo2 install_gemfile <<-G @@ -440,7 +440,7 @@ RSpec.describe "bundle clean" do build_gem "foo", "1.0.1" end - bundle "update", :all => true + bundle "update", all: true files = Pathname.glob(bundled_app(".bundle", Bundler.ruby_scope, "*", "*")) files.map! {|f| f.to_s.sub(bundled_app(".bundle", Bundler.ruby_scope).to_s, "") } @@ -486,7 +486,7 @@ RSpec.describe "bundle clean" do build_gem "foo", "1.0.1" end - bundle :update, :all => true + bundle :update, all: true should_have_gems "foo-1.0", "foo-1.0.1" end @@ -505,7 +505,7 @@ RSpec.describe "bundle clean" do update_repo2 do build_gem "foo", "1.0.1" end - bundle :update, :all => true + bundle :update, all: true gem_command :list expect(out).to include("foo (1.0.1, 1.0)") @@ -560,7 +560,7 @@ RSpec.describe "bundle clean" do FileUtils.chmod(0o500, system_cache_path) - bundle :clean, :force => true, :raise_on_error => false + bundle :clean, force: true, raise_on_error: false expect(err).to include(system_gem_path.to_s) expect(err).to include("grant write permissions") @@ -625,26 +625,22 @@ RSpec.describe "bundle clean" do expect(out).to eq("1.0") end - it "when using --force, it doesn't remove default gem binaries" do - skip "does not work on old rubies because the realworld gems that need to be installed don't support them" if RUBY_VERSION < "2.7.0" - - skip "does not work on rubygems versions where `--install_dir` doesn't respect --default" unless Gem::Installer.for_spec(loaded_gemspec, :install_dir => "/foo").default_spec_file == "/foo/specifications/default/bundler-#{Bundler::VERSION}.gemspec" # Since rubygems 3.2.0.rc.2 - - default_irb_version = ruby "gem 'irb', '< 999999'; require 'irb'; puts IRB::VERSION", :raise_on_error => false + it "when using --force, it doesn't remove default gem binaries", :realworld do + default_irb_version = ruby "gem 'irb', '< 999999'; require 'irb'; puts IRB::VERSION", raise_on_error: false skip "irb isn't a default gem" if default_irb_version.empty? # simulate executable for default gem - build_gem "irb", default_irb_version, :to_system => true, :default => true do |s| + build_gem "irb", default_irb_version, to_system: true, default: true do |s| s.executables = "irb" end - realworld_system_gems "fiddle --version 1.0.6", "tsort --version 0.1.0", "pathname --version 0.1.0", "set --version 1.0.1" + realworld_system_gems "tsort --version 0.1.0", "pathname --version 0.1.0", "set --version 1.0.1" install_gemfile <<-G source "#{file_uri_for(gem_repo2)}" G - bundle "clean --force", :env => { "BUNDLER_GEM_DEFAULT_DIR" => system_gem_path.to_s } + bundle "clean --force", env: { "BUNDLER_GEM_DEFAULT_DIR" => system_gem_path.to_s } expect(out).not_to include("Removing irb") end @@ -787,7 +783,7 @@ RSpec.describe "bundle clean" do should_not_have_gems "foo-1.0" end - it "doesn't remove extensions artifacts from bundled git gems after clean", :ruby_repo do + it "doesn't remove extensions artifacts from bundled git gems after clean" do build_git "very_simple_git_binary", &:add_c_extension revision = revision_for(lib_path("very_simple_git_binary-1.0")) @@ -810,7 +806,7 @@ RSpec.describe "bundle clean" do expect(vendored_gems("bundler/gems/very_simple_git_binary-1.0-#{revision[0..11]}")).to exist end - it "removes extension directories", :ruby_repo do + it "removes extension directories" do gemfile <<-G source "#{file_uri_for(gem_repo1)}" @@ -846,7 +842,7 @@ RSpec.describe "bundle clean" do expect(simple_binary_extensions_dir).to exist end - it "removes git extension directories", :ruby_repo do + it "removes git extension directories" do build_git "very_simple_git_binary", &:add_c_extension revision = revision_for(lib_path("very_simple_git_binary-1.0")) @@ -905,7 +901,7 @@ RSpec.describe "bundle clean" do bundle :lock bundle "config set without development" bundle "config set path vendor/bundle" - bundle "install" + bundle "install", verbose: true bundle :clean very_simple_binary_extensions_dir = diff --git a/spec/bundler/commands/config_spec.rb b/spec/bundler/commands/config_spec.rb index 2d0a7dc989..547fd2d869 100644 --- a/spec/bundler/commands/config_spec.rb +++ b/spec/bundler/commands/config_spec.rb @@ -28,7 +28,7 @@ RSpec.describe ".bundle/config" do context "with env overwrite" do it "prints config with env" do - bundle "config list --parseable", :env => { "BUNDLE_FOO" => "bar3" } + bundle "config list --parseable", env: { "BUNDLE_FOO" => "bar3" } expect(out).to include("foo=bar3") end end @@ -43,6 +43,12 @@ RSpec.describe ".bundle/config" do G end + it "is local by default" do + bundle "config set foo bar" + expect(bundled_app(".bundle/config")).to exist + expect(home(".bundle/config")).not_to exist + end + it "can be moved with an environment variable" do ENV["BUNDLE_APP_CONFIG"] = tmp("foo/bar").to_s bundle "config set --local path vendor/bundle" @@ -58,15 +64,21 @@ RSpec.describe ".bundle/config" do ENV["BUNDLE_APP_CONFIG"] = "../foo" bundle "config set --local path vendor/bundle" - bundle "install", :dir => bundled_app("omg") + bundle "install", dir: bundled_app("omg") expect(bundled_app(".bundle")).not_to exist expect(bundled_app("../foo/config")).to exist - expect(the_bundle).to include_gems "rack 1.0.0", :dir => bundled_app("omg") + expect(the_bundle).to include_gems "rack 1.0.0", dir: bundled_app("omg") end end describe "location without a gemfile" do + it "is global by default" do + bundle "config set foo bar" + expect(bundled_app(".bundle/config")).not_to exist + expect(home(".bundle/config")).to exist + end + it "works with an absolute path" do ENV["BUNDLE_APP_CONFIG"] = tmp("foo/bar").to_s bundle "config set --local path vendor/bundle" @@ -84,8 +96,8 @@ RSpec.describe ".bundle/config" do end it "can be configured through BUNDLE_USER_CONFIG" do - bundle "config set path vendor", :env => { "BUNDLE_USER_CONFIG" => bundle_user_config } - bundle "config get path", :env => { "BUNDLE_USER_CONFIG" => bundle_user_config } + bundle "config set path vendor", env: { "BUNDLE_USER_CONFIG" => bundle_user_config } + bundle "config get path", env: { "BUNDLE_USER_CONFIG" => bundle_user_config } expect(out).to include("Set for the current user (#{bundle_user_config}): \"vendor\"") end @@ -93,8 +105,8 @@ RSpec.describe ".bundle/config" do let(:bundle_user_home) { bundled_app(".bundle").to_s } it "uses the right location" do - bundle "config set path vendor", :env => { "BUNDLE_USER_HOME" => bundle_user_home } - bundle "config get path", :env => { "BUNDLE_USER_HOME" => bundle_user_home } + bundle "config set path vendor", env: { "BUNDLE_USER_HOME" => bundle_user_home } + bundle "config get path", env: { "BUNDLE_USER_HOME" => bundle_user_home } expect(out).to include("Set for the current user (#{bundle_user_home}/config): \"vendor\"") end end @@ -131,17 +143,15 @@ RSpec.describe ".bundle/config" do end it "has lower precedence than env" do - begin - ENV["BUNDLE_FOO"] = "env" + ENV["BUNDLE_FOO"] = "env" - bundle "config set --global foo global" - expect(out).to match(/You have a bundler environment variable for foo set to "env"/) + bundle "config set --global foo global" + expect(out).to match(/You have a bundler environment variable for foo set to "env"/) - run "puts Bundler.settings[:foo]" - expect(out).to eq("env") - ensure - ENV.delete("BUNDLE_FOO") - end + run "puts Bundler.settings[:foo]" + expect(out).to eq("env") + ensure + ENV.delete("BUNDLE_FOO") end it "can be deleted" do @@ -209,15 +219,13 @@ RSpec.describe ".bundle/config" do end it "has higher precedence than env" do - begin - ENV["BUNDLE_FOO"] = "env" - bundle "config set --local foo local" - - run "puts Bundler.settings[:foo]" - expect(out).to eq("local") - ensure - ENV.delete("BUNDLE_FOO") - end + ENV["BUNDLE_FOO"] = "env" + bundle "config set --local foo local" + + run "puts Bundler.settings[:foo]" + expect(out).to eq("local") + ensure + ENV.delete("BUNDLE_FOO") end it "can be deleted" do @@ -359,7 +367,7 @@ E it "doesn't return quotes around values" do bundle "config set foo '1'" - run "puts Bundler.settings.send(:global_config_file).read" + run "puts Bundler.settings.send(:local_config_file).read" expect(out).to include('"1"') run "puts Bundler.settings[:foo]" expect(out).to eq("1") @@ -423,29 +431,57 @@ E end end + describe "commented out settings with urls" do + before do + bundle "config set #mirror.https://rails-assets.org http://localhost:9292" + end + + it "does not make bundler crash and ignores the configuration" do + bundle "config list --parseable" + + expect(out).to eq("#mirror.https://rails-assets.org/=http://localhost:9292") + expect(err).to be_empty + + ruby(<<~RUBY) + require "bundler" + print Bundler.settings.mirror_for("https://rails-assets.org") + RUBY + expect(out).to eq("https://rails-assets.org/") + expect(err).to be_empty + + bundle "config set mirror.all http://localhost:9293" + ruby(<<~RUBY) + require "bundler" + print Bundler.settings.mirror_for("https://rails-assets.org") + RUBY + expect(out).to eq("http://localhost:9293/") + expect(err).to be_empty + end + end + describe "subcommands" do it "list" do - bundle "config list" - expect(out).to eq "Settings are listed in order of priority. The top value will be used.\nspec_run\nSet via BUNDLE_SPEC_RUN: \"true\"" + bundle "config list", env: { "BUNDLE_FOO" => "bar" } + expect(out).to eq "Settings are listed in order of priority. The top value will be used.\nfoo\nSet via BUNDLE_FOO: \"bar\"" - bundle "config list", :parseable => true - expect(out).to eq "spec_run=true" + bundle "config list", env: { "BUNDLE_FOO" => "bar" }, parseable: true + expect(out).to eq "foo=bar" end it "list with credentials" do - bundle "config list", :env => { "BUNDLE_GEMS__MYSERVER__COM" => "user:password" } - expect(out).to eq "Settings are listed in order of priority. The top value will be used.\ngems.myserver.com\nSet via BUNDLE_GEMS__MYSERVER__COM: \"user:[REDACTED]\"\n\nspec_run\nSet via BUNDLE_SPEC_RUN: \"true\"" + bundle "config list", env: { "BUNDLE_GEMS__MYSERVER__COM" => "user:password" } + expect(out).to eq "Settings are listed in order of priority. The top value will be used.\ngems.myserver.com\nSet via BUNDLE_GEMS__MYSERVER__COM: \"user:[REDACTED]\"" - bundle "config list", :parseable => true, :env => { "BUNDLE_GEMS__MYSERVER__COM" => "user:password" } - expect(out).to eq "gems.myserver.com=user:password\nspec_run=true" + bundle "config list", parseable: true, env: { "BUNDLE_GEMS__MYSERVER__COM" => "user:password" } + expect(out).to eq "gems.myserver.com=user:password" end it "list with API token credentials" do - bundle "config list", :env => { "BUNDLE_GEMS__MYSERVER__COM" => "api_token:x-oauth-basic" } - expect(out).to eq "Settings are listed in order of priority. The top value will be used.\ngems.myserver.com\nSet via BUNDLE_GEMS__MYSERVER__COM: \"[REDACTED]:x-oauth-basic\"\n\nspec_run\nSet via BUNDLE_SPEC_RUN: \"true\"" + bundle "config list", env: { "BUNDLE_GEMS__MYSERVER__COM" => "api_token:x-oauth-basic" } + expect(out).to eq "Settings are listed in order of priority. The top value will be used.\ngems.myserver.com\nSet via BUNDLE_GEMS__MYSERVER__COM: \"[REDACTED]:x-oauth-basic\"" - bundle "config list", :parseable => true, :env => { "BUNDLE_GEMS__MYSERVER__COM" => "api_token:x-oauth-basic" } - expect(out).to eq "gems.myserver.com=api_token:x-oauth-basic\nspec_run=true" + bundle "config list", parseable: true, env: { "BUNDLE_GEMS__MYSERVER__COM" => "api_token:x-oauth-basic" } + expect(out).to eq "gems.myserver.com=api_token:x-oauth-basic" end it "get" do @@ -479,7 +515,7 @@ E bundle "config set --local foo 4.1" expect(out).to eq "You are replacing the current local value of foo, which is currently \"4\"" - bundle "config set --global --local foo 5", :raise_on_error => false + bundle "config set --global --local foo 5", raise_on_error: false expect(last_command).to be_failure expect(err).to eq "The options global and local were specified. Please only use one of the switches at a time." end @@ -519,7 +555,7 @@ E expect(out).to eq "" expect(bundle("config get foo")).to eq "Settings for `foo` in order of priority. The top value will be used\nYou have not configured a value for `foo`" - bundle "config unset foo --local --global", :raise_on_error => false + bundle "config unset foo --local --global", raise_on_error: false expect(last_command).to be_failure expect(err).to eq "The options global and local were specified. Please only use one of the switches at a time." end diff --git a/spec/bundler/commands/console_spec.rb b/spec/bundler/commands/console_spec.rb index aa76096e3d..a41432b88a 100644 --- a/spec/bundler/commands/console_spec.rb +++ b/spec/bundler/commands/console_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.describe "bundle console", :bundler => "< 3", :readline => true do +RSpec.describe "bundle console", bundler: "< 3", readline: true do before :each do build_repo2 do # A minimal fake pry console diff --git a/spec/bundler/commands/doctor_spec.rb b/spec/bundler/commands/doctor_spec.rb index 860b638f06..666b23a141 100644 --- a/spec/bundler/commands/doctor_spec.rb +++ b/spec/bundler/commands/doctor_spec.rb @@ -38,6 +38,11 @@ RSpec.describe "bundle doctor" do allow(stat).to receive(:uid) { Process.uid } allow(File).to receive(:writable?).with(unwritable_file) { true } allow(File).to receive(:readable?).with(unwritable_file) { true } + + # The following lines are for `Gem::PathSupport#initialize`. + allow(File).to receive(:exist?).with(Gem.default_dir) + allow(File).to receive(:writable?).with(Gem.default_dir) + allow(File).to receive(:writable?).with(File.expand_path("..", Gem.default_dir)) end it "exits with no message if the installed gem has no C extensions" do @@ -49,8 +54,8 @@ RSpec.describe "bundle doctor" do doctor = Bundler::CLI::Doctor.new({}) expect(doctor).to receive(:bundles_for_gem).exactly(2).times.and_return ["/path/to/rack/rack.bundle"] expect(doctor).to receive(:dylibs).exactly(2).times.and_return ["/usr/lib/libSystem.dylib"] - allow(File).to receive(:exist?).with("/usr/lib/libSystem.dylib").and_return(true) - expect { doctor.run }.not_to(raise_error, @stdout.string) + allow(Fiddle).to receive(:dlopen).with("/usr/lib/libSystem.dylib").and_return(true) + expect { doctor.run }.not_to raise_error expect(@stdout.string).to be_empty end @@ -58,8 +63,8 @@ RSpec.describe "bundle doctor" do doctor = Bundler::CLI::Doctor.new({}) expect(doctor).to receive(:bundles_for_gem).exactly(2).times.and_return ["/path/to/rack/rack.bundle"] expect(doctor).to receive(:dylibs).exactly(2).times.and_return ["/usr/local/opt/icu4c/lib/libicui18n.57.1.dylib"] - allow(File).to receive(:exist?).with("/usr/local/opt/icu4c/lib/libicui18n.57.1.dylib").and_return(false) - expect { doctor.run }.to raise_error(Bundler::ProductionError, strip_whitespace(<<-E).strip), @stdout.string + allow(Fiddle).to receive(:dlopen).with("/usr/local/opt/icu4c/lib/libicui18n.57.1.dylib").and_raise(Fiddle::DLError) + expect { doctor.run }.to raise_error(Bundler::ProductionError, <<~E.strip), @stdout.string The following gems are missing OS dependencies: * bundler: /usr/local/opt/icu4c/lib/libicui18n.57.1.dylib * rack: /usr/local/opt/icu4c/lib/libicui18n.57.1.dylib @@ -134,7 +139,7 @@ RSpec.describe "bundle doctor" do end end - context "when home contains filesname with special characters" do + context "when home contains filenames with special characters" do it "escape filename before command execute" do doctor = Bundler::CLI::Doctor.new({}) expect(doctor).to receive(:`).with("/usr/bin/otool -L \\$\\(date\\)\\ \\\"\\'\\\\.bundle").and_return("dummy string") diff --git a/spec/bundler/commands/exec_spec.rb b/spec/bundler/commands/exec_spec.rb index 2c4c33f374..d59b690d2f 100644 --- a/spec/bundler/commands/exec_spec.rb +++ b/spec/bundler/commands/exec_spec.rb @@ -2,11 +2,10 @@ 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 + system_gems(system_gems_to_install, path: default_bundle_path) + create_file "CustomGemfile", <<-G source "#{file_uri_for(gem_repo1)}" gem "rack", "1.0.0" @@ -17,6 +16,8 @@ RSpec.describe "bundle exec" do end it "activates the correct gem" do + system_gems(system_gems_to_install, path: default_bundle_path) + gemfile <<-G source "#{file_uri_for(gem_repo1)}" gem "rack", "0.9.1" @@ -27,12 +28,14 @@ RSpec.describe "bundle exec" do end it "works and prints no warnings when HOME is not writable" do + system_gems(system_gems_to_install, path: default_bundle_path) + gemfile <<-G source "#{file_uri_for(gem_repo1)}" gem "rack", "0.9.1" G - bundle "exec rackup", :env => { "HOME" => "/" } + bundle "exec rackup", env: { "HOME" => "/" } expect(out).to eq("0.9.1") expect(err).to be_empty end @@ -105,7 +108,7 @@ RSpec.describe "bundle exec" do 2.1.4 L - bundle "exec bundle cache", :env => { "BUNDLER_VERSION" => Bundler::VERSION } + bundle "exec bundle cache", env: { "BUNDLER_VERSION" => Bundler::VERSION } expect(out).to include("Updating files in vendor/cache") end @@ -195,7 +198,7 @@ RSpec.describe "bundle exec" do gem "rack", "0.9.1" G - install_gemfile bundled_app2("Gemfile"), <<-G, :dir => bundled_app2 + install_gemfile bundled_app2("Gemfile"), <<-G, dir: bundled_app2 source "#{file_uri_for(gem_repo2)}" gem "rack_two", "1.0.0" G @@ -204,14 +207,12 @@ RSpec.describe "bundle exec" do expect(out).to eq("0.9.1") - bundle "exec rackup", :dir => bundled_app2 + bundle "exec rackup", 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 } + let(:default_irb_version) { ruby "gem 'irb', '< 999999'; require 'irb'; puts IRB::VERSION", raise_on_error: false } context "when not specified in Gemfile" do before do @@ -291,14 +292,14 @@ RSpec.describe "bundle exec" do end end - bundle "config set path.system true" + bundle "config set --global path.system true" install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" gem "rack", "0.9.1" G - install_gemfile bundled_app2("Gemfile"), <<-G, :dir => bundled_app2 + install_gemfile bundled_app2("Gemfile"), <<-G, dir: bundled_app2 source "#{file_uri_for(gem_repo2)}" gem "rack_two", "1.0.0" G @@ -343,7 +344,7 @@ RSpec.describe "bundle exec" do bundle "exec 'echo $RUBYOPT'" expect(out.split(" ").count(bundler_setup_opt)).to eq(1) - bundle "exec 'echo $RUBYOPT'", :env => { "RUBYOPT" => rubyopt } + bundle "exec 'echo $RUBYOPT'", env: { "RUBYOPT" => rubyopt } expect(out.split(" ").count(bundler_setup_opt)).to eq(1) end @@ -362,7 +363,7 @@ RSpec.describe "bundle exec" do bundle "exec 'echo $RUBYLIB'" expect(out).to include(rubylib) - bundle "exec 'echo $RUBYLIB'", :env => { "RUBYLIB" => rubylib } + bundle "exec 'echo $RUBYLIB'", env: { "RUBYLIB" => rubylib } expect(out).to include(rubylib) end @@ -372,7 +373,7 @@ RSpec.describe "bundle exec" do gem "rack" 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`") @@ -385,7 +386,7 @@ RSpec.describe "bundle exec" do G bundle "exec touch foo" - bundle "exec ./foo", :raise_on_error => false + bundle "exec ./foo", raise_on_error: false expect(exitstatus).to eq(126) expect(err).to include("bundler: not executable: ./foo") end @@ -396,12 +397,14 @@ RSpec.describe "bundle exec" do gem "rack" 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 + system_gems(system_gems_to_install, path: default_bundle_path) + bundle "config set clean false" # want to keep the rackup binstub install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" @@ -409,7 +412,7 @@ RSpec.describe "bundle exec" do G [true, false].each do |l| bundle "config set disable_exec_load #{l}" - bundle "exec rackup", :raise_on_error => false + 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?" end end @@ -528,7 +531,7 @@ RSpec.describe "bundle exec" do 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 @@ -577,7 +580,7 @@ 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 @@ -612,22 +615,39 @@ RSpec.describe "bundle exec" do 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 "#{file_uri_for(gem_repo1)}" + gem "rack", "0.9.1" + gem "foo", :git => "#{lib_path("foo-1.0")}" + G + + bundle "config set auto_install 1" + bundle "exec foo" + expect(out).to include("Fetching rack 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 - if Gem.ruby_version >= Gem::Version.new("3.0.0") && Gem.rubygems_version < Gem::Version.new("3.3.0.a") - skip "optparse is a default gem, and rubygems loads install during install" + if Gem.rubygems_version < Gem::Version.new("3.3.0.a") + skip "optparse is a default gem, and rubygems loads it during install" end build_repo4 do build_gem "fastlane", "2.192.0" do |s| s.executables = "fastlane" - s.add_dependency "optparse", "~> 0.1.1" + s.add_dependency "optparse", "~> 999.999.999" end - build_gem "optparse", "0.1.0" - build_gem "optparse", "0.1.1" + build_gem "optparse", "999.999.998" + build_gem "optparse", "999.999.999" end - system_gems "optparse-0.1.0", :gem_repo => gem_repo4 + system_gems "optparse-999.999.998", gem_repo: gem_repo4 bundle "config set auto_install 1" bundle "config set --local path vendor/bundle" @@ -638,7 +658,7 @@ RSpec.describe "bundle exec" do G bundle "exec fastlane" - expect(out).to include("Installing optparse 0.1.1") + expect(out).to include("Installing optparse 999.999.999") expect(out).to include("2.192.0") end @@ -663,7 +683,7 @@ RSpec.describe "bundle exec" do gem "foo", :path => "#{lib_path("foo-1.0")}" G - bundle "exec irb", :raise_on_error => false + bundle "exec irb", 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') @@ -686,7 +706,7 @@ RSpec.describe "bundle exec" do G bundle "config set 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 @@ -694,7 +714,7 @@ RSpec.describe "bundle exec" do 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" @@ -706,6 +726,8 @@ RSpec.describe "bundle exec" do RUBY before do + system_gems(system_gems_to_install, path: default_bundle_path) + bundled_app(path).open("w") {|f| f << executable } bundled_app(path).chmod(0o755) @@ -727,7 +749,7 @@ RSpec.describe "bundle exec" do let(:expected) { [exec, args, rack, 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? @@ -775,9 +797,7 @@ 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 @@ -811,8 +831,7 @@ 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)}:10:in [`']<top \(required\)>': ERROR \(RuntimeError\)/ end it "runs like a normally executed executable" do @@ -820,7 +839,7 @@ RSpec.describe "bundle exec" do 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 @@ -854,7 +873,7 @@ RSpec.describe "bundle exec" do end end - context "when Bundler.setup fails", :bundler => "< 3" do + context "when Bundler.setup fails", bundler: "< 3" do before do gemfile <<-G source "#{file_uri_for(gem_repo1)}" @@ -866,8 +885,11 @@ RSpec.describe "bundle exec" do 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 +Could not find gem 'rack (= 2)' in cached gems or installed locally. + +The source contains the following gems matching 'rack': + * rack-0.9.1 + * rack-1.0.0 Run `bundle install` to install missing gems. EOS @@ -881,7 +903,7 @@ Run `bundle install` to install missing gems. end end - context "when Bundler.setup fails", :bundler => "3" do + context "when Bundler.setup fails", bundler: "3" do before do gemfile <<-G source "#{file_uri_for(gem_repo1)}" @@ -893,8 +915,10 @@ Run `bundle install` to install missing gems. 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 +Could not find gem 'rack (= 2)' in cached gems or installed locally. + +The source contains the following gems matching 'rack': + * rack-1.0.0 Run `bundle install` to install missing gems. EOS @@ -908,6 +932,30 @@ Run `bundle install` to install missing gems. end end + context "when Bundler.setup fails and Gemfile is not the default" do + before do + create_file "CustomGemfile", <<-G + source "#{file_uri_for(gem_repo1)}" + gem 'rack', '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) { "" } + + it "prints proper suggestion" do + skip "https://github.com/rubygems/rubygems/issues/3351" if Gem.win_platform? + + subject + expect(exitstatus).to eq(exit_code) + expect(err).to include("Run `bundle install --gemfile CustomGemfile` to install missing gems.") + expect(out).to eq(expected) + end + end + context "when the executable exits non-zero via at_exit" do let(:executable) { super() + "\n\nat_exit { $! ? raise($!) : exit(1) }" } let(:exit_code) { 1 } @@ -992,7 +1040,7 @@ __FILE__: #{path.to_s.inspect} end context "signals being trapped by bundler" do - let(:executable) { strip_whitespace <<-RUBY } + let(:executable) { <<~RUBY } #{shebang} begin Thread.new do @@ -1019,7 +1067,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} @@ -1064,7 +1112,7 @@ __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 @@ -1157,7 +1205,7 @@ __FILE__: #{path.to_s.inspect} 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 } + 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? @@ -1173,7 +1221,7 @@ __FILE__: #{path.to_s.inspect} 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) @@ -1186,15 +1234,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}", 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) 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 diff --git a/spec/bundler/commands/fund_spec.rb b/spec/bundler/commands/fund_spec.rb index 5a0c5411da..5415b88eeb 100644 --- a/spec/bundler/commands/fund_spec.rb +++ b/spec/bundler/commands/fund_spec.rb @@ -5,20 +5,20 @@ RSpec.describe "bundle fund" do build_repo2 do build_gem "has_funding_and_other_metadata" do |s| s.metadata = { - "bug_tracker_uri" => "https://example.com/user/bestgemever/issues", - "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md", + "bug_tracker_uri" => "https://example.com/user/bestgemever/issues", + "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md", "documentation_uri" => "https://www.example.info/gems/bestgemever/0.0.1", - "homepage_uri" => "https://bestgemever.example.io", - "mailing_list_uri" => "https://groups.example.com/bestgemever", - "funding_uri" => "https://example.com/has_funding_and_other_metadata/funding", - "source_code_uri" => "https://example.com/user/bestgemever", - "wiki_uri" => "https://example.com/user/bestgemever/wiki", + "homepage_uri" => "https://bestgemever.example.io", + "mailing_list_uri" => "https://groups.example.com/bestgemever", + "funding_uri" => "https://example.com/has_funding_and_other_metadata/funding", + "source_code_uri" => "https://example.com/user/bestgemever", + "wiki_uri" => "https://example.com/user/bestgemever/wiki", } end build_gem "has_funding", "1.2.3" do |s| s.metadata = { - "funding_uri" => "https://example.com/has_funding/funding", + "funding_uri" => "https://example.com/has_funding/funding", } end diff --git a/spec/bundler/commands/help_spec.rb b/spec/bundler/commands/help_spec.rb index f72763900e..0c7031e813 100644 --- a/spec/bundler/commands/help_spec.rb +++ b/spec/bundler/commands/help_spec.rb @@ -15,6 +15,13 @@ RSpec.describe "bundle help" do expect(out).to eq(%(["#{man_dir}/bundle-install.1"])) end + it "prexifes bundle commands with bundle- and resolves aliases when finding the man files" do + with_fake_man do + bundle "help package" + end + expect(out).to eq(%(["#{man_dir}/bundle-cache.1"])) + end + it "simply outputs the human readable file when there is no man on the path" do with_path_as("") do bundle "help install" @@ -23,8 +30,8 @@ RSpec.describe "bundle help" do end it "still outputs the old help for commands that do not have man pages yet" do - bundle "help version" - expect(out).to include("Prints the bundler's version information") + bundle "help fund" + expect(out).to include("Lists information about gems seeking funding assistance") end it "looks for a binary and executes it with --help option if it's named bundler-<task>" do @@ -71,7 +78,7 @@ RSpec.describe "bundle help" do it "has helpful output when using --help flag for a non-existent command" do with_fake_man do - bundle "instill -h", :raise_on_error => false + bundle "instill -h", raise_on_error: false end expect(err).to include('Could not find command "instill".') end diff --git a/spec/bundler/commands/info_spec.rb b/spec/bundler/commands/info_spec.rb index dbfb800c4c..a5a09bc147 100644 --- a/spec/bundler/commands/info_spec.rb +++ b/spec/bundler/commands/info_spec.rb @@ -6,13 +6,13 @@ RSpec.describe "bundle info" do build_repo2 do build_gem "has_metadata" do |s| s.metadata = { - "bug_tracker_uri" => "https://example.com/user/bestgemever/issues", - "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md", + "bug_tracker_uri" => "https://example.com/user/bestgemever/issues", + "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md", "documentation_uri" => "https://www.example.info/gems/bestgemever/0.0.1", - "homepage_uri" => "https://bestgemever.example.io", - "mailing_list_uri" => "https://groups.example.com/bestgemever", - "source_code_uri" => "https://example.com/user/bestgemever", - "wiki_uri" => "https://example.com/user/bestgemever/wiki", + "homepage_uri" => "https://bestgemever.example.io", + "mailing_list_uri" => "https://groups.example.com/bestgemever", + "source_code_uri" => "https://example.com/user/bestgemever", + "wiki_uri" => "https://example.com/user/bestgemever/wiki", } end end @@ -21,6 +21,7 @@ RSpec.describe "bundle info" do source "#{file_uri_for(gem_repo2)}" gem "rails" gem "has_metadata" + gem "thin" G end @@ -50,6 +51,11 @@ RSpec.describe "bundle info" do expect(out).to eq(root.to_s) end + it "prints gem version if exists in bundle" do + bundle "info rails --version" + expect(out).to eq("2.3.2") + end + it "doesn't claim that bundler has been deleted, even if using a custom path without bundler there" do bundle "config set --local path vendor/bundle" bundle "install" @@ -59,7 +65,7 @@ RSpec.describe "bundle info" do end it "complains if gem not in bundle" do - bundle "info missing", :raise_on_error => false + bundle "info missing", raise_on_error: false expect(err).to eq("Could not find gem 'missing'.") end @@ -68,16 +74,16 @@ RSpec.describe "bundle info" do bundle "info rails --path" - expect(err).to match(/The gem rails has been deleted/i) - expect(err).to match(default_bundle_path("gems", "rails-2.3.2").to_s) + expect(err).to include("The gem rails has been deleted.") + expect(err).to include(default_bundle_path("gems", "rails-2.3.2").to_s) bundle "info rail --path" - expect(err).to match(/The gem rails has been deleted/i) - expect(err).to match(default_bundle_path("gems", "rails-2.3.2").to_s) + expect(err).to include("The gem rails has been deleted.") + expect(err).to include(default_bundle_path("gems", "rails-2.3.2").to_s) bundle "info rails" - expect(err).to match(/The gem rails has been deleted/i) - expect(err).to match(default_bundle_path("gems", "rails-2.3.2").to_s) + expect(err).to include("The gem rails has been deleted.") + expect(err).to include(default_bundle_path("gems", "rails-2.3.2").to_s) end context "given a default gem shippped in ruby", :ruby_repo do @@ -118,6 +124,30 @@ RSpec.describe "bundle info" do expect(out).to_not include("Homepage:") end end + + context "when gem has a reverse dependency on any version" do + it "prints the details" do + bundle "info rack" + + expect(out).to include("Reverse Dependencies: \n\t\tthin (1.0) depends on rack (>= 0)") + end + end + + context "when gem has a reverse dependency on a specific version" do + it "prints the details" do + bundle "info actionpack" + + expect(out).to include("Reverse Dependencies: \n\t\trails (2.3.2) depends on actionpack (= 2.3.2)") + end + end + + context "when gem has no reverse dependencies" do + it "excludes the reverse dependencies field from the output" do + bundle "info rails" + + expect(out).not_to include("Reverse Dependencies:") + end + end end context "with a git repo in the Gemfile" do @@ -133,11 +163,11 @@ RSpec.describe "bundle info" do expect(the_bundle).to include_gems "foo 1.0" bundle "info foo" - expect(out).to include("foo (1.0 #{@git.ref_for("master", 6)}") + expect(out).to include("foo (1.0 #{@git.ref_for("main", 6)}") end - it "prints out branch names other than master" do - update_git "foo", :branch => "omg" do |s| + it "prints out branch names other than main" do + update_git "foo", branch: "omg" do |s| s.write "lib/foo.rb", "FOO = '1.0.omg'" end @revision = revision_for(lib_path("foo-1.0"))[0...6] @@ -164,7 +194,7 @@ RSpec.describe "bundle info" do end it "handles when a version is a '-' prerelease" do - @git = build_git("foo", "1.0.0-beta.1", :path => lib_path("foo")) + @git = build_git("foo", "1.0.0-beta.1", path: lib_path("foo")) install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" gem "foo", "1.0.0-beta.1", :git => "#{lib_path("foo")}" @@ -185,7 +215,7 @@ RSpec.describe "bundle info" do G bundle "info rac" - expect(out).to match(/\A1 : rack\n2 : rack-obama\n0 : - exit -(\n>)?\z/) + expect(out).to match(/\A1 : rack\n2 : rack-obama\n0 : - exit -(\n>.*)?\z/) end end @@ -198,7 +228,7 @@ RSpec.describe "bundle info" do invalid_regexp = "[]" - bundle "info #{invalid_regexp}", :raise_on_error => false + bundle "info #{invalid_regexp}", raise_on_error: false expect(err).to include("Could not find gem '#{invalid_regexp}'.") end end @@ -212,7 +242,7 @@ RSpec.describe "bundle info" do gem "rails", group: :test G - bundle "info rails", :raise_on_error => false + bundle "info rails", raise_on_error: false expect(err).to include("Could not find gem 'rails', because it's in the group 'test', configured to be ignored.") end end diff --git a/spec/bundler/commands/init_spec.rb b/spec/bundler/commands/init_spec.rb index 683a453c7d..0a1336572a 100644 --- a/spec/bundler/commands/init_spec.rb +++ b/spec/bundler/commands/init_spec.rb @@ -7,6 +7,29 @@ RSpec.describe "bundle init" do expect(bundled_app_gemfile).to be_file end + context "with a template with permission flags not matching current process umask" do + let(:template_file) do + gemfile = Bundler.preferred_gemfile_name + templates_dir.join(gemfile) + end + + let(:target_dir) { bundled_app("init_permissions_test") } + + around do |example| + old_chmod = File.stat(template_file).mode + FileUtils.chmod(old_chmod | 0o111, template_file) # chmod +x + example.run + FileUtils.chmod(old_chmod, template_file) + end + + it "honours the current process umask when generating from a template" do + FileUtils.mkdir(target_dir) + bundle :init, dir: target_dir + generated_mode = File.stat(File.join(target_dir, "Gemfile")).mode & 0o111 + expect(generated_mode).to be_zero + end + end + context "when a Gemfile already exists" do before do create_file "Gemfile", <<-G @@ -15,11 +38,11 @@ RSpec.describe "bundle init" do end it "does not change existing Gemfiles" do - expect { bundle :init, :raise_on_error => false }.not_to change { File.read(bundled_app_gemfile) } + expect { bundle :init, raise_on_error: false }.not_to change { File.read(bundled_app_gemfile) } end it "notifies the user that an existing Gemfile already exists" do - bundle :init, :raise_on_error => false + bundle :init, raise_on_error: false expect(err).to include("Gemfile already exists") end end @@ -32,7 +55,7 @@ RSpec.describe "bundle init" do FileUtils.mkdir bundled_app(subdir) - bundle :init, :dir => bundled_app(subdir) + bundle :init, dir: bundled_app(subdir) expect(out).to include("Writing new Gemfile") expect(bundled_app("#{subdir}/Gemfile")).to be_file @@ -42,13 +65,13 @@ RSpec.describe "bundle init" do context "when the dir is not writable by the current user" do let(:subdir) { "child_dir" } - it "notifies the user that it can not write to it" do + it "notifies the user that it cannot write to it" do FileUtils.mkdir bundled_app(subdir) # chmod a-w it mode = File.stat(bundled_app(subdir)).mode ^ 0o222 FileUtils.chmod mode, bundled_app(subdir) - bundle :init, :dir => bundled_app(subdir), :raise_on_error => false + bundle :init, dir: bundled_app(subdir), raise_on_error: false expect(err).to include("directory is not writable") expect(Dir[bundled_app("#{subdir}/*")]).to be_empty @@ -69,7 +92,7 @@ RSpec.describe "bundle init" do S end - bundle :init, :gemspec => spec_file + bundle :init, gemspec: spec_file gemfile = bundled_app_gemfile.read expect(gemfile).to match(%r{source 'https://rubygems.org'}) @@ -89,7 +112,7 @@ RSpec.describe "bundle init" do S end - bundle :init, :gemspec => spec_file, :raise_on_error => false + bundle :init, gemspec: spec_file, raise_on_error: false expect(err).to include("There was an error while loading `test.gemspec`") end end @@ -112,11 +135,11 @@ RSpec.describe "bundle init" do end it "does not change existing Gemfiles" do - expect { bundle :init, :raise_on_error => false }.not_to change { File.read(bundled_app("gems.rb")) } + expect { bundle :init, raise_on_error: false }.not_to change { File.read(bundled_app("gems.rb")) } end it "notifies the user that an existing gems.rb already exists" do - bundle :init, :raise_on_error => false + bundle :init, raise_on_error: false expect(err).to include("gems.rb already exists") end end @@ -129,7 +152,7 @@ RSpec.describe "bundle init" do FileUtils.mkdir bundled_app(subdir) - bundle :init, :dir => bundled_app(subdir) + bundle :init, dir: bundled_app(subdir) expect(out).to include("Writing new gems.rb") expect(bundled_app("#{subdir}/gems.rb")).to be_file @@ -152,7 +175,7 @@ RSpec.describe "bundle init" do end it "should generate from an existing gemspec" do - bundle :init, :gemspec => spec_file + bundle :init, gemspec: spec_file gemfile = bundled_app("gems.rb").read expect(gemfile).to match(%r{source 'https://rubygems.org'}) @@ -162,10 +185,23 @@ RSpec.describe "bundle init" do end it "prints message to user" do - bundle :init, :gemspec => spec_file + bundle :init, gemspec: spec_file expect(out).to include("Writing new gems.rb") end end end + + describe "using the --gemfile" do + it "should use the --gemfile value to name the gemfile" do + custom_gemfile_name = "NiceGemfileName" + + bundle :init, gemfile: custom_gemfile_name + + expect(out).to include("Writing new #{custom_gemfile_name}") + used_template = File.read("#{source_root}/lib/bundler/templates/Gemfile") + generated_gemfile = bundled_app(custom_gemfile_name).read + expect(generated_gemfile).to eq(used_template) + end + end end diff --git a/spec/bundler/commands/inject_spec.rb b/spec/bundler/commands/inject_spec.rb index 2d97bf6ff0..255a03c135 100644 --- a/spec/bundler/commands/inject_spec.rb +++ b/spec/bundler/commands/inject_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.describe "bundle inject", :bundler => "< 3" do +RSpec.describe "bundle inject", bundler: "< 3" do before :each do gemfile <<-G source "#{file_uri_for(gem_repo1)}" @@ -36,14 +36,14 @@ RSpec.describe "bundle inject", :bundler => "< 3" do context "with injected gems already in the Gemfile" do it "doesn't add existing gems" do - bundle "inject 'rack' '> 0'", :raise_on_error => false + bundle "inject 'rack' '> 0'", raise_on_error: false expect(err).to match(/cannot specify the same gem twice/i) end end context "incorrect arguments" do it "fails when more than 2 arguments are passed" do - bundle "inject gem_name 1 v", :raise_on_error => false + bundle "inject gem_name 1 v", raise_on_error: false expect(err).to eq(<<-E.strip) ERROR: "bundle inject" was called with arguments ["gem_name", "1", "v"] Usage: "bundle inject GEM VERSION" @@ -99,7 +99,7 @@ Usage: "bundle inject GEM VERSION" it "restores frozen afterwards" do bundle "inject 'rack-obama' '> 0'" - config = YAML.load(bundled_app(".bundle/config").read) + config = Psych.load(bundled_app(".bundle/config").read) expect(config["BUNDLE_DEPLOYMENT"] || config["BUNDLE_FROZEN"]).to eq("true") end @@ -108,8 +108,8 @@ Usage: "bundle inject GEM VERSION" source "#{file_uri_for(gem_repo1)}" gem "rack-obama" G - bundle "inject 'rack' '> 0'", :raise_on_error => false - expect(err).to match(/trying to install in deployment mode after changing/) + bundle "inject 'rack' '> 0'", raise_on_error: false + expect(err).to match(/the lockfile can't be updated because frozen mode is set/) expect(bundled_app_lock.read).not_to match(/rack-obama/) end diff --git a/spec/bundler/commands/install_spec.rb b/spec/bundler/commands/install_spec.rb index e00caa5315..f0c9aaea8e 100644 --- a/spec/bundler/commands/install_spec.rb +++ b/spec/bundler/commands/install_spec.rb @@ -12,7 +12,7 @@ RSpec.describe "bundle install with gem sources" do end it "does not make a lockfile if the install fails" do - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G, raise_on_error: false raise StandardError, "FAIL" G @@ -29,7 +29,7 @@ RSpec.describe "bundle install with gem sources" do expect(bundled_app_lock).to exist end - it "does not create ./.bundle by default", :bundler => "< 3" do + it "does not create ./.bundle by default", bundler: "< 3" do gemfile <<-G source "#{file_uri_for(gem_repo1)}" gem "rack" @@ -45,7 +45,7 @@ RSpec.describe "bundle install with gem sources" do gem "rack" G - bundle :install, :env => { "BUNDLE_PATH__SYSTEM" => "true" } # can't use install_gemfile since it sets retry + bundle :install, env: { "BUNDLE_PATH__SYSTEM" => "true" } # can't use install_gemfile since it sets retry expect(bundled_app(".bundle")).not_to exist end @@ -68,7 +68,7 @@ RSpec.describe "bundle install with gem sources" do lockfile = File.read(bundled_app_lock) - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G, raise_on_error: false raise StandardError, "FAIL" G @@ -130,7 +130,7 @@ RSpec.describe "bundle install with gem sources" do end it "raises an appropriate error when gems are specified using symbols" do - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G, raise_on_error: false source "#{file_uri_for(gem_repo1)}" gem :rack G @@ -197,7 +197,7 @@ RSpec.describe "bundle install with gem sources" do end it "does not reinstall any gem that is already available locally" do - system_gems "activesupport-2.3.2", :path => default_bundle_path + system_gems "activesupport-2.3.2", path: default_bundle_path build_repo2 do build_gem "activesupport", "2.3.2" do |s| @@ -214,7 +214,7 @@ RSpec.describe "bundle install with gem sources" do end it "works when the gemfile specifies gems that only exist in the system" do - build_gem "foo", :to_bundle => true + build_gem "foo", to_bundle: true install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" gem "rack" @@ -225,7 +225,7 @@ RSpec.describe "bundle install with gem sources" do end it "prioritizes local gems over remote gems" do - build_gem "rack", "1.0.0", :to_bundle => true do |s| + build_gem "rack", "1.0.0", to_bundle: true do |s| s.add_dependency "activesupport", "2.3.5" end @@ -241,7 +241,7 @@ RSpec.describe "bundle install with gem sources" do plugin_msg = "hello from an env plugin!" create_file "plugins/rubygems_plugin.rb", "puts '#{plugin_msg}'" rubylib = ENV["RUBYLIB"].to_s.split(File::PATH_SEPARATOR).unshift(bundled_app("plugins").to_s).join(File::PATH_SEPARATOR) - install_gemfile <<-G, :env => { "RUBYLIB" => rubylib } + install_gemfile <<-G, env: { "RUBYLIB" => rubylib } source "#{file_uri_for(gem_repo1)}" gem "rack" G @@ -285,7 +285,7 @@ RSpec.describe "bundle install with gem sources" do end it "installs gems for windows" do - simulate_platform mswin + simulate_platform x86_mswin32 install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" @@ -293,7 +293,7 @@ RSpec.describe "bundle install with gem sources" do G run "require 'platform_specific' ; puts PLATFORM_SPECIFIC" - expect(out).to eq("1.0.0 MSWIN") + expect(out).to eq("1.0 x86-mswin32") end end @@ -311,14 +311,14 @@ RSpec.describe "bundle install with gem sources" do expect(the_bundle).to include_gems "rack 1.0" end - it "allows running bundle install --system without deleting foo", :bundler => "< 3" do + it "allows running bundle install --system without deleting foo", bundler: "< 3" do bundle "install --path vendor" bundle "install --system" FileUtils.rm_rf(bundled_app("vendor")) expect(the_bundle).to include_gems "rack 1.0" end - it "allows running bundle install --system after deleting foo", :bundler => "< 3" do + it "allows running bundle install --system after deleting foo", bundler: "< 3" do bundle "install --path vendor" FileUtils.rm_rf(bundled_app("vendor")) bundle "install --system" @@ -326,7 +326,7 @@ RSpec.describe "bundle install with gem sources" do end end - it "finds gems in multiple sources", :bundler => "< 3" do + it "finds gems in multiple sources", bundler: "< 3" do build_repo2 do build_gem "rack", "1.2" do |s| s.executables = "rackup" @@ -345,7 +345,7 @@ RSpec.describe "bundle install with gem sources" do end it "gives a useful error if no sources are set" do - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G, raise_on_error: false gem "rack" G @@ -364,7 +364,9 @@ RSpec.describe "bundle install with gem sources" do end it "throws a warning if a gem is added twice in Gemfile without version requirements" do - install_gemfile <<-G, :raise_on_error => false + build_repo2 + + install_gemfile <<-G source "#{file_uri_for(gem_repo2)}" gem "rack" gem "rack" @@ -376,7 +378,9 @@ RSpec.describe "bundle install with gem sources" do end it "throws a warning if a gem is added twice in Gemfile with same versions" do - install_gemfile <<-G, :raise_on_error => false + build_repo2 + + install_gemfile <<-G source "#{file_uri_for(gem_repo2)}" gem "rack", "1.0" gem "rack", "1.0" @@ -387,8 +391,24 @@ RSpec.describe "bundle install with gem sources" do expect(err).to include("While it's not a problem now, it could cause errors if you change the version of one of them later.") end + it "throws a warning if a gem is added twice under different platforms and does not crash when using the generated lockfile" do + build_repo2 + + install_gemfile <<-G + source "#{file_uri_for(gem_repo2)}" + gem "rack", :platform => :jruby + gem "rack" + G + + bundle "install" + + expect(err).to include("Your Gemfile lists the gem rack (>= 0) more than once.") + expect(err).to include("Remove any duplicate entries and specify the gem only once.") + expect(err).to include("While it's not a problem now, it could cause errors if you change the version of one of them later.") + end + it "does not throw a warning if a gem is added once in Gemfile and also inside a gemspec as a development dependency" do - build_lib "my-gem", :path => bundled_app do |s| + build_lib "my-gem", path: bundled_app do |s| s.add_development_dependency "my-private-gem" end @@ -410,8 +430,155 @@ RSpec.describe "bundle install with gem sources" do expect(the_bundle).to include_gems("my-private-gem 1.0") end + it "throws a warning if a gem is added once in Gemfile and also inside a gemspec as a development dependency, with different requirements" do + build_lib "my-gem", path: bundled_app do |s| + s.add_development_dependency "rubocop", "~> 1.36.0" + end + + build_repo4 do + build_gem "rubocop", "1.36.0" + build_gem "rubocop", "1.37.1" + end + + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + + gemspec + + gem "rubocop", group: :development + G + + bundle :install + + expect(err).to include("A gemspec development dependency (rubocop, ~> 1.36.0) is being overridden by a Gemfile dependency (rubocop, >= 0).") + expect(err).to include("This behaviour may change in the future. Please remove either of them, or make sure they both have the same requirement") + + # This is not the best behavior I believe, it would be better if both + # requirements are considered if they are compatible, and a version + # satisfying both is chosen. But not sure about changing it right now, so + # I went with a warning for the time being. + expect(the_bundle).to include_gems("rubocop 1.37.1") + end + + it "includes the gem without warning if two gemspecs add it with the same requirement" do + gem1 = tmp.join("my-gem-1") + gem2 = tmp.join("my-gem-2") + + build_lib "my-gem", path: gem1 do |s| + s.add_development_dependency "rubocop", "~> 1.36.0" + end + + build_lib "my-gem-2", path: gem2 do |s| + s.add_development_dependency "rubocop", "~> 1.36.0" + end + + build_repo4 do + build_gem "rubocop", "1.36.0" + end + + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + + gemspec path: "#{gem1}" + gemspec path: "#{gem2}" + G + + bundle :install + + expect(err).to be_empty + expect(the_bundle).to include_gems("rubocop 1.36.0") + end + + it "warns when a Gemfile dependency is overriding a gemspec development dependency, with different requirements" do + build_lib "my-gem", path: bundled_app do |s| + s.add_development_dependency "rails", ">= 5" + end + + build_repo4 do + build_gem "rails", "7.0.8" + end + + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + + gem "rails", "~> 7.0.8" + + gemspec + G + + bundle :install + + expect(err).to include("A gemspec development dependency (rails, >= 5) is being overridden by a Gemfile dependency (rails, ~> 7.0.8).") + expect(err).to include("This behaviour may change in the future. Please remove either of them, or make sure they both have the same requirement") + + # This is not the best behavior I believe, it would be better if both + # requirements are considered if they are compatible, and a version + # satisfying both is chosen. But not sure about changing it right now, so + # I went with a warning for the time being. + expect(the_bundle).to include_gems("rails 7.0.8") + end + + it "does not warn if a gem is added once in Gemfile and also inside a gemspec as a development dependency, with same requirements, and different sources" do + build_lib "my-gem", path: bundled_app do |s| + s.add_development_dependency "activesupport" + end + + build_repo4 do + build_gem "activesupport" + end + + build_git "activesupport", "1.0", path: lib_path("activesupport") + + install_gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + + gemspec + + gem "activesupport", :git => "#{file_uri_for(lib_path("activesupport"))}" + G + + expect(err).to be_empty + expect(the_bundle).to include_gems "activesupport 1.0", source: "git@#{lib_path("activesupport")}" + + # if the Gemfile dependency is specified first + install_gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + + gem "activesupport", :git => "#{file_uri_for(lib_path("activesupport"))}" + + gemspec + G + + expect(err).to be_empty + expect(the_bundle).to include_gems "activesupport 1.0", source: "git@#{lib_path("activesupport")}" + end + + it "considers both dependencies for resolution if a gem is added once in Gemfile and also inside a local gemspec as a runtime dependency, with different requirements" do + build_lib "my-gem", path: bundled_app do |s| + s.add_dependency "rubocop", "~> 1.36.0" + end + + build_repo4 do + build_gem "rubocop", "1.36.0" + build_gem "rubocop", "1.37.1" + end + + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + + gemspec + + gem "rubocop" + G + + bundle :install + + expect(err).to be_empty + expect(the_bundle).to include_gems("rubocop 1.36.0") + end + it "throws an error if a gem is added twice in Gemfile when version of one dependency is not specified" do - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G, raise_on_error: false source "#{file_uri_for(gem_repo2)}" gem "rack" gem "rack", "1.0" @@ -422,7 +589,7 @@ RSpec.describe "bundle install with gem sources" do end it "throws an error if a gem is added twice in Gemfile when different versions of both dependencies are specified" do - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G, raise_on_error: false source "#{file_uri_for(gem_repo2)}" gem "rack", "1.0" gem "rack", "1.1" @@ -435,7 +602,7 @@ RSpec.describe "bundle install with gem sources" do it "gracefully handles error when rubygems server is unavailable" do skip "networking issue" if Gem.win_platform? - install_gemfile <<-G, :artifice => nil, :raise_on_error => false + install_gemfile <<-G, artifice: nil, raise_on_error: false source "#{file_uri_for(gem_repo1)}" source "http://0.0.0.0:9384" do gem 'foo' @@ -447,8 +614,8 @@ RSpec.describe "bundle install with gem sources" do end it "fails gracefully when downloading an invalid specification from the full index" do - build_repo2 do - build_gem "ajp-rails", "0.0.0", :gemspec => false, :skip_validation => true do |s| + build_repo2(build_compact_index: false) do + build_gem "ajp-rails", "0.0.0", gemspec: false, skip_validation: true do |s| bad_deps = [["ruby-ajp", ">= 0.2.0"], ["rails", ">= 0.14"]] s. instance_variable_get(:@spec). @@ -459,7 +626,7 @@ RSpec.describe "bundle install with gem sources" do build_gem "ruby-ajp", "1.0.0" end - install_gemfile <<-G, :full_index => true, :raise_on_error => false + install_gemfile <<-G, full_index: true, raise_on_error: false source "#{file_uri_for(gem_repo2)}" gem "ajp-rails", "0.0.0" @@ -494,30 +661,26 @@ RSpec.describe "bundle install with gem sources" do end describe "Ruby version in Gemfile.lock" do - include Bundler::GemHelpers - context "and using an unsupported Ruby version" do it "prints an error" do - install_gemfile <<-G, :raise_on_error => false - ::RUBY_VERSION = '2.0.1' - ruby '~> 2.2' + install_gemfile <<-G, raise_on_error: false + ruby '~> 1.2' source "#{file_uri_for(gem_repo1)}" G - expect(err).to include("Your Ruby version is 2.0.1, but your Gemfile specified ~> 2.2") + expect(err).to include("Your Ruby version is #{Gem.ruby_version}, but your Gemfile specified ~> 1.2") end end context "and using a supported Ruby version" do before do install_gemfile <<-G - ::RUBY_VERSION = '2.1.3' - ::RUBY_PATCHLEVEL = 100 - ruby '~> 2.1.0' + ruby '~> #{Gem.ruby_version}' source "#{file_uri_for(gem_repo1)}" G end it "writes current Ruby version to Gemfile.lock" do + checksums = checksums_section_when_existing expect(lockfile).to eq <<~L GEM remote: #{file_uri_for(gem_repo1)}/ @@ -527,23 +690,23 @@ RSpec.describe "bundle install with gem sources" do #{lockfile_platforms} DEPENDENCIES - + #{checksums} RUBY VERSION - ruby 2.1.3p100 + #{Bundler::RubyVersion.system} BUNDLED WITH #{Bundler::VERSION} L end - it "updates Gemfile.lock with updated incompatible ruby version" do + it "updates Gemfile.lock with updated yet still compatible ruby version" do install_gemfile <<-G - ::RUBY_VERSION = '2.2.3' - ::RUBY_PATCHLEVEL = 100 - ruby '~> 2.2.0' + ruby '~> #{current_ruby_minor}' source "#{file_uri_for(gem_repo1)}" G + checksums = checksums_section_when_existing + expect(lockfile).to eq <<~L GEM remote: #{file_uri_for(gem_repo1)}/ @@ -553,9 +716,9 @@ RSpec.describe "bundle install with gem sources" do #{lockfile_platforms} DEPENDENCIES - + #{checksums} RUBY VERSION - ruby 2.2.3p100 + #{Bundler::RubyVersion.system} BUNDLED WITH #{Bundler::VERSION} @@ -590,7 +753,7 @@ RSpec.describe "bundle install with gem sources" do file.puts gemfile end - bundle :install, :dir => root_dir + bundle :install, dir: root_dir end it "doesn't blow up when using the `gemspec` DSL" do @@ -598,7 +761,7 @@ RSpec.describe "bundle install with gem sources" do FileUtils.mkdir_p(root_dir) - build_lib "foo", :path => root_dir + build_lib "foo", path: root_dir gemfile = <<-G source "#{file_uri_for(gem_repo1)}" gemspec @@ -607,7 +770,7 @@ RSpec.describe "bundle install with gem sources" do file.puts gemfile end - bundle :install, :dir => root_dir + bundle :install, dir: root_dir end end @@ -620,7 +783,7 @@ RSpec.describe "bundle install with gem sources" do gem 'rack' G - bundle :install, :quiet => true + bundle :install, quiet: true expect(out).to be_empty expect(err).to be_empty end @@ -650,7 +813,7 @@ RSpec.describe "bundle install with gem sources" do gem 'non-existing-gem' G - bundle :install, :quiet => true, :raise_on_error => false, :env => { "RUBYOPT" => "-r#{bundled_app("install_with_warning.rb")}" } + bundle :install, quiet: true, raise_on_error: false, env: { "RUBYOPT" => "-r#{bundled_app("install_with_warning.rb")}" } expect(out).to be_empty expect(err).to include("Could not find gem 'non-existing-gem'") expect(err).to include("BOOOOO") @@ -672,7 +835,7 @@ RSpec.describe "bundle install with gem sources" do FileUtils.chmod(0o500, bundle_path) bundle "config set --local path vendor" - bundle :install, :raise_on_error => false + bundle :install, raise_on_error: false expect(err).to include(bundle_path.to_s) expect(err).to include("grant write permissions") end @@ -694,7 +857,7 @@ RSpec.describe "bundle install with gem sources" do bundle "config set --local path vendor" begin - bundle :install, :raise_on_error => false + bundle :install, raise_on_error: false ensure FileUtils.chmod("+x", gems_path) end @@ -708,6 +871,36 @@ RSpec.describe "bundle install with gem sources" do end end + describe "when bundle extensions path does not have write access", :permissions do + let(:extensions_path) { bundled_app("vendor/#{Bundler.ruby_scope}/extensions/#{Gem::Platform.local}/#{Gem.extension_api_version}") } + + before do + FileUtils.mkdir_p(extensions_path) + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem 'simple_binary' + G + end + + it "should display a proper message to explain the problem" do + FileUtils.chmod("-x", extensions_path) + bundle "config set --local path vendor" + + begin + bundle :install, raise_on_error: false + ensure + FileUtils.chmod("+x", extensions_path) + end + + expect(err).not_to include("ERROR REPORT TEMPLATE") + + expect(err).to include( + "There was an error while trying to create `#{extensions_path.join("simple_binary-1.0")}`. " \ + "It is likely that you need to grant executable permissions for all parent directories and write permissions for `#{extensions_path}`." + ) + end + end + describe "when the path of a specific gem is not writable", :permissions do let(:gems_path) { bundled_app("vendor/#{Bundler.ruby_scope}/gems") } let(:foo_path) { gems_path.join("foo-1.0.0") } @@ -734,18 +927,79 @@ RSpec.describe "bundle install with gem sources" do FileUtils.chmod("-x", foo_path) begin - bundle "install --redownload", :raise_on_error => false + bundle "install --redownload", raise_on_error: false ensure FileUtils.chmod("+x", foo_path) end expect(err).not_to include("ERROR REPORT TEMPLATE") + expect(err).to include("Could not delete previous installation of `#{foo_path}`.") + expect(err).to include("The underlying error was Errno::EACCES") + end + end - expect(err).to include( - "There was an error while trying to delete `#{foo_path}`. " \ - "It is likely that you need to grant executable permissions for all parent directories " \ - "and write permissions for `#{gems_path}`, and the same thing for all subdirectories inside #{foo_path}." - ) + describe "when gem home does not have the writable bit set, yet it's still writable", :permissions do + let(:gem_home) { bundled_app("vendor/#{Bundler.ruby_scope}") } + + before do + build_repo4 do + build_gem "foo", "1.0.0" do |s| + s.write "CHANGELOG.md", "foo" + end + end + + gemfile <<-G + source "#{file_uri_for(gem_repo4)}" + gem 'foo' + G + end + + it "should display a proper message to explain the problem" do + bundle "config set --local path vendor" + bundle :install + expect(out).to include("Bundle complete!") + expect(err).to be_empty + + FileUtils.chmod("-w", gem_home) + + begin + bundle "install --redownload" + ensure + FileUtils.chmod("+w", gem_home) + end + + expect(out).to include("Bundle complete!") + expect(err).to be_empty + end + end + + describe "when gems path is world writable (no sticky bit set)", :permissions do + let(:gems_path) { bundled_app("vendor/#{Bundler.ruby_scope}/gems") } + + before do + build_repo4 do + build_gem "foo", "1.0.0" do |s| + s.write "CHANGELOG.md", "foo" + end + end + + gemfile <<-G + source "#{file_uri_for(gem_repo4)}" + gem 'foo' + G + end + + it "should display a proper message to explain the problem" do + bundle "config set --local path vendor" + bundle :install + expect(out).to include("Bundle complete!") + expect(err).to be_empty + + FileUtils.chmod(0o777, gems_path) + + bundle "install --redownload", raise_on_error: false + + expect(err).to include("The installation path is insecure. Bundler cannot continue.") end end @@ -764,7 +1018,7 @@ RSpec.describe "bundle install with gem sources" do FileUtils.chmod(0o500, cache_path) bundle "config set --local path vendor" - bundle :install, :raise_on_error => false + bundle :install, raise_on_error: false expect(err).to include(cache_path.to_s) expect(err).to include("grant write permissions") end @@ -777,13 +1031,13 @@ RSpec.describe "bundle install with gem sources" do gem "rack" G bundle "config set --local path bundle" - bundle "install", :standalone => true + bundle "install", standalone: true end it "includes the standalone path" do - bundle "binstubs rack", :standalone => true + bundle "binstubs rack", standalone: true standalone_line = File.read(bundled_app("bin/rackup")).each_line.find {|line| line.include? "$:.unshift" }.strip - expect(standalone_line).to eq %($:.unshift File.expand_path "../../bundle", path.realpath) + expect(standalone_line).to eq %($:.unshift File.expand_path "../bundle", __dir__) end end @@ -796,7 +1050,7 @@ RSpec.describe "bundle install with gem sources" do end it "should display a helpful message explaining how to fix it" do - bundle :install, :env => { "BUNDLE_RUBYGEMS__ORG" => "user:pass{word" }, :raise_on_error => false + bundle :install, env: { "BUNDLE_RUBYGEMS__ORG" => "user:pass{word" }, raise_on_error: false expect(exitstatus).to eq(17) expect(err).to eq("Please CGI escape your usernames and passwords before " \ "setting them for authentication.") @@ -837,11 +1091,11 @@ RSpec.describe "bundle install with gem sources" do end it "should fail loudly if the lockfile platforms don't include the current platform" do - simulate_platform(Gem::Platform.new("x86_64-linux")) { bundle "install", :raise_on_error => false } + simulate_platform(Gem::Platform.new("x86_64-linux")) { bundle "install", raise_on_error: false } expect(err).to eq( "Your bundle only supports platforms [\"x86_64-darwin-19\"] but your local platform is x86_64-linux. " \ - "Add the current platform to the lockfile with `bundle lock --add-platform x86_64-linux` and try again." + "Add the current platform to the lockfile with\n`bundle lock --add-platform x86_64-linux` and try again." ) end end @@ -849,16 +1103,16 @@ RSpec.describe "bundle install with gem sources" do context "with missing platform specific gems in lockfile" do before do build_repo4 do - build_gem "racc", "1.5.2" + build_gem "racca", "1.5.2" build_gem "nokogiri", "1.12.4" do |s| s.platform = "x86_64-darwin" - s.add_runtime_dependency "racc", "~> 1.4" + s.add_runtime_dependency "racca", "~> 1.4" end build_gem "nokogiri", "1.12.4" do |s| s.platform = "x86_64-linux" - s.add_runtime_dependency "racc", "~> 1.4" + s.add_runtime_dependency "racca", "~> 1.4" end build_gem "crass", "1.0.6" @@ -872,11 +1126,18 @@ RSpec.describe "bundle install with gem sources" do gemfile <<-G source "https://gem.repo4" - ruby "#{RUBY_VERSION}" + ruby "#{Gem.ruby_version}" gem "loofah", "~> 2.12.0" G + checksums = checksums_section do |c| + c.checksum gem_repo4, "crass", "1.0.6" + c.checksum gem_repo4, "loofah", "2.12.0" + c.checksum gem_repo4, "nokogiri", "1.12.4", "x86_64-darwin" + c.checksum gem_repo4, "racca", "1.5.2" + end + lockfile <<-L GEM remote: https://gem.repo4/ @@ -886,8 +1147,8 @@ RSpec.describe "bundle install with gem sources" do crass (~> 1.0.2) nokogiri (>= 1.5.9) nokogiri (1.12.4-x86_64-darwin) - racc (~> 1.4) - racc (1.5.2) + racca (~> 1.4) + racca (1.5.2) PLATFORMS x86_64-darwin-20 @@ -895,7 +1156,7 @@ RSpec.describe "bundle install with gem sources" do DEPENDENCIES loofah (~> 2.12.0) - + #{checksums} RUBY VERSION #{Bundler::RubyVersion.system} @@ -908,7 +1169,15 @@ RSpec.describe "bundle install with gem sources" do bundle "config set --local path vendor/bundle" simulate_platform "x86_64-linux" do - bundle "install", :artifice => "compact_index" + bundle "install", artifice: "compact_index" + end + + checksums = checksums_section_when_existing do |c| + c.checksum gem_repo4, "crass", "1.0.6" + c.checksum gem_repo4, "loofah", "2.12.0" + c.checksum gem_repo4, "nokogiri", "1.12.4", "x86_64-darwin" + c.checksum gem_repo4, "racca", "1.5.2" + c.checksum gem_repo4, "nokogiri", "1.12.4", "x86_64-linux" end expect(lockfile).to eq <<~L @@ -920,10 +1189,10 @@ RSpec.describe "bundle install with gem sources" do crass (~> 1.0.2) nokogiri (>= 1.5.9) nokogiri (1.12.4-x86_64-darwin) - racc (~> 1.4) + racca (~> 1.4) nokogiri (1.12.4-x86_64-linux) - racc (~> 1.4) - racc (1.5.2) + racca (~> 1.4) + racca (1.5.2) PLATFORMS x86_64-darwin-20 @@ -931,7 +1200,7 @@ RSpec.describe "bundle install with gem sources" do DEPENDENCIES loofah (~> 2.12.0) - + #{checksums} RUBY VERSION #{Bundler::RubyVersion.system} @@ -943,11 +1212,11 @@ RSpec.describe "bundle install with gem sources" do context "with --local flag" do before do - system_gems "rack-1.0.0", :path => default_bundle_path + system_gems "rack-1.0.0", path: default_bundle_path end it "respects installed gems without fetching any remote sources" do - install_gemfile <<-G, :local => true + install_gemfile <<-G, local: true source "#{file_uri_for(gem_repo1)}" source "https://not-existing-source" do @@ -958,4 +1227,161 @@ RSpec.describe "bundle install with gem sources" do expect(last_command).to be_success end end + + context "with only option" do + before do + bundle "config set only a:b" + end + + it "installs only gems of the specified groups" do + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rails" + gem "rack", group: :a + gem "rake", group: :b + gem "yard", group: :c + G + + expect(out).to include("Installing rack") + expect(out).to include("Installing rake") + expect(out).not_to include("Installing yard") + end + end + + context "with --prefer-local flag" do + before do + build_repo4 do + build_gem "foo", "1.0.1" + build_gem "foo", "1.0.0" + build_gem "bar", "1.0.0" + end + + system_gems "foo-1.0.0", path: default_bundle_path, gem_repo: gem_repo4 + end + + it "fetches remote sources only when not available locally" do + install_gemfile <<-G, "prefer-local": true, verbose: true + source "#{file_uri_for(gem_repo4)}" + + gem "foo" + gem "bar" + G + + expect(out).to include("Using foo 1.0.0").and include("Fetching bar 1.0.0").and include("Installing bar 1.0.0") + expect(last_command).to be_success + end + end + + context "with a symlinked configured as bundle path and a gem with symlinks" do + before do + symlinked_bundled_app = tmp("bundled_app-symlink") + File.symlink(bundled_app, symlinked_bundled_app) + bundle "config path #{File.join(symlinked_bundled_app, ".vendor")}" + + binman_path = tmp("binman") + FileUtils.mkdir_p binman_path + + readme_path = File.join(binman_path, "README.markdown") + FileUtils.touch(readme_path) + + man_path = File.join(binman_path, "man", "man0") + FileUtils.mkdir_p man_path + + File.symlink("../../README.markdown", File.join(man_path, "README.markdown")) + + build_repo4 do + build_gem "binman", path: gem_repo4("gems"), lib_path: binman_path, no_default: true do |s| + s.files = ["README.markdown", "man/man0/README.markdown"] + end + end + end + + it "installs fine" do + install_gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + + gem "binman" + G + end + end + + context "when a gem has equivalent versions with inconsistent dependencies" do + before do + build_repo4 do + build_gem "autobuild", "1.10.rc2" do |s| + s.add_dependency "utilrb", ">= 1.6.0" + end + + build_gem "autobuild", "1.10.0.rc2" do |s| + s.add_dependency "utilrb", ">= 2.0" + end + end + end + + it "does not crash unexpectedly" do + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + + gem "autobuild", "1.10.rc2" + G + + bundle "install --jobs 1", raise_on_error: false + + expect(err).not_to include("ERROR REPORT TEMPLATE") + expect(err).to include("Could not find compatible versions") + end + end + + context "when a lockfile has unmet dependencies, and the Gemfile has no resolution" do + before do + build_repo4 do + build_gem "aaa", "0.2.0" do |s| + s.add_dependency "zzz", "< 0.2.0" + end + + build_gem "zzz", "0.2.0" + end + + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + + gem "aaa" + gem "zzz" + G + + lockfile <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + aaa (0.2.0) + zzz (< 0.2.0) + zzz (0.2.0) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + aaa! + zzz! + + BUNDLED WITH + #{Bundler::VERSION} + L + end + + it "does not install, but raises a resolution error" do + bundle "install", raise_on_error: false + expect(err).to include("Could not find compatible versions") + end + end + + context "when --jobs option given" do + before do + install_gemfile "source \"#{file_uri_for(gem_repo1)}\"", jobs: 1 + end + + it "does not save the flag to config" do + expect(bundled_app(".bundle/config")).not_to exist + end + end end diff --git a/spec/bundler/commands/list_spec.rb b/spec/bundler/commands/list_spec.rb index 66930ded75..5ac2077d81 100644 --- a/spec/bundler/commands/list_spec.rb +++ b/spec/bundler/commands/list_spec.rb @@ -3,7 +3,7 @@ RSpec.describe "bundle list" do context "with name-only and paths option" do it "raises an error" do - bundle "list --name-only --paths", :raise_on_error => false + bundle "list --name-only --paths", raise_on_error: false expect(err).to eq "The `--name-only` and `--paths` options cannot be used together" end @@ -11,7 +11,7 @@ RSpec.describe "bundle list" do context "with without-group and only-group option" do it "raises an error" do - bundle "list --without-group dev --only-group test", :raise_on_error => false + bundle "list --without-group dev --only-group test", raise_on_error: false expect(err).to eq "The `--only-group` and `--without-group` options cannot be used together" end @@ -40,7 +40,7 @@ RSpec.describe "bundle list" do context "when group is not found" do it "raises an error" do - bundle "list --without-group random", :raise_on_error => false + bundle "list --without-group random", raise_on_error: false expect(err).to eq "`random` group could not be found." end @@ -79,7 +79,7 @@ RSpec.describe "bundle list" do context "when group is not found" do it "raises an error" do - bundle "list --only-group random", :raise_on_error => false + bundle "list --only-group random", raise_on_error: false expect(err).to eq "`random` group could not be found." end @@ -124,9 +124,9 @@ RSpec.describe "bundle list" do build_gem "bar" end - build_git "git_test", "1.0.0", :path => lib_path("git_test") + build_git "git_test", "1.0.0", path: lib_path("git_test") - build_lib("gemspec_test", :path => tmp.join("gemspec_test")) do |s| + build_lib("gemspec_test", path: tmp.join("gemspec_test")) do |s| s.add_dependency "bar", "=1.0.0" end diff --git a/spec/bundler/commands/lock_spec.rb b/spec/bundler/commands/lock_spec.rb index 22709f4528..f6793d393b 100644 --- a/spec/bundler/commands/lock_spec.rb +++ b/spec/bundler/commands/lock_spec.rb @@ -1,14 +1,6 @@ # frozen_string_literal: true RSpec.describe "bundle lock" do - def strip_lockfile(lockfile) - strip_whitespace(lockfile).sub(/\n\Z/, "") - end - - def read_lockfile(file = "Gemfile.lock") - strip_lockfile bundled_app(file).read - end - let(:repo) { gem_repo1 } before :each do @@ -19,7 +11,19 @@ RSpec.describe "bundle lock" do gem "foo" G - @lockfile = strip_lockfile(<<-L) + checksums = checksums_section_when_existing do |c| + c.checksum repo, "actionmailer", "2.3.2" + c.checksum repo, "actionpack", "2.3.2" + c.checksum repo, "activerecord", "2.3.2" + c.checksum repo, "activeresource", "2.3.2" + c.checksum repo, "activesupport", "2.3.2" + c.checksum repo, "foo", "1.0" + c.checksum repo, "rails", "2.3.2" + c.checksum repo, "rake", rake_version + c.checksum repo, "weakling", "0.0.3" + end + + @lockfile = <<~L GEM remote: #{file_uri_for(repo)}/ specs: @@ -38,8 +42,8 @@ RSpec.describe "bundle lock" do actionpack (= 2.3.2) activerecord (= 2.3.2) activeresource (= 2.3.2) - rake (= 13.0.1) - rake (13.0.1) + rake (= #{rake_version}) + rake (#{rake_version}) weakling (0.0.3) PLATFORMS @@ -49,7 +53,7 @@ RSpec.describe "bundle lock" do foo rails weakling - + #{checksums} BUNDLED WITH #{Bundler::VERSION} L @@ -58,15 +62,23 @@ RSpec.describe "bundle lock" do it "prints a lockfile when there is no existing lockfile with --print" do bundle "lock --print" - expect(out).to eq(@lockfile) + expect(out).to eq(@lockfile.chomp) end it "prints a lockfile when there is an existing lockfile with --print" do + lockfile remove_checksums_section_from_lockfile(@lockfile) + + bundle "lock --print" + + expect(out).to eq(remove_checksums_section_from_lockfile(@lockfile).chomp) + end + + it "prints a lockfile when there is an existing checksums lockfile with --print" do lockfile @lockfile bundle "lock --print" - expect(out).to eq(@lockfile) + expect(out).to eq(@lockfile.chomp) end it "writes a lockfile when there is no existing lockfile" do @@ -75,7 +87,39 @@ RSpec.describe "bundle lock" do expect(read_lockfile).to eq(@lockfile) end + it "prints a lockfile without fetching new checksums if the existing lockfile had no checksums" do + lockfile remove_checksums_from_lockfile(@lockfile) + + bundle "lock --print" + + expect(out).to eq(remove_checksums_from_lockfile(@lockfile).chomp) + end + + it "touches the lockfile when there is an existing lockfile that does not need changes" do + lockfile @lockfile + + expect do + bundle "lock" + end.to change { bundled_app_lock.mtime } + end + + it "does not touch lockfile with --print" do + lockfile @lockfile + + expect do + bundle "lock --print" + end.not_to change { bundled_app_lock.mtime } + end + it "writes a lockfile when there is an outdated lockfile using --update" do + lockfile remove_checksums_from_lockfile(@lockfile.gsub("2.3.2", "2.3.1"), " (2.3.1)") + + bundle "lock --update" + + expect(read_lockfile).to eq(remove_checksums_from_lockfile(@lockfile)) + end + + it "writes a lockfile with checksums on --update when checksums exist" do lockfile @lockfile.gsub("2.3.2", "2.3.1") bundle "lock --update" @@ -83,10 +127,27 @@ RSpec.describe "bundle lock" do expect(read_lockfile).to eq(@lockfile) end + it "writes a lockfile when there is an outdated lockfile and bundle is frozen" do + lockfile @lockfile.gsub("2.3.2", "2.3.1") + + bundle "lock --update", env: { "BUNDLE_FROZEN" => "true" } + + expect(read_lockfile).to eq(@lockfile) + end + it "does not fetch remote specs when using the --local option" do - bundle "lock --update --local", :raise_on_error => false + bundle "lock --update --local", raise_on_error: false - expect(err).to match(/locally installed gems/) + expect(err).to match(/cached gems or installed locally/) + end + + it "does not fetch remote checksums with --local" do + lockfile remove_checksums_from_lockfile(@lockfile) + + bundle "lock --print --local" + + # No checksums because --local prevents fetching them + expect(out).to eq(remove_checksums_from_lockfile(@lockfile).chomp) end it "works with --gemfile flag" do @@ -94,7 +155,11 @@ RSpec.describe "bundle lock" do source "#{file_uri_for(repo)}" gem "foo" G - lockfile = strip_lockfile(<<-L) + checksums = checksums_section_when_existing do |c| + c.no_checksum "foo", "1.0" + end + + lockfile = <<~L GEM remote: #{file_uri_for(repo)}/ specs: @@ -105,7 +170,7 @@ RSpec.describe "bundle lock" do DEPENDENCIES foo - + #{checksums} BUNDLED WITH #{Bundler::VERSION} L @@ -120,7 +185,7 @@ RSpec.describe "bundle lock" do bundle "lock --lockfile=lock" expect(out).to match(/Writing lockfile to.+lock/) - expect(read_lockfile("lock")).to eq(@lockfile) + expect(read_lockfile("lock")).to eq(remove_checksums_from_lockfile(@lockfile)) expect { read_lockfile }.to raise_error(Errno::ENOENT) end @@ -128,22 +193,135 @@ RSpec.describe "bundle lock" do bundle "install" bundle "lock --lockfile=lock" + checksums = checksums_section_when_existing do |c| + c.checksum repo, "actionmailer", "2.3.2" + c.checksum repo, "actionpack", "2.3.2" + c.checksum repo, "activerecord", "2.3.2" + c.checksum repo, "activeresource", "2.3.2" + c.checksum repo, "activesupport", "2.3.2" + c.checksum repo, "foo", "1.0" + c.checksum repo, "rails", "2.3.2" + c.checksum repo, "rake", rake_version + c.checksum repo, "weakling", "0.0.3" + end + + lockfile = <<~L + GEM + remote: #{file_uri_for(repo)}/ + specs: + actionmailer (2.3.2) + activesupport (= 2.3.2) + actionpack (2.3.2) + activesupport (= 2.3.2) + activerecord (2.3.2) + activesupport (= 2.3.2) + activeresource (2.3.2) + activesupport (= 2.3.2) + activesupport (2.3.2) + foo (1.0) + rails (2.3.2) + actionmailer (= 2.3.2) + actionpack (= 2.3.2) + activerecord (= 2.3.2) + activeresource (= 2.3.2) + rake (= #{rake_version}) + rake (#{rake_version}) + weakling (0.0.3) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + foo + rails + weakling + #{checksums} + BUNDLED WITH + #{Bundler::VERSION} + L + expect(out).to match(/Writing lockfile to.+lock/) - expect(read_lockfile("lock")).to eq(@lockfile) + expect(read_lockfile("lock")).to eq(lockfile) end it "update specific gems using --update" do - lockfile @lockfile.gsub("2.3.2", "2.3.1").gsub("13.0.1", "10.0.1") + lockfile @lockfile.gsub("2.3.2", "2.3.1").gsub(rake_version, "10.0.1") bundle "lock --update rails rake" - expect(read_lockfile).to eq(@lockfile) + expect(read_lockfile).to eq(remove_checksums_from_lockfile(@lockfile, "(2.3.2)", "(#{rake_version})")) + end + + it "preserves unknown checksum algorithms" do + lockfile @lockfile.gsub(/(sha256=[a-f0-9]+)$/, "constant=true,\\1,xyz=123") + + previous_lockfile = read_lockfile + + bundle "lock" + + expect(read_lockfile).to eq(previous_lockfile) + end + + it "does not unlock git sources when only uri shape changes" do + build_git("foo") + + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "foo", :git => "#{file_uri_for(lib_path("foo-1.0"))}" + G + + # Change uri format to end with "/" and reinstall + install_gemfile <<-G, verbose: true + source "#{file_uri_for(gem_repo1)}" + gem "foo", :git => "#{file_uri_for(lib_path("foo-1.0"))}/" + G + + expect(out).to include("using resolution from the lockfile") + expect(out).not_to include("re-resolving dependencies because the list of sources changed") + end + + it "updates specific gems using --update using the locked revision of unrelated git gems for resolving" do + ref = build_git("foo").ref_for("HEAD") + + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rake" + gem "foo", :git => "#{file_uri_for(lib_path("foo-1.0"))}", :branch => "deadbeef" + G + + lockfile <<~L + GIT + remote: #{file_uri_for(lib_path("foo-1.0"))} + revision: #{ref} + branch: deadbeef + specs: + foo (1.0) + + GEM + remote: #{file_uri_for(gem_repo1)}/ + specs: + rake (10.0.1) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + foo! + rake + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "lock --update rake --verbose" + expect(out).to match(/Writing lockfile to.+lock/) + expect(lockfile).to include("rake (#{rake_version})") end it "errors when updating a missing specific gems using --update" do lockfile @lockfile - bundle "lock --update blahblah", :raise_on_error => false + bundle "lock --update blahblah", raise_on_error: false expect(err).to eq("Could not find gem 'blahblah'.") expect(read_lockfile).to eq(@lockfile) @@ -157,9 +335,9 @@ RSpec.describe "bundle lock" do gem "rack_middleware", :group => "test" G bundle "config set without test" - bundle "config set path .bundle" - bundle "lock" - expect(bundled_app(".bundle")).not_to exist + bundle "config set path vendor/bundle" + bundle "lock", verbose: true + expect(bundled_app("vendor/bundle")).not_to exist end # see update_spec for more coverage on same options. logic is shared so it's not necessary @@ -176,7 +354,10 @@ RSpec.describe "bundle lock" do build_gem "foo", %w[1.5.1] do |s| s.add_dependency "bar", "~> 3.0" end - build_gem "bar", %w[2.0.3 2.0.4 2.0.5 2.1.0 2.1.1 3.0.0] + build_gem "foo", %w[2.0.0.pre] do |s| + s.add_dependency "bar" + end + build_gem "bar", %w[2.0.3 2.0.4 2.0.5 2.1.0 2.1.1 2.1.2.pre 3.0.0 3.1.0.pre 4.0.0.pre] build_gem "qux", %w[1.0.0 1.0.1 1.1.0 2.0.0] end @@ -210,6 +391,112 @@ RSpec.describe "bundle lock" do expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[foo-1.5.0 bar-2.1.1 qux-1.1.0].sort) end + + it "shows proper error when Gemfile changes forbid patch upgrades, and --patch --strict is given" do + # force next minor via Gemfile + gemfile <<-G + source "#{file_uri_for(gem_repo4)}" + gem 'foo', '1.5.0' + gem 'qux' + G + + bundle "lock --update foo --patch --strict", raise_on_error: false + + expect(err).to include( + "foo is locked to 1.4.3, while Gemfile is requesting foo (= 1.5.0). " \ + "--strict --patch was specified, but there are no patch level upgrades from 1.4.3 satisfying foo (= 1.5.0), so version solving has failed" + ) + end + + context "pre" do + it "defaults to major" do + bundle "lock --update --pre" + + expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[foo-2.0.0.pre bar-4.0.0.pre qux-2.0.0].sort) + end + + it "patch preferred" do + bundle "lock --update --patch --pre" + + expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[foo-1.4.5 bar-2.1.2.pre qux-1.0.1].sort) + end + + it "minor preferred" do + bundle "lock --update --minor --pre" + + expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[foo-1.5.1 bar-3.1.0.pre qux-1.1.0].sort) + end + + it "major preferred" do + bundle "lock --update --major --pre" + + expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[foo-2.0.0.pre bar-4.0.0.pre qux-2.0.0].sort) + end + end + end + + context "conservative updates when minor update adds a new dependency" do + before do + build_repo4 do + build_gem "sequel", "5.71.0" + build_gem "sequel", "5.72.0" do |s| + s.add_dependency "bigdecimal", ">= 0" + end + build_gem "bigdecimal", %w[1.4.4 99.1.4] + end + + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + gem 'sequel' + G + + lockfile <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + sequel (5.71.0) + + PLATFORMS + ruby + + DEPENDENCIES + sequel + + BUNDLED WITH + #{Bundler::VERSION} + L + + allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile) + end + + it "adds the latest version of the new dependency" do + bundle "lock --minor --update sequel" + + expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w[sequel-5.72.0 bigdecimal-99.1.4].sort) + end + end + + it "updates the bundler version in the lockfile to the latest bundler version" do + build_repo4 do + build_gem "bundler", "55" + end + + system_gems "bundler-55", gem_repo: gem_repo4 + + install_gemfile <<-G, artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } + source "https://gems.repo4" + G + lockfile lockfile.sub(/(^\s*)#{Bundler::VERSION}($)/, '\11.0.0\2') + + bundle "lock --update --bundler --verbose", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } + expect(lockfile).to end_with("BUNDLED WITH\n 55\n") + + update_repo4 do + build_gem "bundler", "99" + end + + bundle "lock --update --bundler --verbose", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } + expect(lockfile).to end_with("BUNDLED WITH\n 99\n") end it "supports adding new platforms" do @@ -217,7 +504,7 @@ RSpec.describe "bundle lock" do allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile) lockfile = Bundler::LockfileParser.new(read_lockfile) - expect(lockfile.platforms).to match_array(local_platforms.unshift(java, mingw).uniq) + expect(lockfile.platforms).to match_array(default_platform_list(java, x86_mingw32)) end it "supports adding new platforms with force_ruby_platform = true" do @@ -226,11 +513,11 @@ RSpec.describe "bundle lock" do remote: #{file_uri_for(gem_repo1)}/ specs: platform_specific (1.0) - platform_specific (1.0-x86-linux) + platform_specific (1.0-x86-64_linux) PLATFORMS ruby - x86-linux + x86_64-linux DEPENDENCIES platform_specific @@ -241,7 +528,7 @@ RSpec.describe "bundle lock" do allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile) lockfile = Bundler::LockfileParser.new(read_lockfile) - expect(lockfile.platforms).to contain_exactly(rb, linux, java, mingw) + expect(lockfile.platforms).to contain_exactly(rb, linux, java, x86_mingw32) end it "supports adding the `ruby` platform" do @@ -249,7 +536,7 @@ RSpec.describe "bundle lock" do allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile) lockfile = Bundler::LockfileParser.new(read_lockfile) - expect(lockfile.platforms).to match_array(local_platforms.unshift("ruby").uniq) + expect(lockfile.platforms).to match_array(default_platform_list("ruby")) end it "warns when adding an unknown platform" do @@ -262,16 +549,78 @@ RSpec.describe "bundle lock" do allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile) lockfile = Bundler::LockfileParser.new(read_lockfile) - expect(lockfile.platforms).to match_array(local_platforms.unshift(java, mingw).uniq) + expect(lockfile.platforms).to match_array(default_platform_list(java, x86_mingw32)) bundle "lock --remove-platform java" lockfile = Bundler::LockfileParser.new(read_lockfile) - expect(lockfile.platforms).to match_array(local_platforms.unshift(mingw).uniq) + expect(lockfile.platforms).to match_array(default_platform_list(x86_mingw32)) + end + + it "also cleans up redundant platform gems when removing platforms" do + build_repo4 do + build_gem "nokogiri", "1.12.0" + build_gem "nokogiri", "1.12.0" do |s| + s.platform = "x86_64-darwin" + end + end + + checksums = checksums_section_when_existing do |c| + c.checksum gem_repo4, "nokogiri", "1.12.0" + c.checksum gem_repo4, "nokogiri", "1.12.0", "x86_64-darwin" + end + + simulate_platform "x86_64-darwin-22" do + install_gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + + gem "nokogiri" + G + end + + lockfile <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + nokogiri (1.12.0) + nokogiri (1.12.0-x86_64-darwin) + + PLATFORMS + ruby + x86_64-darwin + + DEPENDENCIES + nokogiri + #{checksums} + BUNDLED WITH + #{Bundler::VERSION} + L + + checksums.delete("nokogiri", Gem::Platform::RUBY) + + simulate_platform "x86_64-darwin-22" do + bundle "lock --remove-platform ruby" + end + + expect(lockfile).to eq <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + nokogiri (1.12.0-x86_64-darwin) + + PLATFORMS + x86_64-darwin + + DEPENDENCIES + nokogiri + #{checksums} + BUNDLED WITH + #{Bundler::VERSION} + L end it "errors when removing all platforms" do - bundle "lock --remove-platform #{local_platforms.join(" ")}", :raise_on_error => false + bundle "lock --remove-platform #{local_platform}", raise_on_error: false expect(err).to include("Removing all platforms from the bundle is not allowed") end @@ -280,7 +629,7 @@ RSpec.describe "bundle lock" do build_repo4 do build_gem "ffi", "1.9.14" build_gem "ffi", "1.9.14" do |s| - s.platform = mingw + s.platform = x86_mingw32 end build_gem "gssapi", "0.1" @@ -312,7 +661,14 @@ RSpec.describe "bundle lock" do gem "gssapi" G - simulate_platform(mingw) { bundle :lock } + checksums = checksums_section_when_existing do |c| + c.no_checksum "ffi", "1.9.14", "x86-mingw32" + c.no_checksum "gssapi", "1.2.0" + c.no_checksum "mixlib-shellout", "2.2.6", "universal-mingw32" + c.no_checksum "win32-process", "0.8.3" + end + + simulate_platform(x86_mingw32) { bundle :lock } expect(lockfile).to eq <<~G GEM @@ -332,12 +688,16 @@ RSpec.describe "bundle lock" do DEPENDENCIES gssapi mixlib-shellout - + #{checksums} BUNDLED WITH #{Bundler::VERSION} G - simulate_platform(rb) { bundle :lock } + bundle "config set --local force_ruby_platform true" + bundle :lock + + checksums.no_checksum "ffi", "1.9.14" + checksums.no_checksum "mixlib-shellout", "2.2.6" expect(lockfile).to eq <<~G GEM @@ -360,7 +720,7 @@ RSpec.describe "bundle lock" do DEPENDENCIES gssapi mixlib-shellout - + #{checksums} BUNDLED WITH #{Bundler::VERSION} G @@ -424,7 +784,12 @@ RSpec.describe "bundle lock" do gem "libv8" G - simulate_platform(Gem::Platform.new("x86_64-darwin")) { bundle "lock" } + simulate_platform(Gem::Platform.new("x86_64-darwin-19")) { bundle "lock" } + + checksums = checksums_section_when_existing do |c| + c.no_checksum "libv8", "8.4.255.0", "x86_64-darwin-19" + c.no_checksum "libv8", "8.4.255.0", "x86_64-darwin-20" + end expect(lockfile).to eq <<~G GEM @@ -434,11 +799,12 @@ RSpec.describe "bundle lock" do libv8 (8.4.255.0-x86_64-darwin-20) PLATFORMS - x86_64-darwin + x86_64-darwin-19 + x86_64-darwin-20 DEPENDENCIES libv8 - + #{checksums} BUNDLED WITH #{Bundler::VERSION} G @@ -455,6 +821,11 @@ RSpec.describe "bundle lock" do end end + checksums = checksums_section_when_existing do |c| + c.checksum gem_repo4, "libv8", "8.4.255.0", "x86_64-darwin-19" + c.checksum gem_repo4, "libv8", "8.4.255.0", "x86_64-darwin-20" + end + gemfile <<-G source "#{file_uri_for(gem_repo4)}" @@ -473,7 +844,7 @@ RSpec.describe "bundle lock" do DEPENDENCIES libv8 - + #{checksums} BUNDLED WITH #{Bundler::VERSION} G @@ -492,22 +863,25 @@ RSpec.describe "bundle lock" do end it "does not conflict on ruby requirements when adding new platforms" do - next_minor = Gem.ruby_version.segments[0..1].map.with_index {|s, i| i == 1 ? s + 1 : s }.join(".") - build_repo4 do build_gem "raygun-apm", "1.0.78" do |s| s.platform = "x86_64-linux" - s.required_ruby_version = "< #{next_minor}.dev" + s.required_ruby_version = "< #{next_ruby_minor}.dev" end build_gem "raygun-apm", "1.0.78" do |s| s.platform = "universal-darwin" - s.required_ruby_version = "< #{next_minor}.dev" + s.required_ruby_version = "< #{next_ruby_minor}.dev" end build_gem "raygun-apm", "1.0.78" do |s| s.platform = "x64-mingw32" - s.required_ruby_version = "< #{next_minor}.dev" + s.required_ruby_version = "< #{next_ruby_minor}.dev" + end + + build_gem "raygun-apm", "1.0.78" do |s| + s.platform = "x64-mingw-ucrt" + s.required_ruby_version = "< #{next_ruby_minor}.dev" end end @@ -533,30 +907,687 @@ RSpec.describe "bundle lock" do #{Bundler::VERSION} L - bundle "lock --add-platform x86_64-linux", :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } + bundle "lock --add-platform x86_64-linux", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } end - context "when an update is available" do - let(:repo) { gem_repo2 } + it "does not crash on conflicting ruby requirements between platform versions in two different gems" do + build_repo4 do + build_gem "unf_ext", "0.0.8.2" - before do - lockfile(@lockfile) + build_gem "unf_ext", "0.0.8.2" do |s| + s.required_ruby_version = [">= 2.4", "< #{previous_ruby_minor}"] + s.platform = "x64-mingw32" + end + + build_gem "unf_ext", "0.0.8.2" do |s| + s.required_ruby_version = [">= #{previous_ruby_minor}", "< #{current_ruby_minor}"] + s.platform = "x64-mingw-ucrt" + end + + build_gem "google-protobuf", "3.21.12" + + build_gem "google-protobuf", "3.21.12" do |s| + s.required_ruby_version = [">= 2.5", "< #{previous_ruby_minor}"] + s.platform = "x64-mingw32" + end + + build_gem "google-protobuf", "3.21.12" do |s| + s.required_ruby_version = [">= #{previous_ruby_minor}", "< #{current_ruby_minor}"] + s.platform = "x64-mingw-ucrt" + end + end + + gemfile <<~G + source "https://gem.repo4" + + gem "google-protobuf" + gem "unf_ext" + G + + lockfile <<~L + GEM + remote: https://gem.repo4/ + specs: + google-protobuf (3.21.12) + unf_ext (0.0.8.2) + + PLATFORMS + x64-mingw-ucrt + x64-mingw32 + + DEPENDENCIES + google-protobuf + unf_ext + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "install --verbose", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s, "DEBUG_RESOLVER" => "1" } + end + + it "respects lower bound ruby requirements" do + build_repo4 do + build_gem "our_private_gem", "0.1.0" do |s| + s.required_ruby_version = ">= #{Gem.ruby_version}" + end + end + + gemfile <<-G + source "https://localgemserver.test" + + gem "our_private_gem" + G + + lockfile <<-L + GEM + remote: https://localgemserver.test/ + specs: + our_private_gem (0.1.0) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + our_private_gem + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "install", artifice: "compact_index", env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } + end + + context "when an update is available" do + let(:repo) do build_repo2 do build_gem "foo", "2.0" end + gem_repo2 + end + + before do + lockfile(@lockfile) end it "does not implicitly update" do bundle "lock" - expect(read_lockfile).to eq(@lockfile) + checksums = checksums_section_when_existing do |c| + c.checksum repo, "actionmailer", "2.3.2" + c.checksum repo, "actionpack", "2.3.2" + c.checksum repo, "activerecord", "2.3.2" + c.checksum repo, "activeresource", "2.3.2" + c.checksum repo, "activesupport", "2.3.2" + c.checksum repo, "foo", "1.0" + c.checksum repo, "rails", "2.3.2" + c.checksum repo, "rake", rake_version + c.checksum repo, "weakling", "0.0.3" + end + + expected_lockfile = <<~L + GEM + remote: #{file_uri_for(repo)}/ + specs: + actionmailer (2.3.2) + activesupport (= 2.3.2) + actionpack (2.3.2) + activesupport (= 2.3.2) + activerecord (2.3.2) + activesupport (= 2.3.2) + activeresource (2.3.2) + activesupport (= 2.3.2) + activesupport (2.3.2) + foo (1.0) + rails (2.3.2) + actionmailer (= 2.3.2) + actionpack (= 2.3.2) + activerecord (= 2.3.2) + activeresource (= 2.3.2) + rake (= #{rake_version}) + rake (#{rake_version}) + weakling (0.0.3) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + foo + rails + weakling + #{checksums} + BUNDLED WITH + #{Bundler::VERSION} + L + + expect(read_lockfile).to eq(expected_lockfile) end it "accounts for changes in the gemfile" do gemfile gemfile.gsub('"foo"', '"foo", "2.0"') bundle "lock" - expect(read_lockfile).to eq(@lockfile.sub("foo (1.0)", "foo (2.0)").sub(/foo$/, "foo (= 2.0)")) + checksums = checksums_section_when_existing do |c| + c.checksum repo, "actionmailer", "2.3.2" + c.checksum repo, "actionpack", "2.3.2" + c.checksum repo, "activerecord", "2.3.2" + c.checksum repo, "activeresource", "2.3.2" + c.checksum repo, "activesupport", "2.3.2" + c.no_checksum "foo", "2.0" + c.checksum repo, "rails", "2.3.2" + c.checksum repo, "rake", rake_version + c.checksum repo, "weakling", "0.0.3" + end + + expected_lockfile = <<~L + GEM + remote: #{file_uri_for(repo)}/ + specs: + actionmailer (2.3.2) + activesupport (= 2.3.2) + actionpack (2.3.2) + activesupport (= 2.3.2) + activerecord (2.3.2) + activesupport (= 2.3.2) + activeresource (2.3.2) + activesupport (= 2.3.2) + activesupport (2.3.2) + foo (2.0) + rails (2.3.2) + actionmailer (= 2.3.2) + actionpack (= 2.3.2) + activerecord (= 2.3.2) + activeresource (= 2.3.2) + rake (= #{rake_version}) + rake (#{rake_version}) + weakling (0.0.3) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + foo (= 2.0) + rails + weakling + #{checksums} + BUNDLED WITH + #{Bundler::VERSION} + L + + expect(read_lockfile).to eq(expected_lockfile) + end + end + + context "when a system gem has incorrect dependencies, different from the lockfile" do + before do + build_repo4 do + build_gem "debug", "1.6.3" do |s| + s.add_dependency "irb", ">= 1.3.6" + end + + build_gem "irb", "1.5.0" + end + + system_gems "irb-1.5.0", gem_repo: gem_repo4 + system_gems "debug-1.6.3", gem_repo: gem_repo4 + + # simulate gemspec with wrong empty dependencies + debug_gemspec_path = system_gem_path("specifications/debug-1.6.3.gemspec") + debug_gemspec = Gem::Specification.load(debug_gemspec_path.to_s) + debug_gemspec.dependencies.clear + File.write(debug_gemspec_path, debug_gemspec.to_ruby) + end + + it "respects the existing lockfile, even when reresolving" do + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + + gem "debug" + G + + lockfile <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + debug (1.6.3) + irb (>= 1.3.6) + irb (1.5.0) + + PLATFORMS + x86_64-linux + + DEPENDENCIES + debug + #{checksums_section} + BUNDLED WITH + #{Bundler::VERSION} + L + + simulate_platform "arm64-darwin-22" do + bundle "lock" + end + + checksums = checksums_section do |c| + c.no_checksum "debug", "1.6.3" + c.no_checksum "irb", "1.5.0" + end + + expect(lockfile).to eq <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + debug (1.6.3) + irb (>= 1.3.6) + irb (1.5.0) + + PLATFORMS + arm64-darwin-22 + x86_64-linux + + DEPENDENCIES + debug + #{checksums} + BUNDLED WITH + #{Bundler::VERSION} + L + end + end + + it "properly shows resolution errors including OR requirements" do + build_repo4 do + build_gem "activeadmin", "2.13.1" do |s| + s.add_dependency "railties", ">= 6.1", "< 7.1" + end + build_gem "actionpack", "6.1.4" + build_gem "actionpack", "7.0.3.1" + build_gem "actionpack", "7.0.4" + build_gem "railties", "6.1.4" do |s| + s.add_dependency "actionpack", "6.1.4" + end + build_gem "rails", "7.0.3.1" do |s| + s.add_dependency "railties", "7.0.3.1" + end + build_gem "rails", "7.0.4" do |s| + s.add_dependency "railties", "7.0.4" + end + end + + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + + gem "rails", ">= 7.0.3.1" + gem "activeadmin", "2.13.1" + G + + bundle "lock", raise_on_error: false + + expect(err).to eq <<~ERR.strip + Could not find compatible versions + + Because rails >= 7.0.4 depends on railties = 7.0.4 + and rails < 7.0.4 depends on railties = 7.0.3.1, + railties = 7.0.3.1 OR = 7.0.4 is required. + So, because railties = 7.0.3.1 OR = 7.0.4 could not be found in rubygems repository #{file_uri_for(gem_repo4)}/, cached gems or installed locally, + version solving has failed. + ERR + end + + it "is able to display some explanation on crazy irresolvable cases" do + build_repo4 do + build_gem "activeadmin", "2.13.1" do |s| + s.add_dependency "ransack", "= 3.1.0" + end + + # Activemodel is missing as a dependency in lockfile + build_gem "ransack", "3.1.0" do |s| + s.add_dependency "activemodel", ">= 6.0.4" + s.add_dependency "activesupport", ">= 6.0.4" + end + + %w[6.0.4 7.0.2.3 7.0.3.1 7.0.4].each do |version| + build_gem "activesupport", version + + # Activemodel is only available on 6.0.4 + if version == "6.0.4" + build_gem "activemodel", version do |s| + s.add_dependency "activesupport", version + end + end + + build_gem "rails", version do |s| + # Depednencies of Rails 7.0.2.3 are in reverse order + if version == "7.0.2.3" + s.add_dependency "activesupport", version + s.add_dependency "activemodel", version + else + s.add_dependency "activemodel", version + s.add_dependency "activesupport", version + end + end + end + end + + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + + gem "rails", ">= 7.0.2.3" + gem "activeadmin", "= 2.13.1" + G + + lockfile <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + activeadmin (2.13.1) + ransack (= 3.1.0) + ransack (3.1.0) + activemodel (>= 6.0.4) + + PLATFORMS + #{local_platform} + + DEPENDENCIES + activeadmin (= 2.13.1) + ransack (= 3.1.0) + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "lock", raise_on_error: false + + expect(err).to eq <<~ERR.strip + Could not find compatible versions + + Because every version of activemodel depends on activesupport = 6.0.4 + and rails >= 7.0.2.3, < 7.0.3.1 depends on activesupport = 7.0.2.3, + every version of activemodel is incompatible with rails >= 7.0.2.3, < 7.0.3.1. + And because rails >= 7.0.2.3, < 7.0.3.1 depends on activemodel = 7.0.2.3, + rails >= 7.0.2.3, < 7.0.3.1 cannot be used. + (1) So, because rails >= 7.0.3.1, < 7.0.4 depends on activemodel = 7.0.3.1 + and rails >= 7.0.4 depends on activemodel = 7.0.4, + rails >= 7.0.2.3 requires activemodel = 7.0.3.1 OR = 7.0.4. + + Because rails >= 7.0.2.3, < 7.0.3.1 depends on activemodel = 7.0.2.3 + and rails >= 7.0.3.1, < 7.0.4 depends on activesupport = 7.0.3.1, + rails >= 7.0.2.3, < 7.0.4 requires activemodel = 7.0.2.3 or activesupport = 7.0.3.1. + And because rails >= 7.0.4 depends on activesupport = 7.0.4 + and every version of activemodel depends on activesupport = 6.0.4, + activemodel != 7.0.2.3 is incompatible with rails >= 7.0.2.3. + And because rails >= 7.0.2.3 requires activemodel = 7.0.3.1 OR = 7.0.4 (1), + rails >= 7.0.2.3 cannot be used. + So, because Gemfile depends on rails >= 7.0.2.3, + version solving has failed. + ERR + + lockfile lockfile.gsub(/PLATFORMS\n #{local_platform}/m, "PLATFORMS\n #{lockfile_platforms("ruby")}") + + bundle "lock", raise_on_error: false + + expect(err).to eq <<~ERR.strip + Could not find compatible versions + + Because rails >= 7.0.3.1, < 7.0.4 depends on activemodel = 7.0.3.1 + and rails >= 7.0.2.3, < 7.0.3.1 depends on activemodel = 7.0.2.3, + rails >= 7.0.2.3, < 7.0.4 requires activemodel = 7.0.2.3 OR = 7.0.3.1. + And because every version of activemodel depends on activesupport = 6.0.4, + rails >= 7.0.2.3, < 7.0.4 requires activesupport = 6.0.4. + Because rails >= 7.0.3.1, < 7.0.4 depends on activesupport = 7.0.3.1 + and rails >= 7.0.2.3, < 7.0.3.1 depends on activesupport = 7.0.2.3, + rails >= 7.0.2.3, < 7.0.4 requires activesupport = 7.0.2.3 OR = 7.0.3.1. + Thus, rails >= 7.0.2.3, < 7.0.4 cannot be used. + And because rails >= 7.0.4 depends on activemodel = 7.0.4, + rails >= 7.0.2.3 requires activemodel = 7.0.4. + So, because activemodel = 7.0.4 could not be found in rubygems repository #{file_uri_for(gem_repo4)}/, cached gems or installed locally + and Gemfile depends on rails >= 7.0.2.3, + version solving has failed. + ERR + end + + it "does not accidentally resolves to prereleases" do + build_repo4 do + build_gem "autoproj", "2.0.3" do |s| + s.add_dependency "autobuild", ">= 1.10.0.a" + s.add_dependency "tty-prompt" + end + + build_gem "tty-prompt", "0.6.0" + build_gem "tty-prompt", "0.7.0" + + build_gem "autobuild", "1.10.0.b3" + build_gem "autobuild", "1.10.1" do |s| + s.add_dependency "tty-prompt", "~> 0.6.0" + end + end + + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + gem "autoproj", ">= 2.0.0" + G + + bundle "lock" + expect(lockfile).to_not include("autobuild (1.10.0.b3)") + expect(lockfile).to include("autobuild (1.10.1)") + end + + # Newer rails depends on Bundler, while ancient Rails does not. Bundler tries + # a first resolution pass that does not consider pre-releases. However, when + # using a pre-release Bundler (like the .dev version), that results in that + # pre-release being ignored and resolving to a version that does not depend on + # Bundler at all. We should avoid that and still consider .dev Bundler. + # + it "does not ignore prereleases with there's only one candidate" do + build_repo4 do + build_gem "rails", "7.4.0.2" do |s| + s.add_dependency "bundler", ">= 1.15.0" + end + + build_gem "rails", "2.3.18" + end + + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + gem "rails" + G + + bundle "lock" + expect(lockfile).to_not include("rails (2.3.18)") + expect(lockfile).to include("rails (7.4.0.2)") + end + + it "deals with platform specific incompatibilities" do + build_repo4 do + build_gem "activerecord", "6.0.6" + build_gem "activerecord-jdbc-adapter", "60.4" do |s| + s.platform = "java" + s.add_dependency "activerecord", "~> 6.0.0" + end + build_gem "activerecord-jdbc-adapter", "61.0" do |s| + s.platform = "java" + s.add_dependency "activerecord", "~> 6.1.0" + end + end + + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + gem "activerecord", "6.0.6" + gem "activerecord-jdbc-adapter", "61.0" + G + + simulate_platform "universal-java-19" do + bundle "lock", raise_on_error: false + end + + expect(err).to include("Could not find compatible versions") + expect(err).not_to include("ERROR REPORT TEMPLATE") + end + + context "when re-resolving to include prereleases" do + before do + build_repo4 do + build_gem "tzinfo-data", "1.2022.7" + build_gem "rails", "7.1.0.alpha" do |s| + s.add_dependency "activesupport" + end + build_gem "activesupport", "7.1.0.alpha" + end + end + + it "does not end up including gems scoped to other platforms in the lockfile" do + gemfile <<-G + source "#{file_uri_for(gem_repo4)}" + gem "rails" + gem "tzinfo-data", platform: :windows + G + + simulate_platform "x86_64-darwin-22" do + bundle "lock" + end + + expect(lockfile).not_to include("tzinfo-data (1.2022.7)") + end + end + + context "when resolving platform specific gems as indirect dependencies on truffleruby", :truffleruby_only do + before do + build_lib "foo", path: bundled_app do |s| + s.add_dependency "nokogiri" + end + + build_repo4 do + build_gem "nokogiri", "1.14.2" + build_gem "nokogiri", "1.14.2" do |s| + s.platform = "x86_64-linux" + end + end + + gemfile <<-G + source "#{file_uri_for(gem_repo4)}" + gemspec + G + end + + it "locks ruby specs" do + checksums = checksums_section_when_existing do |c| + c.no_checksum "foo", "1.0" + c.no_checksum "nokogiri", "1.14.2" + end + + simulate_platform "x86_64-linux" do + bundle "lock" + end + + expect(lockfile).to eq <<~L + PATH + remote: . + specs: + foo (1.0) + nokogiri + + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + nokogiri (1.14.2) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + foo! + #{checksums} + BUNDLED WITH + #{Bundler::VERSION} + L + end + end + + context "when adding a new gem that requires unlocking other transitive deps" do + before do + build_repo4 do + build_gem "govuk_app_config", "0.1.0" + + build_gem "govuk_app_config", "4.13.0" do |s| + s.add_dependency "railties", ">= 5.0" + end + + %w[7.0.4.1 7.0.4.3].each do |v| + build_gem "railties", v do |s| + s.add_dependency "actionpack", v + s.add_dependency "activesupport", v + end + + build_gem "activesupport", v + build_gem "actionpack", v + end + end + + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + + gem "govuk_app_config" + gem "activesupport", "7.0.4.3" + G + + # Simulate out of sync lockfile because top level dependency on + # activesuport has just been added to the Gemfile, and locked to a higher + # version + lockfile <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + actionpack (7.0.4.1) + activesupport (7.0.4.1) + govuk_app_config (4.13.0) + railties (>= 5.0) + railties (7.0.4.1) + actionpack (= 7.0.4.1) + activesupport (= 7.0.4.1) + + PLATFORMS + arm64-darwin-22 + + DEPENDENCIES + govuk_app_config + + BUNDLED WITH + #{Bundler::VERSION} + L + end + + it "does not downgrade top level dependencies" do + checksums = checksums_section_when_existing do |c| + c.no_checksum "actionpack", "7.0.4.3" + c.no_checksum "activesupport", "7.0.4.3" + c.no_checksum "govuk_app_config", "4.13.0" + c.no_checksum "railties", "7.0.4.3" + end + + simulate_platform "arm64-darwin-22" do + bundle "lock" + end + + expect(lockfile).to eq <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + actionpack (7.0.4.3) + activesupport (7.0.4.3) + govuk_app_config (4.13.0) + railties (>= 5.0) + railties (7.0.4.3) + actionpack (= 7.0.4.3) + activesupport (= 7.0.4.3) + + PLATFORMS + arm64-darwin-22 + + DEPENDENCIES + activesupport (= 7.0.4.3) + govuk_app_config + #{checksums} + BUNDLED WITH + #{Bundler::VERSION} + L end end end diff --git a/spec/bundler/commands/newgem_spec.rb b/spec/bundler/commands/newgem_spec.rb index ddefe0ba25..199340b131 100644 --- a/spec/bundler/commands/newgem_spec.rb +++ b/spec/bundler/commands/newgem_spec.rb @@ -12,14 +12,14 @@ RSpec.describe "bundle gem" do def bundle_exec_rubocop prepare_gemspec(bundled_app(gem_name, "#{gem_name}.gemspec")) - bundle "config set path #{rubocop_gems}", :dir => bundled_app(gem_name) - bundle "exec rubocop --debug --config .rubocop.yml", :dir => bundled_app(gem_name) + bundle "config set path #{rubocop_gems}", dir: bundled_app(gem_name) + bundle "exec rubocop --debug --config .rubocop.yml", dir: bundled_app(gem_name) end def bundle_exec_standardrb prepare_gemspec(bundled_app(gem_name, "#{gem_name}.gemspec")) - bundle "config set path #{standard_gems}", :dir => bundled_app(gem_name) - bundle "exec standardrb --debug", :dir => bundled_app(gem_name) + bundle "config set path #{standard_gems}", dir: bundled_app(gem_name) + bundle "exec standardrb --debug", dir: bundled_app(gem_name) end let(:generated_gemspec) { Bundler.load_gemspec_uncached(bundled_app(gem_name).join("#{gem_name}.gemspec")) } @@ -28,6 +28,10 @@ RSpec.describe "bundle gem" do let(:require_path) { "mygem" } + let(:minitest_test_file_path) { "test/test_mygem.rb" } + + let(:minitest_test_class_name) { "class TestMygem < Minitest::Test" } + before do sys_exec("git config --global user.name 'Bundler User'") sys_exec("git config --global user.email user@example.com") @@ -59,7 +63,7 @@ RSpec.describe "bundle gem" do end it "properly initializes git repo", :readline do - bundle "gem #{gem_name}", :dir => bundled_app("path with spaces") + bundle "gem #{gem_name}", dir: bundled_app("path with spaces") expect(bundled_app("path with spaces/#{gem_name}/.git")).to exist end end @@ -99,7 +103,7 @@ RSpec.describe "bundle gem" do expect(bundled_app("#{gem_name}/README.md").read).to match(%r{https://github\.com/bundleuser/#{gem_name}/blob/.*/CODE_OF_CONDUCT.md}) end - it "generates the README with a section for the Code of Conduct, respecting the configured git default branch", :git => ">= 2.28.0" do + it "generates the README with a section for the Code of Conduct, respecting the configured git default branch", git: ">= 2.28.0" do sys_exec("git config --global init.defaultBranch main") bundle "gem #{gem_name} --coc" @@ -144,7 +148,7 @@ RSpec.describe "bundle gem" do end shared_examples_for "--rubocop flag" do - context "is deprecated", :bundler => "< 3" do + context "is deprecated", bundler: "< 3" do before do bundle "gem #{gem_name} --rubocop" end @@ -175,7 +179,7 @@ RSpec.describe "bundle gem" do end shared_examples_for "--no-rubocop flag" do - context "is deprecated", :bundler => "< 3" do + context "is deprecated", bundler: "< 3" do define_negated_matcher :exclude, :include before do @@ -306,30 +310,30 @@ RSpec.describe "bundle gem" do expect(last_command).to be_success end - it "has no rubocop offenses when using --ext and --linter=rubocop flag", :readline do + it "has no rubocop offenses when using --ext=c and --linter=rubocop flag", :readline do skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core? - bundle "gem #{gem_name} --ext --linter=rubocop" + bundle "gem #{gem_name} --ext=c --linter=rubocop" bundle_exec_rubocop expect(last_command).to be_success end - it "has no rubocop offenses when using --ext, --test=minitest, and --linter=rubocop flag", :readline do + it "has no rubocop offenses when using --ext=c, --test=minitest, and --linter=rubocop flag", :readline do skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core? - bundle "gem #{gem_name} --ext --test=minitest --linter=rubocop" + bundle "gem #{gem_name} --ext=c --test=minitest --linter=rubocop" bundle_exec_rubocop expect(last_command).to be_success end - it "has no rubocop offenses when using --ext, --test=rspec, and --linter=rubocop flag", :readline do + it "has no rubocop offenses when using --ext=c, --test=rspec, and --linter=rubocop flag", :readline do skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core? - bundle "gem #{gem_name} --ext --test=rspec --linter=rubocop" + bundle "gem #{gem_name} --ext=c --test=rspec --linter=rubocop" bundle_exec_rubocop expect(last_command).to be_success end - it "has no rubocop offenses when using --ext, --ext=test-unit, and --linter=rubocop flag", :readline do + it "has no rubocop offenses when using --ext=c, --test=test-unit, and --linter=rubocop flag", :readline do skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core? - bundle "gem #{gem_name} --ext --test=test-unit --linter=rubocop" + bundle "gem #{gem_name} --ext=c --test=test-unit --linter=rubocop" bundle_exec_rubocop expect(last_command).to be_success end @@ -341,10 +345,45 @@ RSpec.describe "bundle gem" do expect(last_command).to be_success end + it "has no rubocop offenses when using --ext=rust and --linter=rubocop flag", :readline do + skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core? + skip "RubyGems incompatible with Rust builder" if ::Gem::Version.new("3.3.11") > ::Gem.rubygems_version + + bundle "gem #{gem_name} --ext=rust --linter=rubocop" + bundle_exec_rubocop + expect(last_command).to be_success + end + + it "has no rubocop offenses when using --ext=rust, --test=minitest, and --linter=rubocop flag", :readline do + skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core? + skip "RubyGems incompatible with Rust builder" if ::Gem::Version.new("3.3.11") > ::Gem.rubygems_version + + bundle "gem #{gem_name} --ext=rust --test=minitest --linter=rubocop" + bundle_exec_rubocop + expect(last_command).to be_success + end + + it "has no rubocop offenses when using --ext=rust, --test=rspec, and --linter=rubocop flag", :readline do + skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core? + skip "RubyGems incompatible with Rust builder" if ::Gem::Version.new("3.3.11") > ::Gem.rubygems_version + + bundle "gem #{gem_name} --ext=rust --test=rspec --linter=rubocop" + bundle_exec_rubocop + expect(last_command).to be_success + end + + it "has no rubocop offenses when using --ext=rust, --test=test-unit, and --linter=rubocop flag", :readline do + skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core? + skip "RubyGems incompatible with Rust builder" if ::Gem::Version.new("3.3.11") > ::Gem.rubygems_version + + bundle "gem #{gem_name} --ext=rust --test=test-unit --linter=rubocop" + bundle_exec_rubocop + expect(last_command).to be_success + end + shared_examples_for "CI config is absent" do it "does not create any CI files" do expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to_not exist - expect(bundled_app("#{gem_name}/.travis.yml")).to_not exist expect(bundled_app("#{gem_name}/.gitlab-ci.yml")).to_not exist expect(bundled_app("#{gem_name}/.circleci/config.yml")).to_not exist end @@ -396,7 +435,7 @@ RSpec.describe "bundle gem" do load_paths = [lib_dir, spec_dir] load_path_str = "-I#{load_paths.join(File::PATH_SEPARATOR)}" - sys_exec "#{Gem.ruby} #{load_path_str} #{bindir.join("bundle")} gem #{gem_name}", :env => { "PATH" => "" } + sys_exec "#{Gem.ruby} #{load_path_str} #{bindir.join("bundle")} gem #{gem_name}", env: { "PATH" => "" } end it "creates the gem without the need for git" do @@ -417,10 +456,10 @@ RSpec.describe "bundle gem" do prepare_gemspec(bundled_app("newgem", "newgem.gemspec")) - gems = ["rake-13.0.1"] - path = Bundler.feature_flag.default_install_uses_path? ? local_gem_path(:base => bundled_app("newgem")) : system_gem_path - system_gems gems, :path => path - bundle "exec rake build", :dir => bundled_app("newgem") + gems = ["rake-#{rake_version}"] + path = Bundler.feature_flag.default_install_uses_path? ? local_gem_path(base: bundled_app("newgem")) : system_gem_path + system_gems gems, path: path + bundle "exec rake build", dir: bundled_app("newgem") expect(last_command.stdboth).not_to include("ERROR") end @@ -429,7 +468,7 @@ RSpec.describe "bundle gem" do it "resolves ." do create_temporary_dir("tmp") - bundle "gem .", :dir => bundled_app("tmp") + bundle "gem .", dir: bundled_app("tmp") expect(bundled_app("tmp/lib/tmp.rb")).to exist end @@ -437,7 +476,7 @@ RSpec.describe "bundle gem" do it "resolves .." do create_temporary_dir("temp/empty_dir") - bundle "gem ..", :dir => bundled_app("temp/empty_dir") + bundle "gem ..", dir: bundled_app("temp/empty_dir") expect(bundled_app("temp/lib/temp.rb")).to exist end @@ -445,7 +484,7 @@ RSpec.describe "bundle gem" do it "resolves relative directory" do create_temporary_dir("tmp/empty/tmp") - bundle "gem ../../empty", :dir => bundled_app("tmp/empty/tmp") + bundle "gem ../../empty", dir: bundled_app("tmp/empty/tmp") expect(bundled_app("tmp/empty/lib/empty.rb")).to exist end @@ -594,17 +633,25 @@ RSpec.describe "bundle gem" do it "does not include the gemspec file in files" do bundle "gem #{gem_name}" - bundler_gemspec = Bundler::GemHelper.new(gemspec_dir).gemspec + bundler_gemspec = Bundler::GemHelper.new(bundled_app(gem_name), gem_name).gemspec expect(bundler_gemspec.files).not_to include("#{gem_name}.gemspec") end + it "does not include the Gemfile file in files" do + bundle "gem #{gem_name}" + + bundler_gemspec = Bundler::GemHelper.new(bundled_app(gem_name), gem_name).gemspec + + expect(bundler_gemspec.files).not_to include("Gemfile") + end + it "runs rake without problems" do bundle "gem #{gem_name}" - system_gems ["rake-13.0.1"] + system_gems ["rake-#{rake_version}"] - rakefile = strip_whitespace <<-RAKEFILE + rakefile = <<~RAKEFILE task :default do puts 'SUCCESS' end @@ -613,7 +660,7 @@ RSpec.describe "bundle gem" do file.puts rakefile end - sys_exec(rake, :dir => bundled_app(gem_name)) + sys_exec(rake, dir: bundled_app(gem_name)) expect(out).to include("SUCCESS") end @@ -702,7 +749,7 @@ RSpec.describe "bundle gem" do end it "builds spec skeleton" do - expect(bundled_app("#{gem_name}/test/test_#{require_path}.rb")).to exist + expect(bundled_app("#{gem_name}/#{minitest_test_file_path}")).to exist expect(bundled_app("#{gem_name}/test/test_helper.rb")).to exist end end @@ -722,7 +769,7 @@ RSpec.describe "bundle gem" do end it "builds spec skeleton" do - expect(bundled_app("#{gem_name}/test/test_#{require_path}.rb")).to exist + expect(bundled_app("#{gem_name}/#{minitest_test_file_path}")).to exist expect(bundled_app("#{gem_name}/test/test_helper.rb")).to exist end @@ -731,11 +778,15 @@ RSpec.describe "bundle gem" do end it "requires 'test_helper'" do - expect(bundled_app("#{gem_name}/test/test_#{require_path}.rb").read).to include(%(require "test_helper")) + expect(bundled_app("#{gem_name}/#{minitest_test_file_path}").read).to include(%(require "test_helper")) + end + + it "defines valid test class name" do + expect(bundled_app("#{gem_name}/#{minitest_test_file_path}").read).to include(minitest_test_class_name) end it "creates a default test which fails" do - expect(bundled_app("#{gem_name}/test/test_#{require_path}.rb").read).to include("assert false") + expect(bundled_app("#{gem_name}/#{minitest_test_file_path}").read).to include("assert false") end end @@ -746,17 +797,13 @@ RSpec.describe "bundle gem" do end it "creates a default rake task to run the test suite" do - rakefile = strip_whitespace <<-RAKEFILE + rakefile = <<~RAKEFILE # frozen_string_literal: true require "bundler/gem_tasks" - require "rake/testtask" + require "minitest/test_task" - Rake::TestTask.new(:test) do |t| - t.libs << "test" - t.libs << "lib" - t.test_files = FileList["test/**/test_*.rb"] - end + Minitest::TestTask.create task default: :test RAKEFILE @@ -804,7 +851,7 @@ RSpec.describe "bundle gem" do end it "creates a default rake task to run the test suite" do - rakefile = strip_whitespace <<-RAKEFILE + rakefile = <<~RAKEFILE # frozen_string_literal: true require "bundler/gem_tasks" @@ -880,7 +927,6 @@ RSpec.describe "bundle gem" do bundle "gem #{gem_name}" expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to_not exist - expect(bundled_app("#{gem_name}/.travis.yml")).to_not exist expect(bundled_app("#{gem_name}/.gitlab-ci.yml")).to_not exist expect(bundled_app("#{gem_name}/.circleci/config.yml")).to_not exist end @@ -892,6 +938,12 @@ RSpec.describe "bundle gem" do expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to exist end + + it "contained .gitlab-ci.yml into ignore list" do + bundle "gem #{gem_name} --ci=github" + + expect(bundled_app("#{gem_name}/#{gem_name}.gemspec").read).to include(".git .github appveyor") + end end context "--ci set to gitlab" do @@ -900,6 +952,12 @@ RSpec.describe "bundle gem" do expect(bundled_app("#{gem_name}/.gitlab-ci.yml")).to exist end + + it "contained .gitlab-ci.yml into ignore list" do + bundle "gem #{gem_name} --ci=gitlab" + + expect(bundled_app("#{gem_name}/#{gem_name}.gemspec").read).to include(".git .gitlab-ci.yml appveyor") + end end context "--ci set to circle" do @@ -908,20 +966,17 @@ RSpec.describe "bundle gem" do expect(bundled_app("#{gem_name}/.circleci/config.yml")).to exist end - end - context "--ci set to travis" do - it "generates a Travis CI config file" do - bundle "gem #{gem_name} --ci=travis" + it "contained .circleci into ignore list" do + bundle "gem #{gem_name} --ci=circle" - expect(bundled_app("#{gem_name}/.travis.yml")).to exist + expect(bundled_app("#{gem_name}/#{gem_name}.gemspec").read).to include(".git .circleci appveyor") end end context "gem.ci setting set to none" do it "doesn't generate any CI config" do expect(bundled_app("#{gem_name}/.github/workflows/main.yml")).to_not exist - expect(bundled_app("#{gem_name}/.travis.yml")).to_not exist expect(bundled_app("#{gem_name}/.gitlab-ci.yml")).to_not exist expect(bundled_app("#{gem_name}/.circleci/config.yml")).to_not exist end @@ -936,15 +991,6 @@ RSpec.describe "bundle gem" do end end - context "gem.ci setting set to travis" do - it "generates a Travis CI config file" do - bundle "config set gem.ci travis" - bundle "gem #{gem_name}" - - expect(bundled_app("#{gem_name}/.travis.yml")).to exist - end - end - context "gem.ci setting set to gitlab" do it "generates a GitLab CI config file" do bundle "config set gem.ci gitlab" @@ -1063,7 +1109,7 @@ RSpec.describe "bundle gem" do end end - context "gem.rubocop setting set to true", :bundler => "< 3" do + context "gem.rubocop setting set to true", bundler: "< 3" do before do bundle "config set gem.rubocop true" bundle "gem #{gem_name}" @@ -1300,6 +1346,10 @@ RSpec.describe "bundle gem" do let(:require_relative_path) { "test_gem" } + let(:minitest_test_file_path) { "test/test_test_gem.rb" } + + let(:minitest_test_class_name) { "class TestTestGem < Minitest::Test" } + let(:flags) { nil } it "does not nest constants" do @@ -1310,25 +1360,50 @@ RSpec.describe "bundle gem" do include_examples "generating a gem" - context "--ext parameter set" do - let(:flags) { "--ext" } + context "--ext parameter with no value" do + context "is deprecated", bundler: "< 3" do + it "prints deprecation when used after gem name" do + bundle ["gem", "--ext", gem_name].compact.join(" ") + expect(err).to include "[DEPRECATED]" + expect(err).to include "`--ext` with no arguments has been deprecated" + expect(bundled_app("#{gem_name}/ext/#{gem_name}/#{gem_name}.c")).to exist + end + + it "prints deprecation when used before gem name" do + bundle ["gem", gem_name, "--ext"].compact.join(" ") + expect(err).to include "[DEPRECATED]" + expect(err).to include "`--ext` with no arguments has been deprecated" + expect(bundled_app("#{gem_name}/ext/#{gem_name}/#{gem_name}.c")).to exist + end + end + end + + context "--ext parameter set with C" do + let(:flags) { "--ext=c" } before do bundle ["gem", gem_name, flags].compact.join(" ") end + it "is not deprecated" do + expect(err).not_to include "[DEPRECATED] Option `--ext` without explicit value is deprecated." + end + it "builds ext skeleton" do expect(bundled_app("#{gem_name}/ext/#{gem_name}/extconf.rb")).to exist expect(bundled_app("#{gem_name}/ext/#{gem_name}/#{gem_name}.h")).to exist expect(bundled_app("#{gem_name}/ext/#{gem_name}/#{gem_name}.c")).to exist end - it "includes rake-compiler" do + it "includes rake-compiler, but no Rust related changes" do expect(bundled_app("#{gem_name}/Gemfile").read).to include('gem "rake-compiler"') + + expect(bundled_app("#{gem_name}/Gemfile").read).to_not include('gem "rb_sys"') + expect(bundled_app("#{gem_name}/#{gem_name}.gemspec").read).to_not include('spec.required_rubygems_version = ">= ') end it "depends on compile task for build" do - rakefile = strip_whitespace <<-RAKEFILE + rakefile = <<~RAKEFILE # frozen_string_literal: true require "bundler/gem_tasks" @@ -1336,7 +1411,9 @@ RSpec.describe "bundle gem" do task build: :compile - Rake::ExtensionTask.new("#{gem_name}") do |ext| + GEMSPEC = Gem::Specification.load("#{gem_name}.gemspec") + + Rake::ExtensionTask.new("#{gem_name}", GEMSPEC) do |ext| ext.lib_dir = "lib/#{gem_name}" end @@ -1346,6 +1423,66 @@ RSpec.describe "bundle gem" do expect(bundled_app("#{gem_name}/Rakefile").read).to eq(rakefile) end end + + context "--ext parameter set with rust and old RubyGems" do + it "fails in friendly way" do + if ::Gem::Version.new("3.3.11") <= ::Gem.rubygems_version + skip "RubyGems compatible with Rust builder" + end + + expect do + bundle ["gem", gem_name, "--ext=rust"].compact.join(" ") + end.to raise_error(RuntimeError, /too old to build Rust extension/) + end + end + + context "--ext parameter set with rust" do + let(:flags) { "--ext=rust" } + + before do + skip "RubyGems incompatible with Rust builder" if ::Gem::Version.new("3.3.11") > ::Gem.rubygems_version + + bundle ["gem", gem_name, flags].compact.join(" ") + end + + it "is not deprecated" do + expect(err).not_to include "[DEPRECATED] Option `--ext` without explicit value is deprecated." + end + + it "builds ext skeleton" do + expect(bundled_app("#{gem_name}/Cargo.toml")).to exist + expect(bundled_app("#{gem_name}/ext/#{gem_name}/Cargo.toml")).to exist + expect(bundled_app("#{gem_name}/ext/#{gem_name}/extconf.rb")).to exist + expect(bundled_app("#{gem_name}/ext/#{gem_name}/src/lib.rs")).to exist + end + + it "includes rake-compiler, rb_sys gems and required_rubygems_version constraint" do + expect(bundled_app("#{gem_name}/Gemfile").read).to include('gem "rake-compiler"') + expect(bundled_app("#{gem_name}/Gemfile").read).to include('gem "rb_sys"') + expect(bundled_app("#{gem_name}/#{gem_name}.gemspec").read).to include('spec.required_rubygems_version = ">= ') + end + + it "depends on compile task for build" do + rakefile = <<~RAKEFILE + # frozen_string_literal: true + + require "bundler/gem_tasks" + require "rb_sys/extensiontask" + + task build: :compile + + GEMSPEC = Gem::Specification.load("#{gem_name}.gemspec") + + RbSys::ExtensionTask.new("#{gem_name}", GEMSPEC) do |ext| + ext.lib_dir = "lib/#{gem_name}" + end + + task default: :compile + RAKEFILE + + expect(bundled_app("#{gem_name}/Rakefile").read).to eq(rakefile) + end + end end context "gem naming with dashed", :readline do @@ -1355,6 +1492,10 @@ RSpec.describe "bundle gem" do let(:require_relative_path) { "gem" } + let(:minitest_test_file_path) { "test/test/test_gem.rb" } + + let(:minitest_test_class_name) { "class Test::TestGem < Minitest::Test" } + it "nests constants so they work" do bundle "gem #{gem_name}" expect(bundled_app("#{gem_name}/lib/#{require_path}/version.rb").read).to match(/module Test\n module Gem/) @@ -1372,22 +1513,22 @@ RSpec.describe "bundle gem" do end it "fails gracefully with a ." do - bundle "gem foo.gemspec", :raise_on_error => false + bundle "gem foo.gemspec", raise_on_error: false expect(err).to end_with("Invalid gem name foo.gemspec -- `Foo.gemspec` is an invalid constant name") end it "fails gracefully with a ^" do - bundle "gem ^", :raise_on_error => false + bundle "gem ^", raise_on_error: false expect(err).to end_with("Invalid gem name ^ -- `^` is an invalid constant name") end it "fails gracefully with a space" do - bundle "gem 'foo bar'", :raise_on_error => false + bundle "gem 'foo bar'", raise_on_error: false expect(err).to end_with("Invalid gem name foo bar -- `Foo bar` is an invalid constant name") end it "fails gracefully when multiple names are passed" do - bundle "gem foo bar baz", :raise_on_error => false + bundle "gem foo bar baz", raise_on_error: false expect(err).to eq(<<-E.strip) ERROR: "bundle gem" was called with arguments ["foo", "bar", "baz"] Usage: "bundle gem NAME [OPTIONS]" @@ -1397,7 +1538,7 @@ Usage: "bundle gem NAME [OPTIONS]" describe "#ensure_safe_gem_name", :readline do before do - bundle "gem #{subject}", :raise_on_error => false + bundle "gem #{subject}", raise_on_error: false end context "with an existing const name" do @@ -1430,7 +1571,7 @@ Usage: "bundle gem NAME [OPTIONS]" end expect(bundled_app("foobar/spec/spec_helper.rb")).to exist - rakefile = strip_whitespace <<-RAKEFILE + rakefile = <<~RAKEFILE # frozen_string_literal: true require "bundler/gem_tasks" @@ -1492,7 +1633,7 @@ Usage: "bundle gem NAME [OPTIONS]" context "on conflicts with a previously created file", :readline do it "should fail gracefully" do FileUtils.touch(bundled_app("conflict-foobar")) - bundle "gem conflict-foobar", :raise_on_error => false + bundle "gem conflict-foobar", raise_on_error: false expect(err).to eq("Couldn't create a new gem named `conflict-foobar` because there's an existing file named `conflict-foobar`.") expect(exitstatus).to eql(32) end diff --git a/spec/bundler/commands/open_spec.rb b/spec/bundler/commands/open_spec.rb index 53dc35c2c7..97374f30c3 100644 --- a/spec/bundler/commands/open_spec.rb +++ b/spec/bundler/commands/open_spec.rb @@ -10,66 +10,124 @@ RSpec.describe "bundle open" do end it "opens the gem with BUNDLER_EDITOR as highest priority" do - bundle "open rails", :env => { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" } + bundle "open rails", env: { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" } expect(out).to include("bundler_editor #{default_bundle_path("gems", "rails-2.3.2")}") end it "opens the gem with VISUAL as 2nd highest priority" do - bundle "open rails", :env => { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "" } + bundle "open rails", env: { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "" } expect(out).to include("visual #{default_bundle_path("gems", "rails-2.3.2")}") end it "opens the gem with EDITOR as 3rd highest priority" do - bundle "open rails", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" } + bundle "open rails", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" } expect(out).to include("editor #{default_bundle_path("gems", "rails-2.3.2")}") end it "complains if no EDITOR is set" do - bundle "open rails", :env => { "EDITOR" => "", "VISUAL" => "", "BUNDLER_EDITOR" => "" } + bundle "open rails", env: { "EDITOR" => "", "VISUAL" => "", "BUNDLER_EDITOR" => "" } expect(out).to eq("To open a bundled gem, set $EDITOR or $BUNDLER_EDITOR") end it "complains if gem not in bundle" do - bundle "open missing", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }, :raise_on_error => false + bundle "open missing", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }, raise_on_error: false expect(err).to match(/could not find gem 'missing'/i) end it "does not blow up if the gem to open does not have a Gemfile" do git = build_git "foo" - ref = git.ref_for("master", 11) + ref = git.ref_for("main", 11) install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" gem 'foo', :git => "#{lib_path("foo-1.0")}" G - bundle "open foo", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" } - expect(out).to match("editor #{default_bundle_path.join("bundler/gems/foo-1.0-#{ref}")}") + bundle "open foo", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" } + expect(out).to include("editor #{default_bundle_path.join("bundler", "gems", "foo-1.0-#{ref}")}") end it "suggests alternatives for similar-sounding gems" do - bundle "open Rails", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }, :raise_on_error => false + bundle "open Rails", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }, raise_on_error: false expect(err).to match(/did you mean rails\?/i) end it "opens the gem with short words" do - bundle "open rec", :env => { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" } + bundle "open rec", env: { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" } expect(out).to include("bundler_editor #{default_bundle_path("gems", "activerecord-2.3.2")}") end + it "opens subpath of the gem" do + bundle "open activerecord --path lib/activerecord", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" } + expect(out).to include("editor #{default_bundle_path("gems", "activerecord-2.3.2")}/lib/activerecord") + end + + it "opens subpath file of the gem" do + bundle "open activerecord --path lib/version.rb", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" } + expect(out).to include("editor #{default_bundle_path("gems", "activerecord-2.3.2")}/lib/version.rb") + end + + it "opens deep subpath of the gem" do + bundle "open activerecord --path lib/active_record", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" } + expect(out).to include("editor #{default_bundle_path("gems", "activerecord-2.3.2")}/lib/active_record") + end + + it "requires value for --path arg" do + bundle "open activerecord --path", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }, raise_on_error: false + expect(err).to eq "Cannot specify `--path` option without a value" + end + + it "suggests alternatives for similar-sounding gems when using subpath" do + bundle "open Rails --path README.md", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }, raise_on_error: false + expect(err).to match(/did you mean rails\?/i) + end + + it "suggests alternatives for similar-sounding gems when using deep subpath" do + bundle "open Rails --path some/path/here", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" }, raise_on_error: false + expect(err).to match(/did you mean rails\?/i) + end + + it "opens subpath of the short worded gem" do + bundle "open rec --path CHANGELOG.md", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" } + expect(out).to include("editor #{default_bundle_path("gems", "activerecord-2.3.2")}/CHANGELOG.md") + end + + it "opens deep subpath of the short worded gem" do + bundle "open rec --path lib/activerecord", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" } + expect(out).to include("editor #{default_bundle_path("gems", "activerecord-2.3.2")}/lib/activerecord") + end + + it "opens subpath of the selected matching gem", :readline do + env = { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" } + bundle "open active --path CHANGELOG.md", env: env do |input, _, _| + input.puts "2" + end + + expect(out).to include("bundler_editor #{default_bundle_path("gems", "activerecord-2.3.2").join("CHANGELOG.md")}") + end + + it "opens deep subpath of the selected matching gem", :readline do + env = { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" } + bundle "open active --path lib/activerecord/version.rb", env: env do |input, _, _| + input.puts "2" + end + + expect(out).to include("bundler_editor #{default_bundle_path("gems", "activerecord-2.3.2").join("lib", "activerecord", "version.rb")}") + end + it "select the gem from many match gems", :readline do env = { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" } - bundle "open active", :env => env do |input, _, _| + bundle "open active", env: env do |input, _, _| input.puts "2" end - expect(out).to match(/bundler_editor #{default_bundle_path('gems', 'activerecord-2.3.2')}\z/) + expect(out).to include("bundler_editor #{default_bundle_path("gems", "activerecord-2.3.2")}") end it "allows selecting exit from many match gems", :readline do env = { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" } - bundle "open active", :env => env do |input, _, _| + bundle "open active", env: env do |input, _, _| input.puts "0" end end @@ -82,12 +140,12 @@ RSpec.describe "bundle open" do G bundle "config set auto_install 1" - bundle "open rails", :env => { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" } + bundle "open rails", env: { "EDITOR" => "echo editor", "VISUAL" => "", "BUNDLER_EDITOR" => "" } expect(out).to include("Installing foo 1.0") end it "opens the editor with a clean env" do - bundle "open", :env => { "EDITOR" => "sh -c 'env'", "VISUAL" => "", "BUNDLER_EDITOR" => "" }, :raise_on_error => false + bundle "open", env: { "EDITOR" => "sh -c 'env'", "VISUAL" => "", "BUNDLER_EDITOR" => "" }, raise_on_error: false expect(out).not_to include("BUNDLE_GEMFILE=") end end @@ -111,7 +169,7 @@ RSpec.describe "bundle open" do end it "throws proper error when trying to open default gem" do - bundle "open json", :env => { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" } + bundle "open json", env: { "EDITOR" => "echo editor", "VISUAL" => "echo visual", "BUNDLER_EDITOR" => "echo bundler_editor" } expect(out).to include("Unable to open json because it's a default gem, so the directory it would normally be installed to does not exist.") end end diff --git a/spec/bundler/commands/outdated_spec.rb b/spec/bundler/commands/outdated_spec.rb index 731d67af1b..e7edc67e57 100644 --- a/spec/bundler/commands/outdated_spec.rb +++ b/spec/bundler/commands/outdated_spec.rb @@ -4,8 +4,8 @@ RSpec.describe "bundle outdated" do describe "with no arguments" do before do build_repo2 do - build_git "foo", :path => lib_path("foo") - build_git "zebra", :path => lib_path("zebra") + build_git "foo", path: lib_path("foo") + build_git "zebra", path: lib_path("zebra") end install_gemfile <<-G @@ -23,11 +23,11 @@ RSpec.describe "bundle outdated" do update_repo2 do build_gem "activesupport", "3.0" build_gem "weakling", "0.2" - update_git "foo", :path => lib_path("foo") - update_git "zebra", :path => lib_path("zebra") + update_git "foo", path: lib_path("foo") + update_git "zebra", path: lib_path("zebra") end - bundle "outdated", :raise_on_error => false + bundle "outdated", raise_on_error: false expected_output = <<~TABLE.gsub("x", "\\\h").tr(".", "\.").strip Gem Current Latest Requested Groups @@ -50,7 +50,7 @@ RSpec.describe "bundle outdated" do gem "AAA", "1.0.0" G - bundle "outdated", :raise_on_error => false + bundle "outdated", raise_on_error: false expected_output = <<~TABLE Gem Current Latest Requested Groups @@ -63,10 +63,10 @@ RSpec.describe "bundle outdated" do it "returns non zero exit status if outdated gems present" do update_repo2 do build_gem "activesupport", "3.0" - update_git "foo", :path => lib_path("foo") + update_git "foo", path: lib_path("foo") end - bundle "outdated", :raise_on_error => false + bundle "outdated", raise_on_error: false expect(exitstatus).to_not be_zero end @@ -89,7 +89,7 @@ RSpec.describe "bundle outdated" do update_repo2 { build_gem "activesupport", "3.0" } update_repo2 { build_gem "terranova", "9" } - bundle "outdated", :raise_on_error => false + bundle "outdated", raise_on_error: false expected_output = <<~TABLE.strip Gem Current Latest Requested Groups @@ -104,8 +104,8 @@ RSpec.describe "bundle outdated" do describe "with --verbose option" do before do build_repo2 do - build_git "foo", :path => lib_path("foo") - build_git "zebra", :path => lib_path("zebra") + build_git "foo", path: lib_path("foo") + build_git "zebra", path: lib_path("zebra") end install_gemfile <<-G @@ -139,7 +139,7 @@ RSpec.describe "bundle outdated" do gem 'activesupport', '2.3.5' G - bundle "outdated --verbose", :raise_on_error => false + bundle "outdated --verbose", raise_on_error: false expected_output = <<~TABLE.strip Gem Current Latest Requested Groups Path @@ -151,7 +151,7 @@ RSpec.describe "bundle outdated" do end end - describe "with multiple, duplicated sources, with lockfile in old format", :bundler => "< 3" do + describe "with multiple, duplicated sources, with lockfile in old format", bundler: "< 3" do before do build_repo2 do build_gem "dotenv", "2.7.6" @@ -192,7 +192,7 @@ RSpec.describe "bundle outdated" do vcr (6.0.0) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES dotenv @@ -205,8 +205,8 @@ RSpec.describe "bundle outdated" do end it "works" do - bundle :install, :artifice => "compact_index" - bundle :outdated, :artifice => "compact_index", :raise_on_error => false + bundle :install, artifice: "compact_index" + bundle :outdated, artifice: "compact_index", raise_on_error: false expected_output = <<~TABLE Gem Current Latest Requested Groups @@ -220,8 +220,8 @@ RSpec.describe "bundle outdated" do describe "with --group option" do before do build_repo2 do - build_git "foo", :path => lib_path("foo") - build_git "zebra", :path => lib_path("zebra") + build_git "foo", path: lib_path("foo") + build_git "zebra", path: lib_path("zebra") end install_gemfile <<-G @@ -243,7 +243,7 @@ RSpec.describe "bundle outdated" do build_gem "duradura", "8.0" end - bundle "outdated --group #{group}", :raise_on_error => false + bundle "outdated --group #{group}", raise_on_error: false end it "works when the bundle is up to date" do @@ -290,8 +290,8 @@ RSpec.describe "bundle outdated" do describe "with --groups option and outdated transitive dependencies" do before do build_repo2 do - build_git "foo", :path => lib_path("foo") - build_git "zebra", :path => lib_path("zebra") + build_git "foo", path: lib_path("foo") + build_git "zebra", path: lib_path("zebra") build_gem "bar", %w[2.0.0] @@ -312,7 +312,7 @@ RSpec.describe "bundle outdated" do end it "returns a sorted list of outdated gems" do - bundle "outdated --groups", :raise_on_error => false + bundle "outdated --groups", raise_on_error: false expected_output = <<~TABLE.strip Gem Current Latest Requested Groups @@ -326,8 +326,8 @@ RSpec.describe "bundle outdated" do describe "with --groups option" do before do build_repo2 do - build_git "foo", :path => lib_path("foo") - build_git "zebra", :path => lib_path("zebra") + build_git "foo", path: lib_path("foo") + build_git "zebra", path: lib_path("zebra") end install_gemfile <<-G @@ -354,7 +354,7 @@ RSpec.describe "bundle outdated" do build_gem "duradura", "8.0" end - bundle "outdated --groups", :raise_on_error => false + bundle "outdated --groups", raise_on_error: false expected_output = <<~TABLE.strip Gem Current Latest Requested Groups @@ -370,8 +370,8 @@ RSpec.describe "bundle outdated" do describe "with --local option" do before do build_repo2 do - build_git "foo", :path => lib_path("foo") - build_git "zebra", :path => lib_path("zebra") + build_git "foo", path: lib_path("foo") + build_git "zebra", path: lib_path("zebra") end install_gemfile <<-G @@ -398,7 +398,7 @@ RSpec.describe "bundle outdated" do gem "activesupport", "2.3.4" G - bundle "outdated --local", :raise_on_error => false + bundle "outdated --local", raise_on_error: false expected_output = <<~TABLE.strip Gem Current Latest Requested Groups @@ -420,8 +420,8 @@ RSpec.describe "bundle outdated" do context "and gems are outdated" do before do build_repo2 do - build_git "foo", :path => lib_path("foo") - build_git "zebra", :path => lib_path("zebra") + build_git "foo", path: lib_path("foo") + build_git "zebra", path: lib_path("zebra") build_gem "activesupport", "3.0" build_gem "weakling", "0.2" @@ -455,13 +455,13 @@ RSpec.describe "bundle outdated" do end describe "with --parseable option" do - subject { bundle "outdated --parseable", :raise_on_error => false } + subject { bundle "outdated --parseable", raise_on_error: false } it_behaves_like "a minimal output is desired" end describe "with aliased --porcelain option" do - subject { bundle "outdated --porcelain", :raise_on_error => false } + subject { bundle "outdated --porcelain", raise_on_error: false } it_behaves_like "a minimal output is desired" end @@ -469,8 +469,8 @@ RSpec.describe "bundle outdated" do describe "with specified gems" do it "returns list of outdated gems" do build_repo2 do - build_git "foo", :path => lib_path("foo") - build_git "zebra", :path => lib_path("zebra") + build_git "foo", path: lib_path("foo") + build_git "zebra", path: lib_path("zebra") end install_gemfile <<-G @@ -485,10 +485,10 @@ RSpec.describe "bundle outdated" do update_repo2 do build_gem "activesupport", "3.0" - update_git "foo", :path => lib_path("foo") + update_git "foo", path: lib_path("foo") end - bundle "outdated foo", :raise_on_error => false + bundle "outdated foo", raise_on_error: false expected_output = <<~TABLE.gsub("x", "\\\h").tr(".", "\.").strip Gem Current Latest Requested Groups @@ -502,8 +502,8 @@ RSpec.describe "bundle outdated" do describe "pre-release gems" do before do build_repo2 do - build_git "foo", :path => lib_path("foo") - build_git "zebra", :path => lib_path("zebra") + build_git "foo", path: lib_path("foo") + build_git "zebra", path: lib_path("zebra") end install_gemfile <<-G @@ -535,7 +535,7 @@ RSpec.describe "bundle outdated" do build_gem "activesupport", "3.0.0.beta" end - bundle "outdated --pre", :raise_on_error => false + bundle "outdated --pre", raise_on_error: false expected_output = <<~TABLE.strip Gem Current Latest Requested Groups @@ -558,7 +558,7 @@ RSpec.describe "bundle outdated" do gem "activesupport", "3.0.0.beta.1" G - bundle "outdated", :raise_on_error => false + bundle "outdated", raise_on_error: false expected_output = <<~TABLE.strip Gem Current Latest Requested Groups @@ -570,12 +570,11 @@ RSpec.describe "bundle outdated" do end end - filter_strict_option = Bundler.feature_flag.bundler_2_mode? ? :"filter-strict" : :strict - describe "with --#{filter_strict_option} option" do + describe "with --filter-strict option" do before do build_repo2 do - build_git "foo", :path => lib_path("foo") - build_git "zebra", :path => lib_path("zebra") + build_git "foo", path: lib_path("foo") + build_git "zebra", path: lib_path("zebra") end install_gemfile <<-G @@ -595,7 +594,23 @@ RSpec.describe "bundle outdated" do build_gem "weakling", "0.0.5" end - bundle :outdated, filter_strict_option => true, :raise_on_error => false + bundle :outdated, "filter-strict": true, raise_on_error: false + + expected_output = <<~TABLE.strip + Gem Current Latest Requested Groups + weakling 0.0.3 0.0.5 ~> 0.0.1 default + TABLE + + expect(out).to end_with(expected_output) + end + + it "only reports gems that have a newer version that matches the specified dependency version requirements, using --strict alias" do + update_repo2 do + build_gem "activesupport", "3.0" + build_gem "weakling", "0.0.5" + end + + bundle :outdated, strict: true, raise_on_error: false expected_output = <<~TABLE.strip Gem Current Latest Requested Groups @@ -611,7 +626,7 @@ RSpec.describe "bundle outdated" do gem "activesupport", platforms: [:ruby_22] G - bundle :outdated, filter_strict_option => true + bundle :outdated, "filter-strict": true expect(out).to end_with("Bundle up to date!") end @@ -622,7 +637,7 @@ RSpec.describe "bundle outdated" do gem "rack_middleware", "1.0" G - bundle :outdated, filter_strict_option => true + bundle :outdated, "filter-strict": true expect(out).to end_with("Bundle up to date!") end @@ -640,7 +655,7 @@ RSpec.describe "bundle outdated" do build_gem "weakling", "0.0.5" end - bundle :outdated, filter_strict_option => true, "filter-patch" => true, :raise_on_error => false + bundle :outdated, :"filter-strict" => true, "filter-patch" => true, :raise_on_error => false expected_output = <<~TABLE.strip Gem Current Latest Requested Groups @@ -662,7 +677,7 @@ RSpec.describe "bundle outdated" do build_gem "weakling", "0.1.5" end - bundle :outdated, filter_strict_option => true, "filter-minor" => true, :raise_on_error => false + bundle :outdated, :"filter-strict" => true, "filter-minor" => true, :raise_on_error => false expected_output = <<~TABLE.strip Gem Current Latest Requested Groups @@ -684,7 +699,7 @@ RSpec.describe "bundle outdated" do build_gem "weakling", "1.1.5" end - bundle :outdated, filter_strict_option => true, "filter-major" => true, :raise_on_error => false + bundle :outdated, :"filter-strict" => true, "filter-major" => true, :raise_on_error => false expected_output = <<~TABLE.strip Gem Current Latest Requested Groups @@ -699,8 +714,8 @@ RSpec.describe "bundle outdated" do describe "with invalid gem name" do before do build_repo2 do - build_git "foo", :path => lib_path("foo") - build_git "zebra", :path => lib_path("zebra") + build_git "foo", path: lib_path("foo") + build_git "zebra", path: lib_path("zebra") end install_gemfile <<-G @@ -715,12 +730,12 @@ RSpec.describe "bundle outdated" do end it "returns could not find gem name" do - bundle "outdated invalid_gem_name", :raise_on_error => false + bundle "outdated invalid_gem_name", raise_on_error: false expect(err).to include("Could not find gem 'invalid_gem_name'.") end it "returns non-zero exit code" do - bundle "outdated invalid_gem_name", :raise_on_error => false + bundle "outdated invalid_gem_name", raise_on_error: false expect(exitstatus).to_not be_zero end end @@ -733,11 +748,11 @@ RSpec.describe "bundle outdated" do G bundle "config set auto_install 1" - bundle :outdated, :raise_on_error => false + bundle :outdated, raise_on_error: false expect(out).to include("Installing foo 1.0") end - context "after bundle install --deployment", :bundler => "< 3" do + context "after bundle install --deployment", bundler: "< 3" do before do build_repo2 @@ -748,13 +763,13 @@ RSpec.describe "bundle outdated" do gem "foo" G bundle :lock - bundle :install, :deployment => true + bundle :install, deployment: true end it "outputs a helpful message about being in deployment mode" do update_repo2 { build_gem "activesupport", "3.0" } - bundle "outdated", :raise_on_error => false + bundle "outdated", raise_on_error: false expect(last_command).to be_failure expect(err).to include("You are trying to check outdated gems in deployment mode.") expect(err).to include("Run `bundle outdated` elsewhere.") @@ -766,8 +781,8 @@ RSpec.describe "bundle outdated" do context "after bundle config set --local deployment true" do before do build_repo2 do - build_git "foo", :path => lib_path("foo") - build_git "zebra", :path => lib_path("zebra") + build_git "foo", path: lib_path("foo") + build_git "zebra", path: lib_path("zebra") end install_gemfile <<-G @@ -782,7 +797,7 @@ RSpec.describe "bundle outdated" do it "outputs a helpful message about being in deployment mode" do update_repo2 { build_gem "activesupport", "3.0" } - bundle "outdated", :raise_on_error => false + bundle "outdated", raise_on_error: false expect(last_command).to be_failure expect(err).to include("You are trying to check outdated gems in deployment mode.") expect(err).to include("Run `bundle outdated` elsewhere.") @@ -822,13 +837,13 @@ RSpec.describe "bundle outdated" do expect(out).to end_with("Bundle up to date!") end - it "reports that updates are available if the JRuby platform is used", :jruby do + it "reports that updates are available if the JRuby platform is used", :jruby_only do install_gemfile <<-G source "#{file_uri_for(gem_repo2)}" gem "laduradura", '= 5.15.2', :platforms => [:ruby, :jruby] G - bundle "outdated", :raise_on_error => false + bundle "outdated", raise_on_error: false expected_output = <<~TABLE.strip Gem Current Latest Requested Groups @@ -852,8 +867,8 @@ RSpec.describe "bundle outdated" do shared_examples_for "major version updates are detected" do before do build_repo2 do - build_git "foo", :path => lib_path("foo") - build_git "zebra", :path => lib_path("zebra") + build_git "foo", path: lib_path("foo") + build_git "zebra", path: lib_path("zebra") end install_gemfile <<-G @@ -878,8 +893,8 @@ RSpec.describe "bundle outdated" do context "when on a new machine" do before do build_repo2 do - build_git "foo", :path => lib_path("foo") - build_git "zebra", :path => lib_path("zebra") + build_git "foo", path: lib_path("foo") + build_git "zebra", path: lib_path("zebra") end install_gemfile <<-G @@ -894,22 +909,22 @@ RSpec.describe "bundle outdated" do simulate_new_machine - update_git "foo", :path => lib_path("foo") + update_git "foo", path: lib_path("foo") update_repo2 do build_gem "activesupport", "3.3.5" build_gem "weakling", "0.8.0" end end - subject { bundle "outdated", :raise_on_error => false } + subject { bundle "outdated", raise_on_error: false } it_behaves_like "version update is detected" end shared_examples_for "minor version updates are detected" do before do build_repo2 do - build_git "foo", :path => lib_path("foo") - build_git "zebra", :path => lib_path("zebra") + build_git "foo", path: lib_path("foo") + build_git "zebra", path: lib_path("zebra") end install_gemfile <<-G @@ -934,8 +949,8 @@ RSpec.describe "bundle outdated" do shared_examples_for "patch version updates are detected" do before do build_repo2 do - build_git "foo", :path => lib_path("foo") - build_git "zebra", :path => lib_path("zebra") + build_git "foo", path: lib_path("foo") + build_git "zebra", path: lib_path("zebra") end install_gemfile <<-G @@ -967,8 +982,8 @@ RSpec.describe "bundle outdated" do shared_examples_for "major version is ignored" do before do build_repo2 do - build_git "foo", :path => lib_path("foo") - build_git "zebra", :path => lib_path("zebra") + build_git "foo", path: lib_path("foo") + build_git "zebra", path: lib_path("zebra") end install_gemfile <<-G @@ -993,8 +1008,8 @@ RSpec.describe "bundle outdated" do shared_examples_for "minor version is ignored" do before do build_repo2 do - build_git "foo", :path => lib_path("foo") - build_git "zebra", :path => lib_path("zebra") + build_git "foo", path: lib_path("foo") + build_git "zebra", path: lib_path("zebra") end install_gemfile <<-G @@ -1019,8 +1034,8 @@ RSpec.describe "bundle outdated" do shared_examples_for "patch version is ignored" do before do build_repo2 do - build_git "foo", :path => lib_path("foo") - build_git "zebra", :path => lib_path("zebra") + build_git "foo", path: lib_path("foo") + build_git "zebra", path: lib_path("zebra") end install_gemfile <<-G @@ -1043,7 +1058,7 @@ RSpec.describe "bundle outdated" do end describe "with --filter-major option" do - subject { bundle "outdated --filter-major", :raise_on_error => false } + subject { bundle "outdated --filter-major", raise_on_error: false } it_behaves_like "major version updates are detected" it_behaves_like "minor version is ignored" @@ -1051,7 +1066,7 @@ RSpec.describe "bundle outdated" do end describe "with --filter-minor option" do - subject { bundle "outdated --filter-minor", :raise_on_error => false } + subject { bundle "outdated --filter-minor", raise_on_error: false } it_behaves_like "minor version updates are detected" it_behaves_like "major version is ignored" @@ -1059,7 +1074,7 @@ RSpec.describe "bundle outdated" do end describe "with --filter-patch option" do - subject { bundle "outdated --filter-patch", :raise_on_error => false } + subject { bundle "outdated --filter-patch", raise_on_error: false } it_behaves_like "patch version updates are detected" it_behaves_like "major version is ignored" @@ -1067,7 +1082,7 @@ RSpec.describe "bundle outdated" do end describe "with --filter-minor --filter-patch options" do - subject { bundle "outdated --filter-minor --filter-patch", :raise_on_error => false } + subject { bundle "outdated --filter-minor --filter-patch", raise_on_error: false } it_behaves_like "minor version updates are detected" it_behaves_like "patch version updates are detected" @@ -1075,7 +1090,7 @@ RSpec.describe "bundle outdated" do end describe "with --filter-major --filter-minor options" do - subject { bundle "outdated --filter-major --filter-minor", :raise_on_error => false } + subject { bundle "outdated --filter-major --filter-minor", raise_on_error: false } it_behaves_like "major version updates are detected" it_behaves_like "minor version updates are detected" @@ -1083,7 +1098,7 @@ RSpec.describe "bundle outdated" do end describe "with --filter-major --filter-patch options" do - subject { bundle "outdated --filter-major --filter-patch", :raise_on_error => false } + subject { bundle "outdated --filter-major --filter-patch", raise_on_error: false } it_behaves_like "major version updates are detected" it_behaves_like "patch version updates are detected" @@ -1091,7 +1106,7 @@ RSpec.describe "bundle outdated" do end describe "with --filter-major --filter-minor --filter-patch options" do - subject { bundle "outdated --filter-major --filter-minor --filter-patch", :raise_on_error => false } + subject { bundle "outdated --filter-major --filter-minor --filter-patch", raise_on_error: false } it_behaves_like "major version updates are detected" it_behaves_like "minor version updates are detected" @@ -1099,116 +1114,125 @@ RSpec.describe "bundle outdated" do end context "conservative updates" do - context "without update-strict" do - before do - build_repo4 do - build_gem "patch", %w[1.0.0 1.0.1] - build_gem "minor", %w[1.0.0 1.0.1 1.1.0] - build_gem "major", %w[1.0.0 1.0.1 1.1.0 2.0.0] - end + before do + build_repo4 do + build_gem "patch", %w[1.0.0 1.0.1] + build_gem "minor", %w[1.0.0 1.0.1 1.1.0] + build_gem "major", %w[1.0.0 1.0.1 1.1.0 2.0.0] + end - # establish a lockfile set to 1.0.0 - install_gemfile <<-G - source "#{file_uri_for(gem_repo4)}" - gem 'patch', '1.0.0' - gem 'minor', '1.0.0' - gem 'major', '1.0.0' - G + # establish a lockfile set to 1.0.0 + install_gemfile <<-G + source "#{file_uri_for(gem_repo4)}" + gem 'patch', '1.0.0' + gem 'minor', '1.0.0' + gem 'major', '1.0.0' + G - # remove 1.4.3 requirement and bar altogether - # to setup update specs below - gemfile <<-G - source "#{file_uri_for(gem_repo4)}" - gem 'patch' - gem 'minor' - gem 'major' - G - end + # remove all version requirements + gemfile <<-G + source "#{file_uri_for(gem_repo4)}" + gem 'patch' + gem 'minor' + gem 'major' + G + end - it "shows nothing when patching and filtering to minor" do - bundle "outdated --patch --filter-minor" + it "shows nothing when patching and filtering to minor" do + bundle "outdated --patch --filter-minor" - expect(out).to end_with("No minor updates to display.") - end + expect(out).to end_with("No minor updates to display.") + end - it "shows all gems when patching and filtering to patch" do - bundle "outdated --patch --filter-patch", :raise_on_error => false + it "shows all gems when patching and filtering to patch" do + bundle "outdated --patch --filter-patch", raise_on_error: false - expected_output = <<~TABLE.strip - Gem Current Latest Requested Groups - major 1.0.0 1.0.1 >= 0 default - minor 1.0.0 1.0.1 >= 0 default - patch 1.0.0 1.0.1 >= 0 default - TABLE + expected_output = <<~TABLE.strip + Gem Current Latest Requested Groups + major 1.0.0 1.0.1 >= 0 default + minor 1.0.0 1.0.1 >= 0 default + patch 1.0.0 1.0.1 >= 0 default + TABLE - expect(out).to end_with(expected_output) - end + expect(out).to end_with(expected_output) + end - it "shows minor and major when updating to minor and filtering to patch and minor" do - bundle "outdated --minor --filter-minor", :raise_on_error => false + it "shows minor and major when updating to minor and filtering to patch and minor" do + bundle "outdated --minor --filter-minor", raise_on_error: false - expected_output = <<~TABLE.strip - Gem Current Latest Requested Groups - major 1.0.0 1.1.0 >= 0 default - minor 1.0.0 1.1.0 >= 0 default - TABLE + expected_output = <<~TABLE.strip + Gem Current Latest Requested Groups + major 1.0.0 1.1.0 >= 0 default + minor 1.0.0 1.1.0 >= 0 default + TABLE - expect(out).to end_with(expected_output) - end + expect(out).to end_with(expected_output) + end - it "shows minor when updating to major and filtering to minor with parseable" do - bundle "outdated --major --filter-minor --parseable", :raise_on_error => false + it "shows minor when updating to major and filtering to minor with parseable" do + bundle "outdated --major --filter-minor --parseable", raise_on_error: false - expect(out).not_to include("patch (newest") - expect(out).to include("minor (newest") - expect(out).not_to include("major (newest") - end + expect(out).not_to include("patch (newest") + expect(out).to include("minor (newest") + expect(out).not_to include("major (newest") end + end - context "with update-strict" do - before do - build_repo4 do - build_gem "foo", %w[1.4.3 1.4.4] do |s| - s.add_dependency "bar", "~> 2.0" - end - build_gem "foo", %w[1.4.5 1.5.0] do |s| - s.add_dependency "bar", "~> 2.1" - end - build_gem "foo", %w[1.5.1] do |s| - s.add_dependency "bar", "~> 3.0" - end - build_gem "bar", %w[2.0.3 2.0.4 2.0.5 2.1.0 2.1.1 3.0.0] - build_gem "qux", %w[1.0.0 1.1.0 2.0.0] + context "tricky conservative updates" do + before do + build_repo4 do + build_gem "foo", %w[1.4.3 1.4.4] do |s| + s.add_dependency "bar", "~> 2.0" + end + build_gem "foo", %w[1.4.5 1.5.0] do |s| + s.add_dependency "bar", "~> 2.1" end + build_gem "foo", %w[1.5.1] do |s| + s.add_dependency "bar", "~> 3.0" + end + build_gem "bar", %w[2.0.3 2.0.4 2.0.5 2.1.0 2.1.1 3.0.0] + build_gem "qux", %w[1.0.0 1.1.0 2.0.0] + end - # establish a lockfile set to 1.4.3 - install_gemfile <<-G - source "#{file_uri_for(gem_repo4)}" - gem 'foo', '1.4.3' - gem 'bar', '2.0.3' - gem 'qux', '1.0.0' - G + # establish a lockfile set to 1.4.3 + install_gemfile <<-G + source "#{file_uri_for(gem_repo4)}" + gem 'foo', '1.4.3' + gem 'bar', '2.0.3' + gem 'qux', '1.0.0' + G - # remove 1.4.3 requirement and bar altogether - # to setup update specs below - gemfile <<-G - source "#{file_uri_for(gem_repo4)}" - gem 'foo' - gem 'qux' - G - end + # remove 1.4.3 requirement and bar altogether + # to setup update specs below + gemfile <<-G + source "#{file_uri_for(gem_repo4)}" + gem 'foo' + gem 'qux' + G + end - it "shows gems with update-strict updating to patch and filtering to patch" do - bundle "outdated --patch --update-strict --filter-patch", :raise_on_error => false + it "shows gems updating to patch and filtering to patch" do + bundle "outdated --patch --filter-patch", raise_on_error: false, env: { "DEBUG_RESOLVER" => "1" } - expected_output = <<~TABLE.strip - Gem Current Latest Requested Groups - bar 2.0.3 2.0.5 - foo 1.4.3 1.4.4 >= 0 default - TABLE + expected_output = <<~TABLE.strip + Gem Current Latest Requested Groups + bar 2.0.3 2.0.5 + foo 1.4.3 1.4.4 >= 0 default + TABLE - expect(out).to end_with(expected_output) - end + expect(out).to end_with(expected_output) + end + + it "shows gems updating to patch and filtering to patch, in debug mode" do + bundle "outdated --patch --filter-patch", raise_on_error: false, env: { "DEBUG" => "1" } + + expected_output = <<~TABLE.strip + Gem Current Latest Requested Groups Path + bar 2.0.3 2.0.5 + foo 1.4.3 1.4.4 >= 0 default + TABLE + + expect(out).to end_with(expected_output) end end @@ -1232,7 +1256,7 @@ RSpec.describe "bundle outdated" do gem 'weakling' G - bundle "outdated --only-explicit", :raise_on_error => false + bundle "outdated --only-explicit", raise_on_error: false expected_output = <<~TABLE.strip Gem Current Latest Requested Groups @@ -1282,7 +1306,7 @@ RSpec.describe "bundle outdated" do end it "reports a single entry per gem" do - bundle "outdated", :raise_on_error => false + bundle "outdated", raise_on_error: false expected_output = <<~TABLE.strip Gem Current Latest Requested Groups @@ -1331,7 +1355,7 @@ RSpec.describe "bundle outdated" do end it "works" do - bundle "outdated", :raise_on_error => false + bundle "outdated", raise_on_error: false expected_output = <<~TABLE.strip Gem Current Latest Requested Groups diff --git a/spec/bundler/commands/platform_spec.rb b/spec/bundler/commands/platform_spec.rb new file mode 100644 index 0000000000..61e615acae --- /dev/null +++ b/spec/bundler/commands/platform_spec.rb @@ -0,0 +1,1307 @@ +# frozen_string_literal: true + +RSpec.describe "bundle platform" do + context "without flags" do + it "returns all the output" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + + #{ruby_version_correct} + + gem "foo" + G + + bundle "platform" + expect(out).to eq(<<-G.chomp) +Your platform is: #{Gem::Platform.local} + +Your app has gems that work on these platforms: +* #{local_platform} + +Your Gemfile specifies a Ruby version requirement: +* ruby #{Gem.ruby_version} + +Your current platform satisfies the Ruby version requirement. +G + end + + it "returns all the output including the patchlevel" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + + #{ruby_version_correct_patchlevel} + + gem "foo" + G + + bundle "platform" + expect(out).to eq(<<-G.chomp) +Your platform is: #{Gem::Platform.local} + +Your app has gems that work on these platforms: +* #{local_platform} + +Your Gemfile specifies a Ruby version requirement: +* #{Bundler::RubyVersion.system.single_version_string} + +Your current platform satisfies the Ruby version requirement. +G + end + + it "doesn't print ruby version requirement if it isn't specified" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + + gem "foo" + G + + bundle "platform" + expect(out).to eq(<<-G.chomp) +Your platform is: #{Gem::Platform.local} + +Your app has gems that work on these platforms: +* #{local_platform} + +Your Gemfile does not specify a Ruby version requirement. +G + end + + it "doesn't match the ruby version requirement" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + + #{ruby_version_incorrect} + + gem "foo" + G + + bundle "platform" + expect(out).to eq(<<-G.chomp) +Your platform is: #{Gem::Platform.local} + +Your app has gems that work on these platforms: +* #{local_platform} + +Your Gemfile specifies a Ruby version requirement: +* ruby #{not_local_ruby_version} + +Your Ruby version is #{Gem.ruby_version}, but your Gemfile specified #{not_local_ruby_version} +G + end + end + + context "--ruby" do + it "returns ruby version when explicit" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + ruby "1.9.3", :engine => 'ruby', :engine_version => '1.9.3' + + gem "foo" + G + + bundle "platform --ruby" + + expect(out).to eq("ruby 1.9.3") + end + + it "defaults to MRI" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + ruby "1.9.3" + + gem "foo" + G + + bundle "platform --ruby" + + expect(out).to eq("ruby 1.9.3") + end + + it "handles jruby" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + ruby "1.8.7", :engine => 'jruby', :engine_version => '1.6.5' + + gem "foo" + G + + bundle "platform --ruby" + + expect(out).to eq("ruby 1.8.7 (jruby 1.6.5)") + end + + it "handles rbx" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + ruby "1.8.7", :engine => 'rbx', :engine_version => '1.2.4' + + gem "foo" + G + + bundle "platform --ruby" + + expect(out).to eq("ruby 1.8.7 (rbx 1.2.4)") + end + + it "handles truffleruby" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + ruby "2.5.1", :engine => 'truffleruby', :engine_version => '1.0.0-rc6' + + gem "foo" + G + + bundle "platform --ruby" + + expect(out).to eq("ruby 2.5.1 (truffleruby 1.0.0-rc6)") + end + + it "raises an error if engine is used but engine version is not" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + ruby "1.8.7", :engine => 'rbx' + + gem "foo" + G + + bundle "platform", raise_on_error: false + + expect(exitstatus).not_to eq(0) + end + + it "raises an error if engine_version is used but engine is not" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + ruby "1.8.7", :engine_version => '1.2.4' + + gem "foo" + G + + bundle "platform", raise_on_error: false + + expect(exitstatus).not_to eq(0) + end + + it "raises an error if engine version doesn't match ruby version for MRI" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + ruby "1.8.7", :engine => 'ruby', :engine_version => '1.2.4' + + gem "foo" + G + + bundle "platform", raise_on_error: false + + expect(exitstatus).not_to eq(0) + end + + it "should print if no ruby version is specified" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + + gem "foo" + G + + bundle "platform --ruby" + + expect(out).to eq("No ruby version specified") + end + + it "handles when there is a locked requirement" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + ruby "< 1.8.7" + G + + lockfile <<-L + GEM + remote: #{file_uri_for(gem_repo1)}/ + specs: + + PLATFORMS + ruby + + DEPENDENCIES + + RUBY VERSION + ruby 1.0.0p127 + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "platform --ruby" + expect(out).to eq("ruby 1.0.0") + end + + it "handles when there is a lockfile with no requirement" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + G + + lockfile <<-L + GEM + remote: #{file_uri_for(gem_repo1)}/ + specs: + + PLATFORMS + ruby + + DEPENDENCIES + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "platform --ruby" + expect(out).to eq("No ruby version specified") + end + + it "handles when there is a requirement in the gemfile" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + ruby ">= 1.8.7" + G + + bundle "platform --ruby" + expect(out).to eq("ruby 1.8.7") + end + + it "handles when there are multiple requirements in the gemfile" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + ruby ">= 1.8.7", "< 2.0.0" + G + + bundle "platform --ruby" + expect(out).to eq("ruby 1.8.7") + end + end + + let(:ruby_version_correct) { "ruby \"#{Gem.ruby_version}\", :engine => \"#{local_ruby_engine}\", :engine_version => \"#{local_engine_version}\"" } + let(:ruby_version_correct_engineless) { "ruby \"#{Gem.ruby_version}\"" } + let(:ruby_version_correct_patchlevel) { "#{ruby_version_correct}, :patchlevel => '#{RUBY_PATCHLEVEL}'" } + let(:ruby_version_incorrect) { "ruby \"#{not_local_ruby_version}\", :engine => \"#{local_ruby_engine}\", :engine_version => \"#{not_local_ruby_version}\"" } + let(:engine_incorrect) { "ruby \"#{Gem.ruby_version}\", :engine => \"#{not_local_tag}\", :engine_version => \"#{Gem.ruby_version}\"" } + let(:engine_version_incorrect) { "ruby \"#{Gem.ruby_version}\", :engine => \"#{local_ruby_engine}\", :engine_version => \"#{not_local_engine_version}\"" } + let(:patchlevel_incorrect) { "#{ruby_version_correct}, :patchlevel => '#{not_local_patchlevel}'" } + let(:patchlevel_fixnum) { "#{ruby_version_correct}, :patchlevel => #{RUBY_PATCHLEVEL}1" } + + def should_be_ruby_version_incorrect + expect(exitstatus).to eq(18) + expect(err).to be_include("Your Ruby version is #{Gem.ruby_version}, but your Gemfile specified #{not_local_ruby_version}") + end + + def should_be_engine_incorrect + expect(exitstatus).to eq(18) + expect(err).to be_include("Your Ruby engine is #{local_ruby_engine}, but your Gemfile specified #{not_local_tag}") + end + + def should_be_engine_version_incorrect + expect(exitstatus).to eq(18) + expect(err).to be_include("Your #{local_ruby_engine} version is #{local_engine_version}, but your Gemfile specified #{local_ruby_engine} #{not_local_engine_version}") + end + + def should_be_patchlevel_incorrect + expect(exitstatus).to eq(18) + expect(err).to be_include("Your Ruby patchlevel is #{RUBY_PATCHLEVEL}, but your Gemfile specified #{not_local_patchlevel}") + end + + def should_be_patchlevel_fixnum + expect(exitstatus).to eq(18) + expect(err).to be_include("The Ruby patchlevel in your Gemfile must be a string") + end + + context "bundle install" do + it "installs fine when the ruby version matches" do + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack" + + #{ruby_version_correct} + G + + expect(bundled_app_lock).to exist + end + + it "installs fine with any engine", :jruby_only do + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack" + + #{ruby_version_correct_engineless} + G + + expect(bundled_app_lock).to exist + end + + it "installs fine when the patchlevel matches" do + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack" + + #{ruby_version_correct_patchlevel} + G + + expect(bundled_app_lock).to exist + end + + it "doesn't install when the ruby version doesn't match" do + install_gemfile <<-G, raise_on_error: false + source "#{file_uri_for(gem_repo1)}" + gem "rack" + + #{ruby_version_incorrect} + G + + expect(bundled_app_lock).not_to exist + should_be_ruby_version_incorrect + end + + it "doesn't install when engine doesn't match" do + install_gemfile <<-G, raise_on_error: false + source "#{file_uri_for(gem_repo1)}" + gem "rack" + + #{engine_incorrect} + G + + expect(bundled_app_lock).not_to exist + should_be_engine_incorrect + end + + it "doesn't install when engine version doesn't match", :jruby_only do + install_gemfile <<-G, raise_on_error: false + source "#{file_uri_for(gem_repo1)}" + gem "rack" + + #{engine_version_incorrect} + G + + expect(bundled_app_lock).not_to exist + should_be_engine_version_incorrect + end + + it "doesn't install when patchlevel doesn't match" do + install_gemfile <<-G, raise_on_error: false + source "#{file_uri_for(gem_repo1)}" + gem "rack" + + #{patchlevel_incorrect} + G + + expect(bundled_app_lock).not_to exist + should_be_patchlevel_incorrect + end + end + + context "bundle check" do + it "checks fine when the ruby version matches" do + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack" + G + + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack" + + #{ruby_version_correct} + G + + bundle :check + expect(out).to match(/\AResolving dependencies\.\.\.\.*\nThe Gemfile's dependencies are satisfied\z/) + end + + it "checks fine with any engine", :jruby_only do + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack" + G + + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack" + + #{ruby_version_correct_engineless} + G + + bundle :check + expect(out).to match(/\AResolving dependencies\.\.\.\.*\nThe Gemfile's dependencies are satisfied\z/) + end + + it "fails when ruby version doesn't match" do + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack" + G + + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack" + + #{ruby_version_incorrect} + G + + bundle :check, raise_on_error: false + should_be_ruby_version_incorrect + end + + it "fails when engine doesn't match" do + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack" + G + + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack" + + #{engine_incorrect} + G + + bundle :check, raise_on_error: false + should_be_engine_incorrect + end + + it "fails when engine version doesn't match", :jruby_only do + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack" + G + + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack" + + #{engine_version_incorrect} + G + + bundle :check, raise_on_error: false + should_be_engine_version_incorrect + end + + it "fails when patchlevel doesn't match" do + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack" + G + + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack" + + #{patchlevel_incorrect} + G + + bundle :check, raise_on_error: false + should_be_patchlevel_incorrect + end + end + + context "bundle update" do + before do + build_repo2 + + install_gemfile <<-G + source "#{file_uri_for(gem_repo2)}" + gem "activesupport" + gem "rack-obama" + G + end + + it "updates successfully when the ruby version matches" do + gemfile <<-G + source "#{file_uri_for(gem_repo2)}" + gem "activesupport" + gem "rack-obama" + + #{ruby_version_correct} + G + update_repo2 do + build_gem "rack", "1.2" do |s| + s.executables = "rackup" + end + + build_gem "activesupport", "3.0" + end + + bundle "update", all: true + expect(the_bundle).to include_gems "rack 1.2", "rack-obama 1.0", "activesupport 3.0" + end + + it "updates fine with any engine", :jruby_only do + gemfile <<-G + source "#{file_uri_for(gem_repo2)}" + gem "activesupport" + gem "rack-obama" + + #{ruby_version_correct_engineless} + G + update_repo2 do + build_gem "rack", "1.2" do |s| + s.executables = "rackup" + end + + build_gem "activesupport", "3.0" + end + + bundle "update", all: true + expect(the_bundle).to include_gems "rack 1.2", "rack-obama 1.0", "activesupport 3.0" + end + + it "fails when ruby version doesn't match" do + gemfile <<-G + source "#{file_uri_for(gem_repo2)}" + gem "activesupport" + gem "rack-obama" + + #{ruby_version_incorrect} + G + update_repo2 do + build_gem "activesupport", "3.0" + end + + bundle :update, all: true, raise_on_error: false + should_be_ruby_version_incorrect + end + + it "fails when ruby engine doesn't match", :jruby_only do + gemfile <<-G + source "#{file_uri_for(gem_repo2)}" + gem "activesupport" + gem "rack-obama" + + #{engine_incorrect} + G + update_repo2 do + build_gem "activesupport", "3.0" + end + + bundle :update, all: true, raise_on_error: false + should_be_engine_incorrect + end + + it "fails when ruby engine version doesn't match", :jruby_only do + gemfile <<-G + source "#{file_uri_for(gem_repo2)}" + gem "activesupport" + gem "rack-obama" + + #{engine_version_incorrect} + G + update_repo2 do + build_gem "activesupport", "3.0" + end + + bundle :update, all: true, raise_on_error: false + should_be_engine_version_incorrect + end + + it "fails when patchlevel doesn't match" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack" + + #{patchlevel_incorrect} + G + update_repo2 do + build_gem "activesupport", "3.0" + end + + bundle :update, all: true, raise_on_error: false + should_be_patchlevel_incorrect + end + end + + context "bundle info" do + before do + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rails" + G + end + + it "prints path if ruby version is correct" do + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rails" + + #{ruby_version_correct} + G + + bundle "info rails --path" + expect(out).to eq(default_bundle_path("gems", "rails-2.3.2").to_s) + end + + it "prints path if ruby version is correct for any engine", :jruby_only do + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rails" + + #{ruby_version_correct_engineless} + G + + bundle "info rails --path" + expect(out).to eq(default_bundle_path("gems", "rails-2.3.2").to_s) + end + + it "fails if ruby version doesn't match", bundler: "< 3" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rails" + + #{ruby_version_incorrect} + G + + bundle "show rails", raise_on_error: false + should_be_ruby_version_incorrect + end + + it "fails if engine doesn't match", bundler: "< 3" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rails" + + #{engine_incorrect} + G + + bundle "show rails", raise_on_error: false + should_be_engine_incorrect + end + + it "fails if engine version doesn't match", bundler: "< 3", jruby_only: true do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rails" + + #{engine_version_incorrect} + G + + bundle "show rails", raise_on_error: false + should_be_engine_version_incorrect + end + + it "fails when patchlevel doesn't match", bundler: "< 3" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack" + + #{patchlevel_incorrect} + G + update_repo2 do + build_gem "activesupport", "3.0" + end + + bundle "show rails", raise_on_error: false + should_be_patchlevel_incorrect + end + end + + context "bundle cache" do + before do + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem 'rack' + G + end + + it "copies the .gem file to vendor/cache when ruby version matches" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem 'rack' + + #{ruby_version_correct} + G + + bundle :cache + expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist + end + + it "copies the .gem file to vendor/cache when ruby version matches for any engine", :jruby_only do + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem 'rack' + + #{ruby_version_correct_engineless} + G + + bundle :cache + expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist + end + + it "fails if the ruby version doesn't match" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem 'rack' + + #{ruby_version_incorrect} + G + + bundle :cache, raise_on_error: false + should_be_ruby_version_incorrect + end + + it "fails if the engine doesn't match" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem 'rack' + + #{engine_incorrect} + G + + bundle :cache, raise_on_error: false + should_be_engine_incorrect + end + + it "fails if the engine version doesn't match", :jruby_only do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem 'rack' + + #{engine_version_incorrect} + G + + bundle :cache, raise_on_error: false + should_be_engine_version_incorrect + end + + it "fails when patchlevel doesn't match" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack" + + #{patchlevel_incorrect} + G + + bundle :cache, raise_on_error: false + should_be_patchlevel_incorrect + end + end + + context "bundle pack" do + before do + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem 'rack' + G + end + + it "copies the .gem file to vendor/cache when ruby version matches" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem 'rack' + + #{ruby_version_correct} + G + + bundle :cache + expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist + end + + it "copies the .gem file to vendor/cache when ruby version matches any engine", :jruby_only do + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem 'rack' + + #{ruby_version_correct_engineless} + G + + bundle :cache + expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist + end + + it "fails if the ruby version doesn't match" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem 'rack' + + #{ruby_version_incorrect} + G + + bundle :cache, raise_on_error: false + should_be_ruby_version_incorrect + end + + it "fails if the engine doesn't match" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem 'rack' + + #{engine_incorrect} + G + + bundle :cache, raise_on_error: false + should_be_engine_incorrect + end + + it "fails if the engine version doesn't match", :jruby_only do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem 'rack' + + #{engine_version_incorrect} + G + + bundle :cache, raise_on_error: false + should_be_engine_version_incorrect + end + + it "fails when patchlevel doesn't match" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack" + + #{patchlevel_incorrect} + G + + bundle :cache, raise_on_error: false + should_be_patchlevel_incorrect + end + end + + context "bundle exec" do + before do + ENV["BUNDLER_FORCE_TTY"] = "true" + system_gems "rack-1.0.0", "rack-0.9.1", path: default_bundle_path + end + + it "activates the correct gem when ruby version matches" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack", "0.9.1" + + #{ruby_version_correct} + G + + bundle "exec rackup" + expect(out).to include("0.9.1") + end + + it "activates the correct gem when ruby version matches any engine", :jruby_only do + system_gems "rack-1.0.0", "rack-0.9.1", path: default_bundle_path + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack", "0.9.1" + + #{ruby_version_correct_engineless} + G + + bundle "exec rackup" + expect(out).to include("0.9.1") + end + + it "fails when the ruby version doesn't match" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack", "0.9.1" + + #{ruby_version_incorrect} + G + + bundle "exec rackup", raise_on_error: false + should_be_ruby_version_incorrect + end + + it "fails when the engine doesn't match" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack", "0.9.1" + + #{engine_incorrect} + G + + bundle "exec rackup", raise_on_error: false + should_be_engine_incorrect + end + + # it "fails when the engine version doesn't match", :jruby_only do + # gemfile <<-G + # gem "rack", "0.9.1" + # + # #{engine_version_incorrect} + # G + # + # bundle "exec rackup" + # should_be_engine_version_incorrect + # end + + it "fails when patchlevel doesn't match" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack" + + #{patchlevel_incorrect} + G + + bundle "exec rackup", raise_on_error: false + should_be_patchlevel_incorrect + end + end + + context "bundle console", bundler: "< 3" do + before do + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack" + gem "activesupport", :group => :test + gem "rack_middleware", :group => :development + G + end + + it "starts IRB with the default group loaded when ruby version matches", :readline do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack" + gem "activesupport", :group => :test + gem "rack_middleware", :group => :development + + #{ruby_version_correct} + G + + bundle "console" do |input, _, _| + input.puts("puts RACK") + input.puts("exit") + end + expect(out).to include("0.9.1") + end + + it "starts IRB with the default group loaded when ruby version matches", :readline, :jruby_only do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack" + gem "activesupport", :group => :test + gem "rack_middleware", :group => :development + + #{ruby_version_correct_engineless} + G + + bundle "console" do |input, _, _| + input.puts("puts RACK") + input.puts("exit") + end + expect(out).to include("0.9.1") + end + + it "fails when ruby version doesn't match" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack" + gem "activesupport", :group => :test + gem "rack_middleware", :group => :development + + #{ruby_version_incorrect} + G + + bundle "console", raise_on_error: false + should_be_ruby_version_incorrect + end + + it "fails when engine doesn't match" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack" + gem "activesupport", :group => :test + gem "rack_middleware", :group => :development + + #{engine_incorrect} + G + + bundle "console", raise_on_error: false + should_be_engine_incorrect + end + + it "fails when engine version doesn't match", :jruby_only do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack" + gem "activesupport", :group => :test + gem "rack_middleware", :group => :development + + #{engine_version_incorrect} + G + + bundle "console", raise_on_error: false + should_be_engine_version_incorrect + end + + it "fails when patchlevel doesn't match" do + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "rack" + gem "activesupport", :group => :test + gem "rack_middleware", :group => :development + + #{patchlevel_incorrect} + G + + bundle "console", raise_on_error: false + should_be_patchlevel_incorrect + end + end + + context "Bundler.setup" do + before do + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "yard" + gem "rack", :group => :test + G + + ENV["BUNDLER_FORCE_TTY"] = "true" + end + + it "makes a Gemfile.lock if setup succeeds" do + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "yard" + gem "rack" + + #{ruby_version_correct} + G + + FileUtils.rm(bundled_app_lock) + + run "1" + expect(bundled_app_lock).to exist + end + + it "makes a Gemfile.lock if setup succeeds for any engine", :jruby_only do + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem "yard" + gem "rack" + + #{ruby_version_correct_engineless} + G + + FileUtils.rm(bundled_app_lock) + + run "1" + expect(bundled_app_lock).to exist + end + + it "fails when ruby version doesn't match" do + install_gemfile <<-G, raise_on_error: false + source "#{file_uri_for(gem_repo1)}" + gem "yard" + gem "rack" + + #{ruby_version_incorrect} + G + + FileUtils.rm(bundled_app_lock) + + ruby "require 'bundler/setup'", env: { "BUNDLER_VERSION" => Bundler::VERSION }, raise_on_error: false + + expect(bundled_app_lock).not_to exist + should_be_ruby_version_incorrect + end + + it "fails when engine doesn't match" do + install_gemfile <<-G, raise_on_error: false + source "#{file_uri_for(gem_repo1)}" + gem "yard" + gem "rack" + + #{engine_incorrect} + G + + FileUtils.rm(bundled_app_lock) + + ruby "require 'bundler/setup'", env: { "BUNDLER_VERSION" => Bundler::VERSION }, raise_on_error: false + + expect(bundled_app_lock).not_to exist + should_be_engine_incorrect + end + + it "fails when engine version doesn't match", :jruby_only do + install_gemfile <<-G, raise_on_error: false + source "#{file_uri_for(gem_repo1)}" + gem "yard" + gem "rack" + + #{engine_version_incorrect} + G + + FileUtils.rm(bundled_app_lock) + + ruby "require 'bundler/setup'", env: { "BUNDLER_VERSION" => Bundler::VERSION }, raise_on_error: false + + expect(bundled_app_lock).not_to exist + should_be_engine_version_incorrect + end + + it "fails when patchlevel doesn't match" do + install_gemfile <<-G, raise_on_error: false + source "#{file_uri_for(gem_repo1)}" + gem "yard" + gem "rack" + + #{patchlevel_incorrect} + G + + FileUtils.rm(bundled_app_lock) + + ruby "require 'bundler/setup'", env: { "BUNDLER_VERSION" => Bundler::VERSION }, raise_on_error: false + + expect(bundled_app_lock).not_to exist + should_be_patchlevel_incorrect + end + end + + context "bundle outdated" do + before do + build_repo2 do + build_git "foo", path: lib_path("foo") + end + + install_gemfile <<-G + source "#{file_uri_for(gem_repo2)}" + gem "activesupport", "2.3.5" + gem "foo", :git => "#{lib_path("foo")}" + G + end + + it "returns list of outdated gems when the ruby version matches" do + update_repo2 do + build_gem "activesupport", "3.0" + update_git "foo", path: lib_path("foo") + end + + gemfile <<-G + source "#{file_uri_for(gem_repo2)}" + gem "activesupport", "2.3.5" + gem "foo", :git => "#{lib_path("foo")}" + + #{ruby_version_correct} + G + + bundle "outdated", raise_on_error: false + + expected_output = <<~TABLE.gsub("x", "\\\h").tr(".", "\.").strip + Gem Current Latest Requested Groups + activesupport 2.3.5 3.0 = 2.3.5 default + foo 1.0 xxxxxxx 1.0 xxxxxxx >= 0 default + TABLE + + expect(out).to match(Regexp.new(expected_output)) + end + + it "returns list of outdated gems when the ruby version matches for any engine", :jruby_only do + bundle :install + update_repo2 do + build_gem "activesupport", "3.0" + update_git "foo", path: lib_path("foo") + end + + gemfile <<-G + source "#{file_uri_for(gem_repo2)}" + gem "activesupport", "2.3.5" + gem "foo", :git => "#{lib_path("foo")}" + + #{ruby_version_correct_engineless} + G + + bundle "outdated", raise_on_error: false + + expected_output = <<~TABLE.gsub("x", "\\\h").tr(".", "\.").strip + Gem Current Latest Requested Groups + activesupport 2.3.5 3.0 = 2.3.5 default + foo 1.0 xxxxxxx 1.0 xxxxxxx >= 0 default + TABLE + + expect(out).to match(Regexp.new(expected_output)) + end + + it "fails when the ruby version doesn't match" do + update_repo2 do + build_gem "activesupport", "3.0" + update_git "foo", path: lib_path("foo") + end + + gemfile <<-G + source "#{file_uri_for(gem_repo2)}" + gem "activesupport", "2.3.5" + gem "foo", :git => "#{lib_path("foo")}" + + #{ruby_version_incorrect} + G + + bundle "outdated", raise_on_error: false + should_be_ruby_version_incorrect + end + + it "fails when the engine doesn't match" do + update_repo2 do + build_gem "activesupport", "3.0" + update_git "foo", path: lib_path("foo") + end + + gemfile <<-G + source "#{file_uri_for(gem_repo2)}" + gem "activesupport", "2.3.5" + gem "foo", :git => "#{lib_path("foo")}" + + #{engine_incorrect} + G + + bundle "outdated", raise_on_error: false + should_be_engine_incorrect + end + + it "fails when the engine version doesn't match", :jruby_only do + update_repo2 do + build_gem "activesupport", "3.0" + update_git "foo", path: lib_path("foo") + end + + gemfile <<-G + source "#{file_uri_for(gem_repo2)}" + gem "activesupport", "2.3.5" + gem "foo", :git => "#{lib_path("foo")}" + + #{engine_version_incorrect} + G + + bundle "outdated", raise_on_error: false + should_be_engine_version_incorrect + end + + it "fails when the patchlevel doesn't match", :jruby_only do + update_repo2 do + build_gem "activesupport", "3.0" + update_git "foo", path: lib_path("foo") + end + + gemfile <<-G + source "#{file_uri_for(gem_repo2)}" + gem "activesupport", "2.3.5" + gem "foo", :git => "#{lib_path("foo")}" + + #{patchlevel_incorrect} + G + + bundle "outdated", raise_on_error: false + should_be_patchlevel_incorrect + end + + it "fails when the patchlevel is a fixnum", :jruby_only do + update_repo2 do + build_gem "activesupport", "3.0" + update_git "foo", path: lib_path("foo") + end + + gemfile <<-G + source "#{file_uri_for(gem_repo2)}" + gem "activesupport", "2.3.5" + gem "foo", :git => "#{lib_path("foo")}" + + #{patchlevel_fixnum} + G + + bundle "outdated", raise_on_error: false + should_be_patchlevel_fixnum + end + end +end diff --git a/spec/bundler/commands/post_bundle_message_spec.rb b/spec/bundler/commands/post_bundle_message_spec.rb index 3050b87754..07fd5a79e9 100644 --- a/spec/bundler/commands/post_bundle_message_spec.rb +++ b/spec/bundler/commands/post_bundle_message_spec.rb @@ -114,13 +114,13 @@ RSpec.describe "post bundle message" do end it "should report a helpful error message" do - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G, raise_on_error: false source "#{file_uri_for(gem_repo1)}" gem "rack" gem "not-a-gem", :group => :development G expect(err).to include <<-EOS.strip -Could not find gem 'not-a-gem' in rubygems repository #{file_uri_for(gem_repo1)}/ or installed locally. +Could not find gem 'not-a-gem' in rubygems repository #{file_uri_for(gem_repo1)}/, cached gems or installed locally. EOS end @@ -131,7 +131,7 @@ Could not find gem 'not-a-gem' in rubygems repository #{file_uri_for(gem_repo1)} G bundle :cache expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G, raise_on_error: false source "#{file_uri_for(gem_repo1)}" gem "rack" gem "not-a-gem", :group => :development @@ -142,7 +142,7 @@ Could not find gem 'not-a-gem' in rubygems repository #{file_uri_for(gem_repo1)} end end - describe "for second bundle install run", :bundler => "< 3" do + describe "for second bundle install run", bundler: "< 3" do it "without any options" do 2.times { bundle :install } expect(out).to include(bundle_show_message) @@ -179,25 +179,25 @@ Could not find gem 'not-a-gem' in rubygems repository #{file_uri_for(gem_repo1)} describe "for bundle update" do it "shows proper messages according to the configured groups" do - bundle :update, :all => true + bundle :update, all: true expect(out).not_to include("Gems in the groups") expect(out).to include(bundle_updated_message) bundle "config set --local without emo" bundle :install - bundle :update, :all => true + bundle :update, all: true expect(out).to include("Gems in the group 'emo' were not updated") expect(out).to include(bundle_updated_message) bundle "config set --local without emo test" bundle :install - bundle :update, :all => true + bundle :update, all: true expect(out).to include("Gems in the groups 'emo' and 'test' were not updated") expect(out).to include(bundle_updated_message) bundle "config set --local without emo obama test" bundle :install - bundle :update, :all => true + bundle :update, all: true expect(out).to include("Gems in the groups 'emo', 'obama' and 'test' were not updated") expect(out).to include(bundle_updated_message) end diff --git a/spec/bundler/commands/pristine_spec.rb b/spec/bundler/commands/pristine_spec.rb index 2f730bd4e2..1aec37f850 100644 --- a/spec/bundler/commands/pristine_spec.rb +++ b/spec/bundler/commands/pristine_spec.rb @@ -2,9 +2,9 @@ require "bundler/vendored_fileutils" -RSpec.describe "bundle pristine", :ruby_repo do +RSpec.describe "bundle pristine" do before :each do - build_lib "baz", :path => bundled_app do |s| + build_lib "baz", path: bundled_app do |s| s.version = "1.0.0" s.add_development_dependency "baz-dev", "=1.0.0" end @@ -13,16 +13,16 @@ RSpec.describe "bundle pristine", :ruby_repo do build_gem "weakling" build_gem "baz-dev", "1.0.0" build_gem "very_simple_binary", &:add_c_extension - build_git "foo", :path => lib_path("foo") - build_git "git_with_ext", :path => lib_path("git_with_ext"), &:add_c_extension - build_lib "bar", :path => lib_path("bar") + build_git "foo", path: lib_path("foo") + build_git "git_with_ext", path: lib_path("git_with_ext"), &:add_c_extension + build_lib "bar", path: lib_path("bar") end install_gemfile <<-G source "#{file_uri_for(gem_repo2)}" gem "weakling" gem "very_simple_binary" - gem "foo", :git => "#{lib_path("foo")}", :branch => "master" + gem "foo", :git => "#{lib_path("foo")}", :branch => "main" gem "git_with_ext", :git => "#{lib_path("git_with_ext")}" gem "bar", :path => "#{lib_path("bar")}" @@ -164,7 +164,7 @@ RSpec.describe "bundle pristine", :ruby_repo do end it "raises when one of them is not in the lockfile" do - bundle "pristine abcabcabc", :raise_on_error => false + bundle "pristine abcabcabc", raise_on_error: false expect(err).to include("Could not find gem 'abcabcabc'.") end end @@ -181,8 +181,8 @@ RSpec.describe "bundle pristine", :ruby_repo do bundle "pristine" makefile_contents = File.read(c_ext_dir.join("Makefile").to_s) - expect(makefile_contents).to match(/libpath =.*#{c_ext_dir}/) - expect(makefile_contents).to match(/LIBPATH =.*-L#{c_ext_dir}/) + expect(makefile_contents).to match(/libpath =.*#{Regexp.escape(c_ext_dir.to_s)}/) + expect(makefile_contents).to match(/LIBPATH =.*-L#{Regexp.escape(c_ext_dir.to_s)}/) end end @@ -198,14 +198,14 @@ RSpec.describe "bundle pristine", :ruby_repo do bundle "pristine" makefile_contents = File.read(c_ext_dir.join("Makefile").to_s) - expect(makefile_contents).to match(/libpath =.*#{c_ext_dir}/) - expect(makefile_contents).to match(/LIBPATH =.*-L#{c_ext_dir}/) + expect(makefile_contents).to match(/libpath =.*#{Regexp.escape(c_ext_dir.to_s)}/) + expect(makefile_contents).to match(/LIBPATH =.*-L#{Regexp.escape(c_ext_dir.to_s)}/) end end context "when BUNDLE_GEMFILE doesn't exist" do before do - bundle "pristine", :env => { "BUNDLE_GEMFILE" => "does/not/exist" }, :raise_on_error => false + bundle "pristine", env: { "BUNDLE_GEMFILE" => "does/not/exist" }, raise_on_error: false end it "shows a meaningful error" do diff --git a/spec/bundler/commands/remove_spec.rb b/spec/bundler/commands/remove_spec.rb index 70dc09c9b6..197fcde091 100644 --- a/spec/bundler/commands/remove_spec.rb +++ b/spec/bundler/commands/remove_spec.rb @@ -7,13 +7,43 @@ RSpec.describe "bundle remove" do source "#{file_uri_for(gem_repo1)}" G - bundle "remove", :raise_on_error => false + bundle "remove", raise_on_error: false expect(err).to include("Please specify gems to remove.") end end - context "when --install flag is specified", :bundler => "< 3" do + context "after 'bundle install' is run" do + describe "running 'bundle remove GEM_NAME'" do + it "removes it from the lockfile" do + rack_dep = <<~L + + DEPENDENCIES + rack + + L + + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + + gem "rack" + G + + bundle "install" + + expect(lockfile).to include(rack_dep) + + bundle "remove rack" + + expect(gemfile).to eq <<~G + source "#{file_uri_for(gem_repo1)}" + G + expect(lockfile).to_not include(rack_dep) + end + end + end + + context "when --install flag is specified", bundler: "< 3" do it "removes gems from .bundle" do gemfile <<-G source "#{file_uri_for(gem_repo1)}" @@ -48,13 +78,15 @@ RSpec.describe "bundle remove" do context "when gem is specified in multiple lines" do it "shows success for removed gem" do + build_git "rack" + gemfile <<-G source '#{file_uri_for(gem_repo1)}' gem 'git' gem 'rack', - git: 'https://github.com/rack/rack', - branch: 'master' + git: "#{lib_path("rack-1.0")}", + branch: 'main' gem 'nokogiri' G @@ -77,7 +109,7 @@ RSpec.describe "bundle remove" do source "#{file_uri_for(gem_repo1)}" G - bundle "remove rack", :raise_on_error => false + bundle "remove rack", raise_on_error: false expect(err).to include("`rack` is not specified in #{bundled_app_gemfile} so it could not be removed.") end @@ -114,7 +146,7 @@ RSpec.describe "bundle remove" do gem "rspec" G - bundle "remove rails rack minitest", :raise_on_error => false + bundle "remove rails rack minitest", raise_on_error: false expect(err).to include("`rack` is not specified in #{bundled_app_gemfile} so it could not be removed.") expect(gemfile).to eq <<~G @@ -341,7 +373,7 @@ RSpec.describe "bundle remove" do gem "rack"; gem "rails" G - bundle "remove rails", :raise_on_error => false + bundle "remove rails", raise_on_error: false expect(err).to include("Gems could not be removed. rack (>= 0) would also have been removed.") expect(gemfile).to eq <<~G @@ -353,7 +385,7 @@ RSpec.describe "bundle remove" do context "when some gems could not be removed" do it "shows warning for gems not removed and success for those removed" do - install_gemfile <<-G, :raise_on_error => false + install_gemfile <<-G, raise_on_error: false source "#{file_uri_for(gem_repo1)}" gem"rack" gem"rspec" @@ -459,7 +491,7 @@ RSpec.describe "bundle remove" do eval_gemfile "Gemfile-other" G - bundle "remove rack", :raise_on_error => false + bundle "remove rack", raise_on_error: false expect(err).to include("`rack` is not specified in #{bundled_app_gemfile} so it could not be removed.") end @@ -478,7 +510,7 @@ RSpec.describe "bundle remove" do gem "rack" G - bundle "remove rack", :raise_on_error => false + bundle "remove rack", raise_on_error: false expect(out).to include("rack was removed.") expect(err).to include("`rack` is not specified in #{bundled_app("Gemfile-other")} so it could not be removed.") @@ -490,7 +522,7 @@ RSpec.describe "bundle remove" do end end - context "when gems can not be removed from other gemfile" do + context "when gems cannot be removed from other gemfile" do it "shows error" do create_file "Gemfile-other", <<-G gem "rails"; gem "rack" @@ -503,7 +535,7 @@ RSpec.describe "bundle remove" do gem "rack" G - bundle "remove rack", :raise_on_error => false + bundle "remove rack", raise_on_error: false expect(out).to include("rack was removed.") expect(err).to include("Gems could not be removed. rails (>= 0) would also have been removed.") @@ -528,7 +560,7 @@ RSpec.describe "bundle remove" do gem "rails"; gem "rack" G - bundle "remove rack", :raise_on_error => false + bundle "remove rack", raise_on_error: false expect(err).to include("Gems could not be removed. rails (>= 0) would also have been removed.") expect(bundled_app("Gemfile-other").read).to include("gem \"rack\"") @@ -542,7 +574,7 @@ RSpec.describe "bundle remove" do end context "when gem present in gemfiles but could not be removed from one from one of them" do - it "removes gem which can be removed and shows warning for file from which it can not be removed" do + it "removes gem which can be removed and shows warning for file from which it cannot be removed" do create_file "Gemfile-other", <<-G gem "rack" G @@ -602,7 +634,7 @@ RSpec.describe "bundle remove" do context "with gemspec" do it "should not remove the gem" do - build_lib("foo", :path => tmp.join("foo")) do |s| + build_lib("foo", path: tmp.join("foo")) do |s| s.write("foo.gemspec", "") s.add_dependency "rack" end diff --git a/spec/bundler/commands/show_spec.rb b/spec/bundler/commands/show_spec.rb index 2adb121616..2b6d4d2d00 100644 --- a/spec/bundler/commands/show_spec.rb +++ b/spec/bundler/commands/show_spec.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -RSpec.describe "bundle show", :bundler => "< 3" do +RSpec.describe "bundle show", bundler: "< 3" do context "with a standard Gemfile" do before :each do install_gemfile <<-G @@ -50,14 +50,14 @@ RSpec.describe "bundle show", :bundler => "< 3" do end it "complains if gem not in bundle" do - bundle "show missing", :raise_on_error => false + bundle "show missing", raise_on_error: false expect(err).to match(/could not find gem 'missing'/i) end it "prints path of all gems in bundle sorted by name" do bundle "show --paths" - expect(out).to include(default_bundle_path("gems", "rake-13.0.1").to_s) + expect(out).to include(default_bundle_path("gems", "rake-#{rake_version}").to_s) expect(out).to include(default_bundle_path("gems", "rails-2.3.2").to_s) # Gem names are the last component of their path. @@ -100,11 +100,11 @@ RSpec.describe "bundle show", :bundler => "< 3" do expect(the_bundle).to include_gems "foo 1.0" bundle :show - expect(out).to include("foo (1.0 #{@git.ref_for("master", 6)}") + expect(out).to include("foo (1.0 #{@git.ref_for("main", 6)}") end - it "prints out branch names other than master" do - update_git "foo", :branch => "omg" do |s| + it "prints out branch names other than main" do + update_git "foo", branch: "omg" do |s| s.write "lib/foo.rb", "FOO = '1.0.omg'" end @revision = revision_for(lib_path("foo-1.0"))[0...6] @@ -129,7 +129,7 @@ RSpec.describe "bundle show", :bundler => "< 3" do end it "handles when a version is a '-' prerelease" do - @git = build_git("foo", "1.0.0-beta.1", :path => lib_path("foo")) + @git = build_git("foo", "1.0.0-beta.1", path: lib_path("foo")) install_gemfile <<-G gem "foo", "1.0.0-beta.1", :git => "#{lib_path("foo")}" G @@ -142,13 +142,13 @@ RSpec.describe "bundle show", :bundler => "< 3" do context "in a fresh gem in a blank git repo" do before :each do - build_git "foo", :path => lib_path("foo") + build_git "foo", path: lib_path("foo") File.open(lib_path("foo/Gemfile"), "w") {|f| f.puts "gemspec" } - sys_exec "rm -rf .git && git init", :dir => lib_path("foo") + sys_exec "rm -rf .git && git init", dir: lib_path("foo") end it "does not output git errors" do - bundle :show, :dir => lib_path("foo") + bundle :show, dir: lib_path("foo") expect(err_without_deprecations).to be_empty end end @@ -173,7 +173,7 @@ RSpec.describe "bundle show", :bundler => "< 3" do G bundle "show rac" - expect(out).to match(/\A1 : rack\n2 : rack-obama\n0 : - exit -(\n>)?\z/) + expect(out).to match(/\A1 : rack\n2 : rack-obama\n0 : - exit -(\n>.*)?\z/) end end @@ -186,7 +186,7 @@ RSpec.describe "bundle show", :bundler => "< 3" do invalid_regexp = "[]" - bundle "show #{invalid_regexp}", :raise_on_error => false + bundle "show #{invalid_regexp}", raise_on_error: false expect(err).to include("Could not find gem '#{invalid_regexp}'.") end end @@ -219,6 +219,6 @@ RSpec.describe "bundle show", :bundler => "< 3" do end end -RSpec.describe "bundle show", :bundler => "3" do +RSpec.describe "bundle show", bundler: "3" do pending "shows a friendly error about the command removal" end diff --git a/spec/bundler/commands/update_spec.rb b/spec/bundler/commands/update_spec.rb index 403a48a508..cfb86ebb54 100644 --- a/spec/bundler/commands/update_spec.rb +++ b/spec/bundler/commands/update_spec.rb @@ -34,7 +34,7 @@ RSpec.describe "bundle update" do gem "rack-obama" exit! G - bundle "update", :raise_on_error => false + bundle "update", raise_on_error: false expect(bundled_app_lock).to exist end end @@ -60,7 +60,7 @@ RSpec.describe "bundle update" do build_gem "activesupport", "3.0" end - bundle "update", :all => true + bundle "update", all: true expect(out).to include("Bundle updated!") expect(the_bundle).to include_gems "rack 1.2", "rack-obama 1.0", "activesupport 3.0" end @@ -74,7 +74,7 @@ RSpec.describe "bundle update" do gem "rack-obama" exit! G - bundle "update", :all => true, :raise_on_error => false + bundle "update", all: true, raise_on_error: false expect(bundled_app_lock).to exist end end @@ -86,7 +86,7 @@ RSpec.describe "bundle update" do gem "rack", "1.0" G - bundle "update --gemfile OmgFile", :all => true + bundle "update --gemfile OmgFile", all: true expect(bundled_app("OmgFile.lock")).to exist end @@ -97,13 +97,13 @@ RSpec.describe "bundle update" do it "errors when passed nothing" do install_gemfile "source \"#{file_uri_for(gem_repo1)}\"" - bundle :update, :raise_on_error => false + bundle :update, raise_on_error: false expect(err).to eq("To update everything, pass the `--all` flag.") end it "errors when passed --all and another option" do install_gemfile "source \"#{file_uri_for(gem_repo1)}\"" - bundle "update --all foo", :raise_on_error => false + bundle "update --all foo", raise_on_error: false expect(err).to eq("Cannot specify --all along with specific options.") end @@ -171,11 +171,11 @@ RSpec.describe "bundle update" do end it "should inform the user" do - bundle "update halting-problem-solver", :raise_on_error => false + bundle "update halting-problem-solver", raise_on_error: false expect(err).to include "Could not find gem 'halting-problem-solver'" end it "should suggest alternatives" do - bundle "update platformspecific", :raise_on_error => false + bundle "update platformspecific", raise_on_error: false expect(err).to include "Did you mean platform_specific?" end end @@ -236,7 +236,7 @@ RSpec.describe "bundle update" do end end - bundle "update", :all => true + bundle "update", all: true expect(the_bundle).to include_gems("slim 3.0.9", "slim-rails 3.1.3", "slim_lint 0.16.1") end @@ -275,6 +275,11 @@ RSpec.describe "bundle update" do gem "countries" G + checksums = checksums_section_when_existing do |c| + c.checksum(gem_repo4, "countries", "3.1.0") + c.checksum(gem_repo4, "country_select", "5.1.0") + end + lockfile <<~L GEM remote: #{file_uri_for(gem_repo4)}/ @@ -284,23 +289,145 @@ RSpec.describe "bundle update" do countries (~> 3.0) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES countries country_select - + #{checksums} BUNDLED WITH #{Bundler::VERSION} L previous_lockfile = lockfile - bundle "lock --update" + bundle "lock --update", env: { "DEBUG" => "1" }, verbose: true expect(lockfile).to eq(previous_lockfile) end + it "does not downgrade direct dependencies when run with --conservative" do + build_repo4 do + build_gem "oauth2", "2.0.6" do |s| + s.add_dependency "faraday", ">= 0.17.3", "< 3.0" + end + + build_gem "oauth2", "1.4.10" do |s| + s.add_dependency "faraday", ">= 0.17.3", "< 3.0" + s.add_dependency "multi_json", "~> 1.3" + end + + build_gem "faraday", "2.5.2" + + build_gem "multi_json", "1.15.0" + + build_gem "quickbooks-ruby", "1.0.19" do |s| + s.add_dependency "oauth2", "~> 1.4" + end + + build_gem "quickbooks-ruby", "0.1.9" do |s| + s.add_dependency "oauth2" + end + end + + gemfile <<-G + source "#{file_uri_for(gem_repo4)}" + + gem "oauth2" + gem "quickbooks-ruby" + G + + lockfile <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + faraday (2.5.2) + multi_json (1.15.0) + oauth2 (1.4.10) + faraday (>= 0.17.3, < 3.0) + multi_json (~> 1.3) + quickbooks-ruby (1.0.19) + oauth2 (~> 1.4) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + oauth2 + quickbooks-ruby + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "update --conservative --verbose" + + expect(out).not_to include("Installing quickbooks-ruby 0.1.9") + expect(out).to include("Installing quickbooks-ruby 1.0.19").and include("Installing oauth2 1.4.10") + end + + it "does not downgrade direct dependencies when using gemspec sources" do + create_file("rails.gemspec", <<-G) + Gem::Specification.new do |gem| + gem.name = "rails" + gem.version = "7.1.0.alpha" + gem.author = "DHH" + gem.summary = "Full-stack web application framework." + end + G + + build_repo4 do + build_gem "rake", "12.3.3" + build_gem "rake", "13.0.6" + + build_gem "sneakers", "2.11.0" do |s| + s.add_dependency "rake" + end + + build_gem "sneakers", "2.12.0" do |s| + s.add_dependency "rake", "~> 12.3" + end + end + + gemfile <<-G + source "#{file_uri_for(gem_repo4)}" + + gemspec + + gem "rake" + gem "sneakers" + G + + lockfile <<~L + PATH + remote: . + specs: + + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + rake (13.0.6) + sneakers (2.11.0) + rake + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + rake + sneakers + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "update --verbose" + + expect(out).not_to include("Installing sneakers 2.12.0") + expect(out).not_to include("Installing rake 12.3.3") + expect(out).to include("Installing sneakers 2.11.0").and include("Installing rake 13.0.6") + end + it "does not downgrade indirect dependencies unnecessarily" do build_repo4 do build_gem "a" do |s| @@ -312,7 +439,7 @@ RSpec.describe "bundle update" do build_gem "c", "2.0" end - install_gemfile <<-G, :verbose => true + install_gemfile <<-G, verbose: true source "#{file_uri_for(gem_repo4)}" gem "a" G @@ -325,7 +452,7 @@ RSpec.describe "bundle update" do end end - bundle "update", :all => true, :verbose => true + bundle "update", all: true, verbose: true expect(the_bundle).to include_gems("a 1.0", "b 1.0", "c 2.0") end @@ -383,6 +510,11 @@ RSpec.describe "bundle update" do original_lockfile = lockfile + checksums = checksums_section_when_existing do |c| + c.checksum gem_repo4, "activesupport", "6.0.4.1" + c.checksum gem_repo4, "tzinfo", "1.2.9" + end + expected_lockfile = <<~L GEM remote: #{file_uri_for(gem_repo4)}/ @@ -396,7 +528,7 @@ RSpec.describe "bundle update" do DEPENDENCIES activesupport (~> 6.0.0) - + #{checksums} BUNDLED WITH #{Bundler::VERSION} L @@ -405,6 +537,10 @@ RSpec.describe "bundle update" do expect(the_bundle).to include_gems("activesupport 6.0.4.1", "tzinfo 1.2.9") expect(lockfile).to eq(expected_lockfile) + # needed because regressing to versions already present on the system + # won't add a checksum + expected_lockfile = remove_checksums_from_lockfile(expected_lockfile) + lockfile original_lockfile bundle "update" expect(the_bundle).to include_gems("activesupport 6.0.4.1", "tzinfo 1.2.9") @@ -421,12 +557,36 @@ RSpec.describe "bundle update" do before do build_repo2 - install_gemfile <<-G + gemfile <<-G source "#{file_uri_for(gem_repo2)}" gem "activesupport" gem "rack-obama" gem "platform_specific" G + + lockfile <<~L + GEM + remote: #{file_uri_for(gem_repo2)}/ + specs: + activesupport (2.3.5) + platform_specific (1.0-#{local_platform}) + rack (1.0.0) + rack-obama (1.0) + rack + + PLATFORMS + #{local_platform} + + DEPENDENCIES + activesupport + platform_specific + rack-obama + + BUNDLED WITH + #{Bundler::VERSION} + L + + bundle "install" end it "doesn't hit repo2" do @@ -479,7 +639,7 @@ RSpec.describe "bundle update" do context "when there is a source with the same name as a gem in a group" do before do - build_git "foo", :path => lib_path("activesupport") + build_git "foo", path: lib_path("activesupport") install_gemfile <<-G source "#{file_uri_for(gem_repo2)}" gem "activesupport", :group => :development @@ -489,7 +649,7 @@ RSpec.describe "bundle update" do it "should not update the gems from that source" do update_repo2 { build_gem "activesupport", "3.0" } - update_git "foo", "2.0", :path => lib_path("activesupport") + update_git "foo", "2.0", path: lib_path("activesupport") bundle "update --group development" expect(the_bundle).to include_gems "activesupport 3.0" @@ -531,27 +691,33 @@ RSpec.describe "bundle update" do G end - it "should fail loudly", :bundler => "< 3" do + it "should fail loudly", bundler: "< 3" do bundle "install --deployment" - bundle "update", :all => true, :raise_on_error => false + bundle "update", all: true, raise_on_error: false expect(last_command).to be_failure - expect(err).to match(/You are trying to install in deployment mode after changing.your Gemfile/m) - expect(err).to match(/freeze \nby running `bundle config unset deployment`./m) + expect(err).to match(/Bundler is unlocking, but the lockfile can't be updated because frozen mode is set/) + expect(err).to match(/freeze by running `bundle config set frozen false`./) end - it "should suggest different command when frozen is set globally", :bundler => "< 3" do + it "should fail loudly when frozen is set globally" do bundle "config set --global frozen 1" - bundle "update", :all => true, :raise_on_error => false - expect(err).to match(/You are trying to install in deployment mode after changing.your Gemfile/m). - and match(/freeze \nby running `bundle config unset frozen`./m) + bundle "update", all: true, raise_on_error: false + expect(err).to match(/Bundler is unlocking, but the lockfile can't be updated because frozen mode is set/). + and match(/freeze by running `bundle config set frozen false`./) end - it "should suggest different command when frozen is set globally", :bundler => "3" do + it "should fail loudly when deployment is set globally" do bundle "config set --global deployment true" - bundle "update", :all => true, :raise_on_error => false - expect(err).to match(/You are trying to install in deployment mode after changing.your Gemfile/m). - and match(/freeze \nby running `bundle config unset deployment`./m) + bundle "update", all: true, raise_on_error: false + expect(err).to match(/Bundler is unlocking, but the lockfile can't be updated because frozen mode is set/). + and match(/freeze by running `bundle config set frozen false`./) + end + + it "should not suggest any command to unfreeze bundler if frozen is set through ENV" do + bundle "update", all: true, raise_on_error: false, env: { "BUNDLE_FROZEN" => "true" } + expect(err).to match(/Bundler is unlocking, but the lockfile can't be updated because frozen mode is set/) + expect(err).not_to match(/by running/) end end @@ -644,7 +810,7 @@ RSpec.describe "bundle update" do end end - it "shows the previous version of the gem when updated from rubygems source", :bundler => "< 3" do + it "shows the previous version of the gem when updated from rubygems source" do build_repo2 install_gemfile <<-G @@ -652,43 +818,39 @@ RSpec.describe "bundle update" do gem "activesupport" G - bundle "update", :all => true + bundle "update", all: true, verbose: true expect(out).to include("Using activesupport 2.3.5") update_repo2 do build_gem "activesupport", "3.0" end - bundle "update", :all => true + bundle "update", all: true expect(out).to include("Installing activesupport 3.0 (was 2.3.5)") end - context "with suppress_install_using_messages set" do - before { bundle "config set suppress_install_using_messages true" } - - it "only prints `Using` for versions that have changed" do - build_repo4 do - build_gem "bar" - build_gem "foo" - end - - install_gemfile <<-G - source "#{file_uri_for(gem_repo4)}" - gem "bar" - gem "foo" - G + it "only prints `Using` for versions that have changed" do + build_repo4 do + build_gem "bar" + build_gem "foo" + end - bundle "update", :all => true - expect(out).to match(/Resolving dependencies\.\.\.\.*\nBundle updated!/) + install_gemfile <<-G + source "#{file_uri_for(gem_repo4)}" + gem "bar" + gem "foo" + G - update_repo4 do - build_gem "foo", "2.0" - end + bundle "update", all: true + expect(out).to match(/Resolving dependencies\.\.\.\.*\nBundle updated!/) - bundle "update", :all => true - out.sub!("Removing foo (1.0)\n", "") - expect(out).to match(/Resolving dependencies\.\.\.\.*\nFetching foo 2\.0 \(was 1\.0\)\nInstalling foo 2\.0 \(was 1\.0\)\nBundle updated/) + update_repo4 do + build_gem "foo", "2.0" end + + bundle "update", all: true + out.sub!("Removing foo (1.0)\n", "") + expect(out).to match(/Resolving dependencies\.\.\.\.*\nFetching foo 2\.0 \(was 1\.0\)\nInstalling foo 2\.0 \(was 1\.0\)\nBundle updated/) end it "shows error message when Gemfile.lock is not preset and gem is specified" do @@ -697,12 +859,96 @@ RSpec.describe "bundle update" do gem "activesupport" G - bundle "update nonexisting", :raise_on_error => false + bundle "update nonexisting", raise_on_error: false expect(err).to include("This Bundle hasn't been installed yet. Run `bundle install` to update and install the bundled gems.") expect(exitstatus).to eq(22) end - context "with multiple, duplicated sources, with lockfile in old format", :bundler => "< 3" do + context "with multiple sources and caching enabled" do + before do + build_repo2 do + build_gem "rack", "1.0.0" + + build_gem "request_store", "1.0.0" do |s| + s.add_dependency "rack", "1.0.0" + end + end + + build_repo4 do + # set up repo with no gems + end + + gemfile <<~G + source "#{file_uri_for(gem_repo2)}" + + gem "request_store" + + source "#{file_uri_for(gem_repo4)}" do + end + G + + lockfile <<~L + GEM + remote: #{file_uri_for(gem_repo2)}/ + specs: + rack (1.0.0) + request_store (1.0.0) + rack (= 1.0.0) + + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + + PLATFORMS + #{local_platform} + + DEPENDENCIES + request_store + + BUNDLED WITH + #{Bundler::VERSION} + L + end + + it "works" do + bundle :install + bundle :cache + + update_repo2 do + build_gem "request_store", "1.1.0" do |s| + s.add_dependency "rack", "1.0.0" + end + end + + bundle "update request_store" + + expect(out).to include("Bundle updated!") + + expect(lockfile).to eq <<~L + GEM + remote: #{file_uri_for(gem_repo2)}/ + specs: + rack (1.0.0) + request_store (1.1.0) + rack (= 1.0.0) + + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + + PLATFORMS + #{local_platform} + + DEPENDENCIES + request_store + + BUNDLED WITH + #{Bundler::VERSION} + L + end + end + + context "with multiple, duplicated sources, with lockfile in old format", bundler: "< 3" do before do build_repo2 do build_gem "dotenv", "2.7.6" @@ -743,7 +989,7 @@ RSpec.describe "bundle update" do vcr (6.0.0) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES dotenv @@ -756,8 +1002,8 @@ RSpec.describe "bundle update" do end it "works" do - bundle :install, :artifice => "compact_index" - bundle "update oj", :artifice => "compact_index" + bundle :install, artifice: "compact_index" + bundle "update oj", artifice: "compact_index" expect(out).to include("Bundle updated!") expect(the_bundle).to include_gems "oj 3.11.5" @@ -893,7 +1139,7 @@ RSpec.describe "bundle update in more complicated situations" do end it "allows updating" do - bundle :update, :all => true + bundle :update, all: true expect(the_bundle).to include_gem "a 1.1" end @@ -949,7 +1195,7 @@ RSpec.describe "bundle update without a Gemfile.lock" do gem "rack", "1.0" G - bundle "update", :all => true + bundle "update", all: true expect(the_bundle).to include_gems "rack 1.0.0" end @@ -972,7 +1218,7 @@ RSpec.describe "bundle update when a gem depends on a newer version of bundler" end it "should explain that bundler conflicted and how to resolve the conflict" do - bundle "update", :all => true, :raise_on_error => false + bundle "update", all: true, raise_on_error: false expect(last_command.stdboth).not_to match(/in snapshot/i) expect(err).to match(/current Bundler version/i). and match(/Install the necessary version with `gem install bundler:#{Bundler::VERSION.succ}`/i) @@ -980,23 +1226,18 @@ RSpec.describe "bundle update when a gem depends on a newer version of bundler" end RSpec.describe "bundle update --ruby" do - before do - install_gemfile <<-G - ::RUBY_VERSION = '2.1.3' - ::RUBY_PATCHLEVEL = 100 - ruby '~> 2.1.0' - source "#{file_uri_for(gem_repo1)}" - G - end - context "when the Gemfile removes the ruby" do before do + install_gemfile <<-G + ruby '~> #{Gem.ruby_version}' + source "#{file_uri_for(gem_repo1)}" + G + gemfile <<-G - ::RUBY_VERSION = '2.1.4' - ::RUBY_PATCHLEVEL = 222 - source "#{file_uri_for(gem_repo1)}" + source "#{file_uri_for(gem_repo1)}" G end + it "removes the Ruby from the Gemfile.lock" do bundle "update --ruby" @@ -1018,60 +1259,85 @@ RSpec.describe "bundle update --ruby" do context "when the Gemfile specified an updated Ruby version" do before do + install_gemfile <<-G + ruby '~> #{Gem.ruby_version}' + source "#{file_uri_for(gem_repo1)}" + G + gemfile <<-G - ::RUBY_VERSION = '2.1.4' - ::RUBY_PATCHLEVEL = 222 - ruby '~> 2.1.0' - source "#{file_uri_for(gem_repo1)}" + ruby '~> #{current_ruby_minor}' + source "#{file_uri_for(gem_repo1)}" G end + it "updates the Gemfile.lock with the latest version" do bundle "update --ruby" expect(lockfile).to eq <<~L - GEM - remote: #{file_uri_for(gem_repo1)}/ - specs: + GEM + remote: #{file_uri_for(gem_repo1)}/ + specs: - PLATFORMS - #{lockfile_platforms} + PLATFORMS + #{lockfile_platforms} - DEPENDENCIES + DEPENDENCIES - RUBY VERSION - ruby 2.1.4p222 + RUBY VERSION + #{Bundler::RubyVersion.system} - BUNDLED WITH - #{Bundler::VERSION} + BUNDLED WITH + #{Bundler::VERSION} L end end context "when a different Ruby is being used than has been versioned" do before do + install_gemfile <<-G + ruby '~> #{Gem.ruby_version}' + source "#{file_uri_for(gem_repo1)}" + G + gemfile <<-G - ::RUBY_VERSION = '2.2.2' - ::RUBY_PATCHLEVEL = 505 ruby '~> 2.1.0' source "#{file_uri_for(gem_repo1)}" G end it "shows a helpful error message" do - bundle "update --ruby", :raise_on_error => false + bundle "update --ruby", raise_on_error: false - expect(err).to include("Your Ruby version is 2.2.2, but your Gemfile specified ~> 2.1.0") + expect(err).to include("Your Ruby version is #{Bundler::RubyVersion.system.gem_version}, but your Gemfile specified ~> 2.1.0") end end context "when updating Ruby version and Gemfile `ruby`" do before do + lockfile <<~L + GEM + remote: #{file_uri_for(gem_repo1)}/ + specs: + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + + CHECKSUMS + + RUBY VERSION + ruby 2.1.4p222 + + BUNDLED WITH + #{Bundler::VERSION} + L + gemfile <<-G - ::RUBY_VERSION = '1.8.3' - ::RUBY_PATCHLEVEL = 55 - ruby '~> 1.8.0' + ruby '~> #{Gem.ruby_version}' source "#{file_uri_for(gem_repo1)}" G end + it "updates the Gemfile.lock with the latest version" do bundle "update --ruby" @@ -1085,8 +1351,10 @@ RSpec.describe "bundle update --ruby" do DEPENDENCIES + CHECKSUMS + RUBY VERSION - ruby 1.8.3p55 + #{Bundler::RubyVersion.system} BUNDLED WITH #{Bundler::VERSION} @@ -1096,24 +1364,355 @@ RSpec.describe "bundle update --ruby" do end RSpec.describe "bundle update --bundler" do - it "updates the bundler version in the lockfile without re-resolving" do + it "updates the bundler version in the lockfile" do build_repo4 do build_gem "rack", "1.0" end + checksums = checksums_section_when_existing do |c| + c.checksum(gem_repo4, "rack", "1.0") + end + install_gemfile <<-G source "#{file_uri_for(gem_repo4)}" gem "rack" G - allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile) + expect(lockfile).to eq <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + rack (1.0) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + rack + #{checksums} + BUNDLED WITH + #{Bundler::VERSION} + L lockfile lockfile.sub(/(^\s*)#{Bundler::VERSION}($)/, '\11.0.0\2') - FileUtils.rm_r gem_repo4 + bundle :update, bundler: true, artifice: "compact_index", verbose: true + expect(out).to include("Using bundler #{Bundler::VERSION}") + + expect(lockfile).to eq <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + rack (1.0) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + rack + #{checksums} + BUNDLED WITH + #{Bundler::VERSION} + L - bundle :update, :bundler => true, :verbose => true expect(the_bundle).to include_gem "rack 1.0" + end + + it "updates the bundler version in the lockfile without re-resolving if the highest version is already installed" do + system_gems "bundler-2.3.9" + + build_repo4 do + build_gem "rack", "1.0" + end + + install_gemfile <<-G + source "#{file_uri_for(gem_repo4)}" + gem "rack" + G + lockfile lockfile.sub(/(^\s*)#{Bundler::VERSION}($)/, "2.3.9") + + checksums = checksums_section_when_existing do |c| + c.checksum(gem_repo4, "rack", "1.0") + end + + bundle :update, bundler: true, artifice: "compact_index", verbose: true + expect(out).to include("Using bundler #{Bundler::VERSION}") + + expect(lockfile).to eq <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + rack (1.0) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + rack + #{checksums} + BUNDLED WITH + #{Bundler::VERSION} + L + + expect(the_bundle).to include_gem "rack 1.0" + end + + it "updates the bundler version in the lockfile even if the latest version is not installed", :ruby_repo do + pristine_system_gems "bundler-2.3.9" + + build_repo4 do + build_gem "rack", "1.0" + + build_bundler "999.0.0" + end + + install_gemfile <<-G, artifice: nil, env: { "BUNDLER_IGNORE_DEFAULT_GEM" => "true" } + source "#{file_uri_for(gem_repo4)}" + gem "rack" + G + lockfile lockfile.sub(/(^\s*)#{Bundler::VERSION}($)/, "2.3.9") + + bundle :update, bundler: true, artifice: "compact_index", verbose: true, env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } + + # Only updates properly on modern RubyGems. + + if Gem.rubygems_version >= Gem::Version.new("3.3.0.dev") + expect(out).to include("Updating bundler to 999.0.0") + expect(out).to include("Using bundler 999.0.0") + expect(out).not_to include("Installing Bundler 2.3.9 and restarting using that version.") + + expect(lockfile).to eq <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + rack (1.0) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + rack + + BUNDLED WITH + 999.0.0 + L + + expect(the_bundle).to include_gems "bundler 999.0.0" + expect(the_bundle).to include_gems "rack 1.0" + else + # Old RubyGems versions do not trampoline but they still change BUNDLED + # WITH to the latest bundler version. This means the below check fails + # because it tries to use bundler 999.0.0 which did not get installed. + # Workaround the bug by forcing the version we know is installed. + expect(the_bundle).to include_gems "rack 1.0", env: { "BUNDLER_VERSION" => "2.3.9" } + end + end + + it "does not claim to update to Bundler version to a wrong version when cached gems are present" do + pristine_system_gems "bundler-2.99.0" + + build_repo4 do + build_gem "rack", "3.0.9.1" + + build_bundler "2.99.0" + end + + gemfile <<~G + source "#{file_uri_for(gem_repo4)}" + gem "rack" + G + + lockfile <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + rack (3.0.9.1) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + rack + + BUNDLED WITH + 2.99.0 + L + + bundle :cache, verbose: true + + bundle :update, bundler: true, artifice: "compact_index", verbose: true, env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s } + + expect(out).not_to include("Updating bundler to") + end + + it "does not update the bundler version in the lockfile if the latest version is not compatible with current ruby", :ruby_repo do + pristine_system_gems "bundler-2.3.9" + + build_repo4 do + build_gem "rack", "1.0" + + build_bundler "2.3.9" + build_bundler "999.0.0" do |s| + s.required_ruby_version = "> #{Gem.ruby_version}" + end + end + + install_gemfile <<-G, env: { "BUNDLER_IGNORE_DEFAULT_GEM" => "true" } + source "#{file_uri_for(gem_repo4)}" + gem "rack" + G + lockfile lockfile.sub(/(^\s*)#{Bundler::VERSION}($)/, "2.3.9") + + bundle :update, bundler: true, artifice: "compact_index", verbose: true, env: { "BUNDLER_SPEC_GEM_REPO" => gem_repo4.to_s, "BUNDLER_IGNORE_DEFAULT_GEM" => "true" } - expect(the_bundle.locked_gems.bundler_version).to eq v(Bundler::VERSION) + expect(out).to include("Using bundler 2.3.9") + + expect(lockfile).to eq <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + rack (1.0) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + rack + + BUNDLED WITH + 2.3.9 + L + + expect(the_bundle).to include_gems "bundler 2.3.9" + expect(the_bundle).to include_gems "rack 1.0" + end + + it "errors if the explicit target version does not exist" do + pristine_system_gems "bundler-2.3.9" + + build_repo4 do + build_gem "rack", "1.0" + end + + install_gemfile <<-G, env: { "BUNDLER_IGNORE_DEFAULT_GEM" => "true" } + source "#{file_uri_for(gem_repo4)}" + gem "rack" + G + lockfile lockfile.sub(/(^\s*)#{Bundler::VERSION}($)/, "2.3.9") + + bundle :update, bundler: "999.999.999", artifice: "compact_index", raise_on_error: false + + # Only gives a meaningful error message on modern RubyGems. + + if Gem.rubygems_version >= Gem::Version.new("3.3.0.dev") + expect(last_command).to be_failure + expect(err).to include("The `bundle update --bundler` target version (999.999.999) does not exist") + end + end + + it "allows updating to development versions if already installed locally" do + system_gems "bundler-2.3.0.dev" + + build_repo4 do + build_gem "rack", "1.0" + end + + install_gemfile <<-G + source "#{file_uri_for(gem_repo4)}" + gem "rack" + G + + bundle :update, bundler: "2.3.0.dev", verbose: "true" + + # Only updates properly on modern RubyGems. + if Gem.rubygems_version >= Gem::Version.new("3.3.0.dev") + checksums = checksums_section_when_existing do |c| + c.checksum(gem_repo4, "rack", "1.0") + end + + expect(lockfile).to eq <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + rack (1.0) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + rack + #{checksums} + BUNDLED WITH + 2.3.0.dev + L + + expect(out).to include("Using bundler 2.3.0.dev") + end + end + + it "does not touch the network if not necessary" do + system_gems "bundler-2.3.9" + + build_repo4 do + build_gem "rack", "1.0" + end + + install_gemfile <<-G + source "#{file_uri_for(gem_repo4)}" + gem "rack" + G + + bundle :update, bundler: "2.3.9", verbose: true + + expect(out).not_to include("Fetching gem metadata from https://rubygems.org/") + + # Only updates properly on modern RubyGems. + checksums = checksums_section_when_existing do |c| + c.checksum(gem_repo4, "rack", "1.0") + end + + if Gem.rubygems_version >= Gem::Version.new("3.3.0.dev") + expect(lockfile).to eq <<~L + GEM + remote: #{file_uri_for(gem_repo4)}/ + specs: + rack (1.0) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + rack + #{checksums} + BUNDLED WITH + 2.3.9 + L + + expect(out).to include("Using bundler 2.3.9") + end + end + + it "prints an error when trying to update bundler in frozen mode" do + system_gems "bundler-2.3.9" + + gemfile <<~G + source "#{file_uri_for(gem_repo2)}" + G + + lockfile <<-L + GEM + remote: #{file_uri_for(gem_repo2)}/ + specs: + + PLATFORMS + ruby + + DEPENDENCIES + + BUNDLED WITH + 2.1.4 + L + + bundle "update --bundler=2.3.9", env: { "BUNDLE_FROZEN" => "true" }, raise_on_error: false + expect(err).to include("An update to the version of bundler itself was requested, but the lockfile can't be updated because frozen mode is set") end end @@ -1131,7 +1730,10 @@ RSpec.describe "bundle update conservative" do build_gem "foo", %w[1.5.1] do |s| s.add_dependency "bar", "~> 3.0" end - build_gem "bar", %w[2.0.3 2.0.4 2.0.5 2.1.0 2.1.1 3.0.0] + build_gem "foo", %w[2.0.0.pre] do |s| + s.add_dependency "bar" + end + build_gem "bar", %w[2.0.3 2.0.4 2.0.5 2.1.0 2.1.1 2.1.2.pre 3.0.0 3.1.0.pre 4.0.0.pre] build_gem "qux", %w[1.0.0 1.0.1 1.1.0 2.0.0] end @@ -1169,7 +1771,7 @@ RSpec.describe "bundle update conservative" do end it "update all" do - bundle "update --patch", :all => true + bundle "update --patch", all: true expect(the_bundle).to include_gems "foo 1.4.5", "bar 2.1.1", "qux 1.0.1" end @@ -1191,11 +1793,37 @@ RSpec.describe "bundle update conservative" do end it "minor preferred" do - bundle "update --minor --strict", :all => true + bundle "update --minor --strict", all: true expect(the_bundle).to include_gems "foo 1.5.0", "bar 2.1.1", "qux 1.1.0" end end + + context "pre" do + it "defaults to major" do + bundle "update --pre foo bar" + + expect(the_bundle).to include_gems "foo 2.0.0.pre", "bar 4.0.0.pre", "qux 1.0.0" + end + + it "patch preferred" do + bundle "update --patch --pre foo bar" + + expect(the_bundle).to include_gems "foo 1.4.5", "bar 2.1.2.pre", "qux 1.0.0" + end + + it "minor preferred" do + bundle "update --minor --pre foo bar" + + expect(the_bundle).to include_gems "foo 1.5.1", "bar 3.1.0.pre", "qux 1.0.0" + end + + it "major preferred" do + bundle "update --major --pre foo bar" + + expect(the_bundle).to include_gems "foo 2.0.0.pre", "bar 4.0.0.pre", "qux 1.0.0" + end + end end context "eager unlocking" do @@ -1237,13 +1865,15 @@ RSpec.describe "bundle update conservative" do shared_dep (~> 5.0) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES isolated_owner shared_owner_a shared_owner_b + CHECKSUMS + BUNDLED WITH #{Bundler::VERSION} L @@ -1290,13 +1920,20 @@ RSpec.describe "bundle update conservative" do shared_dep (~> 5.0) PLATFORMS - #{specific_local_platform} + #{local_platform} DEPENDENCIES isolated_owner shared_owner_a shared_owner_b + CHECKSUMS + isolated_dep (2.0.1) + isolated_owner (1.0.2) + shared_dep (5.0.1) + shared_owner_a (3.0.2) + shared_owner_b (4.0.2) + BUNDLED WITH #{Bundler::VERSION} L @@ -1323,7 +1960,7 @@ RSpec.describe "bundle update conservative" do end it "raises if too many flags are provided" do - bundle "update --patch --minor", :all => true, :raise_on_error => false + bundle "update --patch --minor", all: true, raise_on_error: false expect(err).to eq "Provide only one of the following options: minor, patch" end diff --git a/spec/bundler/commands/version_spec.rb b/spec/bundler/commands/version_spec.rb index 53d545f5fa..307058a5dd 100644 --- a/spec/bundler/commands/version_spec.rb +++ b/spec/bundler/commands/version_spec.rb @@ -4,42 +4,42 @@ require_relative "../support/path" RSpec.describe "bundle version" do if Spec::Path.ruby_core? - COMMIT_HASH = /unknown|[a-fA-F0-9]{7,}/.freeze + COMMIT_HASH = /unknown|[a-fA-F0-9]{7,}/ else - COMMIT_HASH = /[a-fA-F0-9]{7,}/.freeze + COMMIT_HASH = /[a-fA-F0-9]{7,}/ end context "with -v" do - it "outputs the version", :bundler => "< 3" do + it "outputs the version", bundler: "< 3" do bundle "-v" expect(out).to eq("Bundler version #{Bundler::VERSION}") end - it "outputs the version", :bundler => "3" do + it "outputs the version", bundler: "3" do bundle "-v" expect(out).to eq(Bundler::VERSION) end end context "with --version" do - it "outputs the version", :bundler => "< 3" do + it "outputs the version", bundler: "< 3" do bundle "--version" expect(out).to eq("Bundler version #{Bundler::VERSION}") end - it "outputs the version", :bundler => "3" do + it "outputs the version", bundler: "3" do bundle "--version" expect(out).to eq(Bundler::VERSION) end end context "with version" do - it "outputs the version with build metadata", :bundler => "< 3" do + it "outputs the version with build metadata", bundler: "< 3" do bundle "version" expect(out).to match(/\ABundler version #{Regexp.escape(Bundler::VERSION)} \(\d{4}-\d{2}-\d{2} commit #{COMMIT_HASH}\)\z/) end - it "outputs the version with build metadata", :bundler => "3" do + it "outputs the version with build metadata", bundler: "3" do bundle "version" expect(out).to match(/\A#{Regexp.escape(Bundler::VERSION)} \(\d{4}-\d{2}-\d{2} commit #{COMMIT_HASH}\)\z/) end diff --git a/spec/bundler/commands/viz_spec.rb b/spec/bundler/commands/viz_spec.rb index 0efb24b504..f8b5f7836e 100644 --- a/spec/bundler/commands/viz_spec.rb +++ b/spec/bundler/commands/viz_spec.rb @@ -1,10 +1,8 @@ # frozen_string_literal: true -RSpec.describe "bundle viz", :bundler => "< 3", :if => Bundler.which("dot") do +RSpec.describe "bundle viz", bundler: "< 3", if: Bundler.which("dot"), realworld: true do before do - graphviz_version = RUBY_VERSION >= "2.4" ? "1.2.5" : "1.2.4" - - realworld_system_gems "ruby-graphviz --version #{graphviz_version}" + realworld_system_gems "ruby-graphviz --version 1.2.5" end it "graphs gems from the Gemfile" do @@ -17,8 +15,8 @@ RSpec.describe "bundle viz", :bundler => "< 3", :if => Bundler.which("dot") do bundle "viz" expect(out).to include("gem_graph.png") - bundle "viz", :format => "debug" - expect(out).to eq(strip_whitespace(<<-DOT).strip) + bundle "viz", format: "debug" + expect(out).to eq(<<~DOT.strip) digraph Gemfile { concentrate = "true"; normalize = "true"; @@ -51,8 +49,8 @@ RSpec.describe "bundle viz", :bundler => "< 3", :if => Bundler.which("dot") do bundle "viz" expect(out).to include("gem_graph.png") - bundle "viz", :format => :debug, :version => true - expect(out).to eq(strip_whitespace(<<-EOS).strip) + bundle "viz", format: :debug, version: true + expect(out).to eq(<<~EOS.strip) digraph Gemfile { concentrate = "true"; normalize = "true"; @@ -79,7 +77,7 @@ RSpec.describe "bundle viz", :bundler => "< 3", :if => Bundler.which("dot") do end end - system_gems "graphviz-999", :gem_repo => gem_repo4 + system_gems "graphviz-999", gem_repo: gem_repo4 end it "loads the correct ruby-graphviz gem" do @@ -89,8 +87,8 @@ RSpec.describe "bundle viz", :bundler => "< 3", :if => Bundler.which("dot") do gem "rack-obama" G - bundle "viz", :format => "debug" - expect(out).to eq(strip_whitespace(<<-DOT).strip) + bundle "viz", format: "debug" + expect(out).to eq(<<~DOT.strip) digraph Gemfile { concentrate = "true"; normalize = "true"; |