diff --git a/lib/bundler/dsl.rb b/lib/bundler/dsl.rb index 000afebbbc..aad8759652 100644 --- a/lib/bundler/dsl.rb +++ b/lib/bundler/dsl.rb @@ -45,11 +45,15 @@ module Bundler with_gemfile(gemfile) do |current_gemfile| contents ||= Bundler.read_file(current_gemfile) instance_eval(contents, current_gemfile, 1) - rescue StandardError, ScriptError => e - message = "There was an error " \ - "#{e.is_a?(GemfileEvalError) ? "evaluating" : "parsing"} " \ - "`#{File.basename current_gemfile}`: #{e.message}" - + rescue GemfileEvalError => e + message = "There was an error evaluating `#{File.basename current_gemfile}`: #{e.message}" + raise DSLError.new(message, current_gemfile, e.backtrace, contents) + rescue GemfileError, InvalidArgumentError, InvalidOption, DeprecatedError, ScriptError => e + message = "There was an error parsing `#{File.basename current_gemfile}`: #{e.message}" + raise DSLError.new(message, current_gemfile, e.backtrace, contents) + rescue StandardError => e + raise unless e.backtrace_locations.first.path == current_gemfile + message = "There was an error parsing `#{File.basename current_gemfile}`: #{e.message}" raise DSLError.new(message, current_gemfile, e.backtrace, contents) end end @@ -215,7 +219,7 @@ module Bundler end def github(repo, options = {}) - raise ArgumentError, "GitHub sources require a block" unless block_given? + raise InvalidArgumentError, "GitHub sources require a block" unless block_given? github_uri = @git_sources["github"].call(repo) git_options = normalize_hash(options).merge("uri" => github_uri) git_source = @sources.add_git_source(git_options) diff --git a/lib/bundler/errors.rb b/lib/bundler/errors.rb index 35b5a55038..a0ce739ad7 100644 --- a/lib/bundler/errors.rb +++ b/lib/bundler/errors.rb @@ -244,4 +244,6 @@ module Bundler status_code(39) end + + class InvalidArgumentError < BundlerError; status_code(40); end end diff --git a/lib/bundler/ruby_version.rb b/lib/bundler/ruby_version.rb index 7e9e072b83..0ed5cbc6ca 100644 --- a/lib/bundler/ruby_version.rb +++ b/lib/bundler/ruby_version.rb @@ -23,7 +23,13 @@ module Bundler # specified must match the version. @versions = Array(versions).map do |v| - op, v = Gem::Requirement.parse(normalize_version(v)) + normalized_v = normalize_version(v) + + unless Gem::Requirement::PATTERN.match?(normalized_v) + raise InvalidArgumentError, "#{v} is not a valid requirement on the Ruby version" + end + + op, v = Gem::Requirement.parse(normalized_v) op == "=" ? v.to_s : "#{op} #{v}" end diff --git a/spec/bundler/bundler/ruby_dsl_spec.rb b/spec/bundler/bundler/ruby_dsl_spec.rb index 384ac4b8b2..c5ebbdf4db 100644 --- a/spec/bundler/bundler/ruby_dsl_spec.rb +++ b/spec/bundler/bundler/ruby_dsl_spec.rb @@ -80,7 +80,7 @@ RSpec.describe Bundler::RubyDsl do context "with two requirements in the same string" do let(:ruby_version) { ">= 2.0.0, < 3.0" } it "raises an error" do - expect { subject }.to raise_error(ArgumentError) + expect { subject }.to raise_error(Bundler::InvalidArgumentError) end end @@ -168,7 +168,7 @@ RSpec.describe Bundler::RubyDsl do let(:file_content) { "ruby-#{version}@gemset\n" } it "raises an error" do - expect { subject }.to raise_error(Gem::Requirement::BadRequirementError, "Illformed requirement [\"#{version}@gemset\"]") + expect { subject }.to raise_error(Bundler::InvalidArgumentError, "2.0.0@gemset is not a valid requirement on the Ruby version") end end diff --git a/spec/bundler/install/gemfile_spec.rb b/spec/bundler/install/gemfile_spec.rb index 0539733d5d..96ed174e9b 100644 --- a/spec/bundler/install/gemfile_spec.rb +++ b/spec/bundler/install/gemfile_spec.rb @@ -66,6 +66,31 @@ RSpec.describe "bundle install" do end end + context "when an internal error happens" do + let(:bundler_bug) do + create_file("bundler_bug.rb", <<~RUBY) + require "bundler" + + module Bundler + class Dsl + def source(source, *args, &blk) + nil.name + end + end + end + RUBY + + bundled_app("bundler_bug.rb").to_s + end + + it "shows culprit file and line" do + skip "ruby-core test setup has always \"lib\" in $LOAD_PATH so `require \"bundler\"` always activates the local version rather than using RubyGems gem activation stuff, causing conflicts" if ruby_core? + + install_gemfile "source 'https://gem.repo1'", requires: [bundler_bug], artifice: nil, raise_on_error: false + expect(err).to include("bundler_bug.rb:6") + end + end + context "with engine specified in symbol", :jruby_only do it "does not raise any error parsing Gemfile" do install_gemfile <<-G