[rubygems/rubygems] Fix gem pristine etc resetting gem twice sometimes

If a default version and a regular version of etc are present at the
same time, RubyGems will end up duplicating work and running pristine
twice.

The `etc` gem is special because it's loaded by RubyGems by default.
When doing this, RubyGems will activate the regularly installed version.
The when `gem pristine` runs, it will find to installed specifications
but materialize both to the already activated specification.

Before:

```
$ gem pristine etc --version 1.4.3
Restoring gems to pristine condition...
Building native extensions. This could take a while...
Restored etc-1.4.3
Building native extensions. This could take a while...
Restored etc-1.4.3
```

After:

```
$ gem pristine etc --version 1.4.3
Restoring gems to pristine condition...
Skipped etc-1.4.3, it is a default gem
Building native extensions. This could take a while...
Restored etc-1.4.3
```

https://github.com/rubygems/rubygems/commit/5c279ac56b
This commit is contained in:
David Rodríguez 2024-10-08 20:01:25 +02:00 committed by git
parent 36e9a6bb41
commit d12c8cbf14
3 changed files with 41 additions and 11 deletions

View File

@ -100,7 +100,7 @@ class Gem::BasicSpecification
end
def default_gem?
loaded_from &&
!loaded_from.nil? &&
File.dirname(loaded_from) == Gem.default_specifications_dir
end

View File

@ -83,11 +83,7 @@ class Gem::StubSpecification < Gem::BasicSpecification
# True when this gem has been activated
def activated?
@activated ||=
begin
loaded = Gem.loaded_specs[name]
loaded && loaded.version == version
end
@activated ||= !loaded_spec.nil?
end
def default_gem?
@ -187,11 +183,7 @@ class Gem::StubSpecification < Gem::BasicSpecification
# The full Gem::Specification for this gem, loaded from evalling its gemspec
def spec
@spec ||= if @data
loaded = Gem.loaded_specs[name]
loaded if loaded && loaded.version == version
end
@spec ||= loaded_spec if @data
@spec ||= Gem::Specification.load(loaded_from)
end
alias_method :to_spec, :spec
@ -231,4 +223,13 @@ class Gem::StubSpecification < Gem::BasicSpecification
def sort_obj # :nodoc:
[name, version, Gem::Platform.sort_priority(platform)]
end
private
def loaded_spec
spec = Gem.loaded_specs[name]
return unless spec && spec.version == version && spec.default_gem? == default_gem?
spec
end
end

View File

@ -171,6 +171,35 @@ class TestStubSpecification < Gem::TestCase
assert_same real_foo, @foo.to_spec
end
def test_to_spec_default
bar_default_spec = File.join(@gemhome, "specifications", "default", "bar-0.0.2.gemspec")
File.open bar_default_spec, "w" do |io|
io.write <<~STUB
# -*- encoding: utf-8 -*-
# stub: bar 0.0.2 ruby lib
Gem::Specification.new do |s|
s.name = "bar"
s.version = "0.0.2"
s.platform = "ruby"
s.require_paths = ["lib"]
s.summary = "A very bar gem"
end
STUB
end
bar = Gem::StubSpecification.gemspec_stub BAR, @base_dir, @gems_dir
bar_default = Gem::StubSpecification.default_gemspec_stub bar_default_spec, @base_dir, @gems_dir
real_bar = util_spec bar_default.name, bar_default.version
real_bar.activate
assert_equal bar.version, Gem.loaded_specs[bar.name].version,
"sanity check"
refute_same real_bar, bar_default.to_spec
end
def test_to_spec_with_other_specs_loaded_does_not_warn
real_foo = util_spec @foo.name, @foo.version
real_foo.activate