From f09865c40e7f277b8e93f94d3b3b15da5becf930 Mon Sep 17 00:00:00 2001 From: Samuel Giddins Date: Fri, 3 Mar 2017 17:31:45 -0600 Subject: [PATCH 1/4] Friendly error when exec'ing to gem outside the bundle --- lib/bundler/rubygems_integration.rb | 11 ++++++++++- spec/commands/exec_spec.rb | 12 ++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/bundler/rubygems_integration.rb b/lib/bundler/rubygems_integration.rb index 90768aac1dd..0769fc7b65d 100644 --- a/lib/bundler/rubygems_integration.rb +++ b/lib/bundler/rubygems_integration.rb @@ -405,8 +405,17 @@ def replace_bin_path(specs) else specs.find {|s| s.name == gem_name } end - raise(Gem::Exception, "can't find executable #{exec_name}") unless spec + + unless spec + message = "can't find executable #{exec_name} for gem #{gem_name}" + if !exec_name || specs.find {|s| s.name == gem_name }.nil? + message += ". #{gem_name} is not currently included in the bundle, perhaps you meant to add it to your #{Bundler.default_gemfile.basename}?" + end + raise Gem::Exception, message + end + raise Gem::Exception, "no default executable for #{spec.full_name}" unless exec_name ||= spec.default_executable + unless spec.name == name Bundler::SharedHelpers.major_deprecation \ "Bundler is using a binstub that was created for a different gem.\n" \ diff --git a/spec/commands/exec_spec.rb b/spec/commands/exec_spec.rb index 3c5f2ca1dd7..63d127f5bc1 100644 --- a/spec/commands/exec_spec.rb +++ b/spec/commands/exec_spec.rb @@ -218,6 +218,18 @@ expect(out).to include("bundler: exec needs a command to run") end + it "raises a helpful error when exec'ing to something outside of the bundle" do + install_gemfile! <<-G + gem "rack" + G + [true, false].each do |l| + bundle! "config disable_exec_load #{l}" + bundle "exec rake" + expect(err).to include "can't find executable rake for gem rake. rake is not currently included in the bundle, perhaps you meant to add it to your Gemfile?" + end + expect(out).to include "bundler: failed to load command: rake" + end + describe "with help flags" do each_prefix = proc do |string, &blk| 1.upto(string.length) {|l| blk.call(string[0, l]) } From 9cb4c7bb4cc091b137e94bdc2456971df49ee196 Mon Sep 17 00:00:00 2001 From: Samuel Giddins Date: Tue, 14 Mar 2017 13:18:04 -0500 Subject: [PATCH 2/4] Remove spec expectation that does not hold across all versions --- spec/commands/exec_spec.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/commands/exec_spec.rb b/spec/commands/exec_spec.rb index 63d127f5bc1..d04d1c99b16 100644 --- a/spec/commands/exec_spec.rb +++ b/spec/commands/exec_spec.rb @@ -227,7 +227,6 @@ bundle "exec rake" expect(err).to include "can't find executable rake for gem rake. rake is not currently included in the bundle, perhaps you meant to add it to your Gemfile?" end - expect(out).to include "bundler: failed to load command: rake" end describe "with help flags" do From 09fb78c5c8cabf9b22800c8455e4d2ddfee5403a Mon Sep 17 00:00:00 2001 From: Samuel Giddins Date: Tue, 28 Mar 2017 20:05:52 -0500 Subject: [PATCH 3/4] [RubygemsIntegration] Also pass in a hash of specs by name when replacing entrypoints This should improve performance slightly and make the code a bit more readable --- lib/bundler/rubygems_integration.rb | 67 +++++++++++++++-------------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/lib/bundler/rubygems_integration.rb b/lib/bundler/rubygems_integration.rb index 0769fc7b65d..1e71d1adfae 100644 --- a/lib/bundler/rubygems_integration.rb +++ b/lib/bundler/rubygems_integration.rb @@ -320,7 +320,7 @@ def reverse_rubygems_kernel_mixin end end - def replace_gem(specs) + def replace_gem(specs, specs_by_name) reverse_rubygems_kernel_mixin executables = specs.map(&:executables).flatten @@ -337,31 +337,26 @@ def replace_gem(specs) dep = Gem::Dependency.new(dep, reqs) end - spec = specs.find {|s| s.name == dep.name } - - if spec.nil? - - e = Gem::LoadError.new "#{dep.name} is not part of the bundle. Add it to Gemfile." - e.name = dep.name - if e.respond_to?(:requirement=) - e.requirement = dep.requirement - else - e.version_requirement = dep.requirement - end - raise e - elsif dep !~ spec - e = Gem::LoadError.new "can't activate #{dep}, already activated #{spec.full_name}. " \ - "Make sure all dependencies are added to Gemfile." - e.name = dep.name - if e.respond_to?(:requirement=) - e.requirement = dep.requirement - else - e.version_requirement = dep.requirement - end - raise e + if spec = specs_by_name[dep.name] + return true if dep.matches_spec?(spec) end - true + message = if spec.nil? + "#{dep.name} is not part of the bundle." \ + " Add it to your #{Bundler.default_gemfile.basename}." + else + "can't activate #{dep}, already activated #{spec.full_name}. " \ + "Make sure all dependencies are added to Gemfile." + end + + e = Gem::LoadError.new(message) + e.name = dep.name + if e.respond_to?(:requirement=) + e.requirement = dep.requirement + elsif e.respond_to?(:version_requirement=) + e.version_requirement = dep.requirement + end + raise e end # TODO: delete this in 2.0, it's a backwards compatibility shim @@ -393,23 +388,28 @@ def stub_source_index(specs) # Used to make bin stubs that are not created by bundler work # under bundler. The new Gem.bin_path only considers gems in # +specs+ - def replace_bin_path(specs) + def replace_bin_path(specs, specs_by_name) gem_class = (class << Gem; self; end) redefine_method(gem_class, :find_spec_for_exe) do |gem_name, *args| exec_name = args.first + spec_with_name = specs_by_name[gem_name] spec = if exec_name - specs.find {|s| s.name == gem_name && s.executables.include?(exec_name) } || + if spec_with_name && spec_with_name.executables.include?(exec_name) + spec_with_name + else specs.find {|s| s.executables.include?(exec_name) } + end else - specs.find {|s| s.name == gem_name } + spec_with_name end unless spec message = "can't find executable #{exec_name} for gem #{gem_name}" - if !exec_name || specs.find {|s| s.name == gem_name }.nil? - message += ". #{gem_name} is not currently included in the bundle, perhaps you meant to add it to your #{Bundler.default_gemfile.basename}?" + if !exec_name || spec_with_name.nil? + message += ". #{gem_name} is not currently included in the bundle, " \ + "perhaps you meant to add it to your #{Bundler.default_gemfile.basename}?" end raise Gem::Exception, message end @@ -459,11 +459,14 @@ def replace_refresh # Replace or hook into Rubygems to provide a bundlerized view # of the world. def replace_entrypoints(specs) - replace_gem(specs) + specs_by_name = specs.reduce({}) do |h, s| + h[s.name] = s + h + end + replace_gem(specs, specs_by_name) stub_rubygems(specs) - - replace_bin_path(specs) + replace_bin_path(specs, specs_by_name) replace_refresh Gem.clear_paths From 7a35043f2e10ba5abd9df8d27d79e3a7f95db4cc Mon Sep 17 00:00:00 2001 From: Samuel Giddins Date: Tue, 28 Mar 2017 20:37:19 -0500 Subject: [PATCH 4/4] Update exec spec to have a different expectation for RG < 2.6.2 --- spec/commands/exec_spec.rb | 23 +++++++++++++++++++---- spec/runtime/setup_spec.rb | 2 +- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/spec/commands/exec_spec.rb b/spec/commands/exec_spec.rb index d04d1c99b16..bf4b3b37bdd 100644 --- a/spec/commands/exec_spec.rb +++ b/spec/commands/exec_spec.rb @@ -218,14 +218,29 @@ expect(out).to include("bundler: exec needs a command to run") end - it "raises a helpful error when exec'ing to something outside of the bundle" do + it "raises a helpful error when exec'ing to something outside of the bundle", :rubygems => ">= 2.6.2" do install_gemfile! <<-G - gem "rack" + source "file://#{gem_repo1}" + gem "with_license" + G + [true, false].each do |l| + bundle! "config disable_exec_load #{l}" + bundle "exec rackup" + expect(err).to include "can't find executable rackup for gem rack. rack is not currently included in the bundle, perhaps you meant to add it to your Gemfile?" + end + end + + # Different error message on old RG versions (before activate_bin_path) because they + # called `Kernel#gem` directly + it "raises a helpful error when exec'ing to something outside of the bundle", :rubygems => "< 2.6.2" do + install_gemfile! <<-G + source "file://#{gem_repo1}" + gem "with_license" G [true, false].each do |l| bundle! "config disable_exec_load #{l}" - bundle "exec rake" - expect(err).to include "can't find executable rake for gem rake. rake is not currently included in the bundle, perhaps you meant to add it to your Gemfile?" + bundle "exec rackup" + expect(err).to include "rack is not part of the bundle. Add it to your Gemfile." end end diff --git a/spec/runtime/setup_spec.rb b/spec/runtime/setup_spec.rb index be0c7a15931..809b0a15fd5 100644 --- a/spec/runtime/setup_spec.rb +++ b/spec/runtime/setup_spec.rb @@ -891,7 +891,7 @@ end R - expect(out).to eq("rack is not part of the bundle. Add it to Gemfile.") + expect(out).to eq("rack is not part of the bundle. Add it to your Gemfile.") end it "sets GEM_HOME appropriately" do