diff --git a/lib/rubygems.rb b/lib/rubygems.rb index 882da967b112..54bd995b8333 100644 --- a/lib/rubygems.rb +++ b/lib/rubygems.rb @@ -268,10 +268,17 @@ def self.find_spec_for_exe(name, exec_name, requirements) return loaded if loaded && dep.matches_spec?(loaded) - spec = dep.spec_for_exe(exec_name) + specs = dep.matching_specs(true) - unless spec + specs = specs.find_all do |spec| + spec.executables.include? exec_name + end if exec_name + + unless spec = specs.first msg = "can't find gem #{dep} with executable #{exec_name}" + if name == "bundler" && bundler_message = Gem::BundlerVersionFinder.missing_version_message + msg = bundler_message + end raise Gem::GemNotFoundException, msg end diff --git a/lib/rubygems/bundler_version_finder.rb b/lib/rubygems/bundler_version_finder.rb index 8d03dce0f9c9..e74baca1ee67 100644 --- a/lib/rubygems/bundler_version_finder.rb +++ b/lib/rubygems/bundler_version_finder.rb @@ -41,11 +41,10 @@ def self.compatible?(spec) spec.version.segments.first == bundler_version.segments.first end - def self.prioritize!(specs) - match_index = specs.find_index { |spec| compatible?(spec) } - return unless match_index + def self.filter!(specs) + return unless bundler_version = self.bundler_version - specs.unshift(specs.delete_at(match_index)) + specs.reject! { |spec| spec.version.segments.first != bundler_version.segments.first } end def self.bundle_update_bundler_version diff --git a/lib/rubygems/dependency.rb b/lib/rubygems/dependency.rb index 8c35ab49c762..3c91392cdd6b 100644 --- a/lib/rubygems/dependency.rb +++ b/lib/rubygems/dependency.rb @@ -281,7 +281,7 @@ def matching_specs(platform_only = false) requirement.satisfied_by?(spec.version) && env_req.satisfied_by?(spec.version) end.map(&:to_spec) - Gem::BundlerVersionFinder.prioritize!(matches) if name == "bundler".freeze + Gem::BundlerVersionFinder.filter!(matches) if name == "bundler".freeze if platform_only matches.reject! do |spec| @@ -325,36 +325,13 @@ def to_spec active = matches.find { |spec| spec.activated? } return active if active - unless prerelease? - # Move prereleases to the end of the list for >= 0 requirements - pre, matches = matches.partition { |spec| spec.version.prerelease? } - matches += pre if requirement == Gem::Requirement.default - end - - select_first(matches) - end - - def spec_for_exe(exec_name) - matches = matching_specs(true) - - matches = matches.find_all do |spec| - spec.executables.include? exec_name - end if exec_name + return matches.first if prerelease? - select_first(matches) - end - - private - - def select_first(matches) - spec = matches.first - return unless spec - - if !Gem::BundlerVersionFinder.compatible?(spec) - warn Gem::BundlerVersionFinder.missing_version_message - end + # Move prereleases to the end of the list for >= 0 requirements + pre, matches = matches.partition { |spec| spec.version.prerelease? } + matches += pre if requirement == Gem::Requirement.default - spec + matches.first end end diff --git a/lib/rubygems/errors.rb b/lib/rubygems/errors.rb index 446cfc6cee51..2cd18e2e20f0 100644 --- a/lib/rubygems/errors.rb +++ b/lib/rubygems/errors.rb @@ -63,6 +63,9 @@ def initialize(name, requirement, specs) private def build_message + if name == "bundler" && message = Gem::BundlerVersionFinder.missing_version_message + return message + end names = specs.map(&:full_name) "Could not find '#{name}' (#{requirement}) - did find: [#{names.join ','}]\n" end diff --git a/test/rubygems/test_gem.rb b/test/rubygems/test_gem.rb index 20964273b595..ad802e0f950e 100644 --- a/test/rubygems/test_gem.rb +++ b/test/rubygems/test_gem.rb @@ -321,14 +321,14 @@ def test_activate_bin_path_gives_proper_error_for_bundler File.open("Gemfile", "w") { |f| f.puts('source "https://rubygems.org"') } - _, err = capture_io do + e = assert_raises Gem::GemNotFoundException do load Gem.activate_bin_path("bundler", "bundle", ">= 0.a") end - assert_includes err, "Could not find 'bundler' (9999) required by your #{File.expand_path("Gemfile.lock")}." - assert_includes err, "To update to the latest version installed on your system, run `bundle update --bundler`." - assert_includes err, "To install the missing version, run `gem install bundler:9999`" - refute_includes err, "can't find gem bundler (>= 0.a) with executable bundle" + assert_includes e.message, "Could not find 'bundler' (9999) required by your #{File.expand_path("Gemfile.lock")}." + assert_includes e.message, "To update to the latest version installed on your system, run `bundle update --bundler`." + assert_includes e.message, "To install the missing version, run `gem install bundler:9999`" + refute_includes e.message, "can't find gem bundler (>= 0.a) with executable bundle" end def test_self_bin_path_no_exec_name diff --git a/test/rubygems/test_gem_bundler_version_finder.rb b/test/rubygems/test_gem_bundler_version_finder.rb index 235afbcc1ad7..8e883bc71984 100644 --- a/test/rubygems/test_gem_bundler_version_finder.rb +++ b/test/rubygems/test_gem_bundler_version_finder.rb @@ -100,33 +100,33 @@ def test_compatible end end - def test_prioritize + def test_filter versions = %w[1 1.0 1.0.1.1 2 2.a 2.0 2.1.1 3 3.a 3.0 3.1.1] specs = versions.map { |v| util_spec("bundler", v) } - assert_equal %w[1 1.0 1.0.1.1 2 2.a 2.0 2.1.1 3 3.a 3.0 3.1.1], util_prioritize_specs(specs) + assert_equal %w[1 1.0 1.0.1.1 2 2.a 2.0 2.1.1 3 3.a 3.0 3.1.1], util_filter_specs(specs).map(&:version).map(&:to_s) bvf.stub(:bundler_version, v("2.1.1.1")) do - assert_equal %w[2 1 1.0 1.0.1.1 2.a 2.0 2.1.1 3 3.a 3.0 3.1.1], util_prioritize_specs(specs) + assert_equal %w[2 2.a 2.0 2.1.1], util_filter_specs(specs).map(&:version).map(&:to_s) end bvf.stub(:bundler_version, v("1.1.1.1")) do - assert_equal %w[1 1.0 1.0.1.1 2 2.a 2.0 2.1.1 3 3.a 3.0 3.1.1], util_prioritize_specs(specs) + assert_equal %w[1 1.0 1.0.1.1], util_filter_specs(specs).map(&:version).map(&:to_s) end bvf.stub(:bundler_version, v("1")) do - assert_equal %w[1 1.0 1.0.1.1 2 2.a 2.0 2.1.1 3 3.a 3.0 3.1.1], util_prioritize_specs(specs) + assert_equal %w[1 1.0 1.0.1.1], util_filter_specs(specs).map(&:version).map(&:to_s) end bvf.stub(:bundler_version, v("2.a")) do - assert_equal %w[2 1 1.0 1.0.1.1 2.a 2.0 2.1.1 3 3.a 3.0 3.1.1], util_prioritize_specs(specs) + assert_equal %w[2 2.a 2.0 2.1.1], util_filter_specs(specs).map(&:version).map(&:to_s) end bvf.stub(:bundler_version, v("3")) do - assert_equal %w[3 1 1.0 1.0.1.1 2 2.a 2.0 2.1.1 3.a 3.0 3.1.1], util_prioritize_specs(specs) + assert_equal %w[3 3.a 3.0 3.1.1], util_filter_specs(specs).map(&:version).map(&:to_s) end end - def util_prioritize_specs(specs) + def util_filter_specs(specs) specs = specs.dup - bvf.prioritize!(specs) - specs.map(&:version).map(&:to_s) + bvf.filter!(specs) + specs end end diff --git a/test/rubygems/test_gem_dependency.rb b/test/rubygems/test_gem_dependency.rb index e96711322601..bf2f0655732e 100644 --- a/test/rubygems/test_gem_dependency.rb +++ b/test/rubygems/test_gem_dependency.rb @@ -353,12 +353,16 @@ def test_to_specs_respects_bundler_version assert_equal [b, b_1], dep.to_specs - Gem::BundlerVersionFinder.stub(:bundler_version_with_reason, ["1.16", "reason"]) do - assert_equal [b_1, b], dep.to_specs + Gem::BundlerVersionFinder.stub(:bundler_version_with_reason, ["3.5", "reason"]) do + e = assert_raises Gem::MissingSpecVersionError do + dep.to_specs + end + + assert_match "Could not find 'bundler' (3.5) required by reason.\nTo update to the latest version installed on your system, run `bundle update --bundler`.\nTo install the missing version, run `gem install bundler:3.5`\n", e.message end Gem::BundlerVersionFinder.stub(:bundler_version_with_reason, ["2.0.0.pre.1", "reason"]) do - assert_equal [b, b_1], dep.to_specs + assert_equal [b], dep.to_specs end end diff --git a/test/rubygems/test_kernel.rb b/test/rubygems/test_kernel.rb index d562820919d7..b9f9b5e2544d 100644 --- a/test/rubygems/test_kernel.rb +++ b/test/rubygems/test_kernel.rb @@ -104,10 +104,10 @@ def test_gem_bundler_missing_bundler_version quick_gem 'bundler', '1' quick_gem 'bundler', '2.a' - _, e = capture_io do + e = assert_raises Gem::MissingSpecVersionError do gem('bundler') end - assert_match "Could not find 'bundler' (55) required by reason.", e + assert_match "Could not find 'bundler' (55) required by reason.", e.message end end diff --git a/test/rubygems/test_require.rb b/test/rubygems/test_require.rb index 56064d20f15f..16159ef65e90 100644 --- a/test/rubygems/test_require.rb +++ b/test/rubygems/test_require.rb @@ -399,10 +399,10 @@ def test_require_bundler_missing_bundler_version b2a = util_spec('bundler', '2.a', nil, "lib/bundler/setup.rb") install_specs b1, b2a - _, e = capture_io do + e = assert_raises Gem::MissingSpecVersionError do gem('bundler') end - assert_match "Could not find 'bundler' (55) required by reason.", e + assert_match "Could not find 'bundler' (55) required by reason.", e.message end end