diff --git a/lib/bundler/installer.rb b/lib/bundler/installer.rb index c2da63c822..0e91327aab 100644 --- a/lib/bundler/installer.rb +++ b/lib/bundler/installer.rb @@ -91,6 +91,11 @@ module Bundler end def generate_bundler_executable_stubs(spec, options = {}) + if spec.name == "bundler" + Bundler.ui.warn "Bundler itself does not use binstubs because its version is selected by RubyGems" + return + end + if options[:binstubs_cmd] && spec.executables.empty? options = {} spec.runtime_dependencies.each do |dep| @@ -115,10 +120,6 @@ module Bundler ruby_command = Thor::Util.ruby_command ruby_command = ruby_command template_path = File.expand_path("templates/Executable", __dir__) - if spec.name == "bundler" - template_path += ".bundler" - spec.executables = %(bundle) - end template = File.read(template_path) exists = [] diff --git a/lib/bundler/templates/Executable b/lib/bundler/templates/Executable index 9ff6f00898..b085c24da6 100644 --- a/lib/bundler/templates/Executable +++ b/lib/bundler/templates/Executable @@ -10,17 +10,6 @@ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("<%= relative_gemfile_path %>", __dir__) -bundle_binstub = File.expand_path("bundle", __dir__) - -if File.file?(bundle_binstub) - if File.read(bundle_binstub, 300).include?("This file was generated by Bundler") - load(bundle_binstub) - else - abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run. -Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.") - end -end - require "rubygems" require "bundler/setup" diff --git a/lib/bundler/templates/Executable.bundler b/lib/bundler/templates/Executable.bundler deleted file mode 100644 index caa2021701..0000000000 --- a/lib/bundler/templates/Executable.bundler +++ /dev/null @@ -1,109 +0,0 @@ -#!/usr/bin/env <%= Bundler.settings[:shebang] || RbConfig::CONFIG["ruby_install_name"] %> -# frozen_string_literal: true - -# -# This file was generated by Bundler. -# -# The application '<%= executable %>' is installed as part of a gem, and -# this file is here to facilitate running it. -# - -require "rubygems" - -m = Module.new do - module_function - - def invoked_as_script? - File.expand_path($0) == File.expand_path(__FILE__) - end - - def env_var_version - ENV["BUNDLER_VERSION"] - end - - def cli_arg_version - return unless invoked_as_script? # don't want to hijack other binstubs - return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update` - bundler_version = nil - update_index = nil - ARGV.each_with_index do |a, i| - if update_index && update_index.succ == i && a.match?(Gem::Version::ANCHORED_VERSION_PATTERN) - bundler_version = a - end - next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/ - bundler_version = $1 - update_index = i - end - bundler_version - end - - def gemfile - gemfile = ENV["BUNDLE_GEMFILE"] - return gemfile if gemfile && !gemfile.empty? - - File.expand_path("<%= relative_gemfile_path %>", __dir__) - end - - def lockfile - lockfile = - case File.basename(gemfile) - when "gems.rb" then gemfile.sub(/\.rb$/, ".locked") - else "#{gemfile}.lock" - end - File.expand_path(lockfile) - end - - def lockfile_version - return unless File.file?(lockfile) - lockfile_contents = File.read(lockfile) - return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/ - Regexp.last_match(1) - end - - def bundler_requirement - @bundler_requirement ||= - env_var_version || - cli_arg_version || - bundler_requirement_for(lockfile_version) - end - - def bundler_requirement_for(version) - return "#{Gem::Requirement.default}.a" unless version - - bundler_gem_version = Gem::Version.new(version) - - bundler_gem_version.approximate_recommendation - end - - def load_bundler! - ENV["BUNDLE_GEMFILE"] ||= gemfile - - activate_bundler - end - - def activate_bundler - gem_error = activation_error_handling do - gem "bundler", bundler_requirement - end - return if gem_error.nil? - require_error = activation_error_handling do - require "bundler/version" - end - return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION)) - warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`" - exit 42 - end - - def activation_error_handling - yield - nil - rescue StandardError, LoadError => e - e - end -end - -m.load_bundler! - -if m.invoked_as_script? - load Gem.bin_path("<%= spec.name %>", "<%= executable %>") -end diff --git a/spec/bundler/commands/binstubs_spec.rb b/spec/bundler/commands/binstubs_spec.rb index 4da6dea16e..c66b9339ee 100644 --- a/spec/bundler/commands/binstubs_spec.rb +++ b/spec/bundler/commands/binstubs_spec.rb @@ -85,220 +85,6 @@ RSpec.describe "bundle binstubs " do expect(err).to include("Cannot specify --all with specific gems") end - context "when generating bundle binstub outside bundler" do - it "should abort" do - install_gemfile <<-G - source "https://gem.repo1" - gem "myrack" - G - - bundle "binstubs myrack" - - File.open(bundled_app("bin/bundle"), "wb") do |file| - file.print "OMG" - end - - sys_exec "bin/myrackup", raise_on_error: false - - expect(err).to include("was not generated by Bundler") - end - end - - context "the bundle binstub" do - before do - pristine_system_gems "bundler-#{system_bundler_version}" - build_repo2 do - build_gem "myrack", "1.2" do |s| - s.executables = "myrackup" - end - - build_gem "prints_loaded_gems", "1.0" do |s| - s.executables = "print_loaded_gems" - s.bindir = "exe" - s.write "exe/print_loaded_gems", <<~R - #!/usr/bin/env ruby - specs = Gem.loaded_specs.values.reject {|s| s.default_gem? } - puts specs.map(&:full_name).sort.inspect - R - end - - build_bundler locked_bundler_version - end - install_gemfile <<-G - source "https://gem.repo2" - gem "myrack" - gem "prints_loaded_gems" - G - bundle "binstubs bundler myrack prints_loaded_gems" - end - - let(:system_bundler_version) { Bundler::VERSION } - let(:locked_bundler_version) { nil } - let(:lockfile_content) { lockfile.gsub(system_bundler_version, locked_bundler_version) } - - it "runs bundler" do - bundle "install --verbose", bundle_bin: "bin/bundle" - expect(out).to include %(Using bundler #{system_bundler_version}\n) - end - - context "when BUNDLER_VERSION is set" do - it "runs the correct version of bundler" do - bundle "install", env: { "BUNDLER_VERSION" => "999.999.999" }, raise_on_error: false, bundle_bin: "bin/bundle" - expect(exitstatus).to eq(42) - expect(err).to include("Activating bundler (999.999.999) failed:"). - and include("To install the version of bundler this project requires, run `gem install bundler -v '999.999.999'`") - end - - it "runs the correct version of bundler even if a higher version is installed" do - system_gems "bundler-999.999.998", "bundler-999.999.999" - - bundle "install --verbose", env: { "BUNDLER_VERSION" => "999.999.998" }, raise_on_error: false, bundle_bin: "bin/bundle" - expect(out).to include %(Using bundler 999.999.998\n) - end - end - - context "when a lockfile exists with a locked bundler version" do - let(:locked_bundler_version) { "999.999" } - - context "and the version is newer" do - before do - lockfile lockfile_content - end - - it "runs the correct version of bundler" do - bundle "install", raise_on_error: false, bundle_bin: "bin/bundle" - expect(exitstatus).to eq(42) - expect(err).to include("Activating bundler (~> 999.999) failed:"). - and include("To install the version of bundler this project requires, run `gem install bundler -v '~> 999.999'`") - end - end - - context "and the version is newer when given `gems.rb` and `gems.locked`" do - before do - gemfile bundled_app("gems.rb"), gemfile - lockfile bundled_app("gems.locked"), lockfile_content - end - - it "runs the correct version of bundler" do - bundle "install", env: { "BUNDLE_GEMFILE" => "gems.rb" }, raise_on_error: false, bundle_bin: "bin/bundle" - - expect(exitstatus).to eq(42) - expect(err).to include("Activating bundler (~> 999.999) failed:"). - and include("To install the version of bundler this project requires, run `gem install bundler -v '~> 999.999'`") - end - end - - context "and the version is older and a different major" do - let(:system_bundler_version) { "55" } - let(:locked_bundler_version) { "44" } - - before do - lockfile lockfile_content - end - - it "runs the correct version of bundler" do - bundle "install", raise_on_error: false, bundle_bin: "bin/bundle" - expect(exitstatus).to eq(42) - expect(err).to include("Activating bundler (~> 44.0) failed:"). - and include("To install the version of bundler this project requires, run `gem install bundler -v '~> 44.0'`") - end - end - - context "and the version is older and a different major when given `gems.rb` and `gems.locked`" do - let(:system_bundler_version) { "55" } - let(:locked_bundler_version) { "44" } - - before do - gemfile bundled_app("gems.rb"), gemfile - lockfile bundled_app("gems.locked"), lockfile_content - end - - it "runs the correct version of bundler" do - bundle "install", env: { "BUNDLE_GEMFILE" => "gems.rb" }, raise_on_error: false, bundle_bin: "bin/bundle" - expect(exitstatus).to eq(42) - expect(err).to include("Activating bundler (~> 44.0) failed:"). - and include("To install the version of bundler this project requires, run `gem install bundler -v '~> 44.0'`") - end - end - - context "and the version is older and the same major" do - let(:system_bundler_version) { "2.999.999" } - let(:locked_bundler_version) { "2.3.0" } - - before do - lockfile lockfile_content - end - - it "installs and runs the exact version of bundler" do - bundle "install --verbose", bundle_bin: "bin/bundle" - expect(exitstatus).not_to eq(42) - expect(out).to include("Bundler 2.999.999 is running, but your lockfile was generated with 2.3.0. Installing Bundler 2.3.0 and restarting using that version.") - expect(out).to include("Using bundler 2.3.0") - expect(err).not_to include("Activating bundler (~> 2.3.0) failed:") - end - end - - context "and the version is a pre-releaser" do - let(:system_bundler_version) { "55" } - let(:locked_bundler_version) { "2.12.0.a" } - - before do - lockfile lockfile_content - end - - it "runs the correct version of bundler when the version is a pre-release" do - bundle "install", raise_on_error: false, bundle_bin: "bin/bundle" - expect(exitstatus).to eq(42) - expect(err).to include("Activating bundler (~> 2.12.a) failed:"). - and include("To install the version of bundler this project requires, run `gem install bundler -v '~> 2.12.a'`") - end - end - end - - context "when update --bundler is called" do - it "calls through to the latest bundler version" do - bundle "update --bundler --verbose", bundle_bin: "bin/bundle" - using_bundler_line = /Using bundler ([\w\.]+)\n/.match(out) - expect(using_bundler_line).to_not be_nil - latest_version = using_bundler_line[1] - expect(Gem::Version.new(latest_version)).to be >= Gem::Version.new(system_bundler_version) - end - - it "calls through to the explicit bundler version" do - bundle "update --bundler=999.999.999", raise_on_error: false, bundle_bin: "bin/bundle" - expect(exitstatus).to eq(42) - expect(err).to include("Activating bundler (999.999.999) failed:"). - and include("To install the version of bundler this project requires, run `gem install bundler -v '999.999.999'`") - end - end - - context "without a lockfile" do - it "falls back to the latest installed bundler" do - FileUtils.rm bundled_app_lock - bundle "install --verbose", bundle_bin: "bin/bundle" - expect(out).to include "Using bundler #{system_bundler_version}\n" - end - end - - context "using another binstub" do - it "loads all gems" do - sys_exec bundled_app("bin/print_loaded_gems").to_s - expect(out).to eq %(["bundler-#{Bundler::VERSION}", "myrack-1.2", "prints_loaded_gems-1.0"]) - end - - context "when requesting a different bundler version" do - before { lockfile lockfile.gsub(Bundler::VERSION, "999.999.999") } - - it "attempts to load that version" do - sys_exec bundled_app("bin/myrackup").to_s, raise_on_error: false - expect(exitstatus).to eq(42) - expect(err).to include("Activating bundler (~> 999.999) failed:"). - and include("To install the version of bundler this project requires, run `gem install bundler -v '~> 999.999'`") - end - end - end - end - it "installs binstubs from git gems" do FileUtils.mkdir_p(lib_path("foo/bin")) FileUtils.touch(lib_path("foo/bin/foo")) diff --git a/spec/bundler/runtime/executable_spec.rb b/spec/bundler/runtime/executable_spec.rb index cf57805708..a4226ed51e 100644 --- a/spec/bundler/runtime/executable_spec.rb +++ b/spec/bundler/runtime/executable_spec.rb @@ -77,7 +77,7 @@ RSpec.describe "Running bin/* commands" do expect(out).to eq("1.0") end - it "creates a bundle binstub" do + it "does not create a bundle binstub" do gemfile <<-G source "https://gem.repo1" gem "bundler" @@ -85,7 +85,9 @@ RSpec.describe "Running bin/* commands" do bundle "binstubs bundler" - expect(bundled_app("bin/bundle")).to exist + expect(bundled_app("bin/bundle")).not_to exist + + expect(err).to include("Bundler itself does not use binstubs because its version is selected by RubyGems") end it "does not generate bin stubs if the option was not specified" do