summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorhsbt <hsbt@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2017-10-08 01:32:18 +0000
committerhsbt <hsbt@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2017-10-08 01:32:18 +0000
commitc00e84327f14845bd484e76b5ee5dfeb1fa9ce3d (patch)
tree9f558dafa363f4f0118d504a50cd4461e2821cd1 /lib
parent6b05153a3a75b74b64553d6a46f501d9ee0f0376 (diff)
Merge rubygems master.
This is RC version of Rubygems 2.7.0. https://github.com/rubygems/rubygems/commit/688fb7e83c13c3fe7c2bb03c49a2db4c82852aee git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@60133 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib')
-rw-r--r--lib/rubygems.rb113
-rw-r--r--lib/rubygems/basic_specification.rb8
-rw-r--r--lib/rubygems/bundler_version_finder.rb112
-rw-r--r--lib/rubygems/command.rb2
-rw-r--r--lib/rubygems/command_manager.rb4
-rw-r--r--lib/rubygems/commands/cert_command.rb37
-rw-r--r--lib/rubygems/commands/cleanup_command.rb2
-rw-r--r--lib/rubygems/commands/help_command.rb2
-rw-r--r--lib/rubygems/commands/owner_command.rb4
-rw-r--r--lib/rubygems/commands/pristine_command.rb21
-rw-r--r--lib/rubygems/commands/push_command.rb3
-rw-r--r--lib/rubygems/commands/query_command.rb27
-rw-r--r--lib/rubygems/commands/setup_command.rb193
-rw-r--r--lib/rubygems/commands/signin_command.rb33
-rw-r--r--lib/rubygems/commands/signout_command.rb33
-rw-r--r--lib/rubygems/commands/uninstall_command.rb7
-rw-r--r--lib/rubygems/commands/unpack_command.rb20
-rw-r--r--lib/rubygems/commands/update_command.rb2
-rw-r--r--lib/rubygems/commands/which_command.rb2
-rw-r--r--lib/rubygems/commands/yank_command.rb15
-rw-r--r--lib/rubygems/config_file.rb37
-rwxr-xr-xlib/rubygems/core_ext/kernel_require.rb12
-rw-r--r--lib/rubygems/dependency.rb2
-rw-r--r--lib/rubygems/dependency_installer.rb4
-rw-r--r--lib/rubygems/errors.rb3
-rw-r--r--lib/rubygems/exceptions.rb6
-rw-r--r--lib/rubygems/ext/builder.rb2
-rw-r--r--lib/rubygems/gem_runner.rb6
-rw-r--r--lib/rubygems/install_update_options.rb33
-rw-r--r--lib/rubygems/installer.rb19
-rw-r--r--lib/rubygems/installer_test_case.rb9
-rw-r--r--lib/rubygems/package/old.rb2
-rw-r--r--lib/rubygems/request.rb2
-rw-r--r--lib/rubygems/request_set.rb23
-rw-r--r--lib/rubygems/request_set/gem_dependency_api.rb6
-rw-r--r--lib/rubygems/requirement.rb6
-rw-r--r--lib/rubygems/resolver.rb24
-rw-r--r--lib/rubygems/resolver/installer_set.rb10
-rw-r--r--lib/rubygems/security.rb13
-rw-r--r--lib/rubygems/security_option.rb43
-rw-r--r--lib/rubygems/server.rb16
-rw-r--r--lib/rubygems/source.rb11
-rw-r--r--lib/rubygems/source/git.rb3
-rw-r--r--lib/rubygems/source/local.rb73
-rw-r--r--lib/rubygems/source/lock.rb5
-rw-r--r--lib/rubygems/source_local.rb4
-rw-r--r--lib/rubygems/source_specific_file.rb5
-rw-r--r--lib/rubygems/spec_fetcher.rb10
-rw-r--r--lib/rubygems/specification.rb512
-rw-r--r--lib/rubygems/stub_specification.rb5
-rw-r--r--lib/rubygems/test_case.rb11
-rw-r--r--lib/rubygems/user_interaction.rb28
-rw-r--r--lib/rubygems/util.rb23
-rw-r--r--lib/rubygems/version.rb20
-rw-r--r--lib/rubygems/version_option.rb7
55 files changed, 1052 insertions, 583 deletions
diff --git a/lib/rubygems.rb b/lib/rubygems.rb
index 55aa85b8b2b..d819bdee02e 100644
--- a/lib/rubygems.rb
+++ b/lib/rubygems.rb
@@ -39,7 +39,7 @@ require 'rubygems/errors'
# Further RubyGems documentation can be found at:
#
# * {RubyGems Guides}[http://guides.rubygems.org]
-# * {RubyGems API}[http://rubygems.rubyforge.org/rdoc] (also available from
+# * {RubyGems API}[http://www.rubydoc.info/github/rubygems/rubygems] (also available from
# <tt>gem server</tt>)
#
# == RubyGems Plugins
@@ -47,15 +47,16 @@ require 'rubygems/errors'
# As of RubyGems 1.3.2, RubyGems will load plugins installed in gems or
# $LOAD_PATH. Plugins must be named 'rubygems_plugin' (.rb, .so, etc) and
# placed at the root of your gem's #require_path. Plugins are discovered via
-# Gem::find_files then loaded. Take care when implementing a plugin as your
+# Gem::find_files and then loaded. Take care when implementing a plugin as your
# plugin file may be loaded multiple times if multiple versions of your gem
# are installed.
#
-# For an example plugin, see the graph gem which adds a `gem graph` command.
+# For an example plugin, see the {Graph gem}[https://github.com/seattlerb/graph]
+# which adds a `gem graph` command.
#
# == RubyGems Defaults, Packaging
#
-# RubyGems defaults are stored in rubygems/defaults.rb. If you're packaging
+# RubyGems defaults are stored in lib/rubygems/defaults.rb. If you're packaging
# RubyGems or implementing Ruby you can change RubyGems' defaults.
#
# For RubyGems packagers, provide lib/rubygems/defaults/operating_system.rb
@@ -65,7 +66,7 @@ require 'rubygems/errors'
# override any defaults from lib/rubygems/defaults.rb.
#
# If you need RubyGems to perform extra work on install or uninstall, your
-# defaults override file can set pre and post install and uninstall hooks.
+# defaults override file can set pre/post install and uninstall hooks.
# See Gem::pre_install, Gem::pre_uninstall, Gem::post_install,
# Gem::post_uninstall.
#
@@ -106,6 +107,8 @@ require 'rubygems/errors'
#
# (If your name is missing, PLEASE let us know!)
#
+# == License
+#
# See {LICENSE.txt}[rdoc-ref:lib/rubygems/LICENSE.txt] for permissions.
#
# Thanks!
@@ -130,6 +133,7 @@ module Gem
GEM_DEP_FILES = %w[
gem.deps.rb
+ gems.rb
Gemfile
Isolate
]
@@ -159,7 +163,7 @@ module Gem
# these are defined in Ruby 1.8.7, hence the need for this convoluted setup.
READ_BINARY_ERRORS = begin
- read_binary_errors = [Errno::EACCES]
+ read_binary_errors = [Errno::EACCES, Errno::EROFS]
read_binary_errors << Errno::ENOTSUP if Errno.const_defined?(:ENOTSUP)
read_binary_errors
end.freeze
@@ -174,6 +178,8 @@ module Gem
write_binary_errors
end.freeze
+ USE_BUNDLER_FOR_GEMDEPS = true # :nodoc:
+
@@win_platform = nil
@configuration = nil
@@ -266,17 +272,22 @@ module Gem
return loaded if loaded && dep.matches_spec?(loaded)
- specs = dep.matching_specs(true)
-
- raise Gem::GemNotFoundException,
- "can't find gem #{dep}" if specs.empty?
+ find_specs = proc { dep.matching_specs(true) }
+ if dep.to_s == "bundler (>= 0.a)"
+ specs = Gem::BundlerVersionFinder.without_filtering(&find_specs)
+ else
+ specs = find_specs.call
+ end
specs = specs.find_all { |spec|
spec.executables.include? exec_name
} if exec_name
unless spec = specs.first
- msg = "can't find gem #{name} (#{requirements}) with executable #{exec_name}"
+ msg = "can't find gem #{dep} with executable #{exec_name}"
+ if name == "bundler" && bundler_message = Gem::BundlerVersionFinder.missing_version_message
+ msg = bundler_message
+ end
raise Gem::GemNotFoundException, msg
end
@@ -297,7 +308,10 @@ module Gem
def self.activate_bin_path name, exec_name, requirement # :nodoc:
spec = find_spec_for_exe name, exec_name, [requirement]
- Gem::LOADED_SPECS_MUTEX.synchronize { spec.activate }
+ Gem::LOADED_SPECS_MUTEX.synchronize do
+ spec.activate
+ finish_resolve
+ end
spec.bin_file exec_name
end
@@ -356,12 +370,16 @@ module Gem
# package is not available as a gem, return nil.
def self.datadir(gem_name)
-# TODO: deprecate
spec = @loaded_specs[gem_name]
return nil if spec.nil?
spec.datadir
end
+ class << self
+ extend Gem::Deprecate
+ deprecate :datadir, "spec.datadir", 2016, 10
+ end
+
##
# A Zlib::Deflate.deflate wrapper
@@ -594,7 +612,6 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
# Zlib::GzipReader wrapper that unzips +data+.
def self.gunzip(data)
- require 'rubygems/util'
Gem::Util.gunzip data
end
@@ -602,7 +619,6 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
# Zlib::GzipWriter wrapper that zips +data+.
def self.gzip(data)
- require 'rubygems/util'
Gem::Util.gzip data
end
@@ -610,7 +626,6 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
# A Zlib::Inflate#inflate wrapper
def self.inflate(data)
- require 'rubygems/util'
Gem::Util.inflate data
end
@@ -715,9 +730,20 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
##
# The file name and line number of the caller of the caller of this method.
+ #
+ # +depth+ is how many layers up the call stack it should go.
+ #
+ # e.g.,
+ #
+ # def a; Gem.location_of_caller; end
+ # a #=> ["x.rb", 2] # (it'll vary depending on file name and line number)
+ #
+ # def b; c; end
+ # def c; Gem.location_of_caller(2); end
+ # b #=> ["x.rb", 6] # (it'll vary depending on file name and line number)
- def self.location_of_caller
- caller[1] =~ /(.*?):(\d+).*?$/i
+ def self.location_of_caller(depth = 1)
+ caller[depth] =~ /(.*?):(\d+).*?$/i
file = $1
lineno = $2.to_i
@@ -1148,8 +1174,6 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
path = path.dup
if path == "-" then
- require 'rubygems/util'
-
Gem::Util.traverse_parents Dir.pwd do |directory|
dep_file = GEM_DEP_FILES.find { |f| File.file?(f) }
@@ -1168,18 +1192,36 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
raise ArgumentError, "Unable to find gem dependencies file at #{path}"
end
- rs = Gem::RequestSet.new
- @gemdeps = rs.load_gemdeps path
+ if USE_BUNDLER_FOR_GEMDEPS
+
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path(path)
+ require 'rubygems/user_interaction'
+ Gem::DefaultUserInteraction.use_ui(ui) do
+ require "bundler"
+ @gemdeps = Bundler.setup
+ Bundler.ui = nil
+ @gemdeps.requested_specs.map(&:to_spec).sort_by(&:name)
+ end
+
+ else
+
+ rs = Gem::RequestSet.new
+ @gemdeps = rs.load_gemdeps path
+
+ rs.resolve_current.map do |s|
+ s.full_spec.tap(&:activate)
+ end
- rs.resolve_current.map do |s|
- sp = s.full_spec
- sp.activate
- sp
end
- rescue Gem::LoadError, Gem::UnsatisfiableDependencyError => e
- warn e.message
- warn "You may need to `gem install -g` to install missing gems"
- warn ""
+ rescue => e
+ case e
+ when Gem::LoadError, Gem::UnsatisfiableDependencyError, (defined?(Bundler::GemNotFound) ? Bundler::GemNotFound : Gem::LoadError)
+ warn e.message
+ warn "You may need to `gem install -g` to install missing gems"
+ warn ""
+ else
+ raise
+ end
end
class << self
@@ -1225,6 +1267,8 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
prefix_pattern = /^(#{prefix_group})/
end
+ suffix_pattern = /#{Regexp.union(Gem.suffixes)}\z/
+
spec.files.each do |file|
if new_format
file = file.sub(prefix_pattern, "")
@@ -1232,6 +1276,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
end
@path_to_default_spec_map[file] = spec
+ @path_to_default_spec_map[file.sub(suffix_pattern, "")] = spec
end
end
@@ -1239,11 +1284,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
# Find a Gem::Specification of default gem from +path+
def find_unresolved_default_spec(path)
- Gem.suffixes.each do |suffix|
- spec = @path_to_default_spec_map["#{path}#{suffix}"]
- return spec if spec
- end
- nil
+ @path_to_default_spec_map[path]
end
##
@@ -1314,6 +1355,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
MARSHAL_SPEC_DIR = "quick/Marshal.#{Gem.marshal_version}/"
+ autoload :BundlerVersionFinder, 'rubygems/bundler_version_finder'
autoload :ConfigFile, 'rubygems/config_file'
autoload :Dependency, 'rubygems/dependency'
autoload :DependencyList, 'rubygems/dependency_list'
@@ -1329,6 +1371,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
autoload :SourceList, 'rubygems/source_list'
autoload :SpecFetcher, 'rubygems/spec_fetcher'
autoload :Specification, 'rubygems/specification'
+ autoload :Util, 'rubygems/util'
autoload :Version, 'rubygems/version'
require "rubygems/specification"
diff --git a/lib/rubygems/basic_specification.rb b/lib/rubygems/basic_specification.rb
index 5aed17437e9..0d50a932307 100644
--- a/lib/rubygems/basic_specification.rb
+++ b/lib/rubygems/basic_specification.rb
@@ -71,7 +71,7 @@ class Gem::BasicSpecification
elsif missing_extensions? then
@ignored = true
- warn "Ignoring #{full_name} because its extensions are not built. " +
+ warn "Ignoring #{full_name} because its extensions are not built. " +
"Try: gem pristine #{name} --version #{version}"
return false
end
@@ -275,10 +275,10 @@ class Gem::BasicSpecification
# for this spec.
def lib_dirs_glob
- dirs = if self.require_paths.size > 1 then
- "{#{self.require_paths.join(',')}}"
+ dirs = if self.raw_require_paths.size > 1 then
+ "{#{self.raw_require_paths.join(',')}}"
else
- self.require_paths.first
+ self.raw_require_paths.first
end
"#{self.full_gem_path}/#{dirs}".dup.untaint
diff --git a/lib/rubygems/bundler_version_finder.rb b/lib/rubygems/bundler_version_finder.rb
new file mode 100644
index 00000000000..baca170840c
--- /dev/null
+++ b/lib/rubygems/bundler_version_finder.rb
@@ -0,0 +1,112 @@
+module Gem::BundlerVersionFinder
+ @without_filtering = false
+
+ def self.without_filtering
+ without_filtering, @without_filtering = true, @without_filtering
+ yield
+ ensure
+ @without_filtering = without_filtering
+ end
+
+ def self.bundler_version
+ version, _ = bundler_version_with_reason
+
+ return unless version
+
+ Gem::Version.new(version)
+ end
+
+ def self.bundler_version_with_reason
+ return if @without_filtering
+
+ if v = ENV["BUNDLER_VERSION"]
+ return [v, "`$BUNDLER_VERSION`"]
+ end
+ if v = bundle_update_bundler_version
+ return if v == true
+ return [v, "`bundle update --bundler`"]
+ end
+ v, lockfile = lockfile_version
+ if v
+ return [v, "your #{lockfile}"]
+ end
+ end
+
+ def self.missing_version_message
+ return unless vr = bundler_version_with_reason
+ <<-EOS
+Could not find 'bundler' (#{vr.first}) required by #{vr.last}.
+To update to the lastest version installed on your system, run `bundle update --bundler`.
+To install the missing version, run `gem install bundler:#{vr.first}`
+ EOS
+ end
+
+ def self.compatible?(spec)
+ return true unless spec.name == "bundler".freeze
+ return true unless bundler_version = self.bundler_version
+ if bundler_version.segments.first >= 2
+ spec.version == bundler_version
+ else # 1.x
+ spec.version.segments.first < 2
+ end
+ end
+
+ def self.filter!(specs)
+ return unless bundler_version = self.bundler_version
+ if bundler_version.segments.first >= 2
+ specs.reject! { |spec| spec.version != bundler_version }
+ else # 1.x
+ specs.reject! { |spec| spec.version.segments.first >= 2}
+ end
+ end
+
+ def self.bundle_update_bundler_version
+ return unless File.basename($0) == "bundle".freeze
+ return unless "update".start_with?(ARGV.first || " ")
+ bundler_version = nil
+ update_index = nil
+ ARGV.each_with_index do |a, i|
+ if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN
+ bundler_version = a
+ end
+ next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/
+ bundler_version = $1 || true
+ update_index = i
+ end
+ bundler_version
+ end
+ private_class_method :bundle_update_bundler_version
+
+ def self.lockfile_version
+ return unless lockfile = lockfile_contents
+ lockfile, contents = lockfile
+ lockfile ||= "lockfile"
+ regexp = /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/
+ return unless contents =~ regexp
+ [$1, lockfile]
+ end
+ private_class_method :lockfile_version
+
+ def self.lockfile_contents
+ gemfile = ENV["BUNDLE_GEMFILE"]
+ gemfile = nil if gemfile && gemfile.empty?
+ Gem::Util.traverse_parents Dir.pwd do |directory|
+ next unless gemfile = Gem::GEM_DEP_FILES.find { |f| File.file?(f.untaint) }
+
+ gemfile = File.join directory, gemfile
+ break
+ end unless gemfile
+
+ return unless gemfile
+
+ lockfile = case gemfile
+ when "gems.rb" then "gems.locked"
+ else "#{gemfile}.lock"
+ end.untaint
+
+ return unless File.file?(lockfile)
+
+ [lockfile, File.read(lockfile)]
+ end
+ private_class_method :lockfile_contents
+end
diff --git a/lib/rubygems/command.rb b/lib/rubygems/command.rb
index 14b70b75ecb..3bdca656b3f 100644
--- a/lib/rubygems/command.rb
+++ b/lib/rubygems/command.rb
@@ -527,7 +527,7 @@ class Gem::Command
end
add_common_option("--silent",
- "Silence rubygems output") do |value, options|
+ "Silence RubyGems output") do |value, options|
options[:silent] = true
end
diff --git a/lib/rubygems/command_manager.rb b/lib/rubygems/command_manager.rb
index 451b719c468..887272378ec 100644
--- a/lib/rubygems/command_manager.rb
+++ b/lib/rubygems/command_manager.rb
@@ -58,6 +58,8 @@ class Gem::CommandManager
:rdoc,
:search,
:server,
+ :signin,
+ :signout,
:sources,
:specification,
:stale,
@@ -161,7 +163,7 @@ class Gem::CommandManager
say Gem::VERSION
terminate_interaction 0
when /^-/ then
- alert_error "Invalid option: #{args.first}. See 'gem --help'."
+ alert_error "Invalid option: #{args.first}. See 'gem --help'."
terminate_interaction 1
else
cmd_name = args.shift.downcase
diff --git a/lib/rubygems/commands/cert_command.rb b/lib/rubygems/commands/cert_command.rb
index 7adf5b01b1a..5542262a50c 100644
--- a/lib/rubygems/commands/cert_command.rb
+++ b/lib/rubygems/commands/cert_command.rb
@@ -84,6 +84,11 @@ class Gem::Commands::CertCommand < Gem::Command
options[:sign] << cert_file
end
+
+ add_option('-d', '--days NUMBER_OF_DAYS',
+ 'Days before the certificate expires') do |days, options|
+ options[:expiration_length_days] = days.to_i
+ end
end
def add_certificate certificate # :nodoc:
@@ -105,16 +110,20 @@ class Gem::Commands::CertCommand < Gem::Command
list_certificates_matching filter
end
- options[:build].each do |name|
- build name
+ options[:build].each do |email|
+ build email
end
sign_certificates unless options[:sign].empty?
end
- def build name
+ def build email
+ if !valid_email?(email)
+ raise Gem::CommandLineError, "Invalid email address #{email}"
+ end
+
key, key_path = build_key
- cert_path = build_cert name, key
+ cert_path = build_cert email, key
say "Certificate: #{cert_path}"
@@ -124,8 +133,16 @@ class Gem::Commands::CertCommand < Gem::Command
end
end
- def build_cert name, key # :nodoc:
- cert = Gem::Security.create_cert_email name, key
+ def build_cert email, key # :nodoc:
+ expiration_length_days = options[:expiration_length_days]
+ age =
+ if expiration_length_days.nil? || expiration_length_days == 0
+ Gem::Security::ONE_YEAR
+ else
+ Gem::Security::ONE_DAY * expiration_length_days
+ end
+
+ cert = Gem::Security.create_cert_email email, key, age
Gem::Security.write cert, "gem-public_cert.pem"
end
@@ -273,5 +290,13 @@ For further reading on signing gems see `ri Gem::Security`.
end
end
+ private
+
+ def valid_email? email
+ # It's simple, but is all we need
+ email =~ /\A.+@.+\z/
+ end
+
+
end if defined?(OpenSSL::SSL)
diff --git a/lib/rubygems/commands/cleanup_command.rb b/lib/rubygems/commands/cleanup_command.rb
index 83ee6b5c7ce..db1bf3a7946 100644
--- a/lib/rubygems/commands/cleanup_command.rb
+++ b/lib/rubygems/commands/cleanup_command.rb
@@ -66,7 +66,7 @@ If no gems are named all gems in GEM_HOME are cleaned.
clean_gems
end
- say "Clean Up Complete"
+ say "Clean up complete"
verbose do
skipped = @default_gems.map { |spec| spec.full_name }
diff --git a/lib/rubygems/commands/help_command.rb b/lib/rubygems/commands/help_command.rb
index de3f175fad3..7d020223696 100644
--- a/lib/rubygems/commands/help_command.rb
+++ b/lib/rubygems/commands/help_command.rb
@@ -367,7 +367,7 @@ platform.
elsif possibilities.size > 1 then
alert_warning "Ambiguous command #{command_name} (#{possibilities.join(', ')})"
else
- alert_warning "Unknown command #{command_name}. Try: gem help commands"
+ alert_warning "Unknown command #{command_name}. Try: gem help commands"
end
end
diff --git a/lib/rubygems/commands/owner_command.rb b/lib/rubygems/commands/owner_command.rb
index 4b99434e874..8e2271657a9 100644
--- a/lib/rubygems/commands/owner_command.rb
+++ b/lib/rubygems/commands/owner_command.rb
@@ -40,7 +40,9 @@ permission to.
options[:remove] << value
end
- add_option '-h', '--host HOST', 'Use another gemcutter-compatible host' do |value, options|
+ add_option '-h', '--host HOST',
+ 'Use another gemcutter-compatible host',
+ ' (e.g. https://rubygems.org)' do |value, options|
options[:host] = value
end
end
diff --git a/lib/rubygems/commands/pristine_command.rb b/lib/rubygems/commands/pristine_command.rb
index 6cab572b866..408672158f8 100644
--- a/lib/rubygems/commands/pristine_command.rb
+++ b/lib/rubygems/commands/pristine_command.rb
@@ -125,14 +125,14 @@ extensions will be restored.
next
end
- unless spec.extensions.empty? or options[:extensions] then
+ unless spec.extensions.empty? or options[:extensions] or options[:only_executables] then
say "Skipped #{spec.full_name}, it needs to compile an extension"
next
end
gem = spec.cache_file
- unless File.exist? gem then
+ unless File.exist? gem or options[:only_executables] then
require 'rubygems/remote_fetcher'
say "Cached gem for #{spec.full_name} not found, attempting to fetch..."
@@ -157,16 +157,19 @@ extensions will be restored.
install_defaults.to_s['--env-shebang']
end
- installer = Gem::Installer.at(gem,
- :wrappers => true,
- :force => true,
- :install_dir => spec.base_dir,
- :env_shebang => env_shebang,
- :build_args => spec.build_args)
-
+ installer_options = {
+ :wrappers => true,
+ :force => true,
+ :install_dir => spec.base_dir,
+ :env_shebang => env_shebang,
+ :build_args => spec.build_args,
+ }
+
if options[:only_executables] then
+ installer = Gem::Installer.for_spec(spec, installer_options)
installer.generate_bin
else
+ installer = Gem::Installer.at(gem, installer_options)
installer.install
end
diff --git a/lib/rubygems/commands/push_command.rb b/lib/rubygems/commands/push_command.rb
index 6adeff6b301..d294cbc8df3 100644
--- a/lib/rubygems/commands/push_command.rb
+++ b/lib/rubygems/commands/push_command.rb
@@ -33,7 +33,8 @@ command. For further discussion see the help for the yank command.
add_key_option
add_option('--host HOST',
- 'Push to another gemcutter-compatible host') do |value, options|
+ 'Push to another gemcutter-compatible host',
+ ' (e.g. https://rubygems.org)') do |value, options|
options[:host] = value
end
diff --git a/lib/rubygems/commands/query_command.rb b/lib/rubygems/commands/query_command.rb
index 44144203e0f..4624e5a1e91 100644
--- a/lib/rubygems/commands/query_command.rb
+++ b/lib/rubygems/commands/query_command.rb
@@ -255,22 +255,21 @@ is too hard to use.
name_tuples.map { |n| n.version }.uniq
else
platforms.sort.reverse.map do |version, pls|
- if pls == [Gem::Platform::RUBY] then
- if options[:domain] == :remote || specs.all? { |spec| spec.is_a? Gem::Source }
- version
- else
- spec = specs.select { |s| s.version == version }
- if spec.first.default_gem?
- "default: #{version}"
- else
- version
- end
+ out = version.to_s
+
+ if options[:domain] == :local
+ default = specs.any? do |s|
+ !s.is_a?(Gem::Source) && s.version == version && s.default_gem?
end
- else
- ruby = pls.delete Gem::Platform::RUBY
- platform_list = [ruby, *pls.sort].compact
- "#{version} #{platform_list.join ' '}"
+ out = "default: #{out}" if default
+ end
+
+ if pls != [Gem::Platform::RUBY] then
+ platform_list = [pls.delete(Gem::Platform::RUBY), *pls.sort].compact
+ out = platform_list.unshift(out).join(' ')
end
+
+ out
end
end
diff --git a/lib/rubygems/commands/setup_command.rb b/lib/rubygems/commands/setup_command.rb
index 2c29786224f..f322ca9df55 100644
--- a/lib/rubygems/commands/setup_command.rb
+++ b/lib/rubygems/commands/setup_command.rb
@@ -15,10 +15,11 @@ class Gem::Commands::SetupCommand < Gem::Command
super 'setup', 'Install RubyGems',
:format_executable => true, :document => %w[ri],
:site_or_vendor => 'sitelibdir',
- :destdir => '', :prefix => '', :previous_version => ''
+ :destdir => '', :prefix => '', :previous_version => '',
+ :regenerate_binstubs => true
add_option '--previous-version=VERSION',
- 'Previous version of rubygems',
+ 'Previous version of RubyGems',
'Used for changelog processing' do |version, options|
options[:previous_version] = version
end
@@ -42,7 +43,7 @@ class Gem::Commands::SetupCommand < Gem::Command
add_option '--[no-]format-executable',
'Makes `gem` match ruby',
- 'If ruby is ruby18, gem will be gem18' do |value, options|
+ 'If Ruby is ruby18, gem will be gem18' do |value, options|
options[:format_executable] = value
end
@@ -79,6 +80,15 @@ class Gem::Commands::SetupCommand < Gem::Command
options[:document].uniq!
end
+ add_option '--[no-]regenerate-binstubs',
+ 'Regenerate gem binstubs' do |value, options|
+ if value then
+ options[:regenerate_binstubs] = true
+ else
+ options.delete(:regenerate_binstubs)
+ end
+ end
+
@verbose = nil
end
@@ -92,7 +102,7 @@ class Gem::Commands::SetupCommand < Gem::Command
end
def defaults_str # :nodoc:
- "--format-executable --document ri"
+ "--format-executable --document ri --regenerate-binstubs"
end
def description # :nodoc:
@@ -142,8 +152,12 @@ By default, this RubyGems will install gem as:
remove_old_lib_files lib_dir
+ install_default_bundler_gem
+
say "RubyGems #{Gem::VERSION} installed"
+ regenerate_binstubs
+
uninstall_old_gemcutter
documentation_success = install_rdoc
@@ -190,7 +204,7 @@ By default, this RubyGems will install gem as:
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 "pages for Ruby libraries. You may access it like this:"
say " ri Classname"
say " ri Classname.class_method"
say " ri Classname#instance_method"
@@ -202,59 +216,64 @@ By default, this RubyGems will install gem as:
end
end
- def install_executables(bin_dir)
- say "Installing gem executable" if @verbose
+ def install_executables(bin_dir)
@bin_file_names = []
- Dir.chdir 'bin' do
- bin_files = Dir['*']
+ executables = { 'gem' => 'bin' }
+ executables['bundler'] = 'bundler/exe' if Gem::USE_BUNDLER_FOR_GEMDEPS
+ executables.each do |tool, path|
+ say "Installing #{tool} executable" if @verbose
- bin_files.delete 'update_rubygems'
+ Dir.chdir path do
+ bin_files = Dir['*']
- bin_files.each do |bin_file|
- bin_file_formatted = if options[:format_executable] then
- Gem.default_exec_format % bin_file
- else
- bin_file
- end
+ bin_files -= %w[update_rubygems bundler bundle_ruby]
- dest_file = File.join bin_dir, bin_file_formatted
- bin_tmp_file = File.join Dir.tmpdir, "#{bin_file}.#{$$}"
+ bin_files.each do |bin_file|
+ bin_file_formatted = if options[:format_executable] then
+ Gem.default_exec_format % bin_file
+ else
+ bin_file
+ end
- begin
- bin = File.readlines bin_file
- bin[0] = "#!#{Gem.ruby}\n"
+ dest_file = File.join bin_dir, bin_file_formatted
+ bin_tmp_file = File.join Dir.tmpdir, "#{bin_file}.#{$$}"
- File.open bin_tmp_file, 'w' do |fp|
- fp.puts bin.join
- end
-
- install bin_tmp_file, dest_file, :mode => 0755
- @bin_file_names << dest_file
- ensure
- rm bin_tmp_file
- end
-
- next unless Gem.win_platform?
+ begin
+ bin = File.readlines bin_file
+ bin[0] = "#!#{Gem.ruby}\n"
- begin
- bin_cmd_file = File.join Dir.tmpdir, "#{bin_file}.bat"
+ File.open bin_tmp_file, 'w' do |fp|
+ fp.puts bin.join
+ end
- File.open bin_cmd_file, 'w' do |file|
- file.puts <<-TEXT
-@ECHO OFF
-IF NOT "%~f0" == "~f0" GOTO :WinNT
-@"#{File.basename(Gem.ruby).chomp('"')}" "#{dest_file}" %1 %2 %3 %4 %5 %6 %7 %8 %9
-GOTO :EOF
-:WinNT
-@"#{File.basename(Gem.ruby).chomp('"')}" "%~dpn0" %*
-TEXT
+ install bin_tmp_file, dest_file, :mode => 0755
+ @bin_file_names << dest_file
+ ensure
+ rm bin_tmp_file
end
- install bin_cmd_file, "#{dest_file}.bat", :mode => 0755
- ensure
- rm bin_cmd_file
+ next unless Gem.win_platform?
+
+ begin
+ bin_cmd_file = File.join Dir.tmpdir, "#{bin_file}.bat"
+
+ File.open bin_cmd_file, 'w' do |file|
+ file.puts <<-TEXT
+ @ECHO OFF
+ IF NOT "%~f0" == "~f0" GOTO :WinNT
+ @"#{File.basename(Gem.ruby).chomp('"')}" "#{dest_file}" %1 %2 %3 %4 %5 %6 %7 %8 %9
+ GOTO :EOF
+ :WinNT
+ @"#{File.basename(Gem.ruby).chomp('"')}" "%~dpn0" %*
+ TEXT
+ end
+
+ install bin_cmd_file, "#{dest_file}.bat", :mode => 0755
+ ensure
+ rm bin_cmd_file
+ end
end
end
end
@@ -269,18 +288,22 @@ TEXT
end
def install_lib(lib_dir)
- say "Installing RubyGems" if @verbose
+ libs = { 'RubyGems' => 'lib' }
+ libs['Bundler'] = 'bundler/lib' if Gem::USE_BUNDLER_FOR_GEMDEPS
+ libs.each do |tool, path|
+ say "Installing #{tool}" if @verbose
- lib_files = rb_files_in 'lib'
- pem_files = pem_files_in 'lib'
+ lib_files = rb_files_in path
+ pem_files = pem_files_in path
- Dir.chdir 'lib' do
- lib_files.each do |lib_file|
- install_file lib_file, lib_dir
- end
+ Dir.chdir path do
+ lib_files.each do |lib_file|
+ install_file lib_file, lib_dir
+ end
- pem_files.each do |pem_file|
- install_file pem_file, lib_dir
+ pem_files.each do |pem_file|
+ install_file pem_file, lib_dir
+ end
end
end
end
@@ -326,6 +349,29 @@ TEXT
return false
end
+ def install_default_bundler_gem
+ return unless Gem::USE_BUNDLER_FOR_GEMDEPS
+
+ bundler_spec = Gem::Specification.load("bundler/bundler.gemspec")
+ bundler_spec.files = Dir["bundler/{*.md,{lib,exe,man}/**/*}"]
+ bundler_spec.executables -= %w[bundler bundle_ruby]
+ Dir.entries(Gem::Specification.default_specifications_dir).
+ select {|gs| gs.start_with?("bundler-") }.
+ each {|gs| File.delete(File.join(Gem::Specification.default_specifications_dir, gs)) }
+
+ default_spec_path = File.join(Gem::Specification.default_specifications_dir, "#{bundler_spec.full_name}.gemspec")
+ Gem.write_binary(default_spec_path, bundler_spec.to_ruby)
+
+ bundler_spec = Gem::Specification.load(default_spec_path)
+
+ Dir.entries(bundler_spec.gems_dir).
+ select {|default_gem| default_gem.start_with?("bundler-") }.
+ each {|default_gem| rm_r File.join(bundler_spec.gems_dir, default_gem) }
+
+ mkdir_p bundler_spec.bin_dir
+ bundler_spec.executables.each {|e| cp File.join("bundler", bundler_spec.bindir, e), File.join(bundler_spec.bin_dir, e) }
+ end
+
def make_destination_dirs(install_destdir)
lib_dir, bin_dir = Gem.default_rubygems_dirs
@@ -397,7 +443,7 @@ TEXT
old_bin_path = File.join bin_dir, old_bin_file
next unless File.exist? old_bin_path
- deprecation_message = "`#{old_bin_file}` has been deprecated. Use `#{new_name}` instead."
+ deprecation_message = "`#{old_bin_file}` has been deprecated. Use `#{new_name}` instead."
File.open old_bin_path, 'w' do |fp|
fp.write <<-EOF
@@ -416,23 +462,26 @@ abort "#{deprecation_message}"
end
def remove_old_lib_files lib_dir
- rubygems_dir = File.join lib_dir, 'rubygems'
- lib_files = rb_files_in 'lib/rubygems'
+ lib_dirs = { File.join(lib_dir, 'rubygems') => 'lib/rubygems' }
+ lib_dirs[File.join(lib_dir, 'bundler')] = 'bundler/lib/bundler' if Gem::USE_BUNDLER_FOR_GEMDEPS
+ lib_dirs.each do |old_lib_dir, new_lib_dir|
+ lib_files = rb_files_in(new_lib_dir)
- old_lib_files = rb_files_in rubygems_dir
+ old_lib_files = rb_files_in(old_lib_dir)
- to_remove = old_lib_files - lib_files
+ to_remove = old_lib_files - lib_files
- to_remove.delete_if do |file|
- file.start_with? 'defaults'
- end
+ to_remove.delete_if do |file|
+ file.start_with? 'defaults'
+ end
- Dir.chdir rubygems_dir do
- to_remove.each do |file|
- FileUtils.rm_f file
+ Dir.chdir old_lib_dir do
+ to_remove.each do |file|
+ FileUtils.rm_f file
- warn "unable to remove old file #{file} please remove it by hand" if
- File.exist? file
+ warn "unable to remove old file #{file} please remove it by hand" if
+ File.exist? file
+ end
end
end
end
@@ -480,5 +529,11 @@ abort "#{deprecation_message}"
rescue Gem::InstallError
end
-end
+ def regenerate_binstubs
+ require "rubygems/commands/pristine_command"
+ say "Regenerating binstubs"
+ command = Gem::Commands::PristineCommand.new
+ command.invoke(*%w[--all --only-executables --silent])
+ end
+end
diff --git a/lib/rubygems/commands/signin_command.rb b/lib/rubygems/commands/signin_command.rb
new file mode 100644
index 00000000000..6556db5a89d
--- /dev/null
+++ b/lib/rubygems/commands/signin_command.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+require 'rubygems/command'
+require 'rubygems/gemcutter_utilities'
+
+class Gem::Commands::SigninCommand < Gem::Command
+ include Gem::GemcutterUtilities
+
+ def initialize
+ super 'signin', 'Sign in to any gemcutter-compatible host. '\
+ 'It defaults to https://rubygems.org'
+
+ add_option('--host HOST', 'Push to another gemcutter-compatible host') do |value, options|
+ options[:host] = value
+ end
+
+ end
+
+ def description # :nodoc:
+ 'The signin command executes host sign in for a push server (the default is'\
+ ' https://rubygems.org). The host can be provided with the host flag or can'\
+ ' be inferred from the provided gem. Host resolution matches the resolution'\
+ ' strategy for the push command.'
+ end
+
+ def usage # :nodoc:
+ program_name
+ end
+
+ def execute
+ sign_in options[:host]
+ end
+
+end
diff --git a/lib/rubygems/commands/signout_command.rb b/lib/rubygems/commands/signout_command.rb
new file mode 100644
index 00000000000..2452e8cae1c
--- /dev/null
+++ b/lib/rubygems/commands/signout_command.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+require 'rubygems/command'
+
+class Gem::Commands::SignoutCommand < Gem::Command
+
+ def initialize
+ super 'signout', 'Sign out from all the current sessions.'
+ end
+
+ def description # :nodoc:
+ 'The `signout` command is used to sign out from all current sessions,'\
+ ' allowing you to sign in using a different set of credentials.'
+ end
+
+ def usage # :nodoc:
+ program_name
+ end
+
+ def execute
+ credentials_path = Gem.configuration.credentials_path
+
+ if !File.exist?(credentials_path) then
+ alert_error 'You are not currently signed in.'
+ elsif !File.writable?(credentials_path) then
+ alert_error "File '#{Gem.configuration.credentials_path}' is read-only."\
+ ' Please make sure it is writable.'
+ else
+ Gem.configuration.unset_api_key!
+ say 'You have successfully signed out from all sessions.'
+ end
+ end
+
+end
diff --git a/lib/rubygems/commands/uninstall_command.rb b/lib/rubygems/commands/uninstall_command.rb
index fe97790194e..20b3a7a1e48 100644
--- a/lib/rubygems/commands/uninstall_command.rb
+++ b/lib/rubygems/commands/uninstall_command.rb
@@ -30,7 +30,7 @@ class Gem::Commands::UninstallCommand < Gem::Command
options[:ignore] = value
end
- add_option('-D', '--[no-]-check-development',
+ add_option('-D', '--[no-]check-development',
'Check development dependencies while uninstalling',
'(default: false)') do |value, options|
options[:check_dev] = value
@@ -143,7 +143,9 @@ that is a dependency of an existing gem. You can use the
deplist = Gem::DependencyList.new
get_all_gem_names.uniq.each do |name|
- Gem::Specification.find_all_by_name(name).each do |spec|
+ gem_specs = Gem::Specification.find_all_by_name(name)
+ say("Gem '#{name}' is not installed") if gem_specs.empty?
+ gem_specs.each do |spec|
deplist.add spec
end
end
@@ -162,4 +164,3 @@ that is a dependency of an existing gem. You can use the
end
end
-
diff --git a/lib/rubygems/commands/unpack_command.rb b/lib/rubygems/commands/unpack_command.rb
index ffa429de6f8..eb7f550673e 100644
--- a/lib/rubygems/commands/unpack_command.rb
+++ b/lib/rubygems/commands/unpack_command.rb
@@ -2,11 +2,20 @@
require 'rubygems/command'
require 'rubygems/installer'
require 'rubygems/version_option'
+require 'rubygems/security_option'
require 'rubygems/remote_fetcher'
+# forward-declare
+
+module Gem::Security # :nodoc:
+ class Policy # :nodoc:
+ end
+end
+
class Gem::Commands::UnpackCommand < Gem::Command
include Gem::VersionOption
+ include Gem::SecurityOption
def initialize
require 'fileutils'
@@ -24,6 +33,7 @@ class Gem::Commands::UnpackCommand < Gem::Command
options[:spec] = true
end
+ add_security_option
add_version_option
end
@@ -63,6 +73,8 @@ command help for an example.
# at the same time.)
def execute
+ security_policy = options[:security_policy]
+
get_all_gem_names.each do |name|
dependency = Gem::Dependency.new name, options[:version]
path = get_path dependency
@@ -73,7 +85,7 @@ command help for an example.
end
if @options[:spec] then
- spec, metadata = get_metadata path
+ spec, metadata = get_metadata path, security_policy
if metadata.nil? then
alert_error "--spec is unsupported on '#{name}' (old format gem)"
@@ -89,7 +101,7 @@ command help for an example.
basename = File.basename path, '.gem'
target_dir = File.expand_path basename, options[:target]
- package = Gem::Package.new path
+ package = Gem::Package.new path, security_policy
package.extract_files target_dir
say "Unpacked gem: '#{target_dir}'"
@@ -158,8 +170,8 @@ command help for an example.
#--
# TODO move to Gem::Package as #raw_spec or something
- def get_metadata path
- format = Gem::Package.new path
+ def get_metadata path, security_policy = nil
+ format = Gem::Package.new path, security_policy
spec = format.spec
metadata = nil
diff --git a/lib/rubygems/commands/update_command.rb b/lib/rubygems/commands/update_command.rb
index f3d70a92a65..93ee60e1abd 100644
--- a/lib/rubygems/commands/update_command.rb
+++ b/lib/rubygems/commands/update_command.rb
@@ -70,7 +70,7 @@ command to remove old versions.
def check_latest_rubygems version # :nodoc:
if Gem.rubygems_version == version then
- say "Latest version currently installed. Aborting."
+ say "Latest version already installed. Done."
terminate_interaction
end
diff --git a/lib/rubygems/commands/which_command.rb b/lib/rubygems/commands/which_command.rb
index c028d5d49f3..704d79fc60c 100644
--- a/lib/rubygems/commands/which_command.rb
+++ b/lib/rubygems/commands/which_command.rb
@@ -56,7 +56,7 @@ requiring to see why it does not behave as you expect.
paths = find_paths arg, dirs
if paths.empty? then
- alert_error "Can't find ruby library file or shared library #{arg}"
+ alert_error "Can't find Ruby library file or shared library #{arg}"
found &&= false
else
diff --git a/lib/rubygems/commands/yank_command.rb b/lib/rubygems/commands/yank_command.rb
index 0d6575b2725..ebf24e5c771 100644
--- a/lib/rubygems/commands/yank_command.rb
+++ b/lib/rubygems/commands/yank_command.rb
@@ -11,19 +11,11 @@ class Gem::Commands::YankCommand < Gem::Command
def description # :nodoc:
<<-EOF
-The yank command removes a gem you pushed to a server from the server's
-index.
-
-Note that if you push a gem to rubygems.org the yank command does not
-prevent other people from downloading the gem via the download link.
+The yank command permanently removes a gem you pushed to a server.
Once you have pushed a gem several downloads will happen automatically
-via the webhooks. If you accidentally pushed passwords or other sensitive
+via the webhooks. If you accidentally pushed passwords or other sensitive
data you will need to change them immediately and yank your gem.
-
-If you are yanking a gem due to intellectual property reasons contact
-http://help.rubygems.org for permanent removal. Be sure to mention this
-as the reason for the removal request.
EOF
end
@@ -42,7 +34,8 @@ as the reason for the removal request.
add_platform_option("remove")
add_option('--host HOST',
- 'Yank from another gemcutter-compatible host') do |value, options|
+ 'Yank from another gemcutter-compatible host',
+ ' (e.g. https://rubygems.org)') do |value, options|
options[:host] = value
end
diff --git a/lib/rubygems/config_file.rb b/lib/rubygems/config_file.rb
index c95d7dd1f14..b98d30cc696 100644
--- a/lib/rubygems/config_file.rb
+++ b/lib/rubygems/config_file.rb
@@ -336,6 +336,15 @@ if you believe they were disclosed to a third party.
load_api_keys # reload
end
+ ##
+ # Remove the +~/.gem/credentials+ file to clear all the current sessions.
+
+ def unset_api_key!
+ return false unless File.exist?(credentials_path)
+
+ File.delete(credentials_path)
+ end
+
def load_file(filename)
Gem.load_yaml
@@ -419,31 +428,11 @@ if you believe they were disclosed to a third party.
# to_yaml only overwrites things you can't override on the command line.
def to_yaml # :nodoc:
yaml_hash = {}
- yaml_hash[:backtrace] = if @hash.key?(:backtrace)
- @hash[:backtrace]
- else
- DEFAULT_BACKTRACE
- end
-
- yaml_hash[:bulk_threshold] = if @hash.key?(:bulk_threshold)
- @hash[:bulk_threshold]
- else
- DEFAULT_BULK_THRESHOLD
- end
-
+ yaml_hash[:backtrace] = @hash.fetch(:backtrace, DEFAULT_BACKTRACE)
+ yaml_hash[:bulk_threshold] = @hash.fetch(:bulk_threshold, DEFAULT_BULK_THRESHOLD)
yaml_hash[:sources] = Gem.sources.to_a
-
- yaml_hash[:update_sources] = if @hash.key?(:update_sources)
- @hash[:update_sources]
- else
- DEFAULT_UPDATE_SOURCES
- end
-
- yaml_hash[:verbose] = if @hash.key?(:verbose)
- @hash[:verbose]
- else
- DEFAULT_VERBOSITY
- end
+ yaml_hash[:update_sources] = @hash.fetch(:update_sources, DEFAULT_UPDATE_SOURCES)
+ yaml_hash[:verbose] = @hash.fetch(:verbose, DEFAULT_VERBOSITY)
yaml_hash[:ssl_verify_mode] =
@hash[:ssl_verify_mode] if @hash.key? :ssl_verify_mode
diff --git a/lib/rubygems/core_ext/kernel_require.rb b/lib/rubygems/core_ext/kernel_require.rb
index 3172ef9be61..d3df9d85f97 100755
--- a/lib/rubygems/core_ext/kernel_require.rb
+++ b/lib/rubygems/core_ext/kernel_require.rb
@@ -41,8 +41,7 @@ module Kernel
path = path.to_path if path.respond_to? :to_path
- spec = Gem.find_unresolved_default_spec(path)
- if spec
+ if spec = Gem.find_unresolved_default_spec(path)
Gem.remove_unresolved_default_spec(spec)
begin
Kernel.send(:gem, spec.name)
@@ -66,12 +65,10 @@ module Kernel
#--
# TODO request access to the C implementation of this to speed up RubyGems
- spec = Gem::Specification.find_active_stub_by_path path
-
- begin
+ if Gem::Specification.find_active_stub_by_path(path)
RUBYGEMS_ACTIVATION_MONITOR.exit
return gem_original_require(path)
- end if spec
+ end
# Attempt to find +path+ in any unresolved gems...
@@ -109,7 +106,7 @@ module Kernel
# Ok, now find a gem that has no conflicts, starting
# at the highest version.
- valid = found_specs.reject { |s| s.has_conflicts? }.first
+ valid = found_specs.find { |s| !s.has_conflicts? }
unless valid then
le = Gem::LoadError.new "unable to find a version of '#{names.first}' to activate"
@@ -143,4 +140,3 @@ module Kernel
private :require
end
-
diff --git a/lib/rubygems/dependency.rb b/lib/rubygems/dependency.rb
index bbdab7ccfaf..55873c71e87 100644
--- a/lib/rubygems/dependency.rb
+++ b/lib/rubygems/dependency.rb
@@ -280,6 +280,8 @@ class Gem::Dependency
requirement.satisfied_by?(spec.version) && env_req.satisfied_by?(spec.version)
}.map(&:to_spec)
+ Gem::BundlerVersionFinder.filter!(matches) if name == "bundler".freeze
+
if platform_only
matches.reject! { |spec|
spec.nil? || !Gem::Platform.match(spec.platform)
diff --git a/lib/rubygems/dependency_installer.rb b/lib/rubygems/dependency_installer.rb
index 28848f7373c..5a87f509560 100644
--- a/lib/rubygems/dependency_installer.rb
+++ b/lib/rubygems/dependency_installer.rb
@@ -7,6 +7,7 @@ require 'rubygems/spec_fetcher'
require 'rubygems/user_interaction'
require 'rubygems/source'
require 'rubygems/available_set'
+require 'rubygems/deprecate'
##
# Installs a gem along with all its dependencies from local and remote gems.
@@ -46,6 +47,9 @@ class Gem::DependencyInstaller
attr_reader :gems_to_install # :nodoc:
+ extend Gem::Deprecate
+ deprecate :gems_to_install, :none, 2016, 10
+
##
# List of gems installed by #install in alphabetic order
diff --git a/lib/rubygems/errors.rb b/lib/rubygems/errors.rb
index 5cd5b14c583..6f2847d548a 100644
--- a/lib/rubygems/errors.rb
+++ b/lib/rubygems/errors.rb
@@ -58,6 +58,9 @@ module Gem
private
def build_message
+ if name == "bundler" && message = Gem::BundlerVersionFinder.missing_version_message
+ return message
+ end
names = specs.map(&:full_name)
"Could not find '#{name}' (#{requirement}) - did find: [#{names.join ','}]\n"
end
diff --git a/lib/rubygems/exceptions.rb b/lib/rubygems/exceptions.rb
index 9089eae4d5f..b7528761fc2 100644
--- a/lib/rubygems/exceptions.rb
+++ b/lib/rubygems/exceptions.rb
@@ -154,6 +154,12 @@ class Gem::ImpossibleDependenciesError < Gem::Exception
end
class Gem::InstallError < Gem::Exception; end
+class Gem::RuntimeRequirementNotMetError < Gem::InstallError
+ attr_accessor :suggestion
+ def message
+ [suggestion, super].compact.join("\n\t")
+ end
+end
##
# Potentially raised when a specification is validated.
diff --git a/lib/rubygems/ext/builder.rb b/lib/rubygems/ext/builder.rb
index 699903ab0ed..a1619c97d70 100644
--- a/lib/rubygems/ext/builder.rb
+++ b/lib/rubygems/ext/builder.rb
@@ -183,7 +183,7 @@ EOF
return if @spec.extensions.empty?
if @build_args.empty?
- say "Building native extensions. This could take a while..."
+ say "Building native extensions. This could take a while..."
else
say "Building native extensions with: '#{@build_args.join ' '}'"
say "This could take a while..."
diff --git a/lib/rubygems/gem_runner.rb b/lib/rubygems/gem_runner.rb
index fec9e403da6..349d49d66e6 100644
--- a/lib/rubygems/gem_runner.rb
+++ b/lib/rubygems/gem_runner.rb
@@ -8,6 +8,7 @@
require 'rubygems'
require 'rubygems/command_manager'
require 'rubygems/config_file'
+require 'rubygems/deprecate'
##
# Load additional plugins from $LOAD_PATH
@@ -26,7 +27,10 @@ Gem.load_env_plugins rescue nil
class Gem::GemRunner
def initialize(options={})
- # TODO: nuke these options
+ if !options.empty? && !Gem::Deprecate.skip
+ Kernel.warn "NOTE: passing options to Gem::GemRunner.new is deprecated with no replacement. It will be removed on or after 2016-10-01."
+ end
+
@command_manager_class = options[:command_manager] || Gem::CommandManager
@config_file_class = options[:config_file] || Gem::ConfigFile
end
diff --git a/lib/rubygems/install_update_options.rb b/lib/rubygems/install_update_options.rb
index 5559c943700..190372739b0 100644
--- a/lib/rubygems/install_update_options.rb
+++ b/lib/rubygems/install_update_options.rb
@@ -6,37 +6,18 @@
#++
require 'rubygems'
-
-# forward-declare
-
-module Gem::Security # :nodoc:
- class Policy # :nodoc:
- end
-end
+require 'rubygems/security_option'
##
# Mixin methods for install and update options for Gem::Commands
module Gem::InstallUpdateOptions
+ include Gem::SecurityOption
##
# Add the install/update options to the option parser.
def add_install_update_options
- # TODO: use @parser.accept
- OptionParser.accept Gem::Security::Policy do |value|
- require 'rubygems/security'
-
- raise OptionParser::InvalidArgument, 'OpenSSL not installed' unless
- defined?(Gem::Security::HighSecurity)
-
- value = Gem::Security::Policies[value]
- valid = Gem::Security::Policies.keys.sort
- message = "#{value} (#{valid.join ', '} are valid)"
- raise OptionParser::InvalidArgument, message if value.nil?
- value
- end
-
add_option(:"Install/Update", '-i', '--install-dir DIR',
'Gem repository directory to get installed',
'gems') do |value, options|
@@ -124,11 +105,7 @@ module Gem::InstallUpdateOptions
options[:wrappers] = value
end
- add_option(:"Install/Update", '-P', '--trust-policy POLICY',
- Gem::Security::Policy,
- 'Specify gem trust policy') do |value, options|
- options[:security_policy] = value
- end
+ add_security_option
add_option(:"Install/Update", '--ignore-dependencies',
'Do not install any required dependent gems') do |value, options|
@@ -136,8 +113,8 @@ module Gem::InstallUpdateOptions
end
add_option(:"Install/Update", '--[no-]format-executable',
- 'Make installed executable names match ruby.',
- 'If ruby is ruby18, foo_exec will be',
+ 'Make installed executable names match Ruby.',
+ 'If Ruby is ruby18, foo_exec will be',
'foo_exec18') do |value, options|
options[:format_executable] = value
end
diff --git a/lib/rubygems/installer.rb b/lib/rubygems/installer.rb
index 6fd3399dd44..0cbca0791b2 100644
--- a/lib/rubygems/installer.rb
+++ b/lib/rubygems/installer.rb
@@ -136,8 +136,9 @@ class Gem::Installer
end
##
- # Constructs an Installer instance that will install the gem located at
- # +gem+. +options+ is a Hash with the following keys:
+ # Constructs an Installer instance that will install the gem at +package+ which
+ # can either be a path or an instance of Gem::Package. +options+ is a Hash
+ # with the following keys:
#
# :bin_dir:: Where to put a bin wrapper if needed.
# :development:: Whether or not development dependencies should be installed.
@@ -157,6 +158,7 @@ class Gem::Installer
# :wrappers:: Install wrappers if true, symlinks if false.
# :build_args:: An Array of arguments to pass to the extension builder
# process. If not set, then Gem::Command.build_args is used
+ # :post_install_message:: Print gem post install message if true
def initialize(package, options={})
require 'fileutils'
@@ -471,7 +473,7 @@ class Gem::Installer
unless File.exist? bin_path then
# TODO change this to a more useful warning
- warn "#{bin_path} maybe `gem pristine #{spec.name}` will fix it?"
+ warn "`#{bin_path}` does not exist, maybe `gem pristine #{spec.name}` will fix it?"
next
end
@@ -608,7 +610,9 @@ class Gem::Installer
def ensure_required_ruby_version_met # :nodoc:
if rrv = spec.required_ruby_version then
unless rrv.satisfied_by? Gem.ruby_version then
- raise Gem::InstallError, "#{spec.name} requires Ruby version #{rrv}."
+ ruby_version = Gem.ruby_api_version
+ raise Gem::RuntimeRequirementNotMetError,
+ "#{spec.name} requires Ruby version #{rrv}. The current ruby version is #{ruby_version}."
end
end
end
@@ -616,8 +620,9 @@ class Gem::Installer
def ensure_required_rubygems_version_met # :nodoc:
if rrgv = spec.required_rubygems_version then
unless rrgv.satisfied_by? Gem.rubygems_version then
- raise Gem::InstallError,
- "#{spec.name} requires RubyGems version #{rrgv}. " +
+ rg_version = Gem::VERSION
+ raise Gem::RuntimeRequirementNotMetError,
+ "#{spec.name} requires RubyGems version #{rrgv}. The current RubyGems version is #{rg_version}. " +
"Try 'gem update --system' to update RubyGems itself."
end
end
@@ -821,7 +826,7 @@ TEXT
#
# Version and dependency checks are skipped if this install is forced.
#
- # The dependent check will be skipped this install is ignoring dependencies.
+ # The dependent check will be skipped if the install is ignoring dependencies.
def pre_install_checks
verify_gem_home options[:unpack]
diff --git a/lib/rubygems/installer_test_case.rb b/lib/rubygems/installer_test_case.rb
index eccd5711c2c..4cec5da3f4c 100644
--- a/lib/rubygems/installer_test_case.rb
+++ b/lib/rubygems/installer_test_case.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require 'rubygems/test_case'
require 'rubygems/installer'
+require 'rubygems/deprecate'
class Gem::Installer
@@ -72,7 +73,7 @@ class Gem::InstallerTestCase < Gem::TestCase
# a spec named 'a', intended for regular installs
# @user_spec::
# a spec named 'b', intended for user installs
-
+ #
# @gem::
# the path to a built gem from @spec
# @user_spec::
@@ -107,15 +108,17 @@ class Gem::InstallerTestCase < Gem::TestCase
end
def util_gem_bindir spec = @spec # :nodoc:
- # TODO: deprecate
spec.bin_dir
end
def util_gem_dir spec = @spec # :nodoc:
- # TODO: deprecate
spec.gem_dir
end
+ extend Gem::Deprecate
+ deprecate :util_gem_bindir, "@spec.bin_dir", 2016, 10
+ deprecate :util_gem_dir, "@spec.gem_dir", 2016, 10
+
##
# The path where installed executables live
diff --git a/lib/rubygems/package/old.rb b/lib/rubygems/package/old.rb
index 5e722baa354..88193b986dc 100644
--- a/lib/rubygems/package/old.rb
+++ b/lib/rubygems/package/old.rb
@@ -124,7 +124,7 @@ class Gem::Package::Old < Gem::Package
break unless line
end
- raise Gem::Exception, "Failed to find end of ruby script while reading gem"
+ raise Gem::Exception, "Failed to find end of Ruby script while reading gem"
end
##
diff --git a/lib/rubygems/request.rb b/lib/rubygems/request.rb
index a0d766d9ae8..81699b98fef 100644
--- a/lib/rubygems/request.rb
+++ b/lib/rubygems/request.rb
@@ -83,7 +83,7 @@ class Gem::Request
e.message =~ / -- openssl$/
raise Gem::Exception.new(
- 'Unable to require openssl, install OpenSSL and rebuild ruby (preferred) or use non-HTTPS sources')
+ 'Unable to require openssl, install OpenSSL and rebuild Ruby (preferred) or use non-HTTPS sources')
end
def self.verify_certificate store_context
diff --git a/lib/rubygems/request_set.rb b/lib/rubygems/request_set.rb
index 5541e64b882..95a8eed1af6 100644
--- a/lib/rubygems/request_set.rb
+++ b/lib/rubygems/request_set.rb
@@ -163,9 +163,26 @@ class Gem::RequestSet
end
end
- spec = req.spec.install options do |installer|
- yield req, installer if block_given?
- end
+ spec =
+ begin
+ req.spec.install options do |installer|
+ yield req, installer if block_given?
+ end
+ rescue Gem::RuntimeRequirementNotMetError => e
+ recent_match = req.spec.set.find_all(req.request).sort_by(&:version).reverse_each.find do |s|
+ s = s.spec
+ s.required_ruby_version.satisfied_by?(Gem.ruby_version) && s.required_rubygems_version.satisfied_by?(Gem.rubygems_version)
+ end
+ if recent_match
+ suggestion = "The last version of #{req.request} to support your Ruby & RubyGems was #{recent_match.version}. Try installing it with `gem install #{recent_match.name} -v #{recent_match.version}`"
+ suggestion += " and then running the current command again" unless @always_install.include?(req.spec.spec)
+ else
+ suggestion = "There are no versions of #{req.request} compatible with your Ruby & RubyGems"
+ suggestion += ". Maybe try installing an older version of the gem you're looking for?" unless @always_install.include?(req.spec.spec)
+ end
+ e.suggestion = suggestion
+ raise
+ end
requests << spec
end
diff --git a/lib/rubygems/request_set/gem_dependency_api.rb b/lib/rubygems/request_set/gem_dependency_api.rb
index 4b2699d7d2f..867086cc0e3 100644
--- a/lib/rubygems/request_set/gem_dependency_api.rb
+++ b/lib/rubygems/request_set/gem_dependency_api.rb
@@ -786,7 +786,7 @@ Gem dependencies file #{@path} includes git reference for both ref/branch and ta
engine_version = options[:engine_version]
raise ArgumentError,
- 'you must specify engine_version along with the ruby engine' if
+ 'You must specify engine_version along with the Ruby engine' if
engine and not engine_version
return true if @installing
@@ -799,7 +799,7 @@ Gem dependencies file #{@path} includes git reference for both ref/branch and ta
end
if engine and engine != Gem.ruby_engine then
- message = "Your ruby engine is #{Gem.ruby_engine}, " +
+ message = "Your Ruby engine is #{Gem.ruby_engine}, " +
"but your #{gem_deps_file} requires #{engine}"
raise Gem::RubyVersionMismatch, message
@@ -810,7 +810,7 @@ Gem dependencies file #{@path} includes git reference for both ref/branch and ta
if engine_version != my_engine_version then
message =
- "Your ruby engine version is #{Gem.ruby_engine} #{my_engine_version}, " +
+ "Your Ruby engine version is #{Gem.ruby_engine} #{my_engine_version}, " +
"but your #{gem_deps_file} requires #{engine} #{engine_version}"
raise Gem::RubyVersionMismatch, message
diff --git a/lib/rubygems/requirement.rb b/lib/rubygems/requirement.rb
index 32dc7690556..2a60c86e690 100644
--- a/lib/rubygems/requirement.rb
+++ b/lib/rubygems/requirement.rb
@@ -51,7 +51,11 @@ class Gem::Requirement
# If the input is "weird", the default version requirement is
# returned.
- def self.create input
+ def self.create *inputs
+ return new inputs if inputs.length > 1
+
+ input = inputs.shift
+
case input
when Gem::Requirement then
input
diff --git a/lib/rubygems/resolver.rb b/lib/rubygems/resolver.rb
index 8f0db512a2b..13ee035e4c2 100644
--- a/lib/rubygems/resolver.rb
+++ b/lib/rubygems/resolver.rb
@@ -230,8 +230,28 @@ class Gem::Resolver
exc.errors = @set.errors
raise exc
end
- possibles.sort_by { |s| [s.source, s.version, Gem::Platform.local =~ s.platform ? 1 : 0] }.
- map { |s| ActivationRequest.new s, dependency, [] }
+
+ sources = []
+
+ groups = Hash.new { |hash, key| hash[key] = [] }
+
+ # create groups & sources in the same loop
+ sources = possibles.map { |spec|
+ source = spec.source
+ groups[source] << spec
+ source
+ }.uniq.reverse
+
+ activation_requests = []
+
+ sources.each do |source|
+ groups[source].
+ sort_by { |spec| [spec.version, Gem::Platform.local =~ spec.platform ? 1 : 0] }.
+ map { |spec| ActivationRequest.new spec, dependency, [] }.
+ each { |activation_request| activation_requests << activation_request }
+ end
+
+ activation_requests
end
def dependencies_for(specification)
diff --git a/lib/rubygems/resolver/installer_set.rb b/lib/rubygems/resolver/installer_set.rb
index 07fffeb1506..f24293c0a03 100644
--- a/lib/rubygems/resolver/installer_set.rb
+++ b/lib/rubygems/resolver/installer_set.rb
@@ -41,6 +41,7 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
@ignore_dependencies = false
@ignore_installed = false
@local = {}
+ @local_source = Gem::Source::Local.new
@remote_set = Gem::Resolver::BestSet.new
@specs = {}
end
@@ -136,13 +137,11 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
res.concat matching_local
- local_source = Gem::Source::Local.new
-
begin
- if local_spec = local_source.find_gem(name, dep.requirement) then
+ if local_spec = @local_source.find_gem(name, dep.requirement) then
res << Gem::Resolver::IndexSpecification.new(
self, local_spec.name, local_spec.version,
- local_source, local_spec.platform)
+ @local_source, local_spec.platform)
end
rescue Gem::Package::FormatError
# ignore
@@ -194,7 +193,7 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
# Has a local gem for +dep_name+ been added to this set?
def local? dep_name # :nodoc:
- spec, = @local[dep_name]
+ spec, _ = @local[dep_name]
spec
end
@@ -226,4 +225,3 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
end
end
-
diff --git a/lib/rubygems/security.rb b/lib/rubygems/security.rb
index 6963ca156f5..4690dd9230e 100644
--- a/lib/rubygems/security.rb
+++ b/lib/rubygems/security.rb
@@ -340,7 +340,9 @@ module Gem::Security
# Digest algorithm used to sign gems
DIGEST_ALGORITHM =
- if defined?(OpenSSL::Digest::SHA1) then
+ if defined?(OpenSSL::Digest::SHA256) then
+ OpenSSL::Digest::SHA256
+ elsif defined?(OpenSSL::Digest::SHA1) then
OpenSSL::Digest::SHA1
end
@@ -363,7 +365,7 @@ module Gem::Security
##
# Length of keys created by KEY_ALGORITHM
- KEY_LENGTH = 2048
+ KEY_LENGTH = 3072
##
# Cipher used to encrypt the key pair used to sign gems.
@@ -372,9 +374,14 @@ module Gem::Security
KEY_CIPHER = OpenSSL::Cipher.new('AES-256-CBC') if defined?(OpenSSL::Cipher)
##
+ # One day in seconds
+
+ ONE_DAY = 86400
+
+ ##
# One year in seconds
- ONE_YEAR = 86400 * 365
+ ONE_YEAR = ONE_DAY * 365
##
# The default set of extensions are:
diff --git a/lib/rubygems/security_option.rb b/lib/rubygems/security_option.rb
new file mode 100644
index 00000000000..4e3473acb48
--- /dev/null
+++ b/lib/rubygems/security_option.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+#--
+# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
+# All rights reserved.
+# See LICENSE.txt for permissions.
+#++
+
+require 'rubygems'
+
+# forward-declare
+
+module Gem::Security # :nodoc:
+ class Policy # :nodoc:
+ end
+end
+
+##
+# Mixin methods for security option for Gem::Commands
+
+module Gem::SecurityOption
+ def add_security_option
+ # TODO: use @parser.accept
+ OptionParser.accept Gem::Security::Policy do |value|
+ require 'rubygems/security'
+
+ raise OptionParser::InvalidArgument, 'OpenSSL not installed' unless
+ defined?(Gem::Security::HighSecurity)
+
+ policy = Gem::Security::Policies[value]
+ unless policy
+ valid = Gem::Security::Policies.keys.sort
+ raise OptionParser::InvalidArgument, "#{value} (#{valid.join ', '} are valid)"
+ end
+ policy
+ end
+
+ add_option(:"Install/Update", '-P', '--trust-policy POLICY',
+ Gem::Security::Policy,
+ 'Specify gem trust policy') do |value, options|
+ options[:security_policy] = value
+ end
+ end
+end
diff --git a/lib/rubygems/server.rb b/lib/rubygems/server.rb
index df4eb566d31..93b3af36f8a 100644
--- a/lib/rubygems/server.rb
+++ b/lib/rubygems/server.rb
@@ -573,19 +573,11 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
add_date res
case req.request_uri.path
- when %r|^/quick/(Marshal.#{Regexp.escape Gem.marshal_version}/)?(.*?)-([0-9.]+[^-]*?)(-.*?)?\.gemspec\.rz$| then
- marshal_format, name, version, platform = $1, $2, $3, $4
- specs = Gem::Specification.find_all_by_name name, version
+ when %r|^/quick/(Marshal.#{Regexp.escape Gem.marshal_version}/)?(.*?)\.gemspec\.rz$| then
+ marshal_format, full_name = $1, $2
+ specs = Gem::Specification.find_all_by_full_name(full_name)
- selector = [name, version, platform].map(&:inspect).join ' '
-
- platform = if platform then
- Gem::Platform.new platform.sub(/^-/, '')
- else
- Gem::Platform::RUBY
- end
-
- specs = specs.select { |s| s.platform == platform }
+ selector = full_name.inspect
if specs.empty? then
res.status = 404
diff --git a/lib/rubygems/source.rb b/lib/rubygems/source.rb
index 85f5268fa34..bd84c217a7c 100644
--- a/lib/rubygems/source.rb
+++ b/lib/rubygems/source.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-require 'uri'
-require 'fileutils'
+autoload :FileUtils, 'fileutils'
+autoload :URI, 'uri'
##
# A Source knows how to list and fetch gems from a RubyGems marshal index.
@@ -67,7 +67,11 @@ class Gem::Source
return -1 if !other.uri
- @uri.to_s <=> other.uri.to_s
+ # Returning 1 here ensures that when sorting a list of sources, the
+ # original ordering of sources supplied by the user is preserved.
+ return 1 unless @uri.to_s == other.uri.to_s
+
+ 0
else
nil
end
@@ -232,4 +236,3 @@ require 'rubygems/source/specific_file'
require 'rubygems/source/local'
require 'rubygems/source/lock'
require 'rubygems/source/vendor'
-
diff --git a/lib/rubygems/source/git.rb b/lib/rubygems/source/git.rb
index 0900da0cbc3..23f8928a4eb 100644
--- a/lib/rubygems/source/git.rb
+++ b/lib/rubygems/source/git.rb
@@ -1,5 +1,4 @@
# frozen_string_literal: true
-require 'digest'
require 'rubygems/util'
##
@@ -226,6 +225,8 @@ class Gem::Source::Git < Gem::Source
# A hash for the git gem based on the git repository URI.
def uri_hash # :nodoc:
+ require 'digest' # required here to avoid deadlocking in Gem.activate_bin_path (because digest is a gem on 2.5+)
+
normalized =
if @repository =~ %r%^\w+://(\w+@)?% then
uri = URI(@repository).normalize.to_s.sub %r%/$%,''
diff --git a/lib/rubygems/source/local.rb b/lib/rubygems/source/local.rb
index 3227fb61b0c..5ab7a467b57 100644
--- a/lib/rubygems/source/local.rb
+++ b/lib/rubygems/source/local.rb
@@ -9,6 +9,7 @@ class Gem::Source::Local < Gem::Source
@specs = nil
@api_uri = nil
@uri = nil
+ @load_specs_names = {}
end
##
@@ -34,45 +35,47 @@ class Gem::Source::Local < Gem::Source
end
def load_specs type # :nodoc:
- names = []
-
- @specs = {}
-
- Dir["*.gem"].each do |file|
- begin
- pkg = Gem::Package.new(file)
- rescue SystemCallError, Gem::Package::FormatError
- # ignore
- else
- tup = pkg.spec.name_tuple
- @specs[tup] = [File.expand_path(file), pkg]
-
- case type
- when :released
- unless pkg.spec.version.prerelease?
- names << pkg.spec.name_tuple
- end
- when :prerelease
- if pkg.spec.version.prerelease?
- names << pkg.spec.name_tuple
- end
- when :latest
- tup = pkg.spec.name_tuple
+ @load_specs_names[type] ||= begin
+ names = []
- cur = names.find { |x| x.name == tup.name }
- if !cur
- names << tup
- elsif cur.version < tup.version
- names.delete cur
- names << tup
- end
+ @specs = {}
+
+ Dir["*.gem"].each do |file|
+ begin
+ pkg = Gem::Package.new(file)
+ rescue SystemCallError, Gem::Package::FormatError
+ # ignore
else
- names << pkg.spec.name_tuple
+ tup = pkg.spec.name_tuple
+ @specs[tup] = [File.expand_path(file), pkg]
+
+ case type
+ when :released
+ unless pkg.spec.version.prerelease?
+ names << pkg.spec.name_tuple
+ end
+ when :prerelease
+ if pkg.spec.version.prerelease?
+ names << pkg.spec.name_tuple
+ end
+ when :latest
+ tup = pkg.spec.name_tuple
+
+ cur = names.find { |x| x.name == tup.name }
+ if !cur
+ names << tup
+ elsif cur.version < tup.version
+ names.delete cur
+ names << tup
+ end
+ else
+ names << pkg.spec.name_tuple
+ end
end
end
- end
- names
+ names
+ end
end
def find_gem gem_name, version = Gem::Requirement.default, # :nodoc:
@@ -88,7 +91,7 @@ class Gem::Source::Local < Gem::Source
if version.satisfied_by?(s.version)
if prerelease
found << s
- elsif !s.version.prerelease?
+ elsif !s.version.prerelease? || version.prerelease?
found << s
end
end
diff --git a/lib/rubygems/source/lock.rb b/lib/rubygems/source/lock.rb
index 86b16e964c6..59717be2c0b 100644
--- a/lib/rubygems/source/lock.rb
+++ b/lib/rubygems/source/lock.rb
@@ -34,6 +34,10 @@ class Gem::Source::Lock < Gem::Source
0 == (self <=> other)
end
+ def hash # :nodoc:
+ @wrapped.hash ^ 3
+ end
+
##
# Delegates to the wrapped source's fetch_spec method.
@@ -46,4 +50,3 @@ class Gem::Source::Lock < Gem::Source
end
end
-
diff --git a/lib/rubygems/source_local.rb b/lib/rubygems/source_local.rb
index 07cb9e6e8f1..9869158e7d7 100644
--- a/lib/rubygems/source_local.rb
+++ b/lib/rubygems/source_local.rb
@@ -2,5 +2,7 @@
require 'rubygems/source'
require 'rubygems/source_local'
-# TODO warn upon require, this file is deprecated.
+unless Gem::Deprecate.skip
+ Kernel.warn "#{Gem.location_of_caller(3).join(':')}: Warning: Requiring rubygems/source_local is deprecated; please use rubygems/source/local instead."
+end
diff --git a/lib/rubygems/source_specific_file.rb b/lib/rubygems/source_specific_file.rb
index d42e6e74407..b676b1d3a2c 100644
--- a/lib/rubygems/source_specific_file.rb
+++ b/lib/rubygems/source_specific_file.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: true
require 'rubygems/source/specific_file'
-# TODO warn upon require, this file is deprecated.
-
+unless Gem::Deprecate.skip
+ Kernel.warn "#{Gem.location_of_caller(3).join(':')}: Warning: Requiring rubygems/source_specific_file is deprecated; please use rubygems/source/specific_file instead."
+end
diff --git a/lib/rubygems/spec_fetcher.rb b/lib/rubygems/spec_fetcher.rb
index 755d4be1eb1..919276e1132 100644
--- a/lib/rubygems/spec_fetcher.rb
+++ b/lib/rubygems/spec_fetcher.rb
@@ -184,10 +184,10 @@ class Gem::SpecFetcher
# Suggests gems based on the supplied +gem_name+. Returns an array of
# alternative gem names.
- def suggest_gems_from_name gem_name
+ def suggest_gems_from_name(gem_name, type = :latest)
gem_name = gem_name.downcase.tr('_-', '')
max = gem_name.size / 2
- names = available_specs(:latest).first.values.flatten(1)
+ names = available_specs(type).first.values.flatten(1)
matches = names.map { |n|
next unless n.match_platform?
@@ -201,7 +201,11 @@ class Gem::SpecFetcher
[n.name, distance]
}.compact
- matches = matches.uniq.sort_by { |name, dist| dist }
+ matches = if matches.empty? && type != :prerelease
+ suggest_gems_from_name gem_name, :prerelease
+ else
+ matches.uniq.sort_by { |name, dist| dist }
+ end
matches.first(5).map { |name, dist| name }
end
diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb
index ea3e3ecf2bd..c811ecec0ef 100644
--- a/lib/rubygems/specification.rb
+++ b/lib/rubygems/specification.rb
@@ -30,6 +30,7 @@ require 'stringio'
# s.email = 'rubycoder@example.com'
# s.files = ["lib/example.rb"]
# s.homepage = 'https://rubygems.org/gems/example'
+# s.metadata = { "source_code_uri" => "https://github.com/example/example" }
# end
#
# Starting in RubyGems 2.0, a Specification can hold arbitrary
@@ -157,16 +158,20 @@ class Gem::Specification < Gem::BasicSpecification
:summary => nil,
:test_files => [],
:version => nil,
- }
+ }.freeze
- Dupable = { } # :nodoc:
+ INITIALIZE_CODE_FOR_DEFAULTS = { } # :nodoc:
@@default_value.each do |k,v|
- case v
- when Time, Numeric, Symbol, true, false, nil
- Dupable[k] = false
+ INITIALIZE_CODE_FOR_DEFAULTS[k] = case v
+ when [], {}, true, false, nil, Numeric, Symbol
+ v.inspect
+ when String
+ v.dump
+ when Numeric
+ "default_value(:#{k})"
else
- Dupable[k] = true
+ "default_value(:#{k}).dup"
end
end
@@ -209,43 +214,51 @@ class Gem::Specification < Gem::BasicSpecification
attr_reader :version
##
- # Paths in the gem to add to <code>$LOAD_PATH</code> when this gem is
- # activated.
- #--
- # See also #require_paths
- #++
- # If you have an extension you do not need to add <code>"ext"</code> to the
- # require path, the extension build process will copy the extension files
- # into "lib" for you.
+ # A short summary of this gem's description. Displayed in `gem list -d`.
#
- # The default value is <code>"lib"</code>
+ # The #description should be more detailed than the summary.
#
# Usage:
#
- # # If all library files are in the root directory...
- # spec.require_paths = ['.']
+ # spec.summary = "This is a small summary of my gem"
- def require_paths=(val)
- @require_paths = Array(val)
- end
+ attr_reader :summary
##
- # The version of RubyGems used to create this gem.
+ # Files included in this gem. You cannot append to this accessor, you must
+ # assign to it.
#
- # Do not set this, it is set automatically when the gem is packaged.
-
- attr_accessor :rubygems_version
-
- ##
- # A short summary of this gem's description. Displayed in `gem list -d`.
+ # Only add files you can require to this list, not directories, etc.
#
- # The #description should be more detailed than the summary.
+ # Directories are automatically stripped from this list when building a gem,
+ # other non-files cause an error.
#
# Usage:
#
- # spec.summary = "This is a small summary of my gem"
+ # require 'rake'
+ # spec.files = FileList['lib/**/*.rb',
+ # 'bin/*',
+ # '[A-Z]*',
+ # 'test/**/*'].to_a
+ #
+ # # or without Rake...
+ # spec.files = Dir['lib/**/*.rb'] + Dir['bin/*']
+ # spec.files += Dir['[A-Z]*'] + Dir['test/**/*']
+ # spec.files.reject! { |fn| fn.include? "CVS" }
- attr_reader :summary
+ def files
+ # DO NOT CHANGE TO ||= ! This is not a normal accessor. (yes, it sucks)
+ # DOC: Why isn't it normal? Why does it suck? How can we fix this?
+ @files = [@files,
+ @test_files,
+ add_bindir(@executables),
+ @extra_rdoc_files,
+ @extensions,
+ ].flatten.compact.uniq.sort
+ end
+
+ ######################################################################
+ # :section: Recommended gemspec attributes
##
# Singular writer for #authors
@@ -270,6 +283,148 @@ class Gem::Specification < Gem::BasicSpecification
end
##
+ # A long description of this gem
+ #
+ # The description should be more detailed than the summary but not
+ # excessively long. A few paragraphs is a recommended length with no
+ # examples or formatting.
+ #
+ # Usage:
+ #
+ # spec.description = <<-EOF
+ # Rake is a Make-like program implemented in Ruby. Tasks and
+ # dependencies are specified in standard Ruby syntax.
+ # EOF
+
+ attr_reader :description
+
+ ##
+ # A contact email address (or addresses) for this gem
+ #
+ # Usage:
+ #
+ # spec.email = 'john.jones@example.com'
+ # spec.email = ['jack@example.com', 'jill@example.com']
+
+ attr_accessor :email
+
+ ##
+ # The URL of this gem's home page
+ #
+ # Usage:
+ #
+ # spec.homepage = 'https://github.com/ruby/rake'
+
+ attr_accessor :homepage
+
+ ##
+ # The license for this gem.
+ #
+ # The license must be no more than 64 characters.
+ #
+ # This should just be the name of your license. The full text of the license
+ # should be inside of the gem (at the top level) when you build it.
+ #
+ # The simplest way, is to specify the standard SPDX ID
+ # https://spdx.org/licenses/ for the license.
+ # Ideally you should pick one that is OSI (Open Source Initiative)
+ # http://opensource.org/licenses/alphabetical approved.
+ #
+ # The most commonly used OSI approved licenses are MIT and Apache-2.0.
+ # GitHub also provides a license picker at http://choosealicense.com/.
+ #
+ # You should specify a license for your gem so that people know how they are
+ # permitted to use it, and any restrictions you're placing on it. Not
+ # specifying a license means all rights are reserved; others have no rights
+ # to use the code for any purpose.
+ #
+ # You can set multiple licenses with #licenses=
+ #
+ # Usage:
+ # spec.license = 'MIT'
+
+ def license=o
+ self.licenses = [o]
+ end
+
+ ##
+ # The license(s) for the library.
+ #
+ # Each license must be a short name, no more than 64 characters.
+ #
+ # This should just be the name of your license. The full
+ # text of the license should be inside of the gem when you build it.
+ #
+ # See #license= for more discussion
+ #
+ # Usage:
+ # spec.licenses = ['MIT', 'GPL-2.0']
+
+ def licenses= licenses
+ @licenses = Array licenses
+ end
+
+ ##
+ # The metadata holds extra data for this gem that may be useful to other
+ # consumers and is settable by gem authors without requiring an update to
+ # the rubygems software.
+ #
+ # Metadata items have the following restrictions:
+ #
+ # * The metadata must be a Hash object
+ # * All keys and values must be Strings
+ # * Keys can be a maximum of 128 bytes and values can be a maximum of 1024
+ # bytes
+ # * All strings must be UTF-8, no binary data is allowed
+ #
+ # You can use metadata to specify links to your gem's homepage, codebase,
+ # documentation, wiki, mailing list, issue tracker and changelog.
+ #
+ # s.metadata = {
+ # "bug_tracker_uri" => "https://example.com/user/bestgemever/issues",
+ # "changelog_uri" => "https://example.com/user/bestgemever/CHANGELOG.md",
+ # "documentation_uri" => "https://www.example.info/gems/bestgemever/0.0.1",
+ # "homepage_uri" => "https://bestgemever.example.io",
+ # "mailing_list_uri" => "https://groups.example.com/bestgemever",
+ # "source_code_uri" => "https://example.com/user/bestgemever",
+ # "wiki_uri" => "https://example.com/user/bestgemever/wiki"
+ # }
+ #
+ # These links will be used on your gem's page on rubygems.org and must pass
+ # validation against following regex.
+ #
+ # %r{\Ahttps?:\/\/([^\s:@]+:[^\s:@]*@)?[A-Za-z\d\-]+(\.[A-Za-z\d\-]+)+\.?(:\d{1,5})?([\/?]\S*)?\z}
+
+ attr_accessor :metadata
+
+ ######################################################################
+ # :section: Optional gemspec attributes
+
+ ##
+ # The path in the gem for executable scripts. Usually 'bin'
+ #
+ # Usage:
+ #
+ # spec.bindir = 'bin'
+
+ attr_accessor :bindir
+
+ ##
+ # The certificate chain used to sign this gem. See Gem::Security for
+ # details.
+
+ attr_accessor :cert_chain
+
+ ##
+ # A message that gets displayed after the gem is installed.
+ #
+ # Usage:
+ #
+ # spec.post_install_message = "Thanks for installing!"
+
+ attr_accessor :post_install_message
+
+ ##
# The platform this gem runs on.
#
# This is usually Gem::Platform::RUBY or Gem::Platform::CURRENT.
@@ -327,104 +482,26 @@ class Gem::Specification < Gem::BasicSpecification
end
##
- # Files included in this gem. You cannot append to this accessor, you must
- # assign to it.
- #
- # Only add files you can require to this list, not directories, etc.
+ # Paths in the gem to add to <code>$LOAD_PATH</code> when this gem is
+ # activated.
+ #--
+ # See also #require_paths
+ #++
+ # If you have an extension you do not need to add <code>"ext"</code> to the
+ # require path, the extension build process will copy the extension files
+ # into "lib" for you.
#
- # Directories are automatically stripped from this list when building a gem,
- # other non-files cause an error.
+ # The default value is <code>"lib"</code>
#
# Usage:
#
- # require 'rake'
- # spec.files = FileList['lib/**/*.rb',
- # 'bin/*',
- # '[A-Z]*',
- # 'test/**/*'].to_a
- #
- # # or without Rake...
- # spec.files = Dir['lib/**/*.rb'] + Dir['bin/*']
- # spec.files += Dir['[A-Z]*'] + Dir['test/**/*']
- # spec.files.reject! { |fn| fn.include? "CVS" }
+ # # If all library files are in the root directory...
+ # spec.require_paths = ['.']
- def files
- # DO NOT CHANGE TO ||= ! This is not a normal accessor. (yes, it sucks)
- # DOC: Why isn't it normal? Why does it suck? How can we fix this?
- @files = [@files,
- @test_files,
- add_bindir(@executables),
- @extra_rdoc_files,
- @extensions,
- ].flatten.compact.uniq.sort
+ def require_paths=(val)
+ @require_paths = Array(val)
end
- ######################################################################
- # :section: Optional gemspec attributes
-
- ##
- # The path in the gem for executable scripts. Usually 'bin'
- #
- # Usage:
- #
- # spec.bindir = 'bin'
-
- attr_accessor :bindir
-
- ##
- # The certificate chain used to sign this gem. See Gem::Security for
- # details.
-
- attr_accessor :cert_chain
-
- ##
- # A long description of this gem
- #
- # The description should be more detailed than the summary but not
- # excessively long. A few paragraphs is a recommended length with no
- # examples or formatting.
- #
- # Usage:
- #
- # spec.description = <<-EOF
- # Rake is a Make-like program implemented in Ruby. Tasks and
- # dependencies are specified in standard Ruby syntax.
- # EOF
-
- attr_reader :description
-
- ##
- # :category: Recommended gemspec attributes
- #
- # A contact email address (or addresses) for this gem
- #
- # Usage:
- #
- # spec.email = 'john.jones@example.com'
- # spec.email = ['jack@example.com', 'jill@example.com']
-
- attr_accessor :email
-
- ##
- # :category: Recommended gemspec attributes
- #
- # The URL of this gem's home page
- #
- # Usage:
- #
- # spec.homepage = 'https://github.com/ruby/rake'
-
- attr_accessor :homepage
-
- ##
- # A message that gets displayed after the gem is installed.
- #
- # Usage:
- #
- # spec.post_install_message = "Thanks for installing!"
-
- attr_accessor :post_install_message
-
##
# The version of Ruby required by this gem
@@ -436,30 +513,16 @@ class Gem::Specification < Gem::BasicSpecification
attr_reader :required_rubygems_version
##
- # The key used to sign this gem. See Gem::Security for details.
+ # The version of RubyGems used to create this gem.
+ #
+ # Do not set this, it is set automatically when the gem is packaged.
- attr_accessor :signing_key
+ attr_accessor :rubygems_version
##
- # :attr_accessor: metadata
- #
- # The metadata holds extra data for this gem that may be useful to other
- # consumers and is settable by gem authors without requiring an update to
- # the rubygems software.
- #
- # Metadata items have the following restrictions:
- #
- # * The metadata must be a Hash object
- # * All keys and values must be Strings
- # * Keys can be a maximum of 128 bytes and values can be a maximum of 1024
- # bytes
- # * All strings must be UTF-8, no binary data is allowed
- #
- # To add metadata for the location of a issue tracker:
- #
- # s.metadata = { "issue_tracker" => "https://example/issues" }
+ # The key used to sign this gem. See Gem::Security for details.
- attr_accessor :metadata
+ attr_accessor :signing_key
##
# Adds a development dependency named +gem+ with +requirements+ to this
@@ -473,7 +536,7 @@ class Gem::Specification < Gem::BasicSpecification
# activated when a gem is required.
def add_development_dependency(gem, *requirements)
- add_dependency_with_type(gem, :development, *requirements)
+ add_dependency_with_type(gem, :development, requirements)
end
##
@@ -484,7 +547,7 @@ class Gem::Specification < Gem::BasicSpecification
# spec.add_runtime_dependency 'example', '~> 1.1', '>= 1.1.4'
def add_runtime_dependency(gem, *requirements)
- add_dependency_with_type(gem, :runtime, *requirements)
+ add_dependency_with_type(gem, :runtime, requirements)
end
##
@@ -557,56 +620,6 @@ class Gem::Specification < Gem::BasicSpecification
end
##
- # :category: Recommended gemspec attributes
- #
- # The license for this gem.
- #
- # The license must be no more than 64 characters.
- #
- # This should just be the name of your license. The full text of the license
- # should be inside of the gem (at the top level) when you build it.
- #
- # The simplest way, is to specify the standard SPDX ID
- # https://spdx.org/licenses/ for the license.
- # Ideally you should pick one that is OSI (Open Source Initiative)
- # http://opensource.org/licenses/alphabetical approved.
- #
- # The most commonly used OSI approved licenses are MIT and Apache-2.0.
- # GitHub also provides a license picker at http://choosealicense.com/.
- #
- # You should specify a license for your gem so that people know how they are
- # permitted to use it, and any restrictions you're placing on it. Not
- # specifying a license means all rights are reserved; others have no rights
- # to use the code for any purpose.
- #
- # You can set multiple licenses with #licenses=
- #
- # Usage:
- # spec.license = 'MIT'
-
- def license=o
- self.licenses = [o]
- end
-
- ##
- # :category: Recommended gemspec attributes
- # The license(s) for the library.
- #
- # Each license must be a short name, no more than 64 characters.
- #
- # This should just be the name of your license. The full
- # text of the license should be inside of the gem when you build it.
- #
- # See #license= for more discussion
- #
- # Usage:
- # spec.licenses = ['MIT', 'GPL-2.0']
-
- def licenses= licenses
- @licenses = Array licenses
- end
-
- ##
# Specifies the rdoc options to be used when generating API documentation.
#
# Usage:
@@ -885,7 +898,7 @@ class Gem::Specification < Gem::BasicSpecification
# properly sorted.
def self.add_spec spec
- warn "Gem::Specification.add_spec is deprecated and will be removed in Rubygems 3.0" unless Gem::Deprecate.skip
+ warn "Gem::Specification.add_spec is deprecated and will be removed in RubyGems 3.0" unless Gem::Deprecate.skip
# TODO: find all extraneous adds
# puts
# p :add_spec => [spec.full_name, caller.reject { |s| s =~ /minitest/ }]
@@ -910,7 +923,7 @@ class Gem::Specification < Gem::BasicSpecification
# Adds multiple specs to the known specifications.
def self.add_specs *specs
- warn "Gem::Specification.add_specs is deprecated and will be removed in Rubygems 3.0" unless Gem::Deprecate.skip
+ warn "Gem::Specification.add_specs is deprecated and will be removed in RubyGems 3.0" unless Gem::Deprecate.skip
raise "nil spec!" if specs.any?(&:nil?) # TODO: remove once we're happy
@@ -1023,6 +1036,13 @@ class Gem::Specification < Gem::BasicSpecification
end
##
+ # Returns every spec that has the given +full_name+
+
+ def self.find_all_by_full_name(full_name)
+ stubs.select {|s| s.full_name == full_name }.map(&:to_spec)
+ end
+
+ ##
# Find the best specification matching a +name+ and +requirements+. Raises
# if the dependency doesn't resolve to a valid specification.
@@ -1040,6 +1060,7 @@ class Gem::Specification < Gem::BasicSpecification
def self.find_by_path path
path = path.dup.freeze
spec = @@spec_with_requirable_file[path] ||= (stubs.find { |s|
+ next unless Gem::BundlerVersionFinder.compatible?(s)
s.contains_requirable_file? path
} || NOT_FOUND)
spec.to_spec
@@ -1051,7 +1072,9 @@ class Gem::Specification < Gem::BasicSpecification
def self.find_inactive_by_path path
stub = stubs.find { |s|
- s.contains_requirable_file? path unless s.activated?
+ next if s.activated?
+ next unless Gem::BundlerVersionFinder.compatible?(s)
+ s.contains_requirable_file? path
}
stub && stub.to_spec
end
@@ -1250,7 +1273,7 @@ class Gem::Specification < Gem::BasicSpecification
# Removes +spec+ from the known specs.
def self.remove_spec spec
- warn "Gem::Specification.remove_spec is deprecated and will be removed in Rubygems 3.0" unless Gem::Deprecate.skip
+ warn "Gem::Specification.remove_spec is deprecated and will be removed in RubyGems 3.0" unless Gem::Deprecate.skip
_all.delete spec
stubs.delete_if { |s| s.full_name == spec.full_name }
(@@stubs_by_name[spec.name] || []).delete_if { |s| s.full_name == spec.full_name }
@@ -1520,7 +1543,7 @@ class Gem::Specification < Gem::BasicSpecification
# +requirements+. Valid types are currently <tt>:runtime</tt> and
# <tt>:development</tt>.
- def add_dependency_with_type(dependency, type, *requirements)
+ def add_dependency_with_type(dependency, type, requirements)
requirements = if requirements.empty? then
Gem::Requirement.default
else
@@ -2026,6 +2049,20 @@ class Gem::Specification < Gem::BasicSpecification
yaml_initialize coder.tag, coder.map
end
+
+
+ eval <<-RB, binding, __FILE__, __LINE__ + 1
+ def set_nil_attributes_to_nil
+ #{@@nil_attributes.map {|key| "@#{key} = nil" }.join "; "}
+ end
+ private :set_nil_attributes_to_nil
+
+ def set_not_nil_attributes_to_default_values
+ #{@@non_nil_attributes.map {|key| "@#{key} = #{INITIALIZE_CODE_FOR_DEFAULTS[key]}" }.join ";"}
+ end
+ private :set_not_nil_attributes_to_default_values
+ RB
+
##
# Specification constructor. Assigns the default values to the attributes
# and yields itself for further initialization. Optionally takes +name+ and
@@ -2041,15 +2078,8 @@ class Gem::Specification < Gem::BasicSpecification
@original_platform = nil
@installed_by_version = nil
- @@nil_attributes.each do |key|
- instance_variable_set "@#{key}", nil
- end
-
- @@non_nil_attributes.each do |key|
- default = default_value(key)
- value = Dupable[key] ? default.dup : default
- instance_variable_set "@#{key}", value
- end
+ set_nil_attributes_to_nil
+ set_not_nil_attributes_to_default_values
@new_platform = Gem::Platform::RUBY
@@ -2746,29 +2776,7 @@ class Gem::Specification < Gem::BasicSpecification
'metadata must be a hash'
end
- metadata.keys.each do |k|
- if !k.kind_of?(String)
- raise Gem::InvalidSpecificationException,
- 'metadata keys must be a String'
- end
-
- if k.size > 128
- raise Gem::InvalidSpecificationException,
- "metadata key too large (#{k.size} > 128)"
- end
- end
-
- metadata.values.each do |k|
- if !k.kind_of?(String)
- raise Gem::InvalidSpecificationException,
- 'metadata values must be a String'
- end
-
- if k.size > 1024
- raise Gem::InvalidSpecificationException,
- "metadata value too large (#{k.size} > 1024)"
- end
- end
+ validate_metadata
licenses.each { |license|
if license.length > 64
@@ -2822,7 +2830,7 @@ http://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard li
# Warnings
- %w[author email homepage summary].each do |attribute|
+ %w[author homepage summary files].each do |attribute|
value = self.send attribute
warning "no #{attribute} specified" if value.nil? or value.empty?
end
@@ -2855,6 +2863,48 @@ http://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard li
end
end
+ def validate_metadata
+ url_validation_regex = %r{\Ahttps?:\/\/([^\s:@]+:[^\s:@]*@)?[A-Za-z\d\-]+(\.[A-Za-z\d\-]+)+\.?(:\d{1,5})?([\/?]\S*)?\z}
+ link_keys = %w(
+ bug_tracker_uri
+ changelog_uri
+ documentation_uri
+ homepage_uri
+ mailing_list_uri
+ source_code_uri
+ wiki_uri
+ )
+
+ metadata.each do|key, value|
+ if !key.kind_of?(String)
+ raise Gem::InvalidSpecificationException,
+ "metadata keys must be a String"
+ end
+
+ if key.size > 128
+ raise Gem::InvalidSpecificationException,
+ "metadata key too large (#{key.size} > 128)"
+ end
+
+ if !value.kind_of?(String)
+ raise Gem::InvalidSpecificationException,
+ "metadata values must be a String"
+ end
+
+ if value.size > 1024
+ raise Gem::InvalidSpecificationException,
+ "metadata value too large (#{value.size} > 1024)"
+ end
+
+ if link_keys.include? key
+ if value !~ url_validation_regex
+ raise Gem::InvalidSpecificationException,
+ "metadata['#{key}'] has invalid link: #{value.inspect}"
+ end
+ end
+ end
+ end
+
##
# Checks that dependencies use requirements as we recommend. Warnings are
# issued when dependencies are open-ended or overly strict for semantic
diff --git a/lib/rubygems/stub_specification.rb b/lib/rubygems/stub_specification.rb
index b741843124a..8337375ab4e 100644
--- a/lib/rubygems/stub_specification.rb
+++ b/lib/rubygems/stub_specification.rb
@@ -188,9 +188,8 @@ class Gem::StubSpecification < Gem::BasicSpecification
def to_spec
@spec ||= if @data then
- Gem.loaded_specs.values.find { |spec|
- spec.name == name and spec.version == version
- }
+ loaded = Gem.loaded_specs[name]
+ loaded if loaded && loaded.version == version
end
@spec ||= Gem::Specification.load(loaded_from)
diff --git a/lib/rubygems/test_case.rb b/lib/rubygems/test_case.rb
index fee6875ef02..efc78edea25 100644
--- a/lib/rubygems/test_case.rb
+++ b/lib/rubygems/test_case.rb
@@ -25,6 +25,7 @@ unless Gem::Dependency.new('rdoc', '>= 3.10').matching_specs.empty?
gem 'json'
end
+require 'bundler'
require 'minitest/autorun'
require 'rubygems/deprecate'
@@ -222,6 +223,7 @@ class Gem::TestCase < MiniTest::Unit::TestCase
@orig_gem_vendor = ENV['GEM_VENDOR']
@orig_gem_spec_cache = ENV['GEM_SPEC_CACHE']
@orig_rubygems_gemdeps = ENV['RUBYGEMS_GEMDEPS']
+ @orig_bundle_gemfile = ENV['BUNDLE_GEMFILE']
@orig_rubygems_host = ENV['RUBYGEMS_HOST']
ENV.keys.find_all { |k| k.start_with?('GEM_REQUIREMENT_') }.each do |k|
ENV.delete k
@@ -232,7 +234,12 @@ class Gem::TestCase < MiniTest::Unit::TestCase
@current_dir = Dir.pwd
@fetcher = nil
- @ui = Gem::MockGemUi.new
+
+ Bundler.ui = Bundler::UI::Silent.new
+ @ui = Gem::MockGemUi.new
+ # This needs to be a new instance since we call use_ui(@ui) when we want to
+ # capture output
+ Gem::DefaultUserInteraction.ui = Gem::MockGemUi.new
tmpdir = File.expand_path Dir.tmpdir
tmpdir.untaint
@@ -323,6 +330,7 @@ class Gem::TestCase < MiniTest::Unit::TestCase
Gem.loaded_specs.clear
Gem.clear_default_specs
Gem::Specification.unresolved_deps.clear
+ Bundler.reset!
Gem.configuration.verbose = true
Gem.configuration.update_sources = true
@@ -394,6 +402,7 @@ class Gem::TestCase < MiniTest::Unit::TestCase
ENV['GEM_VENDOR'] = @orig_gem_vendor
ENV['GEM_SPEC_CACHE'] = @orig_gem_spec_cache
ENV['RUBYGEMS_GEMDEPS'] = @orig_rubygems_gemdeps
+ ENV['BUNDLE_GEMFILE'] = @orig_bundle_gemfile
ENV['RUBYGEMS_HOST'] = @orig_rubygems_host
Gem.ruby = @orig_ruby if @orig_ruby
diff --git a/lib/rubygems/user_interaction.rb b/lib/rubygems/user_interaction.rb
index 390d0f2aea7..cacd782e080 100644
--- a/lib/rubygems/user_interaction.rb
+++ b/lib/rubygems/user_interaction.rb
@@ -7,11 +7,6 @@
require 'rubygems/util'
-begin
- require 'io/console'
-rescue LoadError
-end
-
##
# Module that defines the default UserInteraction. Any class including this
# module will have access to the +ui+ method that returns the default UI.
@@ -314,12 +309,21 @@ class Gem::StreamUI
password
end
- if IO.method_defined?(:noecho) then
- def _gets_noecho
- @ins.noecho {@ins.gets}
+ def require_io_console
+ @require_io_console ||= begin
+ begin
+ require 'io/console'
+ rescue LoadError
+ end
+ true
end
- elsif Gem.win_platform?
- def _gets_noecho
+ end
+
+ def _gets_noecho
+ require_io_console
+ if IO.method_defined?(:noecho) then
+ @ins.noecho {@ins.gets}
+ elsif Gem.win_platform?
require "Win32API"
password = ''
@@ -332,9 +336,7 @@ class Gem::StreamUI
end
end
password
- end
- else
- def _gets_noecho
+ else
system "stty -echo"
begin
@ins.gets
diff --git a/lib/rubygems/util.rb b/lib/rubygems/util.rb
index ead2babc1f0..2de45c900b6 100644
--- a/lib/rubygems/util.rb
+++ b/lib/rubygems/util.rb
@@ -109,26 +109,15 @@ module Gem::Util
##
# Enumerates the parents of +directory+.
- def self.traverse_parents directory
+ def self.traverse_parents directory, &block
return enum_for __method__, directory unless block_given?
here = File.expand_path directory
- start = here
-
- Dir.chdir start
-
- begin
- loop do
- yield here
-
- Dir.chdir '..'
-
- return if Dir.pwd == here # toplevel
-
- here = Dir.pwd
- end
- ensure
- Dir.chdir start
+ loop do
+ Dir.chdir here, &block
+ new_here = File.expand_path('..', here)
+ return if new_here == here # toplevel
+ here = new_here
end
end
diff --git a/lib/rubygems/version.rb b/lib/rubygems/version.rb
index 17dd7b07953..ff5c1c1e72f 100644
--- a/lib/rubygems/version.rb
+++ b/lib/rubygems/version.rb
@@ -170,7 +170,7 @@ class Gem::Version
# True if the +version+ string matches RubyGems' requirements.
def self.correct? version
- version.to_s =~ ANCHORED_VERSION_PATTERN
+ !!(version.to_s =~ ANCHORED_VERSION_PATTERN)
end
##
@@ -241,7 +241,7 @@ class Gem::Version
end
def hash # :nodoc:
- @version.hash
+ canonical_segments.hash
end
def init_with coder # :nodoc:
@@ -335,7 +335,7 @@ class Gem::Version
def <=> other
return unless Gem::Version === other
- return 0 if @version == other._version
+ return 0 if @version == other._version || canonical_segments == other.canonical_segments
lhsegments = _segments
rhsegments = other._segments
@@ -360,6 +360,13 @@ class Gem::Version
return 0
end
+ def canonical_segments
+ @canonical_segments ||=
+ _split_segments.map! do |segments|
+ segments.reverse_each.drop_while {|s| s == 0 }.reverse
+ end.reduce(&:concat)
+ end
+
protected
def _version
@@ -375,4 +382,11 @@ class Gem::Version
/^\d+$/ =~ s ? s.to_i : s
end.freeze
end
+
+ def _split_segments
+ string_start = _segments.index {|s| s.is_a?(String) }
+ string_segments = segments
+ numeric_segments = string_segments.slice!(0, string_start || string_segments.size)
+ return numeric_segments, string_segments
+ end
end
diff --git a/lib/rubygems/version_option.rb b/lib/rubygems/version_option.rb
index 3209d95f0f5..c4d3306652f 100644
--- a/lib/rubygems/version_option.rb
+++ b/lib/rubygems/version_option.rb
@@ -58,7 +58,12 @@ module Gem::VersionOption
add_option('-v', '--version VERSION', Gem::Requirement,
"Specify version of gem to #{task}", *wrap) do
|value, options|
- options[:version] = value
+ # Allow handling for multiple --version operators
+ if options[:version] && !options[:version].none?
+ options[:version].concat([value])
+ else
+ options[:version] = value
+ end
explicit_prerelease_set = !options[:explicit_prerelease].nil?
options[:explicit_prerelease] = false unless explicit_prerelease_set