mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
8f37629519
Pick from 8dd59e3ba97eb80a599f8149f31bf40773b69dc0
647 lines
20 KiB
Ruby
647 lines
20 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
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", :bundler => "< 3" do
|
|
let(:repo3_rack_version) { "1.0.0" }
|
|
|
|
before do
|
|
gemfile <<-G
|
|
source "file://localhost#{gem_repo3}"
|
|
source "file://localhost#{gem_repo1}"
|
|
gem "rack-obama"
|
|
gem "rack"
|
|
G
|
|
end
|
|
|
|
it "warns about ambiguous gems, but installs anyway, prioritizing sources last to first", :bundler => "2" do
|
|
bundle :install
|
|
|
|
expect(err).to include("Warning: the gem 'rack' was found in multiple sources.")
|
|
expect(err).to include(normalize_uri_file("Installed from: file://localhost#{gem_repo1}"))
|
|
expect(the_bundle).to include_gems("rack-obama 1.0.0", "rack 1.0.0", :source => "remote1")
|
|
end
|
|
|
|
it "fails", :bundler => "3" do
|
|
bundle :install
|
|
expect(err).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", :bundler => "< 3" do
|
|
let(:repo3_rack_version) { "1.2" }
|
|
|
|
before do
|
|
gemfile <<-G
|
|
source "file://localhost#{gem_repo3}"
|
|
source "file://localhost#{gem_repo1}"
|
|
gem "rack-obama"
|
|
gem "rack", "1.0.0" # force it to install the working version in repo1
|
|
G
|
|
|
|
bundle :install
|
|
end
|
|
|
|
it "warns about ambiguous gems, but installs anyway", :bundler => "2" do
|
|
expect(err).to include("Warning: the gem 'rack' was found in multiple sources.")
|
|
expect(err).to include(normalize_uri_file("Installed from: file://localhost#{gem_repo1}"))
|
|
expect(the_bundle).to include_gems("rack-obama 1.0.0", "rack 1.0.0", :source => "remote1")
|
|
end
|
|
|
|
it "fails", :bundler => "3" do
|
|
expect(err).to include("Each source after the first must include a block")
|
|
expect(exitstatus).to eq(4) if exitstatus
|
|
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
|
|
|
|
build_gem "rack-obama" do |s|
|
|
s.add_dependency "rack"
|
|
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, forgotten_command_line_options(:deployment => true)
|
|
|
|
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
|
|
|
|
build_gem "rack-obama" do |s|
|
|
s.add_dependency "rack"
|
|
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 "when a pinned gem has 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
|
|
|
|
context "when disable_multisource is set" do
|
|
before do
|
|
bundle! "config set disable_multisource true"
|
|
end
|
|
|
|
it "installs from the same source without any warning" do
|
|
bundle! :install
|
|
|
|
expect(out).not_to include("Warning: the gem 'rack' was found in multiple sources.")
|
|
expect(err).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")
|
|
|
|
# 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(err).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 "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", :bundler => "< 3" do
|
|
before do
|
|
gemfile <<-G
|
|
source "file://localhost#{gem_repo1}"
|
|
source "file://localhost#{gem_repo2}"
|
|
source "file://localhost#{gem_repo3}" do
|
|
gem "depends_on_rack"
|
|
end
|
|
G
|
|
|
|
bundle :install
|
|
end
|
|
|
|
it "installs from the other source and warns about ambiguous gems", :bundler => "2" do
|
|
expect(err).to include("Warning: the gem 'rack' was found in multiple sources.")
|
|
expect(err).to include(normalize_uri_file("Installed from: file://localhost#{gem_repo2}"))
|
|
expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0")
|
|
end
|
|
|
|
it "fails", :bundler => "3" do
|
|
expect(err).to include("Each source after the first must include a block")
|
|
expect(exitstatus).to eq(4) if exitstatus
|
|
end
|
|
end
|
|
|
|
context "and only the dependency is pinned", :bundler => "< 3" 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", :bundler => "2" do
|
|
bundle :install
|
|
|
|
expect(err).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(err).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
|
|
|
|
it "fails", :bundler => "3" do
|
|
bundle :install
|
|
expect(err).to include("Each source after the first must include a block")
|
|
expect(exitstatus).to eq(4) if exitstatus
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when a top-level gem has an indirect dependency" do
|
|
context "when disable_multisource is set" do
|
|
before do
|
|
bundle! "config set disable_multisource true"
|
|
end
|
|
|
|
before do
|
|
build_repo gem_repo2 do
|
|
build_gem "depends_on_rack", "1.0.1" do |s|
|
|
s.add_dependency "rack"
|
|
end
|
|
end
|
|
|
|
build_repo gem_repo3 do
|
|
build_gem "unrelated_gem", "1.0.0"
|
|
end
|
|
|
|
gemfile <<-G
|
|
source "file://#{gem_repo2}"
|
|
|
|
gem "depends_on_rack"
|
|
|
|
source "file://#{gem_repo3}" do
|
|
gem "unrelated_gem"
|
|
end
|
|
G
|
|
end
|
|
|
|
context "and the dependency is only in the top-level source" do
|
|
before do
|
|
update_repo gem_repo2 do
|
|
build_gem "rack", "1.0.0"
|
|
end
|
|
end
|
|
|
|
it "installs all gems without warning" do
|
|
bundle :install
|
|
expect(err).not_to include("Warning")
|
|
expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", "unrelated_gem 1.0.0")
|
|
end
|
|
end
|
|
|
|
context "and the dependency is only in a pinned source" do
|
|
before do
|
|
update_repo gem_repo3 do
|
|
build_gem "rack", "1.0.0" do |s|
|
|
s.write "lib/rack.rb", "RACK = 'FAIL'"
|
|
end
|
|
end
|
|
end
|
|
|
|
it "does not find the dependency" do
|
|
bundle :install
|
|
expect(err).to include("Could not find gem 'rack', which is required by gem 'depends_on_rack', in any of the relevant sources")
|
|
end
|
|
end
|
|
|
|
context "and the dependency is in both the top-level and a pinned source" do
|
|
before do
|
|
update_repo gem_repo2 do
|
|
build_gem "rack", "1.0.0"
|
|
end
|
|
|
|
update_repo gem_repo3 do
|
|
build_gem "rack", "1.0.0" do |s|
|
|
s.write "lib/rack.rb", "RACK = 'FAIL'"
|
|
end
|
|
end
|
|
end
|
|
|
|
it "installs the dependency from the top-level source without warning" do
|
|
bundle :install
|
|
expect(err).not_to include("Warning")
|
|
expect(the_bundle).to include_gems("depends_on_rack 1.0.1", "rack 1.0.0", "unrelated_gem 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(err).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", :path => :bundle_path
|
|
|
|
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")
|
|
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(err).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
|
|
# With these gems,
|
|
build_repo4 do
|
|
build_gem "foo", "0.1"
|
|
build_gem "bar", "0.1"
|
|
end
|
|
|
|
# Installing this gemfile...
|
|
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
|
|
|
|
bundle! :install, forgotten_command_line_options(:path => "../gems/system")
|
|
|
|
# And 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
|
|
# 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
|
|
|
|
# It should update foo to 0.2, but not the (locked) bar 0.1
|
|
expect(the_bundle).to include_gems("foo 0.2", "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...
|
|
install_gemfile <<-G
|
|
source "#{source_uri}"
|
|
|
|
source "#{second_uri}" do
|
|
gem "rack", "2.0.1.1.forked"
|
|
gem "thor"
|
|
end
|
|
gem "rack-obama"
|
|
G
|
|
|
|
# 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
|
|
|
|
describe "source changed to one containing a higher version of a dependency" do
|
|
before do
|
|
install_gemfile! <<-G
|
|
source "file://#{gem_repo1}"
|
|
|
|
gem "rack"
|
|
G
|
|
|
|
build_repo2 do
|
|
build_gem "bar"
|
|
end
|
|
|
|
build_lib("gemspec_test", :path => tmp.join("gemspec_test")) do |s|
|
|
s.add_dependency "bar", "=1.0.0"
|
|
end
|
|
|
|
install_gemfile <<-G
|
|
source "file://#{gem_repo2}"
|
|
gem "rack"
|
|
gemspec :path => "#{tmp.join("gemspec_test")}"
|
|
G
|
|
end
|
|
|
|
it "keeps the old version", :bundler => "2" do
|
|
expect(the_bundle).to include_gems("rack 1.0.0")
|
|
end
|
|
|
|
it "installs the higher version in the new repo", :bundler => "3" do
|
|
expect(the_bundle).to include_gems("rack 1.2")
|
|
end
|
|
end
|
|
|
|
context "when a gem is available from multiple ambiguous sources", :bundler => "3" do
|
|
it "raises, suggesting a source block" do
|
|
build_repo4 do
|
|
build_gem "depends_on_rack" do |s|
|
|
s.add_dependency "rack"
|
|
end
|
|
build_gem "rack"
|
|
end
|
|
|
|
install_gemfile <<-G
|
|
source "file://localhost#{gem_repo4}"
|
|
source "file://localhost#{gem_repo1}" do
|
|
gem "thin"
|
|
end
|
|
gem "depends_on_rack"
|
|
G
|
|
expect(last_command).to be_failure
|
|
expect(err).to eq normalize_uri_file(strip_whitespace(<<-EOS).strip)
|
|
The gem 'rack' was found in multiple relevant sources.
|
|
* rubygems repository file://localhost#{gem_repo1}/ or installed locally
|
|
* rubygems repository file://localhost#{gem_repo4}/ or installed locally
|
|
You must add this gem to the source block for the source you wish it to be installed from.
|
|
EOS
|
|
expect(the_bundle).not_to be_locked
|
|
end
|
|
end
|
|
end
|