diff options
author | Homu <homu@barosl.com> | 2016-02-11 16:06:06 +0900 |
---|---|---|
committer | Homu <homu@barosl.com> | 2016-02-11 16:06:06 +0900 |
commit | e76760d1a492cf457a9952da2e5c24a3ba50b51d (patch) | |
tree | 3a5a2f815f4742d95886f50f8ad1bf605f335a4c | |
parent | 1c2710639ccd8558c8467bb3cd2f95548306bc31 (diff) | |
parent | 57af37d4748df9473aa1295fe20711ffd96c5097 (diff) | |
download | bundler-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.rb | 9 | ||||
-rw-r--r-- | lib/bundler/shared_helpers.rb | 2 | ||||
-rw-r--r-- | spec/bundler/shared_helpers_spec.rb | 244 |
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 |