Permalink
Browse files

Added --doctor to gem check

--doctor cleans up from bad uninstallation and cleans up bad gem
specifications.

Fixes #419
  • Loading branch information...
1 parent db017e2 commit 0832b641808cda46c56e7cab7470837cb001dd96 @drbrain drbrain committed Dec 22, 2012
View
@@ -17,6 +17,8 @@ of bundler will not work with RubyGems 2.0.
* Minor enhancements:
* This release of RubyGems can push gems to rubygems.org. Ordinarily
prerelease versions of RubyGems cannot push gems.
+ * Added `gem check --doctor` to clean up after failed uninstallation. Bug
+ #419 by Erik Hollensbe
* Bug fixes:
* Fixed exception raised when attempting to push gems to rubygems.org. Bug
View
@@ -123,7 +123,22 @@ module Gem
/wince/i,
]
- GEM_DEP_FILES = %w!gem.deps.rb Gemfile Isolate!
+ GEM_DEP_FILES = %w[
+ gem.deps.rb
+ Gemfile
+ Isolate
+ ]
+
+ ##
+ # Subdirectories in a gem repository
+
+ REPOSITORY_SUBDIRECTORIES = %w[
+ build_info
+ cache
+ doc
+ gems
+ specifications
+ ]
@@win_platform = nil
@@ -388,7 +403,7 @@ def self.ensure_gem_subdirectories dir = Gem.dir
require 'fileutils'
- %w[cache build_info doc gems specifications].each do |name|
+ REPOSITORY_SUBDIRECTORIES.each do |name|
subdir = File.join dir, name
next if File.exist? subdir
FileUtils.mkdir_p subdir rescue nil # in case of perms issues -- lame
@@ -1,25 +1,49 @@
require 'rubygems/command'
require 'rubygems/version_option'
require 'rubygems/validator'
+require 'pathname'
class Gem::Commands::CheckCommand < Gem::Command
include Gem::VersionOption
+ REPOSITORY_EXTENSION_MAP = {
+ 'build_info' => '.info',
+ 'cache' => '.gem',
+ 'doc' => '',
+ 'gems' => '',
+ 'specifications' => '.gemspec'
+ }
+
+ raise 'Update REPOSITORY_EXTENSION_MAP' unless
+ Gem::REPOSITORY_SUBDIRECTORIES == REPOSITORY_EXTENSION_MAP.keys.sort
+
def initialize
- super 'check', 'Check installed gems',
- :alien => true
+ super 'check', 'Check a gem repository for added or missing files',
+ :alien => true, :doctor => false, :gems => true
- add_option('-a', '--alien', "Report 'unmanaged' or rogue files in the",
- "gem repository") do |value, options|
- options[:alien] = true
+ add_option('-a', '--[no-]alien',
+ 'Report "unmanaged" or rogue files in the',
+ 'gem repository') do |value, options|
+ options[:alien] = value
+ end
+
+ add_option('--doctor',
+ 'Clean up uninstalled gems and broken',
+ 'specifications') do |value, options|
+ options[:doctor] = value
+ end
+
+ add_option('--[no-]gems',
+ 'Check installed gems for problems') do |value, options|
+ options[:gems] = value
end
add_version_option 'check'
end
- def execute
- say "Checking gems..."
+ def check_gems
+ say 'Checking gems...'
say
gems = get_all_gem_names rescue []
@@ -37,4 +61,56 @@ def execute
end
end
+ def doctor
+ say 'Checking for files from uninstalled gems...'
+ say
+
+ paths = Gem.path
+
+ paths.each do |gem_repo|
+ say "Checking #{gem_repo}"
+
+ Gem.use_paths gem_repo
+
+ gem_repo = Pathname(gem_repo)
+
+ installed_specs = Gem::Specification.map { |s| s.full_name }
+
+ REPOSITORY_EXTENSION_MAP.each do |sub_directory, extension|
+ directory = gem_repo + sub_directory
+
+ directory.each_child do |child|
+ next unless child.exist?
+
+ basename = child.basename(extension).to_s
+ next if installed_specs.include? basename
+ next if /^rubygems-\d/ =~ basename
+
+ type = child.directory? ? 'directory' : 'file'
+
+ child.rmtree
+
+ say "Removed #{type} #{sub_directory}/#{child.basename}"
+ end
+ end
+ end
+ end
+
+ def execute
+ check_gems if options[:gems]
+ doctor if options[:doctor]
+ end
+
+ def arguments # :nodoc:
+ 'GEMNAME name of gem to check'
+ end
+
+ def defaults_str # :nodoc:
+ '--gems --alien'
+ end
+
+ def usage # :nodoc:
+ "#{program_name} [OPTIONS] [GEMNAME ...]"
+ end
+
end
@@ -58,13 +58,11 @@ def find_files_for_gem(gem_directory)
public
ErrorData = Struct.new :path, :problem do
-
def <=> other
return nil unless self.class === other
[path, problem] <=> [other.path, other.problem]
end
-
end
##
@@ -121,7 +119,6 @@ def alien(gems=[])
File.readable? File.join(gem_directory, file_name)
}
- unreadable.map! { |entry, _| entry['path'] }
unreadable.sort.each do |path|
errors[gem_name][path] = "Unreadable file"
end
@@ -153,7 +150,9 @@ def alien(gems=[])
end
errors.each do |name, subhash|
- errors[name] = subhash.map { |path, msg| ErrorData.new(path, msg) }.sort
+ errors[name] = subhash.map do |path, msg|
+ ErrorData.new path, msg
+ end.sort
end
errors
@@ -9,10 +9,86 @@ def setup
@cmd = Gem::Commands::CheckCommand.new
end
+ def gem name
+ spec = quick_gem name do |gem|
+ gem.files = %W[lib/#{name}.rb Rakefile]
+ end
+
+ write_file File.join(*%W[gems #{spec.full_name} lib #{name}.rb])
+ write_file File.join(*%W[gems #{spec.full_name} Rakefile])
+
+ spec
+ end
+
def test_initialize
assert_equal "check", @cmd.command
assert_equal "gem check", @cmd.program_name
assert_match(/Check/, @cmd.summary)
end
+ def test_handle_options
+ @cmd.handle_options %w[--no-alien --no-gems --doctor]
+
+ refute @cmd.options[:alien]
+ refute @cmd.options[:gems]
+ assert @cmd.options[:doctor]
+ end
+
+ def test_handle_options_defaults
+ @cmd.handle_options []
+
+ assert @cmd.options[:alien]
+ assert @cmd.options[:gems]
+ refute @cmd.options[:doctor]
+ end
+
+ def test_doctor
+ a = gem 'a'
+ b = gem 'b'
+ c = gem 'c'
+
+ FileUtils.rm b.spec_file
+
+ open c.spec_file, 'w' do |io|
+ io.write 'this will raise an exception when evaluated.'
+ end
+
+ assert_path_exists File.join(a.gem_dir, 'Rakefile')
+ assert_path_exists File.join(a.gem_dir, 'lib', 'a.rb')
+
+ assert_path_exists b.gem_dir
+ refute_path_exists b.spec_file
+
+ assert_path_exists c.gem_dir
+ assert_path_exists c.spec_file
+
+ Gem.use_paths @gemhome
+
+ capture_io do
+ use_ui @ui do
+ @cmd.doctor
+ end
+ end
+
+ assert_path_exists File.join(a.gem_dir, 'Rakefile')
+ assert_path_exists File.join(a.gem_dir, 'lib', 'a.rb')
+
+ refute_path_exists b.gem_dir
+ refute_path_exists b.spec_file
+
+ refute_path_exists c.gem_dir
+ refute_path_exists c.spec_file
+
+ expected = <<-OUTPUT
+Checking for files from uninstalled gems...
+
+Checking #{@gemhome}
+Removed directory gems/b-2
+Removed directory gems/c-2
+Removed file specifications/c-2.gemspec
+ OUTPUT
+
+ assert_equal expected, @ui.output
+ end
+
end

0 comments on commit 0832b64

Please sign in to comment.