summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authordrbrain <drbrain@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2012-12-23 00:35:09 +0000
committerdrbrain <drbrain@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2012-12-23 00:35:09 +0000
commit6fe32d72667945605ef710395706e04491bfd86a (patch)
tree7fe4b8f779ff75067b45f48502864fb05787cee7 /lib
parentc47c095b9740e7c19d6fdca29ab661c1089221d4 (diff)
* lib/rubygems/commands/check_command.rb: Added --doctor and --dry-run
options to clean up after failed uninstallation. * test/rubygems/test_gem_commands_check_command.rb: Test for above. * lib/rubygems/commands/push_command.rb: Allow pushes from RubyGems 2.0.0.preview3 * lib/rubygems/commands/update_command.rb: Use Gem.ruby_version * lib/rubygems/dependency.rb: Update style. * lib/rubygems/installer.rb: Ensure installed gem specifications will be useable. Refactor. * test/rubygems/test_gem_installer.rb: ditto. * lib/rubygems/validator.rb: Fixed bug with unreadable files. * lib/rubygems.rb: Fixed broken methods. * test/rubygems/test_gem.rb: Test for above. * test/rubygems/test_gem_commands_push_command.rb: Fixed overridden Gem.latest_rubygems_version git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@38564 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib')
-rw-r--r--lib/rubygems.rb57
-rw-r--r--lib/rubygems/commands/check_command.rb60
-rw-r--r--lib/rubygems/commands/push_command.rb12
-rw-r--r--lib/rubygems/commands/update_command.rb2
-rw-r--r--lib/rubygems/dependency.rb10
-rw-r--r--lib/rubygems/doctor.rb124
-rw-r--r--lib/rubygems/installer.rb104
-rw-r--r--lib/rubygems/validator.rb7
8 files changed, 308 insertions, 68 deletions
diff --git a/lib/rubygems.rb b/lib/rubygems.rb
index 29bbb586b5..77d1650056 100644
--- a/lib/rubygems.rb
+++ b/lib/rubygems.rb
@@ -98,7 +98,7 @@
require 'rbconfig'
module Gem
- VERSION = '2.0.0.preview2.2'
+ VERSION = '2.0.0.preview3'
end
# Must be first since it unloads the prelude from 1.9.2
@@ -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 @@ module Gem
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
@@ -750,34 +765,36 @@ module Gem
@ruby
end
- # DOC: needs doc'd or :nodoc'd
+ ##
+ # Returns the latest release-version specification for the gem +name+.
+
def self.latest_spec_for name
- dependency = Gem::Dependency.new name
- fetcher = Gem::SpecFetcher.fetcher
- spec_tuples = fetcher.find_matching dependency
+ dependency = Gem::Dependency.new name
+ fetcher = Gem::SpecFetcher.fetcher
+ spec_tuples, = fetcher.spec_for_dependency dependency
- match = spec_tuples.select { |(n, _, p), _|
- n == name and Gem::Platform.match p
- }.sort_by { |(_, version, _), _|
- version
- }.last
+ spec, = spec_tuples.first
- match and fetcher.fetch_spec(*match)
+ spec
end
- # DOC: needs doc'd or :nodoc'd
- def self.latest_version_for name
- spec = latest_spec_for name
- spec and spec.version
- end
+ ##
+ # Returns the latest release version of RubyGems.
- # DOC: needs doc'd or :nodoc'd
def self.latest_rubygems_version
- latest_version_for("rubygems-update") or
+ latest_version_for('rubygems-update') or
raise "Can't find 'rubygems-update' in any repo. Check `gem source list`."
end
##
+ # Returns the version of the latest release-version of gem +name+
+
+ def self.latest_version_for name
+ spec = latest_spec_for name
+ spec and spec.version
+ end
+
+ ##
# A Gem::Version for the currently running ruby.
def self.ruby_version
diff --git a/lib/rubygems/commands/check_command.rb b/lib/rubygems/commands/check_command.rb
index 4bdfcfa645..d7677d47a1 100644
--- a/lib/rubygems/commands/check_command.rb
+++ b/lib/rubygems/commands/check_command.rb
@@ -1,25 +1,44 @@
require 'rubygems/command'
require 'rubygems/version_option'
require 'rubygems/validator'
+require 'rubygems/doctor'
class Gem::Commands::CheckCommand < Gem::Command
include Gem::VersionOption
def initialize
- super 'check', 'Check installed gems',
- :alien => true
+ super 'check', 'Check a gem repository for added or missing files',
+ :alien => true, :doctor => false, :dry_run => 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('--[no-]doctor',
+ 'Clean up uninstalled gems and broken',
+ 'specifications') do |value, options|
+ options[:doctor] = value
+ end
+
+ add_option('--[no-]dry-run',
+ 'Do not remove files, only report what',
+ 'would be removed') do |value, options|
+ options[:dry_run] = 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 +56,31 @@ class Gem::Commands::CheckCommand < Gem::Command
end
end
+ def doctor
+ say 'Checking for files from uninstalled gems...'
+ say
+
+ Gem.path.each do |gem_repo|
+ doctor = Gem::Doctor.new gem_repo, options[:dry_run]
+ doctor.doctor
+ 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
diff --git a/lib/rubygems/commands/push_command.rb b/lib/rubygems/commands/push_command.rb
index 0667b47dc1..ce70836823 100644
--- a/lib/rubygems/commands/push_command.rb
+++ b/lib/rubygems/commands/push_command.rb
@@ -40,9 +40,17 @@ class Gem::Commands::PushCommand < Gem::Command
def send_gem name
args = [:post, "api/v1/gems"]
+ latest_rubygems_version = Gem.latest_rubygems_version
- if Gem.latest_rubygems_version < Gem::Version.new(Gem::VERSION) then
- alert_error "Using beta/unreleased version of rubygems. Not pushing."
+ if latest_rubygems_version < Gem.rubygems_version and
+ Gem.rubygems_version.prerelease? and
+ Gem::Version.new('2.0.0.preview3') != Gem.rubygems_version then
+ alert_error <<-ERROR
+You are using a beta release of RubyGems (#{Gem::VERSION}) which is not
+allowed to push gems. Please downgrade or upgrade to a release version.
+
+The latest released RubyGems version is #{latest_rubygems_version}
+ ERROR
terminate_interaction 1
end
diff --git a/lib/rubygems/commands/update_command.rb b/lib/rubygems/commands/update_command.rb
index 02f9657435..be76c4c7cd 100644
--- a/lib/rubygems/commands/update_command.rb
+++ b/lib/rubygems/commands/update_command.rb
@@ -142,7 +142,7 @@ class Gem::Commands::UpdateCommand < Gem::Command
gems_to_update = which_to_update hig, options[:args], :system
name, up_ver = gems_to_update.first
- current_ver = Gem::Version.new Gem::VERSION
+ current_ver = Gem.rubygems_version
target = if options[:system] == true then
up_ver
diff --git a/lib/rubygems/dependency.rb b/lib/rubygems/dependency.rb
index 217189bf8e..1e3cc168a8 100644
--- a/lib/rubygems/dependency.rb
+++ b/lib/rubygems/dependency.rb
@@ -10,14 +10,14 @@ class Gem::Dependency
#--
# When this list is updated, be sure to change
# Gem::Specification::CURRENT_SPECIFICATION_VERSION as well.
-
+ #
# REFACTOR: This type of constant, TYPES, indicates we might want
- # two classes, used via inheretance or duck typing.
+ # two classes, used via inheritance or duck typing.
TYPES = [
- :development,
- :runtime,
- ]
+ :development,
+ :runtime,
+ ]
##
# Dependency name or regular expression.
diff --git a/lib/rubygems/doctor.rb b/lib/rubygems/doctor.rb
new file mode 100644
index 0000000000..198c758b00
--- /dev/null
+++ b/lib/rubygems/doctor.rb
@@ -0,0 +1,124 @@
+require 'rubygems'
+require 'rubygems/user_interaction'
+require 'pathname'
+
+##
+# Cleans up after a partially-failed uninstall or for an invalid
+# Gem::Specification.
+#
+# If a specification was removed by hand this will remove any remaining files.
+#
+# If a corrupt specification was installed this will clean up warnings by
+# removing the bogus specification.
+
+class Gem::Doctor
+
+ include Gem::UserInteraction
+
+ ##
+ # Maps a gem subdirectory to the files that are expected to exist in the
+ # subdirectory.
+
+ REPOSITORY_EXTENSION_MAP = { # :nodoc:
+ 'build_info' => '.info',
+ 'cache' => '.gem',
+ 'doc' => '',
+ 'gems' => '',
+ 'specifications' => '.gemspec'
+ }
+
+ raise 'Update REPOSITORY_EXTENSION_MAP' unless
+ Gem::REPOSITORY_SUBDIRECTORIES == REPOSITORY_EXTENSION_MAP.keys.sort
+
+ ##
+ # Creates a new Gem::Doctor that will clean up +gem_repository+. Only one
+ # gem repository may be cleaned at a time.
+ #
+ # If +dry_run+ is true no files or directories will be removed.
+
+ def initialize gem_repository, dry_run = false
+ @gem_repository = Pathname(gem_repository)
+ @dry_run = dry_run
+
+ @installed_specs = nil
+ end
+
+ ##
+ # Specs installed in this gem repository
+
+ def installed_specs # :nodoc:
+ @installed_specs ||= Gem::Specification.map { |s| s.full_name }
+ end
+
+ ##
+ # Are we doctoring a gem repository?
+
+ def gem_repository?
+ not installed_specs.empty?
+ end
+
+ ##
+ # Cleans up uninstalled files and invalid gem specifications
+
+ def doctor
+ @orig_home = Gem.dir
+ @orig_path = Gem.path
+
+ say "Checking #{@gem_repository}"
+
+ Gem.use_paths @gem_repository.to_s
+
+ unless gem_repository? then
+ say 'This directory does not appear to be a RubyGems repository, ' +
+ 'skipping'
+ say
+ return
+ end
+
+ doctor_children
+
+ say
+ ensure
+ Gem.use_paths @orig_home, *@orig_path
+ end
+
+ ##
+ # Cleans up children of this gem repository
+
+ def doctor_children # :nodoc:
+ REPOSITORY_EXTENSION_MAP.each do |sub_directory, extension|
+ doctor_child sub_directory, extension
+ end
+ end
+
+ ##
+ # Removes files in +sub_directory+ with +extension+
+
+ def doctor_child sub_directory, extension # :nodoc:
+ directory = @gem_repository + 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
+ next if 'specifications' == sub_directory and 'default' == basename
+
+ type = child.directory? ? 'directory' : 'file'
+
+ action = if @dry_run then
+ 'Extra'
+ else
+ child.rmtree
+ 'Removed'
+ end
+
+ say "#{action} #{type} #{sub_directory}/#{child.basename}"
+ end
+ rescue Errno::ENOENT
+ # ignore
+ end
+
+end
+
diff --git a/lib/rubygems/installer.rb b/lib/rubygems/installer.rb
index 6c2e6ce5a3..7f9975678f 100644
--- a/lib/rubygems/installer.rb
+++ b/lib/rubygems/installer.rb
@@ -202,47 +202,24 @@ class Gem::Installer
# specifications/<gem-version>.gemspec #=> the Gem::Specification
def install
- verify_gem_home(options[:unpack])
-
- # If we're forcing the install then disable security unless the security
- # policy says that we only install signed gems.
- @security_policy = nil if @force and @security_policy and
- not @security_policy.only_signed
-
- unless @force
- ensure_required_ruby_version_met
- ensure_required_rubygems_version_met
- ensure_dependencies_met unless @ignore_dependencies
- end
+ pre_install_checks
run_pre_install_hooks
- Gem.ensure_gem_subdirectories gem_home
-
# Completely remove any previous gem files
- FileUtils.rm_rf(gem_dir)
+ FileUtils.rm_rf gem_dir
FileUtils.mkdir_p gem_dir
extract_files
- build_extensions
+ build_extensions
+ write_build_info_file
run_post_build_hooks
generate_bin
write_spec
-
- unless @build_args.empty?
- File.open spec.build_info_file, "w" do |f|
- @build_args.each { |a| f.puts a }
- end
- end
-
- # TODO should be always cache the file? Other classes have options
- # to controls if caching is done.
- cache_file = File.join(gem_home, "cache", "#{spec.full_name}.gem")
-
- FileUtils.cp gem, cache_file unless File.exist? cache_file
+ write_cache_file
say spec.post_install_message unless spec.post_install_message.nil?
@@ -255,7 +232,7 @@ class Gem::Installer
spec
# TODO This rescue is in the wrong place. What is raising this exception?
- # move this rescue to arround the code that actually might raise it.
+ # move this rescue to around the code that actually might raise it.
rescue Zlib::GzipFile::Error
raise Gem::InstallError, "gzip error installing #{gem}"
end
@@ -506,6 +483,21 @@ class Gem::Installer
end
end
+ ##
+ # Ensures the Gem::Specification written out for this gem is loadable upon
+ # installation.
+
+ def ensure_loadable_spec
+ ruby = spec.to_ruby_for_cache
+
+ begin
+ eval ruby
+ rescue StandardError, SyntaxError => e
+ raise Gem::InstallError,
+ "The specification for #{spec.full_name} is corrupt (#{e.class})"
+ end
+ end
+
# DOC: Missing docs or :nodoc:.
def ensure_required_ruby_version_met
if rrv = spec.required_ruby_version then
@@ -736,5 +728,59 @@ EOF
def dir
gem_dir.to_s
end
+
+ ##
+ # Performs various checks before installing the gem such as the install
+ # repository is writable and its directories exist, required ruby and
+ # rubygems versions are met and that dependencies are installed.
+ #
+ # Version and dependency checks are skipped if this install is forced.
+ #
+ # The dependent check will be skipped this install is ignoring dependencies.
+
+ def pre_install_checks
+ verify_gem_home options[:unpack]
+
+ # If we're forcing the install then disable security unless the security
+ # policy says that we only install signed gems.
+ @security_policy = nil if
+ @force and @security_policy and not @security_policy.only_signed
+
+ ensure_loadable_spec
+
+ Gem.ensure_gem_subdirectories gem_home
+
+ return true if @force
+
+ ensure_required_ruby_version_met
+ ensure_required_rubygems_version_met
+ ensure_dependencies_met unless @ignore_dependencies
+
+ true
+ end
+
+ ##
+ # Writes the file containing the arguments for building this gem's
+ # extensions.
+
+ def write_build_info_file
+ return if @build_args.empty?
+
+ open spec.build_info_file, 'w' do |io|
+ @build_args.each do |arg|
+ io.puts arg
+ end
+ end
+ end
+
+ ##
+ # Writes the .gem file to the cache directory
+
+ def write_cache_file
+ cache_file = File.join gem_home, 'cache', spec.file_name
+
+ FileUtils.cp @gem, cache_file unless File.exist? cache_file
+ end
+
end
diff --git a/lib/rubygems/validator.rb b/lib/rubygems/validator.rb
index f66b2c1f43..e5183d401f 100644
--- a/lib/rubygems/validator.rb
+++ b/lib/rubygems/validator.rb
@@ -58,13 +58,11 @@ class Gem::Validator
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 @@ class Gem::Validator
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 @@ class Gem::Validator
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