[rubygems/rubygems] Fix activation conflicts when circularly requiring a gem

If a gem is required circular, and there are unresolved specs depending
on it, we may end up in an activation conflict.

The solution is to not try to activate unresolved gems when requiring a
default gem, regardless of it having already been activated or not.

https://github.com/rubygems/rubygems/commit/3b2b8f4e3e
This commit is contained in:
David Rodríguez 2024-01-12 14:53:40 +01:00 коммит произвёл Hiroshi SHIBATA
Родитель f1f5f22d22
Коммит db44088c2a
3 изменённых файлов: 52 добавлений и 2 удалений

Просмотреть файл

@ -1216,6 +1216,13 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
##
# Find a Gem::Specification of default gem from +path+
def find_default_spec(path)
@path_to_default_spec_map[path]
end
##
# Find an unresolved Gem::Specification of default gem from +path+
def find_unresolved_default_spec(path)
default_spec = @path_to_default_spec_map[path]
default_spec if default_spec && loaded_specs[default_spec.name] != default_spec

Просмотреть файл

@ -42,7 +42,11 @@ module Kernel
# If +path+ belongs to a default gem, we activate it and then go straight
# to normal require
if spec = Gem.find_unresolved_default_spec(path)
if spec = Gem.find_default_spec(path)
name = spec.name
next if Gem.loaded_specs[name]
# Ensure -I beats a default gem
resolved_path = begin
rp = nil
@ -60,7 +64,7 @@ module Kernel
rp
end
Kernel.send(:gem, spec.name, Gem::Requirement.default_prerelease) unless
Kernel.send(:gem, name, Gem::Requirement.default_prerelease) unless
resolved_path
next

Просмотреть файл

@ -560,6 +560,45 @@ class TestGemRequire < Gem::TestCase
assert_require "net/http"
end
def test_default_gem_required_circulary_with_unresolved_gems_depending_on_it
net_http_old = util_spec "net-http", "0.1.1", nil, "lib/net/http.rb"
install_gem net_http_old
net_http_default = new_default_spec "net-http", "0.3.0", nil, "net/http.rb"
net_http_default_path = File.join(@tempdir, "default_gems", "lib", "net/http.rb")
install_default_gems net_http_default
File.write(net_http_default_path, 'require "net/http"')
faraday_1 = util_spec "faraday", "1", { "net-http" => ">= 0" }
install_gem faraday_1
faraday_2 = util_spec "faraday", "2", { "net-http" => ">= 0" }
install_gem faraday_2
chef = util_spec "chef", "1", { "faraday" => [">= 1", "< 3"] }, "lib/chef.rb"
install_gem chef
assert_require "chef"
out, err = capture_output do
assert_require "net/http"
end
assert_empty out
circular_require_warning = false
err_lines = err.split("\n").reject do |line|
if line.include?("circular require")
circular_require_warning = true
elsif circular_require_warning # ignore backtrace lines for circular require warning
circular_require_warning = line.start_with?(/[\s]/)
end
end
assert_empty err_lines
end
def loaded_spec_names
Gem.loaded_specs.values.map(&:full_name).sort
end