[rubygems/rubygems] Fix issue when cleaning up plugin stubs

When `gem uninstall <gem> --install-dir <dir>` is run, if the version
removed had a plugin, and that same version happened to also be
installed globally, then the plugin stub would fail to be removed.

https://github.com/rubygems/rubygems/commit/4e2fa0be77
This commit is contained in:
David Rodriguez 2024-05-06 21:00:14 +02:00 committed by git
parent bd84236169
commit 965cb3ab4c
4 changed files with 48 additions and 7 deletions

View File

@ -906,9 +906,7 @@ class Gem::Specification < Gem::BasicSpecification
# Return the directories that Specification uses to find specs.
def self.dirs
@@dirs ||= Gem.path.collect do |dir|
File.join dir, "specifications"
end
@@dirs ||= Gem::SpecificationRecord.dirs_from(Gem.path)
end
##
@ -918,7 +916,7 @@ class Gem::Specification < Gem::BasicSpecification
def self.dirs=(dirs)
reset
@@dirs = Array(dirs).map {|dir| File.join dir, "specifications" }
@@dirs = Gem::SpecificationRecord.dirs_from(Array(dirs))
end
extend Enumerable

View File

@ -2,6 +2,16 @@
module Gem
class SpecificationRecord
def self.dirs_from(paths)
paths.map do |path|
File.join(path, "specifications")
end
end
def self.from_path(path)
new(dirs_from([path]))
end
def initialize(dirs)
@all = nil
@stubs = nil

View File

@ -49,7 +49,8 @@ class Gem::Uninstaller
# TODO: document the valid options
@gem = gem
@version = options[:version] || Gem::Requirement.default
@gem_home = File.realpath(options[:install_dir] || Gem.dir)
@install_dir = options[:install_dir]
@gem_home = File.realpath(@install_dir || Gem.dir)
@force_executables = options[:executables]
@force_all = options[:all]
@force_ignore = options[:ignore]
@ -69,7 +70,7 @@ class Gem::Uninstaller
# only add user directory if install_dir is not set
@user_install = false
@user_install = options[:user_install] unless options[:install_dir]
@user_install = options[:user_install] unless @install_dir
# Optimization: populated during #uninstall
@default_specs_matching_uninstall_params = []
@ -290,7 +291,8 @@ class Gem::Uninstaller
# Regenerates plugin wrappers after removal.
def regenerate_plugins
latest = Gem::Specification.latest_spec_for(@spec.name)
specification_record = @install_dir ? Gem::SpecificationRecord.from_path(@install_dir) : Gem::Specification.specification_record
latest = specification_record.latest_spec_for(@spec.name)
return if latest.nil?
regenerate_plugins_for(latest, plugin_dir_for(@spec))

View File

@ -213,6 +213,37 @@ class TestGemUninstaller < Gem::InstallerTestCase
Gem::Uninstaller.new(@spec.name, executables: true, install_dir: install_dir).uninstall
assert File.exist?(plugin_path), "plugin unintentionally removed"
refute File.exist?(install_dir_plugin_path), "plugin not removed"
end
def test_uninstall_with_install_dir_regenerates_plugins
write_file File.join(@tempdir, "lib", "rubygems_plugin.rb") do |io|
io.write "# do nothing"
end
@spec.files += %w[lib/rubygems_plugin.rb]
install_dir = "#{@gemhome}2"
package = Gem::Package.build(@spec)
spec_v9 = @spec.dup
spec_v9.version = "9"
package_v9 = Gem::Package.build(spec_v9)
Gem::Installer.at(package, force: true, install_dir: install_dir).install
Gem::Installer.at(package_v9, force: true, install_dir: install_dir).install
install_dir_plugin_path = File.join install_dir, "plugins/a_plugin.rb"
assert File.exist?(install_dir_plugin_path), "plugin not written"
Gem::Specification.dirs = [install_dir]
Gem::Uninstaller.new(@spec.name, version: "9", executables: true, install_dir: install_dir).uninstall
assert File.exist?(install_dir_plugin_path), "plugin unintentionally removed"
Gem::Specification.dirs = [install_dir]
Gem::Uninstaller.new(@spec.name, executables: true, install_dir: install_dir).uninstall
refute File.exist?(install_dir_plugin_path), "plugin not removed"
end
def test_remove_plugins_user_installed