diff options
Diffstat (limited to 'lib/rubygems/commands')
29 files changed, 776 insertions, 453 deletions
diff --git a/lib/rubygems/commands/build_command.rb b/lib/rubygems/commands/build_command.rb index 36a6fe48f2..64563ed3db 100644 --- a/lib/rubygems/commands/build_command.rb +++ b/lib/rubygems/commands/build_command.rb @@ -1,5 +1,5 @@ require 'rubygems/command' -require 'rubygems/builder' +require 'rubygems/package' class Gem::Commands::BuildCommand < Gem::Command @@ -22,11 +22,11 @@ class Gem::Commands::BuildCommand < Gem::Command def execute gemspec = get_one_gem_name - if File.exist? gemspec - spec = load_gemspec gemspec + if File.exist? gemspec then + spec = Gem::Specification.load gemspec if spec then - Gem::Builder.new(spec).build options[:force] + Gem::Package.build spec, options[:force] else alert_error "Error loading gemspec. Aborting." terminate_interaction 1 @@ -37,23 +37,5 @@ class Gem::Commands::BuildCommand < Gem::Command end end - def load_gemspec filename - if yaml?(filename) - open(filename) do |f| - begin - Gem::Specification.from_yaml(f) - rescue Gem::EndOfYAMLException - nil - end - end - else - Gem::Specification.load(filename) # can return nil - end - end - - def yaml?(filename) - line = open(filename) { |f| line = f.gets } - result = line =~ %r{!ruby/object:Gem::Specification} - result - end end + diff --git a/lib/rubygems/commands/cert_command.rb b/lib/rubygems/commands/cert_command.rb index b416b3863d..371ab403c6 100644 --- a/lib/rubygems/commands/cert_command.rb +++ b/lib/rubygems/commands/cert_command.rb @@ -4,82 +4,224 @@ require 'rubygems/security' class Gem::Commands::CertCommand < Gem::Command def initialize - super 'cert', 'Manage RubyGems certificates and signing settings' - - add_option('-a', '--add CERT', - 'Add a trusted certificate.') do |value, options| - cert = OpenSSL::X509::Certificate.new(File.read(value)) - Gem::Security.add_trusted_cert(cert) - say "Added '#{cert.subject.to_s}'" - end - - add_option('-l', '--list', - 'List trusted certificates.') do |value, options| - glob_str = File::join(Gem::Security::OPT[:trust_dir], '*.pem') - Dir::glob(glob_str) do |path| - begin - cert = OpenSSL::X509::Certificate.new(File.read(path)) - # this could probably be formatted more gracefully - say cert.subject.to_s - rescue OpenSSL::X509::CertificateError - next - end + super 'cert', 'Manage RubyGems certificates and signing settings', + :add => [], :remove => [], :list => [], :build => [], :sign => [] + + OptionParser.accept OpenSSL::X509::Certificate do |certificate| + begin + OpenSSL::X509::Certificate.new File.read certificate + rescue Errno::ENOENT + raise OptionParser::InvalidArgument, "#{certificate}: does not exist" + rescue OpenSSL::X509::CertificateError + raise OptionParser::InvalidArgument, + "#{certificate}: invalid X509 certificate" end end - add_option('-r', '--remove STRING', - 'Remove trusted certificates containing', - 'STRING.') do |value, options| - trust_dir = Gem::Security::OPT[:trust_dir] - glob_str = File::join(trust_dir, '*.pem') - - Dir::glob(glob_str) do |path| - begin - cert = OpenSSL::X509::Certificate.new(File.read(path)) - if cert.subject.to_s.downcase.index(value) - say "Removed '#{cert.subject.to_s}'" - File.unlink(path) - end - rescue OpenSSL::X509::CertificateError - next - end + OptionParser.accept OpenSSL::PKey::RSA do |key_file| + begin + key = OpenSSL::PKey::RSA.new File.read key_file + rescue Errno::ENOENT + raise OptionParser::InvalidArgument, "#{key_file}: does not exist" + rescue OpenSSL::PKey::RSAError + raise OptionParser::InvalidArgument, "#{key_file}: invalid RSA key" end + + raise OptionParser::InvalidArgument, + "#{key_file}: private key not found" unless key.private? + + key + end + + add_option('-a', '--add CERT', OpenSSL::X509::Certificate, + 'Add a trusted certificate.') do |cert, options| + options[:add] << cert + end + + add_option('-l', '--list [FILTER]', + 'List trusted certificates where the', + 'subject contains FILTER') do |filter, options| + filter ||= '' + + options[:list] << filter + end + + add_option('-r', '--remove FILTER', + 'Remove trusted certificates where the', + 'subject contains FILTER') do |filter, options| + options[:remove] << filter end add_option('-b', '--build EMAIL_ADDR', 'Build private key and self-signed', - 'certificate for EMAIL_ADDR.') do |value, options| - vals = Gem::Security.build_self_signed_cert(value) - FileUtils.chmod 0600, vals[:key_path] - say "Public Cert: #{vals[:cert_path]}" - say "Private Key: #{vals[:key_path]}" - say "Don't forget to move the key file to somewhere private..." + 'certificate for EMAIL_ADDR') do |email_address, options| + options[:build] << email_address end - add_option('-C', '--certificate CERT', - 'Certificate for --sign command.') do |value, options| - cert = OpenSSL::X509::Certificate.new(File.read(value)) + add_option('-C', '--certificate CERT', OpenSSL::X509::Certificate, + 'Signing certificate for --sign') do |cert, options| options[:issuer_cert] = cert end - add_option('-K', '--private-key KEY', - 'Private key for --sign command.') do |value, options| - key = OpenSSL::PKey::RSA.new(File.read(value)) - options[:issuer_key] = key + add_option('-K', '--private-key KEY', OpenSSL::PKey::RSA, + 'Key for --sign or --build') do |key, options| + options[:key] = key end - add_option('-s', '--sign NEWCERT', - 'Sign a certificate with my key and', - 'certificate.') do |value, options| - cert = OpenSSL::X509::Certificate.new(File.read(value)) - my_cert = options[:issuer_cert] - my_key = options[:issuer_key] - cert = Gem::Security.sign_cert(cert, my_key, my_cert) - File.open(value, 'wb') { |file| file.write(cert.to_pem) } + add_option('-s', '--sign CERT', + 'Signs CERT with the key from -K', + 'and the certificate from -C') do |cert_file, options| + raise OptionParser::InvalidArgument, "#{cert_file}: does not exist" unless + File.file? cert_file + + options[:sign] << cert_file end end def execute + options[:add].each do |certificate| + Gem::Security.trust_dir.trust_cert certificate + + say "Added '#{certificate.subject}'" + end + + options[:remove].each do |filter| + certificates_matching filter do |certificate, path| + FileUtils.rm path + say "Removed '#{certificate.subject}'" + end + end + + options[:list].each do |filter| + certificates_matching filter do |certificate, _| + # this could probably be formatted more gracefully + say certificate.subject.to_s + end + end + + options[:build].each do |name| + build name + end + + unless options[:sign].empty? then + load_default_cert unless options[:issuer_cert] + load_default_key unless options[:key] + end + + options[:sign].each do |cert_file| + sign cert_file + end + end + + def build name + key = options[:key] || Gem::Security.create_key + + cert = Gem::Security.create_cert_email name, key + + key_path = Gem::Security.write key, "gem-private_key.pem" + cert_path = Gem::Security.write cert, "gem-public_cert.pem" + + say "Certificate: #{cert_path}" + say "Private Key: #{key_path}" + say "Don't forget to move the key file to somewhere private!" + end + + def certificates_matching filter + return enum_for __method__, filter unless block_given? + + Gem::Security.trusted_certificates.select do |certificate, _| + subject = certificate.subject.to_s + subject.downcase.index filter + end.sort_by do |certificate, _| + certificate.subject.to_a.map { |name, data,| [name, data] } + end.each do |certificate, path| + yield certificate, path + end + end + + def description # :nodoc: + <<-EOF +The cert command manages signing keys and certificates for creating signed +gems. Your signing certificate and private key are typically stored in +~/.gem/gem-public_cert.pem and ~/.gem/gem-private_key.pem respectively. + +To build a certificate for signing gems: + + gem cert --build you@example + +If you already have an RSA key, or are creating a new certificate for an +existing key: + + gem cert --build you@example --private-key /path/to/key.pem + +If you wish to trust a certificate you can add it to the trust list with: + + gem cert --add /path/to/cert.pem + +You can list trusted certificates with: + + gem cert --list + +or: + + gem cert --list cert_subject_substring + +If you wish to remove a previously trusted certificate: + + gem cert --remove cert_subject_substring + +To sign another gem author's certificate: + + gem cert --sign /path/to/other_cert.pem + +For further reading on signing gems see `ri Gem::Security`. + EOF + end + + def load_default_cert + cert_file = File.join Gem.user_home, 'gem-public_cert.pem' + cert = File.read cert_file + options[:issuer_cert] = OpenSSL::X509::Certificate.new cert + rescue Errno::ENOENT + alert_error \ + "--certificate not specified and ~/.gem/gem-public_cert.pem does not exist" + + terminate_interaction 1 + rescue OpenSSL::X509::CertificateError + alert_error \ + "--certificate not specified and ~/.gem/gem-public_cert.pem is not valid" + + terminate_interaction 1 + end + + def load_default_key + key_file = File.join Gem.user_home, 'gem-private_key.pem' + key = File.read key_file + options[:key] = OpenSSL::PKey::RSA.new key + rescue Errno::ENOENT + alert_error \ + "--private-key not specified and ~/.gem/gem-private_key.pem does not exist" + + terminate_interaction 1 + rescue OpenSSL::PKey::RSAError + alert_error \ + "--private-key not specified and ~/.gem/gem-private_key.pem is not valid" + + terminate_interaction 1 + end + + def sign cert_file + cert = File.read cert_file + cert = OpenSSL::X509::Certificate.new cert + + permissions = File.stat(cert_file).mode & 0777 + + issuer_cert = options[:issuer_cert] + issuer_key = options[:key] + + cert = Gem::Security.sign cert, issuer_key, issuer_cert + + Gem::Security.write cert, cert_file, permissions end end diff --git a/lib/rubygems/commands/check_command.rb b/lib/rubygems/commands/check_command.rb index 5a1bfd4f12..4bdfcfa645 100644 --- a/lib/rubygems/commands/check_command.rb +++ b/lib/rubygems/commands/check_command.rb @@ -8,13 +8,7 @@ class Gem::Commands::CheckCommand < Gem::Command def initialize super 'check', 'Check installed gems', - :verify => false, :alien => false - - add_option( '--verify FILE', - 'Verify gem file against its internal', - 'checksum') do |value, options| - options[:verify] = value - end + :alien => true add_option('-a', '--alien', "Report 'unmanaged' or rogue files in the", "gem repository") do |value, options| @@ -25,40 +19,21 @@ class Gem::Commands::CheckCommand < Gem::Command end def execute - if options[:alien] - say "Performing the 'alien' operation" - say - gems = get_all_gem_names rescue [] - Gem::Validator.new.alien(gems).sort.each do |key, val| - unless val.empty? then - say "#{key} has #{val.size} problems" - val.each do |error_entry| - say " #{error_entry.path}:" - say " #{error_entry.problem}" - end - else - say "#{key} is error-free" if Gem.configuration.verbose + say "Checking gems..." + say + gems = get_all_gem_names rescue [] + + Gem::Validator.new.alien(gems).sort.each do |key, val| + unless val.empty? then + say "#{key} has #{val.size} problems" + val.each do |error_entry| + say " #{error_entry.path}:" + say " #{error_entry.problem}" end - say - end - end - - if options[:verify] - gem_name = options[:verify] - unless gem_name - alert_error "Must specify a .gem file with --verify NAME" - return - end - unless File.exist?(gem_name) - alert_error "Unknown file: #{gem_name}." - return - end - say "Verifying gem: '#{gem_name}'" - begin - Gem::Validator.new.verify_gem_file(gem_name) - rescue Exception - alert_error "#{gem_name} is invalid." + else + say "#{key} is error-free" if Gem.configuration.verbose end + say end end diff --git a/lib/rubygems/commands/cleanup_command.rb b/lib/rubygems/commands/cleanup_command.rb index 124c4c203a..dc919e5570 100644 --- a/lib/rubygems/commands/cleanup_command.rb +++ b/lib/rubygems/commands/cleanup_command.rb @@ -26,6 +26,9 @@ class Gem::Commands::CleanupCommand < Gem::Command <<-EOF The cleanup command removes old gems from GEM_HOME. If an older version is installed elsewhere in GEM_PATH the cleanup command won't touch it. + +Older gems that are required to satisify the dependencies of gems +are not removed. EOF end @@ -56,6 +59,8 @@ installed elsewhere in GEM_PATH the cleanup command won't touch it. primary_gems[spec.name].version != spec.version } + full = Gem::DependencyList.from_specs + deplist = Gem::DependencyList.new gems_to_cleanup.uniq.each do |spec| deplist.add spec end @@ -64,6 +69,8 @@ installed elsewhere in GEM_PATH the cleanup command won't touch it. original_path = Gem.path deps.each do |spec| + next unless full.ok_to_remove?(spec.full_name) + if options[:dryrun] then say "Dry Run Mode: Would uninstall #{spec.full_name}" else diff --git a/lib/rubygems/commands/contents_command.rb b/lib/rubygems/commands/contents_command.rb index e483484615..404c6745bd 100644 --- a/lib/rubygems/commands/contents_command.rb +++ b/lib/rubygems/commands/contents_command.rb @@ -1,3 +1,4 @@ +require 'English' require 'rubygems/command' require 'rubygems/version_option' @@ -80,19 +81,36 @@ class Gem::Commands::ContentsCommand < Gem::Command terminate_interaction 1 if gem_names.length == 1 end - gem_path = spec.full_gem_path - extra = "/{#{spec.require_paths.join ','}}" if options[:lib_only] - glob = "#{gem_path}#{extra}/**/*" - files = Dir[glob] - - gem_path = File.join gem_path, '' # add trailing / if missing - - files.sort.each do |file| - next if File.directory? file + if spec.default_gem? + files = spec.files.map do |file| + case file + when /\A#{spec.bindir}\// + [Gem::ConfigMap[:bindir], $POSTMATCH] + when /\.so\z/ + [Gem::ConfigMap[:archdir], file] + else + [Gem::ConfigMap[:rubylibdir], file] + end + end + else + gem_path = spec.full_gem_path + extra = "/{#{spec.require_paths.join ','}}" if options[:lib_only] + glob = "#{gem_path}#{extra}/**/*" + prefix_re = /#{Regexp.escape(gem_path)}\// + files = Dir[glob].map do |file| + [gem_path, file.sub(prefix_re, "")] + end + end - file = file.sub gem_path, '' unless options[:prefix] + files.sort.each do |prefix, basename| + absolute_path = File.join(prefix, basename) + next if File.directory? absolute_path - say file + if options[:prefix] + say absolute_path + else + say basename + end end end end diff --git a/lib/rubygems/commands/dependency_command.rb b/lib/rubygems/commands/dependency_command.rb index 67cbbc1d5e..4690b13a94 100644 --- a/lib/rubygems/commands/dependency_command.rb +++ b/lib/rubygems/commands/dependency_command.rb @@ -71,14 +71,9 @@ class Gem::Commands::DependencyCommand < Gem::Command if remote? and not options[:reverse_dependencies] then fetcher = Gem::SpecFetcher.fetcher - # REFACTOR: fetcher.find_specs_matching => specs - specs_and_sources = fetcher.find_matching(dependency, - dependency.specific?, true, - dependency.prerelease?) - - specs.concat specs_and_sources.map { |spec_tuple, source_uri| - fetcher.fetch_spec spec_tuple, URI.parse(source_uri) - } + ss, _ = fetcher.spec_for_dependency dependency + + ss.each { |s,o| specs << s } end if specs.empty? then diff --git a/lib/rubygems/commands/environment_command.rb b/lib/rubygems/commands/environment_command.rb index 9585c71250..40e71cf094 100644 --- a/lib/rubygems/commands/environment_command.rb +++ b/lib/rubygems/commands/environment_command.rb @@ -24,33 +24,38 @@ class Gem::Commands::EnvironmentCommand < Gem::Command The RubyGems environment can be controlled through command line arguments, gemrc files, environment variables and built-in defaults. -Command line argument defaults and some RubyGems defaults can be set in -~/.gemrc file for individual users and a /etc/gemrc for all users. A gemrc -is a YAML file with the following YAML keys: +Command line argument defaults and some RubyGems defaults can be set in a +~/.gemrc file for individual users and a /etc/gemrc for all users. These +files are YAML files with the following YAML keys: :sources: A YAML array of remote gem repositories to install gems from - :verbose: Verbosity of the gem command. false, true, and :really are the + :verbose: Verbosity of the gem command. false, true, and :really are the levels :update_sources: Enable/disable automatic updating of repository metadata :backtrace: Print backtrace when RubyGems encounters an error :gempath: The paths in which to look for gems - gem_command: A string containing arguments for the specified gem command + :disable_default_gem_server: Force specification of gem server host on push + <gem_command>: A string containing arguments for the specified gem command Example: :verbose: false install: --no-wrappers update: --no-wrappers + :disable_default_gem_server: true RubyGems' default local repository can be overridden with the GEM_PATH and -GEM_HOME environment variables. GEM_HOME sets the default repository to -install into. GEM_PATH allows multiple local repositories to be searched for +GEM_HOME environment variables. GEM_HOME sets the default repository to +install into. GEM_PATH allows multiple local repositories to be searched for gems. If you are behind a proxy server, RubyGems uses the HTTP_PROXY, HTTP_PROXY_USER and HTTP_PROXY_PASS environment variables to discover the proxy server. +If you would like to push gems to a private gem server the RUBYGEMS_HOST +environment variable can be set to the URI for that server. + If you are packaging RubyGems all of RubyGems' defaults are in lib/rubygems/defaults.rb. You may override these in lib/rubygems/defaults/operating_system.rb @@ -74,7 +79,7 @@ lib/rubygems/defaults/operating_system.rb when /^gempath/, /^path/, /^GEM_PATH/ then out << Gem.path.join(File::PATH_SEPARATOR) when /^remotesources/ then - out << Gem.sources.join("\n") + out << Gem.sources.to_a.join("\n") when /^platform/ then out << Gem.platforms.join(File::PATH_SEPARATOR) when nil then diff --git a/lib/rubygems/commands/fetch_command.rb b/lib/rubygems/commands/fetch_command.rb index e7c9cc9525..ec021359b6 100644 --- a/lib/rubygems/commands/fetch_command.rb +++ b/lib/rubygems/commands/fetch_command.rb @@ -34,7 +34,6 @@ class Gem::Commands::FetchCommand < Gem::Command def execute version = options[:version] || Gem::Requirement.default - all = Gem::Requirement.default != version platform = Gem.platforms.last gem_names = get_all_gem_names @@ -43,32 +42,20 @@ class Gem::Commands::FetchCommand < Gem::Command dep = Gem::Dependency.new gem_name, version dep.prerelease = options[:prerelease] - specs_and_sources, errors = - Gem::SpecFetcher.fetcher.fetch_with_errors(dep, all, true, - dep.prerelease?) - + specs_and_sources, errors = Gem::SpecFetcher.fetcher.spec_for_dependency dep if platform then filtered = specs_and_sources.select { |s,| s.platform == platform } specs_and_sources = filtered unless filtered.empty? end - spec, source_uri = specs_and_sources.sort_by { |s,| s.version }.last + spec, source = specs_and_sources.sort_by { |s,| s.version }.first if spec.nil? then show_lookup_failure gem_name, version, errors, options[:domain] next end - file = "#{spec.full_name}.gem" - remote_path = URI.parse(source_uri) + "gems/#{file}" - - fetch = Gem::RemoteFetcher.fetcher - - gem = fetch.fetch_path remote_path.to_s - - File.open file, "wb" do |f| - f.write gem - end + source.download spec say "Downloaded #{spec.full_name}" end diff --git a/lib/rubygems/commands/generate_index_command.rb b/lib/rubygems/commands/generate_index_command.rb index d4b4790649..a7db013caf 100644 --- a/lib/rubygems/commands/generate_index_command.rb +++ b/lib/rubygems/commands/generate_index_command.rb @@ -11,29 +11,16 @@ class Gem::Commands::GenerateIndexCommand < Gem::Command def initialize super 'generate_index', 'Generates the index files for a gem server directory', - :directory => '.', :build_legacy => true, :build_modern => true + :directory => '.', :build_modern => true add_option '-d', '--directory=DIRNAME', 'repository base dir containing gems subdir' do |dir, options| options[:directory] = File.expand_path dir end - add_option '--[no-]legacy', - 'Generate Marshal.4.8' do |value, options| - unless options[:build_modern] or value then - raise OptionParser::InvalidOption, 'no indicies will be built' - end - - options[:build_legacy] = value - end - add_option '--[no-]modern', - 'Generate indexes for RubyGems newer', - 'than 1.2.0' do |value, options| - unless options[:build_legacy] or value then - raise OptionParser::InvalidOption, 'no indicies will be built' - end - + 'Generate indexes for RubyGems', + '(always true)' do |value, options| options[:build_modern] = value end @@ -42,27 +29,10 @@ class Gem::Commands::GenerateIndexCommand < Gem::Command 'since the last update' do |value, options| options[:update] = value end - - add_option :RSS, '--rss-gems-host=GEM_HOST', - 'Host name where gems are served from,', - 'used for GUID and enclosure values' do |value, options| - options[:rss_gems_host] = value - end - - add_option :RSS, '--rss-host=HOST', - 'Host name for more gems information,', - 'used for RSS feed link' do |value, options| - options[:rss_host] = value - end - - add_option :RSS, '--rss-title=TITLE', - 'Set title for RSS feed' do |value, options| - options[:rss_title] = value - end end def defaults_str # :nodoc: - "--directory . --legacy --modern" + "--directory . --modern" end def description # :nodoc: @@ -85,25 +55,15 @@ When done, it will generate a set of files like this: prerelease_specs.<version>.gz # prerelease specs index quick/Marshal.<version>/<gemname>.gemspec.rz # Marshal quick index file - # these files support legacy RubyGems - Marshal.<version> - Marshal.<version>.Z # Marshal full index - -The .Z and .rz extension files are compressed with the inflate algorithm. +The .rz extension files are compressed with the inflate algorithm. The Marshal version number comes from ruby's Marshal::MAJOR_VERSION and Marshal::MINOR_VERSION constants. It is used to ensure compatibility. - -If --rss-host and --rss-gem-host are given an RSS feed will be generated at -index.rss containing gems released in the last two days. EOF end def execute - if options[:update] and - (options[:rss_host] or options[:rss_gems_host]) then - alert_error '--update not compatible with RSS generation' - terminate_interaction 1 - end + # This is always true becasue it's the only way now. + options[:build_modern] = true if not File.exist?(options[:directory]) or not File.directory?(options[:directory]) then diff --git a/lib/rubygems/commands/help_command.rb b/lib/rubygems/commands/help_command.rb index 20b52429b2..8e3d97edd3 100644 --- a/lib/rubygems/commands/help_command.rb +++ b/lib/rubygems/commands/help_command.rb @@ -37,7 +37,7 @@ Some examples of 'gem' usage. * Create a gem: - See http://rubygems.rubyforge.org/wiki/wiki.pl?CreateAGemInTenMinutes + See http://guides.rubygems.org/make-your-own-gem/ * See information about RubyGems: diff --git a/lib/rubygems/commands/install_command.rb b/lib/rubygems/commands/install_command.rb index 003ba8601c..883526c2a5 100644 --- a/lib/rubygems/commands/install_command.rb +++ b/lib/rubygems/commands/install_command.rb @@ -1,10 +1,11 @@ require 'rubygems/command' -require 'rubygems/doc_manager' require 'rubygems/install_update_options' require 'rubygems/dependency_installer' require 'rubygems/local_remote_options' require 'rubygems/validator' require 'rubygems/version_option' +require 'rubygems/install_message' # must come before rdoc for messaging +require 'rubygems/rdoc' ## # Gem installer command line tool @@ -13,14 +14,14 @@ require 'rubygems/version_option' class Gem::Commands::InstallCommand < Gem::Command + attr_reader :installed_specs # :nodoc: + include Gem::VersionOption include Gem::LocalRemoteOptions include Gem::InstallUpdateOptions def initialize defaults = Gem::DependencyInstaller::DEFAULT_OPTIONS.merge({ - :generate_rdoc => true, - :generate_ri => true, :format_executable => false, :version => Gem::Requirement.default, }) @@ -32,6 +33,14 @@ class Gem::Commands::InstallCommand < Gem::Command add_platform_option add_version_option add_prerelease_option "to be installed. (Only for listed gems)" + + add_option(:"Install/Update", '-g', '--file FILE', + 'Read from a gem dependencies API file and', + 'install the listed gems') do |v,o| + o[:gemdeps] = v + end + + @installed_specs = nil end def arguments # :nodoc: @@ -39,7 +48,7 @@ class Gem::Commands::InstallCommand < Gem::Command end def defaults_str # :nodoc: - "--both --version '#{Gem::Requirement.default}' --rdoc --ri --no-force\n" \ + "--both --version '#{Gem::Requirement.default}' --document --no-force\n" \ "--install-dir #{Gem.dir}" end @@ -100,31 +109,73 @@ to write the specification by hand. For example: "#{program_name} GEMNAME [GEMNAME ...] [options] -- --build-flags" end + def install_from_gemdeps(gf) + require 'rubygems/request_set' + rs = Gem::RequestSet.new + rs.load_gemdeps gf + + rs.resolve + + specs = rs.install options do |req, inst| + s = req.full_spec + + if inst + say "Installing #{s.name} (#{s.version})" + else + say "Using #{s.name} (#{s.version})" + end + end + + @installed_specs = specs + + raise Gem::SystemExitException, 0 + end + def execute - if options[:include_dependencies] then - alert "`gem install -y` is now default and will be removed" - alert "use --ignore-dependencies to install only the gems you list" + if gf = options[:gemdeps] then + install_from_gemdeps gf + return end - installed_gems = [] + @installed_specs = [] ENV.delete 'GEM_PATH' if options[:install_dir].nil? and RUBY_VERSION > '1.9' + if options[:install_dir] and options[:user_install] + alert_error "Use --install-dir or --user-install but not both" + terminate_interaction 1 + end + exit_code = 0 - get_all_gem_names.each do |gem_name| + if options[:version] != Gem::Requirement.default && + get_all_gem_names.size > 1 then + alert_error "Can't use --version w/ multiple gems. Use name:ver instead." + terminate_interaction 1 + end + + + get_all_gem_names_and_versions.each do |gem_name, gem_version| + gem_version ||= options[:version] + begin next if options[:conservative] and - not Gem::Dependency.new(gem_name, options[:version]).matching_specs.empty? + not Gem::Dependency.new(gem_name, gem_version).matching_specs.empty? inst = Gem::DependencyInstaller.new options - inst.install gem_name, options[:version] + inst.install gem_name, Gem::Requirement.create(gem_version) - inst.installed_gems.each do |spec| - say "Successfully installed #{spec.full_name}" - end + @installed_specs.push(*inst.installed_gems) - installed_gems.push(*inst.installed_gems) + next unless errs = inst.errors + + errs.each do |x| + next unless Gem::SourceFetchProblem === x + + msg = "Unable to pull data from '#{x.source.uri}': #{x.error.message}" + + alert_warning msg + end rescue Gem::InstallError => e alert_error "Error installing #{gem_name}:\n\t#{e.message}" exit_code |= 1 @@ -135,27 +186,9 @@ to write the specification by hand. For example: end end - unless installed_gems.empty? then - gems = installed_gems.length == 1 ? 'gem' : 'gems' - say "#{installed_gems.length} #{gems} installed" - - # NOTE: *All* of the RI documents must be generated first. For some - # reason, RI docs cannot be generated after any RDoc documents are - # generated. - - if options[:generate_ri] then - installed_gems.each do |gem| - Gem::DocManager.new(gem, options[:rdoc_args]).generate_ri - end - - Gem::DocManager.update_ri_cache - end - - if options[:generate_rdoc] then - installed_gems.each do |gem| - Gem::DocManager.new(gem, options[:rdoc_args]).generate_rdoc - end - end + unless @installed_specs.empty? then + gems = @installed_specs.length == 1 ? 'gem' : 'gems' + say "#{@installed_specs.length} #{gems} installed" end raise Gem::SystemExitException, exit_code diff --git a/lib/rubygems/commands/list_command.rb b/lib/rubygems/commands/list_command.rb index f3e5da9551..d9b7a9535e 100644 --- a/lib/rubygems/commands/list_command.rb +++ b/lib/rubygems/commands/list_command.rb @@ -7,8 +7,9 @@ require 'rubygems/commands/query_command' class Gem::Commands::ListCommand < Gem::Commands::QueryCommand - def initialize - super 'list', 'Display gems whose name starts with STRING' + def initialize(name = 'list', + summary = 'Display gems whose name starts with STRING') + super name, summary remove_option('--name-matches') end @@ -26,8 +27,9 @@ class Gem::Commands::ListCommand < Gem::Commands::QueryCommand end def execute - string = get_one_optional_argument || '' - options[:name] = /^#{string}/i + name = get_one_optional_argument || '' + options[:name] = /^#{name}/i + super end diff --git a/lib/rubygems/commands/lock_command.rb b/lib/rubygems/commands/lock_command.rb index a6dca320ef..6b4b25a281 100644 --- a/lib/rubygems/commands/lock_command.rb +++ b/lib/rubygems/commands/lock_command.rb @@ -30,7 +30,7 @@ generated. Example: - gemlock rails-1.0.0 > lockdown.rb + gem lock rails-1.0.0 > lockdown.rb will produce in lockdown.rb: diff --git a/lib/rubygems/commands/mirror_command.rb b/lib/rubygems/commands/mirror_command.rb new file mode 100644 index 0000000000..0f98077cbd --- /dev/null +++ b/lib/rubygems/commands/mirror_command.rb @@ -0,0 +1,17 @@ +require 'rubygems/command' + +class Gem::Commands::MirrorCommand < Gem::Command + def initialize + super('mirror', 'Mirror all gem files (requires rubygems-mirror)') + begin + Gem::Specification.find_by_name('rubygems-mirror').activate + rescue Gem::LoadError + # no-op + end + end + + def execute + alert_error "Install the rubygems-mirror gem for the mirror command" + end + +end diff --git a/lib/rubygems/commands/outdated_command.rb b/lib/rubygems/commands/outdated_command.rb index ea6b9f0abf..887faab0a2 100644 --- a/lib/rubygems/commands/outdated_command.rb +++ b/lib/rubygems/commands/outdated_command.rb @@ -19,12 +19,15 @@ class Gem::Commands::OutdatedCommand < Gem::Command Gem::Specification.outdated.sort.each do |name| local = Gem::Specification.find_all_by_name(name).max dep = Gem::Dependency.new local.name, ">= #{local.version}" - remotes = Gem::SpecFetcher.fetcher.fetch dep + remotes, _ = Gem::SpecFetcher.fetcher.spec_for_dependency dep next if remotes.empty? - remote = remotes.last.first - say "#{local.name} (#{local.version} < #{remote.version})" + remotes.sort! { |a,b| a[0].version <=> b[0].version } + + highest = remotes.last.first + + say "#{local.name} (#{local.version} < #{highest.version})" end end end diff --git a/lib/rubygems/commands/owner_command.rb b/lib/rubygems/commands/owner_command.rb index 6ebf9aa1aa..92674132e8 100644 --- a/lib/rubygems/commands/owner_command.rb +++ b/lib/rubygems/commands/owner_command.rb @@ -14,6 +14,10 @@ class Gem::Commands::OwnerCommand < Gem::Command "GEM gem to manage owners for" end + def usage # :nodoc: + "#{program_name} GEM" + end + def initialize super 'owner', description add_proxy_option @@ -63,12 +67,16 @@ class Gem::Commands::OwnerCommand < Gem::Command def manage_owners method, name, owners owners.each do |owner| - response = rubygems_api_request method, "api/v1/gems/#{name}/owners" do |request| - request.set_form_data 'email' => owner - request.add_field "Authorization", api_key + begin + response = rubygems_api_request method, "api/v1/gems/#{name}/owners" do |request| + request.set_form_data 'email' => owner + request.add_field "Authorization", api_key + end + + with_response response + rescue + # ignore end - - with_response response end end diff --git a/lib/rubygems/commands/pristine_command.rb b/lib/rubygems/commands/pristine_command.rb index e3771b7212..f7eb9014ea 100644 --- a/lib/rubygems/commands/pristine_command.rb +++ b/lib/rubygems/commands/pristine_command.rb @@ -1,5 +1,5 @@ require 'rubygems/command' -require 'rubygems/format' +require 'rubygems/package' require 'rubygems/installer' require 'rubygems/version_option' @@ -24,6 +24,11 @@ class Gem::Commands::PristineCommand < Gem::Command options[:extensions] = value end + add_option('--only-executables', + 'Only restore executables') do |value, options| + options[:only_executables] = value + end + add_version_option('restore to', 'pristine condition') end @@ -78,6 +83,11 @@ extensions. say "Restoring gems to pristine condition..." specs.each do |spec| + if spec.default_gem? + say "Skipped #{spec.full_name}, it is a default gem" + next + end + unless spec.extensions.empty? or options[:extensions] then say "Skipped #{spec.full_name}, it needs to compile an extension" next @@ -101,8 +111,13 @@ extensions. :wrappers => true, :force => true, :install_dir => spec.base_dir, - :env_shebang => installer_env_shebang) - installer.install + :env_shebang => installer_env_shebang, + :build_args => spec.build_args) + if options[:only_executables] then + installer.generate_bin + else + installer.install + end say "Restored #{spec.full_name}" end diff --git a/lib/rubygems/commands/push_command.rb b/lib/rubygems/commands/push_command.rb index a7663edf4a..0667b47dc1 100644 --- a/lib/rubygems/commands/push_command.rb +++ b/lib/rubygems/commands/push_command.rb @@ -1,6 +1,7 @@ require 'rubygems/command' require 'rubygems/local_remote_options' require 'rubygems/gemcutter_utilities' +require 'rubygems/package' class Gem::Commands::PushCommand < Gem::Command include Gem::LocalRemoteOptions @@ -39,13 +40,23 @@ class Gem::Commands::PushCommand < Gem::Command def send_gem name args = [:post, "api/v1/gems"] - args << options[:host] if options[:host] if Gem.latest_rubygems_version < Gem::Version.new(Gem::VERSION) then alert_error "Using beta/unreleased version of rubygems. Not pushing." terminate_interaction 1 end + host = options[:host] + unless host + if gem_data = Gem::Package.new(name) then + host = gem_data.spec.metadata['default_gem_server'] + end + end + + args << host if host + + say "Pushing gem to #{host || Gem.host}..." + response = rubygems_api_request(*args) do |request| request.body = Gem.read_binary name request.add_field "Content-Length", request.body.size diff --git a/lib/rubygems/commands/query_command.rb b/lib/rubygems/commands/query_command.rb index 725da8787b..b6c910d449 100644 --- a/lib/rubygems/commands/query_command.rb +++ b/lib/rubygems/commands/query_command.rb @@ -21,6 +21,10 @@ class Gem::Commands::QueryCommand < Gem::Command options[:installed] = value end + add_option('-I', 'Equivalent to --no-installed') do |value, options| + options[:installed] = false + end + add_version_option command, "for use with --installed" add_option('-n', '--name-matches REGEXP', @@ -80,6 +84,7 @@ class Gem::Commands::QueryCommand < Gem::Command req = Gem::Requirement.default # TODO: deprecate for real dep = Gem::Deprecate.skip_during { Gem::Dependency.new name, req } + dep.prerelease = prerelease if local? then if prerelease and not both? then @@ -97,7 +102,7 @@ class Gem::Commands::QueryCommand < Gem::Command } spec_tuples = specs.map do |spec| - [[spec.name, spec.version, spec.original_platform, spec], :local] + [spec.name_tuple, spec] end output_query_results spec_tuples @@ -110,13 +115,27 @@ class Gem::Commands::QueryCommand < Gem::Command say end - all = options[:all] - fetcher = Gem::SpecFetcher.fetcher - spec_tuples = fetcher.find_matching dep, all, false, prerelease - spec_tuples += fetcher.find_matching dep, false, false, true if - prerelease and all + type = if options[:all] + if options[:prerelease] + :complete + else + :released + end + elsif options[:prerelease] + :prerelease + else + :latest + end + + if options[:name].source.empty? + spec_tuples = fetcher.detect(type) { true } + else + spec_tuples = fetcher.detect(type) do |gem_name, ver, plat| + options[:name] === gem_name + end + end output_query_results spec_tuples end @@ -135,32 +154,30 @@ class Gem::Commands::QueryCommand < Gem::Command output = [] versions = Hash.new { |h,name| h[name] = [] } - spec_tuples.each do |spec_tuple, source_uri| - versions[spec_tuple.first] << [spec_tuple, source_uri] + spec_tuples.each do |spec_tuple, source| + versions[spec_tuple.name] << [spec_tuple, source] end - versions = versions.sort_by do |(name,_),_| - name.downcase + versions = versions.sort_by do |(n,_),_| + n.downcase end versions.each do |gem_name, matching_tuples| - matching_tuples = matching_tuples.sort_by do |(_, version,_),_| - version - end.reverse + matching_tuples = matching_tuples.sort_by { |n,_| n.version }.reverse platforms = Hash.new { |h,version| h[version] = [] } - matching_tuples.map do |(_, version, platform,_),_| - platforms[version] << platform if platform + matching_tuples.map do |n,_| + platforms[n.version] << n.platform if n.platform end seen = {} - matching_tuples.delete_if do |(_, version,_),_| - if seen[version] then + matching_tuples.delete_if do |n,_| + if seen[n.version] then true else - seen[version] = true + seen[n.version] = true false end end @@ -169,7 +186,7 @@ class Gem::Commands::QueryCommand < Gem::Command if options[:versions] then list = if platforms.empty? or options[:details] then - matching_tuples.map { |(_, version,_),_| version }.uniq + matching_tuples.map { |n,_| n.version }.uniq else platforms.sort.reverse.map do |version, pls| if pls == [Gem::Platform::RUBY] then @@ -188,12 +205,11 @@ class Gem::Commands::QueryCommand < Gem::Command if options[:details] then detail_tuple = matching_tuples.first - spec = if detail_tuple.first.length == 4 then - detail_tuple.first.last - else - uri = URI.parse detail_tuple.last - Gem::SpecFetcher.fetcher.fetch_spec detail_tuple.first, uri - end + spec = detail_tuple.last + + unless spec.kind_of? Gem::Specification + spec = spec.fetch_spec detail_tuple.first + end entry << "\n" @@ -243,9 +259,9 @@ class Gem::Commands::QueryCommand < Gem::Command entry << "\n" << " Installed at: #{loaded_from}" else label = 'Installed at' - matching_tuples.each do |(_,version,_,s),| + matching_tuples.each do |n,s| loaded_from = File.dirname File.dirname(s.loaded_from) - entry << "\n" << " #{label} (#{version}): #{loaded_from}" + entry << "\n" << " #{label} (#{n.version}): #{loaded_from}" label = ' ' * label.length end end diff --git a/lib/rubygems/commands/rdoc_command.rb b/lib/rubygems/commands/rdoc_command.rb index ea0f3ad592..9bb07245cd 100644 --- a/lib/rubygems/commands/rdoc_command.rb +++ b/lib/rubygems/commands/rdoc_command.rb @@ -1,6 +1,6 @@ require 'rubygems/command' require 'rubygems/version_option' -require 'rubygems/doc_manager' +require 'rubygems/rdoc' class Gem::Commands::RdocCommand < Gem::Command include Gem::VersionOption @@ -8,7 +8,7 @@ class Gem::Commands::RdocCommand < Gem::Command def initialize super 'rdoc', 'Generates RDoc for pre-installed gems', :version => Gem::Requirement.default, - :include_rdoc => true, :include_ri => true, :overwrite => false + :include_rdoc => false, :include_ri => true, :overwrite => false add_option('--all', 'Generate RDoc/RI documentation for all', @@ -39,7 +39,7 @@ class Gem::Commands::RdocCommand < Gem::Command end def defaults_str # :nodoc: - "--version '#{Gem::Requirement.default}' --rdoc --ri --no-overwrite" + "--version '#{Gem::Requirement.default}' --ri --no-overwrite" end def description # :nodoc: @@ -54,37 +54,32 @@ The rdoc command builds RDoc and RI documentation for installed gems. Use end def execute - if options[:all] then - specs = Gem::SourceIndex.from_installed_gems.collect { |name, spec| - spec - } - else - gem_name = get_one_gem_name - dep = Gem::Dependency.new gem_name, options[:version] - specs = Gem::SourceIndex.from_installed_gems.search dep + specs = if options[:all] then + Gem::Specification.to_a + else + get_all_gem_names.map do |name| + Gem::Specification.find_by_name name, options[:version] + end.flatten.uniq + end + + if specs.empty? then + alert_error 'No matching gems found' + terminate_interaction 1 end - if specs.empty? - raise "Failed to find gem #{gem_name} to generate RDoc for #{options[:version]}" - end + specs.each do |spec| + doc = Gem::RDoc.new spec, options[:include_rdoc], options[:include_ri] - if options[:include_ri] - specs.sort.each do |spec| - doc = Gem::DocManager.new(spec) - doc.generate_ri if options[:overwrite] || !doc.ri_installed? - end + doc.force = options[:overwrite] - Gem::DocManager.update_ri_cache - end - - if options[:include_rdoc] - specs.sort.each do |spec| - doc = Gem::DocManager.new(spec) - doc.generate_rdoc if options[:overwrite] || !doc.rdoc_installed? + begin + doc.generate + rescue Errno::ENOENT => e + e.message =~ / - / + alert_error "Unable to document #{spec.full_name}, #{$'} is missing, skipping" + terminate_interaction 1 if specs.length == 1 end end - - true end end diff --git a/lib/rubygems/commands/search_command.rb b/lib/rubygems/commands/search_command.rb index 52e96fd1ef..92d4b3672e 100644 --- a/lib/rubygems/commands/search_command.rb +++ b/lib/rubygems/commands/search_command.rb @@ -1,30 +1,16 @@ require 'rubygems/command' -require 'rubygems/commands/query_command' +require 'rubygems/commands/list_command' -class Gem::Commands::SearchCommand < Gem::Commands::QueryCommand +class Gem::Commands::SearchCommand < Gem::Commands::ListCommand def initialize super 'search', 'Display all gems whose name contains STRING' - remove_option '--name-matches' - end - - def arguments # :nodoc: - "STRING fragment of gem name to search for" + @defaults[:domain] = :remote end def defaults_str # :nodoc: - "--local --no-details" - end - - def usage # :nodoc: - "#{program_name} [STRING]" - end - - def execute - string = get_one_optional_argument - options[:name] = /#{string}/i - super + "--remote --no-details" end end diff --git a/lib/rubygems/commands/server_command.rb b/lib/rubygems/commands/server_command.rb index b65d48c4fc..4796ce2ad6 100644 --- a/lib/rubygems/commands/server_command.rb +++ b/lib/rubygems/commands/server_command.rb @@ -78,7 +78,7 @@ You can set up a shortcut to gem server documentation using the URL: end def execute - options[:gemdir] << Gem.dir if options[:gemdir].empty? + options[:gemdir] = Gem.path if options[:gemdir].empty? Gem::Server.run options end diff --git a/lib/rubygems/commands/setup_command.rb b/lib/rubygems/commands/setup_command.rb index 508ff84a0f..2f1cf0091d 100644 --- a/lib/rubygems/commands/setup_command.rb +++ b/lib/rubygems/commands/setup_command.rb @@ -5,14 +5,22 @@ require 'rubygems/command' # RubyGems checkout or tarball. class Gem::Commands::SetupCommand < Gem::Command + HISTORY_HEADER = /^===\s*[\d.]+\s*\/\s*\d{4}-\d{2}-\d{2}\s*$/ + VERSION_MATCHER = /^===\s*([\d.]+)\s*\/\s*\d{4}-\d{2}-\d{2}\s*$/ def initialize require 'tmpdir' super 'setup', 'Install RubyGems', - :format_executable => true, :rdoc => true, :ri => true, + :format_executable => true, :document => %w[ri], :site_or_vendor => :sitelibdir, - :destdir => '', :prefix => '' + :destdir => '', :prefix => '', :previous_version => '' + + add_option '--previous-version=VERSION', + 'Previous version of rubygems', + 'Used for changelog processing' do |version, options| + options[:previous_version] = version + end add_option '--prefix=PREFIX', 'Prefix path for installing RubyGems', @@ -37,14 +45,37 @@ class Gem::Commands::SetupCommand < Gem::Command options[:format_executable] = value end + add_option '--[no-]document [TYPES]', Array, + 'Generate documentation for RubyGems.', + 'List the documentation types you wish to', + 'generate. For example: rdoc,ri' do |value, options| + options[:document] = case value + when nil then %w[rdoc ri] + when false then [] + else value + end + end + add_option '--[no-]rdoc', 'Generate RDoc documentation for RubyGems' do |value, options| - options[:rdoc] = value + if value then + options[:document] << 'rdoc' + else + options[:document].delete 'rdoc' + end + + options[:document].uniq! end add_option '--[no-]ri', 'Generate RI documentation for RubyGems' do |value, options| - options[:ri] = value + if value then + options[:document] << 'ri' + else + options[:document].delete 'ri' + end + + options[:document].uniq! end end @@ -58,7 +89,7 @@ class Gem::Commands::SetupCommand < Gem::Command end def defaults_str # :nodoc: - "--format-executable --rdoc --ri" + "--format-executable --document ri" end def description # :nodoc: @@ -110,7 +141,7 @@ By default, this RubyGems will install gem as: uninstall_old_gemcutter - install_rdoc + documentation_success = install_rdoc say if @verbose then @@ -118,14 +149,30 @@ By default, this RubyGems will install gem as: say end + if options[:previous_version].empty? + options[:previous_version] = Gem::VERSION.sub(/[0-9]+$/, '0') + end + + options[:previous_version] = Gem::Version.new(options[:previous_version]) + release_notes = File.join Dir.pwd, 'History.txt' release_notes = if File.exist? release_notes then - open release_notes do |io| - text = io.gets '===' - text << io.gets('===') - text[0...-3].sub(/^# coding:.*?^=/m, '') + history = File.read release_notes + history = history.sub(/^# coding:.*?^=/m, '') + + text = history.split(HISTORY_HEADER) + text.shift # correct an off-by-one generated by split + version_lines = history.scan(HISTORY_HEADER) + versions = history.scan(VERSION_MATCHER).flatten.map { |x| Gem::Version.new(x) } + + history_string = "" + + until versions.length == 0 or versions.shift < options[:previous_version] + history_string += version_lines.shift + text.shift end + + history_string else "Oh-no! Unable to find release notes!" end @@ -145,6 +192,31 @@ By default, this RubyGems will install gem as: say "to remove it by hand." say end + + if documentation_success + if options[:document].include? 'rdoc' then + say "Rdoc documentation was installed. You may now invoke:" + say " gem server" + say "and then peruse beautifully formatted documentation for your gems" + say "with your web browser." + say "If you do not wish to install this documentation in the future, use the" + say "--no-document flag, or set it as the default in your ~/.gemrc file. See" + say "'gem help env' for details." + say + end + + if options[:document].include? 'ri' then + say "Ruby Interactive (ri) documentation was installed. ri is kind of like man " + say "pages for ruby libraries. You may access it like this:" + say " ri Classname" + say " ri Classname.class_method" + say " ri Classname#instance_method" + say "If you do not wish to install this documentation in the future, use the" + say "--no-document flag, or set it as the default in your ~/.gemrc file. See" + say "'gem help env' for details." + say + end + end end def install_executables(bin_dir) @@ -165,7 +237,7 @@ By default, this RubyGems will install gem as: end dest_file = File.join bin_dir, bin_file_formatted - bin_tmp_file = File.join Dir.tmpdir, bin_file + bin_tmp_file = File.join Dir.tmpdir, "#{bin_file}.#{$$}" begin bin = File.readlines bin_file @@ -209,10 +281,7 @@ TEXT say "Installing RubyGems" if @verbose Dir.chdir 'lib' do - lib_files = Dir[File.join('**', '*rb')] - - # Be sure to include our SSL ca bundles - lib_files += Dir[File.join('**', '*pem')] + lib_files = Dir[File.join('**', '*rb')] lib_files.each do |lib_file| dest_file = File.join lib_dir, lib_file @@ -229,6 +298,12 @@ TEXT rubygems_name = "rubygems-#{Gem::VERSION}" rubygems_doc_dir = File.join gem_doc_dir, rubygems_name + begin + Gem.ensure_gem_subdirectories Gem.dir + rescue SystemCallError + # ignore + end + if File.writable? gem_doc_dir and (not File.exist? rubygems_doc_dir or File.writable? rubygems_doc_dir) then @@ -237,21 +312,26 @@ TEXT rm_rf dir end - if options[:ri] then - ri_dir = File.join rubygems_doc_dir, 'ri' - say "Installing #{rubygems_name} ri into #{ri_dir}" if @verbose - run_rdoc '--ri', '--op', ri_dir - end + require 'rubygems/rdoc' - if options[:rdoc] then - rdoc_dir = File.join rubygems_doc_dir, 'rdoc' - say "Installing #{rubygems_name} rdoc into #{rdoc_dir}" if @verbose - run_rdoc '--op', rdoc_dir + fake_spec = Gem::Specification.new 'rubygems', Gem::VERSION + def fake_spec.full_gem_path + File.expand_path '../../../..', __FILE__ end + + generate_ri = options[:document].include? 'ri' + generate_rdoc = options[:document].include? 'rdoc' + + rdoc = Gem::RDoc.new fake_spec, generate_rdoc, generate_ri + rdoc.generate + + return true elsif @verbose then say "Skipping RDoc generation, #{gem_doc_dir} not writable" say "Set the GEM_HOME environment variable if you want RDoc generated" end + + return false end def make_destination_dirs(install_destdir) @@ -331,23 +411,6 @@ abort "#{deprecation_message}" end end - def run_rdoc(*args) - begin - gem 'rdoc' - rescue Gem::LoadError - end - - require 'rdoc/rdoc' - - args << '--main' << 'README.rdoc' << '--quiet' - args << '.' - args << 'README.rdoc' << 'UPGRADING.rdoc' - args << 'LICENSE.txt' << 'MIT.txt' << 'History.txt' - - r = RDoc::RDoc.new - r.document args - end - def uninstall_old_gemcutter require 'rubygems/uninstaller' diff --git a/lib/rubygems/commands/sources_command.rb b/lib/rubygems/commands/sources_command.rb index ac14313e9d..97ed7329ea 100644 --- a/lib/rubygems/commands/sources_command.rb +++ b/lib/rubygems/commands/sources_command.rb @@ -48,7 +48,7 @@ class Gem::Commands::SourcesCommand < Gem::Command options[:update]) if options[:clear_all] then - path = Gem::SpecFetcher.fetcher.dir + path = File.join Gem.user_home, '.gem', 'specs' FileUtils.rm_rf path unless File.exist? path then @@ -64,16 +64,19 @@ class Gem::Commands::SourcesCommand < Gem::Command end end - if options[:add] then - source_uri = options[:add] - uri = URI.parse source_uri + if source_uri = options[:add] then + source = Gem::Source.new source_uri begin - Gem::SpecFetcher.fetcher.load_specs uri, 'specs' - Gem.sources << source_uri - Gem.configuration.write + if Gem.sources.include? source_uri then + say "source #{source_uri} already present in the cache" + else + source.load_specs :released + Gem.sources << source + Gem.configuration.write - say "#{source_uri} added to sources" + say "#{source_uri} added to sources" + end rescue URI::Error, ArgumentError say "#{source_uri} is not a URI" terminate_interaction 1 @@ -97,12 +100,9 @@ class Gem::Commands::SourcesCommand < Gem::Command end if options[:update] then - fetcher = Gem::SpecFetcher.fetcher - - Gem.sources.each do |update_uri| - update_uri = URI.parse update_uri - fetcher.load_specs update_uri, 'specs' - fetcher.load_specs update_uri, 'latest_specs' + Gem.sources.each_source do |src| + src.load_specs :released + src.load_specs :latest end say "source cache successfully updated" @@ -112,8 +112,8 @@ class Gem::Commands::SourcesCommand < Gem::Command say "*** CURRENT SOURCES ***" say - Gem.sources.each do |source| - say source + Gem.sources.each do |src| + say src end end end diff --git a/lib/rubygems/commands/specification_command.rb b/lib/rubygems/commands/specification_command.rb index 566a9cc66e..63da5fef0f 100644 --- a/lib/rubygems/commands/specification_command.rb +++ b/lib/rubygems/commands/specification_command.rb @@ -1,7 +1,7 @@ require 'rubygems/command' require 'rubygems/local_remote_options' require 'rubygems/version_option' -require 'rubygems/format' +require 'rubygems/package' class Gem::Commands::SpecificationCommand < Gem::Command @@ -17,6 +17,7 @@ class Gem::Commands::SpecificationCommand < Gem::Command add_version_option('examine') add_platform_option + add_prerelease_option add_option('--all', 'Output specifications for all versions of', 'the gem') do |value, options| @@ -62,13 +63,13 @@ FIELD name of gemspec field to show "Please specify a gem name or file on the command line" end - case options[:version] + case v = options[:version] when String - req = Gem::Requirement.parse options[:version] + req = Gem::Requirement.create v when Gem::Requirement - req = options[:version] + req = v else - raise Gem::CommandLineError, "Unsupported version type: #{options[:version]}" + raise Gem::CommandLineError, "Unsupported version type: '#{v}'" end if !req.none? and options[:all] @@ -79,7 +80,7 @@ FIELD name of gemspec field to show if options[:all] dep = Gem::Dependency.new gem else - dep = Gem::Dependency.new gem, options[:version] + dep = Gem::Dependency.new gem, req end field = get_one_optional_argument @@ -89,7 +90,7 @@ FIELD name of gemspec field to show if local? then if File.exist? gem then - specs << Gem::Format.from_file_by_path(gem).spec rescue nil + specs << Gem::Package.new(gem).spec rescue nil end if specs.empty? then @@ -98,17 +99,14 @@ FIELD name of gemspec field to show end if remote? then - found = Gem::SpecFetcher.fetcher.fetch dep, true - - if dep.prerelease? or options[:prerelease] - found += Gem::SpecFetcher.fetcher.fetch dep, false, true, true - end + dep.prerelease = options[:prerelease] + found, _ = Gem::SpecFetcher.fetcher.spec_for_dependency dep specs.push(*found.map { |spec,| spec }) end if specs.empty? then - alert_error "Unknown gem '#{gem}'" + alert_error "No gem matching '#{dep}' found" terminate_interaction 1 end diff --git a/lib/rubygems/commands/uninstall_command.rb b/lib/rubygems/commands/uninstall_command.rb index aaadb762b5..736574ddff 100644 --- a/lib/rubygems/commands/uninstall_command.rb +++ b/lib/rubygems/commands/uninstall_command.rb @@ -13,7 +13,8 @@ class Gem::Commands::UninstallCommand < Gem::Command def initialize super 'uninstall', 'Uninstall gems from the local repository', - :version => Gem::Requirement.default, :user_install => true + :version => Gem::Requirement.default, :user_install => true, + :check_dev => false add_option('-a', '--[no-]all', 'Uninstall all matching versions' @@ -27,6 +28,12 @@ class Gem::Commands::UninstallCommand < Gem::Command options[:ignore] = value end + add_option('-D', '--[no-]-check-development', + 'Check development dependencies while uninstalling', + '(default: false)') do |value, options| + options[:check_dev] = value + end + add_option('-x', '--[no-]executables', 'Uninstall applicable executables without', 'confirmation') do |value, options| @@ -54,6 +61,12 @@ class Gem::Commands::UninstallCommand < Gem::Command options[:format_executable] = value end + add_option('--[no-]force', + 'Uninstall all versions of the named gems', + 'ignoring dependencies') do |value, options| + options[:force] = value + end + add_version_option add_platform_option end @@ -73,19 +86,23 @@ class Gem::Commands::UninstallCommand < Gem::Command end def execute - original_path = Gem.path + # REFACTOR: stolen from cleanup_command + deplist = Gem::DependencyList.new + get_all_gem_names.uniq.each do |name| + Gem::Specification.find_all_by_name(name).each do |spec| + deplist.add spec + end + end + + deps = deplist.strongly_connected_components.flatten.reverse - get_all_gem_names.each do |gem_name| + deps.map(&:name).uniq.each do |gem_name| begin Gem::Uninstaller.new(gem_name, options).uninstall - rescue Gem::InstallError => e - alert e.message rescue Gem::GemNotInHomeException => e spec = e.spec alert("In order to remove #{spec.name}, please execute:\n" \ "\tgem uninstall #{spec.name} --install-dir=#{spec.installation_path}") - ensure - Gem.use_paths(*original_path) end end end diff --git a/lib/rubygems/commands/unpack_command.rb b/lib/rubygems/commands/unpack_command.rb index 64b8ad64f8..7eefd32a6e 100644 --- a/lib/rubygems/commands/unpack_command.rb +++ b/lib/rubygems/commands/unpack_command.rb @@ -69,8 +69,10 @@ class Gem::Commands::UnpackCommand < Gem::Command else basename = File.basename path, '.gem' target_dir = File.expand_path basename, options[:target] - FileUtils.mkdir_p target_dir - Gem::Installer.new(path, :unpack => true).unpack target_dir + + package = Gem::Package.new path + package.extract_files target_dir + say "Unpacked gem: '#{target_dir}'" end end @@ -134,9 +136,11 @@ class Gem::Commands::UnpackCommand < Gem::Command ## # Extracts the Gem::Specification and raw metadata from the .gem file at # +path+. + #-- + # TODO move to Gem::Package as #raw_spec or something def get_metadata path - format = Gem::Format.from_file_by_path path + format = Gem::Package.new path spec = format.spec metadata = nil diff --git a/lib/rubygems/commands/update_command.rb b/lib/rubygems/commands/update_command.rb index d63b943c56..02f9657435 100644 --- a/lib/rubygems/commands/update_command.rb +++ b/lib/rubygems/commands/update_command.rb @@ -1,10 +1,12 @@ require 'rubygems/command' require 'rubygems/command_manager' +require 'rubygems/dependency_installer' require 'rubygems/install_update_options' require 'rubygems/local_remote_options' require 'rubygems/spec_fetcher' require 'rubygems/version_option' -require 'rubygems/commands/install_command' +require 'rubygems/install_message' # must come before rdoc for messaging +require 'rubygems/rdoc' class Gem::Commands::UpdateCommand < Gem::Command @@ -13,11 +15,9 @@ class Gem::Commands::UpdateCommand < Gem::Command include Gem::VersionOption def initialize - super 'update', - 'Update the named gems (or all installed gems) in the local repository', - :generate_rdoc => true, - :generate_ri => true, - :force => false + super 'update', 'Update installed gems to the latest version', + :document => %w[rdoc ri], + :force => false add_install_update_options @@ -37,6 +37,9 @@ class Gem::Commands::UpdateCommand < Gem::Command add_local_remote_options add_platform_option add_prerelease_option "as update targets" + + @updated = [] + @installer = Gem::DependencyInstaller.new options end def arguments # :nodoc: @@ -44,7 +47,7 @@ class Gem::Commands::UpdateCommand < Gem::Command end def defaults_str # :nodoc: - "--rdoc --ri --no-force --install-dir #{Gem.dir}" + "--document --no-force --install-dir #{Gem.dir}" end def usage # :nodoc: @@ -52,9 +55,6 @@ class Gem::Commands::UpdateCommand < Gem::Command end def execute - @installer = Gem::DependencyInstaller.new options - @updated = [] - hig = {} if options[:system] then @@ -79,21 +79,7 @@ class Gem::Commands::UpdateCommand < Gem::Command if updated.empty? then say "Nothing to update" else - say "Gems updated: #{updated.map { |spec| spec.name }.join ', '}" - - if options[:generate_ri] then - updated.each do |gem| - Gem::DocManager.new(gem, options[:rdoc_args]).generate_ri - end - - Gem::DocManager.update_ri_cache - end - - if options[:generate_rdoc] then - updated.each do |gem| - Gem::DocManager.new(gem, options[:rdoc_args]).generate_rdoc - end - end + say "Gems updated: #{updated.map { |spec| spec.name }.join ' '}" end end @@ -112,7 +98,6 @@ class Gem::Commands::UpdateCommand < Gem::Command @installer.installed_gems.each do |spec| @updated << spec - say "Successfully installed #{spec.full_name}" if success end end @@ -178,8 +163,9 @@ class Gem::Commands::UpdateCommand < Gem::Command args = [] args << '--prefix' << Gem.prefix if Gem.prefix - args << '--no-rdoc' unless options[:generate_rdoc] - args << '--no-ri' unless options[:generate_ri] + # TODO use --document for >= 1.9 , --no-rdoc --no-ri < 1.9 + args << '--no-rdoc' unless options[:document].include? 'rdoc' + args << '--no-ri' unless options[:document].include? 'ri' args << '--no-format-executable' if options[:no_format_executable] update_dir = File.join Gem.dir, 'gems', "rubygems-update-#{version}" @@ -205,20 +191,20 @@ class Gem::Commands::UpdateCommand < Gem::Command gem_names.all? { |name| /#{name}/ !~ l_spec.name } dependency = Gem::Dependency.new l_spec.name, "> #{l_spec.version}" + dependency.prerelease = options[:prerelease] fetcher = Gem::SpecFetcher.fetcher - spec_tuples = fetcher.find_matching dependency - matching_gems = spec_tuples.select do |(name, _, platform),| - name == l_name and Gem::Platform.match platform + spec_tuples, _ = fetcher.search_for_dependency dependency + + matching_gems = spec_tuples.select do |g,_| + g.name == l_name and g.match_platform? end - highest_remote_gem = matching_gems.sort_by do |(_, version),| - version - end.last + highest_remote_gem = matching_gems.sort_by { |g,_| g.version }.last - highest_remote_gem ||= [[nil, Gem::Version.new(0), nil]] # "null" object - highest_remote_ver = highest_remote_gem.first[1] + highest_remote_gem ||= [Gem::NameTuple.null] + highest_remote_ver = highest_remote_gem.first.version if system or (l_spec.version < highest_remote_ver) then result << [l_spec.name, [l_spec.version, highest_remote_ver].max] diff --git a/lib/rubygems/commands/yank_command.rb b/lib/rubygems/commands/yank_command.rb new file mode 100644 index 0000000000..da0cf7ad3b --- /dev/null +++ b/lib/rubygems/commands/yank_command.rb @@ -0,0 +1,98 @@ +require 'rubygems/command' +require 'rubygems/local_remote_options' +require 'rubygems/version_option' +require 'rubygems/gemcutter_utilities' + +class Gem::Commands::YankCommand < Gem::Command + include Gem::LocalRemoteOptions + include Gem::VersionOption + include Gem::GemcutterUtilities + + def description # :nodoc: + 'Remove a specific gem version release from RubyGems.org' + end + + def arguments # :nodoc: + "GEM name of gem" + end + + def usage # :nodoc: + "#{program_name} GEM -v VERSION [-p PLATFORM] [--undo] [--key KEY_NAME]" + end + + def initialize + super 'yank', description + + add_version_option("remove") + add_platform_option("remove") + + add_option('--undo') do |value, options| + options[:undo] = true + end + + add_option('-k', '--key KEY_NAME', + 'Use API key from your gem credentials file') do |value, options| + options[:key] = value + end + end + + def execute + sign_in + + version = get_version_from_requirements(options[:version]) + platform = get_platform_from_requirements(options) + api_key = Gem.configuration.rubygems_api_key + api_key = Gem.configuration.api_keys[options[:key].to_sym] if options[:key] + + if version then + if options[:undo] then + unyank_gem(version, platform, api_key) + else + yank_gem(version, platform, api_key) + end + else + say "A version argument is required: #{usage}" + terminate_interaction + end + end + + def yank_gem(version, platform, api_key) + say "Yanking gem from #{self.host}..." + yank_api_request(:delete, version, platform, "api/v1/gems/yank", api_key) + end + + def unyank_gem(version, platform, api_key) + say "Unyanking gem from #{host}..." + yank_api_request(:put, version, platform, "api/v1/gems/unyank", api_key) + end + + private + + def yank_api_request(method, version, platform, api, api_key) + name = get_one_gem_name + response = rubygems_api_request(method, api) do |request| + request.add_field("Authorization", api_key) + + data = { + 'gem_name' => name, + 'version' => version, + } + data['platform'] = platform if platform + + request.set_form_data data + end + say response.body + end + + def get_version_from_requirements(requirements) + requirements.requirements.first[1].version + rescue + nil + end + + def get_platform_from_requirements(requirements) + Gem.platforms[1].to_s if requirements.key? :added_platform + end + +end + |