diff --git a/lib/bundler.rb b/lib/bundler.rb index 81dfd05d26..89865a8254 100644 --- a/lib/bundler.rb +++ b/lib/bundler.rb @@ -66,6 +66,7 @@ module Bundler autoload :RubyDsl, File.expand_path("bundler/ruby_dsl", __dir__) autoload :RubyVersion, File.expand_path("bundler/ruby_version", __dir__) autoload :Runtime, File.expand_path("bundler/runtime", __dir__) + autoload :SelfManager, File.expand_path("bundler/self_manager", __dir__) autoload :Settings, File.expand_path("bundler/settings", __dir__) autoload :SharedHelpers, File.expand_path("bundler/shared_helpers", __dir__) autoload :Source, File.expand_path("bundler/source", __dir__) @@ -643,6 +644,13 @@ EOF Bundler.rubygems.clear_paths end + def self_manager + @self_manager ||= begin + require_relative "bundler/self_manager" + Bundler::SelfManager.new + end + end + private def eval_yaml_gemspec(path, contents) diff --git a/lib/bundler/cli.rb b/lib/bundler/cli.rb index 36d0472c62..f6e20e7c67 100644 --- a/lib/bundler/cli.rb +++ b/lib/bundler/cli.rb @@ -61,6 +61,8 @@ module Bundler Bundler.reset_settings_and_root! end + Bundler.self_manager.restart_with_locked_bundler_if_needed + Bundler.settings.set_command_option_if_given :retry, options[:retry] current_cmd = args.last[:current_command].name diff --git a/lib/bundler/cli/install.rb b/lib/bundler/cli/install.rb index 4c1915fea6..85f702bc64 100644 --- a/lib/bundler/cli/install.rb +++ b/lib/bundler/cli/install.rb @@ -12,6 +12,8 @@ module Bundler warn_if_root + Bundler.self_manager.install_locked_bundler_and_restart_with_it_if_needed + Bundler::SharedHelpers.set_env "RB_USER_INSTALL", "1" if Bundler::FREEBSD # Disable color in deployment mode diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index 84f0367ac7..9a94bd3ed3 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -294,7 +294,7 @@ module Bundler if updating_major = locked_major < current_major Bundler.ui.warn "Warning: the lockfile is being updated to Bundler #{current_major}, " \ - "after which you will be unable to return to Bundler #{@locked_bundler_version.segments.first}." + "after which you will be unable to return to Bundler #{locked_major}." end end diff --git a/lib/bundler/fetcher.rb b/lib/bundler/fetcher.rb index 6dd6d089eb..a453157e68 100644 --- a/lib/bundler/fetcher.rb +++ b/lib/bundler/fetcher.rb @@ -122,7 +122,6 @@ module Bundler # return the specs in the bundler format as an index def specs(gem_names, source) - old = Bundler.rubygems.sources index = Bundler::Index.new if Bundler::Fetcher.disable_endpoint @@ -153,8 +152,6 @@ module Bundler rescue CertificateFailureError Bundler.ui.info "" if gem_names && use_api # newline after dots raise - ensure - Bundler.rubygems.sources = old end def use_api diff --git a/lib/bundler/installer.rb b/lib/bundler/installer.rb index 49ff177c0f..61bf1e06d4 100644 --- a/lib/bundler/installer.rb +++ b/lib/bundler/installer.rb @@ -239,7 +239,7 @@ module Bundler def ensure_specs_are_compatible! system_ruby = Bundler::RubyVersion.system - rubygems_version = Gem::Version.create(Gem::VERSION) + rubygems_version = Bundler.rubygems.version @definition.specs.each do |spec| if required_ruby_version = spec.required_ruby_version unless required_ruby_version.satisfied_by?(system_ruby.gem_version) diff --git a/lib/bundler/lockfile_parser.rb b/lib/bundler/lockfile_parser.rb index 6ff4910a36..e074cbfc33 100644 --- a/lib/bundler/lockfile_parser.rb +++ b/lib/bundler/lockfile_parser.rb @@ -46,6 +46,16 @@ module Bundler attributes end + def self.bundled_with + lockfile = Bundler.default_lockfile + return unless lockfile.file? + + lockfile_contents = Bundler.read_file(lockfile) + return unless lockfile_contents.include?(BUNDLED) + + lockfile_contents.split(BUNDLED).last.strip + end + def initialize(lockfile) @platforms = [] @sources = [] @@ -77,25 +87,12 @@ module Bundler end end @specs = @specs.values.sort_by(&:identifier) - warn_for_outdated_bundler_version rescue ArgumentError => e Bundler.ui.debug(e) raise LockfileError, "Your lockfile is unreadable. Run `rm #{Bundler.default_lockfile.relative_path_from(SharedHelpers.pwd)}` " \ "and then `bundle install` to generate a new lockfile." end - def warn_for_outdated_bundler_version - return unless bundler_version - return if bundler_version.segments.last == "dev" - prerelease_text = bundler_version.prerelease? ? " --pre" : "" - current_version = Gem::Version.create(Bundler::VERSION) - return unless current_version < bundler_version - Bundler.ui.warn "Warning: the running version of Bundler (#{current_version}) is older " \ - "than the version that created the lockfile (#{bundler_version}). We suggest you to " \ - "upgrade to the version that created the lockfile by running `gem install " \ - "bundler:#{bundler_version}#{prerelease_text}`.\n" - end - private TYPES = { diff --git a/lib/bundler/plugin/installer.rb b/lib/bundler/plugin/installer.rb index d7411fff45..81ecafa470 100644 --- a/lib/bundler/plugin/installer.rb +++ b/lib/bundler/plugin/installer.rb @@ -21,7 +21,7 @@ module Bundler elsif options[:local_git] install_local_git(names, version, options) else - sources = options[:source] || Bundler.rubygems.sources + sources = options[:source] || Gem.sources install_rubygems(names, version, sources) end end diff --git a/lib/bundler/rubygems_integration.rb b/lib/bundler/rubygems_integration.rb index effb88c1cd..785f7fa360 100644 --- a/lib/bundler/rubygems_integration.rb +++ b/lib/bundler/rubygems_integration.rb @@ -12,25 +12,21 @@ module Bundler EXT_LOCK = Monitor.new end - def self.version - @version ||= Gem::Version.new(Gem::VERSION) - end - - def self.provides?(req_str) - Gem::Requirement.new(req_str).satisfied_by?(version) - end - def initialize @replaced_methods = {} backport_ext_builder_monitor end def version - self.class.version + @version ||= Gem.rubygems_version end def provides?(req_str) - self.class.provides?(req_str) + Gem::Requirement.new(req_str).satisfied_by?(version) + end + + def supports_bundler_trampolining? + provides?(">= 3.3.0.a") end def build_args @@ -142,19 +138,6 @@ module Bundler end end - def sources=(val) - # Gem.configuration creates a new Gem::ConfigFile, which by default will read ~/.gemrc - # If that file exists, its settings (including sources) will overwrite the values we - # are about to set here. In order to avoid that, we force memoizing the config file now. - configuration - - Gem.sources = val - end - - def sources - Gem.sources - end - def gem_dir Gem.dir end @@ -588,6 +571,10 @@ module Bundler end end + def find_bundler(version) + find_name("bundler").find {|s| s.version.to_s == version } + end + def find_name(name) Gem::Specification.stubs_for(name).map(&:to_spec) end diff --git a/lib/bundler/self_manager.rb b/lib/bundler/self_manager.rb new file mode 100644 index 0000000000..bda2eb51f3 --- /dev/null +++ b/lib/bundler/self_manager.rb @@ -0,0 +1,73 @@ +# frozen_string_literal: true + +module Bundler + # + # This class handles installing and switching to the version of bundler needed + # by an application. + # + class SelfManager + def restart_with_locked_bundler_if_needed + return unless needs_switching? && installed? + + restart_with_locked_bundler + end + + def install_locked_bundler_and_restart_with_it_if_needed + return unless needs_switching? + + Bundler.ui.info \ + "Bundler #{current_version} is running, but your lockfile was generated with #{lockfile_version}. " \ + "Installing Bundler #{lockfile_version} and restarting using that version." + + install_and_restart_with_locked_bundler + end + + private + + def install_and_restart_with_locked_bundler + bundler_dep = Gem::Dependency.new("bundler", lockfile_version) + + Gem.install(bundler_dep) + rescue StandardError => e + Bundler.ui.trace e + Bundler.ui.warn "There was an error installing the locked bundler version (#{lockfile_version}), rerun with the `--verbose` flag for more details. Going on using bundler #{current_version}." + else + restart_with_locked_bundler + end + + def restart_with_locked_bundler + configured_gem_home = ENV["GEM_HOME"] + configured_gem_path = ENV["GEM_PATH"] + + Bundler.with_original_env do + Kernel.exec( + { "GEM_HOME" => configured_gem_home, "GEM_PATH" => configured_gem_path, "BUNDLER_VERSION" => lockfile_version }, + $PROGRAM_NAME, *ARGV + ) + end + end + + def needs_switching? + ENV["BUNDLER_VERSION"].nil? && + Bundler.rubygems.supports_bundler_trampolining? && + SharedHelpers.in_bundle? && + lockfile_version && + !lockfile_version.end_with?(".dev") && + lockfile_version != current_version + end + + def installed? + Bundler.configure + + Bundler.rubygems.find_bundler(lockfile_version) + end + + def current_version + @current_version ||= Bundler::VERSION + end + + def lockfile_version + @lockfile_version ||= Bundler::LockfileParser.bundled_with + end + end +end diff --git a/lib/bundler/source/metadata.rb b/lib/bundler/source/metadata.rb index 50b65ce0ea..8bdbaa5527 100644 --- a/lib/bundler/source/metadata.rb +++ b/lib/bundler/source/metadata.rb @@ -25,7 +25,7 @@ module Bundler s.loaded_from = File.expand_path("..", __FILE__) end - if local_spec = Bundler.rubygems.find_name("bundler").find {|s| s.version.to_s == VERSION } + if local_spec = Bundler.rubygems.find_bundler(VERSION) idx << local_spec end diff --git a/lib/bundler/templates/Executable.bundler b/lib/bundler/templates/Executable.bundler index 8009412ea2..6bb5c51090 100644 --- a/lib/bundler/templates/Executable.bundler +++ b/lib/bundler/templates/Executable.bundler @@ -73,7 +73,7 @@ m = Module.new do requirement = bundler_gem_version.approximate_recommendation - return requirement unless Gem::Version.new(Gem::VERSION) < Gem::Version.new("2.7.0") + return requirement unless Gem.rubygems_version < Gem::Version.new("2.7.0") requirement += ".a" if bundler_gem_version.prerelease? diff --git a/lib/bundler/version.rb b/lib/bundler/version.rb index 0c0d9d360d..d987722f78 100644 --- a/lib/bundler/version.rb +++ b/lib/bundler/version.rb @@ -1,7 +1,7 @@ # frozen_string_literal: false module Bundler - VERSION = "2.3.0.dev".freeze + VERSION = "2.3.0".freeze def self.bundler_major_version @bundler_major_version ||= VERSION.split(".").first.to_i diff --git a/lib/rubygems.rb b/lib/rubygems.rb index 40f3a893d8..a956e06bdb 100644 --- a/lib/rubygems.rb +++ b/lib/rubygems.rb @@ -8,7 +8,7 @@ require 'rbconfig' module Gem - VERSION = "3.3.0.dev".freeze + VERSION = "3.3.0".freeze end # Must be first since it unloads the prelude from 1.9.2 diff --git a/spec/bundler/bundler/plugin/installer_spec.rb b/spec/bundler/bundler/plugin/installer_spec.rb index e89720f6f7..2c50ee5afc 100644 --- a/spec/bundler/bundler/plugin/installer_spec.rb +++ b/spec/bundler/bundler/plugin/installer_spec.rb @@ -7,7 +7,7 @@ RSpec.describe Bundler::Plugin::Installer do it "uses Gem.sources when non of the source is provided" do sources = double(:sources) Bundler.settings # initialize it before we have to touch rubygems.ext_lock - allow(Bundler).to receive_message_chain("rubygems.sources") { sources } + allow(Gem).to receive(:sources) { sources } allow(installer).to receive(:install_rubygems). with("new-plugin", [">= 0"], sources).once diff --git a/spec/bundler/commands/binstubs_spec.rb b/spec/bundler/commands/binstubs_spec.rb index 1cd0e16d95..ceef07d36b 100644 --- a/spec/bundler/commands/binstubs_spec.rb +++ b/spec/bundler/commands/binstubs_spec.rb @@ -186,11 +186,25 @@ RSpec.describe "bundle binstubs " do before do lockfile lockfile.gsub(/BUNDLED WITH\n .*$/m, "BUNDLED WITH\n 55.0") + + update_repo2 do + with_built_bundler("55.0") {|gem_path| FileUtils.mv(gem_path, gem_repo2("gems")) } + end end - it "runs the available version of bundler when the version is older and the same major" do - sys_exec "bin/bundle install" + it "installs and runs the exact version of bundler", :rubygems => ">= 3.3.0.dev" do + sys_exec "bin/bundle install --verbose", :env => { "BUNDLER_SPEC_GEM_SOURCES" => file_uri_for(gem_repo2).to_s, "RUBYOPT" => "-r#{spec_dir}/support/hax.rb" } expect(exitstatus).not_to eq(42) + expect(out).to include("Bundler 55.1 is running, but your lockfile was generated with 55.0. Installing Bundler 55.0 and restarting using that version.") + expect(out).to include("Using bundler 55.0") + expect(err).not_to include("Activating bundler (~> 55.0) failed:") + end + + it "runs the available version of bundler", :rubygems => "< 3.3.0.dev" do + sys_exec "bin/bundle install --verbose" + expect(exitstatus).not_to eq(42) + expect(out).not_to include("Bundler 55.1 is running, but your lockfile was generated with 55.0. Installing Bundler 55.0 and restarting using that version.") + expect(out).to include("Using bundler 55.1") expect(err).not_to include("Activating bundler (~> 55.0) failed:") end end diff --git a/spec/bundler/commands/check_spec.rb b/spec/bundler/commands/check_spec.rb index 1fa35136eb..e0c9a04c25 100644 --- a/spec/bundler/commands/check_spec.rb +++ b/spec/bundler/commands/check_spec.rb @@ -471,10 +471,10 @@ RSpec.describe "bundle check" do end context "is newer" do - it "does not change the lock but warns" do + it "does not change the lock and does not warn" do lockfile lock_with(Bundler::VERSION.succ) bundle :check - expect(err).to include("the running version of Bundler (#{Bundler::VERSION}) is older than the version that created the lockfile (#{Bundler::VERSION.succ})") + expect(err).to be_empty expect(lockfile).to eq lock_with(Bundler::VERSION.succ) end end diff --git a/spec/bundler/lock/lockfile_spec.rb b/spec/bundler/lock/lockfile_spec.rb index 8befb0d400..a2a50d4cf0 100644 --- a/spec/bundler/lock/lockfile_spec.rb +++ b/spec/bundler/lock/lockfile_spec.rb @@ -39,7 +39,7 @@ RSpec.describe "the lockfile format" do G end - it "updates the lockfile's bundler version if current ver. is newer" do + it "updates the lockfile's bundler version if current ver. is newer, and version was forced through BUNDLER_VERSION" do system_gems "bundler-1.8.2" lockfile <<-L @@ -64,12 +64,15 @@ RSpec.describe "the lockfile format" do 1.8.2 L - install_gemfile <<-G, :env => { "BUNDLER_VERSION" => Bundler::VERSION } + install_gemfile <<-G, :verbose => true, :env => { "BUNDLER_VERSION" => Bundler::VERSION } source "#{file_uri_for(gem_repo2)}" gem "rack" G + expect(out).not_to include("Bundler #{Bundler::VERSION} is running, but your lockfile was generated with 1.8.2.") + expect(out).to include("Using bundler #{Bundler::VERSION}") + expect(lockfile).to eq <<~G GEM remote: #{file_uri_for(gem_repo2)}/ @@ -87,9 +90,13 @@ RSpec.describe "the lockfile format" do G end - it "does not update the lockfile's bundler version if nothing changed during bundle install" do + it "does not update the lockfile's bundler version if nothing changed during bundle install, but uses the locked version", :rubygems => ">= 3.3.0.a" do version = "#{Bundler::VERSION.split(".").first}.0.0.a" + update_repo2 do + with_built_bundler(version) {|gem_path| FileUtils.mv(gem_path, gem_repo2("gems")) } + end + lockfile <<-L GEM remote: #{file_uri_for(gem_repo2)}/ @@ -106,12 +113,15 @@ RSpec.describe "the lockfile format" do #{version} L - install_gemfile <<-G + install_gemfile <<-G, :verbose => true, :env => { "BUNDLER_SPEC_GEM_SOURCES" => file_uri_for(gem_repo2).to_s } source "#{file_uri_for(gem_repo2)}" gem "rack" G + expect(out).to include("Bundler #{Bundler::VERSION} is running, but your lockfile was generated with #{version}.") + expect(out).to include("Using bundler #{version}") + expect(lockfile).to eq <<~G GEM remote: #{file_uri_for(gem_repo2)}/ @@ -129,7 +139,56 @@ RSpec.describe "the lockfile format" do G end - it "updates the lockfile's bundler version if not present" do + it "does not update the lockfile's bundler version if nothing changed during bundle install, and uses the latest version", :rubygems => "< 3.3.0.a" do + version = "#{Bundler::VERSION.split(".").first}.0.0.a" + + update_repo2 do + with_built_bundler(version) {|gem_path| FileUtils.mv(gem_path, gem_repo2("gems")) } + end + + lockfile <<-L + GEM + remote: #{file_uri_for(gem_repo2)}/ + specs: + rack (1.0.0) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + rack + + BUNDLED WITH + #{version} + L + + install_gemfile <<-G, :verbose => true, :env => { "BUNDLER_SPEC_GEM_SOURCES" => file_uri_for(gem_repo2).to_s } + source "#{file_uri_for(gem_repo2)}" + + gem "rack" + G + + expect(out).not_to include("Bundler #{Bundler::VERSION} is running, but your lockfile was generated with #{version}.") + expect(out).to include("Using bundler #{Bundler::VERSION}") + + expect(lockfile).to eq <<~G + GEM + remote: #{file_uri_for(gem_repo2)}/ + specs: + rack (1.0.0) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + rack + + BUNDLED WITH + #{version} + G + end + + it "adds the BUNDLED WITH section if not present" do lockfile <<-L GEM remote: #{file_uri_for(gem_repo2)}/ @@ -166,152 +225,6 @@ RSpec.describe "the lockfile format" do G end - it "warns if the current version is older than lockfile's bundler version, and locked version is a final release" do - current_version = "999.998.999" - system_gems "bundler-#{current_version}" - newer_minor = "999.999.0" - - lockfile <<-L - GEM - remote: #{file_uri_for(gem_repo2)}/ - specs: - rack (1.0.0) - - PLATFORMS - #{lockfile_platforms} - - DEPENDENCIES - rack - - BUNDLED WITH - #{newer_minor} - L - - install_gemfile <<-G, :env => { "BUNDLER_VERSION" => current_version } - source "#{file_uri_for(gem_repo2)}" - - gem "rack" - G - - warning_message = "the running version of Bundler (#{current_version}) is older " \ - "than the version that created the lockfile (#{newer_minor}). " \ - "We suggest you to upgrade to the version that created the " \ - "lockfile by running `gem install bundler:#{newer_minor}`." - expect(err).to include warning_message - - expect(lockfile).to eq <<~G - GEM - remote: #{file_uri_for(gem_repo2)}/ - specs: - rack (1.0.0) - - PLATFORMS - #{lockfile_platforms} - - DEPENDENCIES - rack - - BUNDLED WITH - #{newer_minor} - G - end - - it "warns if the current version is older than lockfile's bundler version, and locked version is a prerelease" do - current_version = "999.998.999" - system_gems "bundler-#{current_version}" - newer_minor = "999.999.0.pre1" - - lockfile <<-L - GEM - remote: #{file_uri_for(gem_repo2)}/ - specs: - rack (1.0.0) - - PLATFORMS - #{lockfile_platforms} - - DEPENDENCIES - rack - - BUNDLED WITH - #{newer_minor} - L - - install_gemfile <<-G, :env => { "BUNDLER_VERSION" => current_version } - source "#{file_uri_for(gem_repo2)}" - - gem "rack" - G - - warning_message = "the running version of Bundler (#{current_version}) is older " \ - "than the version that created the lockfile (#{newer_minor}). " \ - "We suggest you to upgrade to the version that created the " \ - "lockfile by running `gem install bundler:#{newer_minor} --pre`." - expect(err).to include warning_message - - expect(lockfile).to eq <<~G - GEM - remote: #{file_uri_for(gem_repo2)}/ - specs: - rack (1.0.0) - - PLATFORMS - #{lockfile_platforms} - - DEPENDENCIES - rack - - BUNDLED WITH - #{newer_minor} - G - end - - it "doesn't warn if the current version is older than lockfile's bundler version, and locked version is a dev version" do - current_version = "999.998.999" - system_gems "bundler-#{current_version}" - newer_minor = "999.999.0.dev" - - lockfile <<-L - GEM - remote: #{file_uri_for(gem_repo2)}/ - specs: - rack (1.0.0) - - PLATFORMS - #{lockfile_platforms} - - DEPENDENCIES - rack - - BUNDLED WITH - #{newer_minor} - L - - install_gemfile <<-G, :env => { "BUNDLER_VERSION" => current_version } - source "#{file_uri_for(gem_repo2)}" - - gem "rack" - G - - expect(err).to be_empty - - expect(lockfile).to eq <<~G - GEM - remote: #{file_uri_for(gem_repo2)}/ - specs: - rack (1.0.0) - - PLATFORMS - #{lockfile_platforms} - - DEPENDENCIES - rack - - BUNDLED WITH - #{newer_minor} - G - end - it "warns when updating bundler major version" do current_version = Bundler::VERSION older_major = previous_major(current_version) diff --git a/spec/bundler/runtime/platform_spec.rb b/spec/bundler/runtime/platform_spec.rb index 433396d106..b937de8059 100644 --- a/spec/bundler/runtime/platform_spec.rb +++ b/spec/bundler/runtime/platform_spec.rb @@ -168,7 +168,7 @@ RSpec.describe "Bundler.setup with multi platform stuff" do nokogiri BUNDLED WITH - 2.1.4 + #{Bundler::VERSION} G bundle "install" diff --git a/spec/bundler/runtime/self_management_spec.rb b/spec/bundler/runtime/self_management_spec.rb new file mode 100644 index 0000000000..def9361d0f --- /dev/null +++ b/spec/bundler/runtime/self_management_spec.rb @@ -0,0 +1,109 @@ +# frozen_string_literal: true + +RSpec.describe "Self management", :rubygems => ">= 3.3.0.dev" do + describe "auto switching" do + let(:next_minor) do + Bundler::VERSION.split(".").map.with_index {|s, i| i == 1 ? s.to_i + 1 : s }[0..2].join(".") + end + + before do + build_repo2 do + with_built_bundler(next_minor) {|gem_path| FileUtils.mv(gem_path, gem_repo2("gems")) } + end + + gemfile <<-G + source "#{file_uri_for(gem_repo2)}" + + gem "rack" + G + end + + it "installs locked version when using system path and uses it" do + lockfile_bundled_with(next_minor) + + bundle "config set --local path.system true" + bundle "install", :env => { "BUNDLER_SPEC_GEM_SOURCES" => file_uri_for(gem_repo2).to_s } + expect(out).to include("Bundler #{Bundler::VERSION} is running, but your lockfile was generated with #{next_minor}. Installing Bundler #{next_minor} and restarting using that version.") + + # It uninstalls the older system bundler + bundle "clean --force" + expect(out).to eq("Removing bundler (#{Bundler::VERSION})") + + # App now uses locked version + bundle "-v" + expect(out).to end_with(next_minor[0] == "2" ? "Bundler version #{next_minor}" : next_minor) + + # Subsequent installs use the locked version without reinstalling + bundle "install --verbose" + expect(out).to include("Using bundler #{next_minor}") + expect(out).not_to include("Bundler #{Bundler::VERSION} is running, but your lockfile was generated with #{next_minor}. Installing Bundler #{next_minor} and restarting using that version.") + end + + it "installs locked version when using local path and uses it" do + lockfile_bundled_with(next_minor) + + bundle "config set --local path vendor/bundle" + bundle "install", :env => { "BUNDLER_SPEC_GEM_SOURCES" => file_uri_for(gem_repo2).to_s } + expect(out).to include("Bundler #{Bundler::VERSION} is running, but your lockfile was generated with #{next_minor}. Installing Bundler #{next_minor} and restarting using that version.") + + # It does not uninstall the locked bundler + bundle "clean" + expect(out).to be_empty + + # App now uses locked version + bundle "-v" + expect(out).to end_with(next_minor[0] == "2" ? "Bundler version #{next_minor}" : next_minor) + + # Subsequent installs use the locked version without reinstalling + bundle "install --verbose" + expect(out).to include("Using bundler #{next_minor}") + expect(out).not_to include("Bundler #{Bundler::VERSION} is running, but your lockfile was generated with #{next_minor}. Installing Bundler #{next_minor} and restarting using that version.") + end + + it "does not try to install a development version" do + lockfile_bundled_with("#{next_minor}.dev") + + bundle "install --verbose" + expect(out).not_to match(/restarting using that version/) + + bundle "-v" + expect(out).to eq(Bundler::VERSION[0] == "2" ? "Bundler version #{Bundler::VERSION}" : Bundler::VERSION) + end + + it "shows a discreet message if locked bundler does not exist, and something more complete in `--verbose` mode" do + missing_minor ="#{Bundler::VERSION[0]}.999.999" + + lockfile_bundled_with(missing_minor) + + bundle "install" + expect(err).to eq("There was an error installing the locked bundler version (#{missing_minor}), rerun with the `--verbose` flag for more details. Going on using bundler #{Bundler::VERSION}.") + + bundle "install --verbose" + expect(err).to include("There was an error installing the locked bundler version (#{missing_minor}), rerun with the `--verbose` flag for more details. Going on using bundler #{Bundler::VERSION}.") + expect(err).to include("Gem::UnsatisfiableDependencyError") + + bundle "-v" + expect(out).to eq(Bundler::VERSION[0] == "2" ? "Bundler version #{Bundler::VERSION}" : Bundler::VERSION) + end + + private + + def lockfile_bundled_with(version) + lockfile <<~L + GEM + remote: #{file_uri_for(gem_repo2)}/ + specs: + rack (1.0.0) + + PLATFORMS + #{lockfile_platforms} + + DEPENDENCIES + rack + + BUNDLED WITH + #{version} + L + end + end +end diff --git a/spec/bundler/support/filters.rb b/spec/bundler/support/filters.rb index 3b91897a2e..96f2e2a274 100644 --- a/spec/bundler/support/filters.rb +++ b/spec/bundler/support/filters.rb @@ -28,6 +28,7 @@ RSpec.configure do |config| config.filter_run_excluding :git => RequirementChecker.against(git_version) config.filter_run_excluding :bundler => RequirementChecker.against(Bundler::VERSION.split(".")[0]) + config.filter_run_excluding :rubygems => RequirementChecker.against(Gem::VERSION) config.filter_run_excluding :ruby_repo => !ENV["GEM_COMMAND"].nil? config.filter_run_excluding :no_color_tty => Gem.win_platform? || !ENV["GITHUB_ACTION"].nil? config.filter_run_excluding :permissions => Gem.win_platform? diff --git a/spec/bundler/support/hax.rb b/spec/bundler/support/hax.rb index 0ad5239128..4a49ffb49f 100644 --- a/spec/bundler/support/hax.rb +++ b/spec/bundler/support/hax.rb @@ -32,7 +32,7 @@ module Gem end if ENV["BUNDLER_SPEC_GEM_SOURCES"] - @sources = [ENV["BUNDLER_SPEC_GEM_SOURCES"]] + self.sources = [ENV["BUNDLER_SPEC_GEM_SOURCES"]] end # We only need this hack for rubygems versions without the BundlerVersionFinder diff --git a/spec/bundler/support/helpers.rb b/spec/bundler/support/helpers.rb index f84e68cee8..5238c8b980 100644 --- a/spec/bundler/support/helpers.rb +++ b/spec/bundler/support/helpers.rb @@ -166,7 +166,7 @@ module Spec def gem_command(command, options = {}) env = options[:env] || {} - env["RUBYOPT"] = opt_add("-r#{spec_dir}/support/hax.rb", env["RUBYOPT"] || ENV["RUBYOPT"]) + env["RUBYOPT"] = opt_add(opt_add("-r#{spec_dir}/support/hax.rb", env["RUBYOPT"]), ENV["RUBYOPT"]) options[:env] = env sys_exec("#{Path.gem_bin} #{command}", options) end @@ -181,7 +181,7 @@ module Spec def sys_exec(cmd, options = {}) env = options[:env] || {} - env["RUBYOPT"] = opt_add("-r#{spec_dir}/support/switch_rubygems.rb", env["RUBYOPT"] || ENV["RUBYOPT"]) + env["RUBYOPT"] = opt_add(opt_add("-r#{spec_dir}/support/switch_rubygems.rb", env["RUBYOPT"]), ENV["RUBYOPT"]) dir = options[:dir] || bundled_app command_execution = CommandExecution.new(cmd.to_s, dir) @@ -348,6 +348,7 @@ module Spec without_env_side_effects do ENV["GEM_HOME"] = path.to_s ENV["GEM_PATH"] = path.to_s + ENV["BUNDLER_ORIG_GEM_HOME"] = nil ENV["BUNDLER_ORIG_GEM_PATH"] = nil yield end diff --git a/tool/bundler/rubocop_gems.rb.lock b/tool/bundler/rubocop_gems.rb.lock index ca0fb5c613..4c33191c9e 100644 --- a/tool/bundler/rubocop_gems.rb.lock +++ b/tool/bundler/rubocop_gems.rb.lock @@ -60,4 +60,4 @@ DEPENDENCIES test-unit BUNDLED WITH - 2.3.0.dev + 2.3.0 diff --git a/tool/bundler/standard_gems.rb.lock b/tool/bundler/standard_gems.rb.lock index af22080659..b29129ed57 100644 --- a/tool/bundler/standard_gems.rb.lock +++ b/tool/bundler/standard_gems.rb.lock @@ -66,4 +66,4 @@ DEPENDENCIES test-unit BUNDLED WITH - 2.3.0.dev + 2.3.0 diff --git a/tool/bundler/test_gems.rb.lock b/tool/bundler/test_gems.rb.lock index 2f1dab91e2..d53195b0c9 100644 --- a/tool/bundler/test_gems.rb.lock +++ b/tool/bundler/test_gems.rb.lock @@ -41,4 +41,4 @@ DEPENDENCIES webrick (= 1.7.0) BUNDLED WITH - 2.3.0.dev + 2.3.0