diff options
Diffstat (limited to 'lib/rubygems/commands')
| -rw-r--r-- | lib/rubygems/commands/build_command.rb | 7 | ||||
| -rw-r--r-- | lib/rubygems/commands/cert_command.rb | 2 | ||||
| -rw-r--r-- | lib/rubygems/commands/environment_command.rb | 1 | ||||
| -rw-r--r-- | lib/rubygems/commands/exec_command.rb | 24 | ||||
| -rw-r--r-- | lib/rubygems/commands/fetch_command.rb | 2 | ||||
| -rw-r--r-- | lib/rubygems/commands/help_command.rb | 4 | ||||
| -rw-r--r-- | lib/rubygems/commands/install_command.rb | 11 | ||||
| -rw-r--r-- | lib/rubygems/commands/owner_command.rb | 5 | ||||
| -rw-r--r-- | lib/rubygems/commands/pristine_command.rb | 30 | ||||
| -rw-r--r-- | lib/rubygems/commands/push_command.rb | 89 | ||||
| -rw-r--r-- | lib/rubygems/commands/query_command.rb | 43 | ||||
| -rw-r--r-- | lib/rubygems/commands/rebuild_command.rb | 1 | ||||
| -rw-r--r-- | lib/rubygems/commands/setup_command.rb | 14 | ||||
| -rw-r--r-- | lib/rubygems/commands/sources_command.rb | 167 | ||||
| -rw-r--r-- | lib/rubygems/commands/specification_command.rb | 8 | ||||
| -rw-r--r-- | lib/rubygems/commands/uninstall_command.rb | 2 |
16 files changed, 278 insertions, 132 deletions
diff --git a/lib/rubygems/commands/build_command.rb b/lib/rubygems/commands/build_command.rb index 2ec8324141..cfe1f8ec3c 100644 --- a/lib/rubygems/commands/build_command.rb +++ b/lib/rubygems/commands/build_command.rb @@ -25,13 +25,6 @@ class Gem::Commands::BuildCommand < Gem::Command add_option "-o", "--output FILE", "output gem with the given filename" do |value, options| options[:output] = value end - - add_option "-C PATH", "Run as if gem build was started in <PATH> instead of the current working directory." do |value, options| - options[:build_path] = value - end - deprecate_option "-C", - version: "4.0", - extra_msg: "-C is a global flag now. Use `gem -C PATH build GEMSPEC_FILE [options]` instead" end def arguments # :nodoc: diff --git a/lib/rubygems/commands/cert_command.rb b/lib/rubygems/commands/cert_command.rb index 72dcf1dd17..fe03841ddb 100644 --- a/lib/rubygems/commands/cert_command.rb +++ b/lib/rubygems/commands/cert_command.rb @@ -158,7 +158,7 @@ class Gem::Commands::CertCommand < Gem::Command cert = Gem::Security.create_cert_email( email, key, - (Gem::Security::ONE_DAY * expiration_length_days) + Gem::Security::ONE_DAY * expiration_length_days ) Gem::Security.write cert, "gem-public_cert.pem" diff --git a/lib/rubygems/commands/environment_command.rb b/lib/rubygems/commands/environment_command.rb index aea8c0d7de..a5eb521a53 100644 --- a/lib/rubygems/commands/environment_command.rb +++ b/lib/rubygems/commands/environment_command.rb @@ -38,6 +38,7 @@ keys: :verbose: Verbosity of the gem command. false, true, and :really are the levels :update_sources: Enable/disable automatic updating of repository metadata + :concurrent_downloads: The number of gem downloads to perform concurrently :backtrace: Print backtrace when RubyGems encounters an error :gempath: The paths in which to look for gems :disable_default_gem_server: Force specification of gem server host on push diff --git a/lib/rubygems/commands/exec_command.rb b/lib/rubygems/commands/exec_command.rb index 519539c694..1feafbdd35 100644 --- a/lib/rubygems/commands/exec_command.rb +++ b/lib/rubygems/commands/exec_command.rb @@ -173,6 +173,9 @@ to the same gem path as user-installed gems. rescue Gem::InstallError => e alert_error "Error installing #{gem_name}:\n\t#{e.message}" terminate_interaction 1 + rescue Gem::DependencyResolutionError => e + alert_error "Error installing #{gem_name}:\n\t#{e.message}" + terminate_interaction 2 rescue Gem::GemNotFoundException => e show_lookup_failure e.name, e.version, e.errors, false @@ -195,7 +198,7 @@ to the same gem path as user-installed gems. argv = ARGV.clone ARGV.replace options[:args] - exe = executable = options[:executable] + executable = options[:executable] contains_executable = Gem.loaded_specs.values.select do |spec| spec.executables.include?(executable) @@ -206,13 +209,22 @@ to the same gem path as user-installed gems. end if contains_executable.empty? - if (spec = Gem.loaded_specs[executable]) && (exe = spec.executable) - contains_executable << spec - else + spec = Gem.loaded_specs[executable] + + if spec.nil? || spec.executables.empty? alert_error "Failed to load executable `#{executable}`," \ " are you sure the gem `#{options[:gem_name]}` contains it?" terminate_interaction 1 end + + if spec.executables.size > 1 + alert_error "Ambiguous which executable from gem `#{executable}` should be run: " \ + "the options are #{spec.executables.sort}, specify one via COMMAND, and use `-g` and `-v` to specify gem and version" + terminate_interaction 1 + end + + contains_executable << spec + executable = spec.executable end if contains_executable.size > 1 @@ -223,8 +235,8 @@ to the same gem path as user-installed gems. end old_exe = $0 - $0 = exe - load Gem.activate_bin_path(contains_executable.first.name, exe, ">= 0.a") + $0 = executable + load Gem.activate_bin_path(contains_executable.first.name, executable, ">= 0.a") ensure $0 = old_exe if old_exe ARGV.replace argv diff --git a/lib/rubygems/commands/fetch_command.rb b/lib/rubygems/commands/fetch_command.rb index c524cf7922..8e64a18cee 100644 --- a/lib/rubygems/commands/fetch_command.rb +++ b/lib/rubygems/commands/fetch_command.rb @@ -56,7 +56,7 @@ then repackaging it. if options[:version] != Gem::Requirement.default && get_all_gem_names.size > 1 alert_error "Can't use --version with multiple gems. You can specify multiple gems with" \ - " version requirements using `gem fetch 'my_gem:1.0.0' 'my_other_gem:~>2.0.0'`" + " version requirements using `gem fetch 'my_gem:1.0.0' 'my_other_gem:>=2'`" terminate_interaction 1 end end diff --git a/lib/rubygems/commands/help_command.rb b/lib/rubygems/commands/help_command.rb index 1619b152e7..664f400561 100644 --- a/lib/rubygems/commands/help_command.rb +++ b/lib/rubygems/commands/help_command.rb @@ -90,7 +90,9 @@ Use #gem to declare which gems you directly depend upon: To depend on a specific set of versions: - gem 'rake', '~> 10.3', '>= 10.3.2' + gem 'rake', '>= 10.3.2' + # or for multiple version restrictions + gem 'rake', '>= 10.3.2', "< 13" RubyGems will require the gem name when activating the gem using the RUBYGEMS_GEMDEPS environment variable or Gem::use_gemdeps. Use the diff --git a/lib/rubygems/commands/install_command.rb b/lib/rubygems/commands/install_command.rb index 2888b6c55a..6d3beec0b4 100644 --- a/lib/rubygems/commands/install_command.rb +++ b/lib/rubygems/commands/install_command.rb @@ -140,7 +140,7 @@ You can use `i` command instead of `install`. if options[:version] != Gem::Requirement.default && get_all_gem_names.size > 1 alert_error "Can't use --version with multiple gems. You can specify multiple gems with" \ - " version requirements using `gem install 'my_gem:1.0.0' 'my_other_gem:~>2.0.0'`" + " version requirements using `gem install 'my_gem:1.0.0' 'my_other_gem:>=2'`" terminate_interaction 1 end end @@ -224,6 +224,9 @@ You can use `i` command instead of `install`. rescue Gem::InstallError => e alert_error "Error installing #{gem_name}:\n\t#{e.message}" exit_code |= 1 + rescue Gem::DependencyResolutionError => e + alert_error "Error installing #{gem_name}:\n\t#{e.message}" + exit_code |= 2 rescue Gem::UnsatisfiableDependencyError => e show_lookup_failure e.name, e.version, e.errors, suppress_suggestions, "'#{gem_name}' (#{gem_version})" @@ -239,11 +242,7 @@ You can use `i` command instead of `install`. # Loads post-install hooks def load_hooks # :nodoc: - if options[:install_as_default] - require_relative "../install_default_message" - else - require_relative "../install_message" - end + require_relative "../install_message" require_relative "../rdoc" end diff --git a/lib/rubygems/commands/owner_command.rb b/lib/rubygems/commands/owner_command.rb index 12bfe3a834..675e866734 100644 --- a/lib/rubygems/commands/owner_command.rb +++ b/lib/rubygems/commands/owner_command.rb @@ -75,11 +75,12 @@ permission to. end with_response response do |resp| - owners = Gem::SafeYAML.load clean_text(resp.body) + owners = Gem::SafeYAML.safe_load clean_text(resp.body) say "Owners for gem: #{name}" owners.each do |owner| - say "- #{owner["email"] || owner["handle"] || owner["id"]}" + identifier = owner["email"] || owner["handle"] || owner["id"] + say "- #{identifier} (#{owner["role"]})" end end end diff --git a/lib/rubygems/commands/pristine_command.rb b/lib/rubygems/commands/pristine_command.rb index 97f1646ba0..10978c2af7 100644 --- a/lib/rubygems/commands/pristine_command.rb +++ b/lib/rubygems/commands/pristine_command.rb @@ -88,6 +88,10 @@ If you have made modifications to an installed gem, the pristine command will revert them. All extensions are rebuilt and all bin stubs for the gem are regenerated after checking for modifications. +Rebuilding extensions also refreshes C-extension gems against updated system +libraries (for example after OS or package upgrades) to avoid mismatches like +outdated library version warnings. + If the cached gem cannot be found it will be downloaded. If --no-extensions is provided pristine will not attempt to restore a gem @@ -128,6 +132,11 @@ extensions will be restored. specs = specs.select {|spec| spec.platform == RUBY_ENGINE || Gem::Platform.local === spec.platform || spec.platform == Gem::Platform::RUBY } if specs.to_a.empty? + if options[:only_missing_extensions] + say "No gems with missing extensions to restore" + return + end + raise Gem::Exception, "Failed to find gems #{options[:args]} #{options[:version]}" end @@ -137,11 +146,14 @@ extensions will be restored. specs.group_by(&:full_name_with_location).values.each do |grouped_specs| spec = grouped_specs.find {|s| !s.default_gem? } || grouped_specs.first - unless only_executables_or_plugins? + only_executables = options[:only_executables] + only_plugins = options[:only_plugins] + + unless only_executables || only_plugins # Default gemspecs include changes provided by ruby-core installer that # can't currently be pristined (inclusion of compiled extension targets in # the file list). So stick to resetting executables if it's a default gem. - options[:only_executables] = true if spec.default_gem? + only_executables = true if spec.default_gem? end if options.key? :skip @@ -151,14 +163,14 @@ extensions will be restored. end end - unless spec.extensions.empty? || options[:extensions] || only_executables_or_plugins? + unless spec.extensions.empty? || options[:extensions] || only_executables || only_plugins say "Skipped #{spec.full_name_with_location}, it needs to compile an extension" next end gem = spec.cache_file - unless File.exist?(gem) || only_executables_or_plugins? + unless File.exist?(gem) || only_executables || only_plugins require_relative "../remote_fetcher" say "Cached gem for #{spec.full_name_with_location} not found, attempting to fetch..." @@ -194,10 +206,10 @@ extensions will be restored. bin_dir: bin_dir, } - if options[:only_executables] + if only_executables installer = Gem::Installer.for_spec(spec, installer_options) installer.generate_bin - elsif options[:only_plugins] + elsif only_plugins installer = Gem::Installer.for_spec(spec, installer_options) installer.generate_plugins else @@ -208,10 +220,4 @@ extensions will be restored. say "Restored #{spec.full_name_with_location}" end end - - private - - def only_executables_or_plugins? - options[:only_executables] || options[:only_plugins] - end end diff --git a/lib/rubygems/commands/push_command.rb b/lib/rubygems/commands/push_command.rb index 726191377a..02931b3025 100644 --- a/lib/rubygems/commands/push_command.rb +++ b/lib/rubygems/commands/push_command.rb @@ -92,20 +92,77 @@ The push command will use ~/.gem/credentials to authenticate to a server, but yo private def send_push_request(name, args) - rubygems_api_request(*args, scope: get_push_scope) do |request| + # Always honor explicit --attestation option + # Auto-attestation is only supported on rubygems.org with GitHub Actions (not JRuby) + if options[:attestations].any? || (RUBY_ENGINE != "jruby" && attestation_supported_host? && ENV["GITHUB_ACTIONS"]) + send_push_request_with_attestation(name, args) + else + send_push_request_without_attestation(name, args) + end + end + + def send_push_request_without_attestation(name, args) + scope = get_push_scope + rubygems_api_request(*args, scope: scope) do |request| body = Gem.read_binary name - if options[:attestations].any? - request.set_form([ - ["gem", body, { filename: name, content_type: "application/octet-stream" }], - get_attestations_part, - ], "multipart/form-data") - else - request.body = body - request.add_field "Content-Type", "application/octet-stream" - request.add_field "Content-Length", request.body.size + request.body = body + request.add_field "Content-Type", "application/octet-stream" + request.add_field "Content-Length", request.body.size + request.add_field "Authorization", api_key + end + end + + def send_push_request_with_attestation(name, args) + attestations = if options[:attestations].any? + options[:attestations].map do |attestation| + Gem.read_binary(attestation) end + else + bundle_path = attest!(name) + begin + [Gem.read_binary(bundle_path)] + ensure + File.unlink(bundle_path) if bundle_path && File.exist?(bundle_path) + end + end + bundles = "[" + attestations.join(",") + "]" + + rubygems_api_request(*args, scope: get_push_scope) do |request| + request.set_form([ + ["gem", Gem.read_binary(name), { filename: name, content_type: "application/octet-stream" }], + ["attestations", bundles, { content_type: "application/json" }], + ], "multipart/form-data") request.add_field "Authorization", api_key end + rescue StandardError => e + message = "Failed to push with attestation, retrying without attestation.\n" + message += if Gem.configuration.really_verbose + e.full_message + else + e.message + end + alert_warning message + send_push_request_without_attestation(name, args) + end + + def attest!(name) + require "open3" + require "tempfile" + + tempfile = Tempfile.new([File.basename(name, ".*"), ".sigstore.json"]) + bundle = tempfile.path + tempfile.close(false) + + env = defined?(Bundler.unbundled_env) ? Bundler.unbundled_env : ENV.to_h + out, st = Open3.capture2e( + env, + Gem.ruby, "-S", "gem", "exec", "--conservative", + "sigstore-cli", "sign", name, "--bundle", bundle, + unsetenv_others: true + ) + raise Gem::Exception, "Failed to sign gem:\n\n#{out}" unless st.success? + + bundle end def get_hosts_for(name) @@ -121,14 +178,8 @@ The push command will use ~/.gem/credentials to authenticate to a server, but yo :push_rubygem end - def get_attestations_part - bundles = "[" + options[:attestations].map do |attestation| - Gem.read_binary(attestation) - end.join(",") + "]" - [ - "attestations", - bundles, - { content_type: "application/json" }, - ] + def attestation_supported_host? + host = (@host || Gem.host).to_s.chomp("/") + host == Gem::DEFAULT_HOST end end diff --git a/lib/rubygems/commands/query_command.rb b/lib/rubygems/commands/query_command.rb deleted file mode 100644 index 3b527974a3..0000000000 --- a/lib/rubygems/commands/query_command.rb +++ /dev/null @@ -1,43 +0,0 @@ -# frozen_string_literal: true - -require_relative "../command" -require_relative "../query_utils" -require_relative "../deprecate" - -class Gem::Commands::QueryCommand < Gem::Command - extend Gem::Deprecate - rubygems_deprecate_command - - include Gem::QueryUtils - - alias_method :warning_without_suggested_alternatives, :deprecation_warning - def deprecation_warning - warning_without_suggested_alternatives - - message = "It is recommended that you use `gem search` or `gem list` instead.\n" - alert_warning message unless Gem::Deprecate.skip - end - - def initialize(name = "query", summary = "Query gem information in local or remote repositories") - super name, summary, - domain: :local, details: false, versions: true, - installed: nil, version: Gem::Requirement.default - - add_option("-n", "--name-matches REGEXP", - "Name of gem(s) to query on matches the", - "provided REGEXP") do |value, options| - options[:name] = /#{value}/i - end - - add_query_options - end - - def description # :nodoc: - <<-EOF -The query command is the basis for the list and search commands. - -You should really use the list and search commands instead. This command -is too hard to use. - EOF - end -end diff --git a/lib/rubygems/commands/rebuild_command.rb b/lib/rubygems/commands/rebuild_command.rb index 77a474ef1d..23b9d7b3ba 100644 --- a/lib/rubygems/commands/rebuild_command.rb +++ b/lib/rubygems/commands/rebuild_command.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -require "date" require "digest" require "fileutils" require "tmpdir" diff --git a/lib/rubygems/commands/setup_command.rb b/lib/rubygems/commands/setup_command.rb index 301ce25314..175599967c 100644 --- a/lib/rubygems/commands/setup_command.rb +++ b/lib/rubygems/commands/setup_command.rb @@ -7,8 +7,8 @@ require_relative "../command" # RubyGems checkout or tarball. class Gem::Commands::SetupCommand < Gem::Command - HISTORY_HEADER = %r{^#\s*[\d.a-zA-Z]+\s*/\s*\d{4}-\d{2}-\d{2}\s*$} - VERSION_MATCHER = %r{^#\s*([\d.a-zA-Z]+)\s*/\s*\d{4}-\d{2}-\d{2}\s*$} + HISTORY_HEADER = %r{^##\s*[\d.a-zA-Z]+\s*/\s*\d{4}-\d{2}-\d{2}\s*$} + VERSION_MATCHER = %r{^##\s*([\d.a-zA-Z]+)\s*/\s*\d{4}-\d{2}-\d{2}\s*$} ENV_PATHS = %w[/usr/bin/env /bin/env].freeze @@ -393,16 +393,20 @@ By default, this RubyGems will install gem as: Dir.chdir("bundler") do built_gem = Gem::Package.build(new_bundler_spec) begin - Gem::Installer.at( + installer = Gem::Installer.at( built_gem, env_shebang: options[:env_shebang], format_executable: options[:format_executable], force: options[:force], - install_as_default: true, bin_dir: bin_dir, install_dir: default_dir, wrappers: true - ).install + ) + # We need to install only executable and default spec files. + # lib/bundler.rb and lib/bundler/* are available under the site_ruby directory. + installer.extract_bin + installer.generate_bin + installer.write_default_spec ensure FileUtils.rm_f built_gem end diff --git a/lib/rubygems/commands/sources_command.rb b/lib/rubygems/commands/sources_command.rb index 976f4a4ea2..b399af2bd3 100644 --- a/lib/rubygems/commands/sources_command.rb +++ b/lib/rubygems/commands/sources_command.rb @@ -18,6 +18,14 @@ class Gem::Commands::SourcesCommand < Gem::Command options[:add] = value end + add_option "--append SOURCE_URI", "Append source (can be used multiple times)" do |value, options| + options[:append] = value + end + + add_option "-p", "--prepend SOURCE_URI", "Prepend source (can be used multiple times)" do |value, options| + options[:prepend] = value + end + add_option "-l", "--list", "List sources" do |value, options| options[:list] = value end @@ -26,8 +34,7 @@ class Gem::Commands::SourcesCommand < Gem::Command options[:remove] = value end - add_option "-c", "--clear-all", - "Remove all sources (clear the cache)" do |value, options| + add_option "-c", "--clear-all", "Remove all sources (clear the cache)" do |value, options| options[:clear_all] = value end @@ -43,11 +50,8 @@ class Gem::Commands::SourcesCommand < Gem::Command end def add_source(source_uri) # :nodoc: - check_rubygems_https source_uri - - source = Gem::Source.new source_uri - - check_typo_squatting(source) + source = build_new_source(source_uri) + source_uri = source.uri.to_s begin if Gem.sources.include? source @@ -68,6 +72,54 @@ class Gem::Commands::SourcesCommand < Gem::Command end end + def append_source(source_uri) # :nodoc: + source = build_new_source(source_uri) + source_uri = source.uri.to_s + + begin + source.load_specs :released + was_present = Gem.sources.include?(source) + Gem.sources.append source + Gem.configuration.write + + if was_present + say "#{source_uri} moved to end of sources" + else + say "#{source_uri} added to sources" + end + rescue Gem::URI::Error, ArgumentError + say "#{source_uri} is not a URI" + terminate_interaction 1 + rescue Gem::RemoteFetcher::FetchError => e + say "Error fetching #{Gem::Uri.redact(source.uri)}:\n\t#{e.message}" + terminate_interaction 1 + end + end + + def prepend_source(source_uri) # :nodoc: + source = build_new_source(source_uri) + source_uri = source.uri.to_s + + begin + source.load_specs :released + was_present = Gem.sources.include?(source) + Gem.sources.prepend source + Gem.configuration.write + + if was_present + say "#{source_uri} moved to top of sources" + else + say "#{source_uri} added to sources" + end + rescue Gem::URI::Error, ArgumentError + say "#{source_uri} is not a URI" + terminate_interaction 1 + rescue Gem::RemoteFetcher::FetchError => e + say "Error fetching #{Gem::Uri.redact(source.uri)}:\n\t#{e.message}" + terminate_interaction 1 + end + end + def check_typo_squatting(source) if source.typo_squatting?("rubygems.org") question = <<-QUESTION.chomp @@ -80,6 +132,19 @@ Do you want to add this source? end end + def normalize_source_uri(source_uri) # :nodoc: + # Ensure the source URI has a trailing slash for proper RFC 2396 path merging + # Without a trailing slash, the last path segment is treated as a file and removed + # during relative path resolution (e.g., "/blish" + "gems/foo.gem" = "/gems/foo.gem") + # With a trailing slash, it's treated as a directory (e.g., "/blish/" + "gems/foo.gem" = "/blish/gems/foo.gem") + uri = Gem::URI.parse(source_uri) + uri.path = uri.path.gsub(%r{/+\z}, "") + "/" if uri.path && !uri.path.empty? + uri.to_s + rescue Gem::URI::Error + # If parsing fails, return the original URI and let later validation handle it + source_uri + end + def check_rubygems_https(source_uri) # :nodoc: uri = Gem::URI source_uri @@ -128,7 +193,7 @@ yourself to use your own gem server. Without any arguments the sources lists your currently configured sources: $ gem sources - *** CURRENT SOURCES *** + *** NO CONFIGURED SOURCES, DEFAULT SOURCES LISTED BELOW *** https://rubygems.org @@ -147,33 +212,49 @@ Since all of these sources point to the same set of gems you only need one of them in your list. https://rubygems.org is recommended as it brings the protections of an SSL connection to gem downloads. -To add a source use the --add argument: +To add a private gem source use the --prepend argument to insert it before +the default source. This is usually the best place for private gem sources: - $ gem sources --add https://rubygems.org - https://rubygems.org added to sources + $ gem sources --prepend https://my.private.source + https://my.private.source added to sources RubyGems will check to see if gems can be installed from the source given before it is added. +To add or move a source after all other sources, use --append: + + $ gem sources --append https://rubygems.org + https://rubygems.org moved to end of sources + To remove a source use the --remove argument: - $ gem sources --remove https://rubygems.org/ - https://rubygems.org/ removed from sources + $ gem sources --remove https://my.private.source/ + https://my.private.source/ removed from sources EOF end def list # :nodoc: - say "*** CURRENT SOURCES ***" + if configured_sources + header = "*** CURRENT SOURCES ***" + list = configured_sources + else + header = "*** NO CONFIGURED SOURCES, DEFAULT SOURCES LISTED BELOW ***" + list = Gem.sources + end + + say header say - Gem.sources.each do |src| + list.each do |src| say src end end def list? # :nodoc: !(options[:add] || + options[:prepend] || + options[:append] || options[:clear_all] || options[:remove] || options[:update]) @@ -182,11 +263,13 @@ To remove a source use the --remove argument: def execute clear_all if options[:clear_all] - source_uri = options[:add] - add_source source_uri if source_uri + add_source options[:add] if options[:add] + + prepend_source options[:prepend] if options[:prepend] + + append_source options[:append] if options[:append] - source_uri = options[:remove] - remove_source source_uri if source_uri + remove_source options[:remove] if options[:remove] update if options[:update] @@ -194,13 +277,22 @@ To remove a source use the --remove argument: end def remove_source(source_uri) # :nodoc: - if Gem.sources.include? source_uri - Gem.sources.delete source_uri + source = build_source(source_uri) + source_uri = source.uri.to_s + + if configured_sources&.include? source + Gem.sources.delete source Gem.configuration.write - say "#{source_uri} removed from sources" + if default_sources.include?(source) && configured_sources.one? + alert_warning "Removing a default source when it is the only source has no effect. Add a different source to #{config_file_name} if you want to stop using it as a source." + else + say "#{source_uri} removed from sources" + end + elsif configured_sources + say "source #{source_uri} cannot be removed because it's not present in #{config_file_name}" else - say "source #{source_uri} not present in cache" + say "source #{source_uri} cannot be removed because there are no configured sources in #{config_file_name}" end end @@ -224,4 +316,33 @@ To remove a source use the --remove argument: say "*** Unable to remove #{desc} source cache ***" end end + + private + + def default_sources + Gem::SourceList.from(Gem.default_sources) + end + + def configured_sources + return @configured_sources if defined?(@configured_sources) + + configuration_sources = Gem.configuration.sources + @configured_sources = Gem::SourceList.from(configuration_sources) if configuration_sources + end + + def config_file_name + Gem.configuration.config_file_name + end + + def build_source(source_uri) + source_uri = normalize_source_uri(source_uri) + Gem::Source.new(source_uri) + end + + def build_new_source(source_uri) + source = build_source(source_uri) + check_rubygems_https(source.uri.to_s) + check_typo_squatting(source) + source + end end diff --git a/lib/rubygems/commands/specification_command.rb b/lib/rubygems/commands/specification_command.rb index a21ed35be3..15e543f1a6 100644 --- a/lib/rubygems/commands/specification_command.rb +++ b/lib/rubygems/commands/specification_command.rb @@ -42,7 +42,7 @@ class Gem::Commands::SpecificationCommand < Gem::Command def arguments # :nodoc: <<-ARGS -GEMFILE name of gem to show the gemspec for +GEM_OR_FILE gem name or a .gem file to show the gemspec for FIELD name of gemspec field to show ARGS end @@ -68,7 +68,7 @@ Specific fields in the specification can be extracted in YAML format: end def usage # :nodoc: - "#{program_name} [GEMFILE] [FIELD]" + "#{program_name} [GEM_OR_FILE] [FIELD]" end def execute @@ -77,7 +77,7 @@ Specific fields in the specification can be extracted in YAML format: unless gem raise Gem::CommandLineError, - "Please specify a gem name or file on the command line" + "Please specify a gem name or a .gem file on the command line" end case v = options[:version] @@ -147,7 +147,7 @@ Specific fields in the specification can be extracted in YAML format: say case options[:format] when :ruby then s.to_ruby when :marshal then Marshal.dump s - else s.to_yaml + else Gem.use_psych? ? s.to_yaml : Gem::YAMLSerializer.dump(s) end say "\n" diff --git a/lib/rubygems/commands/uninstall_command.rb b/lib/rubygems/commands/uninstall_command.rb index 3d6e41e49e..3c26074f93 100644 --- a/lib/rubygems/commands/uninstall_command.rb +++ b/lib/rubygems/commands/uninstall_command.rb @@ -117,7 +117,7 @@ that is a dependency of an existing gem. You can use the if options[:version] != Gem::Requirement.default && get_all_gem_names.size > 1 alert_error "Can't use --version with multiple gems. You can specify multiple gems with" \ - " version requirements using `gem uninstall 'my_gem:1.0.0' 'my_other_gem:~>2.0.0'`" + " version requirements using `gem uninstall 'my_gem:1.0.0' 'my_other_gem:>=2'`" terminate_interaction 1 end end |
