1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
|
# frozen_string_literal: true
require_relative '../command'
require_relative '../dependency_list'
require_relative '../uninstaller'
class Gem::Commands::CleanupCommand < Gem::Command
def initialize
super 'cleanup',
'Clean up old versions of installed gems',
:force => false, :install_dir => Gem.dir,
:check_dev => true
add_option('-n', '-d', '--dry-run',
'Do not uninstall gems') do |value, options|
options[:dryrun] = true
end
add_option(:Deprecated, '--dryrun',
'Do not uninstall gems') do |value, options|
options[:dryrun] = true
end
deprecate_option('--dryrun', extra_msg: 'Use --dry-run instead')
add_option('-D', '--[no-]check-development',
'Check development dependencies while uninstalling',
'(default: true)') do |value, options|
options[:check_dev] = value
end
add_option('--[no-]user-install',
'Cleanup in user\'s home directory instead',
'of GEM_HOME.') do |value, options|
options[:user_install] = value
end
@candidate_gems = nil
@default_gems = []
@full = nil
@gems_to_cleanup = nil
@original_home = nil
@original_path = nil
@primary_gems = nil
end
def arguments # :nodoc:
"GEMNAME name of gem to cleanup"
end
def defaults_str # :nodoc:
"--no-dry-run"
end
def description # :nodoc:
<<-EOF
The cleanup command removes old versions of gems from GEM_HOME that are not
required to meet a dependency. If a gem is installed elsewhere in GEM_PATH
the cleanup command won't delete it.
If no gems are named all gems in GEM_HOME are cleaned.
EOF
end
def usage # :nodoc:
"#{program_name} [GEMNAME ...]"
end
def execute
say "Cleaning up installed gems..."
if options[:args].empty?
done = false
last_set = nil
until done do
clean_gems
this_set = @gems_to_cleanup.map {|spec| spec.full_name }.sort
done = this_set.empty? || last_set == this_set
last_set = this_set
end
else
clean_gems
end
say "Clean up complete"
verbose do
skipped = @default_gems.map {|spec| spec.full_name }
"Skipped default gems: #{skipped.join ', '}"
end
end
def clean_gems
@original_home = Gem.dir
@original_path = Gem.path
get_primary_gems
get_candidate_gems
get_gems_to_cleanup
@full = Gem::DependencyList.from_specs
deplist = Gem::DependencyList.new
@gems_to_cleanup.each {|spec| deplist.add spec }
deps = deplist.strongly_connected_components.flatten
deps.reverse_each do |spec|
uninstall_dep spec
end
Gem::Specification.reset
end
def get_candidate_gems
@candidate_gems = unless options[:args].empty?
options[:args].map do |gem_name|
Gem::Specification.find_all_by_name gem_name
end.flatten
else
Gem::Specification.to_a
end
end
def get_gems_to_cleanup
gems_to_cleanup = @candidate_gems.select do |spec|
@primary_gems[spec.name].version != spec.version
end
default_gems, gems_to_cleanup = gems_to_cleanup.partition do |spec|
spec.default_gem?
end
uninstall_from = options[:user_install] ? Gem.user_dir : @original_home
gems_to_cleanup = gems_to_cleanup.select do |spec|
spec.base_dir == uninstall_from
end
@default_gems += default_gems
@default_gems.uniq!
@gems_to_cleanup = gems_to_cleanup.uniq
end
def get_primary_gems
@primary_gems = {}
Gem::Specification.each do |spec|
if @primary_gems[spec.name].nil? or
@primary_gems[spec.name].version < spec.version
@primary_gems[spec.name] = spec
end
end
end
def uninstall_dep(spec)
return unless @full.ok_to_remove?(spec.full_name, options[:check_dev])
if options[:dryrun]
say "Dry Run Mode: Would uninstall #{spec.full_name}"
return
end
say "Attempting to uninstall #{spec.full_name}"
uninstall_options = {
:executables => false,
:version => "= #{spec.version}",
}
uninstall_options[:user_install] = Gem.user_dir == spec.base_dir
uninstaller = Gem::Uninstaller.new spec.name, uninstall_options
begin
uninstaller.uninstall
rescue Gem::DependencyRemovalException, Gem::InstallError,
Gem::GemNotInHomeException, Gem::FilePermissionError => e
say "Unable to uninstall #{spec.full_name}:"
say "\t#{e.class}: #{e.message}"
end
ensure
# Restore path Gem::Uninstaller may have changed
Gem.use_paths @original_home, *@original_path
end
end
|