Skip to content
This repository has been archived by the owner on Apr 14, 2021. It is now read-only.

Commit

Permalink
Auto merge of #5496 - bundler:seg-exec-outside-bundle, r=segiddins
Browse files Browse the repository at this point in the history
Friendly error when exec'ing to gem outside the bundle

Closes #5487
  • Loading branch information
bundlerbot committed Apr 4, 2017
2 parents 0d887b7 + 7a35043 commit f212d8e
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 32 deletions.
74 changes: 43 additions & 31 deletions lib/bundler/rubygems_integration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -393,20 +388,34 @@ 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
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 || 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

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" \
Expand Down Expand Up @@ -450,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
Expand Down
26 changes: 26 additions & 0 deletions spec/commands/exec_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,32 @@
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", :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 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 rackup"
expect(err).to include "rack is not part of the bundle. Add it to your Gemfile."
end
end

describe "with help flags" do
each_prefix = proc do |string, &blk|
1.upto(string.length) {|l| blk.call(string[0, l]) }
Expand Down
2 changes: 1 addition & 1 deletion spec/runtime/setup_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit f212d8e

Please sign in to comment.