aboutsummaryrefslogtreecommitdiffstats
path: root/spec/bundler/install
diff options
context:
space:
mode:
authorhsbt <hsbt@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2017-09-08 08:45:41 +0000
committerhsbt <hsbt@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2017-09-08 08:45:41 +0000
commit8598f8c2dc78c6d1ae87cb6ae19c34ba2cb29241 (patch)
tree0bbd28f684e745cb212761b7c74fe08668f85cc8 /spec/bundler/install
parentf2e04b77aa8a363d7e36ce5a9cdb60714a537a3c (diff)
downloadruby-8598f8c2dc78c6d1ae87cb6ae19c34ba2cb29241.tar.gz
Merge bundler to standard libraries.
rubygems 2.7.x depends bundler-1.15.x. This is preparation for rubygems and bundler migration. * lib/bundler.rb, lib/bundler/*: files of bundler-1.15.4 * spec/bundler/*: rspec examples of bundler-1.15.4. I applied patches. * https://github.com/bundler/bundler/pull/6007 * Exclude not working examples on ruby repository. * Fake ruby interpriter instead of installed ruby. * Makefile.in: Added test task named `test-bundler`. This task is only working macOS/linux yet. I'm going to support Windows environment later. * tool/sync_default_gems.rb: Added sync task for bundler. [Feature #12733][ruby-core:77172] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@59779 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'spec/bundler/install')
-rw-r--r--spec/bundler/install/allow_offline_install_spec.rb92
-rw-r--r--spec/bundler/install/binstubs_spec.rb50
-rw-r--r--spec/bundler/install/bundler_spec.rb147
-rw-r--r--spec/bundler/install/deploy_spec.rb301
-rw-r--r--spec/bundler/install/failure_spec.rb33
-rw-r--r--spec/bundler/install/force_spec.rb67
-rw-r--r--spec/bundler/install/gemfile/eval_gemfile_spec.rb67
-rw-r--r--spec/bundler/install/gemfile/gemspec_spec.rb563
-rw-r--r--spec/bundler/install/gemfile/git_spec.rb1259
-rw-r--r--spec/bundler/install/gemfile/groups_spec.rb371
-rw-r--r--spec/bundler/install/gemfile/install_if.rb45
-rw-r--r--spec/bundler/install/gemfile/path_spec.rb595
-rw-r--r--spec/bundler/install/gemfile/platform_spec.rb265
-rw-r--r--spec/bundler/install/gemfile/ruby_spec.rb109
-rw-r--r--spec/bundler/install/gemfile/sources_spec.rb518
-rw-r--r--spec/bundler/install/gemfile/specific_platform_spec.rb115
-rw-r--r--spec/bundler/install/gemfile_spec.rb98
-rw-r--r--spec/bundler/install/gems/compact_index_spec.rb805
-rw-r--r--spec/bundler/install/gems/dependency_api_spec.rb671
-rw-r--r--spec/bundler/install/gems/env_spec.rb108
-rw-r--r--spec/bundler/install/gems/flex_spec.rb319
-rw-r--r--spec/bundler/install/gems/mirror_spec.rb40
-rw-r--r--spec/bundler/install/gems/native_extensions_spec.rb92
-rw-r--r--spec/bundler/install/gems/post_install_spec.rb151
-rw-r--r--spec/bundler/install/gems/resolving_spec.rb195
-rw-r--r--spec/bundler/install/gems/standalone_spec.rb318
-rw-r--r--spec/bundler/install/gems/sudo_spec.rb179
-rw-r--r--spec/bundler/install/gems/win32_spec.rb27
-rw-r--r--spec/bundler/install/gemspecs_spec.rb110
-rw-r--r--spec/bundler/install/git_spec.rb66
-rw-r--r--spec/bundler/install/path_spec.rb178
-rw-r--r--spec/bundler/install/post_bundle_message_spec.rb190
-rw-r--r--spec/bundler/install/prereleases_spec.rb42
-rw-r--r--spec/bundler/install/security_policy_spec.rb77
-rw-r--r--spec/bundler/install/yanked_spec.rb72
35 files changed, 8335 insertions, 0 deletions
diff --git a/spec/bundler/install/allow_offline_install_spec.rb b/spec/bundler/install/allow_offline_install_spec.rb
new file mode 100644
index 0000000000..1bca055c9f
--- /dev/null
+++ b/spec/bundler/install/allow_offline_install_spec.rb
@@ -0,0 +1,92 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+RSpec.describe "bundle install with :allow_offline_install" do
+ before do
+ bundle "config allow_offline_install true"
+ end
+
+ context "with no cached data locally" do
+ it "still installs" do
+ install_gemfile! <<-G, :artifice => "compact_index"
+ source "http://testgemserver.local"
+ gem "rack-obama"
+ G
+ expect(the_bundle).to include_gem("rack 1.0")
+ end
+
+ it "still fails when the network is down" do
+ install_gemfile <<-G, :artifice => "fail"
+ source "http://testgemserver.local"
+ gem "rack-obama"
+ G
+ expect(out).to include("Could not reach host testgemserver.local.")
+ expect(the_bundle).to_not be_locked
+ end
+ end
+
+ context "with cached data locally" do
+ it "will install from the compact index" do
+ system_gems ["rack-1.0.0"]
+
+ install_gemfile! <<-G, :artifice => "compact_index"
+ source "http://testgemserver.local"
+ gem "rack-obama"
+ gem "rack", "< 1.0"
+ G
+
+ expect(the_bundle).to include_gems("rack-obama 1.0", "rack 0.9.1")
+
+ gemfile <<-G
+ source "http://testgemserver.local"
+ gem "rack-obama"
+ G
+
+ bundle! :update, :artifice => "fail"
+ expect(out).to include("Using the cached data for the new index because of a network error")
+
+ expect(the_bundle).to include_gems("rack-obama 1.0", "rack 1.0.0")
+ end
+
+ def break_git_remote_ops!
+ FileUtils.mkdir_p(tmp("broken_path"))
+ File.open(tmp("broken_path/git"), "w", 0o755) do |f|
+ f.puts strip_whitespace(<<-RUBY)
+ #!/usr/bin/env ruby
+ if %w(fetch --force --quiet --tags refs/heads/*:refs/heads/*).-(ARGV).empty? || %w(clone --bare --no-hardlinks --quiet).-(ARGV).empty?
+ warn "git remote ops have been disabled"
+ exit 1
+ end
+ ENV["PATH"] = ENV["PATH"].sub(/^.*?:/, "")
+ exec("git", *ARGV)
+ RUBY
+ end
+
+ old_path = ENV["PATH"]
+ ENV["PATH"] = "#{tmp("broken_path")}:#{ENV["PATH"]}"
+ yield if block_given?
+ ensure
+ ENV["PATH"] = old_path if block_given?
+ end
+
+ it "will install from a cached git repo" do
+ git = build_git "a", "1.0.0", :path => lib_path("a")
+ update_git("a", :path => git.path, :branch => "new_branch")
+ install_gemfile! <<-G
+ gem "a", :git => #{git.path.to_s.dump}
+ G
+
+ break_git_remote_ops! { bundle! :update }
+ expect(out).to include("Using cached git data because of network errors")
+ expect(the_bundle).to be_locked
+
+ break_git_remote_ops! do
+ install_gemfile! <<-G
+ gem "a", :git => #{git.path.to_s.dump}, :branch => "new_branch"
+ G
+ end
+ expect(out).to include("Using cached git data because of network errors")
+ expect(the_bundle).to be_locked
+ end
+ end
+end
diff --git a/spec/bundler/install/binstubs_spec.rb b/spec/bundler/install/binstubs_spec.rb
new file mode 100644
index 0000000000..a1a9ab167d
--- /dev/null
+++ b/spec/bundler/install/binstubs_spec.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+RSpec.describe "bundle install" do
+ describe "when system_bindir is set" do
+ # On OS X, Gem.bindir defaults to /usr/bin, so system_bindir is useful if
+ # you want to avoid sudo installs for system gems with OS X's default ruby
+ it "overrides Gem.bindir" do
+ expect(Pathname.new("/usr/bin")).not_to be_writable unless Process.euid == 0
+ gemfile <<-G
+ require 'rubygems'
+ def Gem.bindir; "/usr/bin"; end
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ config "BUNDLE_SYSTEM_BINDIR" => system_gem_path("altbin").to_s
+ bundle :install
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(system_gem_path("altbin/rackup")).to exist
+ end
+ end
+
+ describe "when multiple gems contain the same exe" do
+ before do
+ build_repo2 do
+ build_gem "fake", "14" do |s|
+ s.executables = "rackup"
+ end
+ end
+
+ install_gemfile <<-G, :binstubs => true
+ source "file://#{gem_repo2}"
+ gem "fake"
+ gem "rack"
+ G
+ end
+
+ it "prints a deprecation notice" do
+ bundle "config major_deprecations true"
+ gembin("rackup")
+ expect(out).to include("Bundler is using a binstub that was created for a different gem.")
+ end
+
+ it "loads the correct spec's executable" do
+ gembin("rackup")
+ expect(out).to eq("1.2")
+ end
+ end
+end
diff --git a/spec/bundler/install/bundler_spec.rb b/spec/bundler/install/bundler_spec.rb
new file mode 100644
index 0000000000..c1ce57e60e
--- /dev/null
+++ b/spec/bundler/install/bundler_spec.rb
@@ -0,0 +1,147 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+RSpec.describe "bundle install" do
+ describe "with bundler dependencies" do
+ before(:each) do
+ build_repo2 do
+ build_gem "rails", "3.0" do |s|
+ s.add_dependency "bundler", ">= 0.9.0.pre"
+ end
+ build_gem "bundler", "0.9.1"
+ build_gem "bundler", Bundler::VERSION
+ end
+ end
+
+ it "are forced to the current bundler version" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rails", "3.0"
+ G
+
+ expect(the_bundle).to include_gems "bundler #{Bundler::VERSION}"
+ end
+
+ it "are not added if not already present" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ expect(the_bundle).not_to include_gems "bundler #{Bundler::VERSION}"
+ end
+
+ it "causes a conflict if explicitly requesting a different version" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rails", "3.0"
+ gem "bundler", "0.9.2"
+ G
+
+ nice_error = <<-E.strip.gsub(/^ {8}/, "")
+ Fetching source index from file:#{gem_repo2}/
+ Resolving dependencies...
+ Bundler could not find compatible versions for gem "bundler":
+ In Gemfile:
+ bundler (= 0.9.2)
+
+ Current Bundler version:
+ bundler (#{Bundler::VERSION})
+ This Gemfile requires a different version of Bundler.
+ Perhaps you need to update Bundler by running `gem install bundler`?
+
+ Could not find gem 'bundler (= 0.9.2)' in any of the sources
+ E
+ expect(out).to eq(nice_error)
+ end
+
+ it "works for gems with multiple versions in its dependencies" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+
+ gem "multiple_versioned_deps"
+ G
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+
+ gem "multiple_versioned_deps"
+ gem "rack"
+ G
+
+ expect(the_bundle).to include_gems "multiple_versioned_deps 1.0.0"
+ end
+
+ it "includes bundler in the bundle when it's a child dependency" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rails", "3.0"
+ G
+
+ run "begin; gem 'bundler'; puts 'WIN'; rescue Gem::LoadError; puts 'FAIL'; end"
+ expect(out).to eq("WIN")
+ end
+
+ it "allows gem 'bundler' when Bundler is not in the Gemfile or its dependencies" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack"
+ G
+
+ run "begin; gem 'bundler'; puts 'WIN'; rescue Gem::LoadError => e; puts e.backtrace; end"
+ expect(out).to eq("WIN")
+ end
+
+ it "causes a conflict if child dependencies conflict" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "activemerchant"
+ gem "rails_fail"
+ G
+
+ nice_error = <<-E.strip.gsub(/^ {8}/, "")
+ Fetching source index from file:#{gem_repo2}/
+ Resolving dependencies...
+ Bundler could not find compatible versions for gem "activesupport":
+ In Gemfile:
+ activemerchant was resolved to 1.0, which depends on
+ activesupport (>= 2.0.0)
+
+ rails_fail was resolved to 1.0, which depends on
+ activesupport (= 1.2.3)
+ E
+ expect(out).to include(nice_error)
+ end
+
+ it "causes a conflict if a child dependency conflicts with the Gemfile" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rails_fail"
+ gem "activesupport", "2.3.5"
+ G
+
+ nice_error = <<-E.strip.gsub(/^ {8}/, "")
+ Fetching source index from file:#{gem_repo2}/
+ Resolving dependencies...
+ Bundler could not find compatible versions for gem "activesupport":
+ In Gemfile:
+ activesupport (= 2.3.5)
+
+ rails_fail was resolved to 1.0, which depends on
+ activesupport (= 1.2.3)
+ E
+ expect(out).to include(nice_error)
+ end
+
+ it "can install dependencies with newer bundler version" do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rails", "3.0"
+ G
+
+ simulate_bundler_version "10.0.0"
+
+ bundle "check"
+ expect(out).to include("The Gemfile's dependencies are satisfied")
+ end
+ end
+end
diff --git a/spec/bundler/install/deploy_spec.rb b/spec/bundler/install/deploy_spec.rb
new file mode 100644
index 0000000000..b66135c6b0
--- /dev/null
+++ b/spec/bundler/install/deploy_spec.rb
@@ -0,0 +1,301 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+RSpec.describe "install with --deployment or --frozen" do
+ before do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ end
+
+ it "fails without a lockfile and says that --deployment requires a lock" do
+ bundle "install --deployment"
+ expect(out).to include("The --deployment flag requires a Gemfile.lock")
+ end
+
+ it "fails without a lockfile and says that --frozen requires a lock" do
+ bundle "install --frozen"
+ expect(out).to include("The --frozen flag requires a Gemfile.lock")
+ end
+
+ it "disallows --deployment --system" do
+ bundle "install --deployment --system"
+ expect(out).to include("You have specified both --deployment")
+ expect(out).to include("Please choose only one option")
+ expect(exitstatus).to eq(15) if exitstatus
+ end
+
+ it "disallows --deployment --path --system" do
+ bundle "install --deployment --path . --system"
+ expect(out).to include("You have specified both --path")
+ expect(out).to include("as well as --system")
+ expect(out).to include("Please choose only one option")
+ expect(exitstatus).to eq(15) if exitstatus
+ end
+
+ it "works after you try to deploy without a lock" do
+ bundle "install --deployment"
+ bundle :install
+ expect(exitstatus).to eq(0) if exitstatus
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+
+ it "still works if you are not in the app directory and specify --gemfile" do
+ bundle "install"
+ Dir.chdir tmp
+ simulate_new_machine
+ bundle "install --gemfile #{tmp}/bundled_app/Gemfile --deployment"
+ Dir.chdir bundled_app
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+
+ it "works if you exclude a group with a git gem" do
+ build_git "foo"
+ gemfile <<-G
+ group :test do
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ end
+ G
+ bundle :install
+ bundle "install --deployment --without test"
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+
+ it "works when you bundle exec bundle", :ruby_repo do
+ bundle :install
+ bundle "install --deployment"
+ bundle "exec bundle check"
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+
+ it "works when using path gems from the same path and the version is specified" do
+ build_lib "foo", :path => lib_path("nested/foo")
+ build_lib "bar", :path => lib_path("nested/bar")
+ gemfile <<-G
+ gem "foo", "1.0", :path => "#{lib_path("nested")}"
+ gem "bar", :path => "#{lib_path("nested")}"
+ G
+
+ bundle! :install
+ bundle! "install --deployment"
+ end
+
+ it "works when there are credentials in the source URL" do
+ install_gemfile(<<-G, :artifice => "endpoint_strict_basic_authentication", :quiet => true)
+ source "http://user:pass@localgemserver.test/"
+
+ gem "rack-obama", ">= 1.0"
+ G
+
+ bundle "install --deployment", :artifice => "endpoint_strict_basic_authentication"
+
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+
+ it "works with sources given by a block" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}" do
+ gem "rack"
+ end
+ G
+
+ bundle "install --deployment"
+
+ expect(exitstatus).to eq(0) if exitstatus
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+
+ describe "with an existing lockfile" do
+ before do
+ bundle "install"
+ end
+
+ it "works with the --deployment flag if you didn't change anything" do
+ bundle "install --deployment"
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+
+ it "works with the --frozen flag if you didn't change anything" do
+ bundle "install --frozen"
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+
+ it "explodes with the --deployment flag if you make a change and don't check in the lockfile" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "rack-obama"
+ G
+
+ bundle "install --deployment"
+ expect(out).to include("deployment mode")
+ expect(out).to include("You have added to the Gemfile")
+ expect(out).to include("* rack-obama")
+ expect(out).not_to include("You have deleted from the Gemfile")
+ expect(out).not_to include("You have changed in the Gemfile")
+ end
+
+ it "can have --frozen set via an environment variable" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "rack-obama"
+ G
+
+ ENV["BUNDLE_FROZEN"] = "1"
+ bundle "install"
+ expect(out).to include("deployment mode")
+ expect(out).to include("You have added to the Gemfile")
+ expect(out).to include("* rack-obama")
+ expect(out).not_to include("You have deleted from the Gemfile")
+ expect(out).not_to include("You have changed in the Gemfile")
+ end
+
+ it "can have --frozen set to false via an environment variable" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "rack-obama"
+ G
+
+ ENV["BUNDLE_FROZEN"] = "false"
+ bundle "install"
+ expect(out).not_to include("deployment mode")
+ expect(out).not_to include("You have added to the Gemfile")
+ expect(out).not_to include("* rack-obama")
+ end
+
+ it "explodes with the --frozen flag if you make a change and don't check in the lockfile" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "rack-obama", "1.1"
+ G
+
+ bundle "install --frozen"
+ expect(out).to include("deployment mode")
+ expect(out).to include("You have added to the Gemfile")
+ expect(out).to include("* rack-obama (= 1.1)")
+ expect(out).not_to include("You have deleted from the Gemfile")
+ expect(out).not_to include("You have changed in the Gemfile")
+ end
+
+ it "explodes if you remove a gem and don't check in the lockfile" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "activesupport"
+ G
+
+ bundle "install --deployment"
+ expect(out).to include("deployment mode")
+ expect(out).to include("You have added to the Gemfile:\n* activesupport\n\n")
+ expect(out).to include("You have deleted from the Gemfile:\n* rack")
+ expect(out).not_to include("You have changed in the Gemfile")
+ end
+
+ it "explodes if you add a source" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "git://hubz.com"
+ G
+
+ bundle "install --deployment"
+ expect(out).to include("deployment mode")
+ expect(out).to include("You have added to the Gemfile:\n* source: git://hubz.com (at master)")
+ expect(out).not_to include("You have changed in the Gemfile")
+ end
+
+ it "explodes if you unpin a source" do
+ build_git "rack"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-1.0")}"
+ G
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle "install --deployment"
+ expect(out).to include("deployment mode")
+ expect(out).to include("You have deleted from the Gemfile:\n* source: #{lib_path("rack-1.0")} (at master@#{revision_for(lib_path("rack-1.0"))[0..6]}")
+ expect(out).not_to include("You have added to the Gemfile")
+ expect(out).not_to include("You have changed in the Gemfile")
+ end
+
+ it "explodes if you unpin a source, leaving it pinned somewhere else" do
+ build_lib "foo", :path => lib_path("rack/foo")
+ build_git "rack", :path => lib_path("rack")
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack")}"
+ gem "foo", :git => "#{lib_path("rack")}"
+ G
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "foo", :git => "#{lib_path("rack")}"
+ G
+
+ bundle "install --deployment"
+ expect(out).to include("deployment mode")
+ expect(out).to include("You have changed in the Gemfile:\n* rack from `no specified source` to `#{lib_path("rack")} (at master@#{revision_for(lib_path("rack"))[0..6]})`")
+ expect(out).not_to include("You have added to the Gemfile")
+ expect(out).not_to include("You have deleted from the Gemfile")
+ end
+
+ it "remembers that the bundle is frozen at runtime" do
+ bundle "install --deployment"
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "1.0.0"
+ gem "rack-obama"
+ G
+
+ expect(the_bundle).not_to include_gems "rack 1.0.0"
+ expect(err).to include strip_whitespace(<<-E).strip
+The dependencies in your gemfile changed
+
+You have added to the Gemfile:
+* rack (= 1.0.0)
+* rack-obama
+
+You have deleted from the Gemfile:
+* rack
+ E
+ end
+ end
+
+ context "with path in Gemfile and packed" do
+ it "works fine after bundle package and bundle install --local" do
+ build_lib "foo", :path => lib_path("foo")
+ install_gemfile! <<-G
+ gem "foo", :path => "#{lib_path("foo")}"
+ G
+
+ bundle! :install
+ expect(the_bundle).to include_gems "foo 1.0"
+ bundle! "package --all"
+ expect(bundled_app("vendor/cache/foo")).to be_directory
+
+ bundle! "install --local"
+ expect(out).to include("Using foo 1.0 from source at")
+ expect(out).to include("vendor/cache/foo")
+
+ simulate_new_machine
+ bundle! "install --deployment --verbose"
+ expect(out).not_to include("You are trying to install in deployment mode after changing your Gemfile")
+ expect(out).not_to include("You have added to the Gemfile")
+ expect(out).not_to include("You have deleted from the Gemfile")
+ expect(out).to include("Using foo 1.0 from source at")
+ expect(out).to include("vendor/cache/foo")
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+ end
+end
diff --git a/spec/bundler/install/failure_spec.rb b/spec/bundler/install/failure_spec.rb
new file mode 100644
index 0000000000..738b2cf1bd
--- /dev/null
+++ b/spec/bundler/install/failure_spec.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+RSpec.describe "bundle install" do
+ context "installing a gem fails" do
+ it "prints out why that gem was being installed" do
+ build_repo2 do
+ build_gem "activesupport", "2.3.2" do |s|
+ s.extensions << "Rakefile"
+ s.write "Rakefile", <<-RUBY
+ task :default do
+ abort "make installing activesupport-2.3.2 fail"
+ end
+ RUBY
+ end
+ end
+
+ install_gemfile <<-G
+ source "file:#{gem_repo2}"
+ gem "rails"
+ G
+ expect(out).to end_with(<<-M.strip)
+An error occurred while installing activesupport (2.3.2), and Bundler cannot continue.
+Make sure that `gem install activesupport -v '2.3.2'` succeeds before bundling.
+
+In Gemfile:
+ rails was resolved to 2.3.2, which depends on
+ actionmailer was resolved to 2.3.2, which depends on
+ activesupport
+ M
+ end
+ end
+end
diff --git a/spec/bundler/install/force_spec.rb b/spec/bundler/install/force_spec.rb
new file mode 100644
index 0000000000..dc4956a7ae
--- /dev/null
+++ b/spec/bundler/install/force_spec.rb
@@ -0,0 +1,67 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+RSpec.describe "bundle install" do
+ describe "with --force" do
+ before :each do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ end
+
+ it "re-installs installed gems" do
+ rack_lib = default_bundle_path("gems/rack-1.0.0/lib/rack.rb")
+
+ bundle "install"
+ rack_lib.open("w") {|f| f.write("blah blah blah") }
+ bundle "install --force"
+
+ expect(exitstatus).to eq(0) if exitstatus
+ expect(out).to include "Using bundler"
+ expect(out).to include "Installing rack 1.0.0"
+ expect(rack_lib.open(&:read)).to eq("RACK = '1.0.0'\n")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "works on first bundle install" do
+ bundle "install --force"
+
+ expect(exitstatus).to eq(0) if exitstatus
+ expect(out).to include "Using bundler"
+ expect(out).to include "Installing rack 1.0.0"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ context "with a git gem" do
+ let!(:ref) { build_git("foo", "1.0").ref_for("HEAD", 11) }
+
+ before do
+ gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+ end
+
+ it "re-installs installed gems" do
+ foo_lib = default_bundle_path("bundler/gems/foo-1.0-#{ref}/lib/foo.rb")
+
+ bundle! "install"
+ foo_lib.open("w") {|f| f.write("blah blah blah") }
+ bundle! "install --force"
+
+ expect(out).to include "Using bundler"
+ expect(out).to include "Using foo 1.0 from #{lib_path("foo-1.0")} (at master@#{ref[0, 7]})"
+ expect(foo_lib.open(&:read)).to eq("FOO = '1.0'\n")
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "works on first bundle install" do
+ bundle! "install --force"
+
+ expect(out).to include "Using bundler"
+ expect(out).to include "Using foo 1.0 from #{lib_path("foo-1.0")} (at master@#{ref[0, 7]})"
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+ end
+ end
+end
diff --git a/spec/bundler/install/gemfile/eval_gemfile_spec.rb b/spec/bundler/install/gemfile/eval_gemfile_spec.rb
new file mode 100644
index 0000000000..f02223d34d
--- /dev/null
+++ b/spec/bundler/install/gemfile/eval_gemfile_spec.rb
@@ -0,0 +1,67 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+RSpec.describe "bundle install with gemfile that uses eval_gemfile" do
+ before do
+ build_lib("gunks", :path => bundled_app.join("gems/gunks")) do |s|
+ s.name = "gunks"
+ s.version = "0.0.1"
+ end
+ end
+
+ context "eval-ed Gemfile points to an internal gemspec" do
+ before do
+ create_file "Gemfile-other", <<-G
+ gemspec :path => 'gems/gunks'
+ G
+ end
+
+ it "installs the gemspec specified gem" do
+ install_gemfile <<-G
+ eval_gemfile 'Gemfile-other'
+ G
+ expect(out).to include("Resolving dependencies")
+ expect(out).to include("Using gunks 0.0.1 from source at `gems/gunks`")
+ expect(out).to include("Bundle complete")
+ end
+ end
+
+ context "eval-ed Gemfile has relative-path gems" do
+ before do
+ build_lib("a", :path => "gems/a")
+ create_file "nested/Gemfile-nested", <<-G
+ gem "a", :path => "../gems/a"
+ G
+
+ gemfile <<-G
+ eval_gemfile "nested/Gemfile-nested"
+ G
+ end
+
+ it "installs the path gem" do
+ bundle! :install
+ expect(the_bundle).to include_gem("a 1.0")
+ end
+
+ # Make sure that we are properly comparing path based gems between the
+ # parsed lockfile and the evaluated gemfile.
+ it "bundles with --deployment" do
+ bundle! :install
+ bundle! "install --deployment"
+ end
+ end
+
+ context "Gemfile uses gemspec paths after eval-ing a Gemfile" do
+ before { create_file "other/Gemfile-other" }
+
+ it "installs the gemspec specified gem" do
+ install_gemfile <<-G
+ eval_gemfile 'other/Gemfile-other'
+ gemspec :path => 'gems/gunks'
+ G
+ expect(out).to include("Resolving dependencies")
+ expect(out).to include("Using gunks 0.0.1 from source at `gems/gunks`")
+ expect(out).to include("Bundle complete")
+ end
+ end
+end
diff --git a/spec/bundler/install/gemfile/gemspec_spec.rb b/spec/bundler/install/gemfile/gemspec_spec.rb
new file mode 100644
index 0000000000..1ea613c9d2
--- /dev/null
+++ b/spec/bundler/install/gemfile/gemspec_spec.rb
@@ -0,0 +1,563 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+RSpec.describe "bundle install from an existing gemspec" do
+ before(:each) do
+ build_gem "bar", :to_system => true
+ build_gem "bar-dev", :to_system => true
+ end
+
+ it "should install runtime and development dependencies" do
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.write("Gemfile", "source :rubygems\ngemspec")
+ s.add_dependency "bar", "=1.0.0"
+ s.add_development_dependency "bar-dev", "=1.0.0"
+ end
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gemspec :path => '#{tmp.join("foo")}'
+ G
+
+ expect(the_bundle).to include_gems "bar 1.0.0"
+ expect(the_bundle).to include_gems "bar-dev 1.0.0", :groups => :development
+ end
+
+ it "that is hidden should install runtime and development dependencies" do
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.write("Gemfile", "source :rubygems\ngemspec")
+ s.add_dependency "bar", "=1.0.0"
+ s.add_development_dependency "bar-dev", "=1.0.0"
+ end
+ FileUtils.mv tmp.join("foo", "foo.gemspec"), tmp.join("foo", ".gemspec")
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gemspec :path => '#{tmp.join("foo")}'
+ G
+
+ expect(the_bundle).to include_gems "bar 1.0.0"
+ expect(the_bundle).to include_gems "bar-dev 1.0.0", :groups => :development
+ end
+
+ it "should handle a list of requirements" do
+ build_gem "baz", "1.0", :to_system => true
+ build_gem "baz", "1.1", :to_system => true
+
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.write("Gemfile", "source :rubygems\ngemspec")
+ s.add_dependency "baz", ">= 1.0", "< 1.1"
+ end
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gemspec :path => '#{tmp.join("foo")}'
+ G
+
+ expect(the_bundle).to include_gems "baz 1.0"
+ end
+
+ it "should raise if there are no gemspecs available" do
+ build_lib("foo", :path => tmp.join("foo"), :gemspec => false)
+
+ error = install_gemfile(<<-G)
+ source "file://#{gem_repo2}"
+ gemspec :path => '#{tmp.join("foo")}'
+ G
+ expect(error).to match(/There are no gemspecs at #{tmp.join('foo')}/)
+ end
+
+ it "should raise if there are too many gemspecs available" do
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.write("foo2.gemspec", build_spec("foo", "4.0").first.to_ruby)
+ end
+
+ error = install_gemfile(<<-G)
+ source "file://#{gem_repo2}"
+ gemspec :path => '#{tmp.join("foo")}'
+ G
+ expect(error).to match(/There are multiple gemspecs at #{tmp.join('foo')}/)
+ end
+
+ it "should pick a specific gemspec" do
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.write("foo2.gemspec", "")
+ s.add_dependency "bar", "=1.0.0"
+ s.add_development_dependency "bar-dev", "=1.0.0"
+ end
+
+ install_gemfile(<<-G)
+ source "file://#{gem_repo2}"
+ gemspec :path => '#{tmp.join("foo")}', :name => 'foo'
+ G
+
+ expect(the_bundle).to include_gems "bar 1.0.0"
+ expect(the_bundle).to include_gems "bar-dev 1.0.0", :groups => :development
+ end
+
+ it "should use a specific group for development dependencies" do
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.write("foo2.gemspec", "")
+ s.add_dependency "bar", "=1.0.0"
+ s.add_development_dependency "bar-dev", "=1.0.0"
+ end
+
+ install_gemfile(<<-G)
+ source "file://#{gem_repo2}"
+ gemspec :path => '#{tmp.join("foo")}', :name => 'foo', :development_group => :dev
+ G
+
+ expect(the_bundle).to include_gems "bar 1.0.0"
+ expect(the_bundle).not_to include_gems "bar-dev 1.0.0", :groups => :development
+ expect(the_bundle).to include_gems "bar-dev 1.0.0", :groups => :dev
+ end
+
+ it "should match a lockfile even if the gemspec defines development dependencies" do
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.write("Gemfile", "source 'file://#{gem_repo1}'\ngemspec")
+ s.add_dependency "actionpack", "=2.3.2"
+ s.add_development_dependency "rake", "=10.0.2"
+ end
+
+ Dir.chdir(tmp.join("foo")) do
+ bundle "install"
+ # This should really be able to rely on $stderr, but, it's not written
+ # right, so we can't. In fact, this is a bug negation test, and so it'll
+ # ghost pass in future, and will only catch a regression if the message
+ # doesn't change. Exit codes should be used correctly (they can be more
+ # than just 0 and 1).
+ output = bundle("install --deployment")
+ expect(output).not_to match(/You have added to the Gemfile/)
+ expect(output).not_to match(/You have deleted from the Gemfile/)
+ expect(output).not_to match(/install in deployment mode after changing/)
+ end
+ end
+
+ it "should match a lockfile without needing to re-resolve" do
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.add_dependency "rack"
+ end
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gemspec :path => '#{tmp.join("foo")}'
+ G
+
+ bundle! "install", :verbose => true
+ expect(out).to include("Found no changes, using resolution from the lockfile")
+ end
+
+ it "should match a lockfile without needing to re-resolve with development dependencies" do
+ simulate_platform java
+
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.add_dependency "rack"
+ s.add_development_dependency "thin"
+ end
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gemspec :path => '#{tmp.join("foo")}'
+ G
+
+ bundle! "install", :verbose => true
+ expect(out).to include("Found no changes, using resolution from the lockfile")
+ end
+
+ it "should match a lockfile on non-ruby platforms with a transitive platform dependency" do
+ simulate_platform java
+ simulate_ruby_engine "jruby"
+
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.add_dependency "platform_specific"
+ end
+
+ install_gem "platform_specific-1.0-java"
+
+ install_gemfile! <<-G
+ gemspec :path => '#{tmp.join("foo")}'
+ G
+
+ bundle! "update --bundler", :verbose => true
+ expect(the_bundle).to include_gems "foo 1.0", "platform_specific 1.0 JAVA"
+ end
+
+ it "should evaluate the gemspec in its directory" do
+ build_lib("foo", :path => tmp.join("foo"))
+ File.open(tmp.join("foo/foo.gemspec"), "w") do |s|
+ s.write "raise 'ahh' unless Dir.pwd == '#{tmp.join("foo")}'"
+ end
+
+ install_gemfile <<-G
+ gemspec :path => '#{tmp.join("foo")}'
+ G
+ expect(@err).not_to match(/ahh/)
+ end
+
+ it "allows the gemspec to activate other gems" do
+ # see https://github.com/bundler/bundler/issues/5409
+ #
+ # issue was caused by rubygems having an unresolved gem during a require,
+ # so emulate that
+ system_gems %w(rack-1.0.0 rack-0.9.1 rack-obama-1.0)
+
+ build_lib("foo", :path => bundled_app)
+ gemspec = bundled_app("foo.gemspec").read
+ bundled_app("foo.gemspec").open("w") do |f|
+ f.write "#{gemspec.strip}.tap { gem 'rack-obama'; require 'rack-obama' }"
+ end
+
+ install_gemfile! <<-G
+ gemspec
+ G
+
+ expect(the_bundle).to include_gem "foo 1.0"
+ end
+
+ it "allows conflicts" do
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.version = "1.0.0"
+ s.add_dependency "bar", "= 1.0.0"
+ end
+ build_gem "deps", :to_system => true do |s|
+ s.add_dependency "foo", "= 0.0.1"
+ end
+ build_gem "foo", "0.0.1", :to_system => true
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "deps"
+ gemspec :path => '#{tmp.join("foo")}', :name => 'foo'
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0.0"
+ end
+
+ it "does not break Gem.finish_resolve with conflicts", :rubygems => ">= 2" do
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.version = "1.0.0"
+ s.add_dependency "bar", "= 1.0.0"
+ end
+ build_repo2 do
+ build_gem "deps" do |s|
+ s.add_dependency "foo", "= 0.0.1"
+ end
+ build_gem "foo", "0.0.1"
+ end
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo2}"
+ gem "deps"
+ gemspec :path => '#{tmp.join("foo")}', :name => 'foo'
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0.0"
+
+ run! "Gem.finish_resolve; puts 'WIN'"
+ expect(out).to eq("WIN")
+ end
+
+ context "in deployment mode" do
+ context "when the lockfile was not updated after a change to the gemspec's dependencies" do
+ it "reports that installation failed" do
+ build_lib "cocoapods", :path => bundled_app do |s|
+ s.add_dependency "activesupport", ">= 1"
+ end
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gemspec
+ G
+
+ expect(the_bundle).to include_gems("cocoapods 1.0", "activesupport 2.3.5")
+
+ build_lib "cocoapods", :path => bundled_app do |s|
+ s.add_dependency "activesupport", ">= 1.0.1"
+ end
+
+ bundle "install --deployment"
+
+ expect(out).to include("changed")
+ end
+ end
+ end
+
+ context "when child gemspecs conflict with a released gemspec" do
+ before do
+ # build the "parent" gem that depends on another gem in the same repo
+ build_lib "source_conflict", :path => bundled_app do |s|
+ s.add_dependency "rack_middleware"
+ end
+
+ # build the "child" gem that is the same version as a released gem, but
+ # has completely different and conflicting dependency requirements
+ build_lib "rack_middleware", "1.0", :path => bundled_app("rack_middleware") do |s|
+ s.add_dependency "rack", "1.0" # anything other than 0.9.1
+ end
+ end
+
+ it "should install the child gemspec's deps" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gemspec
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+
+ context "with a lockfile and some missing dependencies" do
+ let(:source_uri) { "http://localgemserver.test" }
+
+ context "previously bundled for Ruby" do
+ let(:platform) { "ruby" }
+ let(:explicit_platform) { false }
+
+ before do
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.add_dependency "rack", "=1.0.0"
+ end
+
+ if explicit_platform
+ create_file(
+ tmp.join("foo", "foo-#{platform}.gemspec"),
+ build_spec("foo", "1.0", platform) do
+ dep "rack", "=1.0.0"
+ @spec.authors = "authors"
+ @spec.summary = "summary"
+ end.first.to_ruby
+ )
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ gemspec :path => "../foo"
+ G
+
+ lockfile <<-L
+ PATH
+ remote: ../foo
+ specs:
+ foo (1.0)
+ rack (= 1.0.0)
+
+ GEM
+ remote: #{source_uri}
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ #{generic_local_platform}
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+
+ context "using JRuby with explicit platform" do
+ let(:platform) { "java" }
+ let(:explicit_platform) { true }
+
+ it "should install" do
+ simulate_ruby_engine "jruby" do
+ simulate_platform "java" do
+ results = bundle "install", :artifice => "endpoint"
+ expect(results).to include("Installing rack 1.0.0")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+ end
+ end
+
+ context "using JRuby" do
+ let(:platform) { "java" }
+
+ it "should install" do
+ simulate_ruby_engine "jruby" do
+ simulate_platform "java" do
+ results = bundle "install", :artifice => "endpoint"
+ expect(results).to include("Installing rack 1.0.0")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+ end
+ end
+
+ context "using Windows" do
+ it "should install" do
+ simulate_windows do
+ results = bundle "install", :artifice => "endpoint"
+ expect(results).to include("Installing rack 1.0.0")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+ end
+ end
+
+ context "bundled for ruby and jruby" do
+ let(:platform_specific_type) { :runtime }
+ let(:dependency) { "platform_specific" }
+ before do
+ build_repo2 do
+ build_gem "indirect_platform_specific" do |s|
+ s.add_runtime_dependency "platform_specific"
+ end
+ end
+
+ build_lib "foo", :path => "." do |s|
+ if platform_specific_type == :runtime
+ s.add_runtime_dependency dependency
+ elsif platform_specific_type == :development
+ s.add_development_dependency dependency
+ else
+ raise "wrong dependency type #{platform_specific_type}, can only be :development or :runtime"
+ end
+ end
+
+ %w(ruby jruby).each do |platform|
+ simulate_platform(platform) do
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gemspec
+ G
+ end
+ end
+ end
+
+ context "on ruby" do
+ before do
+ simulate_platform("ruby")
+ bundle :install
+ end
+
+ context "as a runtime dependency" do
+ it "keeps java dependencies in the lockfile" do
+ expect(the_bundle).to include_gems "foo 1.0", "platform_specific 1.0 RUBY"
+ expect(lockfile).to eq strip_whitespace(<<-L)
+ PATH
+ remote: .
+ specs:
+ foo (1.0)
+ platform_specific
+
+ GEM
+ remote: file:#{gem_repo2}/
+ specs:
+ platform_specific (1.0)
+ platform_specific (1.0-java)
+
+ PLATFORMS
+ java
+ ruby
+
+ DEPENDENCIES
+ foo!
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ context "as a development dependency" do
+ let(:platform_specific_type) { :development }
+
+ it "keeps java dependencies in the lockfile" do
+ expect(the_bundle).to include_gems "foo 1.0", "platform_specific 1.0 RUBY"
+ expect(lockfile).to eq strip_whitespace(<<-L)
+ PATH
+ remote: .
+ specs:
+ foo (1.0)
+
+ GEM
+ remote: file:#{gem_repo2}/
+ specs:
+ platform_specific (1.0)
+ platform_specific (1.0-java)
+
+ PLATFORMS
+ java
+ ruby
+
+ DEPENDENCIES
+ foo!
+ platform_specific
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ context "with an indirect platform-specific development dependency" do
+ let(:platform_specific_type) { :development }
+ let(:dependency) { "indirect_platform_specific" }
+
+ it "keeps java dependencies in the lockfile" do
+ expect(the_bundle).to include_gems "foo 1.0", "indirect_platform_specific 1.0", "platform_specific 1.0 RUBY"
+ expect(lockfile).to eq strip_whitespace(<<-L)
+ PATH
+ remote: .
+ specs:
+ foo (1.0)
+
+ GEM
+ remote: file:#{gem_repo2}/
+ specs:
+ indirect_platform_specific (1.0)
+ platform_specific
+ platform_specific (1.0)
+ platform_specific (1.0-java)
+
+ PLATFORMS
+ java
+ ruby
+
+ DEPENDENCIES
+ foo!
+ indirect_platform_specific
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+ end
+ end
+ end
+
+ context "with multiple platforms" do
+ before do
+ build_lib("foo", :path => tmp.join("foo")) do |s|
+ s.version = "1.0.0"
+ s.add_development_dependency "rack"
+ s.write "foo-universal-java.gemspec", build_spec("foo", "1.0.0", "universal-java") {|sj| sj.runtime "rack", "1.0.0" }.first.to_ruby
+ end
+ end
+
+ it "installs the ruby platform gemspec" do
+ simulate_platform "ruby"
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gemspec :path => '#{tmp.join("foo")}', :name => 'foo'
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0.0", "rack 1.0.0"
+ end
+
+ it "installs the ruby platform gemspec and skips dev deps with --without development" do
+ simulate_platform "ruby"
+
+ install_gemfile! <<-G, :without => "development"
+ source "file://#{gem_repo1}"
+ gemspec :path => '#{tmp.join("foo")}', :name => 'foo'
+ G
+
+ expect(the_bundle).to include_gem "foo 1.0.0"
+ expect(the_bundle).not_to include_gem "rack"
+ end
+ end
+end
diff --git a/spec/bundler/install/gemfile/git_spec.rb b/spec/bundler/install/gemfile/git_spec.rb
new file mode 100644
index 0000000000..5868c76570
--- /dev/null
+++ b/spec/bundler/install/gemfile/git_spec.rb
@@ -0,0 +1,1259 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+RSpec.describe "bundle install with git sources" do
+ describe "when floating on master" do
+ before :each do
+ build_git "foo" do |s|
+ s.executables = "foobar"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ git "#{lib_path("foo-1.0")}" do
+ gem 'foo'
+ end
+ G
+ end
+
+ it "fetches gems" do
+ expect(the_bundle).to include_gems("foo 1.0")
+
+ run <<-RUBY
+ require 'foo'
+ puts "WIN" unless defined?(FOO_PREV_REF)
+ RUBY
+
+ expect(out).to eq("WIN")
+ end
+
+ it "caches the git repo" do
+ expect(Dir["#{default_bundle_path}/cache/bundler/git/foo-1.0-*"].size).to eq(1)
+ end
+
+ it "caches the evaluated gemspec" do
+ git = update_git "foo" do |s|
+ s.executables = ["foobar"] # we added this the first time, so keep it now
+ s.files = ["bin/foobar"] # updating git nukes the files list
+ foospec = s.to_ruby.gsub(/s\.files.*/, 's.files = `git ls-files -z`.split("\x0")')
+ s.write "foo.gemspec", foospec
+ end
+
+ bundle "update foo"
+
+ sha = git.ref_for("master", 11)
+ spec_file = default_bundle_path.join("bundler/gems/foo-1.0-#{sha}/foo.gemspec").to_s
+ ruby_code = Gem::Specification.load(spec_file).to_ruby
+ file_code = File.read(spec_file)
+ expect(file_code).to eq(ruby_code)
+ end
+
+ it "does not update the git source implicitly" do
+ update_git "foo"
+
+ in_app_root2 do
+ install_gemfile bundled_app2("Gemfile"), <<-G
+ git "#{lib_path("foo-1.0")}" do
+ gem 'foo'
+ end
+ G
+ end
+
+ in_app_root do
+ run <<-RUBY
+ require 'foo'
+ puts "fail" if defined?(FOO_PREV_REF)
+ RUBY
+
+ expect(out).to be_empty
+ end
+ end
+
+ it "sets up git gem executables on the path" do
+ bundle "exec foobar"
+ expect(out).to eq("1.0")
+ end
+
+ it "complains if pinned specs don't exist in the git repo" do
+ build_git "foo"
+
+ install_gemfile <<-G
+ gem "foo", "1.1", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ expect(out).to include("Source contains 'foo' at: 1.0 ruby")
+ end
+
+ it "complains with version and platform if pinned specs don't exist in the git repo" do
+ simulate_platform "java"
+
+ build_git "only_java" do |s|
+ s.platform = "java"
+ end
+
+ install_gemfile <<-G
+ platforms :jruby do
+ gem "only_java", "1.2", :git => "#{lib_path("only_java-1.0-java")}"
+ end
+ G
+
+ expect(out).to include("Source contains 'only_java' at: 1.0 java")
+ end
+
+ it "complains with multiple versions and platforms if pinned specs don't exist in the git repo" do
+ simulate_platform "java"
+
+ build_git "only_java", "1.0" do |s|
+ s.platform = "java"
+ end
+
+ build_git "only_java", "1.1" do |s|
+ s.platform = "java"
+ s.write "only_java1-0.gemspec", File.read("#{lib_path("only_java-1.0-java")}/only_java.gemspec")
+ end
+
+ install_gemfile <<-G
+ platforms :jruby do
+ gem "only_java", "1.2", :git => "#{lib_path("only_java-1.1-java")}"
+ end
+ G
+
+ expect(out).to include("Source contains 'only_java' at: 1.0 java, 1.1 java")
+ end
+
+ it "still works after moving the application directory" do
+ bundle "install --path vendor/bundle"
+ FileUtils.mv bundled_app, tmp("bundled_app.bck")
+
+ Dir.chdir tmp("bundled_app.bck")
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "can still install after moving the application directory" do
+ bundle "install --path vendor/bundle"
+ FileUtils.mv bundled_app, tmp("bundled_app.bck")
+
+ update_git "foo", "1.1", :path => lib_path("foo-1.0")
+
+ Dir.chdir tmp("bundled_app.bck")
+ gemfile tmp("bundled_app.bck/Gemfile"), <<-G
+ source "file://#{gem_repo1}"
+ git "#{lib_path("foo-1.0")}" do
+ gem 'foo'
+ end
+
+ gem "rack", "1.0"
+ G
+
+ bundle "update foo"
+
+ expect(the_bundle).to include_gems "foo 1.1", "rack 1.0"
+ end
+ end
+
+ describe "with an empty git block" do
+ before do
+ build_git "foo"
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ git "#{lib_path("foo-1.0")}" do
+ # this page left intentionally blank
+ end
+ G
+ end
+
+ it "does not explode" do
+ bundle "install"
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+
+ describe "when specifying a revision" do
+ before(:each) do
+ build_git "foo"
+ @revision = revision_for(lib_path("foo-1.0"))
+ update_git "foo"
+ end
+
+ it "works" do
+ install_gemfile <<-G
+ git "#{lib_path("foo-1.0")}", :ref => "#{@revision}" do
+ gem "foo"
+ end
+ G
+
+ run <<-RUBY
+ require 'foo'
+ puts "WIN" unless defined?(FOO_PREV_REF)
+ RUBY
+
+ expect(out).to eq("WIN")
+ end
+
+ it "works when the revision is a symbol" do
+ install_gemfile <<-G
+ git "#{lib_path("foo-1.0")}", :ref => #{@revision.to_sym.inspect} do
+ gem "foo"
+ end
+ G
+ expect(err).to lack_errors
+
+ run <<-RUBY
+ require 'foo'
+ puts "WIN" unless defined?(FOO_PREV_REF)
+ RUBY
+
+ expect(out).to eq("WIN")
+ end
+ end
+
+ describe "when specifying a branch" do
+ let(:branch) { "branch" }
+ let(:repo) { build_git("foo").path }
+ before(:each) do
+ update_git("foo", :path => repo, :branch => branch)
+ end
+
+ it "works" do
+ install_gemfile <<-G
+ git "#{repo}", :branch => #{branch.dump} do
+ gem "foo"
+ end
+ G
+
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+
+ context "when the branch starts with a `#`" do
+ let(:branch) { "#149/redirect-url-fragment" }
+ it "works" do
+ install_gemfile <<-G
+ git "#{repo}", :branch => #{branch.dump} do
+ gem "foo"
+ end
+ G
+
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+ end
+
+ context "when the branch includes quotes" do
+ let(:branch) { %('") }
+ it "works" do
+ install_gemfile <<-G
+ git "#{repo}", :branch => #{branch.dump} do
+ gem "foo"
+ end
+ G
+
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+ end
+ end
+
+ describe "when specifying a tag" do
+ let(:tag) { "tag" }
+ let(:repo) { build_git("foo").path }
+ before(:each) do
+ update_git("foo", :path => repo, :tag => tag)
+ end
+
+ it "works" do
+ install_gemfile <<-G
+ git "#{repo}", :tag => #{tag.dump} do
+ gem "foo"
+ end
+ G
+
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+
+ context "when the tag starts with a `#`" do
+ let(:tag) { "#149/redirect-url-fragment" }
+ it "works" do
+ install_gemfile <<-G
+ git "#{repo}", :tag => #{tag.dump} do
+ gem "foo"
+ end
+ G
+
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+ end
+
+ context "when the tag includes quotes" do
+ let(:tag) { %('") }
+ it "works" do
+ install_gemfile <<-G
+ git "#{repo}", :tag => #{tag.dump} do
+ gem "foo"
+ end
+ G
+
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+ end
+ end
+
+ describe "when specifying local override" do
+ it "uses the local repository instead of checking a new one out" do
+ # We don't generate it because we actually don't need it
+ # build_git "rack", "0.8"
+
+ build_git "rack", "0.8", :path => lib_path("local-rack") do |s|
+ s.write "lib/rack.rb", "puts :LOCAL"
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ G
+
+ bundle %(config local.rack #{lib_path("local-rack")})
+ bundle :install
+ expect(out).to match(/at #{lib_path('local-rack')}/)
+
+ run "require 'rack'"
+ expect(out).to eq("LOCAL")
+ end
+
+ it "chooses the local repository on runtime" do
+ build_git "rack", "0.8"
+
+ FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
+
+ update_git "rack", "0.8", :path => lib_path("local-rack") do |s|
+ s.write "lib/rack.rb", "puts :LOCAL"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ G
+
+ bundle %(config local.rack #{lib_path("local-rack")})
+ run "require 'rack'"
+ expect(out).to eq("LOCAL")
+ end
+
+ it "unlocks the source when the dependencies have changed while switching to the local" do
+ build_git "rack", "0.8"
+
+ FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
+
+ update_git "rack", "0.8", :path => lib_path("local-rack") do |s|
+ s.write "rack.gemspec", build_spec("rack", "0.8") { runtime "rspec", "> 0" }.first.to_ruby
+ s.write "lib/rack.rb", "puts :LOCAL"
+ end
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ G
+
+ bundle! %(config local.rack #{lib_path("local-rack")})
+ bundle! :install
+ run! "require 'rack'"
+ expect(out).to eq("LOCAL")
+ end
+
+ it "updates specs on runtime" do
+ system_gems "nokogiri-1.4.2"
+
+ build_git "rack", "0.8"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ G
+
+ lockfile0 = File.read(bundled_app("Gemfile.lock"))
+
+ FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
+ update_git "rack", "0.8", :path => lib_path("local-rack") do |s|
+ s.add_dependency "nokogiri", "1.4.2"
+ end
+
+ bundle %(config local.rack #{lib_path("local-rack")})
+ run "require 'rack'"
+
+ lockfile1 = File.read(bundled_app("Gemfile.lock"))
+ expect(lockfile1).not_to eq(lockfile0)
+ end
+
+ it "updates ref on install" do
+ build_git "rack", "0.8"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ G
+
+ lockfile0 = File.read(bundled_app("Gemfile.lock"))
+
+ FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
+ update_git "rack", "0.8", :path => lib_path("local-rack")
+
+ bundle %(config local.rack #{lib_path("local-rack")})
+ bundle :install
+
+ lockfile1 = File.read(bundled_app("Gemfile.lock"))
+ expect(lockfile1).not_to eq(lockfile0)
+ end
+
+ it "explodes if given path does not exist on install" do
+ build_git "rack", "0.8"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ G
+
+ bundle %(config local.rack #{lib_path("local-rack")})
+ bundle :install
+ expect(out).to match(/Cannot use local override for rack-0.8 because #{Regexp.escape(lib_path('local-rack').to_s)} does not exist/)
+ end
+
+ it "explodes if branch is not given on install" do
+ build_git "rack", "0.8"
+ FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}"
+ G
+
+ bundle %(config local.rack #{lib_path("local-rack")})
+ bundle :install
+ expect(out).to match(/cannot use local override/i)
+ end
+
+ it "does not explode if disable_local_branch_check is given" do
+ build_git "rack", "0.8"
+ FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}"
+ G
+
+ bundle %(config local.rack #{lib_path("local-rack")})
+ bundle %(config disable_local_branch_check true)
+ bundle :install
+ expect(out).to match(/Bundle complete!/)
+ end
+
+ it "explodes on different branches on install" do
+ build_git "rack", "0.8"
+
+ FileUtils.cp_r("#{lib_path("rack-0.8")}/.", lib_path("local-rack"))
+
+ update_git "rack", "0.8", :path => lib_path("local-rack"), :branch => "another" do |s|
+ s.write "lib/rack.rb", "puts :LOCAL"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ G
+
+ bundle %(config local.rack #{lib_path("local-rack")})
+ bundle :install
+ expect(out).to match(/is using branch another but Gemfile specifies master/)
+ end
+
+ it "explodes on invalid revision on install" do
+ build_git "rack", "0.8"
+
+ build_git "rack", "0.8", :path => lib_path("local-rack") do |s|
+ s.write "lib/rack.rb", "puts :LOCAL"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}", :branch => "master"
+ G
+
+ bundle %(config local.rack #{lib_path("local-rack")})
+ bundle :install
+ expect(out).to match(/The Gemfile lock is pointing to revision \w+/)
+ end
+ end
+
+ describe "specified inline" do
+ # TODO: Figure out how to write this test so that it is not flaky depending
+ # on the current network situation.
+ # it "supports private git URLs" do
+ # gemfile <<-G
+ # gem "thingy", :git => "git@notthere.fallingsnow.net:somebody/thingy.git"
+ # G
+ #
+ # bundle :install
+ #
+ # # p out
+ # # p err
+ # puts err unless err.empty? # This spec fails randomly every so often
+ # err.should include("notthere.fallingsnow.net")
+ # err.should include("ssh")
+ # end
+
+ it "installs from git even if a newer gem is available elsewhere" do
+ build_git "rack", "0.8"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack-0.8")}"
+ G
+
+ expect(the_bundle).to include_gems "rack 0.8"
+ end
+
+ it "installs dependencies from git even if a newer gem is available elsewhere" do
+ system_gems "rack-1.0.0"
+
+ build_lib "rack", "1.0", :path => lib_path("nested/bar") do |s|
+ s.write "lib/rack.rb", "puts 'WIN OVERRIDE'"
+ end
+
+ build_git "foo", :path => lib_path("nested") do |s|
+ s.add_dependency "rack", "= 1.0"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "foo", :git => "#{lib_path("nested")}"
+ G
+
+ run "require 'rack'"
+ expect(out).to eq("WIN OVERRIDE")
+ end
+
+ it "correctly unlocks when changing to a git source" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "0.9.1"
+ G
+
+ build_git "rack", :path => lib_path("rack")
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "1.0.0", :git => "#{lib_path("rack")}"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "correctly unlocks when changing to a git source without versions" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ build_git "rack", "1.2", :path => lib_path("rack")
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", :git => "#{lib_path("rack")}"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.2"
+ end
+ end
+
+ describe "block syntax" do
+ it "pulls all gems from a git block" do
+ build_lib "omg", :path => lib_path("hi2u/omg")
+ build_lib "hi2u", :path => lib_path("hi2u")
+
+ install_gemfile <<-G
+ path "#{lib_path("hi2u")}" do
+ gem "omg"
+ gem "hi2u"
+ end
+ G
+
+ expect(the_bundle).to include_gems "omg 1.0", "hi2u 1.0"
+ end
+ end
+
+ it "uses a ref if specified" do
+ build_git "foo"
+ @revision = revision_for(lib_path("foo-1.0"))
+ update_git "foo"
+
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}", :ref => "#{@revision}"
+ G
+
+ run <<-RUBY
+ require 'foo'
+ puts "WIN" unless defined?(FOO_PREV_REF)
+ RUBY
+
+ expect(out).to eq("WIN")
+ end
+
+ it "correctly handles cases with invalid gemspecs" do
+ build_git "foo" do |s|
+ s.summary = nil
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ gem "rails", "2.3.2"
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0"
+ expect(the_bundle).to include_gems "rails 2.3.2"
+ end
+
+ it "runs the gemspec in the context of its parent directory" do
+ build_lib "bar", :path => lib_path("foo/bar"), :gemspec => false do |s|
+ s.write lib_path("foo/bar/lib/version.rb"), %(BAR_VERSION = '1.0')
+ s.write "bar.gemspec", <<-G
+ $:.unshift Dir.pwd # For 1.9
+ require 'lib/version'
+ Gem::Specification.new do |s|
+ s.name = 'bar'
+ s.author = 'no one'
+ s.version = BAR_VERSION
+ s.summary = 'Bar'
+ s.files = Dir["lib/**/*.rb"]
+ end
+ G
+ end
+
+ build_git "foo", :path => lib_path("foo") do |s|
+ s.write "bin/foo", ""
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "bar", :git => "#{lib_path("foo")}"
+ gem "rails", "2.3.2"
+ G
+
+ expect(the_bundle).to include_gems "bar 1.0"
+ expect(the_bundle).to include_gems "rails 2.3.2"
+ end
+
+ it "installs from git even if a rubygems gem is present" do
+ build_gem "foo", "1.0", :path => lib_path("fake_foo"), :to_system => true do |s|
+ s.write "lib/foo.rb", "raise 'FAIL'"
+ end
+
+ build_git "foo", "1.0"
+
+ install_gemfile <<-G
+ gem "foo", "1.0", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "fakes the gem out if there is no gemspec" do
+ build_git "foo", :gemspec => false
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "foo", "1.0", :git => "#{lib_path("foo-1.0")}"
+ gem "rails", "2.3.2"
+ G
+
+ expect(the_bundle).to include_gems("foo 1.0")
+ expect(the_bundle).to include_gems("rails 2.3.2")
+ end
+
+ it "catches git errors and spits out useful output" do
+ gemfile <<-G
+ gem "foo", "1.0", :git => "omgomg"
+ G
+
+ bundle :install
+
+ expect(out).to include("Git error:")
+ expect(err).to include("fatal")
+ expect(err).to include("omgomg")
+ end
+
+ it "works when the gem path has spaces in it" do
+ build_git "foo", :path => lib_path("foo space-1.0")
+
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo space-1.0")}"
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "handles repos that have been force-pushed" do
+ build_git "forced", "1.0"
+
+ install_gemfile <<-G
+ git "#{lib_path("forced-1.0")}" do
+ gem 'forced'
+ end
+ G
+ expect(the_bundle).to include_gems "forced 1.0"
+
+ update_git "forced" do |s|
+ s.write "lib/forced.rb", "FORCED = '1.1'"
+ end
+
+ bundle "update"
+ expect(the_bundle).to include_gems "forced 1.1"
+
+ Dir.chdir(lib_path("forced-1.0")) do
+ `git reset --hard HEAD^`
+ end
+
+ bundle "update"
+ expect(the_bundle).to include_gems "forced 1.0"
+ end
+
+ it "ignores submodules if :submodule is not passed" do
+ build_git "submodule", "1.0"
+ build_git "has_submodule", "1.0" do |s|
+ s.add_dependency "submodule"
+ end
+ Dir.chdir(lib_path("has_submodule-1.0")) do
+ sys_exec "git submodule add #{lib_path("submodule-1.0")} submodule-1.0"
+ `git commit -m "submodulator"`
+ end
+
+ install_gemfile <<-G
+ git "#{lib_path("has_submodule-1.0")}" do
+ gem "has_submodule"
+ end
+ G
+ expect(out).to match(/could not find gem 'submodule/i)
+
+ expect(the_bundle).not_to include_gems "has_submodule 1.0"
+ end
+
+ it "handles repos with submodules" do
+ build_git "submodule", "1.0"
+ build_git "has_submodule", "1.0" do |s|
+ s.add_dependency "submodule"
+ end
+ Dir.chdir(lib_path("has_submodule-1.0")) do
+ sys_exec "git submodule add #{lib_path("submodule-1.0")} submodule-1.0"
+ `git commit -m "submodulator"`
+ end
+
+ install_gemfile <<-G
+ git "#{lib_path("has_submodule-1.0")}", :submodules => true do
+ gem "has_submodule"
+ end
+ G
+
+ expect(the_bundle).to include_gems "has_submodule 1.0"
+ end
+
+ it "handles implicit updates when modifying the source info" do
+ git = build_git "foo"
+
+ install_gemfile <<-G
+ git "#{lib_path("foo-1.0")}" do
+ gem "foo"
+ end
+ G
+
+ update_git "foo"
+ update_git "foo"
+
+ install_gemfile <<-G
+ git "#{lib_path("foo-1.0")}", :ref => "#{git.ref_for("HEAD^")}" do
+ gem "foo"
+ end
+ G
+
+ run <<-RUBY
+ require 'foo'
+ puts "WIN" if FOO_PREV_REF == '#{git.ref_for("HEAD^^")}'
+ RUBY
+
+ expect(out).to eq("WIN")
+ end
+
+ it "does not to a remote fetch if the revision is cached locally" do
+ build_git "foo"
+
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ FileUtils.rm_rf(lib_path("foo-1.0"))
+
+ bundle "install"
+ expect(out).not_to match(/updating/i)
+ end
+
+ it "doesn't blow up if bundle install is run twice in a row" do
+ build_git "foo"
+
+ gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ bundle "install"
+ bundle "install"
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+
+ it "prints a friendly error if a file blocks the git repo" do
+ build_git "foo"
+
+ FileUtils.touch(default_bundle_path("bundler"))
+
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ expect(exitstatus).to_not eq(0) if exitstatus
+ expect(out).to include("Bundler could not install a gem because it " \
+ "needs to create a directory, but a file exists " \
+ "- #{default_bundle_path("bundler")}")
+ end
+
+ it "does not duplicate git gem sources" do
+ build_lib "foo", :path => lib_path("nested/foo")
+ build_lib "bar", :path => lib_path("nested/bar")
+
+ build_git "foo", :path => lib_path("nested")
+ build_git "bar", :path => lib_path("nested")
+
+ gemfile <<-G
+ gem "foo", :git => "#{lib_path("nested")}"
+ gem "bar", :git => "#{lib_path("nested")}"
+ G
+
+ bundle "install"
+ expect(File.read(bundled_app("Gemfile.lock")).scan("GIT").size).to eq(1)
+ end
+
+ describe "switching sources" do
+ it "doesn't explode when switching Path to Git sources" do
+ build_gem "foo", "1.0", :to_system => true do |s|
+ s.write "lib/foo.rb", "raise 'fail'"
+ end
+ build_lib "foo", "1.0", :path => lib_path("bar/foo")
+ build_git "bar", "1.0", :path => lib_path("bar") do |s|
+ s.add_dependency "foo"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "bar", :path => "#{lib_path("bar")}"
+ G
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "bar", :git => "#{lib_path("bar")}"
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0", "bar 1.0"
+ end
+
+ it "doesn't explode when switching Gem to Git source" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack-obama"
+ gem "rack", "1.0.0"
+ G
+
+ build_git "rack", "1.0" do |s|
+ s.write "lib/new_file.rb", "puts 'USING GIT'"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack-obama"
+ gem "rack", "1.0.0", :git => "#{lib_path("rack-1.0")}"
+ G
+
+ run "require 'new_file'"
+ expect(out).to eq("USING GIT")
+ end
+ end
+
+ describe "bundle install after the remote has been updated" do
+ it "installs" do
+ build_git "valim"
+
+ install_gemfile <<-G
+ gem "valim", :git => "file://#{lib_path("valim-1.0")}"
+ G
+
+ old_revision = revision_for(lib_path("valim-1.0"))
+ update_git "valim"
+ new_revision = revision_for(lib_path("valim-1.0"))
+
+ lockfile = File.read(bundled_app("Gemfile.lock"))
+ File.open(bundled_app("Gemfile.lock"), "w") do |file|
+ file.puts lockfile.gsub(/revision: #{old_revision}/, "revision: #{new_revision}")
+ end
+
+ bundle "install"
+
+ run <<-R
+ require "valim"
+ puts VALIM_PREV_REF
+ R
+
+ expect(out).to eq(old_revision)
+ end
+
+ it "gives a helpful error message when the remote ref no longer exists" do
+ build_git "foo"
+ revision = revision_for(lib_path("foo-1.0"))
+
+ install_gemfile <<-G
+ gem "foo", :git => "file://#{lib_path("foo-1.0")}", :ref => "#{revision}"
+ G
+ bundle "install"
+ expect(out).to_not match(/Revision.*does not exist/)
+
+ install_gemfile <<-G
+ gem "foo", :git => "file://#{lib_path("foo-1.0")}", :ref => "deadbeef"
+ G
+ bundle "install"
+ expect(out).to include("Revision deadbeef does not exist in the repository")
+ end
+ end
+
+ describe "bundle install --deployment with git sources" do
+ it "works" do
+ build_git "valim", :path => lib_path("valim")
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "valim", "= 1.0", :git => "#{lib_path("valim")}"
+ G
+
+ simulate_new_machine
+
+ bundle "install --deployment"
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+ end
+
+ describe "gem install hooks" do
+ it "runs pre-install hooks" do
+ build_git "foo"
+ gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ File.open(lib_path("install_hooks.rb"), "w") do |h|
+ h.write <<-H
+ require 'rubygems'
+ Gem.pre_install_hooks << lambda do |inst|
+ STDERR.puts "Ran pre-install hook: \#{inst.spec.full_name}"
+ end
+ H
+ end
+
+ bundle :install,
+ :requires => [lib_path("install_hooks.rb")]
+ expect(err).to eq_err("Ran pre-install hook: foo-1.0")
+ end
+
+ it "runs post-install hooks" do
+ build_git "foo"
+ gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ File.open(lib_path("install_hooks.rb"), "w") do |h|
+ h.write <<-H
+ require 'rubygems'
+ Gem.post_install_hooks << lambda do |inst|
+ STDERR.puts "Ran post-install hook: \#{inst.spec.full_name}"
+ end
+ H
+ end
+
+ bundle :install,
+ :requires => [lib_path("install_hooks.rb")]
+ expect(err).to eq_err("Ran post-install hook: foo-1.0")
+ end
+
+ it "complains if the install hook fails" do
+ build_git "foo"
+ gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ File.open(lib_path("install_hooks.rb"), "w") do |h|
+ h.write <<-H
+ require 'rubygems'
+ Gem.pre_install_hooks << lambda do |inst|
+ false
+ end
+ H
+ end
+
+ bundle :install,
+ :requires => [lib_path("install_hooks.rb")]
+ expect(out).to include("failed for foo-1.0")
+ end
+ end
+
+ context "with an extension" do
+ it "installs the extension", :ruby_repo do
+ build_git "foo" do |s|
+ s.add_dependency "rake"
+ s.extensions << "Rakefile"
+ s.write "Rakefile", <<-RUBY
+ task :default do
+ path = File.expand_path("../lib", __FILE__)
+ FileUtils.mkdir_p(path)
+ File.open("\#{path}/foo.rb", "w") do |f|
+ f.puts "FOO = 'YES'"
+ end
+ end
+ RUBY
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ run <<-R
+ require 'foo'
+ puts FOO
+ R
+ expect(out).to eq("YES")
+
+ run! <<-R
+ puts $:.grep(/ext/)
+ R
+ expect(out).to eq(Pathname.glob(system_gem_path("bundler/gems/extensions/**/foo-1.0-*")).first.to_s)
+ end
+
+ it "does not use old extension after ref changes", :ruby_repo do
+ git_reader = build_git "foo", :no_default => true do |s|
+ s.extensions = ["ext/extconf.rb"]
+ s.write "ext/extconf.rb", <<-RUBY
+ require "mkmf"
+ create_makefile("foo")
+ RUBY
+ s.write "ext/foo.c", "void Init_foo() {}"
+ end
+
+ 2.times do |i|
+ Dir.chdir(git_reader.path) do
+ File.open("ext/foo.c", "w") do |file|
+ file.write <<-C
+ #include "ruby.h"
+ VALUE foo() { return INT2FIX(#{i}); }
+ void Init_foo() { rb_define_global_function("foo", &foo, 0); }
+ C
+ end
+ `git commit -m 'commit for iteration #{i}' ext/foo.c`
+ end
+ git_sha = git_reader.ref_for("HEAD")
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "foo", :git => "#{lib_path("foo-1.0")}", :ref => "#{git_sha}"
+ G
+
+ run <<-R
+ require 'foo'
+ puts foo
+ R
+
+ expect(out).to eq(i.to_s)
+ end
+ end
+
+ it "does not prompt to gem install if extension fails" do
+ build_git "foo" do |s|
+ s.add_dependency "rake"
+ s.extensions << "Rakefile"
+ s.write "Rakefile", <<-RUBY
+ task :default do
+ raise
+ end
+ RUBY
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ expect(out).to end_with(<<-M.strip)
+An error occurred while installing foo (1.0), and Bundler cannot continue.
+
+In Gemfile:
+ foo
+ M
+ expect(out).not_to include("gem install foo")
+ end
+
+ it "does not reinstall the extension", :ruby_repo, :rubygems => ">= 2.3.0" do
+ build_git "foo" do |s|
+ s.add_dependency "rake"
+ s.extensions << "Rakefile"
+ s.write "Rakefile", <<-RUBY
+ task :default do
+ path = File.expand_path("../lib", __FILE__)
+ FileUtils.mkdir_p(path)
+ cur_time = Time.now.to_f.to_s
+ File.open("\#{path}/foo.rb", "w") do |f|
+ f.puts "FOO = \#{cur_time}"
+ end
+ end
+ RUBY
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ run! <<-R
+ require 'foo'
+ puts FOO
+ R
+
+ installed_time = out
+ expect(installed_time).to match(/\A\d+\.\d+\z/)
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ run! <<-R
+ require 'foo'
+ puts FOO
+ R
+ expect(out).to eq(installed_time)
+ end
+ end
+
+ it "ignores git environment variables" do
+ build_git "xxxxxx" do |s|
+ s.executables = "xxxxxxbar"
+ end
+
+ Bundler::SharedHelpers.with_clean_git_env do
+ ENV["GIT_DIR"] = "bar"
+ ENV["GIT_WORK_TREE"] = "bar"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ git "#{lib_path("xxxxxx-1.0")}" do
+ gem 'xxxxxx'
+ end
+ G
+
+ expect(exitstatus).to eq(0) if exitstatus
+ expect(ENV["GIT_DIR"]).to eq("bar")
+ expect(ENV["GIT_WORK_TREE"]).to eq("bar")
+ end
+ end
+
+ describe "without git installed" do
+ it "prints a better error message" do
+ build_git "foo"
+
+ install_gemfile <<-G
+ git "#{lib_path("foo-1.0")}" do
+ gem 'foo'
+ end
+ G
+
+ with_path_as("") do
+ bundle "update"
+ end
+ expect(out).to include("You need to install git to be able to use gems from git repositories. For help installing git, please refer to GitHub's tutorial at https://help.github.com/articles/set-up-git")
+ end
+
+ it "installs a packaged git gem successfully" do
+ build_git "foo"
+
+ install_gemfile <<-G
+ git "#{lib_path("foo-1.0")}" do
+ gem 'foo'
+ end
+ G
+ bundle "package --all"
+ simulate_new_machine
+
+ bundle "install", :env => { "PATH" => "" }
+ expect(out).to_not include("You need to install git to be able to use gems from git repositories.")
+ expect(exitstatus).to be_zero if exitstatus
+ end
+ end
+
+ describe "when the git source is overriden with a local git repo" do
+ before do
+ bundle "config --global local.foo #{lib_path("foo")}"
+ end
+
+ describe "and git output is colorized" do
+ before do
+ File.open("#{ENV["HOME"]}/.gitconfig", "w") do |f|
+ f.write("[color]\n\tui = always\n")
+ end
+ end
+
+ it "installs successfully" do
+ build_git "foo", "1.0", :path => lib_path("foo")
+
+ gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo")}", :branch => "master"
+ G
+
+ bundle :install
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+ end
+ end
+
+ context "git sources that include credentials" do
+ context "that are username and password" do
+ let(:credentials) { "user1:password1" }
+
+ it "does not display the password" do
+ install_gemfile <<-G
+ git "https://#{credentials}@github.com/company/private-repo" do
+ gem "foo"
+ end
+ G
+
+ bundle :install
+ expect(out).to_not include("password1")
+ expect(err).to_not include("password1")
+ expect(out).to include("Fetching https://user1@github.com/company/private-repo")
+ end
+ end
+
+ context "that is an oauth token" do
+ let(:credentials) { "oauth_token" }
+
+ it "displays the oauth scheme but not the oauth token" do
+ install_gemfile <<-G
+ git "https://#{credentials}:x-oauth-basic@github.com/company/private-repo" do
+ gem "foo"
+ end
+ G
+
+ bundle :install
+ expect(out).to_not include("oauth_token")
+ expect(err).to_not include("oauth_token")
+ expect(out).to include("Fetching https://x-oauth-basic@github.com/company/private-repo")
+ end
+ end
+ end
+end
diff --git a/spec/bundler/install/gemfile/groups_spec.rb b/spec/bundler/install/gemfile/groups_spec.rb
new file mode 100644
index 0000000000..a3a5eeefdf
--- /dev/null
+++ b/spec/bundler/install/gemfile/groups_spec.rb
@@ -0,0 +1,371 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+RSpec.describe "bundle install with groups" do
+ describe "installing with no options" do
+ before :each do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ group :emo do
+ gem "activesupport", "2.3.5"
+ end
+ gem "thin", :groups => [:emo]
+ G
+ end
+
+ it "installs gems in the default group" do
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "installs gems in a group block into that group" do
+ expect(the_bundle).to include_gems "activesupport 2.3.5"
+
+ load_error_run <<-R, "activesupport", :default
+ require 'activesupport'
+ puts ACTIVESUPPORT
+ R
+
+ expect(err).to eq_err("ZOMG LOAD ERROR")
+ end
+
+ it "installs gems with inline :groups into those groups" do
+ expect(the_bundle).to include_gems "thin 1.0"
+
+ load_error_run <<-R, "thin", :default
+ require 'thin'
+ puts THIN
+ R
+
+ expect(err).to eq_err("ZOMG LOAD ERROR")
+ end
+
+ it "sets up everything if Bundler.setup is used with no groups" do
+ output = run("require 'rack'; puts RACK")
+ expect(output).to eq("1.0.0")
+
+ output = run("require 'activesupport'; puts ACTIVESUPPORT")
+ expect(output).to eq("2.3.5")
+
+ output = run("require 'thin'; puts THIN")
+ expect(output).to eq("1.0")
+ end
+
+ it "removes old groups when new groups are set up" do
+ load_error_run <<-RUBY, "thin", :emo
+ Bundler.setup(:default)
+ require 'thin'
+ puts THIN
+ RUBY
+
+ expect(err).to eq_err("ZOMG LOAD ERROR")
+ end
+
+ it "sets up old groups when they have previously been removed" do
+ output = run <<-RUBY, :emo
+ Bundler.setup(:default)
+ Bundler.setup(:default, :emo)
+ require 'thin'; puts THIN
+ RUBY
+ expect(output).to eq("1.0")
+ end
+ end
+
+ describe "installing --without" do
+ describe "with gems assigned to a single group" do
+ before :each do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ group :emo do
+ gem "activesupport", "2.3.5"
+ end
+ group :debugging, :optional => true do
+ gem "thin"
+ end
+ G
+ end
+
+ it "installs gems in the default group" do
+ bundle :install, :without => "emo"
+ expect(the_bundle).to include_gems "rack 1.0.0", :groups => [:default]
+ end
+
+ it "does not install gems from the excluded group" do
+ bundle :install, :without => "emo"
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5", :groups => [:default]
+ end
+
+ it "does not install gems from the previously excluded group" do
+ bundle :install, :without => "emo"
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5"
+ bundle :install
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5"
+ end
+
+ it "does not say it installed gems from the excluded group" do
+ bundle :install, :without => "emo"
+ expect(out).not_to include("activesupport")
+ end
+
+ it "allows Bundler.setup for specific groups" do
+ bundle :install, :without => "emo"
+ run("require 'rack'; puts RACK", :default)
+ expect(out).to eq("1.0.0")
+ end
+
+ it "does not effect the resolve" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "activesupport"
+ group :emo do
+ gem "rails", "2.3.2"
+ end
+ G
+
+ bundle :install, :without => "emo"
+ expect(the_bundle).to include_gems "activesupport 2.3.2", :groups => [:default]
+ end
+
+ it "still works on a different machine and excludes gems" do
+ bundle :install, :without => "emo"
+
+ simulate_new_machine
+ bundle :install, :without => "emo"
+
+ expect(the_bundle).to include_gems "rack 1.0.0", :groups => [:default]
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5", :groups => [:default]
+ end
+
+ it "still works when BUNDLE_WITHOUT is set" do
+ ENV["BUNDLE_WITHOUT"] = "emo"
+
+ bundle :install
+ expect(out).not_to include("activesupport")
+
+ expect(the_bundle).to include_gems "rack 1.0.0", :groups => [:default]
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5", :groups => [:default]
+
+ ENV["BUNDLE_WITHOUT"] = nil
+ end
+
+ it "clears without when passed an empty list" do
+ bundle :install, :without => "emo"
+
+ bundle 'install --without ""'
+ expect(the_bundle).to include_gems "activesupport 2.3.5"
+ end
+
+ it "doesn't clear without when nothing is passed" do
+ bundle :install, :without => "emo"
+
+ bundle :install
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5"
+ end
+
+ it "does not install gems from the optional group" do
+ bundle :install
+ expect(the_bundle).not_to include_gems "thin 1.0"
+ end
+
+ it "does install gems from the optional group when requested" do
+ bundle :install, :with => "debugging"
+ expect(the_bundle).to include_gems "thin 1.0"
+ end
+
+ it "does install gems from the previously requested group" do
+ bundle :install, :with => "debugging"
+ expect(the_bundle).to include_gems "thin 1.0"
+ bundle :install
+ expect(the_bundle).to include_gems "thin 1.0"
+ end
+
+ it "does install gems from the optional groups requested with BUNDLE_WITH" do
+ ENV["BUNDLE_WITH"] = "debugging"
+ bundle :install
+ expect(the_bundle).to include_gems "thin 1.0"
+ ENV["BUNDLE_WITH"] = nil
+ end
+
+ it "clears with when passed an empty list" do
+ bundle :install, :with => "debugging"
+ bundle 'install --with ""'
+ expect(the_bundle).not_to include_gems "thin 1.0"
+ end
+
+ it "does remove groups from without when passed at with" do
+ bundle :install, :without => "emo"
+ bundle :install, :with => "emo"
+ expect(the_bundle).to include_gems "activesupport 2.3.5"
+ end
+
+ it "does remove groups from with when passed at without" do
+ bundle :install, :with => "debugging"
+ bundle :install, :without => "debugging"
+ expect(the_bundle).not_to include_gems "thin 1.0"
+ end
+
+ it "errors out when passing a group to with and without" do
+ bundle :install, :with => "emo debugging", :without => "emo"
+ expect(out).to include("The offending groups are: emo")
+ end
+
+ it "can add and remove a group at the same time" do
+ bundle :install, :with => "debugging", :without => "emo"
+ expect(the_bundle).to include_gems "thin 1.0"
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5"
+ end
+
+ it "does have no effect when listing a not optional group in with" do
+ bundle :install, :with => "emo"
+ expect(the_bundle).to include_gems "activesupport 2.3.5"
+ end
+
+ it "does have no effect when listing an optional group in without" do
+ bundle :install, :without => "debugging"
+ expect(the_bundle).not_to include_gems "thin 1.0"
+ end
+ end
+
+ describe "with gems assigned to multiple groups" do
+ before :each do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ group :emo, :lolercoaster do
+ gem "activesupport", "2.3.5"
+ end
+ G
+ end
+
+ it "installs gems in the default group" do
+ bundle :install, :without => "emo lolercoaster"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "installs the gem if any of its groups are installed" do
+ bundle "install --without emo"
+ expect(the_bundle).to include_gems "rack 1.0.0", "activesupport 2.3.5"
+ end
+
+ describe "with a gem defined multiple times in different groups" do
+ before :each do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+
+ group :emo do
+ gem "activesupport", "2.3.5"
+ end
+
+ group :lolercoaster do
+ gem "activesupport", "2.3.5"
+ end
+ G
+ end
+
+ it "installs the gem w/ option --without emo" do
+ bundle "install --without emo"
+ expect(the_bundle).to include_gems "activesupport 2.3.5"
+ end
+
+ it "installs the gem w/ option --without lolercoaster" do
+ bundle "install --without lolercoaster"
+ expect(the_bundle).to include_gems "activesupport 2.3.5"
+ end
+
+ it "does not install the gem w/ option --without emo lolercoaster" do
+ bundle "install --without emo lolercoaster"
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5"
+ end
+
+ it "does not install the gem w/ option --without 'emo lolercoaster'" do
+ bundle "install --without 'emo lolercoaster'"
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5"
+ end
+ end
+ end
+
+ describe "nesting groups" do
+ before :each do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ group :emo do
+ group :lolercoaster do
+ gem "activesupport", "2.3.5"
+ end
+ end
+ G
+ end
+
+ it "installs gems in the default group" do
+ bundle :install, :without => "emo lolercoaster"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "installs the gem if any of its groups are installed" do
+ bundle "install --without emo"
+ expect(the_bundle).to include_gems "rack 1.0.0", "activesupport 2.3.5"
+ end
+ end
+ end
+
+ describe "when loading only the default group" do
+ it "should not load all groups" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "activesupport", :groups => :development
+ G
+
+ ruby <<-R
+ require "bundler"
+ Bundler.setup :default
+ Bundler.require :default
+ puts RACK
+ begin
+ require "activesupport"
+ rescue LoadError
+ puts "no activesupport"
+ end
+ R
+
+ expect(out).to include("1.0")
+ expect(out).to include("no activesupport")
+ end
+ end
+
+ describe "when locked and installed with --without" do
+ before(:each) do
+ build_repo2
+ system_gems "rack-0.9.1" do
+ install_gemfile <<-G, :without => :rack
+ source "file://#{gem_repo2}"
+ gem "rack"
+
+ group :rack do
+ gem "rack_middleware"
+ end
+ G
+ end
+ end
+
+ it "uses the correct versions even if --without was used on the original" do
+ expect(the_bundle).to include_gems "rack 0.9.1"
+ expect(the_bundle).not_to include_gems "rack_middleware 1.0"
+ simulate_new_machine
+
+ bundle :install
+
+ expect(the_bundle).to include_gems "rack 0.9.1"
+ expect(the_bundle).to include_gems "rack_middleware 1.0"
+ end
+
+ it "does not hit the remote a second time" do
+ FileUtils.rm_rf gem_repo2
+ bundle "install --without rack"
+ expect(err).to lack_errors
+ end
+ end
+end
diff --git a/spec/bundler/install/gemfile/install_if.rb b/spec/bundler/install/gemfile/install_if.rb
new file mode 100644
index 0000000000..b1717ad583
--- /dev/null
+++ b/spec/bundler/install/gemfile/install_if.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+describe "bundle install with install_if conditionals" do
+ it "follows the install_if DSL" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ install_if(lambda { true }) do
+ gem "activesupport", "2.3.5"
+ end
+ gem "thin", :install_if => false
+ install_if(lambda { false }) do
+ gem "foo"
+ end
+ gem "rack"
+ G
+
+ expect(the_bundle).to include_gems("rack 1.0", "activesupport 2.3.5")
+ expect(the_bundle).not_to include_gems("thin")
+ expect(the_bundle).not_to include_gems("foo")
+
+ lockfile_should_be <<-L
+ GEM
+ remote: file:#{gem_repo1}/
+ specs:
+ activesupport (2.3.5)
+ foo (1.0)
+ rack (1.0.0)
+ thin (1.0)
+ rack
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ activesupport (= 2.3.5)
+ foo
+ rack
+ thin
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+end
diff --git a/spec/bundler/install/gemfile/path_spec.rb b/spec/bundler/install/gemfile/path_spec.rb
new file mode 100644
index 0000000000..a1c41aebbb
--- /dev/null
+++ b/spec/bundler/install/gemfile/path_spec.rb
@@ -0,0 +1,595 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+RSpec.describe "bundle install with explicit source paths" do
+ it "fetches gems" do
+ build_lib "foo"
+
+ install_gemfile <<-G
+ path "#{lib_path("foo-1.0")}"
+ gem 'foo'
+ G
+
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+
+ it "supports pinned paths" do
+ build_lib "foo"
+
+ install_gemfile <<-G
+ gem 'foo', :path => "#{lib_path("foo-1.0")}"
+ G
+
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+
+ it "supports relative paths" do
+ build_lib "foo"
+
+ relative_path = lib_path("foo-1.0").relative_path_from(Pathname.new(Dir.pwd))
+
+ install_gemfile <<-G
+ gem 'foo', :path => "#{relative_path}"
+ G
+
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+
+ it "expands paths" do
+ build_lib "foo"
+
+ relative_path = lib_path("foo-1.0").relative_path_from(Pathname.new("~").expand_path)
+
+ install_gemfile <<-G
+ gem 'foo', :path => "~/#{relative_path}"
+ G
+
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+
+ it "expands paths raise error with not existing user's home dir" do
+ build_lib "foo"
+ username = "some_unexisting_user"
+ relative_path = lib_path("foo-1.0").relative_path_from(Pathname.new("/home/#{username}").expand_path)
+
+ install_gemfile <<-G
+ gem 'foo', :path => "~#{username}/#{relative_path}"
+ G
+ expect(out).to match("There was an error while trying to use the path `~#{username}/#{relative_path}`.")
+ expect(out).to match("user #{username} doesn't exist")
+ end
+
+ it "expands paths relative to Bundler.root" do
+ build_lib "foo", :path => bundled_app("foo-1.0")
+
+ install_gemfile <<-G
+ gem 'foo', :path => "./foo-1.0"
+ G
+
+ bundled_app("subdir").mkpath
+ Dir.chdir(bundled_app("subdir")) do
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+ end
+
+ it "expands paths when comparing locked paths to Gemfile paths" do
+ build_lib "foo", :path => bundled_app("foo-1.0")
+
+ install_gemfile <<-G
+ gem 'foo', :path => File.expand_path("../foo-1.0", __FILE__)
+ G
+
+ bundle "install --frozen"
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+
+ it "installs dependencies from the path even if a newer gem is available elsewhere" do
+ system_gems "rack-1.0.0"
+
+ build_lib "rack", "1.0", :path => lib_path("nested/bar") do |s|
+ s.write "lib/rack.rb", "puts 'WIN OVERRIDE'"
+ end
+
+ build_lib "foo", :path => lib_path("nested") do |s|
+ s.add_dependency "rack", "= 1.0"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "foo", :path => "#{lib_path("nested")}"
+ G
+
+ run "require 'rack'"
+ expect(out).to eq("WIN OVERRIDE")
+ end
+
+ it "works" do
+ build_gem "foo", "1.0.0", :to_system => true do |s|
+ s.write "lib/foo.rb", "puts 'FAIL'"
+ end
+
+ build_lib "omg", "1.0", :path => lib_path("omg") do |s|
+ s.add_dependency "foo"
+ end
+
+ build_lib "foo", "1.0.0", :path => lib_path("omg/foo")
+
+ install_gemfile <<-G
+ gem "omg", :path => "#{lib_path("omg")}"
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "prefers gemspecs closer to the path root" do
+ build_lib "premailer", "1.0.0", :path => lib_path("premailer") do |s|
+ s.write "gemfiles/ruby187.gemspec", <<-G
+ Gem::Specification.new do |s|
+ s.name = 'premailer'
+ s.version = '1.0.0'
+ s.summary = 'Hi'
+ s.authors = 'Me'
+ end
+ G
+ end
+
+ install_gemfile <<-G
+ gem "premailer", :path => "#{lib_path("premailer")}"
+ G
+
+ # Installation of the 'gemfiles' gemspec would fail since it will be unable
+ # to require 'premailer.rb'
+ expect(the_bundle).to include_gems "premailer 1.0.0"
+ end
+
+ it "warns on invalid specs", :rubygems => "1.7" do
+ build_lib "foo"
+
+ gemspec = lib_path("foo-1.0").join("foo.gemspec").to_s
+ File.open(gemspec, "w") do |f|
+ f.write <<-G
+ Gem::Specification.new do |s|
+ s.name = "foo"
+ end
+ G
+ end
+
+ install_gemfile <<-G
+ gem "foo", :path => "#{lib_path("foo-1.0")}"
+ G
+
+ expect(out).to_not include("ERROR REPORT")
+ expect(out).to_not include("Your Gemfile has no gem server sources.")
+ expect(out).to match(/is not valid. Please fix this gemspec./)
+ expect(out).to match(/The validation error was 'missing value for attribute version'/)
+ expect(out).to match(/You have one or more invalid gemspecs that need to be fixed/)
+ end
+
+ it "supports gemspec syntax" do
+ build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ s.add_dependency "rack", "1.0"
+ end
+
+ gemfile = <<-G
+ source "file://#{gem_repo1}"
+ gemspec
+ G
+
+ File.open(lib_path("foo/Gemfile"), "w") {|f| f.puts gemfile }
+
+ Dir.chdir(lib_path("foo")) do
+ bundle "install"
+ expect(the_bundle).to include_gems "foo 1.0"
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+
+ it "supports gemspec syntax with an alternative path" do
+ build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ s.add_dependency "rack", "1.0"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gemspec :path => "#{lib_path("foo")}"
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0"
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+
+ it "doesn't automatically unlock dependencies when using the gemspec syntax" do
+ build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ s.add_dependency "rack", ">= 1.0"
+ end
+
+ Dir.chdir lib_path("foo")
+
+ install_gemfile lib_path("foo/Gemfile"), <<-G
+ source "file://#{gem_repo1}"
+ gemspec
+ G
+
+ build_gem "rack", "1.0.1", :to_system => true
+
+ bundle "install"
+
+ expect(the_bundle).to include_gems "foo 1.0"
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+
+ it "doesn't automatically unlock dependencies when using the gemspec syntax and the gem has development dependencies" do
+ build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ s.add_dependency "rack", ">= 1.0"
+ s.add_development_dependency "activesupport"
+ end
+
+ Dir.chdir lib_path("foo")
+
+ install_gemfile lib_path("foo/Gemfile"), <<-G
+ source "file://#{gem_repo1}"
+ gemspec
+ G
+
+ build_gem "rack", "1.0.1", :to_system => true
+
+ bundle "install"
+
+ expect(the_bundle).to include_gems "foo 1.0"
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+
+ it "raises if there are multiple gemspecs" do
+ build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ s.write "bar.gemspec", build_spec("bar", "1.0").first.to_ruby
+ end
+
+ install_gemfile <<-G
+ gemspec :path => "#{lib_path("foo")}"
+ G
+
+ expect(exitstatus).to eq(15) if exitstatus
+ expect(out).to match(/There are multiple gemspecs/)
+ end
+
+ it "allows :name to be specified to resolve ambiguity" do
+ build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ s.write "bar.gemspec"
+ end
+
+ install_gemfile <<-G
+ gemspec :path => "#{lib_path("foo")}", :name => "foo"
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "sets up executables" do
+ build_lib "foo" do |s|
+ s.executables = "foobar"
+ end
+
+ install_gemfile <<-G
+ path "#{lib_path("foo-1.0")}"
+ gem 'foo'
+ G
+ expect(the_bundle).to include_gems "foo 1.0"
+
+ bundle "exec foobar"
+ expect(out).to eq("1.0")
+ end
+
+ it "handles directories in bin/" do
+ build_lib "foo"
+ lib_path("foo-1.0").join("foo.gemspec").rmtree
+ lib_path("foo-1.0").join("bin/performance").mkpath
+
+ install_gemfile <<-G
+ gem 'foo', '1.0', :path => "#{lib_path("foo-1.0")}"
+ G
+ expect(err).to lack_errors
+ end
+
+ it "removes the .gem file after installing" do
+ build_lib "foo"
+
+ install_gemfile <<-G
+ gem 'foo', :path => "#{lib_path("foo-1.0")}"
+ G
+
+ expect(lib_path("foo-1.0").join("foo-1.0.gem")).not_to exist
+ end
+
+ describe "block syntax" do
+ it "pulls all gems from a path block" do
+ build_lib "omg"
+ build_lib "hi2u"
+
+ install_gemfile <<-G
+ path "#{lib_path}" do
+ gem "omg"
+ gem "hi2u"
+ end
+ G
+
+ expect(the_bundle).to include_gems "omg 1.0", "hi2u 1.0"
+ end
+ end
+
+ it "keeps source pinning" do
+ build_lib "foo", "1.0", :path => lib_path("foo")
+ build_lib "omg", "1.0", :path => lib_path("omg")
+ build_lib "foo", "1.0", :path => lib_path("omg/foo") do |s|
+ s.write "lib/foo.rb", "puts 'FAIL'"
+ end
+
+ install_gemfile <<-G
+ gem "foo", :path => "#{lib_path("foo")}"
+ gem "omg", :path => "#{lib_path("omg")}"
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "works when the path does not have a gemspec" do
+ build_lib "foo", :gemspec => false
+
+ gemfile <<-G
+ gem "foo", "1.0", :path => "#{lib_path("foo-1.0")}"
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0"
+
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "works when the path does not have a gemspec but there is a lockfile" do
+ lockfile <<-L
+ PATH
+ remote: vendor/bar
+ specs:
+
+ GEM
+ remote: http://rubygems.org
+ L
+
+ in_app_root { FileUtils.mkdir_p("vendor/bar") }
+
+ install_gemfile <<-G
+ gem "bar", "1.0.0", path: "vendor/bar", require: "bar/nyard"
+ G
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+
+ context "existing lockfile" do
+ it "rubygems gems don't re-resolve without changes" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack-obama', '1.0'
+ gem 'net-ssh', '1.0'
+ G
+
+ bundle :check, :env => { "DEBUG" => 1 }
+ expect(out).to match(/using resolution from the lockfile/)
+ expect(the_bundle).to include_gems "rack-obama 1.0", "net-ssh 1.0"
+ end
+
+ it "source path gems w/deps don't re-resolve without changes" do
+ build_lib "rack-obama", "1.0", :path => lib_path("omg") do |s|
+ s.add_dependency "yard"
+ end
+
+ build_lib "net-ssh", "1.0", :path => lib_path("omg") do |s|
+ s.add_dependency "yard"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack-obama', :path => "#{lib_path("omg")}"
+ gem 'net-ssh', :path => "#{lib_path("omg")}"
+ G
+
+ bundle :check, :env => { "DEBUG" => 1 }
+ expect(out).to match(/using resolution from the lockfile/)
+ expect(the_bundle).to include_gems "rack-obama 1.0", "net-ssh 1.0"
+ end
+ end
+
+ it "installs executable stubs" do
+ build_lib "foo" do |s|
+ s.executables = ["foo"]
+ end
+
+ install_gemfile <<-G
+ gem "foo", :path => "#{lib_path("foo-1.0")}"
+ G
+
+ bundle "exec foo"
+ expect(out).to eq("1.0")
+ end
+
+ describe "when the gem version in the path is updated" do
+ before :each do
+ build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ s.add_dependency "bar"
+ end
+ build_lib "bar", "1.0", :path => lib_path("foo/bar")
+
+ install_gemfile <<-G
+ gem "foo", :path => "#{lib_path("foo")}"
+ G
+ end
+
+ it "unlocks all gems when the top level gem is updated" do
+ build_lib "foo", "2.0", :path => lib_path("foo") do |s|
+ s.add_dependency "bar"
+ end
+
+ bundle "install"
+
+ expect(the_bundle).to include_gems "foo 2.0", "bar 1.0"
+ end
+
+ it "unlocks all gems when a child dependency gem is updated" do
+ build_lib "bar", "2.0", :path => lib_path("foo/bar")
+
+ bundle "install"
+
+ expect(the_bundle).to include_gems "foo 1.0", "bar 2.0"
+ end
+ end
+
+ describe "when dependencies in the path are updated" do
+ before :each do
+ build_lib "foo", "1.0", :path => lib_path("foo")
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "foo", :path => "#{lib_path("foo")}"
+ G
+ end
+
+ it "gets dependencies that are updated in the path" do
+ build_lib "foo", "1.0", :path => lib_path("foo") do |s|
+ s.add_dependency "rack"
+ end
+
+ bundle "install"
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+
+ describe "switching sources" do
+ it "doesn't switch pinned git sources to rubygems when pinning the parent gem to a path source" do
+ build_gem "foo", "1.0", :to_system => true do |s|
+ s.write "lib/foo.rb", "raise 'fail'"
+ end
+ build_lib "foo", "1.0", :path => lib_path("bar/foo")
+ build_git "bar", "1.0", :path => lib_path("bar") do |s|
+ s.add_dependency "foo"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "bar", :git => "#{lib_path("bar")}"
+ G
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "bar", :path => "#{lib_path("bar")}"
+ G
+
+ expect(the_bundle).to include_gems "foo 1.0", "bar 1.0"
+ end
+
+ it "switches the source when the gem existed in rubygems and the path was already being used for another gem" do
+ build_lib "foo", "1.0", :path => lib_path("foo")
+ build_gem "bar", "1.0", :to_system => true do |s|
+ s.write "lib/bar.rb", "raise 'fail'"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "bar"
+ path "#{lib_path("foo")}" do
+ gem "foo"
+ end
+ G
+
+ build_lib "bar", "1.0", :path => lib_path("foo/bar")
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ path "#{lib_path("foo")}" do
+ gem "foo"
+ gem "bar"
+ end
+ G
+
+ expect(the_bundle).to include_gems "bar 1.0"
+ end
+ end
+
+ describe "when there are both a gemspec and remote gems" do
+ it "doesn't query rubygems for local gemspec name" do
+ build_lib "private_lib", "2.2", :path => lib_path("private_lib")
+ gemfile = <<-G
+ source "http://localgemserver.test"
+ gemspec
+ gem 'rack'
+ G
+ File.open(lib_path("private_lib/Gemfile"), "w") {|f| f.puts gemfile }
+
+ Dir.chdir(lib_path("private_lib")) do
+ bundle :install, :env => { "DEBUG" => 1 }, :artifice => "endpoint"
+ expect(out).to match(%r{^HTTP GET http://localgemserver\.test/api/v1/dependencies\?gems=rack$})
+ expect(out).not_to match(/^HTTP GET.*private_lib/)
+ expect(the_bundle).to include_gems "private_lib 2.2"
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+ end
+
+ describe "gem install hooks" do
+ it "runs pre-install hooks" do
+ build_git "foo"
+ gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ File.open(lib_path("install_hooks.rb"), "w") do |h|
+ h.write <<-H
+ require 'rubygems'
+ Gem.pre_install_hooks << lambda do |inst|
+ STDERR.puts "Ran pre-install hook: \#{inst.spec.full_name}"
+ end
+ H
+ end
+
+ bundle :install,
+ :requires => [lib_path("install_hooks.rb")]
+ expect(err).to eq_err("Ran pre-install hook: foo-1.0")
+ end
+
+ it "runs post-install hooks" do
+ build_git "foo"
+ gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ File.open(lib_path("install_hooks.rb"), "w") do |h|
+ h.write <<-H
+ require 'rubygems'
+ Gem.post_install_hooks << lambda do |inst|
+ STDERR.puts "Ran post-install hook: \#{inst.spec.full_name}"
+ end
+ H
+ end
+
+ bundle :install,
+ :requires => [lib_path("install_hooks.rb")]
+ expect(err).to eq_err("Ran post-install hook: foo-1.0")
+ end
+
+ it "complains if the install hook fails" do
+ build_git "foo"
+ gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ G
+
+ File.open(lib_path("install_hooks.rb"), "w") do |h|
+ h.write <<-H
+ require 'rubygems'
+ Gem.pre_install_hooks << lambda do |inst|
+ false
+ end
+ H
+ end
+
+ bundle :install,
+ :requires => [lib_path("install_hooks.rb")]
+ expect(out).to include("failed for foo-1.0")
+ end
+ end
+end
diff --git a/spec/bundler/install/gemfile/platform_spec.rb b/spec/bundler/install/gemfile/platform_spec.rb
new file mode 100644
index 0000000000..c6eaec7ca6
--- /dev/null
+++ b/spec/bundler/install/gemfile/platform_spec.rb
@@ -0,0 +1,265 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+RSpec.describe "bundle install across platforms" do
+ it "maintains the same lockfile if all gems are compatible across platforms" do
+ lockfile <<-G
+ GEM
+ remote: file:#{gem_repo1}/
+ specs:
+ rack (0.9.1)
+
+ PLATFORMS
+ #{not_local}
+
+ DEPENDENCIES
+ rack
+ G
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack"
+ G
+
+ expect(the_bundle).to include_gems "rack 0.9.1"
+ end
+
+ it "pulls in the correct platform specific gem" do
+ lockfile <<-G
+ GEM
+ remote: file:#{gem_repo1}
+ specs:
+ platform_specific (1.0)
+ platform_specific (1.0-java)
+ platform_specific (1.0-x86-mswin32)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ platform_specific
+ G
+
+ simulate_platform "java"
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "platform_specific"
+ G
+
+ expect(the_bundle).to include_gems "platform_specific 1.0 JAVA"
+ end
+
+ it "works with gems that have different dependencies" do
+ simulate_platform "java"
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "nokogiri"
+ G
+
+ expect(the_bundle).to include_gems "nokogiri 1.4.2 JAVA", "weakling 0.0.3"
+
+ simulate_new_machine
+
+ simulate_platform "ruby"
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "nokogiri"
+ G
+
+ expect(the_bundle).to include_gems "nokogiri 1.4.2"
+ expect(the_bundle).not_to include_gems "weakling"
+ end
+
+ it "works the other way with gems that have different dependencies" do
+ simulate_platform "ruby"
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "nokogiri"
+ G
+
+ simulate_platform "java"
+ bundle "install"
+
+ expect(the_bundle).to include_gems "nokogiri 1.4.2 JAVA", "weakling 0.0.3"
+ end
+
+ it "works with gems that have extra platform-specific runtime dependencies" do
+ simulate_platform x64_mac
+
+ update_repo2 do
+ build_gem "facter", "2.4.6"
+ build_gem "facter", "2.4.6" do |s|
+ s.platform = "universal-darwin"
+ s.add_runtime_dependency "CFPropertyList"
+ end
+ build_gem "CFPropertyList"
+ end
+
+ install_gemfile! <<-G
+ source "file://#{gem_repo2}"
+
+ gem "facter"
+ G
+
+ expect(out).to include "Unable to use the platform-specific (universal-darwin) version of facter (2.4.6) " \
+ "because it has different dependencies from the ruby version. " \
+ "To use the platform-specific version of the gem, run `bundle config specific_platform true` and install again."
+
+ expect(the_bundle).to include_gem "facter 2.4.6"
+ expect(the_bundle).not_to include_gem "CFPropertyList"
+ end
+
+ it "fetches gems again after changing the version of Ruby" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack", "1.0.0"
+ G
+
+ bundle "install --path vendor/bundle"
+
+ new_version = Gem::ConfigMap[:ruby_version] == "1.8" ? "1.9.1" : "1.8"
+ FileUtils.mv(vendored_gems, bundled_app("vendor/bundle", Gem.ruby_engine, new_version))
+
+ bundle "install --path vendor/bundle"
+ expect(vendored_gems("gems/rack-1.0.0")).to exist
+ end
+end
+
+RSpec.describe "bundle install with platform conditionals" do
+ it "installs gems tagged w/ the current platforms" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ platforms :#{local_tag} do
+ gem "nokogiri"
+ end
+ G
+
+ expect(the_bundle).to include_gems "nokogiri 1.4.2"
+ end
+
+ it "does not install gems tagged w/ another platforms" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ platforms :#{not_local_tag} do
+ gem "nokogiri"
+ end
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0"
+ expect(the_bundle).not_to include_gems "nokogiri 1.4.2"
+ end
+
+ it "installs gems tagged w/ the current platforms inline" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "nokogiri", :platforms => :#{local_tag}
+ G
+ expect(the_bundle).to include_gems "nokogiri 1.4.2"
+ end
+
+ it "does not install gems tagged w/ another platforms inline" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "nokogiri", :platforms => :#{not_local_tag}
+ G
+ expect(the_bundle).to include_gems "rack 1.0"
+ expect(the_bundle).not_to include_gems "nokogiri 1.4.2"
+ end
+
+ it "installs gems tagged w/ the current platform inline" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "nokogiri", :platform => :#{local_tag}
+ G
+ expect(the_bundle).to include_gems "nokogiri 1.4.2"
+ end
+
+ it "doesn't install gems tagged w/ another platform inline" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "nokogiri", :platform => :#{not_local_tag}
+ G
+ expect(the_bundle).not_to include_gems "nokogiri 1.4.2"
+ end
+
+ it "does not blow up on sources with all platform-excluded specs" do
+ build_git "foo"
+
+ install_gemfile <<-G
+ platform :#{not_local_tag} do
+ gem "foo", :git => "#{lib_path("foo-1.0")}"
+ end
+ G
+
+ bundle :show
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+
+ it "does not attempt to install gems from :rbx when using --local" do
+ simulate_platform "ruby"
+ simulate_ruby_engine "ruby"
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "some_gem", :platform => :rbx
+ G
+
+ bundle "install --local"
+ expect(out).not_to match(/Could not find gem 'some_gem/)
+ end
+
+ it "does not attempt to install gems from other rubies when using --local" do
+ simulate_platform "ruby"
+ simulate_ruby_engine "ruby"
+ other_ruby_version_tag = RUBY_VERSION =~ /^1\.8/ ? :ruby_19 : :ruby_18
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "some_gem", platform: :#{other_ruby_version_tag}
+ G
+
+ bundle "install --local"
+ expect(out).not_to match(/Could not find gem 'some_gem/)
+ end
+
+ it "prints a helpful warning when a dependency is unused on any platform" do
+ simulate_platform "ruby"
+ simulate_ruby_engine "ruby"
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack", :platform => [:mingw, :mswin, :x64_mingw, :jruby]
+ G
+
+ bundle! "install"
+
+ expect(out).to include <<-O.strip
+The dependency #{Gem::Dependency.new("rack", ">= 0")} will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`.
+ O
+ end
+end
+
+RSpec.describe "when a gem has no architecture" do
+ it "still installs correctly" do
+ simulate_platform mswin
+
+ gemfile <<-G
+ # Try to install gem with nil arch
+ source "http://localgemserver.test/"
+ gem "rcov"
+ G
+
+ bundle :install, :artifice => "windows"
+ expect(the_bundle).to include_gems "rcov 1.0.0"
+ end
+end
diff --git a/spec/bundler/install/gemfile/ruby_spec.rb b/spec/bundler/install/gemfile/ruby_spec.rb
new file mode 100644
index 0000000000..b9d9683758
--- /dev/null
+++ b/spec/bundler/install/gemfile/ruby_spec.rb
@@ -0,0 +1,109 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+RSpec.describe "ruby requirement" do
+ def locked_ruby_version
+ Bundler::RubyVersion.from_string(Bundler::LockfileParser.new(lockfile).ruby_version)
+ end
+
+ # As discovered by https://github.com/bundler/bundler/issues/4147, there is
+ # no test coverage to ensure that adding a gem is possible with a ruby
+ # requirement. This test verifies the fix, committed in bfbad5c5.
+ it "allows adding gems" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "#{RUBY_VERSION}"
+ gem "rack"
+ G
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "#{RUBY_VERSION}"
+ gem "rack"
+ gem "rack-obama"
+ G
+
+ expect(exitstatus).to eq(0) if exitstatus
+ expect(the_bundle).to include_gems "rack-obama 1.0"
+ end
+
+ it "allows removing the ruby version requirement" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby "~> #{RUBY_VERSION}"
+ gem "rack"
+ G
+
+ expect(lockfile).to include("RUBY VERSION")
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(lockfile).not_to include("RUBY VERSION")
+ end
+
+ it "allows changing the ruby version requirement to something compatible" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby ">= 1.0.0"
+ gem "rack"
+ G
+
+ expect(locked_ruby_version).to eq(Bundler::RubyVersion.system)
+
+ simulate_ruby_version "5100"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby ">= 1.0.1"
+ gem "rack"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(locked_ruby_version).to eq(Bundler::RubyVersion.system)
+ end
+
+ it "allows changing the ruby version requirement to something incompatible" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby ">= 1.0.0"
+ gem "rack"
+ G
+
+ expect(locked_ruby_version).to eq(Bundler::RubyVersion.system)
+
+ simulate_ruby_version "5100"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby ">= 5000.0"
+ gem "rack"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(locked_ruby_version.versions).to eq(["5100"])
+ end
+
+ it "allows requirements with trailing whitespace" do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ ruby "#{RUBY_VERSION}\\n \t\\n"
+ gem "rack"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "fails gracefully with malformed requirements" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ ruby ">= 0", "-.\\0"
+ gem "rack"
+ G
+
+ expect(out).to include("There was an error parsing") # i.e. DSL error, not error template
+ end
+end
diff --git a/spec/bundler/install/gemfile/sources_spec.rb b/spec/bundler/install/gemfile/sources_spec.rb
new file mode 100644
index 0000000000..c5375b4abf
--- /dev/null
+++ b/spec/bundler/install/gemfile/sources_spec.rb
@@ -0,0 +1,518 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+RSpec.describe "bundle install with gems on multiple sources" do
+ # repo1 is built automatically before all of the specs run
+ # it contains rack-obama 1.0.0 and rack 0.9.1 & 1.0.0 amongst other gems
+
+ context "without source affinity" do
+ before do
+ # Oh no! Someone evil is trying to hijack rack :(
+ # need this to be broken to check for correct source ordering
+ build_repo gem_repo3 do
+ build_gem "rack", repo3_rack_version do |s|
+ s.write "lib/rack.rb", "RACK = 'FAIL'"
+ end
+ end
+ end
+
+ context "with multiple toplevel sources" do
+ let(:repo3_rack_version) { "1.0.0" }
+
+ before do
+ gemfile <<-G
+ source "file://#{gem_repo3}"
+ source "file://#{gem_repo1}"
+ gem "rack-obama"
+ gem "rack"
+ G
+ bundle "config major_deprecations true"
+ end
+
+ it "warns about ambiguous gems, but installs anyway, prioritizing sources last to first" do
+ bundle :install
+
+ expect(out).to have_major_deprecation a_string_including("Your Gemfile contains multiple primary sources.")
+ expect(out).to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(out).to include("Installed from: file:#{gem_repo1}")
+ expect(the_bundle).to include_gems("rack-obama 1.0.0", "rack 1.0.0", :source => "remote1")
+ end
+
+ it "errors when disable_multisource is set" do
+ bundle "config disable_multisource true"
+ bundle :install
+ expect(out).to include("Each source after the first must include a block")
+ expect(exitstatus).to eq(4) if exitstatus
+ end
+ end
+
+ context "when different versions of the same gem are in multiple sources" do
+ let(:repo3_rack_version) { "1.2" }
+
+ before do
+ gemfile <<-G
+ source "file://#{gem_repo3}"
+ source "file://#{gem_repo1}"
+ gem "rack-obama"
+ gem "rack", "1.0.0" # force it to install the working version in repo1
+ G
+ bundle "config major_deprecations true"
+ end
+
+ it "warns about ambiguous gems, but installs anyway" do
+ bundle :install
+
+ expect(out).to have_major_deprecation a_string_including("Your Gemfile contains multiple primary sources.")
+ expect(out).to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(out).to include("Installed from: file:#{gem_repo1}")
+ expect(the_bundle).to include_gems("rack-obama 1.0.0", "rack 1.0.0", :source => "remote1")
+ end
+ end
+ end
+
+ context "with source affinity" do
+ context "with sources given by a block" do
+ before do
+ # Oh no! Someone evil is trying to hijack rack :(
+ # need this to be broken to check for correct source ordering
+ build_repo gem_repo3 do
+ build_gem "rack", "1.0.0" do |s|
+ s.write "lib/rack.rb", "RACK = 'FAIL'"
+ end
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo3}"
+ source "file://#{gem_repo1}" do
+ gem "thin" # comes first to test name sorting
+ gem "rack"
+ end
+ gem "rack-obama" # shoud come from repo3!
+ G
+ end
+
+ it "installs the gems without any warning" do
+ bundle :install
+ expect(out).not_to include("Warning")
+ expect(the_bundle).to include_gems("rack-obama 1.0.0")
+ expect(the_bundle).to include_gems("rack 1.0.0", :source => "remote1")
+ end
+
+ it "can cache and deploy" do
+ bundle :package
+
+ expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ expect(bundled_app("vendor/cache/rack-obama-1.0.gem")).to exist
+
+ bundle "install --deployment"
+
+ expect(exitstatus).to eq(0) if exitstatus
+ expect(the_bundle).to include_gems("rack-obama 1.0.0", "rack 1.0.0")
+ end
+ end
+
+ context "with sources set by an option" do
+ before do
+ # Oh no! Someone evil is trying to hijack rack :(
+ # need this to be broken to check for correct source ordering
+ build_repo gem_repo3 do
+ build_gem "rack", "1.0.0" do |s|
+ s.write "lib/rack.rb", "RACK = 'FAIL'"
+ end
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo3}"
+ gem "rack-obama" # should come from repo3!
+ gem "rack", :source => "file://#{gem_repo1}"
+ G
+ end
+
+ it "installs the gems without any warning" do
+ bundle :install
+ expect(out).not_to include("Warning")
+ expect(the_bundle).to include_gems("rack-obama 1.0.0", "rack 1.0.0")
+ end
+ end
+
+ context "with an indirect dependency" do
+ before do
+ build_repo gem_repo3 do
+ build_gem "depends_on_rack", "1.0.1" do |s|
+ s.add_dependency "rack"
+ end
+ end
+ end
+
+ context "when the indirect dependency is in the pinned source" do
+ before do
+ # we need a working rack gem in repo3
+ update_repo gem_repo3 do
+ build_gem "rack", "1.0.0"
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ source "file://#{gem_repo3}" do
+ gem "depends_on_rack"
+ end
+ G
+ end
+
+ context "and not in any other sources" do
+ before do
+ build_repo(gem_repo2) {}
+ end
+
+ it "installs from the same source without any warning" do
+ bundle :install
+ expect(out).not_to include("Warning")
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0")
+ end
+ end
+
+ context "and in another source" do
+ before do
+ # need this to be broken to check for correct source ordering
+ build_repo gem_repo2 do
+ build_gem "rack", "1.0.0" do |s|
+ s.write "lib/rack.rb", "RACK = 'FAIL'"
+ end
+ end
+ end
+
+ it "installs from the same source without any warning" do
+ bundle :install
+ expect(out).not_to include("Warning")
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0")
+ end
+ end
+ end
+
+ context "when the indirect dependency is in a different source" do
+ before do
+ # In these tests, we need a working rack gem in repo2 and not repo3
+ build_repo gem_repo2 do
+ build_gem "rack", "1.0.0"
+ end
+ end
+
+ context "and not in any other sources" do
+ before do
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ source "file://#{gem_repo3}" do
+ gem "depends_on_rack"
+ end
+ G
+ end
+
+ it "installs from the other source without any warning" do
+ bundle :install
+ expect(out).not_to include("Warning")
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0")
+ end
+ end
+
+ context "and in yet another source" do
+ before do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ source "file://#{gem_repo2}"
+ source "file://#{gem_repo3}" do
+ gem "depends_on_rack"
+ end
+ G
+ end
+
+ it "installs from the other source and warns about ambiguous gems" do
+ bundle "config major_deprecations true"
+ bundle :install
+ expect(out).to have_major_deprecation a_string_including("Your Gemfile contains multiple primary sources.")
+ expect(out).to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(out).to include("Installed from: file:#{gem_repo2}")
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0")
+ end
+ end
+
+ context "and only the dependency is pinned" do
+ before do
+ # need this to be broken to check for correct source ordering
+ build_repo gem_repo2 do
+ build_gem "rack", "1.0.0" do |s|
+ s.write "lib/rack.rb", "RACK = 'FAIL'"
+ end
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo3}" # contains depends_on_rack
+ source "file://#{gem_repo2}" # contains broken rack
+
+ gem "depends_on_rack" # installed from gem_repo3
+ gem "rack", :source => "file://#{gem_repo1}"
+ G
+ end
+
+ it "installs the dependency from the pinned source without warning" do
+ bundle :install
+
+ expect(out).not_to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0")
+
+ # In https://github.com/bundler/bundler/issues/3585 this failed
+ # when there is already a lock file, and the gems are missing, so try again
+ system_gems []
+ bundle :install
+
+ expect(out).not_to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0")
+ end
+ end
+ end
+ end
+
+ context "with a gem that is only found in the wrong source" do
+ before do
+ build_repo gem_repo3 do
+ build_gem "not_in_repo1", "1.0.0"
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo3}"
+ gem "not_in_repo1", :source => "file://#{gem_repo1}"
+ G
+ end
+
+ it "does not install the gem" do
+ bundle :install
+ expect(out).to include("Could not find gem 'not_in_repo1'")
+ end
+ end
+
+ context "with an existing lockfile" do
+ before do
+ system_gems "rack-0.9.1", "rack-1.0.0"
+
+ lockfile <<-L
+ GEM
+ remote: file:#{gem_repo1}
+ remote: file:#{gem_repo3}
+ specs:
+ rack (0.9.1)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ rack!
+ L
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ source "file://#{gem_repo3}" do
+ gem 'rack'
+ end
+ G
+ end
+
+ # Reproduction of https://github.com/bundler/bundler/issues/3298
+ it "does not unlock the installed gem on exec" do
+ expect(the_bundle).to include_gems("rack 0.9.1")
+ end
+ end
+
+ context "with a path gem in the same Gemfile" do
+ before do
+ build_lib "foo"
+
+ gemfile <<-G
+ gem "rack", :source => "file://#{gem_repo1}"
+ gem "foo", :path => "#{lib_path("foo-1.0")}"
+ G
+ end
+
+ it "does not unlock the non-path gem after install" do
+ bundle :install
+
+ bundle %(exec ruby -e 'puts "OK"')
+
+ expect(out).to include("OK")
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+ end
+ end
+
+ context "when an older version of the same gem also ships with Ruby" do
+ before do
+ system_gems "rack-0.9.1"
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack" # shoud come from repo1!
+ G
+ end
+
+ it "installs the gems without any warning" do
+ bundle :install
+ expect(out).not_to include("Warning")
+ expect(the_bundle).to include_gems("rack 1.0.0")
+ end
+ end
+
+ context "when a single source contains multiple locked gems" do
+ before do
+ # 1. With these gems,
+ build_repo4 do
+ build_gem "foo", "0.1"
+ build_gem "bar", "0.1"
+ end
+
+ # 2. Installing this gemfile will produce...
+ gemfile <<-G
+ source 'file://#{gem_repo1}'
+ gem 'rack'
+ gem 'foo', '~> 0.1', :source => 'file://#{gem_repo4}'
+ gem 'bar', '~> 0.1', :source => 'file://#{gem_repo4}'
+ G
+
+ # 3. this lockfile.
+ lockfile <<-L
+ GEM
+ remote: file:/Users/andre/src/bundler/bundler/tmp/gems/remote1/
+ remote: file:/Users/andre/src/bundler/bundler/tmp/gems/remote4/
+ specs:
+ bar (0.1)
+ foo (0.1)
+ rack (1.0.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ bar (~> 0.1)!
+ foo (~> 0.1)!
+ rack
+ L
+
+ bundle "install --path ../gems/system"
+
+ # 4. Then we add some new versions...
+ update_repo4 do
+ build_gem "foo", "0.2"
+ build_gem "bar", "0.3"
+ end
+ end
+
+ it "allows them to be unlocked separately" do
+ # 5. and install this gemfile, updating only foo.
+ install_gemfile <<-G
+ source 'file://#{gem_repo1}'
+ gem 'rack'
+ gem 'foo', '~> 0.2', :source => 'file://#{gem_repo4}'
+ gem 'bar', '~> 0.1', :source => 'file://#{gem_repo4}'
+ G
+
+ # 6. Which should update foo to 0.2, but not the (locked) bar 0.1
+ expect(the_bundle).to include_gems("foo 0.2")
+ expect(the_bundle).to include_gems("bar 0.1")
+ end
+ end
+
+ context "re-resolving" do
+ context "when there is a mix of sources in the gemfile" do
+ before do
+ build_repo3
+ build_lib "path1"
+ build_lib "path2"
+ build_git "git1"
+ build_git "git2"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rails"
+
+ source "file://#{gem_repo3}" do
+ gem "rack"
+ end
+
+ gem "path1", :path => "#{lib_path("path1-1.0")}"
+ gem "path2", :path => "#{lib_path("path2-1.0")}"
+ gem "git1", :git => "#{lib_path("git1-1.0")}"
+ gem "git2", :git => "#{lib_path("git2-1.0")}"
+ G
+ end
+
+ it "does not re-resolve" do
+ bundle :install, :verbose => true
+ expect(out).to include("using resolution from the lockfile")
+ expect(out).not_to include("re-resolving dependencies")
+ end
+ end
+ end
+
+ context "when a gem is installed to system gems" do
+ before do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ end
+
+ context "and the gemfile changes" do
+ it "is still able to find that gem from remote sources" do
+ source_uri = "file://#{gem_repo1}"
+ second_uri = "file://#{gem_repo4}"
+
+ build_repo4 do
+ build_gem "rack", "2.0.1.1.forked"
+ build_gem "thor", "0.19.1.1.forked"
+ end
+
+ # When this gemfile is installed...
+ gemfile <<-G
+ source "#{source_uri}"
+
+ source "#{second_uri}" do
+ gem "rack", "2.0.1.1.forked"
+ gem "thor"
+ end
+ gem "rack-obama"
+ G
+
+ # It creates this lockfile.
+ lockfile <<-L
+ GEM
+ remote: #{source_uri}/
+ remote: #{second_uri}/
+ specs:
+ rack (2.0.1.1.forked)
+ rack-obama (1.0)
+ rack
+ thor (0.19.1.1.forked)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ rack (= 2.0.1.1.forked)!
+ rack-obama
+ thor!
+ L
+
+ # Then we change the Gemfile by adding a version to thor
+ gemfile <<-G
+ source "#{source_uri}"
+
+ source "#{second_uri}" do
+ gem "rack", "2.0.1.1.forked"
+ gem "thor", "0.19.1.1.forked"
+ end
+ gem "rack-obama"
+ G
+
+ # But we should still be able to find rack 2.0.1.1.forked and install it
+ bundle! :install
+ end
+ end
+ end
+end
diff --git a/spec/bundler/install/gemfile/specific_platform_spec.rb b/spec/bundler/install/gemfile/specific_platform_spec.rb
new file mode 100644
index 0000000000..cc6c82c0ff
--- /dev/null
+++ b/spec/bundler/install/gemfile/specific_platform_spec.rb
@@ -0,0 +1,115 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+RSpec.describe "bundle install with specific_platform enabled" do
+ before do
+ bundle "config specific_platform true"
+
+ build_repo2 do
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1")
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1") {|s| s.platform = "x86_64-linux" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1") {|s| s.platform = "x86-mingw32" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1") {|s| s.platform = "x86-linux" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1") {|s| s.platform = "x64-mingw32" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5.1") {|s| s.platform = "universal-darwin" }
+
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5") {|s| s.platform = "x86_64-linux" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5") {|s| s.platform = "x86-linux" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5") {|s| s.platform = "x64-mingw32" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5") {|s| s.platform = "x86-mingw32" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.5")
+
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.4") {|s| s.platform = "universal-darwin" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.4") {|s| s.platform = "x86_64-linux" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.4") {|s| s.platform = "x86-mingw32" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.4") {|s| s.platform = "x86-linux" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.4") {|s| s.platform = "x64-mingw32" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.4")
+
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.3")
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.3") {|s| s.platform = "x86_64-linux" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.3") {|s| s.platform = "x86-mingw32" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.3") {|s| s.platform = "x86-linux" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.3") {|s| s.platform = "x64-mingw32" }
+ build_gem("google-protobuf", "3.0.0.alpha.5.0.3") {|s| s.platform = "universal-darwin" }
+
+ build_gem("google-protobuf", "3.0.0.alpha.4.0")
+ build_gem("google-protobuf", "3.0.0.alpha.3.1.pre")
+ build_gem("google-protobuf", "3.0.0.alpha.3")
+ build_gem("google-protobuf", "3.0.0.alpha.2.0")
+ build_gem("google-protobuf", "3.0.0.alpha.1.1")
+ build_gem("google-protobuf", "3.0.0.alpha.1.0")
+
+ build_gem("facter", "2.4.6")
+ build_gem("facter", "2.4.6") do |s|
+ s.platform = "universal-darwin"
+ s.add_runtime_dependency "CFPropertyList"
+ end
+ build_gem("CFPropertyList")
+ end
+ end
+
+ let(:google_protobuf) { <<-G }
+ source "file:#{gem_repo2}"
+ gem "google-protobuf"
+ G
+
+ context "when on a darwin machine" do
+ before { simulate_platform "x86_64-darwin-15" }
+
+ it "locks to both the specific darwin platform and ruby" do
+ install_gemfile!(google_protobuf)
+ expect(the_bundle.locked_gems.platforms).to eq([pl("ruby"), pl("x86_64-darwin-15")])
+ expect(the_bundle).to include_gem("google-protobuf 3.0.0.alpha.5.0.5.1 universal-darwin")
+ expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w(
+ google-protobuf-3.0.0.alpha.5.0.5.1
+ google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin
+ ))
+ end
+
+ it "caches both the universal-darwin and ruby gems when --all-platforms is passed" do
+ gemfile(google_protobuf)
+ bundle! "package --all-platforms"
+ expect([cached_gem("google-protobuf-3.0.0.alpha.5.0.5.1"), cached_gem("google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin")]).
+ to all(exist)
+ end
+
+ it "uses the platform-specific gem with extra dependencies" do
+ install_gemfile! <<-G
+ source "file:#{gem_repo2}"
+ gem "facter"
+ G
+
+ expect(the_bundle.locked_gems.platforms).to eq([pl("ruby"), pl("x86_64-darwin-15")])
+ expect(the_bundle).to include_gems("facter 2.4.6 universal-darwin", "CFPropertyList 1.0")
+ expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(["CFPropertyList-1.0",
+ "facter-2.4.6",
+ "facter-2.4.6-universal-darwin"])
+ end
+
+ context "when adding a platform via lock --add_platform" do
+ it "adds the foreign platform" do
+ install_gemfile!(google_protobuf)
+ bundle! "lock --add-platform=#{x64_mingw}"
+
+ expect(the_bundle.locked_gems.platforms).to eq([rb, x64_mingw, pl("x86_64-darwin-15")])
+ expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w(
+ google-protobuf-3.0.0.alpha.5.0.5.1
+ google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin
+ google-protobuf-3.0.0.alpha.5.0.5.1-x64-mingw32
+ ))
+ end
+
+ it "falls back on plain ruby when that version doesnt have a platform-specific gem" do
+ install_gemfile!(google_protobuf)
+ bundle! "lock --add-platform=#{java}"
+
+ expect(the_bundle.locked_gems.platforms).to eq([java, rb, pl("x86_64-darwin-15")])
+ expect(the_bundle.locked_gems.specs.map(&:full_name)).to eq(%w(
+ google-protobuf-3.0.0.alpha.5.0.5.1
+ google-protobuf-3.0.0.alpha.5.0.5.1-universal-darwin
+ ))
+ end
+ end
+ end
+end
diff --git a/spec/bundler/install/gemfile_spec.rb b/spec/bundler/install/gemfile_spec.rb
new file mode 100644
index 0000000000..bc49053081
--- /dev/null
+++ b/spec/bundler/install/gemfile_spec.rb
@@ -0,0 +1,98 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+RSpec.describe "bundle install" do
+ context "with duplicated gems" do
+ it "will display a warning" do
+ install_gemfile <<-G
+ gem 'rails', '~> 4.0.0'
+ gem 'rails', '~> 4.0.0'
+ G
+ expect(out).to include("more than once")
+ end
+ end
+
+ context "with --gemfile" do
+ it "finds the gemfile" do
+ gemfile bundled_app("NotGemfile"), <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ G
+
+ bundle :install, :gemfile => bundled_app("NotGemfile")
+
+ ENV["BUNDLE_GEMFILE"] = "NotGemfile"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+
+ context "with gemfile set via config" do
+ before do
+ gemfile bundled_app("NotGemfile"), <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ G
+
+ bundle "config --local gemfile #{bundled_app("NotGemfile")}"
+ end
+ it "uses the gemfile to install" do
+ bundle "install"
+ bundle "show"
+
+ expect(out).to include("rack (1.0.0)")
+ end
+ it "uses the gemfile while in a subdirectory" do
+ bundled_app("subdir").mkpath
+ Dir.chdir(bundled_app("subdir")) do
+ bundle "install"
+ bundle "show"
+
+ expect(out).to include("rack (1.0.0)")
+ end
+ end
+ end
+
+ context "with deprecated features" do
+ before :each do
+ in_app_root
+ end
+
+ it "reports that lib is an invalid option" do
+ gemfile <<-G
+ gem "rack", :lib => "rack"
+ G
+
+ bundle :install
+ expect(out).to match(/You passed :lib as an option for gem 'rack', but it is invalid/)
+ end
+ end
+
+ context "with engine specified in symbol" do
+ it "does not raise any error parsing Gemfile" do
+ simulate_ruby_version "2.3.0" do
+ simulate_ruby_engine "jruby", "9.1.2.0" do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ ruby "2.3.0", :engine => :jruby, :engine_version => "9.1.2.0"
+ G
+
+ expect(out).to match(/Bundle complete!/)
+ end
+ end
+ end
+
+ it "installation succeeds" do
+ simulate_ruby_version "2.3.0" do
+ simulate_ruby_engine "jruby", "9.1.2.0" do
+ install_gemfile! <<-G
+ source "file://#{gem_repo1}"
+ ruby "2.3.0", :engine => :jruby, :engine_version => "9.1.2.0"
+ gem "rack"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+ end
+ end
+end
diff --git a/spec/bundler/install/gems/compact_index_spec.rb b/spec/bundler/install/gems/compact_index_spec.rb
new file mode 100644
index 0000000000..e9e671105a
--- /dev/null
+++ b/spec/bundler/install/gems/compact_index_spec.rb
@@ -0,0 +1,805 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+RSpec.describe "compact index api" do
+ let(:source_hostname) { "localgemserver.test" }
+ let(:source_uri) { "http://#{source_hostname}" }
+
+ it "should use the API" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle! :install, :artifice => "compact_index"
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "should URI encode gem names" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem " sinatra"
+ G
+
+ bundle :install, :artifice => "compact_index"
+ expect(out).to include("' sinatra' is not a valid gem name because it contains whitespace.")
+ end
+
+ it "should handle nested dependencies" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rails"
+ G
+
+ bundle! :install, :artifice => "compact_index"
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems(
+ "rails 2.3.2",
+ "actionpack 2.3.2",
+ "activerecord 2.3.2",
+ "actionmailer 2.3.2",
+ "activeresource 2.3.2",
+ "activesupport 2.3.2"
+ )
+ end
+
+ it "should handle case sensitivity conflicts" do
+ build_repo4 do
+ build_gem "rack", "1.0" do |s|
+ s.add_runtime_dependency("Rack", "0.1")
+ end
+ build_gem "Rack", "0.1"
+ end
+
+ install_gemfile! <<-G, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo4 }
+ source "#{source_uri}"
+ gem "rack", "1.0"
+ gem "Rack", "0.1"
+ G
+
+ # can't use `include_gems` here since the `require` will conflict on a
+ # case-insensitive FS
+ run! "Bundler.require; puts Gem.loaded_specs.values_at('rack', 'Rack').map(&:full_name)"
+ expect(out).to eq("rack-1.0\nRack-0.1")
+ end
+
+ it "should handle multiple gem dependencies on the same gem" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "net-sftp"
+ G
+
+ bundle! :install, :artifice => "compact_index"
+ expect(the_bundle).to include_gems "net-sftp 1.1.1"
+ end
+
+ it "should use the endpoint when using --deployment" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+ bundle! :install, :artifice => "compact_index"
+
+ bundle "install --deployment", :artifice => "compact_index"
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "handles git dependencies that are in rubygems" do
+ build_git "foo" do |s|
+ s.executables = "foobar"
+ s.add_dependency "rails", "2.3.2"
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ git "file:///#{lib_path("foo-1.0")}" do
+ gem 'foo'
+ end
+ G
+
+ bundle! :install, :artifice => "compact_index"
+
+ expect(the_bundle).to include_gems("rails 2.3.2")
+ end
+
+ it "handles git dependencies that are in rubygems using --deployment" do
+ build_git "foo" do |s|
+ s.executables = "foobar"
+ s.add_dependency "rails", "2.3.2"
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'foo', :git => "file:///#{lib_path("foo-1.0")}"
+ G
+
+ bundle! :install, :artifice => "compact_index"
+
+ bundle "install --deployment", :artifice => "compact_index"
+
+ expect(the_bundle).to include_gems("rails 2.3.2")
+ end
+
+ it "doesn't fail if you only have a git gem with no deps when using --deployment" do
+ build_git "foo"
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'foo', :git => "file:///#{lib_path("foo-1.0")}"
+ G
+
+ bundle "install", :artifice => "compact_index"
+ bundle "install --deployment", :artifice => "compact_index"
+
+ expect(exitstatus).to eq(0) if exitstatus
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+
+ it "falls back when the API errors out" do
+ simulate_platform mswin
+
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rcov"
+ G
+
+ bundle! :install, :artifice => "windows"
+ expect(out).to include("Fetching source index from #{source_uri}")
+ expect(the_bundle).to include_gems "rcov 1.0.0"
+ end
+
+ it "falls back when the API URL returns 403 Forbidden" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle! :install, :verbose => true, :artifice => "compact_index_forbidden"
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "falls back when the versions endpoint has a checksum mismatch" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle! :install, :verbose => true, :artifice => "compact_index_checksum_mismatch"
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(out).to include <<-'WARN'
+The checksum of /versions does not match the checksum provided by the server! Something is wrong (local checksum is "\"d41d8cd98f00b204e9800998ecf8427e\"", was expecting "\"123\"").
+ WARN
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "falls back when the user's home directory does not exist or is not writable" do
+ ENV["HOME"] = nil
+
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle! :install, :artifice => "compact_index"
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "handles host redirects" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle! :install, :artifice => "compact_index_host_redirect"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "handles host redirects without Net::HTTP::Persistent" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ FileUtils.mkdir_p lib_path
+ File.open(lib_path("disable_net_http_persistent.rb"), "w") do |h|
+ h.write <<-H
+ module Kernel
+ alias require_without_disabled_net_http require
+ def require(*args)
+ raise LoadError, 'simulated' if args.first == 'openssl' && !caller.grep(/vendored_persistent/).empty?
+ require_without_disabled_net_http(*args)
+ end
+ end
+ H
+ end
+
+ bundle! :install, :artifice => "compact_index_host_redirect", :requires => [lib_path("disable_net_http_persistent.rb")]
+ expect(out).to_not match(/Too many redirects/)
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "times out when Bundler::Fetcher redirects too much" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle :install, :artifice => "compact_index_redirects"
+ expect(out).to match(/Too many redirects/)
+ end
+
+ context "when --full-index is specified" do
+ it "should use the modern index for install" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle "install --full-index", :artifice => "compact_index"
+ expect(out).to include("Fetching source index from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "should use the modern index for update" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle "update --full-index", :artifice => "compact_index"
+ expect(out).to include("Fetching source index from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+
+ it "fetches again when more dependencies are found in subsequent sources" do
+ build_repo2 do
+ build_gem "back_deps" do |s|
+ s.add_dependency "foo"
+ end
+ FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra"
+ gem "back_deps"
+ G
+
+ bundle! :install, :artifice => "compact_index_extra"
+ expect(the_bundle).to include_gems "back_deps 1.0"
+ end
+
+ it "fetches gem versions even when those gems are already installed" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack", "1.0.0"
+ G
+ bundle! :install, :artifice => "compact_index_extra_api"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+
+ build_repo4 do
+ build_gem "rack", "1.2" do |s|
+ s.executables = "rackup"
+ end
+ end
+
+ gemfile <<-G
+ source "#{source_uri}" do; end
+ source "#{source_uri}/extra"
+ gem "rack", "1.2"
+ G
+ bundle! :install, :artifice => "compact_index_extra_api"
+ expect(the_bundle).to include_gems "rack 1.2"
+ end
+
+ it "considers all possible versions of dependencies from all api gem sources" do
+ # In this scenario, the gem "somegem" only exists in repo4. It depends on specific version of activesupport that
+ # exists only in repo1. There happens also be a version of activesupport in repo4, but not the one that version 1.0.0
+ # of somegem wants. This test makes sure that bundler actually finds version 1.2.3 of active support in the other
+ # repo and installs it.
+ build_repo4 do
+ build_gem "activesupport", "1.2.0"
+ build_gem "somegem", "1.0.0" do |s|
+ s.add_dependency "activesupport", "1.2.3" # This version exists only in repo1
+ end
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra"
+ gem 'somegem', '1.0.0'
+ G
+
+ bundle! :install, :artifice => "compact_index_extra_api"
+
+ expect(the_bundle).to include_gems "somegem 1.0.0"
+ expect(the_bundle).to include_gems "activesupport 1.2.3"
+ end
+
+ it "prints API output properly with back deps" do
+ build_repo2 do
+ build_gem "back_deps" do |s|
+ s.add_dependency "foo"
+ end
+ FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra"
+ gem "back_deps"
+ G
+
+ bundle! :install, :artifice => "compact_index_extra"
+
+ expect(out).to include("Fetching gem metadata from http://localgemserver.test/")
+ expect(out).to include("Fetching source index from http://localgemserver.test/extra")
+ end
+
+ it "does not fetch every spec if the index of gems is large when doing back deps" do
+ build_repo2 do
+ build_gem "back_deps" do |s|
+ s.add_dependency "foo"
+ end
+ build_gem "missing"
+ # need to hit the limit
+ 1.upto(Bundler::Source::Rubygems::API_REQUEST_LIMIT) do |i|
+ build_gem "gem#{i}"
+ end
+
+ FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra"
+ gem "back_deps"
+ G
+
+ bundle! :install, :artifice => "compact_index_extra_missing"
+ expect(the_bundle).to include_gems "back_deps 1.0"
+ end
+
+ it "uses the endpoint if all sources support it" do
+ gemfile <<-G
+ source "#{source_uri}"
+
+ gem 'foo'
+ G
+
+ bundle! :install, :artifice => "compact_index_api_missing"
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "fetches again when more dependencies are found in subsequent sources using --deployment" do
+ build_repo2 do
+ build_gem "back_deps" do |s|
+ s.add_dependency "foo"
+ end
+ FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra"
+ gem "back_deps"
+ G
+
+ bundle! :install, :artifice => "compact_index_extra"
+
+ bundle "install --deployment", :artifice => "compact_index_extra"
+ expect(the_bundle).to include_gems "back_deps 1.0"
+ end
+
+ it "does not refetch if the only unmet dependency is bundler" do
+ gemfile <<-G
+ source "#{source_uri}"
+
+ gem "bundler_dep"
+ G
+
+ bundle! :install, :artifice => "compact_index"
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ end
+
+ it "should install when EndpointSpecification has a bin dir owned by root", :sudo => true do
+ sudo "mkdir -p #{system_gem_path("bin")}"
+ sudo "chown -R root #{system_gem_path("bin")}"
+
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rails"
+ G
+ bundle! :install, :artifice => "compact_index"
+ expect(the_bundle).to include_gems "rails 2.3.2"
+ end
+
+ it "installs the binstubs" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle "install --binstubs", :artifice => "compact_index"
+
+ gembin "rackup"
+ expect(out).to eq("1.0.0")
+ end
+
+ it "installs the bins when using --path and uses autoclean" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle "install --path vendor/bundle", :artifice => "compact_index"
+
+ expect(vendored_gems("bin/rackup")).to exist
+ end
+
+ it "installs the bins when using --path and uses bundle clean" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle "install --path vendor/bundle --no-clean", :artifice => "compact_index"
+
+ expect(vendored_gems("bin/rackup")).to exist
+ end
+
+ it "prints post_install_messages" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'rack-obama'
+ G
+
+ bundle! :install, :artifice => "compact_index"
+ expect(out).to include("Post-install message from rack:")
+ end
+
+ it "should display the post install message for a dependency" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'rack_middleware'
+ G
+
+ bundle! :install, :artifice => "compact_index"
+ expect(out).to include("Post-install message from rack:")
+ expect(out).to include("Rack's post install message")
+ end
+
+ context "when using basic authentication" do
+ let(:user) { "user" }
+ let(:password) { "pass" }
+ let(:basic_auth_source_uri) do
+ uri = URI.parse(source_uri)
+ uri.user = user
+ uri.password = password
+
+ uri
+ end
+
+ it "passes basic authentication details and strips out creds" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ gem "rack"
+ G
+
+ bundle! :install, :artifice => "compact_index_basic_authentication"
+ expect(out).not_to include("#{user}:#{password}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "strips http basic authentication creds for modern index" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ gem "rack"
+ G
+
+ bundle! :install, :artifice => "endopint_marshal_fail_basic_authentication"
+ expect(out).not_to include("#{user}:#{password}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "strips http basic auth creds when it can't reach the server" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ gem "rack"
+ G
+
+ bundle :install, :artifice => "endpoint_500"
+ expect(out).not_to include("#{user}:#{password}")
+ end
+
+ it "strips http basic auth creds when warning about ambiguous sources" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle! :install, :artifice => "compact_index_basic_authentication"
+ expect(out).to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(out).not_to include("#{user}:#{password}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "does not pass the user / password to different hosts on redirect" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ gem "rack"
+ G
+
+ bundle! :install, :artifice => "compact_index_creds_diff_host"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ describe "with authentication details in bundle config" do
+ before do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+ end
+
+ it "reads authentication details by host name from bundle config" do
+ bundle "config #{source_hostname} #{user}:#{password}"
+
+ bundle! :install, :artifice => "compact_index_strict_basic_authentication"
+
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "reads authentication details by full url from bundle config" do
+ # The trailing slash is necessary here; Fetcher canonicalizes the URI.
+ bundle "config #{source_uri}/ #{user}:#{password}"
+
+ bundle! :install, :artifice => "compact_index_strict_basic_authentication"
+
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "should use the API" do
+ bundle "config #{source_hostname} #{user}:#{password}"
+ bundle! :install, :artifice => "compact_index_strict_basic_authentication"
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "prefers auth supplied in the source uri" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ gem "rack"
+ G
+
+ bundle "config #{source_hostname} otheruser:wrong"
+
+ bundle! :install, :artifice => "compact_index_strict_basic_authentication"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "shows instructions if auth is not provided for the source" do
+ bundle :install, :artifice => "compact_index_strict_basic_authentication"
+ expect(out).to include("bundle config #{source_hostname} username:password")
+ end
+
+ it "fails if authentication has already been provided, but failed" do
+ bundle "config #{source_hostname} #{user}:wrong"
+
+ bundle :install, :artifice => "compact_index_strict_basic_authentication"
+ expect(out).to include("Bad username or password")
+ end
+ end
+
+ describe "with no password" do
+ let(:password) { nil }
+
+ it "passes basic authentication details" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ gem "rack"
+ G
+
+ bundle! :install, :artifice => "compact_index_basic_authentication"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+ end
+
+ context "when ruby is compiled without openssl", :ruby_repo do
+ before do
+ # Install a monkeypatch that reproduces the effects of openssl being
+ # missing when the fetcher runs, as happens in real life. The reason
+ # we can't just overwrite openssl.rb is that Artifice uses it.
+ bundled_app("broken_ssl").mkpath
+ bundled_app("broken_ssl/openssl.rb").open("w") do |f|
+ f.write <<-RUBY
+ raise LoadError, "cannot load such file -- openssl"
+ RUBY
+ end
+ end
+
+ it "explains what to do to get it" do
+ gemfile <<-G
+ source "#{source_uri.gsub(/http/, "https")}"
+ gem "rack"
+ G
+
+ bundle :install, :env => { "RUBYOPT" => "-I#{bundled_app("broken_ssl")}" }
+ expect(out).to include("OpenSSL")
+ end
+ end
+
+ context "when SSL certificate verification fails" do
+ it "explains what happened" do
+ # Install a monkeypatch that reproduces the effects of openssl raising
+ # a certificate validation error when Rubygems tries to connect.
+ gemfile <<-G
+ class Net::HTTP
+ def start
+ raise OpenSSL::SSL::SSLError, "certificate verify failed"
+ end
+ end
+
+ source "#{source_uri.gsub(/http/, "https")}"
+ gem "rack"
+ G
+
+ bundle :install
+ expect(out).to match(/could not verify the SSL certificate/i)
+ end
+ end
+
+ context ".gemrc with sources is present" do
+ before do
+ File.open(home(".gemrc"), "w") do |file|
+ file.puts({ :sources => ["https://rubygems.org"] }.to_yaml)
+ end
+ end
+
+ after do
+ home(".gemrc").rmtree
+ end
+
+ it "uses other sources declared in the Gemfile" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'rack'
+ G
+
+ bundle! :install, :artifice => "compact_index_forbidden"
+ end
+ end
+
+ it "performs partial update with a non-empty range" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'rack', '0.9.1'
+ G
+
+ # Initial install creates the cached versions file
+ bundle! :install, :artifice => "compact_index"
+
+ # Update the Gemfile so we can check subsequent install was successful
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'rack', '1.0.0'
+ G
+
+ # Second install should make only a partial request to /versions
+ bundle! :install, :artifice => "compact_index_partial_update"
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "performs partial update while local cache is updated by another process" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'rack'
+ G
+
+ # Create an empty file to trigger a partial download
+ versions = File.join(Bundler.rubygems.user_home, ".bundle", "cache", "compact_index",
+ "localgemserver.test.80.dd34752a738ee965a2a4298dc16db6c5", "versions")
+ FileUtils.mkdir_p(File.dirname(versions))
+ FileUtils.touch(versions)
+
+ bundle! :install, :artifice => "compact_index_concurrent_download"
+
+ expect(File.read(versions)).to start_with("created_at")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "fails gracefully when the source URI has an invalid scheme" do
+ install_gemfile <<-G
+ source "htps://rubygems.org"
+ gem "rack"
+ G
+ expect(exitstatus).to eq(15) if exitstatus
+ expect(out).to end_with(<<-E.strip)
+ The request uri `htps://index.rubygems.org/versions` has an invalid scheme (`htps`). Did you mean `http` or `https`?
+ E
+ end
+
+ describe "checksum validation", :rubygems => ">= 2.3.0" do
+ it "raises when the checksum does not match" do
+ install_gemfile <<-G, :artifice => "compact_index_wrong_gem_checksum"
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ expect(exitstatus).to eq(19) if exitstatus
+ expect(out).
+ to include("Bundler cannot continue installing rack (1.0.0).").
+ and include("The checksum for the downloaded `rack-1.0.0.gem` does not match the checksum given by the server.").
+ and include("This means the contents of the downloaded gem is different from what was uploaded to the server, and could be a potential security issue.").
+ and include("To resolve this issue:").
+ and include("1. delete the downloaded gem located at: `#{system_gem_path}/gems/rack-1.0.0/rack-1.0.0.gem`").
+ and include("2. run `bundle install`").
+ and include("If you wish to continue installing the downloaded gem, and are certain it does not pose a security issue despite the mismatching checksum, do the following:").
+ and include("1. run `bundle config disable_checksum_validation true` to turn off checksum verification").
+ and include("2. run `bundle install`").
+ and match(/\(More info: The expected SHA256 checksum was "#{"ab" * 22}", but the checksum for the downloaded gem was ".+?"\.\)/)
+ end
+
+ it "raises when the checksum is the wrong length" do
+ install_gemfile <<-G, :artifice => "compact_index_wrong_gem_checksum", :env => { "BUNDLER_SPEC_RACK_CHECKSUM" => "checksum!" }
+ source "#{source_uri}"
+ gem "rack"
+ G
+ expect(exitstatus).to eq(5) if exitstatus
+ expect(out).to include("The given checksum for rack-1.0.0 (\"checksum!\") is not a valid SHA256 hexdigest nor base64digest")
+ end
+
+ it "does not raise when disable_checksum_validation is set" do
+ bundle! "config disable_checksum_validation true"
+ install_gemfile! <<-G, :artifice => "compact_index_wrong_gem_checksum"
+ source "#{source_uri}"
+ gem "rack"
+ G
+ end
+ end
+
+ it "works when cache dir is world-writable" do
+ install_gemfile! <<-G, :artifice => "compact_index"
+ File.umask(0000)
+ source "#{source_uri}"
+ gem "rack"
+ G
+ end
+
+ it "doesn't explode when the API dependencies are wrong" do
+ install_gemfile <<-G, :artifice => "compact_index_wrong_dependencies", :env => { "DEBUG" => "true" }
+ source "#{source_uri}"
+ gem "rails"
+ G
+ deps = [Gem::Dependency.new("rake", "= 10.0.2"),
+ Gem::Dependency.new("actionpack", "= 2.3.2"),
+ Gem::Dependency.new("activerecord", "= 2.3.2"),
+ Gem::Dependency.new("actionmailer", "= 2.3.2"),
+ Gem::Dependency.new("activeresource", "= 2.3.2")]
+ expect(out).to include(<<-E.strip).and include("rails-2.3.2 from rubygems remote at #{source_uri}/ has either corrupted API or lockfile dependencies")
+Bundler::APIResponseMismatchError: Downloading rails-2.3.2 revealed dependencies not in the API or the lockfile (#{deps.map(&:to_s).join(", ")}).
+Either installing with `--full-index` or running `bundle update rails` should fix the problem.
+ E
+ end
+
+ it "does not duplicate specs in the lockfile when updating and a dependency is not installed" do
+ install_gemfile! <<-G, :artifice => "compact_index"
+ source "#{source_uri}" do
+ gem "rails"
+ gem "activemerchant"
+ end
+ G
+ gem_command! :uninstall, "activemerchant"
+ bundle! "update rails", :artifice => "compact_index"
+ expect(lockfile.scan(/activemerchant \(/).size).to eq(1)
+ end
+end
diff --git a/spec/bundler/install/gems/dependency_api_spec.rb b/spec/bundler/install/gems/dependency_api_spec.rb
new file mode 100644
index 0000000000..d495490745
--- /dev/null
+++ b/spec/bundler/install/gems/dependency_api_spec.rb
@@ -0,0 +1,671 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+RSpec.describe "gemcutter's dependency API" do
+ let(:source_hostname) { "localgemserver.test" }
+ let(:source_uri) { "http://#{source_hostname}" }
+
+ it "should use the API" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle :install, :artifice => "endpoint"
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "should URI encode gem names" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem " sinatra"
+ G
+
+ bundle :install, :artifice => "endpoint"
+ expect(out).to include("' sinatra' is not a valid gem name because it contains whitespace.")
+ end
+
+ it "should handle nested dependencies" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rails"
+ G
+
+ bundle :install, :artifice => "endpoint"
+ expect(out).to include("Fetching gem metadata from #{source_uri}/...")
+ expect(the_bundle).to include_gems(
+ "rails 2.3.2",
+ "actionpack 2.3.2",
+ "activerecord 2.3.2",
+ "actionmailer 2.3.2",
+ "activeresource 2.3.2",
+ "activesupport 2.3.2"
+ )
+ end
+
+ it "should handle multiple gem dependencies on the same gem" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "net-sftp"
+ G
+
+ bundle :install, :artifice => "endpoint"
+ expect(the_bundle).to include_gems "net-sftp 1.1.1"
+ end
+
+ it "should use the endpoint when using --deployment" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+ bundle :install, :artifice => "endpoint"
+
+ bundle "install --deployment", :artifice => "endpoint"
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "handles git dependencies that are in rubygems" do
+ build_git "foo" do |s|
+ s.executables = "foobar"
+ s.add_dependency "rails", "2.3.2"
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ git "file:///#{lib_path("foo-1.0")}" do
+ gem 'foo'
+ end
+ G
+
+ bundle :install, :artifice => "endpoint"
+
+ expect(the_bundle).to include_gems("rails 2.3.2")
+ end
+
+ it "handles git dependencies that are in rubygems using --deployment" do
+ build_git "foo" do |s|
+ s.executables = "foobar"
+ s.add_dependency "rails", "2.3.2"
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'foo', :git => "file:///#{lib_path("foo-1.0")}"
+ G
+
+ bundle :install, :artifice => "endpoint"
+
+ bundle "install --deployment", :artifice => "endpoint"
+
+ expect(the_bundle).to include_gems("rails 2.3.2")
+ end
+
+ it "doesn't fail if you only have a git gem with no deps when using --deployment" do
+ build_git "foo"
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'foo', :git => "file:///#{lib_path("foo-1.0")}"
+ G
+
+ bundle "install", :artifice => "endpoint"
+ bundle "install --deployment", :artifice => "endpoint"
+
+ expect(exitstatus).to eq(0) if exitstatus
+ expect(the_bundle).to include_gems("foo 1.0")
+ end
+
+ it "falls back when the API errors out" do
+ simulate_platform mswin
+
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rcov"
+ G
+
+ bundle :install, :artifice => "windows"
+ expect(out).to include("Fetching source index from #{source_uri}")
+ expect(the_bundle).to include_gems "rcov 1.0.0"
+ end
+
+ it "falls back when hitting the Gemcutter Dependency Limit" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "activesupport"
+ gem "actionpack"
+ gem "actionmailer"
+ gem "activeresource"
+ gem "thin"
+ gem "rack"
+ gem "rails"
+ G
+ bundle :install, :artifice => "endpoint_fallback"
+ expect(out).to include("Fetching source index from #{source_uri}")
+
+ expect(the_bundle).to include_gems(
+ "activesupport 2.3.2",
+ "actionpack 2.3.2",
+ "actionmailer 2.3.2",
+ "activeresource 2.3.2",
+ "activesupport 2.3.2",
+ "thin 1.0.0",
+ "rack 1.0.0",
+ "rails 2.3.2"
+ )
+ end
+
+ it "falls back when Gemcutter API doesn't return proper Marshal format" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle :install, :verbose => true, :artifice => "endpoint_marshal_fail"
+ expect(out).to include("could not fetch from the dependency API, trying the full index")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "falls back when the API URL returns 403 Forbidden" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle :install, :verbose => true, :artifice => "endpoint_api_forbidden"
+ expect(out).to include("Fetching source index from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "handles host redirects" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle :install, :artifice => "endpoint_host_redirect"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "handles host redirects without Net::HTTP::Persistent" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ FileUtils.mkdir_p lib_path
+ File.open(lib_path("disable_net_http_persistent.rb"), "w") do |h|
+ h.write <<-H
+ module Kernel
+ alias require_without_disabled_net_http require
+ def require(*args)
+ raise LoadError, 'simulated' if args.first == 'openssl' && !caller.grep(/vendored_persistent/).empty?
+ require_without_disabled_net_http(*args)
+ end
+ end
+ H
+ end
+
+ bundle :install, :artifice => "endpoint_host_redirect", :requires => [lib_path("disable_net_http_persistent.rb")]
+ expect(out).to_not match(/Too many redirects/)
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "timeouts when Bundler::Fetcher redirects too much" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle :install, :artifice => "endpoint_redirect"
+ expect(out).to match(/Too many redirects/)
+ end
+
+ context "when --full-index is specified" do
+ it "should use the modern index for install" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle "install --full-index", :artifice => "endpoint"
+ expect(out).to include("Fetching source index from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "should use the modern index for update" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle "update --full-index", :artifice => "endpoint"
+ expect(out).to include("Fetching source index from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+
+ it "fetches again when more dependencies are found in subsequent sources" do
+ build_repo2 do
+ build_gem "back_deps" do |s|
+ s.add_dependency "foo"
+ end
+ FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra"
+ gem "back_deps"
+ G
+
+ bundle :install, :artifice => "endpoint_extra"
+ expect(the_bundle).to include_gems "back_deps 1.0"
+ end
+
+ it "fetches gem versions even when those gems are already installed" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack", "1.0.0"
+ G
+ bundle :install, :artifice => "endpoint_extra_api"
+
+ build_repo4 do
+ build_gem "rack", "1.2" do |s|
+ s.executables = "rackup"
+ end
+ end
+
+ gemfile <<-G
+ source "#{source_uri}" do; end
+ source "#{source_uri}/extra"
+ gem "rack", "1.2"
+ G
+ bundle :install, :artifice => "endpoint_extra_api"
+ expect(the_bundle).to include_gems "rack 1.2"
+ end
+
+ it "considers all possible versions of dependencies from all api gem sources" do
+ # In this scenario, the gem "somegem" only exists in repo4. It depends on specific version of activesupport that
+ # exists only in repo1. There happens also be a version of activesupport in repo4, but not the one that version 1.0.0
+ # of somegem wants. This test makes sure that bundler actually finds version 1.2.3 of active support in the other
+ # repo and installs it.
+ build_repo4 do
+ build_gem "activesupport", "1.2.0"
+ build_gem "somegem", "1.0.0" do |s|
+ s.add_dependency "activesupport", "1.2.3" # This version exists only in repo1
+ end
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra"
+ gem 'somegem', '1.0.0'
+ G
+
+ bundle :install, :artifice => "endpoint_extra_api"
+
+ expect(the_bundle).to include_gems "somegem 1.0.0"
+ expect(the_bundle).to include_gems "activesupport 1.2.3"
+ end
+
+ it "prints API output properly with back deps" do
+ build_repo2 do
+ build_gem "back_deps" do |s|
+ s.add_dependency "foo"
+ end
+ FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra"
+ gem "back_deps"
+ G
+
+ bundle :install, :artifice => "endpoint_extra"
+
+ expect(out).to include("Fetching gem metadata from http://localgemserver.test/..")
+ expect(out).to include("Fetching source index from http://localgemserver.test/extra")
+ end
+
+ it "does not fetch every spec if the index of gems is large when doing back deps" do
+ build_repo2 do
+ build_gem "back_deps" do |s|
+ s.add_dependency "foo"
+ end
+ build_gem "missing"
+ # need to hit the limit
+ 1.upto(Bundler::Source::Rubygems::API_REQUEST_LIMIT) do |i|
+ build_gem "gem#{i}"
+ end
+
+ FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra"
+ gem "back_deps"
+ G
+
+ bundle :install, :artifice => "endpoint_extra_missing"
+ expect(the_bundle).to include_gems "back_deps 1.0"
+ end
+
+ it "uses the endpoint if all sources support it" do
+ gemfile <<-G
+ source "#{source_uri}"
+
+ gem 'foo'
+ G
+
+ bundle :install, :artifice => "endpoint_api_missing"
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "fetches again when more dependencies are found in subsequent sources using --deployment" do
+ build_repo2 do
+ build_gem "back_deps" do |s|
+ s.add_dependency "foo"
+ end
+ FileUtils.rm_rf Dir[gem_repo2("gems/foo-*.gem")]
+ end
+
+ gemfile <<-G
+ source "#{source_uri}"
+ source "#{source_uri}/extra"
+ gem "back_deps"
+ G
+
+ bundle :install, :artifice => "endpoint_extra"
+
+ bundle "install --deployment", :artifice => "endpoint_extra"
+ expect(the_bundle).to include_gems "back_deps 1.0"
+ end
+
+ it "does not refetch if the only unmet dependency is bundler" do
+ gemfile <<-G
+ source "#{source_uri}"
+
+ gem "bundler_dep"
+ G
+
+ bundle :install, :artifice => "endpoint"
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ end
+
+ it "should install when EndpointSpecification has a bin dir owned by root", :sudo => true do
+ sudo "mkdir -p #{system_gem_path("bin")}"
+ sudo "chown -R root #{system_gem_path("bin")}"
+
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rails"
+ G
+ bundle :install, :artifice => "endpoint"
+ expect(the_bundle).to include_gems "rails 2.3.2"
+ end
+
+ it "installs the binstubs" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle "install --binstubs", :artifice => "endpoint"
+
+ gembin "rackup"
+ expect(out).to eq("1.0.0")
+ end
+
+ it "installs the bins when using --path and uses autoclean" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle "install --path vendor/bundle", :artifice => "endpoint"
+
+ expect(vendored_gems("bin/rackup")).to exist
+ end
+
+ it "installs the bins when using --path and uses bundle clean" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+
+ bundle "install --path vendor/bundle --no-clean", :artifice => "endpoint"
+
+ expect(vendored_gems("bin/rackup")).to exist
+ end
+
+ it "prints post_install_messages" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'rack-obama'
+ G
+
+ bundle :install, :artifice => "endpoint"
+ expect(out).to include("Post-install message from rack:")
+ end
+
+ it "should display the post install message for a dependency" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'rack_middleware'
+ G
+
+ bundle :install, :artifice => "endpoint"
+ expect(out).to include("Post-install message from rack:")
+ expect(out).to include("Rack's post install message")
+ end
+
+ context "when using basic authentication" do
+ let(:user) { "user" }
+ let(:password) { "pass" }
+ let(:basic_auth_source_uri) do
+ uri = URI.parse(source_uri)
+ uri.user = user
+ uri.password = password
+
+ uri
+ end
+
+ it "passes basic authentication details and strips out creds" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ gem "rack"
+ G
+
+ bundle :install, :artifice => "endpoint_basic_authentication"
+ expect(out).not_to include("#{user}:#{password}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "strips http basic authentication creds for modern index" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ gem "rack"
+ G
+
+ bundle :install, :artifice => "endopint_marshal_fail_basic_authentication"
+ expect(out).not_to include("#{user}:#{password}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "strips http basic auth creds when it can't reach the server" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ gem "rack"
+ G
+
+ bundle :install, :artifice => "endpoint_500"
+ expect(out).not_to include("#{user}:#{password}")
+ end
+
+ it "strips http basic auth creds when warning about ambiguous sources" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle :install, :artifice => "endpoint_basic_authentication"
+ expect(out).to include("Warning: the gem 'rack' was found in multiple sources.")
+ expect(out).not_to include("#{user}:#{password}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "does not pass the user / password to different hosts on redirect" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ gem "rack"
+ G
+
+ bundle :install, :artifice => "endpoint_creds_diff_host"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ describe "with authentication details in bundle config" do
+ before do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rack"
+ G
+ end
+
+ it "reads authentication details by host name from bundle config" do
+ bundle "config #{source_hostname} #{user}:#{password}"
+
+ bundle :install, :artifice => "endpoint_strict_basic_authentication"
+
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "reads authentication details by full url from bundle config" do
+ # The trailing slash is necessary here; Fetcher canonicalizes the URI.
+ bundle "config #{source_uri}/ #{user}:#{password}"
+
+ bundle :install, :artifice => "endpoint_strict_basic_authentication"
+
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "should use the API" do
+ bundle "config #{source_hostname} #{user}:#{password}"
+ bundle :install, :artifice => "endpoint_strict_basic_authentication"
+ expect(out).to include("Fetching gem metadata from #{source_uri}")
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "prefers auth supplied in the source uri" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ gem "rack"
+ G
+
+ bundle "config #{source_hostname} otheruser:wrong"
+
+ bundle :install, :artifice => "endpoint_strict_basic_authentication"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "shows instructions if auth is not provided for the source" do
+ bundle :install, :artifice => "endpoint_strict_basic_authentication"
+ expect(out).to include("bundle config #{source_hostname} username:password")
+ end
+
+ it "fails if authentication has already been provided, but failed" do
+ bundle "config #{source_hostname} #{user}:wrong"
+
+ bundle :install, :artifice => "endpoint_strict_basic_authentication"
+ expect(out).to include("Bad username or password")
+ end
+ end
+
+ describe "with no password" do
+ let(:password) { nil }
+
+ it "passes basic authentication details" do
+ gemfile <<-G
+ source "#{basic_auth_source_uri}"
+ gem "rack"
+ G
+
+ bundle :install, :artifice => "endpoint_basic_authentication"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+ end
+
+ context "when ruby is compiled without openssl", :ruby_repo do
+ before do
+ # Install a monkeypatch that reproduces the effects of openssl being
+ # missing when the fetcher runs, as happens in real life. The reason
+ # we can't just overwrite openssl.rb is that Artifice uses it.
+ bundled_app("broken_ssl").mkpath
+ bundled_app("broken_ssl/openssl.rb").open("w") do |f|
+ f.write <<-RUBY
+ raise LoadError, "cannot load such file -- openssl"
+ RUBY
+ end
+ end
+
+ it "explains what to do to get it" do
+ gemfile <<-G
+ source "#{source_uri.gsub(/http/, "https")}"
+ gem "rack"
+ G
+
+ bundle :install, :env => { "RUBYOPT" => "-I#{bundled_app("broken_ssl")}" }
+ expect(out).to include("OpenSSL")
+ end
+ end
+
+ context "when SSL certificate verification fails" do
+ it "explains what happened" do
+ # Install a monkeypatch that reproduces the effects of openssl raising
+ # a certificate validation error when Rubygems tries to connect.
+ gemfile <<-G
+ class Net::HTTP
+ def start
+ raise OpenSSL::SSL::SSLError, "certificate verify failed"
+ end
+ end
+
+ source "#{source_uri.gsub(/http/, "https")}"
+ gem "rack"
+ G
+
+ bundle :install
+ expect(out).to match(/could not verify the SSL certificate/i)
+ end
+ end
+
+ context ".gemrc with sources is present" do
+ before do
+ File.open(home(".gemrc"), "w") do |file|
+ file.puts({ :sources => ["https://rubygems.org"] }.to_yaml)
+ end
+ end
+
+ after do
+ home(".gemrc").rmtree
+ end
+
+ it "uses other sources declared in the Gemfile" do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem 'rack'
+ G
+
+ bundle "install", :artifice => "endpoint_marshal_fail"
+
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+ end
+end
diff --git a/spec/bundler/install/gems/env_spec.rb b/spec/bundler/install/gems/env_spec.rb
new file mode 100644
index 0000000000..9b1d8e5424
--- /dev/null
+++ b/spec/bundler/install/gems/env_spec.rb
@@ -0,0 +1,108 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+RSpec.describe "bundle install with ENV conditionals" do
+ describe "when just setting an ENV key as a string" do
+ before :each do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ env "BUNDLER_TEST" do
+ gem "rack"
+ end
+ G
+ end
+
+ it "excludes the gems when the ENV variable is not set" do
+ bundle :install
+ expect(the_bundle).not_to include_gems "rack"
+ end
+
+ it "includes the gems when the ENV variable is set" do
+ ENV["BUNDLER_TEST"] = "1"
+ bundle :install
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+
+ describe "when just setting an ENV key as a symbol" do
+ before :each do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ env :BUNDLER_TEST do
+ gem "rack"
+ end
+ G
+ end
+
+ it "excludes the gems when the ENV variable is not set" do
+ bundle :install
+ expect(the_bundle).not_to include_gems "rack"
+ end
+
+ it "includes the gems when the ENV variable is set" do
+ ENV["BUNDLER_TEST"] = "1"
+ bundle :install
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+
+ describe "when setting a string to match the env" do
+ before :each do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ env "BUNDLER_TEST" => "foo" do
+ gem "rack"
+ end
+ G
+ end
+
+ it "excludes the gems when the ENV variable is not set" do
+ bundle :install
+ expect(the_bundle).not_to include_gems "rack"
+ end
+
+ it "excludes the gems when the ENV variable is set but does not match the condition" do
+ ENV["BUNDLER_TEST"] = "1"
+ bundle :install
+ expect(the_bundle).not_to include_gems "rack"
+ end
+
+ it "includes the gems when the ENV variable is set and matches the condition" do
+ ENV["BUNDLER_TEST"] = "foo"
+ bundle :install
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+
+ describe "when setting a regex to match the env" do
+ before :each do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ env "BUNDLER_TEST" => /foo/ do
+ gem "rack"
+ end
+ G
+ end
+
+ it "excludes the gems when the ENV variable is not set" do
+ bundle :install
+ expect(the_bundle).not_to include_gems "rack"
+ end
+
+ it "excludes the gems when the ENV variable is set but does not match the condition" do
+ ENV["BUNDLER_TEST"] = "fo"
+ bundle :install
+ expect(the_bundle).not_to include_gems "rack"
+ end
+
+ it "includes the gems when the ENV variable is set and matches the condition" do
+ ENV["BUNDLER_TEST"] = "foobar"
+ bundle :install
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+end
diff --git a/spec/bundler/install/gems/flex_spec.rb b/spec/bundler/install/gems/flex_spec.rb
new file mode 100644
index 0000000000..2c2d3c16a1
--- /dev/null
+++ b/spec/bundler/install/gems/flex_spec.rb
@@ -0,0 +1,319 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+RSpec.describe "bundle flex_install" do
+ it "installs the gems as expected" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to be_locked
+ end
+
+ it "installs even when the lockfile is invalid" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to be_locked
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack', '1.0'
+ G
+
+ bundle :install
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).to be_locked
+ end
+
+ it "keeps child dependencies at the same version" do
+ build_repo2
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack-obama"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0", "rack-obama 1.0.0"
+
+ update_repo2
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack-obama", "1.0"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0", "rack-obama 1.0.0"
+ end
+
+ describe "adding new gems" do
+ it "installs added gems without updating previously installed gems" do
+ build_repo2
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem 'rack'
+ G
+
+ update_repo2
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem 'rack'
+ gem 'activesupport', '2.3.5'
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0", "activesupport 2.3.5"
+ end
+
+ it "keeps child dependencies pinned" do
+ build_repo2
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack-obama"
+ G
+
+ update_repo2
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack-obama"
+ gem "thin"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0", "rack-obama 1.0", "thin 1.0"
+ end
+ end
+
+ describe "removing gems" do
+ it "removes gems without changing the versions of remaining gems" do
+ build_repo2
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem 'rack'
+ gem 'activesupport', '2.3.5'
+ G
+
+ update_repo2
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem 'rack'
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5"
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem 'rack'
+ gem 'activesupport', '2.3.2'
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0", "activesupport 2.3.2"
+ end
+
+ it "removes top level dependencies when removed from the Gemfile while leaving other dependencies intact" do
+ build_repo2
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem 'rack'
+ gem 'activesupport', '2.3.5'
+ G
+
+ update_repo2
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem 'rack'
+ G
+
+ expect(the_bundle).not_to include_gems "activesupport 2.3.5"
+ end
+
+ it "removes child dependencies" do
+ build_repo2
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem 'rack-obama'
+ gem 'activesupport'
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0.0", "rack-obama 1.0.0", "activesupport 2.3.5"
+
+ update_repo2
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem 'activesupport'
+ G
+
+ expect(the_bundle).to include_gems "activesupport 2.3.5"
+ expect(the_bundle).not_to include_gems "rack-obama", "rack"
+ end
+ end
+
+ describe "when Gemfile conflicts with lockfile" do
+ before(:each) do
+ build_repo2
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack_middleware"
+ G
+
+ expect(the_bundle).to include_gems "rack_middleware 1.0", "rack 0.9.1"
+
+ build_repo2
+ update_repo2 do
+ build_gem "rack-obama", "2.0" do |s|
+ s.add_dependency "rack", "=1.2"
+ end
+ build_gem "rack_middleware", "2.0" do |s|
+ s.add_dependency "rack", ">=1.0"
+ end
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rack-obama", "2.0"
+ gem "rack_middleware"
+ G
+ end
+
+ it "does not install gems whose dependencies are not met" do
+ bundle :install
+ ruby <<-RUBY
+ require 'bundler/setup'
+ RUBY
+ expect(err).to match(/could not find gem 'rack-obama/i)
+ end
+
+ it "suggests bundle update when the Gemfile requires different versions than the lock" do
+ nice_error = <<-E.strip.gsub(/^ {8}/, "")
+ Fetching source index from file:#{gem_repo2}/
+ Resolving dependencies...
+ Bundler could not find compatible versions for gem "rack":
+ In snapshot (Gemfile.lock):
+ rack (= 0.9.1)
+
+ In Gemfile:
+ rack-obama (= 2.0) was resolved to 2.0, which depends on
+ rack (= 1.2)
+
+ rack_middleware was resolved to 1.0, which depends on
+ rack (= 0.9.1)
+
+ Running `bundle update` will rebuild your snapshot from scratch, using only
+ the gems in your Gemfile, which may resolve the conflict.
+ E
+
+ bundle :install, :retry => 0
+ expect(out).to eq(nice_error)
+ end
+ end
+
+ describe "subtler cases" do
+ before :each do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "rack-obama"
+ G
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "0.9.1"
+ gem "rack-obama"
+ G
+ end
+
+ it "does something" do
+ expect do
+ bundle "install"
+ end.not_to change { File.read(bundled_app("Gemfile.lock")) }
+
+ expect(out).to include("rack = 0.9.1")
+ expect(out).to include("locked at 1.0.0")
+ expect(out).to include("bundle update rack")
+ end
+
+ it "should work when you update" do
+ bundle "update rack"
+ end
+ end
+
+ describe "when adding a new source" do
+ it "updates the lockfile" do
+ build_repo2
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ source "file://#{gem_repo2}"
+ gem "rack"
+ G
+
+ lockfile_should_be <<-L
+ GEM
+ remote: file:#{gem_repo1}/
+ remote: file:#{gem_repo2}/
+ specs:
+ rack (1.0.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ rack
+
+ BUNDLED WITH
+ #{Bundler::VERSION}
+ L
+ end
+ end
+
+ # This was written to test github issue #636
+ describe "when a locked child dependency conflicts" do
+ before(:each) do
+ build_repo2 do
+ build_gem "capybara", "0.3.9" do |s|
+ s.add_dependency "rack", ">= 1.0.0"
+ end
+
+ build_gem "rack", "1.1.0"
+ build_gem "rails", "3.0.0.rc4" do |s|
+ s.add_dependency "rack", "~> 1.1.0"
+ end
+
+ build_gem "rack", "1.2.1"
+ build_gem "rails", "3.0.0" do |s|
+ s.add_dependency "rack", "~> 1.2.1"
+ end
+ end
+ end
+
+ it "prints the correct error message" do
+ # install Rails 3.0.0.rc
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rails", "3.0.0.rc4"
+ gem "capybara", "0.3.9"
+ G
+
+ # upgrade Rails to 3.0.0 and then install again
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "rails", "3.0.0"
+ gem "capybara", "0.3.9"
+ G
+
+ expect(out).to include("Gemfile.lock")
+ end
+ end
+end
diff --git a/spec/bundler/install/gems/mirror_spec.rb b/spec/bundler/install/gems/mirror_spec.rb
new file mode 100644
index 0000000000..798156fb12
--- /dev/null
+++ b/spec/bundler/install/gems/mirror_spec.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+RSpec.describe "bundle install with a mirror configured" do
+ describe "when the mirror does not match the gem source" do
+ before :each do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack"
+ G
+ bundle "config --local mirror.http://gems.example.org http://gem-mirror.example.org"
+ end
+
+ it "installs from the normal location" do
+ bundle :install
+ expect(out).to include("Fetching source index from file:#{gem_repo1}")
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+
+ describe "when the gem source matches a configured mirror" do
+ before :each do
+ gemfile <<-G
+ # This source is bogus and doesn't have the gem we're looking for
+ source "file://#{gem_repo2}"
+
+ gem "rack"
+ G
+ bundle "config --local mirror.file://#{gem_repo2} file://#{gem_repo1}"
+ end
+
+ it "installs the gem from the mirror" do
+ bundle :install
+ expect(out).to include("Fetching source index from file:#{gem_repo1}")
+ expect(out).not_to include("Fetching source index from file:#{gem_repo2}")
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+end
diff --git a/spec/bundler/install/gems/native_extensions_spec.rb b/spec/bundler/install/gems/native_extensions_spec.rb
new file mode 100644
index 0000000000..dcf67e976e
--- /dev/null
+++ b/spec/bundler/install/gems/native_extensions_spec.rb
@@ -0,0 +1,92 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+RSpec.describe "installing a gem with native extensions", :ruby_repo do
+ it "installs" do
+ build_repo2 do
+ build_gem "c_extension" do |s|
+ s.extensions = ["ext/extconf.rb"]
+ s.write "ext/extconf.rb", <<-E
+ require "mkmf"
+ name = "c_extension_bundle"
+ dir_config(name)
+ raise "OMG" unless with_config("c_extension") == "hello"
+ create_makefile(name)
+ E
+
+ s.write "ext/c_extension.c", <<-C
+ #include "ruby.h"
+
+ VALUE c_extension_true(VALUE self) {
+ return Qtrue;
+ }
+
+ void Init_c_extension_bundle() {
+ VALUE c_Extension = rb_define_class("CExtension", rb_cObject);
+ rb_define_method(c_Extension, "its_true", c_extension_true, 0);
+ }
+ C
+
+ s.write "lib/c_extension.rb", <<-C
+ require "c_extension_bundle"
+ C
+ end
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "c_extension"
+ G
+
+ bundle "config build.c_extension --with-c_extension=hello"
+ bundle "install"
+
+ expect(out).not_to include("extconf.rb failed")
+ expect(out).to include("Installing c_extension 1.0 with native extensions")
+
+ run "Bundler.require; puts CExtension.new.its_true"
+ expect(out).to eq("true")
+ end
+
+ it "installs from git" do
+ build_git "c_extension" do |s|
+ s.extensions = ["ext/extconf.rb"]
+ s.write "ext/extconf.rb", <<-E
+ require "mkmf"
+ name = "c_extension_bundle"
+ dir_config(name)
+ raise "OMG" unless with_config("c_extension") == "hello"
+ create_makefile(name)
+ E
+
+ s.write "ext/c_extension.c", <<-C
+ #include "ruby.h"
+
+ VALUE c_extension_true(VALUE self) {
+ return Qtrue;
+ }
+
+ void Init_c_extension_bundle() {
+ VALUE c_Extension = rb_define_class("CExtension", rb_cObject);
+ rb_define_method(c_Extension, "its_true", c_extension_true, 0);
+ }
+ C
+
+ s.write "lib/c_extension.rb", <<-C
+ require "c_extension_bundle"
+ C
+ end
+
+ bundle! "config build.c_extension --with-c_extension=hello"
+
+ install_gemfile! <<-G
+ gem "c_extension", :git => #{lib_path("c_extension-1.0").to_s.dump}
+ G
+
+ expect(out).not_to include("extconf.rb failed")
+ expect(out).to include("Using c_extension 1.0")
+
+ run! "Bundler.require; puts CExtension.new.its_true"
+ expect(out).to eq("true")
+ end
+end
diff --git a/spec/bundler/install/gems/post_install_spec.rb b/spec/bundler/install/gems/post_install_spec.rb
new file mode 100644
index 0000000000..c3ea3e7c51
--- /dev/null
+++ b/spec/bundler/install/gems/post_install_spec.rb
@@ -0,0 +1,151 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+RSpec.describe "bundle install" do
+ context "with gem sources" do
+ context "when gems include post install messages" do
+ it "should display the post-install messages after installing" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack'
+ gem 'thin'
+ gem 'rack-obama'
+ G
+
+ bundle :install
+ expect(out).to include("Post-install message from rack:")
+ expect(out).to include("Rack's post install message")
+ expect(out).to include("Post-install message from thin:")
+ expect(out).to include("Thin's post install message")
+ expect(out).to include("Post-install message from rack-obama:")
+ expect(out).to include("Rack-obama's post install message")
+ end
+ end
+
+ context "when gems do not include post install messages" do
+ it "should not display any post-install messages" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "activesupport"
+ G
+
+ bundle :install
+ expect(out).not_to include("Post-install message")
+ end
+ end
+
+ context "when a dependecy includes a post install message" do
+ it "should display the post install message" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'rack_middleware'
+ G
+
+ bundle :install
+ expect(out).to include("Post-install message from rack:")
+ expect(out).to include("Rack's post install message")
+ end
+ end
+ end
+
+ context "with git sources" do
+ context "when gems include post install messages" do
+ it "should display the post-install messages after installing" do
+ build_git "foo" do |s|
+ s.post_install_message = "Foo's post install message"
+ end
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'foo', :git => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle :install
+ expect(out).to include("Post-install message from foo:")
+ expect(out).to include("Foo's post install message")
+ end
+
+ it "should display the post-install messages if repo is updated" do
+ build_git "foo" do |s|
+ s.post_install_message = "Foo's post install message"
+ end
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'foo', :git => '#{lib_path("foo-1.0")}'
+ G
+ bundle :install
+
+ build_git "foo", "1.1" do |s|
+ s.post_install_message = "Foo's 1.1 post install message"
+ end
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'foo', :git => '#{lib_path("foo-1.1")}'
+ G
+ bundle :install
+
+ expect(out).to include("Post-install message from foo:")
+ expect(out).to include("Foo's 1.1 post install message")
+ end
+
+ it "should not display the post-install messages if repo is not updated" do
+ build_git "foo" do |s|
+ s.post_install_message = "Foo's post install message"
+ end
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'foo', :git => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle :install
+ expect(out).to include("Post-install message from foo:")
+ expect(out).to include("Foo's post install message")
+
+ bundle :install
+ expect(out).not_to include("Post-install message")
+ end
+ end
+
+ context "when gems do not include post install messages" do
+ it "should not display any post-install messages" do
+ build_git "foo" do |s|
+ s.post_install_message = nil
+ end
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem 'foo', :git => '#{lib_path("foo-1.0")}'
+ G
+
+ bundle :install
+ expect(out).not_to include("Post-install message")
+ end
+ end
+ end
+
+ context "when ignore post-install messages for gem is set" do
+ it "doesn't display any post-install messages" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle "config ignore_messages.rack true"
+
+ bundle :install
+ expect(out).not_to include("Post-install message")
+ end
+ end
+
+ context "when ignore post-install messages for all gems" do
+ it "doesn't display any post-install messages" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle "config ignore_messages true"
+
+ bundle :install
+ expect(out).not_to include("Post-install message")
+ end
+ end
+end
diff --git a/spec/bundler/install/gems/resolving_spec.rb b/spec/bundler/install/gems/resolving_spec.rb
new file mode 100644
index 0000000000..7a341fd14f
--- /dev/null
+++ b/spec/bundler/install/gems/resolving_spec.rb
@@ -0,0 +1,195 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+RSpec.describe "bundle install with install-time dependencies" do
+ it "installs gems with implicit rake dependencies", :ruby_repo do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "with_implicit_rake_dep"
+ gem "another_implicit_rake_dep"
+ gem "rake"
+ G
+
+ run <<-R
+ require 'implicit_rake_dep'
+ require 'another_implicit_rake_dep'
+ puts IMPLICIT_RAKE_DEP
+ puts ANOTHER_IMPLICIT_RAKE_DEP
+ R
+ expect(out).to eq("YES\nYES")
+ end
+
+ it "installs gems with a dependency with no type" do
+ build_repo2
+
+ path = "#{gem_repo2}/#{Gem::MARSHAL_SPEC_DIR}/actionpack-2.3.2.gemspec.rz"
+ spec = Marshal.load(Gem.inflate(File.read(path)))
+ spec.dependencies.each do |d|
+ d.instance_variable_set(:@type, :fail)
+ end
+ File.open(path, "w") do |f|
+ f.write Gem.deflate(Marshal.dump(spec))
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "actionpack", "2.3.2"
+ G
+
+ expect(the_bundle).to include_gems "actionpack 2.3.2", "activesupport 2.3.2"
+ end
+
+ describe "with crazy rubygem plugin stuff" do
+ it "installs plugins" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "net_b"
+ G
+
+ expect(the_bundle).to include_gems "net_b 1.0"
+ end
+
+ it "installs plugins depended on by other plugins", :ruby_repo do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "net_a"
+ G
+
+ expect(the_bundle).to include_gems "net_a 1.0", "net_b 1.0"
+ end
+
+ it "installs multiple levels of dependencies", :ruby_repo do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "net_c"
+ gem "net_e"
+ G
+
+ expect(the_bundle).to include_gems "net_a 1.0", "net_b 1.0", "net_c 1.0", "net_d 1.0", "net_e 1.0"
+ end
+
+ context "with ENV['DEBUG_RESOLVER'] set" do
+ it "produces debug output" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "net_c"
+ gem "net_e"
+ G
+
+ bundle :install, :env => { "DEBUG_RESOLVER" => "1" }
+
+ expect(err).to include("Creating possibility state for net_c")
+ end
+ end
+
+ context "with ENV['DEBUG_RESOLVER_TREE'] set" do
+ it "produces debug output" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "net_c"
+ gem "net_e"
+ G
+
+ bundle :install, :env => { "DEBUG_RESOLVER_TREE" => "1" }
+
+ expect(err).to include(" net_b")
+ expect(err).to include(" net_build_extensions (1.0)")
+ end
+ end
+ end
+
+ describe "when a required ruby version" do
+ context "allows only an older version" do
+ it "installs the older version" do
+ build_repo2 do
+ build_gem "rack", "9001.0.0" do |s|
+ s.required_ruby_version = "> 9000"
+ end
+ end
+
+ install_gemfile <<-G, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2 }
+ ruby "#{RUBY_VERSION}"
+ source "http://localgemserver.test/"
+ gem 'rack'
+ G
+
+ expect(out).to_not include("rack-9001.0.0 requires ruby version > 9000")
+ expect(the_bundle).to include_gems("rack 1.2")
+ end
+ end
+
+ context "allows no gems" do
+ before do
+ build_repo2 do
+ build_gem "require_ruby" do |s|
+ s.required_ruby_version = "> 9000"
+ end
+ end
+ end
+
+ let(:ruby_requirement) { %("#{RUBY_VERSION}") }
+ let(:error_message_requirement) { "~> #{RUBY_VERSION}.0" }
+
+ shared_examples_for "ruby version conflicts" do
+ it "raises an error during resolution" do
+ install_gemfile <<-G, :artifice => "compact_index", :env => { "BUNDLER_SPEC_GEM_REPO" => gem_repo2 }
+ source "http://localgemserver.test/"
+ ruby #{ruby_requirement}
+ gem 'require_ruby'
+ G
+
+ expect(out).to_not include("Gem::InstallError: require_ruby requires Ruby version > 9000")
+
+ nice_error = strip_whitespace(<<-E).strip
+ Fetching gem metadata from http://localgemserver.test/.
+ Fetching version metadata from http://localgemserver.test/
+ Resolving dependencies...
+ Bundler could not find compatible versions for gem "ruby\0":
+ In Gemfile:
+ ruby\0 (#{error_message_requirement})
+
+ require_ruby was resolved to 1.0, which depends on
+ ruby\0 (> 9000)
+
+ Could not find gem 'ruby\0 (> 9000)', which is required by gem 'require_ruby', in any of the sources.
+ E
+ expect(out).to eq(nice_error)
+ end
+ end
+
+ it_behaves_like "ruby version conflicts"
+
+ describe "with a < requirement" do
+ let(:ruby_requirement) { %("< 5000") }
+ let(:error_message_requirement) { "< 5000" }
+
+ it_behaves_like "ruby version conflicts"
+ end
+
+ describe "with a compound requirement" do
+ let(:ruby_requirement) { %("< 5000", "> 0.1") }
+ let(:error_message_requirement) { "< 5000, > 0.1" }
+
+ it_behaves_like "ruby version conflicts"
+ end
+ end
+ end
+
+ describe "when a required rubygems version disallows a gem" do
+ it "does not try to install those gems" do
+ build_repo2 do
+ build_gem "require_rubygems" do |s|
+ s.required_rubygems_version = "> 9000"
+ end
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem 'require_rubygems'
+ G
+
+ expect(out).to_not include("Gem::InstallError: require_rubygems requires RubyGems version > 9000")
+ expect(out).to include("require_rubygems-1.0 requires rubygems version > 9000, which is incompatible with the current version, #{Gem::VERSION}")
+ end
+ end
+end
diff --git a/spec/bundler/install/gems/standalone_spec.rb b/spec/bundler/install/gems/standalone_spec.rb
new file mode 100644
index 0000000000..9a79a05b32
--- /dev/null
+++ b/spec/bundler/install/gems/standalone_spec.rb
@@ -0,0 +1,318 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+RSpec.shared_examples "bundle install --standalone" do
+ shared_examples "common functionality" do
+ it "still makes the gems available to normal bundler" do
+ args = expected_gems.map {|k, v| "#{k} #{v}" }
+ expect(the_bundle).to include_gems(*args)
+ end
+
+ it "generates a bundle/bundler/setup.rb" do
+ expect(bundled_app("bundle/bundler/setup.rb")).to exist
+ end
+
+ it "makes the gems available without bundler" do
+ testrb = String.new <<-RUBY
+ $:.unshift File.expand_path("bundle")
+ require "bundler/setup"
+
+ RUBY
+ expected_gems.each do |k, _|
+ testrb << "\nrequire \"#{k}\""
+ testrb << "\nputs #{k.upcase}"
+ end
+ Dir.chdir(bundled_app) do
+ ruby testrb, :no_lib => true
+ end
+
+ expect(out).to eq(expected_gems.values.join("\n"))
+ end
+
+ it "works on a different system" do
+ FileUtils.mv(bundled_app, "#{bundled_app}2")
+
+ testrb = String.new <<-RUBY
+ $:.unshift File.expand_path("bundle")
+ require "bundler/setup"
+
+ RUBY
+ expected_gems.each do |k, _|
+ testrb << "\nrequire \"#{k}\""
+ testrb << "\nputs #{k.upcase}"
+ end
+ Dir.chdir("#{bundled_app}2") do
+ ruby testrb, :no_lib => true
+ end
+
+ expect(out).to eq(expected_gems.values.join("\n"))
+ end
+ end
+
+ describe "with simple gems" do
+ before do
+ install_gemfile <<-G, :standalone => true
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+ end
+
+ let(:expected_gems) do
+ {
+ "actionpack" => "2.3.2",
+ "rails" => "2.3.2",
+ }
+ end
+
+ include_examples "common functionality"
+ end
+
+ describe "with gems with native extension", :ruby_repo do
+ before do
+ install_gemfile <<-G, :standalone => true
+ source "file://#{gem_repo1}"
+ gem "very_simple_binary"
+ G
+ end
+
+ it "generates a bundle/bundler/setup.rb with the proper paths", :rubygems => "2.4" do
+ extension_line = File.read(bundled_app("bundle/bundler/setup.rb")).each_line.find {|line| line.include? "/extensions/" }.strip
+ expect(extension_line).to start_with '$:.unshift "#{path}/../#{ruby_engine}/#{ruby_version}/extensions/'
+ expect(extension_line).to end_with '/very_simple_binary-1.0"'
+ end
+ end
+
+ describe "with gem that has an invalid gemspec" do
+ before do
+ build_git "bar", :gemspec => false do |s|
+ s.write "lib/bar/version.rb", %(BAR_VERSION = '1.0')
+ s.write "bar.gemspec", <<-G
+ lib = File.expand_path('../lib/', __FILE__)
+ $:.unshift lib unless $:.include?(lib)
+ require 'bar/version'
+
+ Gem::Specification.new do |s|
+ s.name = 'bar'
+ s.version = BAR_VERSION
+ s.summary = 'Bar'
+ s.files = Dir["lib/**/*.rb"]
+ s.author = 'Anonymous'
+ s.require_path = [1,2]
+ end
+ G
+ end
+ install_gemfile <<-G, :standalone => true
+ gem "bar", :git => "#{lib_path("bar-1.0")}"
+ G
+ end
+
+ it "outputs a helpful error message" do
+ expect(out).to include("You have one or more invalid gemspecs that need to be fixed.")
+ expect(out).to include("bar 1.0 has an invalid gemspec")
+ end
+ end
+
+ describe "with a combination of gems and git repos" do
+ before do
+ build_git "devise", "1.0"
+
+ install_gemfile <<-G, :standalone => true
+ source "file://#{gem_repo1}"
+ gem "rails"
+ gem "devise", :git => "#{lib_path("devise-1.0")}"
+ G
+ end
+
+ let(:expected_gems) do
+ {
+ "actionpack" => "2.3.2",
+ "devise" => "1.0",
+ "rails" => "2.3.2",
+ }
+ end
+
+ include_examples "common functionality"
+ end
+
+ describe "with groups" do
+ before do
+ build_git "devise", "1.0"
+
+ install_gemfile <<-G, :standalone => true
+ source "file://#{gem_repo1}"
+ gem "rails"
+
+ group :test do
+ gem "rspec"
+ gem "rack-test"
+ end
+ G
+ end
+
+ let(:expected_gems) do
+ {
+ "actionpack" => "2.3.2",
+ "rails" => "2.3.2",
+ }
+ end
+
+ include_examples "common functionality"
+
+ it "allows creating a standalone file with limited groups" do
+ bundle "install --standalone default"
+
+ Dir.chdir(bundled_app) do
+ load_error_ruby <<-RUBY, "spec", :no_lib => true
+ $:.unshift File.expand_path("bundle")
+ require "bundler/setup"
+
+ require "actionpack"
+ puts ACTIONPACK
+ require "spec"
+ RUBY
+ end
+
+ expect(out).to eq("2.3.2")
+ expect(err).to eq("ZOMG LOAD ERROR")
+ end
+
+ it "allows --without to limit the groups used in a standalone" do
+ bundle "install --standalone --without test"
+
+ Dir.chdir(bundled_app) do
+ load_error_ruby <<-RUBY, "spec", :no_lib => true
+ $:.unshift File.expand_path("bundle")
+ require "bundler/setup"
+
+ require "actionpack"
+ puts ACTIONPACK
+ require "spec"
+ RUBY
+ end
+
+ expect(out).to eq("2.3.2")
+ expect(err).to eq("ZOMG LOAD ERROR")
+ end
+
+ it "allows --path to change the location of the standalone bundle" do
+ bundle "install --standalone --path path/to/bundle"
+
+ Dir.chdir(bundled_app) do
+ ruby <<-RUBY, :no_lib => true
+ $:.unshift File.expand_path("path/to/bundle")
+ require "bundler/setup"
+
+ require "actionpack"
+ puts ACTIONPACK
+ RUBY
+ end
+
+ expect(out).to eq("2.3.2")
+ end
+
+ it "allows remembered --without to limit the groups used in a standalone" do
+ bundle "install --without test"
+ bundle "install --standalone"
+
+ Dir.chdir(bundled_app) do
+ load_error_ruby <<-RUBY, "spec", :no_lib => true
+ $:.unshift File.expand_path("bundle")
+ require "bundler/setup"
+
+ require "actionpack"
+ puts ACTIONPACK
+ require "spec"
+ RUBY
+ end
+
+ expect(out).to eq("2.3.2")
+ expect(err).to eq("ZOMG LOAD ERROR")
+ end
+ end
+
+ describe "with gemcutter's dependency API" do
+ let(:source_uri) { "http://localgemserver.test" }
+
+ describe "simple gems" do
+ before do
+ gemfile <<-G
+ source "#{source_uri}"
+ gem "rails"
+ G
+ bundle "install --standalone", :artifice => "endpoint"
+ end
+
+ let(:expected_gems) do
+ {
+ "actionpack" => "2.3.2",
+ "rails" => "2.3.2",
+ }
+ end
+
+ include_examples "common functionality"
+ end
+ end
+
+ describe "with --binstubs" do
+ before do
+ install_gemfile <<-G, :standalone => true, :binstubs => true
+ source "file://#{gem_repo1}"
+ gem "rails"
+ G
+ end
+
+ let(:expected_gems) do
+ {
+ "actionpack" => "2.3.2",
+ "rails" => "2.3.2",
+ }
+ end
+
+ include_examples "common functionality"
+
+ it "creates stubs that use the standalone load path" do
+ Dir.chdir(bundled_app) do
+ expect(`bin/rails -v`.chomp).to eql "2.3.2"
+ end
+ end
+
+ it "creates stubs that can be executed from anywhere" do
+ require "tmpdir"
+ Dir.chdir(Dir.tmpdir) do
+ sys_exec!(%(#{bundled_app("bin/rails")} -v))
+ expect(out).to eq("2.3.2")
+ end
+ end
+
+ it "creates stubs that can be symlinked" do
+ pending "File.symlink is unsupported on Windows" if Bundler::WINDOWS
+
+ symlink_dir = tmp("symlink")
+ FileUtils.mkdir_p(symlink_dir)
+ symlink = File.join(symlink_dir, "rails")
+
+ File.symlink(bundled_app("bin/rails"), symlink)
+ sys_exec!("#{symlink} -v")
+ expect(out).to eq("2.3.2")
+ end
+
+ it "creates stubs with the correct load path" do
+ extension_line = File.read(bundled_app("bin/rails")).each_line.find {|line| line.include? "$:.unshift" }.strip
+ expect(extension_line).to eq %($:.unshift File.expand_path "../../bundle", path.realpath)
+ end
+ end
+end
+
+RSpec.describe "bundle install --standalone" do
+ include_examples("bundle install --standalone")
+end
+
+RSpec.describe "bundle install --standalone run in a subdirectory" do
+ before do
+ subdir = bundled_app("bob")
+ FileUtils.mkdir_p(subdir)
+ Dir.chdir(subdir)
+ end
+
+ include_examples("bundle install --standalone")
+end
diff --git a/spec/bundler/install/gems/sudo_spec.rb b/spec/bundler/install/gems/sudo_spec.rb
new file mode 100644
index 0000000000..13abffc14e
--- /dev/null
+++ b/spec/bundler/install/gems/sudo_spec.rb
@@ -0,0 +1,179 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+RSpec.describe "when using sudo", :sudo => true do
+ describe "and BUNDLE_PATH is writable" do
+ context "but BUNDLE_PATH/build_info is not writable" do
+ before do
+ subdir = system_gem_path("cache")
+ subdir.mkpath
+ sudo "chmod u-w #{subdir}"
+ end
+
+ it "installs" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ expect(out).to_not match(/an error occurred/i)
+ expect(system_gem_path("cache/rack-1.0.0.gem")).to exist
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+ end
+
+ describe "and GEM_HOME is owned by root" do
+ before :each do
+ chown_system_gems_to_root
+ end
+
+ it "installs" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", '1.0'
+ gem "thin"
+ G
+
+ expect(system_gem_path("gems/rack-1.0.0")).to exist
+ expect(system_gem_path("gems/rack-1.0.0").stat.uid).to eq(0)
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+
+ it "installs rake and a gem dependent on rake in the same session" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rake"
+ gem "another_implicit_rake_dep"
+ G
+ bundle "install"
+ expect(system_gem_path("gems/another_implicit_rake_dep-1.0")).to exist
+ end
+
+ it "installs when BUNDLE_PATH is owned by root" do
+ bundle_path = tmp("owned_by_root")
+ FileUtils.mkdir_p bundle_path
+ sudo "chown -R root #{bundle_path}"
+
+ ENV["BUNDLE_PATH"] = bundle_path.to_s
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", '1.0'
+ G
+
+ expect(bundle_path.join("gems/rack-1.0.0")).to exist
+ expect(bundle_path.join("gems/rack-1.0.0").stat.uid).to eq(0)
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+
+ it "installs when BUNDLE_PATH does not exist" do
+ root_path = tmp("owned_by_root")
+ FileUtils.mkdir_p root_path
+ sudo "chown -R root #{root_path}"
+ bundle_path = root_path.join("does_not_exist")
+
+ ENV["BUNDLE_PATH"] = bundle_path.to_s
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", '1.0'
+ G
+
+ expect(bundle_path.join("gems/rack-1.0.0")).to exist
+ expect(bundle_path.join("gems/rack-1.0.0").stat.uid).to eq(0)
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+
+ it "installs extensions/ compiled by Rubygems 2.2", :rubygems => "2.2" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "very_simple_binary"
+ G
+
+ expect(system_gem_path("gems/very_simple_binary-1.0")).to exist
+ binary_glob = system_gem_path("extensions/*/*/very_simple_binary-1.0")
+ expect(Dir.glob(binary_glob).first).to be
+ end
+ end
+
+ describe "and BUNDLE_PATH is not writable" do
+ before do
+ sudo "chmod ugo-w #{default_bundle_path}"
+ end
+
+ it "installs" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", '1.0'
+ G
+
+ expect(default_bundle_path("gems/rack-1.0.0")).to exist
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+
+ it "cleans up the tmpdirs generated" do
+ require "tmpdir"
+ Dir.glob("#{Dir.tmpdir}/bundler*").each do |tmpdir|
+ FileUtils.remove_entry_secure(tmpdir)
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ tmpdirs = Dir.glob("#{Dir.tmpdir}/bundler*")
+
+ expect(tmpdirs).to be_empty
+ end
+ end
+
+ describe "and GEM_HOME is not writable" do
+ it "installs" do
+ gem_home = tmp("sudo_gem_home")
+ sudo "mkdir -p #{gem_home}"
+ sudo "chmod ugo-w #{gem_home}"
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", '1.0'
+ G
+
+ bundle :install, :env => { "GEM_HOME" => gem_home.to_s, "GEM_PATH" => nil }
+ expect(gem_home.join("bin/rackup")).to exist
+ expect(the_bundle).to include_gems "rack 1.0", :env => { "GEM_HOME" => gem_home.to_s, "GEM_PATH" => nil }
+ end
+ end
+
+ describe "and root runs install" do
+ let(:warning) { "Don't run Bundler as root." }
+
+ before do
+ gemfile %(source "file://#{gem_repo1}")
+ end
+
+ it "warns against that" do
+ bundle :install, :sudo => true
+ expect(out).to include(warning)
+ end
+
+ context "when ENV['BUNDLE_SILENCE_ROOT_WARNING'] is set" do
+ it "skips the warning" do
+ bundle :install, :sudo => :preserve_env, :env => { "BUNDLE_SILENCE_ROOT_WARNING" => true }
+ expect(out).to_not include(warning)
+ end
+ end
+
+ context "when silence_root_warning is passed as an option" do
+ it "skips the warning" do
+ bundle :install, :sudo => true, :silence_root_warning => true
+ expect(out).to_not include(warning)
+ end
+ end
+
+ context "when silence_root_warning = false" do
+ it "warns against that" do
+ bundle :install, :sudo => true, :silence_root_warning => false
+ expect(out).to include(warning)
+ end
+ end
+ end
+end
diff --git a/spec/bundler/install/gems/win32_spec.rb b/spec/bundler/install/gems/win32_spec.rb
new file mode 100644
index 0000000000..cdad9a8821
--- /dev/null
+++ b/spec/bundler/install/gems/win32_spec.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+RSpec.describe "bundle install with win32-generated lockfile" do
+ it "should read lockfile" do
+ File.open(bundled_app("Gemfile.lock"), "wb") do |f|
+ f << "GEM\r\n"
+ f << " remote: file:#{gem_repo1}/\r\n"
+ f << " specs:\r\n"
+ f << "\r\n"
+ f << " rack (1.0.0)\r\n"
+ f << "\r\n"
+ f << "PLATFORMS\r\n"
+ f << " ruby\r\n"
+ f << "\r\n"
+ f << "DEPENDENCIES\r\n"
+ f << " rack\r\n"
+ end
+
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+
+ gem "rack"
+ G
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+end
diff --git a/spec/bundler/install/gemspecs_spec.rb b/spec/bundler/install/gemspecs_spec.rb
new file mode 100644
index 0000000000..97eaf149c1
--- /dev/null
+++ b/spec/bundler/install/gemspecs_spec.rb
@@ -0,0 +1,110 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+RSpec.describe "bundle install" do
+ describe "when a gem has a YAML gemspec" do
+ before :each do
+ build_repo2 do
+ build_gem "yaml_spec", :gemspec => :yaml
+ end
+ end
+
+ it "still installs correctly" do
+ gemfile <<-G
+ source "file://#{gem_repo2}"
+ gem "yaml_spec"
+ G
+ bundle :install
+ expect(err).to lack_errors
+ end
+
+ it "still installs correctly when using path" do
+ build_lib "yaml_spec", :gemspec => :yaml
+
+ install_gemfile <<-G
+ gem 'yaml_spec', :path => "#{lib_path("yaml_spec-1.0")}"
+ G
+ expect(err).to lack_errors
+ end
+ end
+
+ it "should use gemspecs in the system cache when available" do
+ gemfile <<-G
+ source "http://localtestserver.gem"
+ gem 'rack'
+ G
+
+ FileUtils.mkdir_p "#{tmp}/gems/system/specifications"
+ File.open("#{tmp}/gems/system/specifications/rack-1.0.0.gemspec", "w+") do |f|
+ spec = Gem::Specification.new do |s|
+ s.name = "rack"
+ s.version = "1.0.0"
+ s.add_runtime_dependency "activesupport", "2.3.2"
+ end
+ f.write spec.to_ruby
+ end
+ bundle :install, :artifice => "endpoint_marshal_fail" # force gemspec load
+ expect(the_bundle).to include_gems "activesupport 2.3.2"
+ end
+
+ context "when ruby version is specified in gemspec and gemfile" do
+ it "installs when patch level is not specified and the version matches" do
+ build_lib("foo", :path => bundled_app) do |s|
+ s.required_ruby_version = "~> #{RUBY_VERSION}.0"
+ end
+
+ install_gemfile <<-G
+ ruby '#{RUBY_VERSION}', :engine_version => '#{RUBY_VERSION}', :engine => 'ruby'
+ gemspec
+ G
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "installs when patch level is specified and the version still matches the current version",
+ :if => RUBY_PATCHLEVEL >= 0 do
+ build_lib("foo", :path => bundled_app) do |s|
+ s.required_ruby_version = "#{RUBY_VERSION}.#{RUBY_PATCHLEVEL}"
+ end
+
+ install_gemfile <<-G
+ ruby '#{RUBY_VERSION}', :engine_version => '#{RUBY_VERSION}', :engine => 'ruby', :patchlevel => '#{RUBY_PATCHLEVEL}'
+ gemspec
+ G
+ expect(the_bundle).to include_gems "foo 1.0"
+ end
+
+ it "fails and complains about patchlevel on patchlevel mismatch",
+ :if => RUBY_PATCHLEVEL >= 0 do
+ patchlevel = RUBY_PATCHLEVEL.to_i + 1
+ build_lib("foo", :path => bundled_app) do |s|
+ s.required_ruby_version = "#{RUBY_VERSION}.#{patchlevel}"
+ end
+
+ install_gemfile <<-G
+ ruby '#{RUBY_VERSION}', :engine_version => '#{RUBY_VERSION}', :engine => 'ruby', :patchlevel => '#{patchlevel}'
+ gemspec
+ G
+
+ expect(out).to include("Ruby patchlevel")
+ expect(out).to include("but your Gemfile specified")
+ expect(exitstatus).to eq(18) if exitstatus
+ end
+
+ it "fails and complains about version on version mismatch" do
+ version = Gem::Requirement.create(RUBY_VERSION).requirements.first.last.bump.version
+
+ build_lib("foo", :path => bundled_app) do |s|
+ s.required_ruby_version = version
+ end
+
+ install_gemfile <<-G
+ ruby '#{version}', :engine_version => '#{version}', :engine => 'ruby'
+ gemspec
+ G
+
+ expect(out).to include("Ruby version")
+ expect(out).to include("but your Gemfile specified")
+ expect(exitstatus).to eq(18) if exitstatus
+ end
+ end
+end
diff --git a/spec/bundler/install/git_spec.rb b/spec/bundler/install/git_spec.rb
new file mode 100644
index 0000000000..04f2380b45
--- /dev/null
+++ b/spec/bundler/install/git_spec.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+RSpec.describe "bundle install" do
+ context "git sources" do
+ it "displays the revision hash of the gem repository" do
+ build_git "foo", "1.0", :path => lib_path("foo")
+
+ install_gemfile <<-G
+ gem "foo", :git => "#{lib_path("foo")}"
+ G
+
+ bundle :install
+ expect(out).to include("Using foo 1.0 from #{lib_path("foo")} (at master@#{revision_for(lib_path("foo"))[0..6]})")
+ expect(the_bundle).to include_gems "foo 1.0", :source => "git@#{lib_path("foo")}"
+ end
+
+ it "displays the ref of the gem repository when using branch~num as a ref" do
+ build_git "foo", "1.0", :path => lib_path("foo")
+ rev = revision_for(lib_path("foo"))[0..6]
+ update_git "foo", "2.0", :path => lib_path("foo"), :gemspec => true
+ rev2 = revision_for(lib_path("foo"))[0..6]
+ update_git "foo", "3.0", :path => lib_path("foo"), :gemspec => true
+
+ install_gemfile! <<-G
+ gem "foo", :git => "#{lib_path("foo")}", :ref => "master~2"
+ G
+
+ bundle! :install
+ expect(out).to include("Using foo 1.0 from #{lib_path("foo")} (at master~2@#{rev})")
+ expect(the_bundle).to include_gems "foo 1.0", :source => "git@#{lib_path("foo")}"
+
+ update_git "foo", "4.0", :path => lib_path("foo"), :gemspec => true
+
+ bundle! :update
+ expect(out).to include("Using foo 2.0 (was 1.0) from #{lib_path("foo")} (at master~2@#{rev2})")
+ expect(the_bundle).to include_gems "foo 2.0", :source => "git@#{lib_path("foo")}"
+ end
+
+ it "should check out git repos that are missing but not being installed" do
+ build_git "foo"
+
+ gemfile <<-G
+ gem "foo", :git => "file://#{lib_path("foo-1.0")}", :group => :development
+ G
+
+ lockfile <<-L
+ GIT
+ remote: file://#{lib_path("foo-1.0")}
+ specs:
+ foo (1.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ foo!
+ L
+
+ bundle "install --path=vendor/bundle --without development"
+
+ expect(out).to include("Bundle complete!")
+ expect(vendored_gems("bundler/gems/foo-1.0-#{revision_for(lib_path("foo-1.0"))[0..11]}")).to be_directory
+ end
+ end
+end
diff --git a/spec/bundler/install/path_spec.rb b/spec/bundler/install/path_spec.rb
new file mode 100644
index 0000000000..7a501d42b3
--- /dev/null
+++ b/spec/bundler/install/path_spec.rb
@@ -0,0 +1,178 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+RSpec.describe "bundle install" do
+ describe "with --path" do
+ before :each do
+ build_gem "rack", "1.0.0", :to_system => true do |s|
+ s.write "lib/rack.rb", "puts 'FAIL'"
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ end
+
+ it "does not use available system gems with bundle --path vendor/bundle" do
+ bundle "install --path vendor/bundle"
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "handles paths with regex characters in them" do
+ dir = bundled_app("bun++dle")
+ dir.mkpath
+
+ Dir.chdir(dir) do
+ bundle "install --path vendor/bundle"
+ expect(out).to include("installed into ./vendor/bundle")
+ end
+
+ dir.rmtree
+ end
+
+ it "prints a warning to let the user know what has happened with bundle --path vendor/bundle" do
+ bundle "install --path vendor/bundle"
+ expect(out).to include("gems are installed into ./vendor")
+ end
+
+ it "disallows --path vendor/bundle --system" do
+ bundle "install --path vendor/bundle --system"
+ expect(out).to include("Please choose only one option.")
+ expect(exitstatus).to eq(15) if exitstatus
+ end
+
+ it "remembers to disable system gems after the first time with bundle --path vendor/bundle" do
+ bundle "install --path vendor/bundle"
+ FileUtils.rm_rf bundled_app("vendor")
+ bundle "install"
+
+ expect(vendored_gems("gems/rack-1.0.0")).to be_directory
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+
+ describe "when BUNDLE_PATH or the global path config is set" do
+ before :each do
+ build_lib "rack", "1.0.0", :to_system => true do |s|
+ s.write "lib/rack.rb", "raise 'FAIL'"
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ end
+
+ def set_bundle_path(type, location)
+ if type == :env
+ ENV["BUNDLE_PATH"] = location
+ elsif type == :global
+ bundle "config path #{location}", "no-color" => nil
+ end
+ end
+
+ [:env, :global].each do |type|
+ it "installs gems to a path if one is specified" do
+ set_bundle_path(type, bundled_app("vendor2").to_s)
+ bundle "install --path vendor/bundle"
+
+ expect(vendored_gems("gems/rack-1.0.0")).to be_directory
+ expect(bundled_app("vendor2")).not_to be_directory
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "installs gems to BUNDLE_PATH with #{type}" do
+ set_bundle_path(type, bundled_app("vendor").to_s)
+
+ bundle :install
+
+ expect(bundled_app("vendor/gems/rack-1.0.0")).to be_directory
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "installs gems to BUNDLE_PATH relative to root when relative" do
+ set_bundle_path(type, "vendor")
+
+ FileUtils.mkdir_p bundled_app("lol")
+ Dir.chdir(bundled_app("lol")) do
+ bundle :install
+ end
+
+ expect(bundled_app("vendor/gems/rack-1.0.0")).to be_directory
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+ end
+
+ it "installs gems to BUNDLE_PATH from .bundle/config" do
+ config "BUNDLE_PATH" => bundled_app("vendor/bundle").to_s
+
+ bundle :install
+
+ expect(vendored_gems("gems/rack-1.0.0")).to be_directory
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "sets BUNDLE_PATH as the first argument to bundle install" do
+ bundle "install --path ./vendor/bundle"
+
+ expect(vendored_gems("gems/rack-1.0.0")).to be_directory
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "disables system gems when passing a path to install" do
+ # This is so that vendored gems can be distributed to others
+ build_gem "rack", "1.1.0", :to_system => true
+ bundle "install --path ./vendor/bundle"
+
+ expect(vendored_gems("gems/rack-1.0.0")).to be_directory
+ expect(the_bundle).to include_gems "rack 1.0.0"
+ end
+
+ it "re-installs gems whose extensions have been deleted", :ruby_repo, :rubygems => ">= 2.3" do
+ build_lib "very_simple_binary", "1.0.0", :to_system => true do |s|
+ s.write "lib/very_simple_binary.rb", "raise 'FAIL'"
+ end
+
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "very_simple_binary"
+ G
+
+ bundle "install --path ./vendor/bundle"
+
+ expect(vendored_gems("gems/very_simple_binary-1.0")).to be_directory
+ expect(vendored_gems("extensions")).to be_directory
+ expect(the_bundle).to include_gems "very_simple_binary 1.0", :source => "remote1"
+
+ vendored_gems("extensions").rmtree
+
+ run "require 'very_simple_binary_c'"
+ expect(err).to include("Bundler::GemNotFound")
+
+ bundle "install --path ./vendor/bundle"
+
+ expect(vendored_gems("gems/very_simple_binary-1.0")).to be_directory
+ expect(vendored_gems("extensions")).to be_directory
+ expect(the_bundle).to include_gems "very_simple_binary 1.0", :source => "remote1"
+ end
+ end
+
+ describe "to a file" do
+ before do
+ in_app_root do
+ `touch /tmp/idontexist bundle`
+ end
+ end
+
+ it "reports the file exists" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+
+ bundle "install --path bundle"
+ expect(out).to match(/file already exists/)
+ end
+ end
+end
diff --git a/spec/bundler/install/post_bundle_message_spec.rb b/spec/bundler/install/post_bundle_message_spec.rb
new file mode 100644
index 0000000000..4453e4190f
--- /dev/null
+++ b/spec/bundler/install/post_bundle_message_spec.rb
@@ -0,0 +1,190 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+RSpec.describe "post bundle message" do
+ before :each do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "activesupport", "2.3.5", :group => [:emo, :test]
+ group :test do
+ gem "rspec"
+ end
+ gem "rack-obama", :group => :obama
+ G
+ end
+
+ let(:bundle_show_message) { "Use `bundle info [gemname]` to see where a bundled gem is installed." }
+ let(:bundle_deployment_message) { "Bundled gems are installed into ./vendor" }
+ let(:bundle_complete_message) { "Bundle complete!" }
+ let(:bundle_updated_message) { "Bundle updated!" }
+ let(:installed_gems_stats) { "4 Gemfile dependencies, 5 gems now installed." }
+
+ describe "for fresh bundle install" do
+ it "without any options" do
+ bundle :install
+ expect(out).to include(bundle_show_message)
+ expect(out).not_to include("Gems in the group")
+ expect(out).to include(bundle_complete_message)
+ expect(out).to include(installed_gems_stats)
+ end
+
+ it "with --without one group" do
+ bundle "install --without emo"
+ expect(out).to include(bundle_show_message)
+ expect(out).to include("Gems in the group emo were not installed")
+ expect(out).to include(bundle_complete_message)
+ expect(out).to include(installed_gems_stats)
+ end
+
+ it "with --without two groups" do
+ bundle "install --without emo test"
+ expect(out).to include(bundle_show_message)
+ expect(out).to include("Gems in the groups emo and test were not installed")
+ expect(out).to include(bundle_complete_message)
+ expect(out).to include("4 Gemfile dependencies, 3 gems now installed.")
+ end
+
+ it "with --without more groups" do
+ bundle "install --without emo obama test"
+ expect(out).to include(bundle_show_message)
+ expect(out).to include("Gems in the groups emo, obama and test were not installed")
+ expect(out).to include(bundle_complete_message)
+ expect(out).to include("4 Gemfile dependencies, 2 gems now installed.")
+ end
+
+ describe "with --path and" do
+ it "without any options" do
+ bundle "install --path vendor"
+ expect(out).to include(bundle_deployment_message)
+ expect(out).to_not include("Gems in the group")
+ expect(out).to include(bundle_complete_message)
+ end
+
+ it "with --without one group" do
+ bundle "install --without emo --path vendor"
+ expect(out).to include(bundle_deployment_message)
+ expect(out).to include("Gems in the group emo were not installed")
+ expect(out).to include(bundle_complete_message)
+ end
+
+ it "with --without two groups" do
+ bundle "install --without emo test --path vendor"
+ expect(out).to include(bundle_deployment_message)
+ expect(out).to include("Gems in the groups emo and test were not installed")
+ expect(out).to include(bundle_complete_message)
+ end
+
+ it "with --without more groups" do
+ bundle "install --without emo obama test --path vendor"
+ expect(out).to include(bundle_deployment_message)
+ expect(out).to include("Gems in the groups emo, obama and test were not installed")
+ expect(out).to include(bundle_complete_message)
+ end
+
+ it "with an absolute --path inside the cwd" do
+ bundle "install --path #{bundled_app}/cache"
+ expect(out).to include("Bundled gems are installed into ./cache")
+ expect(out).to_not include("Gems in the group")
+ expect(out).to include(bundle_complete_message)
+ end
+
+ it "with an absolute --path outside the cwd" do
+ bundle "install --path #{bundled_app}_cache"
+ expect(out).to include("Bundled gems are installed into #{bundled_app}_cache")
+ expect(out).to_not include("Gems in the group")
+ expect(out).to include(bundle_complete_message)
+ end
+ end
+
+ describe "with misspelled or non-existent gem name" do
+ it "should report a helpful error message" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "not-a-gem", :group => :development
+ G
+ expect(out).to include("Could not find gem 'not-a-gem' in any of the gem sources listed in your Gemfile.")
+ end
+
+ it "should report a helpful error message with reference to cache if available" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ G
+ bundle :cache
+ expect(bundled_app("vendor/cache/rack-1.0.0.gem")).to exist
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack"
+ gem "not-a-gem", :group => :development
+ G
+ expect(out).to include("Could not find gem 'not-a-gem' in any of the gem sources listed in your Gemfile or in gems cached in vendor/cache.")
+ end
+ end
+ end
+
+ describe "for second bundle install run" do
+ it "without any options" do
+ 2.times { bundle :install }
+ expect(out).to include(bundle_show_message)
+ expect(out).to_not include("Gems in the groups")
+ expect(out).to include(bundle_complete_message)
+ expect(out).to include(installed_gems_stats)
+ end
+
+ it "with --without one group" do
+ bundle "install --without emo"
+ bundle :install
+ expect(out).to include(bundle_show_message)
+ expect(out).to include("Gems in the group emo were not installed")
+ expect(out).to include(bundle_complete_message)
+ expect(out).to include(installed_gems_stats)
+ end
+
+ it "with --without two groups" do
+ bundle "install --without emo test"
+ bundle :install
+ expect(out).to include(bundle_show_message)
+ expect(out).to include("Gems in the groups emo and test were not installed")
+ expect(out).to include(bundle_complete_message)
+ end
+
+ it "with --without more groups" do
+ bundle "install --without emo obama test"
+ bundle :install
+ expect(out).to include(bundle_show_message)
+ expect(out).to include("Gems in the groups emo, obama and test were not installed")
+ expect(out).to include(bundle_complete_message)
+ end
+ end
+
+ describe "for bundle update" do
+ it "without any options" do
+ bundle :update
+ expect(out).not_to include("Gems in the groups")
+ expect(out).to include(bundle_updated_message)
+ end
+
+ it "with --without one group" do
+ bundle :install, :without => :emo
+ bundle :update
+ expect(out).to include("Gems in the group emo were not installed")
+ expect(out).to include(bundle_updated_message)
+ end
+
+ it "with --without two groups" do
+ bundle "install --without emo test"
+ bundle :update
+ expect(out).to include("Gems in the groups emo and test were not installed")
+ expect(out).to include(bundle_updated_message)
+ end
+
+ it "with --without more groups" do
+ bundle "install --without emo obama test"
+ bundle :update
+ expect(out).to include("Gems in the groups emo, obama and test were not installed")
+ expect(out).to include(bundle_updated_message)
+ end
+ end
+end
diff --git a/spec/bundler/install/prereleases_spec.rb b/spec/bundler/install/prereleases_spec.rb
new file mode 100644
index 0000000000..6c32094d90
--- /dev/null
+++ b/spec/bundler/install/prereleases_spec.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+RSpec.describe "bundle install" do
+ describe "when prerelease gems are available" do
+ it "finds prereleases" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "not_released"
+ G
+ expect(the_bundle).to include_gems "not_released 1.0.pre"
+ end
+
+ it "uses regular releases if available" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "has_prerelease"
+ G
+ expect(the_bundle).to include_gems "has_prerelease 1.0"
+ end
+
+ it "uses prereleases if requested" do
+ install_gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "has_prerelease", "1.1.pre"
+ G
+ expect(the_bundle).to include_gems "has_prerelease 1.1.pre"
+ end
+ end
+
+ describe "when prerelease gems are not available" do
+ it "still works" do
+ build_repo3
+ install_gemfile <<-G
+ source "file://#{gem_repo3}"
+ gem "rack"
+ G
+
+ expect(the_bundle).to include_gems "rack 1.0"
+ end
+ end
+end
diff --git a/spec/bundler/install/security_policy_spec.rb b/spec/bundler/install/security_policy_spec.rb
new file mode 100644
index 0000000000..ab531bdad6
--- /dev/null
+++ b/spec/bundler/install/security_policy_spec.rb
@@ -0,0 +1,77 @@
+# frozen_string_literal: true
+require "spec_helper"
+require "rubygems/security"
+
+# unfortunately, testing signed gems with a provided CA is extremely difficult
+# as 'gem cert' is currently the only way to add CAs to the system.
+
+RSpec.describe "policies with unsigned gems" do
+ before do
+ build_security_repo
+ gemfile <<-G
+ source "file://#{security_repo}"
+ gem "rack"
+ gem "signed_gem"
+ G
+ end
+
+ it "will work after you try to deploy without a lock" do
+ bundle "install --deployment"
+ bundle :install
+ expect(exitstatus).to eq(0) if exitstatus
+ expect(the_bundle).to include_gems "rack 1.0", "signed_gem 1.0"
+ end
+
+ it "will fail when given invalid security policy" do
+ bundle "install --trust-policy=InvalidPolicyName"
+ expect(out).to include("Rubygems doesn't know about trust policy")
+ end
+
+ it "will fail with High Security setting due to presence of unsigned gem" do
+ bundle "install --trust-policy=HighSecurity"
+ expect(out).to include("security policy didn't allow")
+ end
+
+ # This spec will fail on Rubygems 2 rc1 due to a bug in policy.rb. the bug is fixed in rc3.
+ it "will fail with Medium Security setting due to presence of unsigned gem", :unless => ENV["RGV"] == "v2.0.0.rc.1" do
+ bundle "install --trust-policy=MediumSecurity"
+ expect(out).to include("security policy didn't allow")
+ end
+
+ it "will succeed with no policy" do
+ bundle "install"
+ expect(exitstatus).to eq(0) if exitstatus
+ end
+end
+
+RSpec.describe "policies with signed gems and no CA" do
+ before do
+ build_security_repo
+ gemfile <<-G
+ source "file://#{security_repo}"
+ gem "signed_gem"
+ G
+ end
+
+ it "will fail with High Security setting, gem is self-signed" do
+ bundle "install --trust-policy=HighSecurity"
+ expect(out).to include("security policy didn't allow")
+ end
+
+ it "will fail with Medium Security setting, gem is self-signed" do
+ bundle "install --trust-policy=MediumSecurity"
+ expect(out).to include("security policy didn't allow")
+ end
+
+ it "will succeed with Low Security setting, low security accepts self signed gem" do
+ bundle "install --trust-policy=LowSecurity"
+ expect(exitstatus).to eq(0) if exitstatus
+ expect(the_bundle).to include_gems "signed_gem 1.0"
+ end
+
+ it "will succeed with no policy" do
+ bundle "install"
+ expect(exitstatus).to eq(0) if exitstatus
+ expect(the_bundle).to include_gems "signed_gem 1.0"
+ end
+end
diff --git a/spec/bundler/install/yanked_spec.rb b/spec/bundler/install/yanked_spec.rb
new file mode 100644
index 0000000000..d42978ce4c
--- /dev/null
+++ b/spec/bundler/install/yanked_spec.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+require "spec_helper"
+
+RSpec.context "when installing a bundle that includes yanked gems" do
+ before(:each) do
+ build_repo4 do
+ build_gem "foo", "9.0.0"
+ end
+ end
+
+ it "throws an error when the original gem version is yanked" do
+ lockfile <<-L
+ GEM
+ remote: file://#{gem_repo4}
+ specs:
+ foo (10.0.0)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ foo (= 10.0.0)
+
+ L
+
+ install_gemfile <<-G
+ source "file://#{gem_repo4}"
+ gem "foo", "10.0.0"
+ G
+
+ expect(out).to include("Your bundle is locked to foo (10.0.0)")
+ end
+
+ it "throws the original error when only the Gemfile specifies a gem version that doesn't exist" do
+ install_gemfile <<-G
+ source "file://#{gem_repo4}"
+ gem "foo", "10.0.0"
+ G
+
+ expect(out).not_to include("Your bundle is locked to foo (10.0.0)")
+ expect(out).to include("Could not find gem 'foo (= 10.0.0)' in any of the gem sources")
+ end
+end
+
+RSpec.context "when using gem before installing" do
+ it "does not suggest the author has yanked the gem" do
+ gemfile <<-G
+ source "file://#{gem_repo1}"
+ gem "rack", "0.9.1"
+ G
+
+ lockfile <<-L
+ GEM
+ remote: file://#{gem_repo1}
+ specs:
+ rack (0.9.1)
+
+ PLATFORMS
+ ruby
+
+ DEPENDENCIES
+ rack (= 0.9.1)
+ L
+
+ bundle :list
+
+ expect(out).to include("Could not find rack-0.9.1 in any of the sources")
+ expect(out).to_not include("Your bundle is locked to rack (0.9.1), but that version could not be found in any of the sources listed in your Gemfile.")
+ expect(out).to_not include("If you haven't changed sources, that means the author of rack (0.9.1) has removed it.")
+ expect(out).to_not include("You'll need to update your bundle to a different version of rack (0.9.1) that hasn't been removed in order to install.")
+ end
+end