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
This commit is contained in:
hsbt 2018-08-27 10:05:04 +00:00
parent 3a83ba90c3
commit 85d461456c
56 changed files with 868 additions and 242 deletions

View File

@ -7,7 +7,6 @@
#++ #++
require 'rbconfig' require 'rbconfig'
require 'thread'
module Gem module Gem
VERSION = "3.0.0.beta1" VERSION = "3.0.0.beta1"
@ -526,8 +525,9 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
end end
def self.find_files_from_load_path glob # :nodoc: def self.find_files_from_load_path glob # :nodoc:
glob_with_suffixes = "#{glob}#{Gem.suffix_pattern}"
$LOAD_PATH.map { |load_path| $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 } }.flatten.select { |file| File.file? file.untaint }
end end
@ -1119,8 +1119,9 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
path = "rubygems_plugin" path = "rubygems_plugin"
files = [] files = []
glob = "#{path}#{Gem.suffix_pattern}"
$LOAD_PATH.each do |load_path| $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| globbed.each do |load_path_file|
files << load_path_file if File.file?(load_path_file.untaint) files << load_path_file if File.file?(load_path_file.untaint)

View File

@ -152,7 +152,7 @@ class Gem::BasicSpecification
# The path to the data directory for this gem. # The path to the data directory for this gem.
def datadir 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 File.expand_path(File.join(gems_dir, full_name, "data", name)).untaint
end end
@ -282,7 +282,7 @@ class Gem::BasicSpecification
self.raw_require_paths.first self.raw_require_paths.first
end end
else else
"lib" # default value for require_paths for bundler/inline "lib" # default value for require_paths for bundler/inline
end end
"#{self.full_gem_path}/#{dirs}".dup.untaint "#{self.full_gem_path}/#{dirs}".dup.untaint

View File

@ -71,6 +71,10 @@ class Gem::CommandManager
:yank, :yank,
] ]
ALIAS_COMMANDS = {
'i' => 'install'
}
## ##
# Return the authoritative instance of the command manager. # Return the authoritative instance of the command manager.
@ -174,6 +178,8 @@ class Gem::CommandManager
end end
def find_command(cmd_name) def find_command(cmd_name)
cmd_name = find_alias_command cmd_name
possibilities = find_command_possibilities cmd_name possibilities = find_command_possibilities cmd_name
if possibilities.size > 1 then if possibilities.size > 1 then
@ -186,6 +192,11 @@ class Gem::CommandManager
self[possibilities.first] self[possibilities.first]
end 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) def find_command_possibilities(cmd_name)
len = cmd_name.length len = cmd_name.length

View File

@ -10,6 +10,10 @@ class Gem::Commands::BuildCommand < Gem::Command
add_option '--force', 'skip validation of the spec' do |value, options| add_option '--force', 'skip validation of the spec' do |value, options|
options[:force] = true options[:force] = true
end end
add_option '--strict', 'consider warnings as errors when validating the spec' do |value, options|
options[:strict] = true
end
end end
def arguments # :nodoc: def arguments # :nodoc:
@ -51,7 +55,7 @@ with gem spec:
spec = Gem::Specification.load File.basename(gemspec) spec = Gem::Specification.load File.basename(gemspec)
if spec then if spec then
Gem::Package.build spec, options[:force] Gem::Package.build spec, options[:force], options[:strict]
else else
alert_error "Error loading gemspec. Aborting." alert_error "Error loading gemspec. Aborting."
terminate_interaction 1 terminate_interaction 1

View File

@ -87,7 +87,7 @@ class Gem::Commands::CertCommand < Gem::Command
add_option('-d', '--days NUMBER_OF_DAYS', add_option('-d', '--days NUMBER_OF_DAYS',
'Days before the certificate expires') do |days, options| 'Days before the certificate expires') do |days, options|
options[:expiration_length_days] = days.to_i options[:expiration_length_days] = days.to_i
end end
end end

View File

@ -22,6 +22,12 @@ class Gem::Commands::CleanupCommand < Gem::Command
options[:check_dev] = value options[:check_dev] = value
end 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 @candidate_gems = nil
@default_gems = [] @default_gems = []
@full = nil @full = nil
@ -124,8 +130,10 @@ If no gems are named all gems in GEM_HOME are cleaned.
spec.default_gem? spec.default_gem?
} }
uninstall_from = options[:user_install] ? Gem.user_dir : @original_home
gems_to_cleanup = gems_to_cleanup.select { |spec| gems_to_cleanup = gems_to_cleanup.select { |spec|
spec.base_dir == @original_home spec.base_dir == uninstall_from
} }
@default_gems += default_gems @default_gems += default_gems

View File

@ -117,6 +117,13 @@ to write the specification by hand. For example:
some_extension_gem (1.0) some_extension_gem (1.0)
$ $
Command Alias
==========================
You can use `i` command instead of `install`.
$ gem i GEMNAME
EOF EOF
end end

View File

@ -60,8 +60,14 @@ class Gem::Commands::OpenCommand < Gem::Command
def open_gem name def open_gem name
spec = spec_for name spec = spec_for name
return false unless spec 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) open_editor(spec.full_gem_path)
end end

View File

@ -46,6 +46,12 @@ class Gem::Commands::PristineCommand < Gem::Command
options[:env_shebang] = value options[:env_shebang] = value
end 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') add_version_option('restore to', 'pristine condition')
end end
@ -160,12 +166,15 @@ extensions will be restored.
install_defaults.to_s['--env-shebang'] install_defaults.to_s['--env-shebang']
end end
bin_dir = options[:bin_dir] if options[:bin_dir]
installer_options = { installer_options = {
:wrappers => true, :wrappers => true,
:force => true, :force => true,
:install_dir => spec.base_dir, :install_dir => spec.base_dir,
:env_shebang => env_shebang, :env_shebang => env_shebang,
:build_args => spec.build_args, :build_args => spec.build_args,
:bin_dir => bin_dir
} }
if options[:only_executables] then if options[:only_executables] then

View File

@ -29,6 +29,8 @@ command. For further discussion see the help for the yank command.
def initialize def initialize
super 'push', 'Push a gem up to the gem server', :host => self.host super 'push', 'Push a gem up to the gem server', :host => self.host
@user_defined_host = false
add_proxy_option add_proxy_option
add_key_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', 'Push to another gemcutter-compatible host',
' (e.g. https://rubygems.org)') do |value, options| ' (e.g. https://rubygems.org)') do |value, options|
options[:host] = value options[:host] = value
@user_defined_host = true
end end
@host = nil @host = nil
end end
def execute 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 sign_in @host
send_gem get_one_gem_name send_gem(gem_name)
end end
def send_gem name def send_gem(name)
args = [:post, "api/v1/gems"] args = [:post, "api/v1/gems"]
latest_rubygems_version = Gem.latest_rubygems_version 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 with_response response
end 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 end

View File

@ -84,7 +84,7 @@ class Gem::Commands::SetupCommand < Gem::Command
add_option '--[no-]regenerate-binstubs', add_option '--[no-]regenerate-binstubs',
'Regenerate gem binstubs' do |value, options| 'Regenerate gem binstubs' do |value, options|
options[:regenerate_binstubs] = value options[:regenerate_binstubs] = value
end end
add_option('-E', '--[no-]env-shebang', add_option('-E', '--[no-]env-shebang',
@ -468,8 +468,8 @@ By default, this RubyGems will install gem as:
(prefix == RbConfig::CONFIG['libdir'] or (prefix == RbConfig::CONFIG['libdir'] or
# this one is important # this one is important
prefix == File.join(RbConfig::CONFIG['libdir'], 'ruby')) then prefix == File.join(RbConfig::CONFIG['libdir'], 'ruby')) then
lib_dir = RbConfig::CONFIG[site_or_vendor] lib_dir = RbConfig::CONFIG[site_or_vendor]
bin_dir = RbConfig::CONFIG['bindir'] bin_dir = RbConfig::CONFIG['bindir']
else else
lib_dir = File.join prefix, 'lib' lib_dir = File.join prefix, 'lib'
bin_dir = File.join prefix, 'bin' bin_dir = File.join prefix, 'bin'

View File

@ -10,7 +10,7 @@ class Gem::Commands::SigninCommand < Gem::Command
'It defaults to https://rubygems.org' 'It defaults to https://rubygems.org'
add_option('--host HOST', 'Push to another gemcutter-compatible host') do |value, options| add_option('--host HOST', 'Push to another gemcutter-compatible host') do |value, options|
options[:host] = value options[:host] = value
end end
end end

View File

@ -48,7 +48,7 @@ class Gem::Commands::UninstallCommand < Gem::Command
end end
add_option('-n', '--bindir DIR', 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) options[:bin_dir] = File.expand_path(value)
end end

View File

@ -458,7 +458,7 @@ class Gem::DependencyInstaller
rescue Gem::Package::FormatError rescue Gem::Package::FormatError
end end
end end
# else This is a dependency. InstallerSet handles this case # else This is a dependency. InstallerSet handles this case
end end
end end

View File

@ -6,7 +6,6 @@
#++ #++
require 'rubygems/user_interaction' require 'rubygems/user_interaction'
require 'thread'
class Gem::Ext::Builder class Gem::Ext::Builder

View File

@ -271,7 +271,7 @@ class Gem::Indexer
# List of gem file names to index. # List of gem file names to index.
def gem_file_list def gem_file_list
Dir[File.join(@dest_directory, "gems", '*.gem')] Gem::Util.glob_files_in_dir("*.gem", File.join(@dest_directory, "gems"))
end end
## ##

View File

@ -25,7 +25,7 @@ module Gem::InstallUpdateOptions
end end
add_option(:"Install/Update", '-n', '--bindir DIR', add_option(:"Install/Update", '-n', '--bindir DIR',
'Directory where binary files are', 'Directory where executables are',
'located') do |value, options| 'located') do |value, options|
options[:bin_dir] = File.expand_path(value) options[:bin_dir] = File.expand_path(value)
end end

View File

@ -187,6 +187,8 @@ class Gem::Installer
@package.prog_mode = options[:prog_mode] @package.prog_mode = options[:prog_mode]
@package.data_mode = options[:data_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 if options[:user_install] and not options[:unpack] then
@gem_home = Gem.user_dir @gem_home = Gem.user_dir
@bin_dir = Gem.bindir gem_home unless options[:bin_dir] @bin_dir = Gem.bindir gem_home unless options[:bin_dir]
@ -379,7 +381,7 @@ class Gem::Installer
@specs ||= begin @specs ||= begin
specs = [] 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 spec = Gem::Specification.load path.untaint
specs << spec if spec specs << spec if spec
end end
@ -769,15 +771,30 @@ TEXT
# return the stub script text used to launch the true Ruby script # return the stub script text used to launch the true Ruby script
def windows_stub_script(bindir, bin_file_name) def windows_stub_script(bindir, bin_file_name)
ruby = Gem.ruby.gsub(/^\"|\"$/, "").tr(File::SEPARATOR, "\\") # All comparisons should be case insensitive
return <<-TEXT if bindir.downcase == RbConfig::CONFIG["bindir"].downcase
# stub & ruby.exe withing same folder. Portable
<<-TEXT
@ECHO OFF @ECHO OFF
IF NOT "%~f0" == "~f0" GOTO :WinNT @"%~dp0ruby.exe" "%~dpn0" %*
@"#{ruby}" "#{File.join(bindir, bin_file_name)}" %1 %2 %3 %4 %5 %6 %7 %8 %9 TEXT
GOTO :EOF elsif bindir.downcase.start_with? RbConfig::TOPDIR.downcase
:WinNT # stub within ruby folder, but not standard bin. Not portable
@"#{ruby}" "%~dpn0" %* require 'pathname'
TEXT 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 end
## ##

View File

@ -119,12 +119,12 @@ class Gem::Package
# Permission for other files # Permission for other files
attr_accessor :data_mode 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 gem_file = spec.file_name
package = new gem_file package = new gem_file
package.spec = spec package.spec = spec
package.build skip_validation package.build skip_validation, strict_validation
gem_file gem_file
end end
@ -254,12 +254,14 @@ class Gem::Package
## ##
# Builds this package based on the specification set by #spec= # 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 Gem.load_yaml
require 'rubygems/security' require 'rubygems/security'
@spec.mark_version @spec.mark_version
@spec.validate unless skip_validation @spec.validate true, strict_validation unless skip_validation
setup_signer setup_signer

View File

@ -119,6 +119,12 @@ class Gem::Package::TarReader::Entry
bytes_read bytes_read
end end
def size
@header.size
end
alias length size
## ##
# Reads +len+ bytes from the tar file entry, or the rest of the entry if # Reads +len+ bytes from the tar file entry, or the rest of the entry if
# nil # nil
@ -137,7 +143,19 @@ class Gem::Package::TarReader::Entry
ret ret
end 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 # Rewinds to the beginning of the tar file entry

View File

@ -23,12 +23,14 @@ class Gem::PathSupport
# hashtable, or defaults to ENV, the system environment. # hashtable, or defaults to ENV, the system environment.
# #
def initialize(env) def initialize(env)
@home = env["GEM_HOME"] || Gem.default_dir @home = env["GEM_HOME"] || Gem.default_dir
if File::ALT_SEPARATOR then if File::ALT_SEPARATOR then
@home = @home.gsub(File::ALT_SEPARATOR, File::SEPARATOR) @home = @home.gsub(File::ALT_SEPARATOR, File::SEPARATOR)
end end
@home = expand(@home)
@path = split_gem_path env["GEM_PATH"], @home @path = split_gem_path env["GEM_PATH"], @home
@spec_cache_dir = env["GEM_SPEC_CACHE"] || Gem.default_spec_cache_dir @spec_cache_dir = env["GEM_SPEC_CACHE"] || Gem.default_spec_cache_dir
@ -65,7 +67,7 @@ class Gem::PathSupport
gem_path = default_path gem_path = default_path
end end
gem_path.uniq gem_path.map { |path| expand(path) }.uniq
end end
# Return the default Gem path # Return the default Gem path
@ -77,4 +79,12 @@ class Gem::PathSupport
end end
gem_path gem_path
end end
def expand(path)
if File.directory?(path)
File.realpath(path)
else
path
end
end
end end

View File

@ -384,17 +384,15 @@ class Gem::RemoteFetcher
require 'base64' require 'base64'
require 'openssl' require 'openssl'
unless uri.user && uri.password id, secret = s3_source_auth uri
raise FetchError.new("credentials needed in s3 source, like s3://key:secret@bucket-name/", uri.to_s)
end
expiration ||= s3_expiration expiration ||= s3_expiration
canonical_path = "/#{uri.host}#{uri.path}" canonical_path = "/#{uri.host}#{uri.path}"
payload = "GET\n\n\n#{expiration}\n#{canonical_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 # 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] } 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 end
def s3_expiration def s3_expiration
@ -414,4 +412,21 @@ class Gem::RemoteFetcher
@pools[proxy] ||= Gem::Request::ConnectionPools.new proxy, @cert_files @pools[proxy] ||= Gem::Request::ConnectionPools.new proxy, @cert_files
end end
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 end

View File

@ -1,6 +1,5 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'net/http' require 'net/http'
require 'thread'
require 'time' require 'time'
require 'rubygems/user_interaction' require 'rubygems/user_interaction'

View File

@ -1,5 +1,4 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'thread'
class Gem::Request::ConnectionPools # :nodoc: class Gem::Request::ConnectionPools # :nodoc:

View File

@ -417,7 +417,7 @@ class Gem::RequestSet
end end
def specs_in dir 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 Gem::Specification.load g
end end
end end

View File

@ -2,8 +2,12 @@
## ##
# Basic OpenSSL-based package signing class. # Basic OpenSSL-based package signing class.
require "rubygems/user_interaction"
class Gem::Security::Signer class Gem::Security::Signer
include Gem::UserInteraction
## ##
# The chain of certificates for signing including the signing certificate # 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 def initialize key, cert_chain, passphrase = nil
@cert_chain = cert_chain @cert_chain = cert_chain
@key = key @key = key
@passphrase = passphrase
unless @key then unless @key then
default_key = File.join Gem.default_key_path default_key = File.join Gem.default_key_path
@ -47,8 +52,10 @@ class Gem::Security::Signer
@digest_algorithm = Gem::Security::DIGEST_ALGORITHM @digest_algorithm = Gem::Security::DIGEST_ALGORITHM
@digest_name = Gem::Security::DIGEST_NAME @digest_name = Gem::Security::DIGEST_NAME
@key = OpenSSL::PKey::RSA.new File.read(@key), passphrase if if @key && !@key.is_a?(OpenSSL::PKey::RSA)
@key and not OpenSSL::PKey::RSA === @key @passphrase ||= ask_for_password("Enter PEM pass phrase:")
@key = OpenSSL::PKey::RSA.new(File.read(@key), @passphrase)
end
if @cert_chain then if @cert_chain then
@cert_chain = @cert_chain.compact.map do |cert| @cert_chain = @cert_chain.compact.map do |cert|
@ -121,6 +128,7 @@ class Gem::Security::Signer
# The key will be re-signed if: # The key will be re-signed if:
# * The expired certificate is self-signed # * The expired certificate is self-signed
# * The expired certificate is saved at ~/.gem/gem-public_cert.pem # * 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 # * There is no file matching the expiry date at
# ~/.gem/gem-public_cert.pem.expired.%Y%m%d%H%M%S # ~/.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: def re_sign_key # :nodoc:
old_cert = @cert_chain.last old_cert = @cert_chain.last
disk_cert_path = File.join Gem.default_cert_path disk_cert_path = File.join(Gem.default_cert_path)
disk_cert = File.read disk_cert_path rescue nil disk_cert = File.read(disk_cert_path) rescue nil
disk_key =
File.read File.join(Gem.default_key_path) rescue nil
if disk_key == @key.to_pem and disk_cert == old_cert.to_pem then disk_key_path = File.join(Gem.default_key_path)
expiry = old_cert.not_after.strftime '%Y%m%d%H%M%S' 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_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 then unless File.exist?(old_cert_path)
Gem::Security.write old_cert, old_cert_path 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] @cert_chain = [cert]
end end

View File

@ -202,7 +202,7 @@ class Gem::SpecFetcher
}.compact }.compact
matches = if matches.empty? && type != :prerelease matches = if matches.empty? && type != :prerelease
suggest_gems_from_name gem_name, :prerelease suggest_gems_from_name gem_name, :prerelease
else else
matches.uniq.sort_by { |name, dist| dist } matches.uniq.sort_by { |name, dist| dist }
end end

View File

@ -172,9 +172,9 @@ class Gem::Specification < Gem::BasicSpecification
when String when String
v.dump v.dump
when Numeric when Numeric
"default_value(:#{k})" "default_value(:#{k})"
else else
"default_value(:#{k}).dup" "default_value(:#{k}).dup"
end end
end end
@ -761,14 +761,14 @@ class Gem::Specification < Gem::BasicSpecification
def self.each_gemspec(dirs) # :nodoc: def self.each_gemspec(dirs) # :nodoc:
dirs.each do |dir| 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 yield path.untaint
end end
end end
end end
def self.gemspec_stubs_in dir, pattern 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 end
private_class_method :gemspec_stubs_in private_class_method :gemspec_stubs_in
@ -820,11 +820,11 @@ class Gem::Specification < Gem::BasicSpecification
def self.stubs def self.stubs
@@stubs ||= begin @@stubs ||= begin
pattern = "*.gemspec" 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 } stubs = uniq_by(stubs) { |stub| stub.full_name }
_resort!(stubs) _resort!(stubs)
@@stubs_by_name = stubs.group_by(&:name) @@stubs_by_name = stubs.select { |s| Gem::Platform.match s.platform }.group_by(&:name)
stubs stubs
end end
end end
@ -833,13 +833,15 @@ class Gem::Specification < Gem::BasicSpecification
## ##
# Returns a Gem::StubSpecification for installed gem named +name+ # Returns a Gem::StubSpecification for installed gem named +name+
# only returns stubs that match Gem.platforms
def self.stubs_for name def self.stubs_for name
if @@stubs if @@stubs
@@stubs_by_name[name] || [] @@stubs_by_name[name] || []
else else
pattern = "#{name}-*.gemspec" 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 = uniq_by(stubs) { |stub| stub.full_name }.group_by(&:name)
stubs.each_value { |v| _resort!(v) } stubs.each_value { |v| _resort!(v) }
@ -1280,11 +1282,17 @@ class Gem::Specification < Gem::BasicSpecification
unresolved = unresolved_deps unresolved = unresolved_deps
unless unresolved.empty? then unless unresolved.empty? then
w = "W" + "ARN" 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| unresolved.values.each do |dep|
warn " #{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 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." warn "Please report a bug if this causes problems."
unresolved.clear unresolved.clear
end end
@ -2645,19 +2653,14 @@ class Gem::Specification < Gem::BasicSpecification
# Raises InvalidSpecificationException if the spec does not pass the # Raises InvalidSpecificationException if the spec does not pass the
# checks.. # checks..
def validate packaging = true def validate packaging = true, strict = false
@warnings = 0
require 'rubygems/user_interaction' require 'rubygems/user_interaction'
extend Gem::UserInteraction extend Gem::UserInteraction
normalize normalize
validation_policy = Gem::SpecificationPolicy.new(self) validation_policy = Gem::SpecificationPolicy.new(self)
validation_policy.packaging = packaging validation_policy.packaging = packaging
validation_policy.validate validation_policy.validate(strict)
ensure
if $! or @warnings > 0 then
alert_warning "See http://guides.rubygems.org/specification-reference/ for help"
end
end end
def keep_only_files_and_directories def keep_only_files_and_directories
@ -2744,12 +2747,6 @@ class Gem::Specification < Gem::BasicSpecification
@installed_by_version ||= nil @installed_by_version ||= nil
end end
def warning statement # :nodoc:
@warnings += 1
alert_warning statement
end
def raw_require_paths # :nodoc: def raw_require_paths # :nodoc:
@require_paths @require_paths
end end

View File

@ -16,6 +16,12 @@ class Gem::SpecificationPolicy < SimpleDelegator
wiki_uri wiki_uri
] # :nodoc: ] # :nodoc:
def initialize(specification)
@warnings = 0
super(specification)
end
## ##
# If set to true, run packaging-specific checks, as well. # 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 # Raises InvalidSpecificationException if the spec does not pass the
# checks. # checks.
def validate def validate(strict = false)
validate_nil_attributes validate_nil_attributes
validate_rubygems_version validate_rubygems_version
@ -64,6 +70,15 @@ class Gem::SpecificationPolicy < SimpleDelegator
validate_values validate_values
validate_dependencies validate_dependencies
if @warnings > 0
if strict
error "specification has warnings"
else
alert_warning help_text
end
end
true true
end end
@ -72,35 +87,29 @@ class Gem::SpecificationPolicy < SimpleDelegator
def validate_metadata def validate_metadata
unless Hash === metadata then unless Hash === metadata then
raise Gem::InvalidSpecificationException, error 'metadata must be a hash'
'metadata must be a hash'
end end
metadata.each do |key, value| metadata.each do |key, value|
if !key.kind_of?(String) then if !key.kind_of?(String) then
raise Gem::InvalidSpecificationException, error "metadata keys must be a String"
"metadata keys must be a String"
end end
if key.size > 128 then if key.size > 128 then
raise Gem::InvalidSpecificationException, error "metadata key too large (#{key.size} > 128)"
"metadata key too large (#{key.size} > 128)"
end end
if !value.kind_of?(String) then if !value.kind_of?(String) then
raise Gem::InvalidSpecificationException, error "metadata values must be a String"
"metadata values must be a String"
end end
if value.size > 1024 then if value.size > 1024 then
raise Gem::InvalidSpecificationException, error "metadata value too large (#{value.size} > 1024)"
"metadata value too large (#{value.size} > 1024)"
end end
if METADATA_LINK_KEYS.include? key then if METADATA_LINK_KEYS.include? key then
if value !~ VALID_URI_PATTERN then if value !~ VALID_URI_PATTERN then
raise Gem::InvalidSpecificationException, error "metadata['#{key}'] has invalid link: #{value.inspect}"
"metadata['#{key}'] has invalid link: #{value.inspect}"
end end
end end
end end
@ -132,30 +141,6 @@ duplicate dependency on #{dep}, (#{prev.requirement}) use:
warning_messages << "prerelease dependency on #{dep} is not recommended" if warning_messages << "prerelease dependency on #{dep} is not recommended" if
prerelease_dep && !version.prerelease? 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| open_ended = dep.requirement.requirements.all? do |op, version|
not version.prerelease? and (op == '>' or op == '>=') not version.prerelease? and (op == '>' or op == '>=')
end end
@ -179,7 +164,7 @@ open-ended dependency on #{dep} is not recommended
end end
end end
if error_messages.any? then if error_messages.any? then
raise Gem::InvalidSpecificationException, error_messages.join error error_messages.join
end end
if warning_messages.any? then if warning_messages.any? then
warning_messages.each { |warning_message| warning warning_message } 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? __getobj__.instance_variable_get("@#{attrname}").nil?
end end
return if nil_attributes.empty? return if nil_attributes.empty?
raise Gem::InvalidSpecificationException, error "#{nil_attributes.join ', '} must not be nil"
"#{nil_attributes.join ', '} must not be nil"
end end
def validate_rubygems_version def validate_rubygems_version
return unless packaging return unless packaging
return if rubygems_version == Gem::VERSION return if rubygems_version == Gem::VERSION
raise Gem::InvalidSpecificationException, error "expected RubyGems version #{Gem::VERSION}, was #{rubygems_version}"
"expected RubyGems version #{Gem::VERSION}, was #{rubygems_version}"
end end
def validate_required_attributes def validate_required_attributes
Gem::Specification.required_attributes.each do |symbol| Gem::Specification.required_attributes.each do |symbol|
unless send symbol then unless send symbol then
raise Gem::InvalidSpecificationException, error "missing value for attribute #{symbol}"
"missing value for attribute #{symbol}"
end end
end end
end end
def validate_name def validate_name
if !name.is_a?(String) then if !name.is_a?(String) then
raise Gem::InvalidSpecificationException, error "invalid value for attribute name: \"#{name.inspect}\" must be a string"
"invalid value for attribute name: \"#{name.inspect}\" must be a string"
elsif name !~ /[a-zA-Z]/ then elsif name !~ /[a-zA-Z]/ then
raise Gem::InvalidSpecificationException, error "invalid value for attribute name: #{name.dump} must include at least one letter"
"invalid value for attribute name: #{name.dump} must include at least one letter"
elsif name !~ VALID_NAME_PATTERN then elsif name !~ VALID_NAME_PATTERN then
raise Gem::InvalidSpecificationException, error "invalid value for attribute name: #{name.dump} can only include letters, numbers, dashes, and underscores"
"invalid value for attribute name: #{name.dump} can only include letters, numbers, dashes, and underscores"
end end
end end
def validate_require_paths def validate_require_paths
return unless raw_require_paths.empty? return unless raw_require_paths.empty?
raise Gem::InvalidSpecificationException, error 'specification must have at least one require_path'
'specification must have at least one require_path'
end end
def validate_non_files 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)} non_files = files.reject {|x| File.file?(x) || File.symlink?(x)}
unless non_files.empty? then unless non_files.empty? then
raise Gem::InvalidSpecificationException, error "[\"#{non_files.join "\", \""}\"] are not files"
"[\"#{non_files.join "\", \""}\"] are not files"
end end
end end
def validate_self_inclusion_in_files_list def validate_self_inclusion_in_files_list
return unless files.include?(file_name) return unless files.include?(file_name)
raise Gem::InvalidSpecificationException, error "#{full_name} contains itself (#{file_name}), check your files list"
"#{full_name} contains itself (#{file_name}), check your files list"
end end
def validate_specification_version def validate_specification_version
return if specification_version.is_a?(Integer) return if specification_version.is_a?(Integer)
raise Gem::InvalidSpecificationException, error 'specification_version must be an Integer (did you mean version?)'
'specification_version must be an Integer (did you mean version?)'
end end
def validate_platform def validate_platform
case platform case platform
when Gem::Platform, Gem::Platform::RUBY then # ok when Gem::Platform, Gem::Platform::RUBY then # ok
else else
raise Gem::InvalidSpecificationException, error "invalid platform #{platform.inspect}, see Gem::Platform"
"invalid platform #{platform.inspect}, see Gem::Platform"
end end
end end
@ -313,15 +287,13 @@ open-ended dependency on #{dep} is not recommended
def validate_authors_field def validate_authors_field
return unless authors.empty? return unless authors.empty?
raise Gem::InvalidSpecificationException, error "authors may not be empty"
"authors may not be empty"
end end
def validate_licenses def validate_licenses
licenses.each { |license| licenses.each { |license|
if license.length > 64 then if license.length > 64 then
raise Gem::InvalidSpecificationException, error "each license must be 64 characters or less"
"each license must be 64 characters or less"
end end
if !Gem::Licenses.match?(license) then 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 def validate_lazy_metadata
unless authors.grep(LAZY_PATTERN).empty? then unless authors.grep(LAZY_PATTERN).empty? then
raise Gem::InvalidSpecificationException, "#{LAZY} is not an author" error "#{LAZY} is not an author"
end end
unless Array(email).grep(LAZY_PATTERN).empty? then unless Array(email).grep(LAZY_PATTERN).empty? then
raise Gem::InvalidSpecificationException, "#{LAZY} is not an email" error "#{LAZY} is not an email"
end end
if description =~ LAZY_PATTERN then if description =~ LAZY_PATTERN then
raise Gem::InvalidSpecificationException, "#{LAZY} is not a description" error "#{LAZY} is not a description"
end end
if summary =~ LAZY_PATTERN then if summary =~ LAZY_PATTERN then
raise Gem::InvalidSpecificationException, "#{LAZY} is not a summary" error "#{LAZY} is not a summary"
end end
# Make sure a homepage is valid HTTP/HTTPS URI # 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 begin
homepage_uri = URI.parse(homepage) homepage_uri = URI.parse(homepage)
unless [URI::HTTP, URI::HTTPS].member? homepage_uri.class 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 end
rescue URI::InvalidURIError rescue URI::InvalidURIError
raise Gem::InvalidSpecificationException, "\"#{homepage}\" is not a valid HTTP URI" error "\"#{homepage}\" is not a valid HTTP URI"
end end
end 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" warning "#{executable_path} is missing #! line"
end 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 end

View File

@ -13,6 +13,15 @@ else
require 'rubygems' require 'rubygems'
end 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 begin
gem 'minitest' gem 'minitest'
rescue Gem::LoadError rescue Gem::LoadError
@ -382,6 +391,11 @@ class Gem::TestCase < (defined?(Minitest::Test) ? Minitest::Test : MiniTest::Uni
util_set_arch 'i686-darwin8.10.1' util_set_arch 'i686-darwin8.10.1'
end 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}" @marshal_version = "#{Marshal::MAJOR_VERSION}.#{Marshal::MINOR_VERSION}"
@orig_LOADED_FEATURES = $LOADED_FEATURES.dup @orig_LOADED_FEATURES = $LOADED_FEATURES.dup
end end
@ -449,6 +463,10 @@ class Gem::TestCase < (defined?(Minitest::Test) ? Minitest::Test : MiniTest::Uni
Gem::Specification.unresolved_deps.clear Gem::Specification.unresolved_deps.clear
Gem::refresh Gem::refresh
@orig_hooks.each do |name, hooks|
Gem.send(name).replace hooks
end
@back_ui.close @back_ui.close
end end

View File

@ -80,8 +80,6 @@ module Gem::Util
end end
return system(*(cmds << opt)) return system(*(cmds << opt))
rescue TypeError rescue TypeError
require 'thread'
@silent_mutex ||= Mutex.new @silent_mutex ||= Mutex.new
@silent_mutex.synchronize do @silent_mutex.synchronize do
@ -118,4 +116,16 @@ module Gem::Util
end end
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 end

View File

@ -170,7 +170,10 @@ class Gem::Version
# True if the +version+ string matches RubyGems' requirements. # True if the +version+ string matches RubyGems' requirements.
def self.correct? version 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) !!(version.to_s =~ ANCHORED_VERSION_PATTERN)
end end
@ -325,7 +328,9 @@ class Gem::Version
segments.pop while segments.size > 2 segments.pop while segments.size > 2
segments.push 0 while segments.size < 2 segments.push 0 while segments.size < 2
"~> #{segments.join(".")}" recommendation = "~> #{segments.join(".")}"
recommendation += ".a" if prerelease?
recommendation
end end
## ##

View File

@ -1758,7 +1758,7 @@ class TestGem < Gem::TestCase
platform = " #{platform}" platform = " #{platform}"
end end
expected = if Gem::USE_BUNDLER_FOR_GEMDEPS expected = if Gem::USE_BUNDLER_FOR_GEMDEPS
<<-EXPECTED <<-EXPECTED
Could not find gem 'a#{platform}' in any of the gem sources listed in your Gemfile. Could not find gem 'a#{platform}' in any of the gem sources listed in your Gemfile.
You may need to `gem install -g` to install missing gems You may need to `gem install -g` to install missing gems

View File

@ -29,6 +29,12 @@ class TestGemCommandManager < Gem::TestCase
e.message e.message
end end
def test_find_alias_command
command = @command_manager.find_command 'i'
assert_kind_of Gem::Commands::InstallCommand, command
end
def test_find_command_ambiguous_exact def test_find_command_ambiguous_exact
ins_command = Class.new ins_command = Class.new
Gem::Commands.send :const_set, :InsCommand, ins_command Gem::Commands.send :const_set, :InsCommand, ins_command

View File

@ -9,13 +9,35 @@ class TestGemCommandsBuildCommand < Gem::TestCase
def setup def setup
super super
readme_file = File.join(@tempdir, 'README.md')
File.open readme_file, 'w' do |f|
f.write 'My awesome gem'
end
@gem = util_spec 'some_gem' do |s| @gem = util_spec 'some_gem' do |s|
s.rubyforge_project = 'example' s.rubyforge_project = 'example'
s.license = 'AGPL-3.0'
s.files = ['README.md']
end end
@cmd = Gem::Commands::BuildCommand.new @cmd = Gem::Commands::BuildCommand.new
end end
def test_handle_options
@cmd.handle_options %w[--force --strict]
assert @cmd.options[:force]
assert @cmd.options[:strict]
end
def test_handle_options_defaults
@cmd.handle_options []
refute @cmd.options[:force]
refute @cmd.options[:strict]
end
def test_execute def test_execute
gemspec_file = File.join(@tempdir, @gem.spec_name) gemspec_file = File.join(@tempdir, @gem.spec_name)
@ -23,7 +45,55 @@ class TestGemCommandsBuildCommand < Gem::TestCase
gs.write @gem.to_ruby gs.write @gem.to_ruby
end end
util_test_build_gem @gem, gemspec_file @cmd.options[:args] = [gemspec_file]
util_test_build_gem @gem
end
def test_execute_strict_without_warnings
gemspec_file = File.join(@tempdir, @gem.spec_name)
File.open gemspec_file, 'w' do |gs|
gs.write @gem.to_ruby
end
@cmd.options[:strict] = true
@cmd.options[:args] = [gemspec_file]
util_test_build_gem @gem
end
def test_execute_strict_with_warnings
bad_gem = util_spec 'some_bad_gem' do |s|
s.rubyforge_project = 'example'
s.files = ['README.md']
end
gemspec_file = File.join(@tempdir, bad_gem.spec_name)
File.open gemspec_file, 'w' do |gs|
gs.write bad_gem.to_ruby
end
@cmd.options[:args] = [gemspec_file]
@cmd.options[:strict] = true
use_ui @ui do
Dir.chdir @tempdir do
assert_raises Gem::InvalidSpecificationException do
@cmd.execute
end
end
end
error = @ui.error.split "\n"
assert_equal "WARNING: licenses is empty, but is recommended. Use a license identifier from", error.shift
assert_equal "http://spdx.org/licenses or 'Nonstandard' for a nonstandard license.", error.shift
assert_equal "WARNING: See http://guides.rubygems.org/specification-reference/ for help", error.shift
assert_equal [], error
gem_file = File.join @tempdir, File.basename(@gem.cache_file)
refute File.exist?(gem_file)
end end
def test_execute_bad_spec def test_execute_bad_spec
@ -67,9 +137,14 @@ class TestGemCommandsBuildCommand < Gem::TestCase
def test_execute_outside_dir def test_execute_outside_dir
gemspec_dir = File.join @tempdir, 'build_command_gem' gemspec_dir = File.join @tempdir, 'build_command_gem'
gemspec_file = File.join gemspec_dir, @gem.spec_name gemspec_file = File.join gemspec_dir, @gem.spec_name
readme_file = File.join gemspec_dir, 'README.md'
FileUtils.mkdir_p gemspec_dir FileUtils.mkdir_p gemspec_dir
File.open readme_file, 'w' do |f|
f.write "My awesome gem"
end
File.open gemspec_file, 'w' do |gs| File.open gemspec_file, 'w' do |gs|
gs.write @gem.to_ruby gs.write @gem.to_ruby
end end
@ -103,12 +178,12 @@ class TestGemCommandsBuildCommand < Gem::TestCase
gs.write @gem.to_ruby gs.write @gem.to_ruby
end end
util_test_build_gem @gem, gemspec_file
end
def util_test_build_gem(gem, gemspec_file, check_licenses=true)
@cmd.options[:args] = [gemspec_file] @cmd.options[:args] = [gemspec_file]
util_test_build_gem @gem
end
def util_test_build_gem(gem)
use_ui @ui do use_ui @ui do
Dir.chdir @tempdir do Dir.chdir @tempdir do
@cmd.execute @cmd.execute
@ -122,10 +197,6 @@ class TestGemCommandsBuildCommand < Gem::TestCase
assert_equal " File: some_gem-2.gem", output.shift assert_equal " File: some_gem-2.gem", output.shift
assert_equal [], output assert_equal [], output
if check_licenses
assert_match "WARNING: licenses is empty", @ui.error
end
gem_file = File.join @tempdir, File.basename(gem.cache_file) gem_file = File.join @tempdir, File.basename(gem.cache_file)
assert File.exist?(gem_file) assert File.exist?(gem_file)
@ -147,7 +218,7 @@ class TestGemCommandsBuildCommand < Gem::TestCase
@cmd.options[:args] = [gemspec_file] @cmd.options[:args] = [gemspec_file]
@cmd.options[:force] = true @cmd.options[:force] = true
util_test_build_gem @gem, gemspec_file, false util_test_build_gem @gem
end end
CERT_FILE = cert_path 'public3072' CERT_FILE = cert_path 'public3072'
@ -169,7 +240,9 @@ class TestGemCommandsBuildCommand < Gem::TestCase
gs.write spec.to_ruby gs.write spec.to_ruby
end end
util_test_build_gem spec, gemspec_file @cmd.options[:args] = [gemspec_file]
util_test_build_gem spec
trust_dir.trust_cert OpenSSL::X509::Certificate.new(File.read(CERT_FILE)) trust_dir.trust_cert OpenSSL::X509::Certificate.new(File.read(CERT_FILE))

View File

@ -236,5 +236,32 @@ class TestGemCommandsCleanupCommand < Gem::TestCase
refute_path_exists d_1.gem_dir refute_path_exists d_1.gem_dir
refute_path_exists e_1.gem_dir refute_path_exists e_1.gem_dir
end end
def test_execute_user_install
c_1, = util_gem 'c', '1.0'
c_2, = util_gem 'c', '1.1'
d_1, = util_gem 'd', '1.0'
d_2, = util_gem 'd', '1.1'
c_1 = install_gem c_1, :user_install => true # pick up user install path
c_2 = install_gem c_2, :user_install => true # pick up user install path
d_1 = install_gem d_1
d_2 = install_gem d_2
Gem::Specification.dirs = [Gem.dir, Gem.user_dir]
@cmd.handle_options %w[--user-install]
@cmd.options[:args] = []
@cmd.execute
refute_path_exists c_1.gem_dir
assert_path_exists c_2.gem_dir
assert_path_exists d_1.gem_dir
assert_path_exists d_2.gem_dir
end
end end

View File

@ -229,7 +229,7 @@ ERROR: Could not find a valid gem 'bar' (= 0.5) (required by 'foo' (>= 0)) in a
@cmd.handle_options %w[-p=foo.bar.com] @cmd.handle_options %w[-p=foo.bar.com]
end end
assert_match "Invalid uri scheme for =foo.bar.com\nPreface URLs with one of [\"http://\", \"https://\", \"file://\", \"s3://\"]", e.message assert_match "Invalid uri scheme for =foo.bar.com\nPreface URLs with one of [\"http://\", \"https://\", \"file://\", \"s3://\"]", e.message
end end
end end

View File

@ -68,4 +68,33 @@ class TestGemCommandsOpenCommand < Gem::TestCase
assert_equal "", @ui.error assert_equal "", @ui.error
end end
def test_default_gem
@cmd.options[:version] = "1.0"
@cmd.options[:args] = %w[foo]
version = @cmd.options[:version]
@cmd.define_singleton_method(:spec_for) do |name|
spec = Gem::Specification.find_all_by_name(name, version).first
spec.define_singleton_method(:default_gem?) do
true
end
return spec if spec
say "Unable to find gem '#{name}'"
end
gem("foo", "1.0")
assert_raises Gem::MockGemUi::TermError do
use_ui @ui do
@cmd.execute
end
end
assert_match %r|'foo' is a default gem and can't be opened\.| , @ui.output
assert_equal "", @ui.error
end
end end

View File

@ -433,6 +433,39 @@ class TestGemCommandsPristineCommand < Gem::TestCase
refute File.exist? gem_lib refute File.exist? gem_lib
end end
def test_execute_bindir
a = util_spec 'a' do |s|
s.name = "test_gem"
s.executables = %w[foo]
s.files = %w[bin/foo]
end
write_file File.join(@tempdir, 'bin', 'foo') do |fp|
fp.puts "#!/usr/bin/ruby"
end
write_file File.join(@tempdir, 'test_bin', 'foo') do |fp|
fp.puts "#!/usr/bin/ruby"
end
install_gem a
gem_exec = File.join @gemhome, 'bin', 'foo'
gem_bindir = File.join @tempdir, 'test_bin', 'foo'
FileUtils.rm gem_exec
FileUtils.rm gem_bindir
@cmd.handle_options ["--all", "--only-executables", "--bindir", "#{gem_bindir}"]
use_ui @ui do
@cmd.execute
end
refute File.exist? gem_exec
assert File.exist? gem_bindir
end
def test_execute_unknown_gem_at_remote_source def test_execute_unknown_gem_at_remote_source
install_specs util_spec 'a' install_specs util_spec 'a'

View File

@ -95,6 +95,26 @@ class TestGemCommandsPushCommand < Gem::TestCase
@fetcher.last_request["Content-Type"] @fetcher.last_request["Content-Type"]
end end
def test_execute_allowed_push_host
@spec, @path = util_gem "freebird", "1.0.1" do |spec|
spec.metadata['allowed_push_host'] = "https://privategemserver.example"
end
@response = "Successfully registered gem: freewill (1.0.0)"
@fetcher.data["#{@spec.metadata['allowed_push_host']}/api/v1/gems"] = [@response, 200, 'OK']
@fetcher.data["#{Gem.host}/api/v1/gems"] =
['fail', 500, 'Internal Server Error']
@cmd.options[:args] = [@path]
@cmd.execute
assert_equal Net::HTTP::Post, @fetcher.last_request.class
assert_equal Gem.read_binary(@path), @fetcher.last_request.body
assert_equal "application/octet-stream",
@fetcher.last_request["Content-Type"]
end
def test_sending_when_default_host_disabled def test_sending_when_default_host_disabled
Gem.configuration.disable_default_gem_server = true Gem.configuration.disable_default_gem_server = true
response = "You must specify a gem server" response = "You must specify a gem server"

View File

@ -25,6 +25,7 @@ class TestGemExtCmakeBuilder < Gem::TestCase
File.open File.join(@ext, 'CMakeLists.txt'), 'w' do |cmakelists| File.open File.join(@ext, 'CMakeLists.txt'), 'w' do |cmakelists|
cmakelists.write <<-eo_cmake cmakelists.write <<-eo_cmake
cmake_minimum_required(VERSION 2.6) cmake_minimum_required(VERSION 2.6)
project(self_build LANGUAGES NONE)
install (FILES test.txt DESTINATION bin) install (FILES test.txt DESTINATION bin)
eo_cmake eo_cmake
end end

View File

@ -179,8 +179,6 @@ class TestGemGemcutterUtilities < Gem::TestCase
end end
def test_sign_in_with_bad_credentials def test_sign_in_with_bad_credentials
skip 'Always uses $stdin on windows' if Gem.win_platform?
assert_raises Gem::MockGemUi::TermError do assert_raises Gem::MockGemUi::TermError do
util_sign_in ['Access Denied.', 403, 'Forbidden'] util_sign_in ['Access Denied.', 403, 'Forbidden']
end end
@ -190,8 +188,6 @@ class TestGemGemcutterUtilities < Gem::TestCase
end end
def util_sign_in response, host = nil, args = [] def util_sign_in response, host = nil, args = []
skip 'Always uses $stdin on windows' if Gem.win_platform?
email = 'you@example.com' email = 'you@example.com'
password = 'secret' password = 'secret'

View File

@ -141,7 +141,7 @@ end
end end
File.open File.join(util_inst_bindir, 'executable'), 'w' do |io| File.open File.join(util_inst_bindir, 'executable'), 'w' do |io|
io.write <<-EXEC io.write <<-EXEC
#!/usr/local/bin/ruby #!/usr/local/bin/ruby
# #
# This file was generated by RubyGems # This file was generated by RubyGems
@ -336,6 +336,9 @@ gem 'other', version
bin_dir = Gem.win_platform? ? File.expand_path(ENV["WINDIR"]).upcase : bin_dir = Gem.win_platform? ? File.expand_path(ENV["WINDIR"]).upcase :
"/usr/bin" "/usr/bin"
old_path = ENV["PATH"]
ENV["PATH"] = [ENV["PATH"], bin_dir].compact.join(File::PATH_SEPARATOR)
options = { options = {
:bin_dir => bin_dir, :bin_dir => bin_dir,
:install_dir => "/non/existent" :install_dir => "/non/existent"
@ -350,6 +353,9 @@ gem 'other', version
end end
assert_equal "", @ui.error assert_equal "", @ui.error
ensure
ENV["PATH"] = old_path
end end
def test_generate_bin_script def test_generate_bin_script
@ -1409,7 +1415,7 @@ gem 'other', version
def spec.full_name # so the spec is buildable def spec.full_name # so the spec is buildable
"malicious-1" "malicious-1"
end end
def spec.validate; end def spec.validate packaging, strict; end
util_build_gem spec util_build_gem spec

View File

@ -150,7 +150,7 @@ class TestGemPackage < Gem::Package::TarTestCase
end end
def test_add_files_symlink def test_add_files_symlink
skip 'symlink not supported' if Gem.win_platform? skip 'symlink not supported' if Gem.win_platform? && RUBY_VERSION < '2.3'
spec = Gem::Specification.new spec = Gem::Specification.new
spec.files = %w[lib/code.rb lib/code_sym.rb] spec.files = %w[lib/code.rb lib/code_sym.rb]
@ -159,7 +159,15 @@ class TestGemPackage < Gem::Package::TarTestCase
File.open 'lib/code.rb', 'w' do |io| io.write '# lib/code.rb' end File.open 'lib/code.rb', 'w' do |io| io.write '# lib/code.rb' end
# NOTE: 'code.rb' is correct, because it's relative to lib/code_sym.rb # NOTE: 'code.rb' is correct, because it's relative to lib/code_sym.rb
File.symlink('code.rb', 'lib/code_sym.rb') begin
File.symlink('code.rb', 'lib/code_sym.rb')
rescue Errno::EACCES => e
if win_platform?
skip "symlink - must be admin with no UAC on Windows"
else
raise e
end
end
package = Gem::Package.new 'bogus.gem' package = Gem::Package.new 'bogus.gem'
package.spec = spec package.spec = spec
@ -315,6 +323,19 @@ class TestGemPackage < Gem::Package::TarTestCase
assert_equal 'missing value for attribute summary', e.message assert_equal 'missing value for attribute summary', e.message
end end
def test_build_invalid_arguments
spec = Gem::Specification.new 'build', '1'
package = Gem::Package.new spec.file_name
package.spec = spec
e = assert_raises ArgumentError do
package.build true, true
end
assert_equal "skip_validation = true and strict_validation = true are incompatible", e.message
end
def test_build_signed def test_build_signed
skip 'openssl is missing' unless defined?(OpenSSL::SSL) skip 'openssl is missing' unless defined?(OpenSSL::SSL)
@ -451,7 +472,7 @@ class TestGemPackage < Gem::Package::TarTestCase
end end
def test_extract_tar_gz_symlink_relative_path def test_extract_tar_gz_symlink_relative_path
skip 'symlink not supported' if Gem.win_platform? skip 'symlink not supported' if Gem.win_platform? && RUBY_VERSION < '2.3'
package = Gem::Package.new @gem package = Gem::Package.new @gem
@ -461,7 +482,15 @@ class TestGemPackage < Gem::Package::TarTestCase
tar.add_symlink 'lib/foo.rb', '../relative.rb', 0644 tar.add_symlink 'lib/foo.rb', '../relative.rb', 0644
end end
package.extract_tar_gz tgz_io, @destination begin
package.extract_tar_gz tgz_io, @destination
rescue Errno::EACCES => e
if win_platform?
skip "symlink - must be admin with no UAC on Windows"
else
raise e
end
end
extracted = File.join @destination, 'lib/foo.rb' extracted = File.join @destination, 'lib/foo.rb'
assert_path_exists extracted assert_path_exists extracted
@ -472,28 +501,34 @@ class TestGemPackage < Gem::Package::TarTestCase
end end
def test_extract_symlink_parent def test_extract_symlink_parent
skip 'symlink not supported' if Gem.win_platform? skip 'symlink not supported' if Gem.win_platform? && RUBY_VERSION < '2.3'
package = Gem::Package.new @gem package = Gem::Package.new @gem
tgz_io = util_tar_gz do |tar| tgz_io = util_tar_gz do |tar|
tar.mkdir 'lib', 0755 tar.mkdir 'lib', 0755
tar.add_symlink 'lib/link', '../..', 0644 tar.add_symlink 'lib/link', '../..', 0644
tar.add_file 'lib/link/outside.txt', 0644 do |io| io.write 'hi' end tar.add_file 'lib/link/outside.txt', 0644 do |io| io.write 'hi' end
end end
# Extract into a subdirectory of @destination; if this test fails it writes # Extract into a subdirectory of @destination; if this test fails it writes
# a file outside destination_subdir, but we want the file to remain inside # a file outside destination_subdir, but we want the file to remain inside
# @destination so it will be cleaned up. # @destination so it will be cleaned up.
destination_subdir = File.join @destination, 'subdir' destination_subdir = File.join @destination, 'subdir'
FileUtils.mkdir_p destination_subdir FileUtils.mkdir_p destination_subdir
e = assert_raises Gem::Package::PathError do e = assert_raises(Gem::Package::PathError, Errno::EACCES) do
package.extract_tar_gz tgz_io, destination_subdir package.extract_tar_gz tgz_io, destination_subdir
end end
assert_equal("installing into parent path lib/link/outside.txt of " + if Gem::Package::PathError === e
"#{destination_subdir} is not allowed", e.message) assert_equal("installing into parent path lib/link/outside.txt of " +
"#{destination_subdir} is not allowed", e.message)
elsif win_platform?
skip "symlink - must be admin with no UAC on Windows"
else
raise e
end
end end
def test_extract_tar_gz_directory def test_extract_tar_gz_directory

View File

@ -34,6 +34,10 @@ class TestGemPackageTarReaderEntry < Gem::Package::TarTestCase
assert_equal 1, @entry.bytes_read assert_equal 1, @entry.bytes_read
end end
def test_size
assert_equal @contents.size, @entry.size
end
def test_close def test_close
@entry.close @entry.close
@ -129,6 +133,13 @@ class TestGemPackageTarReaderEntry < Gem::Package::TarTestCase
assert_equal @contents[0...100], @entry.read(100) assert_equal @contents[0...100], @entry.read(100)
end end
def test_readpartial
assert_raises(EOFError) do
@entry.read(@contents.size)
@entry.readpartial(1)
end
end
def test_rewind def test_rewind
char = @entry.getc char = @entry.getc

View File

@ -118,4 +118,21 @@ class TestGemPathSupport < Gem::TestCase
ps = Gem::PathSupport.new "GEM_SPEC_CACHE" => "foo" ps = Gem::PathSupport.new "GEM_SPEC_CACHE" => "foo"
assert_equal "foo", ps.spec_cache_dir assert_equal "foo", ps.spec_cache_dir
end end
def test_gem_paths_do_not_contain_symlinks
dir = "#{@tempdir}/realgemdir"
symlink = "#{@tempdir}/symdir"
Dir.mkdir dir
begin
File.symlink(dir, symlink)
rescue NotImplementedError, SystemCallError
skip 'symlinks not supported'
end
not_existing = "#{@tempdir}/does_not_exist"
path = "#{symlink}#{File::PATH_SEPARATOR}#{not_existing}"
ps = Gem::PathSupport.new "GEM_PATH" => path, "GEM_HOME" => symlink
assert_equal dir, ps.home
assert_equal [dir, not_existing], ps.path
end
end end

View File

@ -731,10 +731,9 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
assert_equal "murphy", fetcher.fetch_path(@server_uri) assert_equal "murphy", fetcher.fetch_path(@server_uri)
end end
def test_fetch_s3 def assert_fetch_s3(url)
fetcher = Gem::RemoteFetcher.new nil fetcher = Gem::RemoteFetcher.new nil
@fetcher = fetcher @fetcher = fetcher
url = 's3://testuser:testpass@my-bucket/gems/specs.4.8.gz'
$fetched_uri = nil $fetched_uri = nil
def fetcher.request(uri, request_class, last_modified = nil) def fetcher.request(uri, request_class, last_modified = nil)
@ -756,15 +755,64 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
$fetched_uri = nil $fetched_uri = nil
end end
def test_fetch_s3_no_creds def test_fetch_s3_config_creds
Gem.configuration[:s3_source] = {
'my-bucket' => {:id => 'testuser', :secret => 'testpass'}
}
url = 's3://my-bucket/gems/specs.4.8.gz'
assert_fetch_s3 url
ensure
Gem.configuration[:s3_source] = nil
end
def test_fetch_s3_url_creds
url = 's3://testuser:testpass@my-bucket/gems/specs.4.8.gz'
assert_fetch_s3 url
end
def refute_fetch_s3(url, expected_message)
fetcher = Gem::RemoteFetcher.new nil fetcher = Gem::RemoteFetcher.new nil
@fetcher = fetcher @fetcher = fetcher
url = 's3://my-bucket/gems/specs.4.8.gz'
e = assert_raises Gem::RemoteFetcher::FetchError do e = assert_raises Gem::RemoteFetcher::FetchError do
fetcher.fetch_s3 URI.parse(url) fetcher.fetch_s3 URI.parse(url)
end end
assert_match "credentials needed", e.message assert_match expected_message, e.message
end
def test_fetch_s3_no_source_key
url = 's3://my-bucket/gems/specs.4.8.gz'
refute_fetch_s3 url, 'no s3_source key exists in .gemrc'
end
def test_fetch_s3_no_host
Gem.configuration[:s3_source] = {
'my-bucket' => {:id => 'testuser', :secret => 'testpass'}
}
url = 's3://other-bucket/gems/specs.4.8.gz'
refute_fetch_s3 url, 'no key for host other-bucket in s3_source in .gemrc'
ensure
Gem.configuration[:s3_source] = nil
end
def test_fetch_s3_no_id
Gem.configuration[:s3_source] = { 'my-bucket' => {:secret => 'testpass'} }
url = 's3://my-bucket/gems/specs.4.8.gz'
refute_fetch_s3 url, 's3_source for my-bucket missing id or secret'
ensure
Gem.configuration[:s3_source] = nil
end
def test_fetch_s3_no_secret
Gem.configuration[:s3_source] = { 'my-bucket' => {:id => 'testuser'} }
url = 's3://my-bucket/gems/specs.4.8.gz'
refute_fetch_s3 url, 's3_source for my-bucket missing id or secret'
ensure
Gem.configuration[:s3_source] = nil
end end
def test_observe_no_proxy_env_single_host def test_observe_no_proxy_env_single_host
@ -846,9 +894,9 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
with_configured_fetcher( with_configured_fetcher(
":ssl_ca_cert: #{temp_ca_cert}\n" + ":ssl_ca_cert: #{temp_ca_cert}\n" +
":ssl_client_cert: #{temp_client_cert}\n") do |fetcher| ":ssl_client_cert: #{temp_client_cert}\n") do |fetcher|
assert_raises Gem::RemoteFetcher::FetchError do assert_raises Gem::RemoteFetcher::FetchError do
fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}/yaml") fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}/yaml")
end end
end end
end end

View File

@ -299,7 +299,7 @@ class TestGemResolver < Gem::TestCase
a2_p1 = a3_p2 = nil a2_p1 = a3_p2 = nil
spec_fetcher do |fetcher| spec_fetcher do |fetcher|
fetcher.spec 'a', 2 fetcher.spec 'a', 2
a2_p1 = fetcher.spec 'a', 2 do |s| s.platform = Gem::Platform.local end a2_p1 = fetcher.spec 'a', 2 do |s| s.platform = Gem::Platform.local end
a3_p2 = fetcher.spec 'a', 3 do |s| s.platform = unknown end a3_p2 = fetcher.spec 'a', 3 do |s| s.platform = unknown end
end end

View File

@ -135,9 +135,11 @@ toqvglr0kdbknSRRjBVLK6tsgr07aLT9gNP7mTW2PA==
def test_sign_expired def test_sign_expired
signer = Gem::Security::Signer.new PRIVATE_KEY, [EXPIRED_CERT] signer = Gem::Security::Signer.new PRIVATE_KEY, [EXPIRED_CERT]
assert_raises Gem::Security::Exception do e = assert_raises Gem::Security::Exception do
signer.sign 'hello' signer.sign 'hello'
end end
assert_match "certificate /CN=nobody/DC=example not valid after 1970-01-01 00:00:00 UTC", e.message
end end
def test_sign_expired_auto_update def test_sign_expired_auto_update

View File

@ -108,7 +108,7 @@ end
# objects are present in the @stubs collection. This test verifies that # objects are present in the @stubs collection. This test verifies that
# this scenario works correctly. # this scenario works correctly.
Gem::Specification.all = [spec] Gem::Specification.all = [spec]
Gem::Specification.find_active_stub_by_path('foo') assert_equal spec, Gem::Specification.find_active_stub_by_path('foo')
end end
def test_self_activate def test_self_activate
@ -387,8 +387,8 @@ end
def test_self_activate_checks_dependencies def test_self_activate_checks_dependencies
a = util_spec 'a', '1.0' a = util_spec 'a', '1.0'
a.add_dependency 'c', '= 1.0' a.add_dependency 'c', '= 1.0'
a.add_dependency 'b', '~> 1.0' a.add_dependency 'b', '~> 1.0'
b1 = util_spec 'b', '1.0' b1 = util_spec 'b', '1.0'
b2 = util_spec 'b', '2.0' b2 = util_spec 'b', '2.0'
@ -1126,6 +1126,88 @@ dependencies: []
refute_includes Gem::Specification.stubs.map { |s| s.full_name }, 'a-1' refute_includes Gem::Specification.stubs.map { |s| s.full_name }, 'a-1'
end end
def test_self_stubs
Gem.loaded_specs.clear
Gem::Specification.class_variable_set(:@@stubs, nil)
dir_standard_specs = File.join Gem.dir, 'specifications'
dir_default_specs = Gem::BasicSpecification.default_specifications_dir
# Create gemspecs in three locations used in stubs
loaded_spec = Gem::Specification.new 'a', '3'
Gem.loaded_specs['a'] = loaded_spec
save_gemspec 'a', '2', dir_default_specs
save_gemspec 'a', '1', dir_standard_specs
full_names = ['a-3', 'a-2', 'a-1']
assert_equal full_names, Gem::Specification.stubs.map { |s| s.full_name }
Gem.loaded_specs.delete 'a'
Gem::Specification.class_variable_set(:@@stubs, nil)
end
def test_self_stubs_for
Gem.loaded_specs.clear
Gem::Specification.class_variable_set(:@@stubs, nil)
dir_standard_specs = File.join Gem.dir, 'specifications'
dir_default_specs = Gem::BasicSpecification.default_specifications_dir
# Create gemspecs in three locations used in stubs
loaded_spec = Gem::Specification.new 'a', '3'
Gem.loaded_specs['a'] = loaded_spec
save_gemspec 'a', '2', dir_default_specs
save_gemspec 'a', '1', dir_standard_specs
full_names = ['a-3', 'a-2', 'a-1']
full_names = Gem::Specification.stubs_for('a').map { |s| s.full_name }
assert_equal full_names, Gem::Specification.stubs_for('a').map { |s| s.full_name }
assert_equal 1, Gem::Specification.class_variable_get(:@@stubs_by_name).length
Gem.loaded_specs.delete 'a'
Gem::Specification.class_variable_set(:@@stubs, nil)
end
def test_self_stubs_for_mult_platforms
# gems for two different platforms are installed with --user-install
# the correct one should be returned in the array
orig_platform = Gem.platforms.dup
# create user spec
user_spec_dir = File.join Gem.user_dir, 'specifications'
FileUtils.mkdir_p(user_spec_dir) unless Dir.exist? user_spec_dir
# dirs doesn't include user ?
Gem::Specification.dirs << user_spec_dir
gem = 'mingw'
v = '1.1.1'
platforms = ['x86-mingw32', 'x64-mingw32']
#create specs
platforms.each do |plat|
spec = Gem::Specification.new(gem, v) { |s| s.platform = plat }
File.open File.join(user_spec_dir, "#{gem}-#{v}-#{plat}.gemspec"), 'w' do |io|
io.write spec.to_ruby
end
end
platforms.each do |plat|
cur_plat = Gem::Platform.new plat
Gem.platforms = ['ruby', cur_plat]
Gem::Specification.class_variable_set :@@stubs, nil
Gem::Specification.stubs if plat == platforms.last # test loading via stubs
t = Gem::Specification.stubs_for 'mingw'
assert_equal 1, t.length
assert_equal cur_plat, t.first.platform
end
Gem.platforms = orig_platform
end
DATA_PATH = File.expand_path "../data", __FILE__ DATA_PATH = File.expand_path "../data", __FILE__
def test_handles_private_null_type def test_handles_private_null_type
@ -2615,16 +2697,6 @@ end
expected = <<-EXPECTED expected = <<-EXPECTED
#{w}: prerelease dependency on b (>= 1.0.rc1) is not recommended #{w}: prerelease dependency on b (>= 1.0.rc1) is not recommended
#{w}: prerelease dependency on c (>= 2.0.rc2, development) is not recommended #{w}: prerelease dependency on c (>= 2.0.rc2, development) is not recommended
#{w}: pessimistic dependency on d (~> 1.2.3) may be overly strict
if d is semantically versioned, use:
add_runtime_dependency 'd', '~> 1.2', '>= 1.2.3'
if d is not semantically versioned, you can bypass this warning with:
add_runtime_dependency 'd', '>= 1.2.3', '< 1.3.a'
#{w}: pessimistic dependency on e (~> 1.2.3.4) may be overly strict
if e is semantically versioned, use:
add_runtime_dependency 'e', '~> 1.2', '>= 1.2.3.4'
if e is not semantically versioned, you can bypass this warning with:
add_runtime_dependency 'e', '>= 1.2.3.4', '< 1.2.4.a'
#{w}: open-ended dependency on i (>= 1.2) is not recommended #{w}: open-ended dependency on i (>= 1.2) is not recommended
if i is semantically versioned, use: if i is semantically versioned, use:
add_runtime_dependency 'i', '~> 1.2' add_runtime_dependency 'i', '~> 1.2'
@ -2637,11 +2709,6 @@ end
#{w}: open-ended dependency on l (> 1.2.3) is not recommended #{w}: open-ended dependency on l (> 1.2.3) is not recommended
if l is semantically versioned, use: if l is semantically versioned, use:
add_runtime_dependency 'l', '~> 1.2', '> 1.2.3' add_runtime_dependency 'l', '~> 1.2', '> 1.2.3'
#{w}: pessimistic dependency on m (~> 2.1.0) may be overly strict
if m is semantically versioned, use:
add_runtime_dependency 'm', '~> 2.1', '>= 2.1.0'
if m is not semantically versioned, you can bypass this warning with:
add_runtime_dependency 'm', '>= 2.1.0', '< 2.2.a'
#{w}: See http://guides.rubygems.org/specification-reference/ for help #{w}: See http://guides.rubygems.org/specification-reference/ for help
EXPECTED EXPECTED
@ -2844,6 +2911,58 @@ duplicate dependency on c (>= 1.2.3, development), (~> 1.2) use:
@a1.files @a1.files
end end
def test_unresolved_specs
specification = Gem::Specification.clone
specification.define_singleton_method(:unresolved_deps) do
{ b: Gem::Dependency.new("x","1") }
end
specification.define_singleton_method(:find_all_by_name) do |dep_name|
[]
end
expected = <<-EXPECTED
WARN: Unresolved or ambigious specs during Gem::Specification.reset:
x (= 1)
WARN: Clearing out unresolved specs. Try 'gem cleanup <gem>'
Please report a bug if this causes problems.
EXPECTED
assert_output nil, expected do
specification.reset
end
end
def test_unresolved_specs_with_versions
specification = Gem::Specification.clone
specification.define_singleton_method(:unresolved_deps) do
{ b: Gem::Dependency.new("x","1") }
end
specification.define_singleton_method(:find_all_by_name) do |dep_name|
[
specification.new { |s| s.name = "z", s.version = Gem::Version.new("1") },
specification.new { |s| s.name = "z", s.version = Gem::Version.new("2") }
]
end
expected = <<-EXPECTED
WARN: Unresolved or ambigious specs during Gem::Specification.reset:
x (= 1)
Available/installed versions of this gem:
- 1
- 2
WARN: Clearing out unresolved specs. Try 'gem cleanup <gem>'
Please report a bug if this causes problems.
EXPECTED
assert_output nil, expected do
specification.reset
end
end
def test_validate_files_recursive def test_validate_files_recursive
util_setup_validate util_setup_validate
FileUtils.touch @a1.file_name FileUtils.touch @a1.file_name

View File

@ -21,6 +21,10 @@ class TestGemText < Gem::TestCase
assert_equal " text to wrap", format_text("text to wrap", 40, 2) assert_equal " text to wrap", format_text("text to wrap", 40, 2)
end end
def test_format_text_no_space
assert_equal "texttowr\nap", format_text("texttowrap", 8)
end
def test_format_text_trailing # for two spaces after . def test_format_text_trailing # for two spaces after .
text = <<-TEXT text = <<-TEXT
This line is really, really long. So long, in fact, that it is more than eighty characters long! The purpose of this line is for testing wrapping behavior because sometimes people don't wrap their text to eighty characters. Without the wrapping, the text might not look good in the RSS feed. This line is really, really long. So long, in fact, that it is more than eighty characters long! The purpose of this line is for testing wrapping behavior because sometimes people don't wrap their text to eighty characters. Without the wrapping, the text might not look good in the RSS feed.

View File

@ -38,6 +38,8 @@ class TestGemUtil < Gem::TestCase
# impossible to cd into it and its children # impossible to cd into it and its children
FileUtils.chmod(0666, 'd/e') FileUtils.chmod(0666, 'd/e')
skip 'skipped in root privilege' if Process.uid.zero?
paths = Gem::Util.traverse_parents('d/e/f').to_a paths = Gem::Util.traverse_parents('d/e/f').to_a
assert_equal File.join(@tempdir, 'd'), paths[0] assert_equal File.join(@tempdir, 'd'), paths[0]

View File

@ -46,7 +46,11 @@ class TestGemVersion < Gem::TestCase
def test_class_correct def test_class_correct
assert_equal true, Gem::Version.correct?("5.1") assert_equal true, Gem::Version.correct?("5.1")
assert_equal false, Gem::Version.correct?("an incorrect version") assert_equal false, Gem::Version.correct?("an incorrect version")
assert_equal false, Gem::Version.correct?(nil)
expected = "nil versions are discouraged and will be deprecated in Rubygems 4\n"
assert_output nil, expected do
Gem::Version.correct?(nil)
end
end end
def test_class_new_subclass def test_class_new_subclass
@ -158,11 +162,25 @@ class TestGemVersion < Gem::TestCase
def test_approximate_recommendation def test_approximate_recommendation
assert_approximate_equal "~> 1.0", "1" assert_approximate_equal "~> 1.0", "1"
assert_approximate_satisfies_itself "1"
assert_approximate_equal "~> 1.0", "1.0" assert_approximate_equal "~> 1.0", "1.0"
assert_approximate_satisfies_itself "1.0"
assert_approximate_equal "~> 1.2", "1.2" assert_approximate_equal "~> 1.2", "1.2"
assert_approximate_satisfies_itself "1.2"
assert_approximate_equal "~> 1.2", "1.2.0" assert_approximate_equal "~> 1.2", "1.2.0"
assert_approximate_satisfies_itself "1.2.0"
assert_approximate_equal "~> 1.2", "1.2.3" assert_approximate_equal "~> 1.2", "1.2.3"
assert_approximate_equal "~> 1.2", "1.2.3.a.4" assert_approximate_satisfies_itself "1.2.3"
assert_approximate_equal "~> 1.2.a", "1.2.3.a.4"
assert_approximate_satisfies_itself "1.2.3.a.4"
assert_approximate_equal "~> 1.9.a", "1.9.0.dev"
assert_approximate_satisfies_itself "1.9.0.dev"
end end
def test_to_s def test_to_s
@ -198,12 +216,20 @@ class TestGemVersion < Gem::TestCase
assert v(version).prerelease?, "#{version} is a prerelease" assert v(version).prerelease?, "#{version} is a prerelease"
end end
# Assert that +expected+ is the "approximate" recommendation for +version". # Assert that +expected+ is the "approximate" recommendation for +version+.
def assert_approximate_equal expected, version def assert_approximate_equal expected, version
assert_equal expected, v(version).approximate_recommendation assert_equal expected, v(version).approximate_recommendation
end end
# Assert that the "approximate" recommendation for +version+ satifies +version+.
def assert_approximate_satisfies_itself version
gem_version = v(version)
assert Gem::Requirement.new(gem_version.approximate_recommendation).satisfied_by?(gem_version)
end
# Assert that bumping the +unbumped+ version yields the +expected+. # Assert that bumping the +unbumped+ version yields the +expected+.
def assert_bumped_version_equal expected, unbumped def assert_bumped_version_equal expected, unbumped