mirror of
https://github.com/ruby/ruby.git
synced 2025-08-25 14:05:02 +02:00

If we're in inline mode, Bundler first resolves using only local gems,
and if some gems are missing, then it re-resolves using remote gems.
However, "source resolution" from the initial "local" try was being
memoized, resulting in Bundler not looking for some gems remotely in the
second resolution.
This commit forces a proper re-resolve in this case.
fdc631075e
626 lines
15 KiB
Ruby
626 lines
15 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
RSpec.describe "bundler/inline#gemfile" do
|
|
def script(code, options = {})
|
|
requires = ["#{entrypoint}/inline"]
|
|
requires.unshift "#{spec_dir}/support/artifice/" + options.delete(:artifice) if options.key?(:artifice)
|
|
requires = requires.map {|r| "require '#{r}'" }.join("\n")
|
|
ruby("#{requires}\n\n" + code, options)
|
|
end
|
|
|
|
before :each do
|
|
build_lib "one", "1.0.0" do |s|
|
|
s.write "lib/baz.rb", "puts 'baz'"
|
|
s.write "lib/qux.rb", "puts 'qux'"
|
|
end
|
|
|
|
build_lib "two", "1.0.0" do |s|
|
|
s.write "lib/two.rb", "puts 'two'"
|
|
s.add_dependency "three", "= 1.0.0"
|
|
end
|
|
|
|
build_lib "three", "1.0.0" do |s|
|
|
s.write "lib/three.rb", "puts 'three'"
|
|
s.add_dependency "seven", "= 1.0.0"
|
|
end
|
|
|
|
build_lib "four", "1.0.0" do |s|
|
|
s.write "lib/four.rb", "puts 'four'"
|
|
end
|
|
|
|
build_lib "five", "1.0.0", :no_default => true do |s|
|
|
s.write "lib/mofive.rb", "puts 'five'"
|
|
end
|
|
|
|
build_lib "six", "1.0.0" do |s|
|
|
s.write "lib/six.rb", "puts 'six'"
|
|
end
|
|
|
|
build_lib "seven", "1.0.0" do |s|
|
|
s.write "lib/seven.rb", "puts 'seven'"
|
|
end
|
|
|
|
build_lib "eight", "1.0.0" do |s|
|
|
s.write "lib/eight.rb", "puts 'eight'"
|
|
end
|
|
end
|
|
|
|
it "requires the gems" do
|
|
script <<-RUBY
|
|
gemfile do
|
|
source "#{file_uri_for(gem_repo1)}"
|
|
path "#{lib_path}" do
|
|
gem "two"
|
|
end
|
|
end
|
|
RUBY
|
|
|
|
expect(out).to eq("two")
|
|
|
|
script <<-RUBY, :raise_on_error => false
|
|
gemfile do
|
|
source "#{file_uri_for(gem_repo1)}"
|
|
path "#{lib_path}" do
|
|
gem "eleven"
|
|
end
|
|
end
|
|
|
|
puts "success"
|
|
RUBY
|
|
|
|
expect(err).to include "Could not find gem 'eleven'"
|
|
expect(out).not_to include "success"
|
|
|
|
script <<-RUBY
|
|
gemfile(true) do
|
|
source "#{file_uri_for(gem_repo1)}"
|
|
gem "rack"
|
|
end
|
|
RUBY
|
|
|
|
expect(out).to include("Rack's post install message")
|
|
|
|
script <<-RUBY, :artifice => "endpoint"
|
|
gemfile(true) do
|
|
source "https://notaserver.com"
|
|
gem "activesupport", :require => true
|
|
end
|
|
RUBY
|
|
|
|
expect(out).to include("Installing activesupport")
|
|
err_lines = err.split("\n")
|
|
err_lines.reject! {|line| line =~ /\.rb:\d+: warning: / } unless RUBY_VERSION < "2.7"
|
|
expect(err_lines).to be_empty
|
|
end
|
|
|
|
it "lets me use my own ui object" do
|
|
script <<-RUBY, :artifice => "endpoint"
|
|
require '#{entrypoint}'
|
|
class MyBundlerUI < Bundler::UI::Shell
|
|
def confirm(msg, newline = nil)
|
|
puts "CONFIRMED!"
|
|
end
|
|
end
|
|
my_ui = MyBundlerUI.new
|
|
my_ui.level = "confirm"
|
|
gemfile(true, :ui => my_ui) do
|
|
source "https://notaserver.com"
|
|
gem "activesupport", :require => true
|
|
end
|
|
RUBY
|
|
|
|
expect(out).to eq("CONFIRMED!\nCONFIRMED!")
|
|
end
|
|
|
|
it "has an option for quiet installation" do
|
|
script <<-RUBY, :artifice => "endpoint"
|
|
require '#{entrypoint}/inline'
|
|
|
|
gemfile(true, :quiet => true) do
|
|
source "https://notaserver.com"
|
|
gem "activesupport", :require => true
|
|
end
|
|
RUBY
|
|
|
|
expect(out).to be_empty
|
|
end
|
|
|
|
it "raises an exception if passed unknown arguments" do
|
|
script <<-RUBY, :raise_on_error => false
|
|
gemfile(true, :arglebargle => true) do
|
|
path "#{lib_path}"
|
|
gem "two"
|
|
end
|
|
|
|
puts "success"
|
|
RUBY
|
|
expect(err).to include "Unknown options: arglebargle"
|
|
expect(out).not_to include "success"
|
|
end
|
|
|
|
it "does not mutate the option argument" do
|
|
script <<-RUBY
|
|
require '#{entrypoint}'
|
|
options = { :ui => Bundler::UI::Shell.new }
|
|
gemfile(false, options) do
|
|
source "#{file_uri_for(gem_repo1)}"
|
|
path "#{lib_path}" do
|
|
gem "two"
|
|
end
|
|
end
|
|
puts "OKAY" if options.key?(:ui)
|
|
RUBY
|
|
|
|
expect(out).to match("OKAY")
|
|
end
|
|
|
|
it "installs quietly if necessary when the install option is not set" do
|
|
script <<-RUBY
|
|
gemfile do
|
|
source "#{file_uri_for(gem_repo1)}"
|
|
gem "rack"
|
|
end
|
|
|
|
puts RACK
|
|
RUBY
|
|
|
|
expect(out).to eq("1.0.0")
|
|
expect(err).to be_empty
|
|
end
|
|
|
|
it "installs subdependencies quietly if necessary when the install option is not set" do
|
|
build_repo4 do
|
|
build_gem "rack" do |s|
|
|
s.add_dependency "rackdep"
|
|
end
|
|
|
|
build_gem "rackdep", "1.0.0"
|
|
end
|
|
|
|
script <<-RUBY
|
|
gemfile do
|
|
source "#{file_uri_for(gem_repo4)}"
|
|
gem "rack"
|
|
end
|
|
|
|
require "rackdep"
|
|
puts RACKDEP
|
|
RUBY
|
|
|
|
expect(out).to eq("1.0.0")
|
|
expect(err).to be_empty
|
|
end
|
|
|
|
it "installs subdependencies quietly if necessary when the install option is not set, and multiple sources used" do
|
|
build_repo4 do
|
|
build_gem "rack" do |s|
|
|
s.add_dependency "rackdep"
|
|
end
|
|
|
|
build_gem "rackdep", "1.0.0"
|
|
end
|
|
|
|
script <<-RUBY
|
|
gemfile do
|
|
source "#{file_uri_for(gem_repo1)}"
|
|
source "#{file_uri_for(gem_repo4)}" do
|
|
gem "rack"
|
|
end
|
|
end
|
|
|
|
require "rackdep"
|
|
puts RACKDEP
|
|
RUBY
|
|
|
|
expect(out).to eq("1.0.0")
|
|
expect(err).to be_empty
|
|
end
|
|
|
|
it "installs quietly from git if necessary when the install option is not set" do
|
|
build_git "foo", "1.0.0"
|
|
baz_ref = build_git("baz", "2.0.0").ref_for("HEAD")
|
|
script <<-RUBY
|
|
gemfile do
|
|
source "#{file_uri_for(gem_repo1)}"
|
|
gem "foo", :git => #{lib_path("foo-1.0.0").to_s.dump}
|
|
gem "baz", :git => #{lib_path("baz-2.0.0").to_s.dump}, :ref => #{baz_ref.dump}
|
|
end
|
|
|
|
puts FOO
|
|
puts BAZ
|
|
RUBY
|
|
|
|
expect(out).to eq("1.0.0\n2.0.0")
|
|
expect(err).to be_empty
|
|
end
|
|
|
|
it "allows calling gemfile twice" do
|
|
script <<-RUBY
|
|
gemfile do
|
|
path "#{lib_path}" do
|
|
source "#{file_uri_for(gem_repo1)}"
|
|
gem "two"
|
|
end
|
|
end
|
|
|
|
gemfile do
|
|
path "#{lib_path}" do
|
|
source "#{file_uri_for(gem_repo1)}"
|
|
gem "four"
|
|
end
|
|
end
|
|
RUBY
|
|
|
|
expect(out).to eq("two\nfour")
|
|
expect(err).to be_empty
|
|
end
|
|
|
|
it "doesn't reinstall already installed gems" do
|
|
system_gems "rack-1.0.0"
|
|
|
|
script <<-RUBY
|
|
require '#{entrypoint}'
|
|
ui = Bundler::UI::Shell.new
|
|
ui.level = "confirm"
|
|
|
|
gemfile(true, ui: ui) do
|
|
source "#{file_uri_for(gem_repo1)}"
|
|
gem "activesupport"
|
|
gem "rack"
|
|
end
|
|
RUBY
|
|
|
|
expect(out).to include("Installing activesupport")
|
|
expect(out).not_to include("Installing rack")
|
|
expect(err).to be_empty
|
|
end
|
|
|
|
it "installs gems in later gemfile calls" do
|
|
system_gems "rack-1.0.0"
|
|
|
|
script <<-RUBY
|
|
require '#{entrypoint}'
|
|
ui = Bundler::UI::Shell.new
|
|
ui.level = "confirm"
|
|
gemfile(true, ui: ui) do
|
|
source "#{file_uri_for(gem_repo1)}"
|
|
gem "rack"
|
|
end
|
|
|
|
gemfile(true, ui: ui) do
|
|
source "#{file_uri_for(gem_repo1)}"
|
|
gem "activesupport"
|
|
end
|
|
RUBY
|
|
|
|
expect(out).to include("Installing activesupport")
|
|
expect(out).not_to include("Installing rack")
|
|
expect(err).to be_empty
|
|
end
|
|
|
|
it "doesn't reinstall already installed gems in later gemfile calls" do
|
|
system_gems "rack-1.0.0"
|
|
|
|
script <<-RUBY
|
|
require '#{entrypoint}'
|
|
ui = Bundler::UI::Shell.new
|
|
ui.level = "confirm"
|
|
gemfile(true, ui: ui) do
|
|
source "#{file_uri_for(gem_repo1)}"
|
|
gem "activesupport"
|
|
end
|
|
|
|
gemfile(true, ui: ui) do
|
|
source "#{file_uri_for(gem_repo1)}"
|
|
gem "rack"
|
|
end
|
|
RUBY
|
|
|
|
expect(out).to include("Installing activesupport")
|
|
expect(out).not_to include("Installing rack")
|
|
expect(err).to be_empty
|
|
end
|
|
|
|
it "installs gems with native extensions in later gemfile calls" do
|
|
system_gems "rack-1.0.0"
|
|
|
|
build_git "foo" do |s|
|
|
s.add_dependency "rake"
|
|
s.extensions << "Rakefile"
|
|
s.write "Rakefile", <<-RUBY
|
|
task :default do
|
|
path = File.expand_path("lib", __dir__)
|
|
FileUtils.mkdir_p(path)
|
|
File.open("\#{path}/foo.rb", "w") do |f|
|
|
f.puts "FOO = 'YES'"
|
|
end
|
|
end
|
|
RUBY
|
|
end
|
|
|
|
script <<-RUBY
|
|
require '#{entrypoint}'
|
|
ui = Bundler::UI::Shell.new
|
|
ui.level = "confirm"
|
|
gemfile(true, ui: ui) do
|
|
source "#{file_uri_for(gem_repo1)}"
|
|
gem "rack"
|
|
end
|
|
|
|
gemfile(true, ui: ui) do
|
|
source "#{file_uri_for(gem_repo1)}"
|
|
gem "foo", :git => "#{lib_path("foo-1.0")}"
|
|
end
|
|
|
|
require 'foo'
|
|
puts FOO
|
|
puts $:.grep(/ext/)
|
|
RUBY
|
|
|
|
expect(out).to include("YES")
|
|
expect(out).to include(Pathname.glob(default_bundle_path("bundler/gems/extensions/**/foo-1.0-*")).first.to_s)
|
|
expect(err).to be_empty
|
|
end
|
|
|
|
it "installs inline gems when a Gemfile.lock is present" do
|
|
gemfile <<-G
|
|
source "https://notaserver.com"
|
|
gem "rake"
|
|
G
|
|
|
|
lockfile <<-G
|
|
GEM
|
|
remote: https://rubygems.org/
|
|
specs:
|
|
rake (11.3.0)
|
|
|
|
PLATFORMS
|
|
ruby
|
|
|
|
DEPENDENCIES
|
|
rake
|
|
|
|
BUNDLED WITH
|
|
#{Bundler::VERSION}
|
|
G
|
|
|
|
script <<-RUBY
|
|
gemfile do
|
|
source "#{file_uri_for(gem_repo1)}"
|
|
gem "rack"
|
|
end
|
|
|
|
puts RACK
|
|
RUBY
|
|
|
|
expect(err).to be_empty
|
|
end
|
|
|
|
it "does not leak Gemfile.lock versions to the installation output" do
|
|
gemfile <<-G
|
|
source "https://notaserver.com"
|
|
gem "rake"
|
|
G
|
|
|
|
lockfile <<-G
|
|
GEM
|
|
remote: https://rubygems.org/
|
|
specs:
|
|
rake (11.3.0)
|
|
|
|
PLATFORMS
|
|
ruby
|
|
|
|
DEPENDENCIES
|
|
rake
|
|
|
|
BUNDLED WITH
|
|
#{Bundler::VERSION}
|
|
G
|
|
|
|
script <<-RUBY
|
|
gemfile(true) do
|
|
source "#{file_uri_for(gem_repo1)}"
|
|
gem "rake", "~> 13.0"
|
|
end
|
|
RUBY
|
|
|
|
expect(out).to include("Installing rake 13.0")
|
|
expect(out).not_to include("was 11.3.0")
|
|
expect(err).to be_empty
|
|
end
|
|
|
|
it "installs inline gems when frozen is set" do
|
|
script <<-RUBY, :env => { "BUNDLE_FROZEN" => "true" }
|
|
gemfile do
|
|
source "#{file_uri_for(gem_repo1)}"
|
|
gem "rack"
|
|
end
|
|
|
|
puts RACK
|
|
RUBY
|
|
|
|
expect(last_command.stderr).to be_empty
|
|
end
|
|
|
|
it "installs inline gems when deployment is set" do
|
|
script <<-RUBY, :env => { "BUNDLE_DEPLOYMENT" => "true" }
|
|
gemfile do
|
|
source "#{file_uri_for(gem_repo1)}"
|
|
gem "rack"
|
|
end
|
|
|
|
puts RACK
|
|
RUBY
|
|
|
|
expect(last_command.stderr).to be_empty
|
|
end
|
|
|
|
it "installs inline gems when BUNDLE_GEMFILE is set to an empty string" do
|
|
ENV["BUNDLE_GEMFILE"] = ""
|
|
|
|
script <<-RUBY
|
|
gemfile do
|
|
source "#{file_uri_for(gem_repo1)}"
|
|
gem "rack"
|
|
end
|
|
|
|
puts RACK
|
|
RUBY
|
|
|
|
expect(err).to be_empty
|
|
end
|
|
|
|
it "installs inline gems when BUNDLE_BIN is set" do
|
|
ENV["BUNDLE_BIN"] = "/usr/local/bundle/bin"
|
|
|
|
script <<-RUBY
|
|
gemfile do
|
|
source "#{file_uri_for(gem_repo1)}"
|
|
gem "rack" # has the rackup executable
|
|
end
|
|
|
|
puts RACK
|
|
RUBY
|
|
expect(last_command).to be_success
|
|
expect(out).to eq "1.0.0"
|
|
end
|
|
|
|
context "when BUNDLE_PATH is set" do
|
|
it "installs inline gems to the system path regardless" do
|
|
script <<-RUBY, :env => { "BUNDLE_PATH" => "./vendor/inline" }
|
|
gemfile(true) do
|
|
source "#{file_uri_for(gem_repo1)}"
|
|
gem "rack"
|
|
end
|
|
RUBY
|
|
expect(last_command).to be_success
|
|
expect(system_gem_path("gems/rack-1.0.0")).to exist
|
|
end
|
|
end
|
|
|
|
it "skips platform warnings" do
|
|
bundle "config set --local force_ruby_platform true"
|
|
|
|
script <<-RUBY
|
|
gemfile(true) do
|
|
source "#{file_uri_for(gem_repo1)}"
|
|
gem "rack", platform: :jruby
|
|
end
|
|
RUBY
|
|
|
|
expect(err).to be_empty
|
|
end
|
|
|
|
it "still installs if the application has `bundle package` no_install config set" do
|
|
bundle "config set --local no_install true"
|
|
|
|
script <<-RUBY
|
|
gemfile do
|
|
source "#{file_uri_for(gem_repo1)}"
|
|
gem "rack"
|
|
end
|
|
RUBY
|
|
|
|
expect(last_command).to be_success
|
|
expect(system_gem_path("gems/rack-1.0.0")).to exist
|
|
end
|
|
|
|
it "preserves previous BUNDLE_GEMFILE value" do
|
|
ENV["BUNDLE_GEMFILE"] = ""
|
|
script <<-RUBY
|
|
gemfile do
|
|
source "#{file_uri_for(gem_repo1)}"
|
|
gem "rack"
|
|
end
|
|
|
|
puts "BUNDLE_GEMFILE is empty" if ENV["BUNDLE_GEMFILE"].empty?
|
|
system("#{Gem.ruby} -w -e '42'") # this should see original value of BUNDLE_GEMFILE
|
|
exit $?.exitstatus
|
|
RUBY
|
|
|
|
expect(last_command).to be_success
|
|
expect(out).to include("BUNDLE_GEMFILE is empty")
|
|
end
|
|
|
|
it "resets BUNDLE_GEMFILE to the empty string if it wasn't set previously" do
|
|
ENV["BUNDLE_GEMFILE"] = nil
|
|
script <<-RUBY
|
|
gemfile do
|
|
source "#{file_uri_for(gem_repo1)}"
|
|
gem "rack"
|
|
end
|
|
|
|
puts "BUNDLE_GEMFILE is empty" if ENV["BUNDLE_GEMFILE"].empty?
|
|
system("#{Gem.ruby} -w -e '42'") # this should see original value of BUNDLE_GEMFILE
|
|
exit $?.exitstatus
|
|
RUBY
|
|
|
|
expect(last_command).to be_success
|
|
expect(out).to include("BUNDLE_GEMFILE is empty")
|
|
end
|
|
|
|
it "does not error out if library requires optional dependencies" do
|
|
Dir.mkdir tmp("path_without_gemfile")
|
|
|
|
foo_code = <<~RUBY
|
|
begin
|
|
gem "bar"
|
|
rescue LoadError
|
|
end
|
|
|
|
puts "WIN"
|
|
RUBY
|
|
|
|
build_lib "foo", "1.0.0" do |s|
|
|
s.write "lib/foo.rb", foo_code
|
|
end
|
|
|
|
script <<-RUBY, :dir => tmp("path_without_gemfile")
|
|
gemfile do
|
|
source "#{file_uri_for(gem_repo2)}"
|
|
path "#{lib_path}" do
|
|
gem "foo", require: false
|
|
end
|
|
end
|
|
|
|
require "foo"
|
|
RUBY
|
|
|
|
expect(out).to eq("WIN")
|
|
expect(err).to be_empty
|
|
end
|
|
|
|
it "when requiring fileutils after does not show redefinition warnings", :realworld do
|
|
dependency_installer_loads_fileutils = ruby "require 'rubygems/dependency_installer'; puts $LOADED_FEATURES.grep(/fileutils/)", :raise_on_error => false
|
|
skip "does not work if rubygems/dependency_installer loads fileutils, which happens until rubygems 3.2.0" unless dependency_installer_loads_fileutils.empty?
|
|
|
|
skip "pathname does not install cleanly on this ruby" if RUBY_VERSION < "2.7.0"
|
|
|
|
Dir.mkdir tmp("path_without_gemfile")
|
|
|
|
default_fileutils_version = ruby "gem 'fileutils', '< 999999'; require 'fileutils'; puts FileUtils::VERSION", :raise_on_error => false
|
|
skip "fileutils isn't a default gem" if default_fileutils_version.empty?
|
|
|
|
realworld_system_gems "fileutils --version 1.4.1"
|
|
|
|
realworld_system_gems "pathname --version 0.2.0"
|
|
|
|
realworld_system_gems "timeout uri" # this spec uses net/http which requires these default gems
|
|
|
|
# on prerelease rubies, a required_rubygems_version constraint is added by RubyGems to the resolution, causing Molinillo to load the `set` gem
|
|
realworld_system_gems "set --version 1.0.3" if Gem.ruby_version.prerelease?
|
|
|
|
script <<-RUBY, :dir => tmp("path_without_gemfile"), :env => { "BUNDLER_GEM_DEFAULT_DIR" => system_gem_path.to_s }
|
|
require "bundler/inline"
|
|
|
|
gemfile(true) do
|
|
source "#{file_uri_for(gem_repo2)}"
|
|
end
|
|
|
|
require "fileutils"
|
|
RUBY
|
|
|
|
expect(err).to eq("The Gemfile specifies no dependencies")
|
|
end
|
|
end
|