summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorhsbt <hsbt@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-08-27 10:05:04 +0000
committerhsbt <hsbt@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-08-27 10:05:04 +0000
commit85d461456c154d7b4a72b20369e0d65d7880ce02 (patch)
tree21e1be2c786c8040a426841768e046fb4dc365b1 /lib
parent3a83ba90c35833bab757998def36cfe872dec461 (diff)
Merge master branch from rubygems upstream.
* It's preparation to release RubyGems 3.0.0.beta2 and Ruby 2.6.0 preview 3. * https://github.com/rubygems/rubygems/compare/v3.0.0.beta1...fad2eb15a282b19dfcb4b48bc95b8b39ebb4511f git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@64555 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib')
-rw-r--r--lib/rubygems.rb7
-rw-r--r--lib/rubygems/basic_specification.rb4
-rw-r--r--lib/rubygems/command_manager.rb11
-rw-r--r--lib/rubygems/commands/build_command.rb6
-rw-r--r--lib/rubygems/commands/cert_command.rb2
-rw-r--r--lib/rubygems/commands/cleanup_command.rb10
-rw-r--r--lib/rubygems/commands/install_command.rb7
-rw-r--r--lib/rubygems/commands/open_command.rb6
-rw-r--r--lib/rubygems/commands/pristine_command.rb9
-rw-r--r--lib/rubygems/commands/push_command.rb39
-rw-r--r--lib/rubygems/commands/setup_command.rb6
-rw-r--r--lib/rubygems/commands/signin_command.rb2
-rw-r--r--lib/rubygems/commands/uninstall_command.rb2
-rw-r--r--lib/rubygems/dependency_installer.rb2
-rw-r--r--lib/rubygems/ext/builder.rb1
-rw-r--r--lib/rubygems/indexer.rb2
-rw-r--r--lib/rubygems/install_update_options.rb2
-rw-r--r--lib/rubygems/installer.rb35
-rw-r--r--lib/rubygems/package.rb10
-rw-r--r--lib/rubygems/package/tar_reader/entry.rb20
-rw-r--r--lib/rubygems/path_support.rb16
-rw-r--r--lib/rubygems/remote_fetcher.rb25
-rw-r--r--lib/rubygems/request.rb1
-rw-r--r--lib/rubygems/request/connection_pools.rb1
-rw-r--r--lib/rubygems/request_set.rb2
-rw-r--r--lib/rubygems/requirement.rb2
-rw-r--r--lib/rubygems/security/signer.rb41
-rw-r--r--lib/rubygems/spec_fetcher.rb2
-rw-r--r--lib/rubygems/specification.rb41
-rw-r--r--lib/rubygems/specification_policy.rb132
-rw-r--r--lib/rubygems/test_case.rb18
-rw-r--r--lib/rubygems/util.rb14
-rw-r--r--lib/rubygems/version.rb9
33 files changed, 329 insertions, 158 deletions
diff --git a/lib/rubygems.rb b/lib/rubygems.rb
index d263f29dd2..858d910610 100644
--- a/lib/rubygems.rb
+++ b/lib/rubygems.rb
@@ -7,7 +7,6 @@
#++
require 'rbconfig'
-require 'thread'
module Gem
VERSION = "3.0.0.beta1"
@@ -526,8 +525,9 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
end
def self.find_files_from_load_path glob # :nodoc:
+ glob_with_suffixes = "#{glob}#{Gem.suffix_pattern}"
$LOAD_PATH.map { |load_path|
- Dir["#{File.expand_path glob, load_path}#{Gem.suffix_pattern}"]
+ Gem::Util.glob_files_in_dir(glob_with_suffixes, load_path)
}.flatten.select { |file| File.file? file.untaint }
end
@@ -1119,8 +1119,9 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
path = "rubygems_plugin"
files = []
+ glob = "#{path}#{Gem.suffix_pattern}"
$LOAD_PATH.each do |load_path|
- globbed = Dir["#{File.expand_path path, load_path}#{Gem.suffix_pattern}"]
+ globbed = Gem::Util.glob_files_in_dir(glob, load_path)
globbed.each do |load_path_file|
files << load_path_file if File.file?(load_path_file.untaint)
diff --git a/lib/rubygems/basic_specification.rb b/lib/rubygems/basic_specification.rb
index 72954a7863..54242983ff 100644
--- a/lib/rubygems/basic_specification.rb
+++ b/lib/rubygems/basic_specification.rb
@@ -152,7 +152,7 @@ class Gem::BasicSpecification
# The path to the data directory for this gem.
def datadir
-# TODO: drop the extra ", gem_name" which is uselessly redundant
+ # TODO: drop the extra ", gem_name" which is uselessly redundant
File.expand_path(File.join(gems_dir, full_name, "data", name)).untaint
end
@@ -282,7 +282,7 @@ class Gem::BasicSpecification
self.raw_require_paths.first
end
else
- "lib" # default value for require_paths for bundler/inline
+ "lib" # default value for require_paths for bundler/inline
end
"#{self.full_gem_path}/#{dirs}".dup.untaint
diff --git a/lib/rubygems/command_manager.rb b/lib/rubygems/command_manager.rb
index e22dc5deb3..3dc5779c91 100644
--- a/lib/rubygems/command_manager.rb
+++ b/lib/rubygems/command_manager.rb
@@ -71,6 +71,10 @@ class Gem::CommandManager
:yank,
]
+ ALIAS_COMMANDS = {
+ 'i' => 'install'
+ }
+
##
# Return the authoritative instance of the command manager.
@@ -174,6 +178,8 @@ class Gem::CommandManager
end
def find_command(cmd_name)
+ cmd_name = find_alias_command cmd_name
+
possibilities = find_command_possibilities cmd_name
if possibilities.size > 1 then
@@ -186,6 +192,11 @@ class Gem::CommandManager
self[possibilities.first]
end
+ def find_alias_command(cmd_name)
+ alias_name = ALIAS_COMMANDS[cmd_name]
+ alias_name ? alias_name : cmd_name
+ end
+
def find_command_possibilities(cmd_name)
len = cmd_name.length
diff --git a/lib/rubygems/commands/build_command.rb b/lib/rubygems/commands/build_command.rb
index 0ba24e5ea3..f1d700349f 100644
--- a/lib/rubygems/commands/build_command.rb
+++ b/lib/rubygems/commands/build_command.rb
@@ -10,6 +10,10 @@ class Gem::Commands::BuildCommand < Gem::Command
add_option '--force', 'skip validation of the spec' do |value, options|
options[:force] = true
end
+
+ add_option '--strict', 'consider warnings as errors when validating the spec' do |value, options|
+ options[:strict] = true
+ end
end
def arguments # :nodoc:
@@ -51,7 +55,7 @@ with gem spec:
spec = Gem::Specification.load File.basename(gemspec)
if spec then
- Gem::Package.build spec, options[:force]
+ Gem::Package.build spec, options[:force], options[:strict]
else
alert_error "Error loading gemspec. Aborting."
terminate_interaction 1
diff --git a/lib/rubygems/commands/cert_command.rb b/lib/rubygems/commands/cert_command.rb
index 5542262a50..aa26f340ff 100644
--- a/lib/rubygems/commands/cert_command.rb
+++ b/lib/rubygems/commands/cert_command.rb
@@ -87,7 +87,7 @@ class Gem::Commands::CertCommand < Gem::Command
add_option('-d', '--days NUMBER_OF_DAYS',
'Days before the certificate expires') do |days, options|
- options[:expiration_length_days] = days.to_i
+ options[:expiration_length_days] = days.to_i
end
end
diff --git a/lib/rubygems/commands/cleanup_command.rb b/lib/rubygems/commands/cleanup_command.rb
index 79c23c840d..fe85deddda 100644
--- a/lib/rubygems/commands/cleanup_command.rb
+++ b/lib/rubygems/commands/cleanup_command.rb
@@ -22,6 +22,12 @@ class Gem::Commands::CleanupCommand < Gem::Command
options[:check_dev] = value
end
+ add_option('--[no-]user-install',
+ 'Cleanup in user\'s home directory instead',
+ 'of GEM_HOME.') do |value, options|
+ options[:user_install] = value
+ end
+
@candidate_gems = nil
@default_gems = []
@full = nil
@@ -124,8 +130,10 @@ If no gems are named all gems in GEM_HOME are cleaned.
spec.default_gem?
}
+ uninstall_from = options[:user_install] ? Gem.user_dir : @original_home
+
gems_to_cleanup = gems_to_cleanup.select { |spec|
- spec.base_dir == @original_home
+ spec.base_dir == uninstall_from
}
@default_gems += default_gems
diff --git a/lib/rubygems/commands/install_command.rb b/lib/rubygems/commands/install_command.rb
index c6abf9cd7c..9fe131c290 100644
--- a/lib/rubygems/commands/install_command.rb
+++ b/lib/rubygems/commands/install_command.rb
@@ -117,6 +117,13 @@ to write the specification by hand. For example:
some_extension_gem (1.0)
$
+Command Alias
+==========================
+
+You can use `i` command instead of `install`.
+
+ $ gem i GEMNAME
+
EOF
end
diff --git a/lib/rubygems/commands/open_command.rb b/lib/rubygems/commands/open_command.rb
index 059635e835..fdac19dc1f 100644
--- a/lib/rubygems/commands/open_command.rb
+++ b/lib/rubygems/commands/open_command.rb
@@ -60,8 +60,14 @@ class Gem::Commands::OpenCommand < Gem::Command
def open_gem name
spec = spec_for name
+
return false unless spec
+ if spec.default_gem?
+ say "'#{name}' is a default gem and can't be opened."
+ return false
+ end
+
open_editor(spec.full_gem_path)
end
diff --git a/lib/rubygems/commands/pristine_command.rb b/lib/rubygems/commands/pristine_command.rb
index 817e752266..575c344130 100644
--- a/lib/rubygems/commands/pristine_command.rb
+++ b/lib/rubygems/commands/pristine_command.rb
@@ -46,6 +46,12 @@ class Gem::Commands::PristineCommand < Gem::Command
options[:env_shebang] = value
end
+ add_option('-n', '--bindir DIR',
+ 'Directory where executables are',
+ 'located') do |value, options|
+ options[:bin_dir] = File.expand_path(value)
+ end
+
add_version_option('restore to', 'pristine condition')
end
@@ -160,12 +166,15 @@ extensions will be restored.
install_defaults.to_s['--env-shebang']
end
+ bin_dir = options[:bin_dir] if options[:bin_dir]
+
installer_options = {
:wrappers => true,
:force => true,
:install_dir => spec.base_dir,
:env_shebang => env_shebang,
:build_args => spec.build_args,
+ :bin_dir => bin_dir
}
if options[:only_executables] then
diff --git a/lib/rubygems/commands/push_command.rb b/lib/rubygems/commands/push_command.rb
index 613c4e6ddf..83c7131afc 100644
--- a/lib/rubygems/commands/push_command.rb
+++ b/lib/rubygems/commands/push_command.rb
@@ -29,6 +29,8 @@ command. For further discussion see the help for the yank command.
def initialize
super 'push', 'Push a gem up to the gem server', :host => self.host
+ @user_defined_host = false
+
add_proxy_option
add_key_option
@@ -36,20 +38,41 @@ command. For further discussion see the help for the yank command.
'Push to another gemcutter-compatible host',
' (e.g. https://rubygems.org)') do |value, options|
options[:host] = value
+ @user_defined_host = true
end
@host = nil
end
def execute
- @host = options[:host]
+ gem_name = get_one_gem_name
+ default_gem_server, push_host = get_hosts_for(gem_name)
+
+ default_host = nil
+ user_defined_host = nil
+
+ if @user_defined_host
+ user_defined_host = options[:host]
+ else
+ default_host = options[:host]
+ end
+
+ @host = if user_defined_host
+ user_defined_host
+ elsif default_gem_server
+ default_gem_server
+ elsif push_host
+ push_host
+ else
+ default_host
+ end
sign_in @host
- send_gem get_one_gem_name
+ send_gem(gem_name)
end
- def send_gem name
+ def send_gem(name)
args = [:post, "api/v1/gems"]
latest_rubygems_version = Gem.latest_rubygems_version
@@ -100,5 +123,15 @@ You can upgrade or downgrade to the latest release version with:
with_response response
end
+ private
+
+ def get_hosts_for(name)
+ gem_metadata = Gem::Package.new(name).spec.metadata
+
+ [
+ gem_metadata["default_gem_server"],
+ gem_metadata["allowed_push_host"]
+ ]
+ end
end
diff --git a/lib/rubygems/commands/setup_command.rb b/lib/rubygems/commands/setup_command.rb
index fc87063fe3..281108ea1f 100644
--- a/lib/rubygems/commands/setup_command.rb
+++ b/lib/rubygems/commands/setup_command.rb
@@ -84,7 +84,7 @@ class Gem::Commands::SetupCommand < Gem::Command
add_option '--[no-]regenerate-binstubs',
'Regenerate gem binstubs' do |value, options|
- options[:regenerate_binstubs] = value
+ options[:regenerate_binstubs] = value
end
add_option('-E', '--[no-]env-shebang',
@@ -468,8 +468,8 @@ By default, this RubyGems will install gem as:
(prefix == RbConfig::CONFIG['libdir'] or
# this one is important
prefix == File.join(RbConfig::CONFIG['libdir'], 'ruby')) then
- lib_dir = RbConfig::CONFIG[site_or_vendor]
- bin_dir = RbConfig::CONFIG['bindir']
+ lib_dir = RbConfig::CONFIG[site_or_vendor]
+ bin_dir = RbConfig::CONFIG['bindir']
else
lib_dir = File.join prefix, 'lib'
bin_dir = File.join prefix, 'bin'
diff --git a/lib/rubygems/commands/signin_command.rb b/lib/rubygems/commands/signin_command.rb
index 6556db5a89..a48c32b52e 100644
--- a/lib/rubygems/commands/signin_command.rb
+++ b/lib/rubygems/commands/signin_command.rb
@@ -10,7 +10,7 @@ class Gem::Commands::SigninCommand < Gem::Command
'It defaults to https://rubygems.org'
add_option('--host HOST', 'Push to another gemcutter-compatible host') do |value, options|
- options[:host] = value
+ options[:host] = value
end
end
diff --git a/lib/rubygems/commands/uninstall_command.rb b/lib/rubygems/commands/uninstall_command.rb
index 55a052284a..1ddc12c737 100644
--- a/lib/rubygems/commands/uninstall_command.rb
+++ b/lib/rubygems/commands/uninstall_command.rb
@@ -48,7 +48,7 @@ class Gem::Commands::UninstallCommand < Gem::Command
end
add_option('-n', '--bindir DIR',
- 'Directory to remove binaries from') do |value, options|
+ 'Directory to remove executables from') do |value, options|
options[:bin_dir] = File.expand_path(value)
end
diff --git a/lib/rubygems/dependency_installer.rb b/lib/rubygems/dependency_installer.rb
index 4b474e1d19..89ce9afe29 100644
--- a/lib/rubygems/dependency_installer.rb
+++ b/lib/rubygems/dependency_installer.rb
@@ -458,7 +458,7 @@ class Gem::DependencyInstaller
rescue Gem::Package::FormatError
end
end
- # else This is a dependency. InstallerSet handles this case
+ # else This is a dependency. InstallerSet handles this case
end
end
diff --git a/lib/rubygems/ext/builder.rb b/lib/rubygems/ext/builder.rb
index 805ef02422..6382a8f5c7 100644
--- a/lib/rubygems/ext/builder.rb
+++ b/lib/rubygems/ext/builder.rb
@@ -6,7 +6,6 @@
#++
require 'rubygems/user_interaction'
-require 'thread'
class Gem::Ext::Builder
diff --git a/lib/rubygems/indexer.rb b/lib/rubygems/indexer.rb
index 2e59e790d4..5607bf3c77 100644
--- a/lib/rubygems/indexer.rb
+++ b/lib/rubygems/indexer.rb
@@ -271,7 +271,7 @@ class Gem::Indexer
# List of gem file names to index.
def gem_file_list
- Dir[File.join(@dest_directory, "gems", '*.gem')]
+ Gem::Util.glob_files_in_dir("*.gem", File.join(@dest_directory, "gems"))
end
##
diff --git a/lib/rubygems/install_update_options.rb b/lib/rubygems/install_update_options.rb
index 190372739b..75968605f1 100644
--- a/lib/rubygems/install_update_options.rb
+++ b/lib/rubygems/install_update_options.rb
@@ -25,7 +25,7 @@ module Gem::InstallUpdateOptions
end
add_option(:"Install/Update", '-n', '--bindir DIR',
- 'Directory where binary files are',
+ 'Directory where executables are',
'located') do |value, options|
options[:bin_dir] = File.expand_path(value)
end
diff --git a/lib/rubygems/installer.rb b/lib/rubygems/installer.rb
index b142454c09..e9ad4d03d4 100644
--- a/lib/rubygems/installer.rb
+++ b/lib/rubygems/installer.rb
@@ -187,6 +187,8 @@ class Gem::Installer
@package.prog_mode = options[:prog_mode]
@package.data_mode = options[:data_mode]
+ @bin_dir = options[:bin_dir] if options[:bin_dir]
+
if options[:user_install] and not options[:unpack] then
@gem_home = Gem.user_dir
@bin_dir = Gem.bindir gem_home unless options[:bin_dir]
@@ -379,7 +381,7 @@ class Gem::Installer
@specs ||= begin
specs = []
- Dir[File.join(gem_home, "specifications", "*.gemspec")].each do |path|
+ Gem::Util.glob_files_in_dir("*.gemspec", File.join(gem_home, "specifications")).each do |path|
spec = Gem::Specification.load path.untaint
specs << spec if spec
end
@@ -769,15 +771,30 @@ TEXT
# return the stub script text used to launch the true Ruby script
def windows_stub_script(bindir, bin_file_name)
- ruby = Gem.ruby.gsub(/^\"|\"$/, "").tr(File::SEPARATOR, "\\")
- return <<-TEXT
+ # All comparisons should be case insensitive
+ if bindir.downcase == RbConfig::CONFIG["bindir"].downcase
+ # stub & ruby.exe withing same folder. Portable
+ <<-TEXT
@ECHO OFF
-IF NOT "%~f0" == "~f0" GOTO :WinNT
-@"#{ruby}" "#{File.join(bindir, bin_file_name)}" %1 %2 %3 %4 %5 %6 %7 %8 %9
-GOTO :EOF
-:WinNT
-@"#{ruby}" "%~dpn0" %*
-TEXT
+@"%~dp0ruby.exe" "%~dpn0" %*
+ TEXT
+ elsif bindir.downcase.start_with? RbConfig::TOPDIR.downcase
+ # stub within ruby folder, but not standard bin. Not portable
+ require 'pathname'
+ from = Pathname.new bindir
+ to = Pathname.new RbConfig::CONFIG["bindir"]
+ rel = to.relative_path_from from
+ <<-TEXT
+@ECHO OFF
+@"%~dp0#{rel}/ruby.exe" "%~dpn0" %*
+ TEXT
+ else
+ # outside ruby folder, maybe -user-install or bundler. Portable
+ <<-TEXT
+@ECHO OFF
+@ruby.exe "%~dpn0" %*
+ TEXT
+ end
end
##
diff --git a/lib/rubygems/package.rb b/lib/rubygems/package.rb
index b20334c8ca..ec9541d19b 100644
--- a/lib/rubygems/package.rb
+++ b/lib/rubygems/package.rb
@@ -119,12 +119,12 @@ class Gem::Package
# Permission for other files
attr_accessor :data_mode
- def self.build spec, skip_validation=false
+ def self.build spec, skip_validation=false, strict_validation=false
gem_file = spec.file_name
package = new gem_file
package.spec = spec
- package.build skip_validation
+ package.build skip_validation, strict_validation
gem_file
end
@@ -254,12 +254,14 @@ class Gem::Package
##
# Builds this package based on the specification set by #spec=
- def build skip_validation = false
+ def build skip_validation = false, strict_validation = false
+ raise ArgumentError, "skip_validation = true and strict_validation = true are incompatible" if skip_validation && strict_validation
+
Gem.load_yaml
require 'rubygems/security'
@spec.mark_version
- @spec.validate unless skip_validation
+ @spec.validate true, strict_validation unless skip_validation
setup_signer
diff --git a/lib/rubygems/package/tar_reader/entry.rb b/lib/rubygems/package/tar_reader/entry.rb
index b6fb8c3a3a..77b06af233 100644
--- a/lib/rubygems/package/tar_reader/entry.rb
+++ b/lib/rubygems/package/tar_reader/entry.rb
@@ -119,6 +119,12 @@ class Gem::Package::TarReader::Entry
bytes_read
end
+ def size
+ @header.size
+ end
+
+ alias length size
+
##
# Reads +len+ bytes from the tar file entry, or the rest of the entry if
# nil
@@ -137,7 +143,19 @@ class Gem::Package::TarReader::Entry
ret
end
- alias readpartial read # :nodoc:
+ def readpartial(maxlen = nil, outbuf = "".b)
+ check_closed
+
+ raise EOFError if @read >= @header.size
+
+ maxlen ||= @header.size - @read
+ max_read = [maxlen, @header.size - @read].min
+
+ @io.readpartial(max_read, outbuf)
+ @read += outbuf.size
+
+ outbuf
+ end
##
# Rewinds to the beginning of the tar file entry
diff --git a/lib/rubygems/path_support.rb b/lib/rubygems/path_support.rb
index 618bc793c4..02332cef80 100644
--- a/lib/rubygems/path_support.rb
+++ b/lib/rubygems/path_support.rb
@@ -23,12 +23,14 @@ class Gem::PathSupport
# hashtable, or defaults to ENV, the system environment.
#
def initialize(env)
- @home = env["GEM_HOME"] || Gem.default_dir
+ @home = env["GEM_HOME"] || Gem.default_dir
if File::ALT_SEPARATOR then
- @home = @home.gsub(File::ALT_SEPARATOR, File::SEPARATOR)
+ @home = @home.gsub(File::ALT_SEPARATOR, File::SEPARATOR)
end
+ @home = expand(@home)
+
@path = split_gem_path env["GEM_PATH"], @home
@spec_cache_dir = env["GEM_SPEC_CACHE"] || Gem.default_spec_cache_dir
@@ -65,7 +67,7 @@ class Gem::PathSupport
gem_path = default_path
end
- gem_path.uniq
+ gem_path.map { |path| expand(path) }.uniq
end
# Return the default Gem path
@@ -77,4 +79,12 @@ class Gem::PathSupport
end
gem_path
end
+
+ def expand(path)
+ if File.directory?(path)
+ File.realpath(path)
+ else
+ path
+ end
+ end
end
diff --git a/lib/rubygems/remote_fetcher.rb b/lib/rubygems/remote_fetcher.rb
index a829268f39..940523f246 100644
--- a/lib/rubygems/remote_fetcher.rb
+++ b/lib/rubygems/remote_fetcher.rb
@@ -384,17 +384,15 @@ class Gem::RemoteFetcher
require 'base64'
require 'openssl'
- unless uri.user && uri.password
- raise FetchError.new("credentials needed in s3 source, like s3://key:secret@bucket-name/", uri.to_s)
- end
+ id, secret = s3_source_auth uri
expiration ||= s3_expiration
canonical_path = "/#{uri.host}#{uri.path}"
payload = "GET\n\n\n#{expiration}\n#{canonical_path}"
- digest = OpenSSL::HMAC.digest('sha1', uri.password, payload)
+ digest = OpenSSL::HMAC.digest('sha1', secret, payload)
# URI.escape is deprecated, and there isn't yet a replacement that does quite what we want
signature = Base64.encode64(digest).gsub("\n", '').gsub(/[\+\/=]/) { |c| BASE64_URI_TRANSLATE[c] }
- URI.parse("https://#{uri.host}.s3.amazonaws.com#{uri.path}?AWSAccessKeyId=#{uri.user}&Expires=#{expiration}&Signature=#{signature}")
+ URI.parse("https://#{uri.host}.s3.amazonaws.com#{uri.path}?AWSAccessKeyId=#{id}&Expires=#{expiration}&Signature=#{signature}")
end
def s3_expiration
@@ -414,4 +412,21 @@ class Gem::RemoteFetcher
@pools[proxy] ||= Gem::Request::ConnectionPools.new proxy, @cert_files
end
end
+
+ def s3_source_auth(uri)
+ return [uri.user, uri.password] if uri.user && uri.password
+
+ s3_source = Gem.configuration[:s3_source] || Gem.configuration['s3_source']
+ host = uri.host
+ raise FetchError.new("no s3_source key exists in .gemrc", "s3://#{host}") unless s3_source
+
+ auth = s3_source[host] || s3_source[host.to_sym]
+ raise FetchError.new("no key for host #{host} in s3_source in .gemrc", "s3://#{host}") unless auth
+
+ id = auth[:id] || auth['id']
+ secret = auth[:secret] || auth['secret']
+ raise FetchError.new("s3_source for #{host} missing id or secret", "s3://#{host}") unless id and secret
+
+ [id, secret]
+ end
end
diff --git a/lib/rubygems/request.rb b/lib/rubygems/request.rb
index 81699b98fe..d8d5d1bc31 100644
--- a/lib/rubygems/request.rb
+++ b/lib/rubygems/request.rb
@@ -1,6 +1,5 @@
# frozen_string_literal: true
require 'net/http'
-require 'thread'
require 'time'
require 'rubygems/user_interaction'
diff --git a/lib/rubygems/request/connection_pools.rb b/lib/rubygems/request/connection_pools.rb
index d70dea785a..f95dd8aabf 100644
--- a/lib/rubygems/request/connection_pools.rb
+++ b/lib/rubygems/request/connection_pools.rb
@@ -1,5 +1,4 @@
# frozen_string_literal: true
-require 'thread'
class Gem::Request::ConnectionPools # :nodoc:
diff --git a/lib/rubygems/request_set.rb b/lib/rubygems/request_set.rb
index 89f47616ec..ed99d29295 100644
--- a/lib/rubygems/request_set.rb
+++ b/lib/rubygems/request_set.rb
@@ -417,7 +417,7 @@ class Gem::RequestSet
end
def specs_in dir
- Dir["#{dir}/specifications/*.gemspec"].map do |g|
+ Gem::Util.glob_files_in_dir("*.gemspec", File.join(dir, "specifications")).map do |g|
Gem::Specification.load g
end
end
diff --git a/lib/rubygems/requirement.rb b/lib/rubygems/requirement.rb
index 0717739dc0..430f351280 100644
--- a/lib/rubygems/requirement.rb
+++ b/lib/rubygems/requirement.rb
@@ -284,7 +284,7 @@ class Gem::Requirement
end
def sort_requirements! # :nodoc:
- @requirements.sort! do |l, r|
+ @requirements.sort! do |l, r|
comp = l.last <=> r.last # first, sort by the requirement's version
next comp unless comp == 0
l.first <=> r.first # then, sort by the operator (for stability)
diff --git a/lib/rubygems/security/signer.rb b/lib/rubygems/security/signer.rb
index 0c6ef60a9a..1ee9c31be6 100644
--- a/lib/rubygems/security/signer.rb
+++ b/lib/rubygems/security/signer.rb
@@ -2,8 +2,12 @@
##
# Basic OpenSSL-based package signing class.
+require "rubygems/user_interaction"
+
class Gem::Security::Signer
+ include Gem::UserInteraction
+
##
# The chain of certificates for signing including the signing certificate
@@ -33,6 +37,7 @@ class Gem::Security::Signer
def initialize key, cert_chain, passphrase = nil
@cert_chain = cert_chain
@key = key
+ @passphrase = passphrase
unless @key then
default_key = File.join Gem.default_key_path
@@ -47,8 +52,10 @@ class Gem::Security::Signer
@digest_algorithm = Gem::Security::DIGEST_ALGORITHM
@digest_name = Gem::Security::DIGEST_NAME
- @key = OpenSSL::PKey::RSA.new File.read(@key), passphrase if
- @key and not OpenSSL::PKey::RSA === @key
+ if @key && !@key.is_a?(OpenSSL::PKey::RSA)
+ @passphrase ||= ask_for_password("Enter PEM pass phrase:")
+ @key = OpenSSL::PKey::RSA.new(File.read(@key), @passphrase)
+ end
if @cert_chain then
@cert_chain = @cert_chain.compact.map do |cert|
@@ -121,6 +128,7 @@ class Gem::Security::Signer
# The key will be re-signed if:
# * The expired certificate is self-signed
# * The expired certificate is saved at ~/.gem/gem-public_cert.pem
+ # and the private key is saved at ~/.gem/gem-private_key.pem
# * There is no file matching the expiry date at
# ~/.gem/gem-public_cert.pem.expired.%Y%m%d%H%M%S
#
@@ -131,22 +139,29 @@ class Gem::Security::Signer
def re_sign_key # :nodoc:
old_cert = @cert_chain.last
- disk_cert_path = File.join Gem.default_cert_path
- disk_cert = File.read disk_cert_path rescue nil
- disk_key =
- File.read File.join(Gem.default_key_path) rescue nil
+ disk_cert_path = File.join(Gem.default_cert_path)
+ disk_cert = File.read(disk_cert_path) rescue nil
- if disk_key == @key.to_pem and disk_cert == old_cert.to_pem then
- expiry = old_cert.not_after.strftime '%Y%m%d%H%M%S'
+ disk_key_path = File.join(Gem.default_key_path)
+ disk_key =
+ OpenSSL::PKey::RSA.new(File.read(disk_key_path), @passphrase) rescue nil
+
+ return unless disk_key
+
+ if disk_key.to_pem == @key.to_pem && disk_cert == old_cert.to_pem
+ expiry = old_cert.not_after.strftime('%Y%m%d%H%M%S')
old_cert_file = "gem-public_cert.pem.expired.#{expiry}"
- old_cert_path = File.join Gem.user_home, ".gem", old_cert_file
+ old_cert_path = File.join(Gem.user_home, ".gem", old_cert_file)
+
+ unless File.exist?(old_cert_path)
+ Gem::Security.write(old_cert, old_cert_path)
- unless File.exist? old_cert_path then
- Gem::Security.write old_cert, old_cert_path
+ cert = Gem::Security.re_sign(old_cert, @key)
- cert = Gem::Security.re_sign old_cert, @key
+ Gem::Security.write(cert, disk_cert_path)
- Gem::Security.write cert, disk_cert_path
+ alert("Your cert: #{disk_cert_path} has been auto re-signed with the key: #{disk_key_path}")
+ alert("Your expired cert will be located at: #{old_cert_path}")
@cert_chain = [cert]
end
diff --git a/lib/rubygems/spec_fetcher.rb b/lib/rubygems/spec_fetcher.rb
index 919276e113..4d224ca173 100644
--- a/lib/rubygems/spec_fetcher.rb
+++ b/lib/rubygems/spec_fetcher.rb
@@ -202,7 +202,7 @@ class Gem::SpecFetcher
}.compact
matches = if matches.empty? && type != :prerelease
- suggest_gems_from_name gem_name, :prerelease
+ suggest_gems_from_name gem_name, :prerelease
else
matches.uniq.sort_by { |name, dist| dist }
end
diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb
index 2fe315f4f6..1ec4ed9227 100644
--- a/lib/rubygems/specification.rb
+++ b/lib/rubygems/specification.rb
@@ -172,9 +172,9 @@ class Gem::Specification < Gem::BasicSpecification
when String
v.dump
when Numeric
- "default_value(:#{k})"
+ "default_value(:#{k})"
else
- "default_value(:#{k}).dup"
+ "default_value(:#{k}).dup"
end
end
@@ -761,14 +761,14 @@ class Gem::Specification < Gem::BasicSpecification
def self.each_gemspec(dirs) # :nodoc:
dirs.each do |dir|
- Dir[File.join(dir, "*.gemspec")].each do |path|
+ Gem::Util.glob_files_in_dir("*.gemspec", dir).each do |path|
yield path.untaint
end
end
end
def self.gemspec_stubs_in dir, pattern
- Dir[File.join(dir, pattern)].map { |path| yield path }.select(&:valid?)
+ Gem::Util.glob_files_in_dir(pattern, dir).map { |path| yield path }.select(&:valid?)
end
private_class_method :gemspec_stubs_in
@@ -820,11 +820,11 @@ class Gem::Specification < Gem::BasicSpecification
def self.stubs
@@stubs ||= begin
pattern = "*.gemspec"
- stubs = default_stubs(pattern).concat installed_stubs(dirs, pattern)
+ stubs = Gem.loaded_specs.values + default_stubs(pattern) + installed_stubs(dirs, pattern)
stubs = uniq_by(stubs) { |stub| stub.full_name }
_resort!(stubs)
- @@stubs_by_name = stubs.group_by(&:name)
+ @@stubs_by_name = stubs.select { |s| Gem::Platform.match s.platform }.group_by(&:name)
stubs
end
end
@@ -833,13 +833,15 @@ class Gem::Specification < Gem::BasicSpecification
##
# Returns a Gem::StubSpecification for installed gem named +name+
+ # only returns stubs that match Gem.platforms
def self.stubs_for name
if @@stubs
@@stubs_by_name[name] || []
else
pattern = "#{name}-*.gemspec"
- stubs = default_stubs(pattern) + installed_stubs(dirs, pattern)
+ stubs = Gem.loaded_specs.values + default_stubs(pattern) +
+ installed_stubs(dirs, pattern).select { |s| Gem::Platform.match s.platform }
stubs = uniq_by(stubs) { |stub| stub.full_name }.group_by(&:name)
stubs.each_value { |v| _resort!(v) }
@@ -1280,11 +1282,17 @@ class Gem::Specification < Gem::BasicSpecification
unresolved = unresolved_deps
unless unresolved.empty? then
w = "W" + "ARN"
- warn "#{w}: Unresolved specs during Gem::Specification.reset:"
+ warn "#{w}: Unresolved or ambigious specs during Gem::Specification.reset:"
unresolved.values.each do |dep|
warn " #{dep}"
+
+ versions = find_all_by_name(dep.name)
+ unless versions.empty?
+ warn " Available/installed versions of this gem:"
+ versions.each { |s| warn " - #{s.version}" }
+ end
end
- warn "#{w}: Clearing out unresolved specs."
+ warn "#{w}: Clearing out unresolved specs. Try 'gem cleanup <gem>'"
warn "Please report a bug if this causes problems."
unresolved.clear
end
@@ -2645,19 +2653,14 @@ class Gem::Specification < Gem::BasicSpecification
# Raises InvalidSpecificationException if the spec does not pass the
# checks..
- def validate packaging = true
- @warnings = 0
+ def validate packaging = true, strict = false
require 'rubygems/user_interaction'
extend Gem::UserInteraction
normalize
validation_policy = Gem::SpecificationPolicy.new(self)
validation_policy.packaging = packaging
- validation_policy.validate
- ensure
- if $! or @warnings > 0 then
- alert_warning "See http://guides.rubygems.org/specification-reference/ for help"
- end
+ validation_policy.validate(strict)
end
def keep_only_files_and_directories
@@ -2744,12 +2747,6 @@ class Gem::Specification < Gem::BasicSpecification
@installed_by_version ||= nil
end
- def warning statement # :nodoc:
- @warnings += 1
-
- alert_warning statement
- end
-
def raw_require_paths # :nodoc:
@require_paths
end
diff --git a/lib/rubygems/specification_policy.rb b/lib/rubygems/specification_policy.rb
index cb8457ed62..853cca5126 100644
--- a/lib/rubygems/specification_policy.rb
+++ b/lib/rubygems/specification_policy.rb
@@ -16,6 +16,12 @@ class Gem::SpecificationPolicy < SimpleDelegator
wiki_uri
] # :nodoc:
+ def initialize(specification)
+ @warnings = 0
+
+ super(specification)
+ end
+
##
# If set to true, run packaging-specific checks, as well.
@@ -28,7 +34,7 @@ class Gem::SpecificationPolicy < SimpleDelegator
# Raises InvalidSpecificationException if the spec does not pass the
# checks.
- def validate
+ def validate(strict = false)
validate_nil_attributes
validate_rubygems_version
@@ -64,6 +70,15 @@ class Gem::SpecificationPolicy < SimpleDelegator
validate_values
validate_dependencies
+
+ if @warnings > 0
+ if strict
+ error "specification has warnings"
+ else
+ alert_warning help_text
+ end
+ end
+
true
end
@@ -72,35 +87,29 @@ class Gem::SpecificationPolicy < SimpleDelegator
def validate_metadata
unless Hash === metadata then
- raise Gem::InvalidSpecificationException,
- 'metadata must be a hash'
+ error 'metadata must be a hash'
end
metadata.each do |key, value|
if !key.kind_of?(String) then
- raise Gem::InvalidSpecificationException,
- "metadata keys must be a String"
+ error "metadata keys must be a String"
end
if key.size > 128 then
- raise Gem::InvalidSpecificationException,
- "metadata key too large (#{key.size} > 128)"
+ error "metadata key too large (#{key.size} > 128)"
end
if !value.kind_of?(String) then
- raise Gem::InvalidSpecificationException,
- "metadata values must be a String"
+ error "metadata values must be a String"
end
if value.size > 1024 then
- raise Gem::InvalidSpecificationException,
- "metadata value too large (#{value.size} > 1024)"
+ error "metadata value too large (#{value.size} > 1024)"
end
if METADATA_LINK_KEYS.include? key then
if value !~ VALID_URI_PATTERN then
- raise Gem::InvalidSpecificationException,
- "metadata['#{key}'] has invalid link: #{value.inspect}"
+ error "metadata['#{key}'] has invalid link: #{value.inspect}"
end
end
end
@@ -132,30 +141,6 @@ duplicate dependency on #{dep}, (#{prev.requirement}) use:
warning_messages << "prerelease dependency on #{dep} is not recommended" if
prerelease_dep && !version.prerelease?
- overly_strict = dep.requirement.requirements.length == 1 &&
- dep.requirement.requirements.any? do |op, version|
- op == '~>' and
- not version.prerelease? and
- version.segments.length > 2 and
- version.segments.first != 0
- end
-
- if overly_strict then
- _, dep_version = dep.requirement.requirements.first
-
- base = dep_version.segments.first 2
- upper_bound = dep_version.segments.first(dep_version.segments.length - 1)
- upper_bound[-1] += 1
-
- warning_messages << <<-WARNING
-pessimistic dependency on #{dep} may be overly strict
- if #{dep.name} is semantically versioned, use:
- add_#{dep.type}_dependency '#{dep.name}', '~> #{base.join '.'}', '>= #{dep_version}'
- if #{dep.name} is not semantically versioned, you can bypass this warning with:
- add_#{dep.type}_dependency '#{dep.name}', '>= #{dep_version}', '< #{upper_bound.join '.'}.a'
- WARNING
- end
-
open_ended = dep.requirement.requirements.all? do |op, version|
not version.prerelease? and (op == '>' or op == '>=')
end
@@ -179,7 +164,7 @@ open-ended dependency on #{dep} is not recommended
end
end
if error_messages.any? then
- raise Gem::InvalidSpecificationException, error_messages.join
+ error error_messages.join
end
if warning_messages.any? then
warning_messages.each { |warning_message| warning warning_message }
@@ -215,45 +200,38 @@ open-ended dependency on #{dep} is not recommended
__getobj__.instance_variable_get("@#{attrname}").nil?
end
return if nil_attributes.empty?
- raise Gem::InvalidSpecificationException,
- "#{nil_attributes.join ', '} must not be nil"
+ error "#{nil_attributes.join ', '} must not be nil"
end
def validate_rubygems_version
return unless packaging
return if rubygems_version == Gem::VERSION
- raise Gem::InvalidSpecificationException,
- "expected RubyGems version #{Gem::VERSION}, was #{rubygems_version}"
+ error "expected RubyGems version #{Gem::VERSION}, was #{rubygems_version}"
end
def validate_required_attributes
Gem::Specification.required_attributes.each do |symbol|
unless send symbol then
- raise Gem::InvalidSpecificationException,
- "missing value for attribute #{symbol}"
+ error "missing value for attribute #{symbol}"
end
end
end
def validate_name
if !name.is_a?(String) then
- raise Gem::InvalidSpecificationException,
- "invalid value for attribute name: \"#{name.inspect}\" must be a string"
+ error "invalid value for attribute name: \"#{name.inspect}\" must be a string"
elsif name !~ /[a-zA-Z]/ then
- raise Gem::InvalidSpecificationException,
- "invalid value for attribute name: #{name.dump} must include at least one letter"
+ error "invalid value for attribute name: #{name.dump} must include at least one letter"
elsif name !~ VALID_NAME_PATTERN then
- raise Gem::InvalidSpecificationException,
- "invalid value for attribute name: #{name.dump} can only include letters, numbers, dashes, and underscores"
+ error "invalid value for attribute name: #{name.dump} can only include letters, numbers, dashes, and underscores"
end
end
def validate_require_paths
return unless raw_require_paths.empty?
- raise Gem::InvalidSpecificationException,
- 'specification must have at least one require_path'
+ error 'specification must have at least one require_path'
end
def validate_non_files
@@ -261,31 +239,27 @@ open-ended dependency on #{dep} is not recommended
non_files = files.reject {|x| File.file?(x) || File.symlink?(x)}
unless non_files.empty? then
- raise Gem::InvalidSpecificationException,
- "[\"#{non_files.join "\", \""}\"] are not files"
+ error "[\"#{non_files.join "\", \""}\"] are not files"
end
end
def validate_self_inclusion_in_files_list
return unless files.include?(file_name)
-
- raise Gem::InvalidSpecificationException,
- "#{full_name} contains itself (#{file_name}), check your files list"
+
+ error "#{full_name} contains itself (#{file_name}), check your files list"
end
def validate_specification_version
return if specification_version.is_a?(Integer)
-
- raise Gem::InvalidSpecificationException,
- 'specification_version must be an Integer (did you mean version?)'
+
+ error 'specification_version must be an Integer (did you mean version?)'
end
def validate_platform
case platform
when Gem::Platform, Gem::Platform::RUBY then # ok
else
- raise Gem::InvalidSpecificationException,
- "invalid platform #{platform.inspect}, see Gem::Platform"
+ error "invalid platform #{platform.inspect}, see Gem::Platform"
end
end
@@ -313,15 +287,13 @@ open-ended dependency on #{dep} is not recommended
def validate_authors_field
return unless authors.empty?
- raise Gem::InvalidSpecificationException,
- "authors may not be empty"
+ error "authors may not be empty"
end
def validate_licenses
licenses.each { |license|
if license.length > 64 then
- raise Gem::InvalidSpecificationException,
- "each license must be 64 characters or less"
+ error "each license must be 64 characters or less"
end
if !Gem::Licenses.match?(license) then
@@ -347,19 +319,19 @@ http://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard li
def validate_lazy_metadata
unless authors.grep(LAZY_PATTERN).empty? then
- raise Gem::InvalidSpecificationException, "#{LAZY} is not an author"
+ error "#{LAZY} is not an author"
end
unless Array(email).grep(LAZY_PATTERN).empty? then
- raise Gem::InvalidSpecificationException, "#{LAZY} is not an email"
+ error "#{LAZY} is not an email"
end
if description =~ LAZY_PATTERN then
- raise Gem::InvalidSpecificationException, "#{LAZY} is not a description"
+ error "#{LAZY} is not a description"
end
if summary =~ LAZY_PATTERN then
- raise Gem::InvalidSpecificationException, "#{LAZY} is not a summary"
+ error "#{LAZY} is not a summary"
end
# Make sure a homepage is valid HTTP/HTTPS URI
@@ -367,10 +339,10 @@ http://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard li
begin
homepage_uri = URI.parse(homepage)
unless [URI::HTTP, URI::HTTPS].member? homepage_uri.class
- raise Gem::InvalidSpecificationException, "\"#{homepage}\" is not a valid HTTP URI"
+ error "\"#{homepage}\" is not a valid HTTP URI"
end
rescue URI::InvalidURIError
- raise Gem::InvalidSpecificationException, "\"#{homepage}\" is not a valid HTTP URI"
+ error "\"#{homepage}\" is not a valid HTTP URI"
end
end
end
@@ -407,4 +379,20 @@ http://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard li
warning "#{executable_path} is missing #! line"
end
+
+ def warning statement # :nodoc:
+ @warnings += 1
+
+ alert_warning statement
+ end
+
+ def error statement # :nodoc:
+ raise Gem::InvalidSpecificationException, statement
+ ensure
+ alert_warning help_text
+ end
+
+ def help_text # :nodoc:
+ "See http://guides.rubygems.org/specification-reference/ for help"
+ end
end
diff --git a/lib/rubygems/test_case.rb b/lib/rubygems/test_case.rb
index f1cd3d274c..ced33c4d11 100644
--- a/lib/rubygems/test_case.rb
+++ b/lib/rubygems/test_case.rb
@@ -13,6 +13,15 @@ else
require 'rubygems'
end
+# If bundler gemspec exists, add to stubs
+bundler_gemspec = File.expand_path("../../../bundler/bundler.gemspec", __FILE__)
+if File.exist?(bundler_gemspec)
+ Gem::Specification.dirs.unshift File.dirname(bundler_gemspec)
+ Gem::Specification.class_variable_set :@@stubs, nil
+ Gem::Specification.stubs
+ Gem::Specification.dirs.shift
+end
+
begin
gem 'minitest'
rescue Gem::LoadError
@@ -382,6 +391,11 @@ class Gem::TestCase < (defined?(Minitest::Test) ? Minitest::Test : MiniTest::Uni
util_set_arch 'i686-darwin8.10.1'
end
+ @orig_hooks = {}
+ %w[post_install_hooks done_installing_hooks post_uninstall_hooks pre_uninstall_hooks pre_install_hooks pre_reset_hooks post_reset_hooks post_build_hooks].each do |name|
+ @orig_hooks[name] = Gem.send(name).dup
+ end
+
@marshal_version = "#{Marshal::MAJOR_VERSION}.#{Marshal::MINOR_VERSION}"
@orig_LOADED_FEATURES = $LOADED_FEATURES.dup
end
@@ -449,6 +463,10 @@ class Gem::TestCase < (defined?(Minitest::Test) ? Minitest::Test : MiniTest::Uni
Gem::Specification.unresolved_deps.clear
Gem::refresh
+ @orig_hooks.each do |name, hooks|
+ Gem.send(name).replace hooks
+ end
+
@back_ui.close
end
diff --git a/lib/rubygems/util.rb b/lib/rubygems/util.rb
index 9f5b9a2239..6dbcd4ba21 100644
--- a/lib/rubygems/util.rb
+++ b/lib/rubygems/util.rb
@@ -80,8 +80,6 @@ module Gem::Util
end
return system(*(cmds << opt))
rescue TypeError
- require 'thread'
-
@silent_mutex ||= Mutex.new
@silent_mutex.synchronize do
@@ -118,4 +116,16 @@ module Gem::Util
end
end
+ ##
+ # Globs for files matching +pattern+ inside of +directory+,
+ # returning absolute paths to the matching files.
+
+ def self.glob_files_in_dir(glob, base_path)
+ if RUBY_VERSION >= "2.5"
+ Dir.glob(glob, base: base_path).map! {|f| File.join(base_path, f) }
+ else
+ Dir.glob(File.expand_path(glob, base_path))
+ end
+ end
+
end
diff --git a/lib/rubygems/version.rb b/lib/rubygems/version.rb
index fbf75996cd..08f0d1e7a5 100644
--- a/lib/rubygems/version.rb
+++ b/lib/rubygems/version.rb
@@ -170,7 +170,10 @@ class Gem::Version
# True if the +version+ string matches RubyGems' requirements.
def self.correct? version
- return false if version.nil?
+ unless Gem::Deprecate.skip
+ warn "nil versions are discouraged and will be deprecated in Rubygems 4" if version.nil?
+ end
+
!!(version.to_s =~ ANCHORED_VERSION_PATTERN)
end
@@ -325,7 +328,9 @@ class Gem::Version
segments.pop while segments.size > 2
segments.push 0 while segments.size < 2
- "~> #{segments.join(".")}"
+ recommendation = "~> #{segments.join(".")}"
+ recommendation += ".a" if prerelease?
+ recommendation
end
##