summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHomu <homu@barosl.com>2016-02-11 16:06:06 +0900
committerHomu <homu@barosl.com>2016-02-11 16:06:06 +0900
commite76760d1a492cf457a9952da2e5c24a3ba50b51d (patch)
tree3a5a2f815f4742d95886f50f8ad1bf605f335a4c
parent1c2710639ccd8558c8467bb3cd2f95548306bc31 (diff)
parent57af37d4748df9473aa1295fe20711ffd96c5097 (diff)
downloadbundler-e76760d1a492cf457a9952da2e5c24a3ba50b51d.tar.gz
Auto merge of #4292 - RochesterinNYC:add-error-message-for-shared-helpers-EPROTO, r=indirect
Raise new `Bundler::VirtualProtocolError` in response to `Errno::EPROTO` - Related to #4163, #3932, and #3581 Ideally, a custom error and/or error message would be returned in these cases rather than just error out because of the uncaught `Errno::EPROTO`. I'd like some feedback on what the appropriate error message should be however. The caveat is that it seems like a wide range of errors (possibly any error?) that occur in the presence of virtualization will raise this `Errno::EPROTO` error.
-rw-r--r--lib/bundler/errors.rb9
-rw-r--r--lib/bundler/shared_helpers.rb2
-rw-r--r--spec/bundler/shared_helpers_spec.rb244
3 files changed, 170 insertions, 85 deletions
diff --git a/lib/bundler/errors.rb b/lib/bundler/errors.rb
index eec8d00ef5..7b893e664f 100644
--- a/lib/bundler/errors.rb
+++ b/lib/bundler/errors.rb
@@ -98,4 +98,13 @@ module Bundler
status_code(26)
end
+
+ class VirtualProtocolError < BundlerError
+ def message
+ "There was an error relating to virtualization and file access." \
+ "It is likely that you need to grant access to or mount some file system correctly."
+ end
+
+ status_code(27)
+ end
end
diff --git a/lib/bundler/shared_helpers.rb b/lib/bundler/shared_helpers.rb
index 654359d1ed..c79c88c5e1 100644
--- a/lib/bundler/shared_helpers.rb
+++ b/lib/bundler/shared_helpers.rb
@@ -107,6 +107,8 @@ module Bundler
raise PermissionError.new(path, action)
rescue Errno::EAGAIN
raise TemporaryResourceError.new(path, action)
+ rescue Errno::EPROTO
+ raise VirtualProtocolError.new
end
def const_get_safely(constant_name, namespace)
diff --git a/spec/bundler/shared_helpers_spec.rb b/spec/bundler/shared_helpers_spec.rb
index d72a7f49c9..348889a326 100644
--- a/spec/bundler/shared_helpers_spec.rb
+++ b/spec/bundler/shared_helpers_spec.rb
@@ -1,322 +1,396 @@
# frozen_string_literal: true
require "spec_helper"
-require "bundler/shared_helpers"
describe Bundler::SharedHelpers do
+ let(:ext_lock_double) { double(:ext_lock) }
+
before do
- ext_lock_double = double(:ext_lock)
allow(Bundler.rubygems).to receive(:ext_lock).and_return(ext_lock_double)
- allow(ext_lock_double).to receive(:synchronize) do |&block|
- block.call
- end
+ allow(ext_lock_double).to receive(:synchronize) {|&block| block.call }
end
+
subject { Bundler::SharedHelpers }
+
describe "#default_gemfile" do
- before do
- ENV["BUNDLE_GEMFILE"] = "/path/Gemfile"
- end
+ before { ENV["BUNDLE_GEMFILE"] = "/path/Gemfile" }
+
context "Gemfile is present" do
+ let(:expected_gemfile_path) { Pathname.new("/path/Gemfile") }
+
it "returns the Gemfile path" do
- expected_gemfile_path = Pathname.new("/path/Gemfile")
expect(subject.default_gemfile).to eq(expected_gemfile_path)
end
end
+
context "Gemfile is not present" do
- before do
- ENV["BUNDLE_GEMFILE"] = nil
- end
+ before { ENV["BUNDLE_GEMFILE"] = nil }
+
it "raises a GemfileNotFound error" do
- expect { subject.default_gemfile }.to raise_error(Bundler::GemfileNotFound, "Could not locate Gemfile")
+ expect { subject.default_gemfile }.to raise_error(
+ Bundler::GemfileNotFound, "Could not locate Gemfile")
end
end
end
+
describe "#default_lockfile" do
context "gemfile is gems.rb" do
- before do
- gemfile_path = Pathname.new("/path/gems.rb")
- allow(subject).to receive(:default_gemfile).and_return(gemfile_path)
- end
+ let(:gemfile_path) { Pathname.new("/path/gems.rb") }
+ let(:expected_lockfile_path) { Pathname.new("/path/gems.locked") }
+
+ before { allow(subject).to receive(:default_gemfile).and_return(gemfile_path) }
+
it "returns the gems.locked path" do
- expected_lockfile_path = Pathname.new("/path/gems.locked")
expect(subject.default_lockfile).to eq(expected_lockfile_path)
end
end
+
context "is a regular Gemfile" do
- before do
- gemfile_path = Pathname.new("/path/Gemfile")
- allow(subject).to receive(:default_gemfile).and_return(gemfile_path)
- end
+ let(:gemfile_path) { Pathname.new("/path/Gemfile") }
+ let(:expected_lockfile_path) { Pathname.new("/path/Gemfile.lock") }
+
+ before { allow(subject).to receive(:default_gemfile).and_return(gemfile_path) }
+
it "returns the lock file path" do
- expected_lockfile_path = Pathname.new("/path/Gemfile.lock")
expect(subject.default_lockfile).to eq(expected_lockfile_path)
end
end
end
+
describe "#default_bundle_dir" do
context ".bundle does not exist" do
it "returns nil" do
- expect(subject.default_bundle_dir).to eq(nil)
+ expect(subject.default_bundle_dir).to be_nil
end
end
+
context ".bundle is global .bundle" do
+ let(:global_rubygems_dir) { Pathname.new("#{bundled_app}") }
+
before do
Dir.mkdir ".bundle"
- global_rubygems_dir = Pathname.new("#{bundled_app}")
allow(Bundler.rubygems).to receive(:user_home).and_return(global_rubygems_dir)
end
+
it "returns nil" do
- expect(subject.default_bundle_dir).to eq(nil)
+ expect(subject.default_bundle_dir).to be_nil
end
end
+
context ".bundle is not global .bundle" do
+ let(:global_rubygems_dir) { Pathname.new("/path/rubygems") }
+ let(:expected_bundle_dir_path) { Pathname.new("#{bundled_app}/.bundle") }
+
before do
Dir.mkdir ".bundle"
- global_rubygems_dir = Pathname.new("/path/rubygems")
allow(Bundler.rubygems).to receive(:user_home).and_return(global_rubygems_dir)
end
+
it "returns the .bundle path" do
- expected_bundle_dir_path = Pathname.new("#{bundled_app}/.bundle")
expect(subject.default_bundle_dir).to eq(expected_bundle_dir_path)
end
end
end
+
describe "#in_bundle?" do
it "calls the find_gemfile method" do
expect(subject).to receive(:find_gemfile)
subject.in_bundle?
end
+
shared_examples_for "correctly determines whether to return a Gemfile path" do
context "currently in directory with a Gemfile" do
- before do
- File.new("Gemfile", "w")
- end
+ before { File.new("Gemfile", "w") }
+
it "returns path of the bundle gemfile" do
expect(subject.in_bundle?).to eq("#{bundled_app}/Gemfile")
end
end
+
context "currently in directory without a Gemfile" do
it "returns nil" do
- expect(subject.in_bundle?).to eq(nil)
+ expect(subject.in_bundle?).to be_nil
end
end
end
+
context "ENV['BUNDLE_GEMFILE'] set" do
- before do
- ENV["BUNDLE_GEMFILE"] = "/path/Gemfile"
- end
+ before { ENV["BUNDLE_GEMFILE"] = "/path/Gemfile" }
+
it "returns ENV['BUNDLE_GEMFILE']" do
expect(subject.in_bundle?).to eq("/path/Gemfile")
end
end
+
context "ENV['BUNDLE_GEMFILE'] not set" do
- before do
- ENV["BUNDLE_GEMFILE"] = nil
- end
+ before { ENV["BUNDLE_GEMFILE"] = nil }
+
it_behaves_like "correctly determines whether to return a Gemfile path"
end
+
context "ENV['BUNDLE_GEMFILE'] is blank" do
- before do
- ENV["BUNDLE_GEMFILE"] = ""
- end
+ before { ENV["BUNDLE_GEMFILE"] = "" }
+
it_behaves_like "correctly determines whether to return a Gemfile path"
end
end
+
describe "#chdir" do
- before do
- Dir.mkdir "chdir_test_dir"
- end
+ let(:op_block) { proc { Dir.mkdir "nested_dir" } }
+
+ before { Dir.mkdir "chdir_test_dir" }
+
it "executes the passed block while in the specified directory" do
- op_block = proc { Dir.mkdir "nested_dir" }
subject.chdir("chdir_test_dir", &op_block)
expect(Pathname.new("chdir_test_dir/nested_dir")).to exist
end
end
+
describe "#pwd" do
it "returns the current absolute path" do
expect(subject.pwd).to eq(bundled_app)
end
end
+
describe "#with_clean_git_env" do
+ let(:with_clean_git_env_block) { proc { Dir.mkdir "with_clean_git_env_test_dir" } }
+
before do
ENV["GIT_DIR"] = "ORIGINAL_ENV_GIT_DIR"
ENV["GIT_WORK_TREE"] = "ORIGINAL_ENV_GIT_WORK_TREE"
end
+
it "executes the passed block" do
- with_clean_git_env_block = proc do
- Dir.mkdir "with_clean_git_env_test_dir"
- end
subject.with_clean_git_env(&with_clean_git_env_block)
expect(Pathname.new("with_clean_git_env_test_dir")).to exist
end
- it "uses a fresh git env for execution" do
- with_clean_git_env_block = proc do
- Dir.mkdir "git_dir_test_dir" unless ENV["GIT_DIR"].nil?
- Dir.mkdir "git_work_tree_test_dir" unless ENV["GIT_WORK_TREE"].nil?
+
+ context "when a block is passed" do
+ let(:with_clean_git_env_block) do
+ proc do
+ Dir.mkdir "git_dir_test_dir" unless ENV["GIT_DIR"].nil?
+ Dir.mkdir "git_work_tree_test_dir" unless ENV["GIT_WORK_TREE"].nil?
+ end end
+
+ it "uses a fresh git env for execution" do
+ subject.with_clean_git_env(&with_clean_git_env_block)
+ expect(Pathname.new("git_dir_test_dir")).to_not exist
+ expect(Pathname.new("git_work_tree_test_dir")).to_not exist
end
- subject.with_clean_git_env(&with_clean_git_env_block)
- expect(Pathname.new("git_dir_test_dir")).to_not exist
- expect(Pathname.new("git_work_tree_test_dir")).to_not exist
end
+
context "passed block does not throw errors" do
- it "restores the git env after" do
- with_clean_git_env_block = proc do
+ let(:with_clean_git_env_block) do
+ proc do
ENV["GIT_DIR"] = "NEW_ENV_GIT_DIR"
ENV["GIT_WORK_TREE"] = "NEW_ENV_GIT_WORK_TREE"
- end
+ end end
+
+ it "restores the git env after" do
subject.with_clean_git_env(&with_clean_git_env_block)
expect(ENV["GIT_DIR"]).to eq("ORIGINAL_ENV_GIT_DIR")
expect(ENV["GIT_WORK_TREE"]).to eq("ORIGINAL_ENV_GIT_WORK_TREE")
end
end
+
context "passed block throws errors" do
- it "restores the git env after" do
- with_clean_git_env_block = proc do
+ let(:with_clean_git_env_block) do
+ proc do
ENV["GIT_DIR"] = "NEW_ENV_GIT_DIR"
ENV["GIT_WORK_TREE"] = "NEW_ENV_GIT_WORK_TREE"
raise RuntimeError.new
- end
+ end end
+
+ it "restores the git env after" do
expect { subject.with_clean_git_env(&with_clean_git_env_block) }.to raise_error(RuntimeError)
expect(ENV["GIT_DIR"]).to eq("ORIGINAL_ENV_GIT_DIR")
expect(ENV["GIT_WORK_TREE"]).to eq("ORIGINAL_ENV_GIT_WORK_TREE")
end
end
end
+
describe "#set_bundle_environment" do
shared_examples_for "ENV['PATH'] gets set correctly" do
- before do
- Dir.mkdir ".bundle"
- end
+ before { Dir.mkdir ".bundle" }
+
it "ensures bundle bin path is in ENV['PATH']" do
subject.set_bundle_environment
paths = ENV["PATH"].split(File::PATH_SEPARATOR)
expect(paths).to include("#{Bundler.bundle_path}/bin")
end
end
+
shared_examples_for "ENV['RUBYOPT'] gets set correctly" do
it "ensures -rbundler/setup is at the beginning of ENV['RUBYOPT']" do
subject.set_bundle_environment
expect(ENV["RUBYOPT"].split(" ").first).to include("-rbundler/setup")
end
end
+
shared_examples_for "ENV['RUBYLIB'] gets set correctly" do
- before do
- @ruby_lib_path = "stubbed_ruby_lib_dir"
- allow(File).to receive(:expand_path).and_return(@ruby_lib_path)
- end
+ let(:ruby_lib_path) { "stubbed_ruby_lib_dir" }
+
+ before { allow(File).to receive(:expand_path).and_return(ruby_lib_path) }
+
it "ensures bundler's ruby version lib path is in ENV['RUBYLIB']" do
subject.set_bundle_environment
paths = (ENV["RUBYLIB"]).split(File::PATH_SEPARATOR)
- expect(paths.include? @ruby_lib_path).to eq(true)
+ expect(paths).to include(ruby_lib_path)
end
end
+
it "calls the appropriate set methods" do
expect(subject).to receive(:set_path)
expect(subject).to receive(:set_rubyopt)
expect(subject).to receive(:set_rubylib)
subject.set_bundle_environment
end
+
context "ENV['PATH'] does not exist" do
before { ENV.delete("PATH") }
+
it_behaves_like "ENV['PATH'] gets set correctly"
end
+
context "ENV['PATH'] is empty" do
before { ENV["PATH"] = "" }
+
it_behaves_like "ENV['PATH'] gets set correctly"
end
+
context "ENV['PATH'] exists" do
before { ENV["PATH"] = "/some_path/bin" }
+
it_behaves_like "ENV['PATH'] gets set correctly"
end
+
context "ENV['PATH'] already contains the bundle bin path" do
+ let(:bundle_path) { "#{Bundler.bundle_path}/bin" }
+
before do
- @bundle_path = "#{Bundler.bundle_path}/bin"
- ENV["PATH"] = @bundle_path
+ ENV["PATH"] = bundle_path
end
+
it_behaves_like "ENV['PATH'] gets set correctly"
+
it "ENV['PATH'] should only contain one instance of bundle bin path" do
subject.set_bundle_environment
paths = (ENV["PATH"]).split(File::PATH_SEPARATOR)
- expect(paths.count(@bundle_path)).to eq(1)
+ expect(paths.count(bundle_path)).to eq(1)
end
end
+
context "ENV['RUBYOPT'] does not exist" do
before { ENV.delete("RUBYOPT") }
+
it_behaves_like "ENV['RUBYOPT'] gets set correctly"
end
+
context "ENV['RUBYOPT'] exists without -rbundler/setup" do
before { ENV["RUBYOPT"] = "-I/some_app_path/lib" }
+
it_behaves_like "ENV['RUBYOPT'] gets set correctly"
end
+
context "ENV['RUBYOPT'] exists and contains -rbundler/setup" do
- before do
- ENV["RUBYOPT"] = "-rbundler/setup"
- end
+ before { ENV["RUBYOPT"] = "-rbundler/setup" }
+
it_behaves_like "ENV['RUBYOPT'] gets set correctly"
end
+
context "ENV['RUBYLIB'] does not exist" do
before { ENV.delete("RUBYLIB") }
+
it_behaves_like "ENV['RUBYLIB'] gets set correctly"
end
+
context "ENV['RUBYLIB'] is empty" do
before { ENV["PATH"] = "" }
+
it_behaves_like "ENV['RUBYLIB'] gets set correctly"
end
+
context "ENV['RUBYLIB'] exists" do
before { ENV["PATH"] = "/some_path/bin" }
+
it_behaves_like "ENV['RUBYLIB'] gets set correctly"
end
+
context "ENV['RUBYLIB'] already contains the bundler's ruby version lib path" do
+ let(:ruby_lib_path) { "stubbed_ruby_lib_dir" }
+
before do
- @ruby_lib_path = "stubbed_ruby_lib_dir"
- allow(File).to receive(:expand_path).and_return(@ruby_lib_path)
- ENV["RUBYLIB"] = @ruby_lib_path
+ allow(File).to receive(:expand_path).and_return(ruby_lib_path)
+ ENV["RUBYLIB"] = ruby_lib_path
end
+
it_behaves_like "ENV['RUBYLIB'] gets set correctly"
+
it "ENV['RUBYLIB'] should only contain one instance of bundler's ruby version lib path" do
subject.set_bundle_environment
paths = (ENV["RUBYLIB"]).split(File::PATH_SEPARATOR)
- expect(paths.count(@ruby_lib_path)).to eq(1)
+ expect(paths.count(ruby_lib_path)).to eq(1)
end
end
end
+
describe "#filesystem_access" do
context "system has proper permission access" do
+ let(:file_op_block) { proc {|path| FileUtils.mkdir_p(path) } }
+
it "performs the operation in the passed block" do
- file_op_block = proc {|path| FileUtils.mkdir_p(path) }
subject.filesystem_access("./test_dir", &file_op_block)
expect(Pathname.new("test_dir")).to exist
end
end
+
context "system throws Errno::EACESS" do
+ let(:file_op_block) { proc {|_path| raise Errno::EACCES } }
+
it "raises a PermissionError" do
- file_op_block = proc {|_path| raise Errno::EACCES }
- expect { subject.filesystem_access("/path", &file_op_block) }.to raise_error(Bundler::PermissionError)
+ expect { subject.filesystem_access("/path", &file_op_block) }.to raise_error(
+ Bundler::PermissionError)
end
end
+
context "system throws Errno::EAGAIN" do
+ let(:file_op_block) { proc {|_path| raise Errno::EAGAIN } }
+
it "raises a TemporaryResourceError" do
- file_op_block = proc {|_path| raise Errno::EAGAIN }
- expect { subject.filesystem_access("/path", &file_op_block) }.to raise_error(Bundler::TemporaryResourceError)
+ expect { subject.filesystem_access("/path", &file_op_block) }.to raise_error(
+ Bundler::TemporaryResourceError)
+ end
+ end
+
+ context "system throws Errno::EPROTO" do
+ let(:file_op_block) { proc {|_path| raise Errno::EPROTO } }
+
+ it "raises a VirtualProtocolError" do
+ expect { subject.filesystem_access("/path", &file_op_block) }.to raise_error(
+ Bundler::VirtualProtocolError)
end
end
end
+
describe "#const_get_safely" do
module TargetNamespace
VALID_CONSTANT = 1
end
+
context "when the namespace does have the requested constant" do
it "returns the value of the requested constant" do
expect(subject.const_get_safely(:VALID_CONSTANT, TargetNamespace)).to eq(1)
end
end
+
context "when the requested constant is passed as a string" do
it "returns the value of the requested constant" do
expect(subject.const_get_safely("VALID_CONSTANT", TargetNamespace)).to eq(1)
end
end
+
context "when the namespace does not have the requested constant" do
it "returns nil" do
- expect(subject.const_get_safely("INVALID_CONSTANT", TargetNamespace)).to eq(nil)
+ expect(subject.const_get_safely("INVALID_CONSTANT", TargetNamespace)).to be_nil
end
end
end