Skip to content

Commit

Permalink
[rubygems/rubygems] Fix issue when cleaning up plugin stubs
Browse files Browse the repository at this point in the history
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.

rubygems/rubygems@4e2fa0be77
  • Loading branch information
deivid-rodriguez authored and matzbot committed May 14, 2024
1 parent bd84236 commit 965cb3a
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 7 deletions.
6 changes: 2 additions & 4 deletions lib/rubygems/specification.rb
Original file line number Diff line number Diff line change
Expand Up @@ -906,9 +906,7 @@ def self.attribute_names
# 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

##
Expand All @@ -918,7 +916,7 @@ def self.dirs
def self.dirs=(dirs)
reset

@@dirs = Array(dirs).map {|dir| File.join dir, "specifications" }
@@dirs = Gem::SpecificationRecord.dirs_from(Array(dirs))
end

extend Enumerable
Expand Down
10 changes: 10 additions & 0 deletions lib/rubygems/specification_record.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 5 additions & 3 deletions lib/rubygems/uninstaller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ def initialize(gem, options = {})
# 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]
Expand All @@ -69,7 +70,7 @@ def initialize(gem, options = {})

# 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 = []
Expand Down Expand Up @@ -290,7 +291,8 @@ def remove_plugins(spec) # :nodoc:
# 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))
Expand Down
31 changes: 31 additions & 0 deletions test/rubygems/test_gem_uninstaller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,37 @@ def test_uninstall_with_install_dir_removes_plugins
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
Expand Down

0 comments on commit 965cb3a

Please sign in to comment.