diff options
Diffstat (limited to 'spec/bundler/commands/install_spec.rb')
-rw-r--r-- | spec/bundler/commands/install_spec.rb | 739 |
1 files changed, 682 insertions, 57 deletions
diff --git a/spec/bundler/commands/install_spec.rb b/spec/bundler/commands/install_spec.rb index 412000341f..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 @@ -94,6 +94,21 @@ RSpec.describe "bundle install with gem sources" do expect(the_bundle).to include_gems("rack 1.0.0") end + it "auto-heals missing gems" do + install_gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem 'rack' + G + + FileUtils.rm_rf(default_bundle_path("gems/rack-1.0.0")) + + bundle "install --verbose" + + expect(out).to include("Installing rack 1.0.0") + expect(default_bundle_path("gems/rack-1.0.0")).to exist + expect(the_bundle).to include_gems("rack 1.0.0") + end + it "fetches gems when multiple versions are specified" do install_gemfile <<-G source "#{file_uri_for(gem_repo1)}" @@ -115,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 @@ -182,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| @@ -199,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" @@ -210,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 @@ -226,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 @@ -270,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)}" @@ -278,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 @@ -296,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" @@ -311,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" @@ -330,13 +345,13 @@ 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 expect(err).to include("This Gemfile does not include an explicit global source. " \ "Not using an explicit global source may result in a different lockfile being generated depending on " \ - "the gems you have installed locally before bundler is run." \ + "the gems you have installed locally before bundler is run. " \ "Instead, define a global source in your Gemfile like this: source \"https://rubygems.org\".") end @@ -349,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" @@ -361,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" @@ -372,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 @@ -395,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" @@ -407,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" @@ -420,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' @@ -432,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). @@ -444,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" @@ -479,31 +661,27 @@ 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 - lockfile_should_be <<-L + checksums = checksums_section_when_existing + expect(lockfile).to eq <<~L GEM remote: #{file_uri_for(gem_repo1)}/ specs: @@ -512,24 +690,24 @@ 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 - lockfile_should_be <<-L + checksums = checksums_section_when_existing + + expect(lockfile).to eq <<~L GEM remote: #{file_uri_for(gem_repo1)}/ specs: @@ -538,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} @@ -575,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 @@ -583,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 @@ -592,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 @@ -605,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 @@ -635,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") @@ -657,12 +835,174 @@ 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 end + describe "when bundle gems path does not have write access", :permissions do + let(:gems_path) { bundled_app("vendor/#{Bundler.ruby_scope}/gems") } + + before do + FileUtils.mkdir_p(gems_path) + gemfile <<-G + source "#{file_uri_for(gem_repo1)}" + gem 'rack' + G + end + + it "should display a proper message to explain the problem" do + FileUtils.chmod("-x", gems_path) + bundle "config set --local path vendor" + + begin + bundle :install, raise_on_error: false + ensure + FileUtils.chmod("+x", gems_path) + end + + expect(err).not_to include("ERROR REPORT TEMPLATE") + + expect(err).to include( + "There was an error while trying to create `#{gems_path.join("rack-1.0.0")}`. " \ + "It is likely that you need to grant executable permissions for all parent directories and write permissions for `#{gems_path}`." + ) + 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") } + + 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("-x", foo_path) + + begin + 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 + + 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 + describe "when bundle cache path does not have write access", :permissions do let(:cache_path) { bundled_app("vendor/#{Bundler.ruby_scope}/cache") } @@ -678,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 @@ -691,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 @@ -710,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.") @@ -751,12 +1091,297 @@ 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 + + context "with missing platform specific gems in lockfile" do + before do + build_repo4 do + build_gem "racca", "1.5.2" + + build_gem "nokogiri", "1.12.4" do |s| + s.platform = "x86_64-darwin" + 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 "racca", "~> 1.4" + end + + build_gem "crass", "1.0.6" + + build_gem "loofah", "2.12.0" do |s| + s.add_runtime_dependency "crass", "~> 1.0.2" + s.add_runtime_dependency "nokogiri", ">= 1.5.9" + end + end + + gemfile <<-G + source "https://gem.repo4" + + 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/ + specs: + crass (1.0.6) + loofah (2.12.0) + crass (~> 1.0.2) + nokogiri (>= 1.5.9) + nokogiri (1.12.4-x86_64-darwin) + racca (~> 1.4) + racca (1.5.2) + + PLATFORMS + x86_64-darwin-20 + x86_64-linux + + DEPENDENCIES + loofah (~> 2.12.0) + #{checksums} + RUBY VERSION + #{Bundler::RubyVersion.system} + + BUNDLED WITH + #{Bundler::VERSION} + L + end + + it "automatically fixes the lockfile" do + bundle "config set --local path vendor/bundle" + + simulate_platform "x86_64-linux" do + 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 + GEM + remote: https://gem.repo4/ + specs: + crass (1.0.6) + loofah (2.12.0) + crass (~> 1.0.2) + nokogiri (>= 1.5.9) + nokogiri (1.12.4-x86_64-darwin) + racca (~> 1.4) + nokogiri (1.12.4-x86_64-linux) + racca (~> 1.4) + racca (1.5.2) + + PLATFORMS + x86_64-darwin-20 + x86_64-linux + + DEPENDENCIES + loofah (~> 2.12.0) + #{checksums} + RUBY VERSION + #{Bundler::RubyVersion.system} + + BUNDLED WITH + #{Bundler::VERSION} + L + end + end + + context "with --local flag" do + before do + 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 + source "#{file_uri_for(gem_repo1)}" + + source "https://not-existing-source" do + gem "rack" + end + G + + 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 |