summaryrefslogtreecommitdiff
path: root/lib/rubygems/commands
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rubygems/commands')
-rw-r--r--lib/rubygems/commands/build_command.rb13
-rw-r--r--lib/rubygems/commands/cert_command.rb2
-rw-r--r--lib/rubygems/commands/check_command.rb2
-rw-r--r--lib/rubygems/commands/cleanup_command.rb8
-rw-r--r--lib/rubygems/commands/contents_command.rb4
-rw-r--r--lib/rubygems/commands/dependency_command.rb2
-rw-r--r--lib/rubygems/commands/fetch_command.rb4
-rw-r--r--lib/rubygems/commands/generate_index_command.rb113
-rw-r--r--lib/rubygems/commands/help_command.rb6
-rw-r--r--lib/rubygems/commands/info_command.rb4
-rw-r--r--lib/rubygems/commands/install_command.rb20
-rw-r--r--lib/rubygems/commands/list_command.rb4
-rw-r--r--lib/rubygems/commands/lock_command.rb2
-rw-r--r--lib/rubygems/commands/open_command.rb4
-rw-r--r--lib/rubygems/commands/owner_command.rb2
-rw-r--r--lib/rubygems/commands/pristine_command.rb43
-rw-r--r--lib/rubygems/commands/push_command.rb2
-rw-r--r--lib/rubygems/commands/query_command.rb4
-rw-r--r--lib/rubygems/commands/rdoc_command.rb13
-rw-r--r--lib/rubygems/commands/rebuild_command.rb262
-rw-r--r--lib/rubygems/commands/search_command.rb4
-rw-r--r--lib/rubygems/commands/setup_command.rb61
-rw-r--r--lib/rubygems/commands/sources_command.rb4
-rw-r--r--lib/rubygems/commands/specification_command.rb4
-rw-r--r--lib/rubygems/commands/uninstall_command.rb14
-rw-r--r--lib/rubygems/commands/unpack_command.rb10
-rw-r--r--lib/rubygems/commands/update_command.rb33
-rw-r--r--lib/rubygems/commands/which_command.rb2
28 files changed, 418 insertions, 228 deletions
diff --git a/lib/rubygems/commands/build_command.rb b/lib/rubygems/commands/build_command.rb
index 0ebdec565b..2ec8324141 100644
--- a/lib/rubygems/commands/build_command.rb
+++ b/lib/rubygems/commands/build_command.rb
@@ -1,11 +1,13 @@
# frozen_string_literal: true
require_relative "../command"
+require_relative "../gemspec_helpers"
require_relative "../package"
require_relative "../version_option"
class Gem::Commands::BuildCommand < Gem::Command
include Gem::VersionOption
+ include Gem::GemspecHelpers
def initialize
super "build", "Build a gem from a gemspec"
@@ -75,17 +77,6 @@ Gems can be saved to a specified filename with the output option:
private
- def find_gemspec(glob = "*.gemspec")
- gemspecs = Dir.glob(glob).sort
-
- if gemspecs.size > 1
- alert_error "Multiple gemspecs found: #{gemspecs}, please specify one"
- terminate_interaction(1)
- end
-
- gemspecs.first
- end
-
def build_gem
gemspec = resolve_gem_name
diff --git a/lib/rubygems/commands/cert_command.rb b/lib/rubygems/commands/cert_command.rb
index 22864a9b29..72dcf1dd17 100644
--- a/lib/rubygems/commands/cert_command.rb
+++ b/lib/rubygems/commands/cert_command.rb
@@ -6,7 +6,7 @@ require_relative "../security"
class Gem::Commands::CertCommand < Gem::Command
def initialize
super "cert", "Manage RubyGems certificates and signing settings",
- :add => [], :remove => [], :list => [], :build => [], :sign => []
+ add: [], remove: [], list: [], build: [], sign: []
add_option("-a", "--add CERT",
"Add a trusted certificate.") do |cert_file, options|
diff --git a/lib/rubygems/commands/check_command.rb b/lib/rubygems/commands/check_command.rb
index 6552258552..fb23dd9cb4 100644
--- a/lib/rubygems/commands/check_command.rb
+++ b/lib/rubygems/commands/check_command.rb
@@ -10,7 +10,7 @@ class Gem::Commands::CheckCommand < Gem::Command
def initialize
super "check", "Check a gem repository for added or missing files",
- :alien => true, :doctor => false, :dry_run => false, :gems => true
+ alien: true, doctor: false, dry_run: false, gems: true
add_option("-a", "--[no-]alien",
'Report "unmanaged" or rogue files in the',
diff --git a/lib/rubygems/commands/cleanup_command.rb b/lib/rubygems/commands/cleanup_command.rb
index 26beba48df..08fb598cea 100644
--- a/lib/rubygems/commands/cleanup_command.rb
+++ b/lib/rubygems/commands/cleanup_command.rb
@@ -8,8 +8,8 @@ class Gem::Commands::CleanupCommand < Gem::Command
def initialize
super "cleanup",
"Clean up old versions of installed gems",
- :force => false, :install_dir => Gem.dir,
- :check_dev => true
+ force: false, install_dir: Gem.dir,
+ check_dev: true
add_option("-n", "-d", "--dry-run",
"Do not uninstall gems") do |_value, options|
@@ -166,8 +166,8 @@ If no gems are named all gems in GEM_HOME are cleaned.
say "Attempting to uninstall #{spec.full_name}"
uninstall_options = {
- :executables => false,
- :version => "= #{spec.version}",
+ executables: false,
+ version: "= #{spec.version}",
}
uninstall_options[:user_install] = Gem.user_dir == spec.base_dir
diff --git a/lib/rubygems/commands/contents_command.rb b/lib/rubygems/commands/contents_command.rb
index f3783468aa..807158d9c9 100644
--- a/lib/rubygems/commands/contents_command.rb
+++ b/lib/rubygems/commands/contents_command.rb
@@ -8,8 +8,8 @@ class Gem::Commands::ContentsCommand < Gem::Command
def initialize
super "contents", "Display the contents of the installed gems",
- :specdirs => [], :lib_only => false, :prefix => true,
- :show_install_dir => false
+ specdirs: [], lib_only: false, prefix: true,
+ show_install_dir: false
add_version_option
diff --git a/lib/rubygems/commands/dependency_command.rb b/lib/rubygems/commands/dependency_command.rb
index 109bf03aff..9aaefae999 100644
--- a/lib/rubygems/commands/dependency_command.rb
+++ b/lib/rubygems/commands/dependency_command.rb
@@ -11,7 +11,7 @@ class Gem::Commands::DependencyCommand < Gem::Command
def initialize
super "dependency",
"Show the dependencies of an installed gem",
- :version => Gem::Requirement.default, :domain => :local
+ version: Gem::Requirement.default, domain: :local
add_version_option
add_platform_option
diff --git a/lib/rubygems/commands/fetch_command.rb b/lib/rubygems/commands/fetch_command.rb
index 950d4fe30e..f7f5b62306 100644
--- a/lib/rubygems/commands/fetch_command.rb
+++ b/lib/rubygems/commands/fetch_command.rb
@@ -10,8 +10,8 @@ class Gem::Commands::FetchCommand < Gem::Command
def initialize
defaults = {
- :suggest_alternate => true,
- :version => Gem::Requirement.default,
+ suggest_alternate: true,
+ version: Gem::Requirement.default,
}
super "fetch", "Download a gem and place it in the current directory", defaults
diff --git a/lib/rubygems/commands/generate_index_command.rb b/lib/rubygems/commands/generate_index_command.rb
index ce580cfaf9..13be92593b 100644
--- a/lib/rubygems/commands/generate_index_command.rb
+++ b/lib/rubygems/commands/generate_index_command.rb
@@ -1,86 +1,51 @@
# frozen_string_literal: true
require_relative "../command"
-require_relative "../indexer"
-##
-# Generates a index files for use as a gem server.
-#
-# See `gem help generate_index`
-
-class Gem::Commands::GenerateIndexCommand < Gem::Command
- def initialize
- super "generate_index",
- "Generates the index files for a gem server directory",
- :directory => ".", :build_modern => true
+unless defined? Gem::Commands::GenerateIndexCommand
+ class Gem::Commands::GenerateIndexCommand < Gem::Command
+ module RubygemsTrampoline
+ def description # :nodoc:
+ <<~EOF
+ The generate_index command has been moved to the rubygems-generate_index gem.
+ EOF
+ end
- add_option "-d", "--directory=DIRNAME",
- "repository base dir containing gems subdir" do |dir, options|
- options[:directory] = File.expand_path dir
- end
+ def execute
+ alert_error "Install the rubygems-generate_index gem for the generate_index command"
+ end
- add_option "--[no-]modern",
- "Generate indexes for RubyGems",
- "(always true)" do |value, options|
- options[:build_modern] = value
+ def invoke_with_build_args(args, build_args)
+ name = "rubygems-generate_index"
+ spec = begin
+ Gem::Specification.find_by_name(name)
+ rescue Gem::LoadError
+ require "rubygems/dependency_installer"
+ Gem.install(name, Gem::Requirement.default, Gem::DependencyInstaller::DEFAULT_OPTIONS).find {|s| s.name == name }
+ end
+
+ # remove the methods defined in this file so that the methods defined in the gem are used instead,
+ # and without a method redefinition warning
+ %w[description execute invoke_with_build_args].each do |method|
+ RubygemsTrampoline.remove_method(method)
+ end
+ self.class.singleton_class.remove_method(:new)
+
+ spec.activate
+ Gem.load_plugin_files spec.matches_for_glob("rubygems_plugin#{Gem.suffix_pattern}")
+
+ self.class.new.invoke_with_build_args(args, build_args)
+ end
end
+ private_constant :RubygemsTrampoline
- deprecate_option("--modern", version: "4.0", extra_msg: "Modern indexes (specs, latest_specs, and prerelease_specs) are always generated, so this option is not needed.")
- deprecate_option("--no-modern", version: "4.0", extra_msg: "The `--no-modern` option is currently ignored. Modern indexes (specs, latest_specs, and prerelease_specs) are always generated.")
-
- add_option "--update",
- "Update modern indexes with gems added",
- "since the last update" do |value, options|
- options[:update] = value
+ # remove_method(:initialize) warns, but removing new does not warn
+ def self.new
+ command = allocate
+ command.send(:initialize, "generate_index", "Generates the index files for a gem server directory (requires rubygems-generate_index)")
+ command
end
- end
-
- def defaults_str # :nodoc:
- "--directory . --modern"
- end
-
- def description # :nodoc:
- <<-EOF
-The generate_index command creates a set of indexes for serving gems
-statically. The command expects a 'gems' directory under the path given to
-the --directory option. The given directory will be the directory you serve
-as the gem repository.
-For `gem generate_index --directory /path/to/repo`, expose /path/to/repo via
-your HTTP server configuration (not /path/to/repo/gems).
-
-When done, it will generate a set of files like this:
-
- gems/*.gem # .gem files you want to
- # index
-
- specs.<version>.gz # specs index
- latest_specs.<version>.gz # latest specs index
- prerelease_specs.<version>.gz # prerelease specs index
- quick/Marshal.<version>/<gemname>.gemspec.rz # Marshal quick index file
-
-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.
- EOF
- end
-
- def execute
- # This is always true because it's the only way now.
- options[:build_modern] = true
-
- if !File.exist?(options[:directory]) ||
- !File.directory?(options[:directory])
- alert_error "unknown directory name #{options[:directory]}."
- terminate_interaction 1
- else
- indexer = Gem::Indexer.new options.delete(:directory), options
-
- if options[:update]
- indexer.update_index
- else
- indexer.generate_index
- end
- end
+ prepend(RubygemsTrampoline)
end
end
diff --git a/lib/rubygems/commands/help_command.rb b/lib/rubygems/commands/help_command.rb
index 043e7d3691..1619b152e7 100644
--- a/lib/rubygems/commands/help_command.rb
+++ b/lib/rubygems/commands/help_command.rb
@@ -59,7 +59,7 @@ multiple environments. The RubyGems implementation is designed to be
compatible with Bundler's Gemfile format. You can see additional
documentation on the format at:
- http://bundler.io
+ https://bundler.io
RubyGems automatically looks for these gem dependencies files:
@@ -172,7 +172,7 @@ and #platforms methods:
See the bundler Gemfile manual page for a list of platforms supported in a gem
dependencies file.:
- http://bundler.io/v1.6/man/gemfile.5.html
+ https://bundler.io/v2.5/man/gemfile.5.html
Ruby Version and Engine Dependency
==================================
@@ -333,7 +333,7 @@ platform.
@command_manager.command_names.each do |cmd_name|
command = @command_manager[cmd_name]
- next if command.deprecated?
+ next if command&.deprecated?
summary =
if command
diff --git a/lib/rubygems/commands/info_command.rb b/lib/rubygems/commands/info_command.rb
index ced7751ff5..f65c639662 100644
--- a/lib/rubygems/commands/info_command.rb
+++ b/lib/rubygems/commands/info_command.rb
@@ -8,8 +8,8 @@ class Gem::Commands::InfoCommand < Gem::Command
def initialize
super "info", "Show information for the given gem",
- :name => //, :domain => :local, :details => false, :versions => true,
- :installed => nil, :version => Gem::Requirement.default
+ name: //, domain: :local, details: false, versions: true,
+ installed: nil, version: Gem::Requirement.default
add_query_options
diff --git a/lib/rubygems/commands/install_command.rb b/lib/rubygems/commands/install_command.rb
index b8dfc90111..2091634a29 100644
--- a/lib/rubygems/commands/install_command.rb
+++ b/lib/rubygems/commands/install_command.rb
@@ -23,11 +23,11 @@ class Gem::Commands::InstallCommand < Gem::Command
def initialize
defaults = Gem::DependencyInstaller::DEFAULT_OPTIONS.merge({
- :format_executable => false,
- :lock => true,
- :suggest_alternate => true,
- :version => Gem::Requirement.default,
- :without_groups => [],
+ format_executable: false,
+ lock: true,
+ suggest_alternate: true,
+ version: Gem::Requirement.default,
+ without_groups: [],
})
defaults.merge!(install_update_options)
@@ -136,13 +136,6 @@ You can use `i` command instead of `install`.
"#{program_name} [options] GEMNAME [GEMNAME ...] -- --build-flags"
end
- def check_install_dir # :nodoc:
- if options[:install_dir] && options[:user_install]
- alert_error "Use --install-dir or --user-install but not both"
- terminate_interaction 1
- end
- end
-
def check_version # :nodoc:
if options[:version] != Gem::Requirement.default &&
get_all_gem_names.size > 1
@@ -162,7 +155,6 @@ You can use `i` command instead of `install`.
ENV.delete "GEM_PATH" if options[:install_dir].nil?
- check_install_dir
check_version
load_hooks
@@ -171,7 +163,7 @@ You can use `i` command instead of `install`.
show_installed
- say update_suggestion if eglible_for_update?
+ say update_suggestion if eligible_for_update?
terminate_interaction exit_code
end
diff --git a/lib/rubygems/commands/list_command.rb b/lib/rubygems/commands/list_command.rb
index 522c771f90..fab4b73814 100644
--- a/lib/rubygems/commands/list_command.rb
+++ b/lib/rubygems/commands/list_command.rb
@@ -11,8 +11,8 @@ class Gem::Commands::ListCommand < Gem::Command
def initialize
super "list", "Display local gems whose name matches REGEXP",
- :domain => :local, :details => false, :versions => true,
- :installed => nil, :version => Gem::Requirement.default
+ domain: :local, details: false, versions: true,
+ installed: nil, version: Gem::Requirement.default
add_query_options
end
diff --git a/lib/rubygems/commands/lock_command.rb b/lib/rubygems/commands/lock_command.rb
index 3a9512fe3f..f7fd5ada16 100644
--- a/lib/rubygems/commands/lock_command.rb
+++ b/lib/rubygems/commands/lock_command.rb
@@ -5,7 +5,7 @@ require_relative "../command"
class Gem::Commands::LockCommand < Gem::Command
def initialize
super "lock", "Generate a lockdown list of gems",
- :strict => false
+ strict: false
add_option "-s", "--[no-]strict",
"fail if unable to satisfy a dependency" do |strict, options|
diff --git a/lib/rubygems/commands/open_command.rb b/lib/rubygems/commands/open_command.rb
index 5a13074a1d..0fe90dc8b8 100644
--- a/lib/rubygems/commands/open_command.rb
+++ b/lib/rubygems/commands/open_command.rb
@@ -70,9 +70,7 @@ class Gem::Commands::OpenCommand < Gem::Command
end
def open_editor(path)
- Dir.chdir(path) do
- system(*@editor.split(/\s+/) + [path])
- end
+ system(*@editor.split(/\s+/) + [path], { chdir: path })
end
def spec_for(name)
diff --git a/lib/rubygems/commands/owner_command.rb b/lib/rubygems/commands/owner_command.rb
index fce32aca3e..12bfe3a834 100644
--- a/lib/rubygems/commands/owner_command.rb
+++ b/lib/rubygems/commands/owner_command.rb
@@ -39,7 +39,7 @@ permission to.
add_proxy_option
add_key_option
add_otp_option
- defaults.merge! :add => [], :remove => []
+ defaults.merge! add: [], remove: []
add_option "-a", "--add NEW_OWNER", "Add an owner by user identifier" do |value, options|
options[:add] << value
diff --git a/lib/rubygems/commands/pristine_command.rb b/lib/rubygems/commands/pristine_command.rb
index fff59855f5..999c9fef0f 100644
--- a/lib/rubygems/commands/pristine_command.rb
+++ b/lib/rubygems/commands/pristine_command.rb
@@ -11,10 +11,10 @@ class Gem::Commands::PristineCommand < Gem::Command
def initialize
super "pristine",
"Restores installed gems to pristine condition from files located in the gem cache",
- :version => Gem::Requirement.default,
- :extensions => true,
- :extensions_set => false,
- :all => false
+ version: Gem::Requirement.default,
+ extensions: true,
+ extensions_set: false,
+ all: false
add_option("--all",
"Restore all installed gems to pristine",
@@ -57,7 +57,7 @@ class Gem::Commands::PristineCommand < Gem::Command
end
add_option("-i", "--install-dir DIR",
- "Gem repository to get binstubs and plugins installed") do |value, options|
+ "Gem repository to get gems restored") do |value, options|
options[:install_dir] = File.expand_path(value)
end
@@ -103,25 +103,29 @@ extensions will be restored.
end
def execute
+ install_dir = options[:install_dir]
+
+ specification_record = install_dir ? Gem::SpecificationRecord.from_path(install_dir) : Gem::Specification.specification_record
+
specs = if options[:all]
- Gem::Specification.map
+ specification_record.map
# `--extensions` must be explicitly given to pristine only gems
# with extensions.
elsif options[:extensions_set] &&
options[:extensions] && options[:args].empty?
- Gem::Specification.select do |spec|
+ specification_record.select do |spec|
spec.extensions && !spec.extensions.empty?
end
elsif options[:only_missing_extensions]
- Gem::Specification.select(&:missing_extensions?)
+ specification_record.select(&:missing_extensions?)
else
get_all_gem_names.sort.map do |gem_name|
- Gem::Specification.find_all_by_name(gem_name, options[:version]).reverse
+ specification_record.find_all_by_name(gem_name, options[:version]).reverse
end.flatten
end
- specs = specs.select {|spec| RUBY_ENGINE == spec.platform || Gem::Platform.local === spec.platform || spec.platform == Gem::Platform::RUBY }
+ specs = specs.select {|spec| spec.platform == RUBY_ENGINE || Gem::Platform.local === spec.platform || spec.platform == Gem::Platform::RUBY }
if specs.to_a.empty?
raise Gem::Exception,
@@ -144,7 +148,7 @@ extensions will be restored.
end
unless spec.extensions.empty? || options[:extensions] || options[:only_executables] || options[:only_plugins]
- say "Skipped #{spec.full_name}, it needs to compile an extension"
+ say "Skipped #{spec.full_name_with_location}, it needs to compile an extension"
next
end
@@ -153,7 +157,7 @@ extensions will be restored.
unless File.exist?(gem) || options[:only_executables] || options[:only_plugins]
require_relative "../remote_fetcher"
- say "Cached gem for #{spec.full_name} not found, attempting to fetch..."
+ say "Cached gem for #{spec.full_name_with_location} not found, attempting to fetch..."
dep = Gem::Dependency.new spec.name, spec.version
found, _ = Gem::SpecFetcher.fetcher.spec_for_dependency dep
@@ -176,15 +180,14 @@ extensions will be restored.
end
bin_dir = options[:bin_dir] if options[:bin_dir]
- install_dir = options[:install_dir] if options[:install_dir]
installer_options = {
- :wrappers => true,
- :force => true,
- :install_dir => install_dir || spec.base_dir,
- :env_shebang => env_shebang,
- :build_args => spec.build_args,
- :bin_dir => bin_dir,
+ wrappers: true,
+ force: true,
+ install_dir: install_dir || spec.base_dir,
+ env_shebang: env_shebang,
+ build_args: spec.build_args,
+ bin_dir: bin_dir,
}
if options[:only_executables]
@@ -198,7 +201,7 @@ extensions will be restored.
installer.install
end
- say "Restored #{spec.full_name}"
+ say "Restored #{spec.full_name_with_location}"
end
end
end
diff --git a/lib/rubygems/commands/push_command.rb b/lib/rubygems/commands/push_command.rb
index 418b46acc5..591ddc3a80 100644
--- a/lib/rubygems/commands/push_command.rb
+++ b/lib/rubygems/commands/push_command.rb
@@ -30,7 +30,7 @@ The push command will use ~/.gem/credentials to authenticate to a server, but yo
end
def initialize
- super "push", "Push a gem up to the gem server", :host => host
+ super "push", "Push a gem up to the gem server", host: host
@user_defined_host = false
diff --git a/lib/rubygems/commands/query_command.rb b/lib/rubygems/commands/query_command.rb
index e7d21f341b..3b527974a3 100644
--- a/lib/rubygems/commands/query_command.rb
+++ b/lib/rubygems/commands/query_command.rb
@@ -20,8 +20,8 @@ class Gem::Commands::QueryCommand < Gem::Command
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
+ 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",
diff --git a/lib/rubygems/commands/rdoc_command.rb b/lib/rubygems/commands/rdoc_command.rb
index e318a52914..977c90b8c4 100644
--- a/lib/rubygems/commands/rdoc_command.rb
+++ b/lib/rubygems/commands/rdoc_command.rb
@@ -10,8 +10,8 @@ class Gem::Commands::RdocCommand < Gem::Command
def initialize
super "rdoc", "Generates RDoc for pre-installed gems",
- :version => Gem::Requirement.default,
- :include_rdoc => false, :include_ri => true, :overwrite => false
+ version: Gem::Requirement.default,
+ include_rdoc: false, include_ri: true, overwrite: false
add_option("--all",
"Generate RDoc/RI documentation for all",
@@ -84,14 +84,7 @@ Use --overwrite to force rebuilding of documentation.
FileUtils.rm_rf File.join(spec.doc_dir, "rdoc")
end
- begin
- doc.generate
- rescue Errno::ENOENT => e
- match = / - /.match(e.message)
- alert_error "Unable to document #{spec.full_name}, " \
- " #{match.post_match} is missing, skipping"
- terminate_interaction 1 if specs.length == 1
- end
+ doc.generate
end
end
end
diff --git a/lib/rubygems/commands/rebuild_command.rb b/lib/rubygems/commands/rebuild_command.rb
new file mode 100644
index 0000000000..77a474ef1d
--- /dev/null
+++ b/lib/rubygems/commands/rebuild_command.rb
@@ -0,0 +1,262 @@
+# frozen_string_literal: true
+
+require "date"
+require "digest"
+require "fileutils"
+require "tmpdir"
+require_relative "../gemspec_helpers"
+require_relative "../package"
+
+class Gem::Commands::RebuildCommand < Gem::Command
+ include Gem::GemspecHelpers
+
+ def initialize
+ super "rebuild", "Attempt to reproduce a build of a gem."
+
+ add_option "--diff", "If the files don't match, compare them using diffoscope." do |_value, options|
+ options[:diff] = true
+ end
+
+ add_option "--force", "Skip validation of the spec." do |_value, options|
+ options[:force] = true
+ end
+
+ add_option "--strict", "Consider warnings as errors when validating the spec." do |_value, options|
+ options[:strict] = true
+ end
+
+ add_option "--source GEM_SOURCE", "Specify the source to download the gem from." do |value, options|
+ options[:source] = value
+ end
+
+ add_option "--original GEM_FILE", "Specify a local file to compare against (instead of downloading it)." do |value, options|
+ options[:original_gem_file] = value
+ end
+
+ add_option "--gemspec GEMSPEC_FILE", "Specify the name of the gemspec file." do |value, options|
+ options[:gemspec_file] = 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
+ end
+
+ def arguments # :nodoc:
+ "GEM_NAME gem name on gem server\n" \
+ "GEM_VERSION gem version you are attempting to rebuild"
+ end
+
+ def description # :nodoc:
+ <<-EOF
+The rebuild command allows you to (attempt to) reproduce a build of a gem
+from a ruby gemspec.
+
+This command assumes the gemspec can be built with the `gem build` command.
+If you use any of `gem build`, `rake build`, or`rake release` in the
+build/release process for a gem, it is a potential candidate.
+
+You will need to match the RubyGems version used, since this is included in
+the Gem metadata.
+
+If the gem includes lockfiles (e.g. Gemfile.lock) and similar, it will
+require more effort to reproduce a build. For example, it might require
+more precisely matched versions of Ruby and/or Bundler to be used.
+ EOF
+ end
+
+ def usage # :nodoc:
+ "#{program_name} GEM_NAME GEM_VERSION"
+ end
+
+ def execute
+ gem_name, gem_version = get_gem_name_and_version
+
+ old_dir, new_dir = prep_dirs
+
+ gem_filename = "#{gem_name}-#{gem_version}.gem"
+ old_file = File.join(old_dir, gem_filename)
+ new_file = File.join(new_dir, gem_filename)
+
+ if options[:original_gem_file]
+ FileUtils.copy_file(options[:original_gem_file], old_file)
+ else
+ download_gem(gem_name, gem_version, old_file)
+ end
+
+ rg_version = rubygems_version(old_file)
+ unless rg_version == Gem::VERSION
+ alert_error <<-EOF
+You need to use the same RubyGems version #{gem_name} v#{gem_version} was built with.
+
+#{gem_name} v#{gem_version} was built using RubyGems v#{rg_version}.
+Gem files include the version of RubyGems used to build them.
+This means in order to reproduce #{gem_filename}, you must also use RubyGems v#{rg_version}.
+
+You're using RubyGems v#{Gem::VERSION}.
+
+Please install RubyGems v#{rg_version} and try again.
+ EOF
+ terminate_interaction 1
+ end
+
+ source_date_epoch = get_timestamp(old_file).to_s
+
+ if build_path = options[:build_path]
+ Dir.chdir(build_path) { build_gem(gem_name, source_date_epoch, new_file) }
+ else
+ build_gem(gem_name, source_date_epoch, new_file)
+ end
+
+ compare(source_date_epoch, old_file, new_file)
+ end
+
+ private
+
+ def sha256(file)
+ Digest::SHA256.hexdigest(Gem.read_binary(file))
+ end
+
+ def get_timestamp(file)
+ mtime = nil
+ File.open(file, Gem.binary_mode) do |f|
+ Gem::Package::TarReader.new(f) do |tar|
+ mtime = tar.seek("metadata.gz") {|tf| tf.header.mtime }
+ end
+ end
+
+ mtime
+ end
+
+ def compare(source_date_epoch, old_file, new_file)
+ date = Time.at(source_date_epoch.to_i).strftime("%F %T %Z")
+
+ old_hash = sha256(old_file)
+ new_hash = sha256(new_file)
+
+ say
+ say "Built at: #{date} (#{source_date_epoch})"
+ say "Original build saved to: #{old_file}"
+ say "Reproduced build saved to: #{new_file}"
+ say "Working directory: #{options[:build_path] || Dir.pwd}"
+ say
+ say "Hash comparison:"
+ say " #{old_hash}\t#{old_file}"
+ say " #{new_hash}\t#{new_file}"
+ say
+
+ if old_hash == new_hash
+ say "SUCCESS - original and rebuild hashes matched"
+ else
+ say "FAILURE - original and rebuild hashes did not match"
+ say
+
+ if options[:diff]
+ if system("diffoscope", old_file, new_file).nil?
+ alert_error "error: could not find `diffoscope` executable"
+ end
+ else
+ say "Pass --diff for more details (requires diffoscope to be installed)."
+ end
+
+ terminate_interaction 1
+ end
+ end
+
+ def prep_dirs
+ rebuild_dir = Dir.mktmpdir("gem_rebuild")
+ old_dir = File.join(rebuild_dir, "old")
+ new_dir = File.join(rebuild_dir, "new")
+
+ FileUtils.mkdir_p(old_dir)
+ FileUtils.mkdir_p(new_dir)
+
+ [old_dir, new_dir]
+ end
+
+ def get_gem_name_and_version
+ args = options[:args] || []
+ if args.length == 2
+ gem_name, gem_version = args
+ elsif args.length > 2
+ raise Gem::CommandLineError, "Too many arguments"
+ else
+ raise Gem::CommandLineError, "Expected GEM_NAME and GEM_VERSION arguments (gem rebuild GEM_NAME GEM_VERSION)"
+ end
+
+ [gem_name, gem_version]
+ end
+
+ def build_gem(gem_name, source_date_epoch, output_file)
+ gemspec = options[:gemspec_file] || find_gemspec("#{gem_name}.gemspec")
+
+ if gemspec
+ build_package(gemspec, source_date_epoch, output_file)
+ else
+ alert_error error_message(gem_name)
+ terminate_interaction(1)
+ end
+ end
+
+ def build_package(gemspec, source_date_epoch, output_file)
+ with_source_date_epoch(source_date_epoch) do
+ spec = Gem::Specification.load(gemspec)
+ if spec
+ Gem::Package.build(
+ spec,
+ options[:force],
+ options[:strict],
+ output_file
+ )
+ else
+ alert_error "Error loading gemspec. Aborting."
+ terminate_interaction 1
+ end
+ end
+ end
+
+ def with_source_date_epoch(source_date_epoch)
+ old_sde = ENV["SOURCE_DATE_EPOCH"]
+ ENV["SOURCE_DATE_EPOCH"] = source_date_epoch.to_s
+
+ yield
+ ensure
+ ENV["SOURCE_DATE_EPOCH"] = old_sde
+ end
+
+ def error_message(gem_name)
+ if gem_name
+ "Couldn't find a gemspec file matching '#{gem_name}' in #{Dir.pwd}"
+ else
+ "Couldn't find a gemspec file in #{Dir.pwd}"
+ end
+ end
+
+ def download_gem(gem_name, gem_version, old_file)
+ # This code was based loosely off the `gem fetch` command.
+ version = "= #{gem_version}"
+ dep = Gem::Dependency.new gem_name, version
+
+ specs_and_sources, errors =
+ Gem::SpecFetcher.fetcher.spec_for_dependency dep
+
+ # There should never be more than one item in specs_and_sources,
+ # since we search for an exact version.
+ spec, source = specs_and_sources[0]
+
+ if spec.nil?
+ show_lookup_failure gem_name, version, errors, options[:domain]
+ terminate_interaction 1
+ end
+
+ download_path = source.download spec
+
+ FileUtils.move(download_path, old_file)
+
+ say "Downloaded #{gem_name} version #{gem_version} as #{old_file}."
+ end
+
+ def rubygems_version(gem_file)
+ Gem::Package.new(gem_file).spec.rubygems_version
+ end
+end
diff --git a/lib/rubygems/commands/search_command.rb b/lib/rubygems/commands/search_command.rb
index c7469e1fa8..50e161ac9b 100644
--- a/lib/rubygems/commands/search_command.rb
+++ b/lib/rubygems/commands/search_command.rb
@@ -8,8 +8,8 @@ class Gem::Commands::SearchCommand < Gem::Command
def initialize
super "search", "Display remote gems whose name matches REGEXP",
- :domain => :remote, :details => false, :versions => true,
- :installed => nil, :version => Gem::Requirement.default
+ domain: :remote, details: false, versions: true,
+ installed: nil, version: Gem::Requirement.default
add_query_options
end
diff --git a/lib/rubygems/commands/setup_command.rb b/lib/rubygems/commands/setup_command.rb
index c35d0f5ccc..9c633d6ef7 100644
--- a/lib/rubygems/commands/setup_command.rb
+++ b/lib/rubygems/commands/setup_command.rb
@@ -7,19 +7,19 @@ 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*$}.freeze
- VERSION_MATCHER = %r{^#\s*([\d.a-zA-Z]+)\s*/\s*\d{4}-\d{2}-\d{2}\s*$}.freeze
+ 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
def initialize
super "setup", "Install RubyGems",
- :format_executable => false, :document => %w[ri],
- :force => true,
- :site_or_vendor => "sitelibdir",
- :destdir => "", :prefix => "", :previous_version => "",
- :regenerate_binstubs => true,
- :regenerate_plugins => true
+ format_executable: false, document: %w[ri],
+ force: true,
+ site_or_vendor: "sitelibdir",
+ destdir: "", prefix: "", previous_version: "",
+ regenerate_binstubs: true,
+ regenerate_plugins: true
add_option "--previous-version=VERSION",
"Previous version of RubyGems",
@@ -265,7 +265,7 @@ By default, this RubyGems will install gem as:
fp.puts bin.join
end
- install bin_tmp_file, dest_file, :mode => prog_mode
+ install bin_tmp_file, dest_file, mode: prog_mode
bin_file_names << dest_file
ensure
rm bin_tmp_file
@@ -287,7 +287,7 @@ By default, this RubyGems will install gem as:
TEXT
end
- install bin_cmd_file, "#{dest_file}.bat", :mode => prog_mode
+ install bin_cmd_file, "#{dest_file}.bat", mode: prog_mode
ensure
rm bin_cmd_file
end
@@ -369,18 +369,21 @@ By default, this RubyGems will install gem as:
File.dirname(loaded_from)
else
target_specs_dir = File.join(default_dir, "specifications", "default")
- mkdir_p target_specs_dir, :mode => 0o755
+ mkdir_p target_specs_dir, mode: 0o755
target_specs_dir
end
- bundler_spec = Dir.chdir("bundler") { Gem::Specification.load("bundler.gemspec") }
- default_spec_path = File.join(specs_dir, "#{bundler_spec.full_name}.gemspec")
- Gem.write_binary(default_spec_path, bundler_spec.to_ruby)
+ new_bundler_spec = Dir.chdir("bundler") { Gem::Specification.load("bundler.gemspec") }
+ full_name = new_bundler_spec.full_name
+ gemspec_path = "#{full_name}.gemspec"
+
+ default_spec_path = File.join(specs_dir, gemspec_path)
+ Gem.write_binary(default_spec_path, new_bundler_spec.to_ruby)
bundler_spec = Gem::Specification.load(default_spec_path)
# Remove gemspec that was same version of vendored bundler.
- normal_gemspec = File.join(default_dir, "specifications", "bundler-#{bundler_spec.version}.gemspec")
+ normal_gemspec = File.join(default_dir, "specifications", gemspec_path)
if File.file? normal_gemspec
File.delete normal_gemspec
end
@@ -388,20 +391,14 @@ By default, this RubyGems will install gem as:
# Remove gem files that were same version of vendored bundler.
if File.directory? bundler_spec.gems_dir
Dir.entries(bundler_spec.gems_dir).
- select {|default_gem| File.basename(default_gem) == "bundler-#{bundler_spec.version}" }.
+ select {|default_gem| File.basename(default_gem) == full_name }.
each {|default_gem| rm_r File.join(bundler_spec.gems_dir, default_gem) }
end
- bundler_bin_dir = bundler_spec.bin_dir
- mkdir_p bundler_bin_dir, :mode => 0o755
- bundler_spec.executables.each do |e|
- cp File.join("bundler", bundler_spec.bindir, e), File.join(bundler_bin_dir, e)
- end
-
require_relative "../installer"
Dir.chdir("bundler") do
- built_gem = Gem::Package.build(bundler_spec)
+ built_gem = Gem::Package.build(new_bundler_spec)
begin
Gem::Installer.at(
built_gem,
@@ -418,9 +415,9 @@ By default, this RubyGems will install gem as:
end
end
- bundler_spec.executables.each {|executable| bin_file_names << target_bin_path(bin_dir, executable) }
+ new_bundler_spec.executables.each {|executable| bin_file_names << target_bin_path(bin_dir, executable) }
- say "Bundler #{bundler_spec.version} installed"
+ say "Bundler #{new_bundler_spec.version} installed"
end
def make_destination_dirs
@@ -430,8 +427,8 @@ By default, this RubyGems will install gem as:
lib_dir, bin_dir = generate_default_dirs
end
- mkdir_p lib_dir, :mode => 0o755
- mkdir_p bin_dir, :mode => 0o755
+ mkdir_p lib_dir, mode: 0o755
+ mkdir_p bin_dir, mode: 0o755
[lib_dir, bin_dir]
end
@@ -576,8 +573,8 @@ abort "#{deprecation_message}"
def uninstall_old_gemcutter
require_relative "../uninstaller"
- ui = Gem::Uninstaller.new("gemcutter", :all => true, :ignore => true,
- :version => "< 0.4")
+ ui = Gem::Uninstaller.new("gemcutter", all: true, ignore: true,
+ version: "< 0.4")
ui.uninstall
rescue Gem::InstallError
end
@@ -588,6 +585,8 @@ abort "#{deprecation_message}"
args = %w[--all --only-executables --silent]
args << "--bindir=#{bindir}"
+ args << "--install-dir=#{default_dir}"
+
if options[:env_shebang]
args << "--env-shebang"
end
@@ -639,10 +638,10 @@ abort "#{deprecation_message}"
dest_file = File.join dest_dir, file
dest_dir = File.dirname dest_file
unless File.directory? dest_dir
- mkdir_p dest_dir, :mode => 0o755
+ mkdir_p dest_dir, mode: 0o755
end
- install file, dest_file, :mode => options[:data_mode] || 0o644
+ install file, dest_file, mode: options[:data_mode] || 0o644
end
def remove_file_list(files, dir)
diff --git a/lib/rubygems/commands/sources_command.rb b/lib/rubygems/commands/sources_command.rb
index c9c6ee80ed..976f4a4ea2 100644
--- a/lib/rubygems/commands/sources_command.rb
+++ b/lib/rubygems/commands/sources_command.rb
@@ -59,7 +59,7 @@ class Gem::Commands::SourcesCommand < Gem::Command
say "#{source_uri} added to sources"
end
- rescue URI::Error, ArgumentError
+ rescue Gem::URI::Error, ArgumentError
say "#{source_uri} is not a URI"
terminate_interaction 1
rescue Gem::RemoteFetcher::FetchError => e
@@ -81,7 +81,7 @@ Do you want to add this source?
end
def check_rubygems_https(source_uri) # :nodoc:
- uri = URI source_uri
+ uri = Gem::URI source_uri
if uri.scheme && uri.scheme.casecmp("http").zero? &&
uri.host.casecmp("rubygems.org").zero?
diff --git a/lib/rubygems/commands/specification_command.rb b/lib/rubygems/commands/specification_command.rb
index 938b9507f9..a21ed35be3 100644
--- a/lib/rubygems/commands/specification_command.rb
+++ b/lib/rubygems/commands/specification_command.rb
@@ -13,8 +13,8 @@ class Gem::Commands::SpecificationCommand < Gem::Command
Gem.load_yaml
super "specification", "Display gem specification (in yaml)",
- :domain => :local, :version => Gem::Requirement.default,
- :format => :yaml
+ domain: :local, version: Gem::Requirement.default,
+ format: :yaml
add_version_option("examine")
add_platform_option
diff --git a/lib/rubygems/commands/uninstall_command.rb b/lib/rubygems/commands/uninstall_command.rb
index c66bbe42e1..283bc96ce3 100644
--- a/lib/rubygems/commands/uninstall_command.rb
+++ b/lib/rubygems/commands/uninstall_command.rb
@@ -15,8 +15,8 @@ class Gem::Commands::UninstallCommand < Gem::Command
def initialize
super "uninstall", "Uninstall gems from the local repository",
- :version => Gem::Requirement.default, :user_install => true,
- :check_dev => false, :vendor => false
+ version: Gem::Requirement.default, user_install: true,
+ check_dev: false, vendor: false
add_option("-a", "--[no-]all",
"Uninstall all matching versions") do |value, options|
@@ -168,10 +168,10 @@ that is a dependency of an existing gem. You can use the
gems_to_uninstall = {}
deps.each do |dep|
- next if gems_to_uninstall[dep.name]
- gems_to_uninstall[dep.name] = true
-
- unless original_gem_version[dep.name] == Gem::Requirement.default
+ if original_gem_version[dep.name] == Gem::Requirement.default
+ next if gems_to_uninstall[dep.name]
+ gems_to_uninstall[dep.name] = true
+ else
options[:version] = dep.version
end
@@ -184,7 +184,7 @@ that is a dependency of an existing gem. You can use the
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}")
+ "\tgem uninstall #{spec.name} --install-dir=#{spec.base_dir}")
rescue Gem::UninstallError => e
spec = e.spec
alert_error("Error: unable to successfully uninstall '#{spec.name}' which is " \
diff --git a/lib/rubygems/commands/unpack_command.rb b/lib/rubygems/commands/unpack_command.rb
index 73eefe153c..c2fc720297 100644
--- a/lib/rubygems/commands/unpack_command.rb
+++ b/lib/rubygems/commands/unpack_command.rb
@@ -21,8 +21,8 @@ class Gem::Commands::UnpackCommand < Gem::Command
require "fileutils"
super "unpack", "Unpack an installed gem to the current directory",
- :version => Gem::Requirement.default,
- :target => Dir.pwd
+ version: Gem::Requirement.default,
+ target: Dir.pwd
add_option("--target=DIR",
"target directory for unpacking") do |value, options|
@@ -143,12 +143,6 @@ command help for an example.
# get_path 'rake', '< 0.1' # nil
# get_path 'rak' # nil (exact name required)
#--
- # TODO: This should be refactored so that it's a general service. I don't
- # think any of our existing classes are the right place though. Just maybe
- # 'Cache'?
- #
- # TODO: It just uses Gem.dir for now. What's an easy way to get the list of
- # source directories?
def get_path(dependency)
return dependency.name if /\.gem$/i.match?(dependency.name)
diff --git a/lib/rubygems/commands/update_command.rb b/lib/rubygems/commands/update_command.rb
index fb27e755bc..8e80d46856 100644
--- a/lib/rubygems/commands/update_command.rb
+++ b/lib/rubygems/commands/update_command.rb
@@ -21,7 +21,7 @@ class Gem::Commands::UpdateCommand < Gem::Command
def initialize
options = {
- :force => false,
+ force: false,
}
options.merge!(install_update_options)
@@ -197,18 +197,17 @@ command to remove old versions.
yield
else
require "tmpdir"
- tmpdir = Dir.mktmpdir
- FileUtils.mv Gem.plugindir, tmpdir
+ Dir.mktmpdir("gem_update") do |tmpdir|
+ FileUtils.mv Gem.plugindir, tmpdir
- status = yield
+ status = yield
- if status
- FileUtils.rm_rf tmpdir
- else
- FileUtils.mv File.join(tmpdir, "plugins"), Gem.plugindir
- end
+ unless status
+ FileUtils.mv File.join(tmpdir, "plugins"), Gem.plugindir
+ end
- status
+ status
+ end
end
end
@@ -244,7 +243,7 @@ command to remove old versions.
@installer = Gem::DependencyInstaller.new update_options
- say "Updating #{name}" unless options[:system] && options[:silent]
+ say "Updating #{name}" unless options[:system]
begin
@installer.install name, Gem::Requirement.new(version)
rescue Gem::InstallError, Gem::DependencyError => e
@@ -282,7 +281,7 @@ command to remove old versions.
check_oldest_rubygems version
installed_gems = Gem::Specification.find_all_by_name "rubygems-update", requirement
- installed_gems = update_gem("rubygems-update", version) if installed_gems.empty? || installed_gems.first.version != version
+ installed_gems = update_gem("rubygems-update", requirement) if installed_gems.empty? || installed_gems.first.version != version
return if installed_gems.empty?
install_rubygems installed_gems.first
@@ -294,9 +293,7 @@ command to remove old versions.
args << "--prefix" << Gem.prefix if Gem.prefix
args << "--no-document" unless options[:document].include?("rdoc") || options[:document].include?("ri")
args << "--no-format-executable" if options[:no_format_executable]
- args << "--previous-version" << Gem::VERSION if
- options[:system] == true ||
- Gem::Version.new(options[:system]) >= Gem::Version.new(2)
+ args << "--previous-version" << Gem::VERSION
args
end
@@ -328,12 +325,8 @@ command to remove old versions.
@oldest_supported_version ||=
if Gem.ruby_version > Gem::Version.new("3.1.a")
Gem::Version.new("3.3.3")
- elsif Gem.ruby_version > Gem::Version.new("3.0.a")
- Gem::Version.new("3.2.3")
- elsif Gem.ruby_version > Gem::Version.new("2.7.a")
- Gem::Version.new("3.1.2")
else
- Gem::Version.new("3.0.1")
+ Gem::Version.new("3.2.3")
end
end
end
diff --git a/lib/rubygems/commands/which_command.rb b/lib/rubygems/commands/which_command.rb
index ec464d9672..5ed4d9d142 100644
--- a/lib/rubygems/commands/which_command.rb
+++ b/lib/rubygems/commands/which_command.rb
@@ -5,7 +5,7 @@ require_relative "../command"
class Gem::Commands::WhichCommand < Gem::Command
def initialize
super "which", "Find the location of a library file you can require",
- :search_gems_first => false, :show_all => false
+ search_gems_first: false, show_all: false
add_option "-a", "--[no-]all", "show all matching files" do |show_all, options|
options[:show_all] = show_all