diff --git a/lib/rubygems.rb b/lib/rubygems.rb index 21af9ac8f841..a18cb9905bcb 100644 --- a/lib/rubygems.rb +++ b/lib/rubygems.rb @@ -266,17 +266,10 @@ def self.find_spec_for_exe(name, exec_name, requirements) return loaded if loaded && dep.matches_spec?(loaded) - specs = dep.matching_specs(true) + spec = dep.spec_for_exe(exec_name) - specs = specs.find_all do |spec| - spec.executables.include? exec_name - end if exec_name - - unless spec = specs.first + unless spec 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 @@ -1182,6 +1175,8 @@ def self.use_gemdeps(path = nil) end ENV["BUNDLE_GEMFILE"] ||= File.expand_path(path) + ENV["BUNDLE_PATH__SYSTEM"] ||= "true" + require 'rubygems/user_interaction' Gem::DefaultUserInteraction.use_ui(ui) do require "bundler" diff --git a/lib/rubygems/bundler_version_finder.rb b/lib/rubygems/bundler_version_finder.rb index e74baca1ee67..8d03dce0f9c9 100644 --- a/lib/rubygems/bundler_version_finder.rb +++ b/lib/rubygems/bundler_version_finder.rb @@ -41,10 +41,11 @@ def self.compatible?(spec) spec.version.segments.first == bundler_version.segments.first end - def self.filter!(specs) - return unless bundler_version = self.bundler_version + def self.prioritize!(specs) + match_index = specs.find_index { |spec| compatible?(spec) } + return unless match_index - specs.reject! { |spec| spec.version.segments.first != bundler_version.segments.first } + specs.unshift(specs.delete_at(match_index)) end def self.bundle_update_bundler_version diff --git a/lib/rubygems/core_ext/kernel_require.rb b/lib/rubygems/core_ext/kernel_require.rb index 3b7801161978..014090a16ef7 100755 --- a/lib/rubygems/core_ext/kernel_require.rb +++ b/lib/rubygems/core_ext/kernel_require.rb @@ -39,7 +39,7 @@ def require(path) if spec = Gem.find_unresolved_default_spec(path) Gem.remove_unresolved_default_spec(spec) begin - Kernel.send(:gem, spec.name) + Kernel.send(:gem, spec.name, "#{Gem::Requirement.default}.a") rescue Exception RUBYGEMS_ACTIVATION_MONITOR.exit raise diff --git a/lib/rubygems/dependency.rb b/lib/rubygems/dependency.rb index 3c91392cdd6b..8c35ab49c762 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.filter!(matches) if name == "bundler".freeze + Gem::BundlerVersionFinder.prioritize!(matches) if name == "bundler".freeze if platform_only matches.reject! do |spec| @@ -325,13 +325,36 @@ def to_spec active = matches.find { |spec| spec.activated? } return active if active - return matches.first if prerelease? + 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 - # 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 + 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 - matches.first + spec end end diff --git a/lib/rubygems/errors.rb b/lib/rubygems/errors.rb index 2cd18e2e20f0..446cfc6cee51 100644 --- a/lib/rubygems/errors.rb +++ b/lib/rubygems/errors.rb @@ -63,9 +63,6 @@ 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 af927ffa29be..1d6484502006 100644 --- a/test/rubygems/test_gem.rb +++ b/test/rubygems/test_gem.rb @@ -329,14 +329,14 @@ def test_activate_bin_path_gives_proper_error_for_bundler File.open("Gemfile", "w") { |f| f.puts('source "https://rubygems.org"') } - e = assert_raises Gem::GemNotFoundException do + _, err = capture_io do load Gem.activate_bin_path("bundler", "bundle", ">= 0.a") end - 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" + 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" end def test_self_bin_path_no_exec_name @@ -1824,11 +1824,21 @@ def test_use_gemdeps_missing_gem platform = " #{platform}" end - expected = <<-EXPECTED + expected = + if Bundler.feature_flag.lockfile_uses_separate_rubygems_sources? + <<-EXPECTED +Could not find gem 'a' in locally installed gems. +The source does not contain any versions of 'a' +You may need to `gem install -g` to install missing gems + + EXPECTED + else + <<-EXPECTED Could not find gem 'a#{platform}' in any of the gem sources listed in your Gemfile. You may need to `gem install -g` to install missing gems - EXPECTED + EXPECTED + end assert_output nil, expected do Gem.use_gemdeps diff --git a/test/rubygems/test_gem_bundler_version_finder.rb b/test/rubygems/test_gem_bundler_version_finder.rb index 8e883bc71984..235afbcc1ad7 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_filter + def test_prioritize 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_filter_specs(specs).map(&:version).map(&:to_s) + 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) bvf.stub(:bundler_version, v("2.1.1.1")) do - assert_equal %w[2 2.a 2.0 2.1.1], util_filter_specs(specs).map(&:version).map(&:to_s) + 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) end bvf.stub(:bundler_version, v("1.1.1.1")) do - assert_equal %w[1 1.0 1.0.1.1], util_filter_specs(specs).map(&:version).map(&:to_s) + 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) end bvf.stub(:bundler_version, v("1")) do - assert_equal %w[1 1.0 1.0.1.1], util_filter_specs(specs).map(&:version).map(&:to_s) + 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) end bvf.stub(:bundler_version, v("2.a")) do - assert_equal %w[2 2.a 2.0 2.1.1], util_filter_specs(specs).map(&:version).map(&:to_s) + 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) end bvf.stub(:bundler_version, v("3")) do - assert_equal %w[3 3.a 3.0 3.1.1], util_filter_specs(specs).map(&:version).map(&:to_s) + 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) end end - def util_filter_specs(specs) + def util_prioritize_specs(specs) specs = specs.dup - bvf.filter!(specs) - specs + bvf.prioritize!(specs) + specs.map(&:version).map(&:to_s) end end diff --git a/test/rubygems/test_gem_dependency.rb b/test/rubygems/test_gem_dependency.rb index bf2f0655732e..e96711322601 100644 --- a/test/rubygems/test_gem_dependency.rb +++ b/test/rubygems/test_gem_dependency.rb @@ -353,16 +353,12 @@ def test_to_specs_respects_bundler_version assert_equal [b, b_1], 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 + Gem::BundlerVersionFinder.stub(:bundler_version_with_reason, ["1.16", "reason"]) do + assert_equal [b_1, b], dep.to_specs end Gem::BundlerVersionFinder.stub(:bundler_version_with_reason, ["2.0.0.pre.1", "reason"]) do - assert_equal [b], dep.to_specs + assert_equal [b, b_1], dep.to_specs end end diff --git a/test/rubygems/test_kernel.rb b/test/rubygems/test_kernel.rb index b9f9b5e2544d..d562820919d7 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 = assert_raises Gem::MissingSpecVersionError do + _, e = capture_io do gem('bundler') end - assert_match "Could not find 'bundler' (55) required by reason.", e.message + assert_match "Could not find 'bundler' (55) required by reason.", e end end diff --git a/test/rubygems/test_require.rb b/test/rubygems/test_require.rb index 16159ef65e90..2e3664a3384f 100644 --- a/test/rubygems/test_require.rb +++ b/test/rubygems/test_require.rb @@ -323,6 +323,19 @@ def test_default_gem_and_normal_gem assert_equal %w(default-3.0), loaded_spec_names end + def test_default_gem_prerelease + default_gem_spec = new_default_spec("default", "2.0.0", + nil, "default/gem.rb") + install_default_specs(default_gem_spec) + + default_gem_prerelease_spec = new_default_spec("default", "3.0.0.rc2", + nil, "default/gem.rb") + install_default_specs(default_gem_prerelease_spec) + + assert_require "default/gem" + assert_equal %w(default-3.0.0.rc2), loaded_spec_names + end + def loaded_spec_names Gem.loaded_specs.values.map(&:full_name).sort end @@ -399,10 +412,10 @@ def test_require_bundler_missing_bundler_version b2a = util_spec('bundler', '2.a', nil, "lib/bundler/setup.rb") install_specs b1, b2a - e = assert_raises Gem::MissingSpecVersionError do + _, e = capture_io do gem('bundler') end - assert_match "Could not find 'bundler' (55) required by reason.", e.message + assert_match "Could not find 'bundler' (55) required by reason.", e end end