зеркало из https://github.com/github/ruby.git
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:
Родитель
3a83ba90c3
Коммит
85d461456c
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
#++
|
||||
|
||||
require 'rubygems/user_interaction'
|
||||
require 'thread'
|
||||
|
||||
class Gem::Ext::Builder
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
##
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
##
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
require 'net/http'
|
||||
require 'thread'
|
||||
require 'time'
|
||||
require 'rubygems/user_interaction'
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
# frozen_string_literal: true
|
||||
require 'thread'
|
||||
|
||||
class Gem::Request::ConnectionPools # :nodoc:
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 then
|
||||
Gem::Security.write old_cert, old_cert_path
|
||||
unless File.exist?(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]
|
||||
end
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
##
|
||||
|
|
|
@ -1758,7 +1758,7 @@ class TestGem < Gem::TestCase
|
|||
platform = " #{platform}"
|
||||
end
|
||||
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.
|
||||
You may need to `gem install -g` to install missing gems
|
||||
|
||||
|
|
|
@ -29,6 +29,12 @@ class TestGemCommandManager < Gem::TestCase
|
|||
e.message
|
||||
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
|
||||
ins_command = Class.new
|
||||
Gem::Commands.send :const_set, :InsCommand, ins_command
|
||||
|
|
|
@ -9,13 +9,35 @@ class TestGemCommandsBuildCommand < Gem::TestCase
|
|||
def setup
|
||||
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|
|
||||
s.rubyforge_project = 'example'
|
||||
s.license = 'AGPL-3.0'
|
||||
s.files = ['README.md']
|
||||
end
|
||||
|
||||
@cmd = Gem::Commands::BuildCommand.new
|
||||
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
|
||||
gemspec_file = File.join(@tempdir, @gem.spec_name)
|
||||
|
||||
|
@ -23,7 +45,55 @@ class TestGemCommandsBuildCommand < Gem::TestCase
|
|||
gs.write @gem.to_ruby
|
||||
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
|
||||
|
||||
def test_execute_bad_spec
|
||||
|
@ -67,9 +137,14 @@ class TestGemCommandsBuildCommand < Gem::TestCase
|
|||
def test_execute_outside_dir
|
||||
gemspec_dir = File.join @tempdir, 'build_command_gem'
|
||||
gemspec_file = File.join gemspec_dir, @gem.spec_name
|
||||
readme_file = File.join gemspec_dir, 'README.md'
|
||||
|
||||
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|
|
||||
gs.write @gem.to_ruby
|
||||
end
|
||||
|
@ -103,12 +178,12 @@ class TestGemCommandsBuildCommand < Gem::TestCase
|
|||
gs.write @gem.to_ruby
|
||||
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]
|
||||
|
||||
util_test_build_gem @gem
|
||||
end
|
||||
|
||||
def util_test_build_gem(gem)
|
||||
use_ui @ui do
|
||||
Dir.chdir @tempdir do
|
||||
@cmd.execute
|
||||
|
@ -122,10 +197,6 @@ class TestGemCommandsBuildCommand < Gem::TestCase
|
|||
assert_equal " File: some_gem-2.gem", output.shift
|
||||
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)
|
||||
assert File.exist?(gem_file)
|
||||
|
||||
|
@ -147,7 +218,7 @@ class TestGemCommandsBuildCommand < Gem::TestCase
|
|||
@cmd.options[:args] = [gemspec_file]
|
||||
@cmd.options[:force] = true
|
||||
|
||||
util_test_build_gem @gem, gemspec_file, false
|
||||
util_test_build_gem @gem
|
||||
end
|
||||
|
||||
CERT_FILE = cert_path 'public3072'
|
||||
|
@ -169,7 +240,9 @@ class TestGemCommandsBuildCommand < Gem::TestCase
|
|||
gs.write spec.to_ruby
|
||||
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))
|
||||
|
||||
|
|
|
@ -236,5 +236,32 @@ class TestGemCommandsCleanupCommand < Gem::TestCase
|
|||
refute_path_exists d_1.gem_dir
|
||||
refute_path_exists e_1.gem_dir
|
||||
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
|
||||
|
||||
|
|
|
@ -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]
|
||||
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
|
||||
|
||||
|
@ -451,23 +451,23 @@ ERROR: Possible alternatives: non_existent_with_hint
|
|||
specs = spec_fetcher do |fetcher|
|
||||
fetcher.gem 'a', 2
|
||||
end
|
||||
|
||||
|
||||
Gem.done_installing(&Gem::RDoc.method(:generation_hook))
|
||||
|
||||
|
||||
@cmd.options[:document] = %w[rdoc ri]
|
||||
@cmd.options[:domain] = :local
|
||||
@cmd.options[:install_dir] = 'whatever'
|
||||
|
||||
|
||||
a2 = specs['a-2']
|
||||
FileUtils.mv a2.cache_file, @tempdir
|
||||
|
||||
|
||||
@cmd.options[:args] = %w[a]
|
||||
|
||||
|
||||
use_ui @ui do
|
||||
# Don't use Dir.chdir with a block, it warnings a lot because
|
||||
# of a downstream Dir.chdir with a block
|
||||
old = Dir.getwd
|
||||
|
||||
|
||||
begin
|
||||
Dir.chdir @tempdir
|
||||
assert_raises Gem::MockGemUi::SystemExitException, @ui.error do
|
||||
|
@ -477,9 +477,9 @@ ERROR: Possible alternatives: non_existent_with_hint
|
|||
Dir.chdir old
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
wait_for_child_process_to_exit
|
||||
|
||||
|
||||
assert_path_exists 'whatever/doc/a-2', 'documentation not installed'
|
||||
end
|
||||
|
||||
|
|
|
@ -68,4 +68,33 @@ class TestGemCommandsOpenCommand < Gem::TestCase
|
|||
assert_equal "", @ui.error
|
||||
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
|
||||
|
|
|
@ -433,6 +433,39 @@ class TestGemCommandsPristineCommand < Gem::TestCase
|
|||
refute File.exist? gem_lib
|
||||
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
|
||||
install_specs util_spec 'a'
|
||||
|
||||
|
|
|
@ -95,6 +95,26 @@ class TestGemCommandsPushCommand < Gem::TestCase
|
|||
@fetcher.last_request["Content-Type"]
|
||||
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
|
||||
Gem.configuration.disable_default_gem_server = true
|
||||
response = "You must specify a gem server"
|
||||
|
|
|
@ -11,7 +11,7 @@ module TestGemCommandsQueryCommandSetup
|
|||
@specs = add_gems_to_fetcher
|
||||
@stub_ui = Gem::MockGemUi.new
|
||||
@stub_fetcher = Gem::FakeFetcher.new
|
||||
|
||||
|
||||
@stub_fetcher.data["#{@gem_repo}Marshal.#{Gem.marshal_version}"] = proc do
|
||||
raise Gem::RemoteFetcher::FetchError
|
||||
end
|
||||
|
|
|
@ -25,6 +25,7 @@ class TestGemExtCmakeBuilder < Gem::TestCase
|
|||
File.open File.join(@ext, 'CMakeLists.txt'), 'w' do |cmakelists|
|
||||
cmakelists.write <<-eo_cmake
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
project(self_build LANGUAGES NONE)
|
||||
install (FILES test.txt DESTINATION bin)
|
||||
eo_cmake
|
||||
end
|
||||
|
|
|
@ -179,8 +179,6 @@ class TestGemGemcutterUtilities < Gem::TestCase
|
|||
end
|
||||
|
||||
def test_sign_in_with_bad_credentials
|
||||
skip 'Always uses $stdin on windows' if Gem.win_platform?
|
||||
|
||||
assert_raises Gem::MockGemUi::TermError do
|
||||
util_sign_in ['Access Denied.', 403, 'Forbidden']
|
||||
end
|
||||
|
@ -190,8 +188,6 @@ class TestGemGemcutterUtilities < Gem::TestCase
|
|||
end
|
||||
|
||||
def util_sign_in response, host = nil, args = []
|
||||
skip 'Always uses $stdin on windows' if Gem.win_platform?
|
||||
|
||||
email = 'you@example.com'
|
||||
password = 'secret'
|
||||
|
||||
|
|
|
@ -141,7 +141,7 @@ end
|
|||
end
|
||||
|
||||
File.open File.join(util_inst_bindir, 'executable'), 'w' do |io|
|
||||
io.write <<-EXEC
|
||||
io.write <<-EXEC
|
||||
#!/usr/local/bin/ruby
|
||||
#
|
||||
# This file was generated by RubyGems
|
||||
|
@ -336,6 +336,9 @@ gem 'other', version
|
|||
bin_dir = Gem.win_platform? ? File.expand_path(ENV["WINDIR"]).upcase :
|
||||
"/usr/bin"
|
||||
|
||||
old_path = ENV["PATH"]
|
||||
ENV["PATH"] = [ENV["PATH"], bin_dir].compact.join(File::PATH_SEPARATOR)
|
||||
|
||||
options = {
|
||||
:bin_dir => bin_dir,
|
||||
:install_dir => "/non/existent"
|
||||
|
@ -350,6 +353,9 @@ gem 'other', version
|
|||
end
|
||||
|
||||
assert_equal "", @ui.error
|
||||
|
||||
ensure
|
||||
ENV["PATH"] = old_path
|
||||
end
|
||||
|
||||
def test_generate_bin_script
|
||||
|
@ -1409,7 +1415,7 @@ gem 'other', version
|
|||
def spec.full_name # so the spec is buildable
|
||||
"malicious-1"
|
||||
end
|
||||
def spec.validate; end
|
||||
def spec.validate packaging, strict; end
|
||||
|
||||
util_build_gem spec
|
||||
|
||||
|
|
|
@ -150,7 +150,7 @@ class TestGemPackage < Gem::Package::TarTestCase
|
|||
end
|
||||
|
||||
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.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
|
||||
|
||||
# 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.spec = spec
|
||||
|
@ -315,6 +323,19 @@ class TestGemPackage < Gem::Package::TarTestCase
|
|||
assert_equal 'missing value for attribute summary', e.message
|
||||
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
|
||||
skip 'openssl is missing' unless defined?(OpenSSL::SSL)
|
||||
|
||||
|
@ -451,7 +472,7 @@ class TestGemPackage < Gem::Package::TarTestCase
|
|||
end
|
||||
|
||||
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
|
||||
|
||||
|
@ -461,7 +482,15 @@ class TestGemPackage < Gem::Package::TarTestCase
|
|||
tar.add_symlink 'lib/foo.rb', '../relative.rb', 0644
|
||||
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'
|
||||
assert_path_exists extracted
|
||||
|
@ -472,28 +501,34 @@ class TestGemPackage < Gem::Package::TarTestCase
|
|||
end
|
||||
|
||||
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|
|
||||
tar.mkdir 'lib', 0755
|
||||
tar.add_symlink 'lib/link', '../..', 0644
|
||||
tar.add_file 'lib/link/outside.txt', 0644 do |io| io.write 'hi' end
|
||||
end
|
||||
tgz_io = util_tar_gz do |tar|
|
||||
tar.mkdir 'lib', 0755
|
||||
tar.add_symlink 'lib/link', '../..', 0644
|
||||
tar.add_file 'lib/link/outside.txt', 0644 do |io| io.write 'hi' end
|
||||
end
|
||||
|
||||
# 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
|
||||
# @destination so it will be cleaned up.
|
||||
destination_subdir = File.join @destination, 'subdir'
|
||||
FileUtils.mkdir_p destination_subdir
|
||||
# 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
|
||||
# @destination so it will be cleaned up.
|
||||
destination_subdir = File.join @destination, 'subdir'
|
||||
FileUtils.mkdir_p destination_subdir
|
||||
|
||||
e = assert_raises Gem::Package::PathError do
|
||||
package.extract_tar_gz tgz_io, destination_subdir
|
||||
end
|
||||
e = assert_raises(Gem::Package::PathError, Errno::EACCES) do
|
||||
package.extract_tar_gz tgz_io, destination_subdir
|
||||
end
|
||||
|
||||
assert_equal("installing into parent path lib/link/outside.txt of " +
|
||||
"#{destination_subdir} is not allowed", e.message)
|
||||
if Gem::Package::PathError === e
|
||||
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
|
||||
|
||||
def test_extract_tar_gz_directory
|
||||
|
|
|
@ -34,6 +34,10 @@ class TestGemPackageTarReaderEntry < Gem::Package::TarTestCase
|
|||
assert_equal 1, @entry.bytes_read
|
||||
end
|
||||
|
||||
def test_size
|
||||
assert_equal @contents.size, @entry.size
|
||||
end
|
||||
|
||||
def test_close
|
||||
@entry.close
|
||||
|
||||
|
@ -129,6 +133,13 @@ class TestGemPackageTarReaderEntry < Gem::Package::TarTestCase
|
|||
assert_equal @contents[0...100], @entry.read(100)
|
||||
end
|
||||
|
||||
def test_readpartial
|
||||
assert_raises(EOFError) do
|
||||
@entry.read(@contents.size)
|
||||
@entry.readpartial(1)
|
||||
end
|
||||
end
|
||||
|
||||
def test_rewind
|
||||
char = @entry.getc
|
||||
|
||||
|
|
|
@ -118,4 +118,21 @@ class TestGemPathSupport < Gem::TestCase
|
|||
ps = Gem::PathSupport.new "GEM_SPEC_CACHE" => "foo"
|
||||
assert_equal "foo", ps.spec_cache_dir
|
||||
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
|
||||
|
|
|
@ -731,10 +731,9 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
|
|||
assert_equal "murphy", fetcher.fetch_path(@server_uri)
|
||||
end
|
||||
|
||||
def test_fetch_s3
|
||||
def assert_fetch_s3(url)
|
||||
fetcher = Gem::RemoteFetcher.new nil
|
||||
@fetcher = fetcher
|
||||
url = 's3://testuser:testpass@my-bucket/gems/specs.4.8.gz'
|
||||
$fetched_uri = nil
|
||||
|
||||
def fetcher.request(uri, request_class, last_modified = nil)
|
||||
|
@ -756,15 +755,64 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
|
|||
$fetched_uri = nil
|
||||
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 = fetcher
|
||||
url = 's3://my-bucket/gems/specs.4.8.gz'
|
||||
|
||||
e = assert_raises Gem::RemoteFetcher::FetchError do
|
||||
fetcher.fetch_s3 URI.parse(url)
|
||||
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
|
||||
|
||||
def test_observe_no_proxy_env_single_host
|
||||
|
@ -846,9 +894,9 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
|
|||
with_configured_fetcher(
|
||||
":ssl_ca_cert: #{temp_ca_cert}\n" +
|
||||
":ssl_client_cert: #{temp_client_cert}\n") do |fetcher|
|
||||
assert_raises Gem::RemoteFetcher::FetchError do
|
||||
fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}/yaml")
|
||||
end
|
||||
assert_raises Gem::RemoteFetcher::FetchError do
|
||||
fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}/yaml")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -299,7 +299,7 @@ class TestGemResolver < Gem::TestCase
|
|||
a2_p1 = a3_p2 = nil
|
||||
|
||||
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
|
||||
a3_p2 = fetcher.spec 'a', 3 do |s| s.platform = unknown end
|
||||
end
|
||||
|
|
|
@ -135,9 +135,11 @@ toqvglr0kdbknSRRjBVLK6tsgr07aLT9gNP7mTW2PA==
|
|||
def test_sign_expired
|
||||
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'
|
||||
end
|
||||
|
||||
assert_match "certificate /CN=nobody/DC=example not valid after 1970-01-01 00:00:00 UTC", e.message
|
||||
end
|
||||
|
||||
def test_sign_expired_auto_update
|
||||
|
|
|
@ -377,9 +377,9 @@ class TestGemServer < Gem::TestCase
|
|||
assert_equal 200, @res.status
|
||||
assert_match 'xsshomepagegem 1', @res.body
|
||||
|
||||
# This verifies that the homepage for this spec is not displayed and is set to ".", because it's not a
|
||||
# This verifies that the homepage for this spec is not displayed and is set to ".", because it's not a
|
||||
# valid HTTP/HTTPS URL and could be unsafe in an HTML context. We would prefer to throw an exception here,
|
||||
# but spec.homepage is currently free form and not currently required to be a URL, this behavior may be
|
||||
# but spec.homepage is currently free form and not currently required to be a URL, this behavior may be
|
||||
# validated in future versions of Gem::Specification.
|
||||
#
|
||||
# There are two variant we're checking here, one where rdoc is not present, and one where rdoc is present in the same regex:
|
||||
|
@ -432,9 +432,9 @@ class TestGemServer < Gem::TestCase
|
|||
assert_equal 200, @res.status
|
||||
assert_match 'invalidhomepagegem 1', @res.body
|
||||
|
||||
# This verifies that the homepage for this spec is not displayed and is set to ".", because it's not a
|
||||
# This verifies that the homepage for this spec is not displayed and is set to ".", because it's not a
|
||||
# valid HTTP/HTTPS URL and could be unsafe in an HTML context. We would prefer to throw an exception here,
|
||||
# but spec.homepage is currently free form and not currently required to be a URL, this behavior may be
|
||||
# but spec.homepage is currently free form and not currently required to be a URL, this behavior may be
|
||||
# validated in future versions of Gem::Specification.
|
||||
#
|
||||
# There are two variant we're checking here, one where rdoc is not present, and one where rdoc is present in the same regex:
|
||||
|
|
|
@ -108,7 +108,7 @@ end
|
|||
# objects are present in the @stubs collection. This test verifies that
|
||||
# this scenario works correctly.
|
||||
Gem::Specification.all = [spec]
|
||||
Gem::Specification.find_active_stub_by_path('foo')
|
||||
assert_equal spec, Gem::Specification.find_active_stub_by_path('foo')
|
||||
end
|
||||
|
||||
def test_self_activate
|
||||
|
@ -387,8 +387,8 @@ end
|
|||
|
||||
def test_self_activate_checks_dependencies
|
||||
a = util_spec 'a', '1.0'
|
||||
a.add_dependency 'c', '= 1.0'
|
||||
a.add_dependency 'b', '~> 1.0'
|
||||
a.add_dependency 'c', '= 1.0'
|
||||
a.add_dependency 'b', '~> 1.0'
|
||||
|
||||
b1 = util_spec 'b', '1.0'
|
||||
b2 = util_spec 'b', '2.0'
|
||||
|
@ -1126,6 +1126,88 @@ dependencies: []
|
|||
refute_includes Gem::Specification.stubs.map { |s| s.full_name }, 'a-1'
|
||||
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__
|
||||
|
||||
def test_handles_private_null_type
|
||||
|
@ -2615,16 +2697,6 @@ end
|
|||
expected = <<-EXPECTED
|
||||
#{w}: prerelease dependency on b (>= 1.0.rc1) 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
|
||||
if i is semantically versioned, use:
|
||||
add_runtime_dependency 'i', '~> 1.2'
|
||||
|
@ -2637,11 +2709,6 @@ end
|
|||
#{w}: open-ended dependency on l (> 1.2.3) is not recommended
|
||||
if l is semantically versioned, use:
|
||||
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
|
||||
EXPECTED
|
||||
|
||||
|
@ -2844,6 +2911,58 @@ duplicate dependency on c (>= 1.2.3, development), (~> 1.2) use:
|
|||
@a1.files
|
||||
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
|
||||
util_setup_validate
|
||||
FileUtils.touch @a1.file_name
|
||||
|
|
|
@ -21,6 +21,10 @@ class TestGemText < Gem::TestCase
|
|||
assert_equal " text to wrap", format_text("text to wrap", 40, 2)
|
||||
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 .
|
||||
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.
|
||||
|
|
|
@ -38,6 +38,8 @@ class TestGemUtil < Gem::TestCase
|
|||
# impossible to cd into it and its children
|
||||
FileUtils.chmod(0666, 'd/e')
|
||||
|
||||
skip 'skipped in root privilege' if Process.uid.zero?
|
||||
|
||||
paths = Gem::Util.traverse_parents('d/e/f').to_a
|
||||
|
||||
assert_equal File.join(@tempdir, 'd'), paths[0]
|
||||
|
|
|
@ -46,7 +46,11 @@ class TestGemVersion < Gem::TestCase
|
|||
def test_class_correct
|
||||
assert_equal true, Gem::Version.correct?("5.1")
|
||||
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
|
||||
|
||||
def test_class_new_subclass
|
||||
|
@ -158,11 +162,25 @@ class TestGemVersion < Gem::TestCase
|
|||
|
||||
def test_approximate_recommendation
|
||||
assert_approximate_equal "~> 1.0", "1"
|
||||
assert_approximate_satisfies_itself "1"
|
||||
|
||||
assert_approximate_equal "~> 1.0", "1.0"
|
||||
assert_approximate_satisfies_itself "1.0"
|
||||
|
||||
assert_approximate_equal "~> 1.2", "1.2"
|
||||
assert_approximate_satisfies_itself "1.2"
|
||||
|
||||
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.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
|
||||
|
||||
def test_to_s
|
||||
|
@ -198,12 +216,20 @@ class TestGemVersion < Gem::TestCase
|
|||
assert v(version).prerelease?, "#{version} is a prerelease"
|
||||
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
|
||||
assert_equal expected, v(version).approximate_recommendation
|
||||
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+.
|
||||
|
||||
def assert_bumped_version_equal expected, unbumped
|
||||
|
|
Загрузка…
Ссылка в новой задаче