mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
* lib/rubygems: Import RubyGems 2.1
* test/rubygems: Ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@41873 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
cd9f9e4719
commit
47f0248b08
113 changed files with 4964 additions and 2610 deletions
|
@ -1,3 +1,8 @@
|
||||||
|
Wed Jul 10 08:21:15 2013 Eric Hodel <drbrain@segment7.net>
|
||||||
|
|
||||||
|
* lib/rubygems: Import RubyGems 2.1
|
||||||
|
* test/rubygems: Ditto.
|
||||||
|
|
||||||
Wed Jul 10 07:34:34 2013 Eric Hodel <drbrain@segment7.net>
|
Wed Jul 10 07:34:34 2013 Eric Hodel <drbrain@segment7.net>
|
||||||
|
|
||||||
* lib/rubygems/ext/ext_conf_builder.rb: Remove siteconf file after
|
* lib/rubygems/ext/ext_conf_builder.rb: Remove siteconf file after
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
require 'rbconfig'
|
require 'rbconfig'
|
||||||
|
|
||||||
module Gem
|
module Gem
|
||||||
VERSION = '2.0.4'
|
VERSION = '2.1.0'
|
||||||
end
|
end
|
||||||
|
|
||||||
# Must be first since it unloads the prelude from 1.9.2
|
# Must be first since it unloads the prelude from 1.9.2
|
||||||
|
@ -143,6 +143,14 @@ module Gem
|
||||||
specifications
|
specifications
|
||||||
]
|
]
|
||||||
|
|
||||||
|
##
|
||||||
|
# Subdirectories in a gem repository for default gems
|
||||||
|
|
||||||
|
REPOSITORY_DEFAULT_GEM_SUBDIRECTORIES = %w[
|
||||||
|
gems
|
||||||
|
specifications/default
|
||||||
|
]
|
||||||
|
|
||||||
@@win_platform = nil
|
@@win_platform = nil
|
||||||
|
|
||||||
@configuration = nil
|
@configuration = nil
|
||||||
|
@ -379,6 +387,10 @@ module Gem
|
||||||
paths.path
|
paths.path
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.spec_cache_dir
|
||||||
|
paths.spec_cache_dir
|
||||||
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# Quietly ensure the Gem directory +dir+ contains all the proper
|
# Quietly ensure the Gem directory +dir+ contains all the proper
|
||||||
# subdirectories. If we can't create a directory due to a permission
|
# subdirectories. If we can't create a directory due to a permission
|
||||||
|
@ -389,6 +401,23 @@ module Gem
|
||||||
# World-writable directories will never be created.
|
# World-writable directories will never be created.
|
||||||
|
|
||||||
def self.ensure_gem_subdirectories dir = Gem.dir, mode = nil
|
def self.ensure_gem_subdirectories dir = Gem.dir, mode = nil
|
||||||
|
ensure_subdirectories(dir, mode, REPOSITORY_SUBDIRECTORIES)
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Quietly ensure the Gem directory +dir+ contains all the proper
|
||||||
|
# subdirectories for handling default gems. If we can't create a
|
||||||
|
# directory due to a permission problem, then we will silently continue.
|
||||||
|
#
|
||||||
|
# If +mode+ is given, missing directories are created with this mode.
|
||||||
|
#
|
||||||
|
# World-writable directories will never be created.
|
||||||
|
|
||||||
|
def self.ensure_default_gem_subdirectories dir = Gem.dir, mode = nil
|
||||||
|
ensure_subdirectories(dir, mode, REPOSITORY_DEFAULT_GEM_SUBDIRECTORIES)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.ensure_subdirectories dir, mode, subdirs # :nodoc:
|
||||||
old_umask = File.umask
|
old_umask = File.umask
|
||||||
File.umask old_umask | 002
|
File.umask old_umask | 002
|
||||||
|
|
||||||
|
@ -398,7 +427,7 @@ module Gem
|
||||||
|
|
||||||
options[:mode] = mode if mode
|
options[:mode] = mode if mode
|
||||||
|
|
||||||
REPOSITORY_SUBDIRECTORIES.each do |name|
|
subdirs.each do |name|
|
||||||
subdir = File.join dir, name
|
subdir = File.join dir, name
|
||||||
next if File.exist? subdir
|
next if File.exist? subdir
|
||||||
FileUtils.mkdir_p subdir, options rescue nil
|
FileUtils.mkdir_p subdir, options rescue nil
|
||||||
|
@ -971,10 +1000,33 @@ module Gem
|
||||||
attr_reader :loaded_specs
|
attr_reader :loaded_specs
|
||||||
|
|
||||||
##
|
##
|
||||||
# Register a Gem::Specification for default gem
|
# Register a Gem::Specification for default gem.
|
||||||
|
#
|
||||||
|
# Two formats for the specification are supported:
|
||||||
|
#
|
||||||
|
# * MRI 2.0 style, where spec.files contains unprefixed require names.
|
||||||
|
# The spec's filenames will be registered as-is.
|
||||||
|
# * New style, where spec.files contains files prefixed with paths
|
||||||
|
# from spec.require_paths. The prefixes are stripped before
|
||||||
|
# registering the spec's filenames. Unprefixed files are omitted.
|
||||||
|
#
|
||||||
|
|
||||||
def register_default_spec(spec)
|
def register_default_spec(spec)
|
||||||
|
new_format, prefix_pattern = nil
|
||||||
|
|
||||||
spec.files.each do |file|
|
spec.files.each do |file|
|
||||||
|
if new_format == nil
|
||||||
|
new_format = spec.require_paths.any? {|path| file.start_with? path}
|
||||||
|
|
||||||
|
prefix_group = spec.require_paths.map {|f| f + "/"}.join("|")
|
||||||
|
prefix_pattern = /^(#{prefix_group})/
|
||||||
|
end
|
||||||
|
|
||||||
|
if new_format
|
||||||
|
file = file.sub(prefix_pattern, "")
|
||||||
|
next unless $~
|
||||||
|
end
|
||||||
|
|
||||||
@path_to_default_spec_map[file] = spec
|
@path_to_default_spec_map[file] = spec
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
class Gem::AvailableSet
|
class Gem::AvailableSet
|
||||||
|
|
||||||
|
include Enumerable
|
||||||
|
|
||||||
Tuple = Struct.new(:spec, :source)
|
Tuple = Struct.new(:spec, :source)
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
|
@ -36,6 +39,28 @@ class Gem::AvailableSet
|
||||||
self
|
self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Yields each Tuple in this AvailableSet
|
||||||
|
|
||||||
|
def each
|
||||||
|
return enum_for __method__ unless block_given?
|
||||||
|
|
||||||
|
@set.each do |tuple|
|
||||||
|
yield tuple
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Yields the Gem::Specification for each Tuple in this AvailableSet
|
||||||
|
|
||||||
|
def each_spec
|
||||||
|
return enum_for __method__ unless block_given?
|
||||||
|
|
||||||
|
each do |tuple|
|
||||||
|
yield tuple.spec
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def empty?
|
def empty?
|
||||||
@set.empty?
|
@set.empty?
|
||||||
end
|
end
|
||||||
|
@ -66,6 +91,49 @@ class Gem::AvailableSet
|
||||||
f.source
|
f.source
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Converts this AvailableSet into a RequestSet that can be used to install
|
||||||
|
# gems.
|
||||||
|
#
|
||||||
|
# If +development+ is :none then no development dependencies are installed.
|
||||||
|
# Other options are :shallow for only direct development dependencies of the
|
||||||
|
# gems in this set or :all for all development dependencies.
|
||||||
|
|
||||||
|
def to_request_set development = :none
|
||||||
|
request_set = Gem::RequestSet.new
|
||||||
|
request_set.development = :all == development
|
||||||
|
|
||||||
|
each_spec do |spec|
|
||||||
|
request_set.always_install << spec
|
||||||
|
|
||||||
|
request_set.gem spec.name, spec.version
|
||||||
|
request_set.import spec.development_dependencies if
|
||||||
|
:shallow == development
|
||||||
|
end
|
||||||
|
|
||||||
|
request_set
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
#
|
||||||
|
# Used by the DependencyResolver, the protocol to use a AvailableSet as a
|
||||||
|
# search Set.
|
||||||
|
|
||||||
|
def find_all(req)
|
||||||
|
dep = req.dependency
|
||||||
|
|
||||||
|
match = @set.find_all do |t|
|
||||||
|
dep.matches_spec? t.spec
|
||||||
|
end
|
||||||
|
|
||||||
|
match.map do |t|
|
||||||
|
Gem::DependencyResolver::InstalledSpecification.new(self, t.spec, t.source)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def prefetch(reqs)
|
||||||
|
end
|
||||||
|
|
||||||
def pick_best!
|
def pick_best!
|
||||||
return self if empty?
|
return self if empty?
|
||||||
|
|
||||||
|
|
139
lib/rubygems/basic_specification.rb
Normal file
139
lib/rubygems/basic_specification.rb
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
module Gem
|
||||||
|
# BasicSpecification is an abstract class which implements some common code used by
|
||||||
|
# both Specification and StubSpecification.
|
||||||
|
class BasicSpecification
|
||||||
|
def self.default_specifications_dir
|
||||||
|
File.join(Gem.default_dir, "specifications", "default")
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Name of the gem
|
||||||
|
|
||||||
|
def name
|
||||||
|
raise NotImplementedError
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Version of the gem
|
||||||
|
|
||||||
|
def version
|
||||||
|
raise NotImplementedError
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Platform of the gem
|
||||||
|
|
||||||
|
def platform
|
||||||
|
raise NotImplementedError
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Require paths of the gem
|
||||||
|
|
||||||
|
def require_paths
|
||||||
|
raise NotImplementedError
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# True when the gem has been activated
|
||||||
|
|
||||||
|
def activated?
|
||||||
|
raise NotImplementedError
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Return a Gem::Specification from this gem
|
||||||
|
|
||||||
|
def to_spec
|
||||||
|
raise NotImplementedError
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# The filename of the gem specification
|
||||||
|
attr_reader :filename
|
||||||
|
|
||||||
|
##
|
||||||
|
# Set the filename of the Specification was loaded from. +path+ is converted
|
||||||
|
# to a String.
|
||||||
|
|
||||||
|
def filename= path
|
||||||
|
@filename = path && path.to_s
|
||||||
|
|
||||||
|
@full_gem_path = nil
|
||||||
|
@gems_dir = nil
|
||||||
|
@base_dir = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Return true if this spec can require +file+.
|
||||||
|
|
||||||
|
def contains_requirable_file? file
|
||||||
|
root = full_gem_path
|
||||||
|
suffixes = Gem.suffixes
|
||||||
|
|
||||||
|
require_paths.any? do |lib|
|
||||||
|
base = "#{root}/#{lib}/#{file}"
|
||||||
|
suffixes.any? { |suf| File.file? "#{base}#{suf}" }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# The full path to the gem (install path + full name).
|
||||||
|
|
||||||
|
def full_gem_path
|
||||||
|
# TODO: This is a heavily used method by gems, so we'll need
|
||||||
|
# to aleast just alias it to #gem_dir rather than remove it.
|
||||||
|
@full_gem_path ||= find_full_gem_path
|
||||||
|
end
|
||||||
|
|
||||||
|
# :nodoc:
|
||||||
|
def find_full_gem_path
|
||||||
|
# TODO: also, shouldn't it default to full_name if it hasn't been written?
|
||||||
|
path = File.expand_path File.join(gems_dir, full_name)
|
||||||
|
path.untaint
|
||||||
|
path if File.directory? path
|
||||||
|
end
|
||||||
|
private :find_full_gem_path
|
||||||
|
|
||||||
|
##
|
||||||
|
# Returns the full path to the gems directory containing this spec's
|
||||||
|
# gem directory. eg: /usr/local/lib/ruby/1.8/gems
|
||||||
|
|
||||||
|
def gems_dir
|
||||||
|
# TODO: this logic seems terribly broken, but tests fail if just base_dir
|
||||||
|
@gems_dir ||= File.join(filename && base_dir || Gem.dir, "gems")
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Returns the full path to the base gem directory.
|
||||||
|
#
|
||||||
|
# eg: /usr/local/lib/ruby/gems/1.8
|
||||||
|
|
||||||
|
def base_dir
|
||||||
|
return Gem.dir unless filename
|
||||||
|
@base_dir ||= if default_gem? then
|
||||||
|
File.dirname File.dirname File.dirname filename
|
||||||
|
else
|
||||||
|
File.dirname File.dirname filename
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def default_gem?
|
||||||
|
filename &&
|
||||||
|
File.dirname(filename) == self.class.default_specifications_dir
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Returns the full name (name-version) of this Gem. Platform information
|
||||||
|
# is included (name-version-platform) if it is specified and not the
|
||||||
|
# default Ruby platform.
|
||||||
|
|
||||||
|
def full_name
|
||||||
|
if platform == Gem::Platform::RUBY or platform.nil? then
|
||||||
|
"#{name}-#{version}".untaint
|
||||||
|
else
|
||||||
|
"#{name}-#{version}-#{platform}".untaint
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,6 +1,11 @@
|
||||||
require 'rubygems/command'
|
require 'rubygems/command'
|
||||||
require 'rubygems/security'
|
require 'rubygems/security'
|
||||||
require 'openssl'
|
begin
|
||||||
|
require 'openssl'
|
||||||
|
rescue LoadError => e
|
||||||
|
raise unless (e.respond_to?(:path) && e.path == 'openssl') ||
|
||||||
|
e.message =~ / -- openssl$/
|
||||||
|
end
|
||||||
|
|
||||||
class Gem::Commands::CertCommand < Gem::Command
|
class Gem::Commands::CertCommand < Gem::Command
|
||||||
|
|
||||||
|
@ -21,7 +26,8 @@ class Gem::Commands::CertCommand < Gem::Command
|
||||||
|
|
||||||
OptionParser.accept OpenSSL::PKey::RSA do |key_file|
|
OptionParser.accept OpenSSL::PKey::RSA do |key_file|
|
||||||
begin
|
begin
|
||||||
key = OpenSSL::PKey::RSA.new File.read key_file
|
passphrase = ENV['GEM_PRIVATE_KEY_PASSPHRASE']
|
||||||
|
key = OpenSSL::PKey::RSA.new File.read(key_file), passphrase
|
||||||
rescue Errno::ENOENT
|
rescue Errno::ENOENT
|
||||||
raise OptionParser::InvalidArgument, "#{key_file}: does not exist"
|
raise OptionParser::InvalidArgument, "#{key_file}: does not exist"
|
||||||
rescue OpenSSL::PKey::RSAError
|
rescue OpenSSL::PKey::RSAError
|
||||||
|
@ -115,16 +121,31 @@ class Gem::Commands::CertCommand < Gem::Command
|
||||||
end
|
end
|
||||||
|
|
||||||
def build name
|
def build name
|
||||||
key = options[:key] || Gem::Security.create_key
|
if options[:key]
|
||||||
|
key = options[:key]
|
||||||
|
else
|
||||||
|
passphrase = ask_for_password 'Passphrase for your Private Key:'
|
||||||
|
say "\n"
|
||||||
|
|
||||||
cert = Gem::Security.create_cert_email name, key
|
passphrase_confirmation = ask_for_password 'Please repeat the passphrase for your Private Key:'
|
||||||
|
say "\n"
|
||||||
|
|
||||||
key_path = Gem::Security.write key, "gem-private_key.pem"
|
raise Gem::CommandLineError,
|
||||||
|
"Passphrase and passphrase confirmation don't match" unless passphrase == passphrase_confirmation
|
||||||
|
|
||||||
|
key = Gem::Security.create_key
|
||||||
|
key_path = Gem::Security.write key, "gem-private_key.pem", 0600, passphrase
|
||||||
|
end
|
||||||
|
|
||||||
|
cert = Gem::Security.create_cert_email name, key
|
||||||
cert_path = Gem::Security.write cert, "gem-public_cert.pem"
|
cert_path = Gem::Security.write cert, "gem-public_cert.pem"
|
||||||
|
|
||||||
say "Certificate: #{cert_path}"
|
say "Certificate: #{cert_path}"
|
||||||
say "Private Key: #{key_path}"
|
|
||||||
say "Don't forget to move the key file to somewhere private!"
|
if key_path
|
||||||
|
say "Private Key: #{key_path}"
|
||||||
|
say "Don't forget to move the key file to somewhere private!"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def certificates_matching filter
|
def certificates_matching filter
|
||||||
|
@ -198,7 +219,8 @@ For further reading on signing gems see `ri Gem::Security`.
|
||||||
def load_default_key
|
def load_default_key
|
||||||
key_file = File.join Gem.default_key_path
|
key_file = File.join Gem.default_key_path
|
||||||
key = File.read key_file
|
key = File.read key_file
|
||||||
options[:key] = OpenSSL::PKey::RSA.new key
|
passphrase = ENV['GEM_PRIVATE_KEY_PASSPHRASE']
|
||||||
|
options[:key] = OpenSSL::PKey::RSA.new key, passphrase
|
||||||
rescue Errno::ENOENT
|
rescue Errno::ENOENT
|
||||||
alert_error \
|
alert_error \
|
||||||
"--private-key not specified and ~/.gem/gem-private_key.pem does not exist"
|
"--private-key not specified and ~/.gem/gem-private_key.pem does not exist"
|
||||||
|
@ -225,5 +247,5 @@ For further reading on signing gems see `ri Gem::Security`.
|
||||||
Gem::Security.write cert, cert_file, permissions
|
Gem::Security.write cert, cert_file, permissions
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end if defined?(OpenSSL::SSL)
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,8 @@ class Gem::Commands::CleanupCommand < Gem::Command
|
||||||
'Clean up old versions of installed gems in the local repository',
|
'Clean up old versions of installed gems in the local repository',
|
||||||
:force => false, :install_dir => Gem.dir
|
:force => false, :install_dir => Gem.dir
|
||||||
|
|
||||||
add_option('-d', '--dryrun', "") do |value, options|
|
add_option('-n', '-d', '--dryrun',
|
||||||
|
'Do not uninstall gems') do |value, options|
|
||||||
options[:dryrun] = true
|
options[:dryrun] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -162,4 +163,3 @@ are not removed.
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -99,6 +99,8 @@ lib/rubygems/defaults/operating_system.rb
|
||||||
|
|
||||||
out << " - EXECUTABLE DIRECTORY: #{Gem.bindir}\n"
|
out << " - EXECUTABLE DIRECTORY: #{Gem.bindir}\n"
|
||||||
|
|
||||||
|
out << " - SPEC CACHE DIRECTORY: #{Gem.spec_cache_dir}\n"
|
||||||
|
|
||||||
out << " - RUBYGEMS PLATFORMS:\n"
|
out << " - RUBYGEMS PLATFORMS:\n"
|
||||||
Gem.platforms.each do |platform|
|
Gem.platforms.each do |platform|
|
||||||
out << " - #{platform}\n"
|
out << " - #{platform}\n"
|
||||||
|
@ -107,11 +109,9 @@ lib/rubygems/defaults/operating_system.rb
|
||||||
out << " - GEM PATHS:\n"
|
out << " - GEM PATHS:\n"
|
||||||
out << " - #{Gem.dir}\n"
|
out << " - #{Gem.dir}\n"
|
||||||
|
|
||||||
path = Gem.path.dup
|
gem_path = Gem.path.dup
|
||||||
path.delete Gem.dir
|
gem_path.delete Gem.dir
|
||||||
path.each do |p|
|
add_path out, gem_path
|
||||||
out << " - #{p}\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
out << " - GEM CONFIGURATION:\n"
|
out << " - GEM CONFIGURATION:\n"
|
||||||
Gem.configuration.each do |name, value|
|
Gem.configuration.each do |name, value|
|
||||||
|
@ -124,6 +124,11 @@ lib/rubygems/defaults/operating_system.rb
|
||||||
out << " - #{s}\n"
|
out << " - #{s}\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
out << " - SHELL PATH:\n"
|
||||||
|
|
||||||
|
shell_path = ENV['PATH'].split(File::PATH_SEPARATOR)
|
||||||
|
add_path out, shell_path
|
||||||
|
|
||||||
else
|
else
|
||||||
raise Gem::CommandLineError, "Unknown environment option [#{arg}]"
|
raise Gem::CommandLineError, "Unknown environment option [#{arg}]"
|
||||||
end
|
end
|
||||||
|
@ -131,5 +136,11 @@ lib/rubygems/defaults/operating_system.rb
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def add_path out, path
|
||||||
|
path.each do |component|
|
||||||
|
out << " - #{component}\n"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,10 @@ Some examples of 'gem' usage.
|
||||||
* Update all gems on your system:
|
* Update all gems on your system:
|
||||||
|
|
||||||
gem update
|
gem update
|
||||||
|
|
||||||
|
* Update your local version of RubyGems
|
||||||
|
|
||||||
|
gem update --system
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
PLATFORMS = <<-'EOF'
|
PLATFORMS = <<-'EOF'
|
||||||
|
@ -55,8 +59,9 @@ your current platform by running `gem environment`.
|
||||||
|
|
||||||
RubyGems matches platforms as follows:
|
RubyGems matches platforms as follows:
|
||||||
|
|
||||||
* The CPU must match exactly, unless one of the platforms has
|
* The CPU must match exactly unless one of the platforms has
|
||||||
"universal" as the CPU.
|
"universal" as the CPU or the local CPU starts with "arm" and the gem's
|
||||||
|
CPU is exactly "arm" (for gems that support generic ARM architecture).
|
||||||
* The OS must match exactly.
|
* The OS must match exactly.
|
||||||
* The versions must match exactly unless one of the versions is nil.
|
* The versions must match exactly unless one of the versions is nil.
|
||||||
|
|
||||||
|
@ -66,11 +71,20 @@ you pass must match "#{cpu}-#{os}" or "#{cpu}-#{os}-#{version}". On mswin
|
||||||
platforms, the version is the compiler version, not the OS version. (Ruby
|
platforms, the version is the compiler version, not the OS version. (Ruby
|
||||||
compiled with VC6 uses "60" as the compiler version, VC8 uses "80".)
|
compiled with VC6 uses "60" as the compiler version, VC8 uses "80".)
|
||||||
|
|
||||||
|
For the ARM architecture, gems with a platform of "arm-linux" should run on a
|
||||||
|
reasonable set of ARM CPUs and not depend on instructions present on a limited
|
||||||
|
subset of the architecture. For example, the binary should run on platforms
|
||||||
|
armv5, armv6hf, armv6l, armv7, etc. If you use the "arm-linux" platform
|
||||||
|
please test your gem on a variety of ARM hardware before release to ensure it
|
||||||
|
functions correctly.
|
||||||
|
|
||||||
Example platforms:
|
Example platforms:
|
||||||
|
|
||||||
x86-freebsd # Any FreeBSD version on an x86 CPU
|
x86-freebsd # Any FreeBSD version on an x86 CPU
|
||||||
universal-darwin-8 # Darwin 8 only gems that run on any CPU
|
universal-darwin-8 # Darwin 8 only gems that run on any CPU
|
||||||
x86-mswin32-80 # Windows gems compiled with VC8
|
x86-mswin32-80 # Windows gems compiled with VC8
|
||||||
|
armv7-linux # Gem complied for an ARMv7 CPU running linux
|
||||||
|
arm-linux # Gem compiled for any ARM CPU running linux
|
||||||
|
|
||||||
When building platform gems, set the platform in the gem specification to
|
When building platform gems, set the platform in the gem specification to
|
||||||
Gem::Platform::CURRENT. This will correctly mark the gem with your ruby's
|
Gem::Platform::CURRENT. This will correctly mark the gem with your ruby's
|
||||||
|
@ -119,7 +133,7 @@ platform.
|
||||||
if command then
|
if command then
|
||||||
command.summary
|
command.summary
|
||||||
else
|
else
|
||||||
"[No command found for #{cmd_name}, bug?]"
|
"[No command found for #{cmd_name}]"
|
||||||
end
|
end
|
||||||
|
|
||||||
summary = wrap(summary, summary_width).split "\n"
|
summary = wrap(summary, summary_width).split "\n"
|
||||||
|
|
|
@ -4,8 +4,6 @@ require 'rubygems/dependency_installer'
|
||||||
require 'rubygems/local_remote_options'
|
require 'rubygems/local_remote_options'
|
||||||
require 'rubygems/validator'
|
require 'rubygems/validator'
|
||||||
require 'rubygems/version_option'
|
require 'rubygems/version_option'
|
||||||
require 'rubygems/install_message' # must come before rdoc for messaging
|
|
||||||
require 'rubygems/rdoc'
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Gem installer command line tool
|
# Gem installer command line tool
|
||||||
|
@ -39,6 +37,12 @@ class Gem::Commands::InstallCommand < Gem::Command
|
||||||
'install the listed gems') do |v,o|
|
'install the listed gems') do |v,o|
|
||||||
o[:gemdeps] = v
|
o[:gemdeps] = v
|
||||||
end
|
end
|
||||||
|
|
||||||
|
add_option(:"Install/Update", '--default',
|
||||||
|
'Add the gem\'s full specification to',
|
||||||
|
'specifications/default and extract only its bin') do |v,o|
|
||||||
|
o[:install_as_default] = v
|
||||||
|
end
|
||||||
|
|
||||||
@installed_specs = nil
|
@installed_specs = nil
|
||||||
end
|
end
|
||||||
|
@ -153,7 +157,14 @@ to write the specification by hand. For example:
|
||||||
alert_error "Can't use --version w/ multiple gems. Use name:ver instead."
|
alert_error "Can't use --version w/ multiple gems. Use name:ver instead."
|
||||||
terminate_interaction 1
|
terminate_interaction 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# load post-install hooks appropriate to options
|
||||||
|
if options[:install_as_default]
|
||||||
|
require 'rubygems/install_default_message'
|
||||||
|
else
|
||||||
|
require 'rubygems/install_message'
|
||||||
|
end
|
||||||
|
require 'rubygems/rdoc'
|
||||||
|
|
||||||
get_all_gem_names_and_versions.each do |gem_name, gem_version|
|
get_all_gem_names_and_versions.each do |gem_name, gem_version|
|
||||||
gem_version ||= options[:version]
|
gem_version ||= options[:version]
|
||||||
|
|
|
@ -31,9 +31,15 @@ class Gem::Commands::OwnerCommand < Gem::Command
|
||||||
add_option '-r', '--remove EMAIL', 'Remove an owner' do |value, options|
|
add_option '-r', '--remove EMAIL', 'Remove an owner' do |value, options|
|
||||||
options[:remove] << value
|
options[:remove] << value
|
||||||
end
|
end
|
||||||
|
|
||||||
|
add_option '-h', '--host HOST', 'Use another gemcutter-compatible host' do |value, options|
|
||||||
|
options[:host] = value
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def execute
|
def execute
|
||||||
|
@host = options[:host]
|
||||||
|
|
||||||
sign_in
|
sign_in
|
||||||
name = get_one_gem_name
|
name = get_one_gem_name
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,12 @@ class Gem::Commands::PristineCommand < Gem::Command
|
||||||
options[:only_executables] = value
|
options[:only_executables] = value
|
||||||
end
|
end
|
||||||
|
|
||||||
|
add_option('-E', '--[no-]env-shebang',
|
||||||
|
'Rewrite executables with a shebang',
|
||||||
|
'of /usr/bin/env') do |value, options|
|
||||||
|
options[:env_shebang] = value
|
||||||
|
end
|
||||||
|
|
||||||
add_version_option('restore to', 'pristine condition')
|
add_version_option('restore to', 'pristine condition')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -104,16 +110,21 @@ with extensions.
|
||||||
Gem::RemoteFetcher.fetcher.download_to_cache dep
|
Gem::RemoteFetcher.fetcher.download_to_cache dep
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO use installer options
|
env_shebang =
|
||||||
install_defaults = Gem::ConfigFile::PLATFORM_DEFAULTS['install']
|
if options.include? :env_shebang then
|
||||||
installer_env_shebang = install_defaults.to_s['--env-shebang']
|
options[:env_shebang]
|
||||||
|
else
|
||||||
|
install_defaults = Gem::ConfigFile::PLATFORM_DEFAULTS['install']
|
||||||
|
install_defaults.to_s['--env-shebang']
|
||||||
|
end
|
||||||
|
|
||||||
installer = Gem::Installer.new(gem,
|
installer = Gem::Installer.new(gem,
|
||||||
:wrappers => true,
|
:wrappers => true,
|
||||||
:force => true,
|
:force => true,
|
||||||
:install_dir => spec.base_dir,
|
:install_dir => spec.base_dir,
|
||||||
:env_shebang => installer_env_shebang,
|
:env_shebang => env_shebang,
|
||||||
:build_args => spec.build_args)
|
:build_args => spec.build_args)
|
||||||
|
|
||||||
if options[:only_executables] then
|
if options[:only_executables] then
|
||||||
installer.generate_bin
|
installer.generate_bin
|
||||||
else
|
else
|
||||||
|
|
|
@ -48,7 +48,7 @@ class Gem::Commands::SourcesCommand < Gem::Command
|
||||||
options[:update])
|
options[:update])
|
||||||
|
|
||||||
if options[:clear_all] then
|
if options[:clear_all] then
|
||||||
path = File.join Gem.user_home, '.gem', 'specs'
|
path = Gem.spec_cache_dir
|
||||||
FileUtils.rm_rf path
|
FileUtils.rm_rf path
|
||||||
|
|
||||||
unless File.exist? path then
|
unless File.exist? path then
|
||||||
|
|
|
@ -67,6 +67,12 @@ class Gem::Commands::UninstallCommand < Gem::Command
|
||||||
options[:force] = value
|
options[:force] = value
|
||||||
end
|
end
|
||||||
|
|
||||||
|
add_option('--[no-]abort-on-dependent',
|
||||||
|
'Prevent uninstalling gems that are',
|
||||||
|
'depended on by other gems.') do |value, options|
|
||||||
|
options[:abort_on_dependent] = value
|
||||||
|
end
|
||||||
|
|
||||||
add_version_option
|
add_version_option
|
||||||
add_platform_option
|
add_platform_option
|
||||||
end
|
end
|
||||||
|
|
|
@ -140,6 +140,11 @@ class Gem::ConfigFile
|
||||||
|
|
||||||
attr_reader :ssl_ca_cert
|
attr_reader :ssl_ca_cert
|
||||||
|
|
||||||
|
##
|
||||||
|
# Path name of directory or file of openssl client certificate, used for remote https connection with client authentication
|
||||||
|
|
||||||
|
attr_reader :ssl_client_cert
|
||||||
|
|
||||||
##
|
##
|
||||||
# Create the config file object. +args+ is the list of arguments
|
# Create the config file object. +args+ is the list of arguments
|
||||||
# from the command line.
|
# from the command line.
|
||||||
|
@ -210,6 +215,7 @@ class Gem::ConfigFile
|
||||||
|
|
||||||
@ssl_verify_mode = @hash[:ssl_verify_mode] if @hash.key? :ssl_verify_mode
|
@ssl_verify_mode = @hash[:ssl_verify_mode] if @hash.key? :ssl_verify_mode
|
||||||
@ssl_ca_cert = @hash[:ssl_ca_cert] if @hash.key? :ssl_ca_cert
|
@ssl_ca_cert = @hash[:ssl_ca_cert] if @hash.key? :ssl_ca_cert
|
||||||
|
@ssl_client_cert = @hash[:ssl_client_cert] if @hash.key? :ssl_client_cert
|
||||||
|
|
||||||
@api_keys = nil
|
@api_keys = nil
|
||||||
@rubygems_api_key = nil
|
@rubygems_api_key = nil
|
||||||
|
@ -246,6 +252,10 @@ Your gem push credentials file located at:
|
||||||
|
|
||||||
has file permissions of 0#{existing_permissions.to_s 8} but 0600 is required.
|
has file permissions of 0#{existing_permissions.to_s 8} but 0600 is required.
|
||||||
|
|
||||||
|
To fix this error run:
|
||||||
|
|
||||||
|
\tchmod 0600 #{credentials_path}
|
||||||
|
|
||||||
You should reset your credentials at:
|
You should reset your credentials at:
|
||||||
|
|
||||||
\thttps://rubygems.org/profile/edit
|
\thttps://rubygems.org/profile/edit
|
||||||
|
@ -309,6 +319,9 @@ if you believe they were disclosed to a third party.
|
||||||
@rubygems_api_key = api_key
|
@rubygems_api_key = api_key
|
||||||
end
|
end
|
||||||
|
|
||||||
|
YAMLErrors = [ArgumentError]
|
||||||
|
YAMLErrors << Psych::SyntaxError if defined?(Psych::SyntaxError)
|
||||||
|
|
||||||
def load_file(filename)
|
def load_file(filename)
|
||||||
Gem.load_yaml
|
Gem.load_yaml
|
||||||
|
|
||||||
|
@ -321,8 +334,8 @@ if you believe they were disclosed to a third party.
|
||||||
return {}
|
return {}
|
||||||
end
|
end
|
||||||
return content
|
return content
|
||||||
rescue ArgumentError
|
rescue *YAMLErrors => e
|
||||||
warn "Failed to load #{filename}"
|
warn "Failed to load #{filename}, #{e.to_s}"
|
||||||
rescue Errno::EACCES
|
rescue Errno::EACCES
|
||||||
warn "Failed to load #{filename} due to permissions problem."
|
warn "Failed to load #{filename} due to permissions problem."
|
||||||
end
|
end
|
||||||
|
|
|
@ -57,7 +57,7 @@ module Kernel
|
||||||
#--
|
#--
|
||||||
# TODO request access to the C implementation of this to speed up RubyGems
|
# TODO request access to the C implementation of this to speed up RubyGems
|
||||||
|
|
||||||
spec = Gem::Specification.find { |s|
|
spec = Gem::Specification.stubs.find { |s|
|
||||||
s.activated? and s.contains_requirable_file? path
|
s.activated? and s.contains_requirable_file? path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,14 @@ module Gem
|
||||||
%w[https://rubygems.org/]
|
%w[https://rubygems.org/]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Default spec directory path to be used if an alternate value is not
|
||||||
|
# specified in the environment
|
||||||
|
|
||||||
|
def self.default_spec_cache_dir
|
||||||
|
File.join Gem.user_home, '.gem', 'specs'
|
||||||
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# Default home directory path to be used if an alternate value is not
|
# Default home directory path to be used if an alternate value is not
|
||||||
# specified in the environment
|
# specified in the environment
|
||||||
|
|
|
@ -203,6 +203,8 @@ class Gem::Dependency
|
||||||
requirement.satisfied_by? version
|
requirement.satisfied_by? version
|
||||||
end
|
end
|
||||||
|
|
||||||
|
alias === =~
|
||||||
|
|
||||||
# DOC: this method needs either documented or :nodoc'd
|
# DOC: this method needs either documented or :nodoc'd
|
||||||
|
|
||||||
def match? obj, version=nil
|
def match? obj, version=nil
|
||||||
|
@ -250,10 +252,10 @@ class Gem::Dependency
|
||||||
# DOC: this method needs either documented or :nodoc'd
|
# DOC: this method needs either documented or :nodoc'd
|
||||||
|
|
||||||
def matching_specs platform_only = false
|
def matching_specs platform_only = false
|
||||||
matches = Gem::Specification.find_all { |spec|
|
matches = Gem::Specification.stubs.find_all { |spec|
|
||||||
self.name === spec.name and # TODO: == instead of ===
|
self.name === spec.name and # TODO: == instead of ===
|
||||||
requirement.satisfied_by? spec.version
|
requirement.satisfied_by? spec.version
|
||||||
}
|
}.map(&:to_spec)
|
||||||
|
|
||||||
if platform_only
|
if platform_only
|
||||||
matches.reject! { |spec|
|
matches.reject! { |spec|
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
require 'rubygems'
|
require 'rubygems'
|
||||||
require 'rubygems/dependency_list'
|
require 'rubygems/dependency_list'
|
||||||
|
require 'rubygems/dependency_resolver'
|
||||||
require 'rubygems/package'
|
require 'rubygems/package'
|
||||||
require 'rubygems/installer'
|
require 'rubygems/installer'
|
||||||
require 'rubygems/spec_fetcher'
|
require 'rubygems/spec_fetcher'
|
||||||
require 'rubygems/user_interaction'
|
require 'rubygems/user_interaction'
|
||||||
require 'rubygems/source_local'
|
require 'rubygems/source/local'
|
||||||
require 'rubygems/source_specific_file'
|
require 'rubygems/source/specific_file'
|
||||||
require 'rubygems/available_set'
|
require 'rubygems/available_set'
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -15,15 +16,7 @@ class Gem::DependencyInstaller
|
||||||
|
|
||||||
include Gem::UserInteraction
|
include Gem::UserInteraction
|
||||||
|
|
||||||
attr_reader :gems_to_install
|
DEFAULT_OPTIONS = { # :nodoc:
|
||||||
attr_reader :installed_gems
|
|
||||||
|
|
||||||
##
|
|
||||||
# Documentation types. For use by the Gem.done_installing hook
|
|
||||||
|
|
||||||
attr_reader :document
|
|
||||||
|
|
||||||
DEFAULT_OPTIONS = {
|
|
||||||
:env_shebang => false,
|
:env_shebang => false,
|
||||||
:document => %w[ri],
|
:document => %w[ri],
|
||||||
:domain => :both, # HACK dup
|
:domain => :both, # HACK dup
|
||||||
|
@ -35,8 +28,30 @@ class Gem::DependencyInstaller
|
||||||
:wrappers => true,
|
:wrappers => true,
|
||||||
:build_args => nil,
|
:build_args => nil,
|
||||||
:build_docs_in_background => false,
|
:build_docs_in_background => false,
|
||||||
|
:install_as_default => false
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
|
##
|
||||||
|
# Documentation types. For use by the Gem.done_installing hook
|
||||||
|
|
||||||
|
attr_reader :document
|
||||||
|
|
||||||
|
##
|
||||||
|
# Errors from SpecFetcher while searching for remote specifications
|
||||||
|
|
||||||
|
attr_reader :errors
|
||||||
|
|
||||||
|
##
|
||||||
|
#--
|
||||||
|
# TODO remove, no longer used
|
||||||
|
|
||||||
|
attr_reader :gems_to_install # :nodoc:
|
||||||
|
|
||||||
|
##
|
||||||
|
# List of gems installed by #install in alphabetic order
|
||||||
|
|
||||||
|
attr_reader :installed_gems
|
||||||
|
|
||||||
##
|
##
|
||||||
# Creates a new installer instance.
|
# Creates a new installer instance.
|
||||||
#
|
#
|
||||||
|
@ -56,7 +71,8 @@ class Gem::DependencyInstaller
|
||||||
# :wrappers:: See Gem::Installer::new
|
# :wrappers:: See Gem::Installer::new
|
||||||
# :build_args:: See Gem::Installer::new
|
# :build_args:: See Gem::Installer::new
|
||||||
|
|
||||||
def initialize(options = {})
|
def initialize options = {}
|
||||||
|
@only_install_dir = !!options[:install_dir]
|
||||||
@install_dir = options[:install_dir] || Gem.dir
|
@install_dir = options[:install_dir] || Gem.dir
|
||||||
|
|
||||||
if options[:install_dir] then
|
if options[:install_dir] then
|
||||||
|
@ -82,6 +98,7 @@ class Gem::DependencyInstaller
|
||||||
@wrappers = options[:wrappers]
|
@wrappers = options[:wrappers]
|
||||||
@build_args = options[:build_args]
|
@build_args = options[:build_args]
|
||||||
@build_docs_in_background = options[:build_docs_in_background]
|
@build_docs_in_background = options[:build_docs_in_background]
|
||||||
|
@install_as_default = options[:install_as_default]
|
||||||
|
|
||||||
# Indicates that we should not try to update any deps unless
|
# Indicates that we should not try to update any deps unless
|
||||||
# we absolutely must.
|
# we absolutely must.
|
||||||
|
@ -93,128 +110,14 @@ class Gem::DependencyInstaller
|
||||||
|
|
||||||
@cache_dir = options[:cache_dir] || @install_dir
|
@cache_dir = options[:cache_dir] || @install_dir
|
||||||
|
|
||||||
# Set with any errors that SpecFetcher finds while search through
|
|
||||||
# gemspecs for a dep
|
|
||||||
@errors = nil
|
@errors = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
attr_reader :errors
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Creates an AvailableSet to install from based on +dep_or_name+ and
|
#--
|
||||||
# +version+
|
# TODO remove, no longer used
|
||||||
|
|
||||||
def available_set_for dep_or_name, version # :nodoc:
|
def add_found_dependencies to_do, dependency_list # :nodoc:
|
||||||
if String === dep_or_name then
|
|
||||||
find_spec_by_name_and_version dep_or_name, version, @prerelease
|
|
||||||
else
|
|
||||||
dep = dep_or_name.dup
|
|
||||||
dep.prerelease = @prerelease
|
|
||||||
@available = find_gems_with_sources dep
|
|
||||||
end
|
|
||||||
|
|
||||||
@available.pick_best!
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# Indicated, based on the requested domain, if local
|
|
||||||
# gems should be considered.
|
|
||||||
|
|
||||||
def consider_local?
|
|
||||||
@domain == :both or @domain == :local
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# Indicated, based on the requested domain, if remote
|
|
||||||
# gems should be considered.
|
|
||||||
|
|
||||||
def consider_remote?
|
|
||||||
@domain == :both or @domain == :remote
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# Returns a list of pairs of gemspecs and source_uris that match
|
|
||||||
# Gem::Dependency +dep+ from both local (Dir.pwd) and remote (Gem.sources)
|
|
||||||
# sources. Gems are sorted with newer gems preferred over older gems, and
|
|
||||||
# local gems preferred over remote gems.
|
|
||||||
|
|
||||||
def find_gems_with_sources(dep)
|
|
||||||
set = Gem::AvailableSet.new
|
|
||||||
|
|
||||||
if consider_local?
|
|
||||||
sl = Gem::Source::Local.new
|
|
||||||
|
|
||||||
if spec = sl.find_gem(dep.name)
|
|
||||||
if dep.matches_spec? spec
|
|
||||||
set.add spec, sl
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if consider_remote?
|
|
||||||
begin
|
|
||||||
found, errors = Gem::SpecFetcher.fetcher.spec_for_dependency dep
|
|
||||||
|
|
||||||
if @errors
|
|
||||||
@errors += errors
|
|
||||||
else
|
|
||||||
@errors = errors
|
|
||||||
end
|
|
||||||
|
|
||||||
set << found
|
|
||||||
|
|
||||||
rescue Gem::RemoteFetcher::FetchError => e
|
|
||||||
# FIX if there is a problem talking to the network, we either need to always tell
|
|
||||||
# the user (no really_verbose) or fail hard, not silently tell them that we just
|
|
||||||
# couldn't find their requested gem.
|
|
||||||
if Gem.configuration.really_verbose then
|
|
||||||
say "Error fetching remote data:\t\t#{e.message}"
|
|
||||||
say "Falling back to local-only install"
|
|
||||||
end
|
|
||||||
@domain = :local
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
set
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# Gathers all dependencies necessary for the installation from local and
|
|
||||||
# remote sources unless the ignore_dependencies was given.
|
|
||||||
|
|
||||||
def gather_dependencies
|
|
||||||
specs = @available.all_specs
|
|
||||||
|
|
||||||
# these gems were listed by the user, always install them
|
|
||||||
keep_names = specs.map { |spec| spec.full_name }
|
|
||||||
|
|
||||||
if @dev_shallow
|
|
||||||
@toplevel_specs = keep_names
|
|
||||||
end
|
|
||||||
|
|
||||||
dependency_list = Gem::DependencyList.new @development
|
|
||||||
dependency_list.add(*specs)
|
|
||||||
to_do = specs.dup
|
|
||||||
add_found_dependencies to_do, dependency_list unless @ignore_dependencies
|
|
||||||
|
|
||||||
# REFACTOR maybe abstract away using Gem::Specification.include? so
|
|
||||||
# that this isn't dependent only on the currently installed gems
|
|
||||||
dependency_list.specs.reject! { |spec|
|
|
||||||
not keep_names.include?(spec.full_name) and
|
|
||||||
Gem::Specification.include?(spec)
|
|
||||||
}
|
|
||||||
|
|
||||||
unless dependency_list.ok? or @ignore_dependencies or @force then
|
|
||||||
reason = dependency_list.why_not_ok?.map { |k,v|
|
|
||||||
"#{k} requires #{v.join(", ")}"
|
|
||||||
}.join("; ")
|
|
||||||
raise Gem::DependencyError, "Unable to resolve dependencies: #{reason}"
|
|
||||||
end
|
|
||||||
|
|
||||||
@gems_to_install = dependency_list.dependency_order.reverse
|
|
||||||
end
|
|
||||||
|
|
||||||
def add_found_dependencies to_do, dependency_list
|
|
||||||
seen = {}
|
seen = {}
|
||||||
dependencies = Hash.new { |h, name| h[name] = Gem::Dependency.new name }
|
dependencies = Hash.new { |h, name| h[name] = Gem::Dependency.new name }
|
||||||
|
|
||||||
|
@ -262,15 +165,92 @@ class Gem::DependencyInstaller
|
||||||
|
|
||||||
dependency_list.remove_specs_unsatisfied_by dependencies
|
dependency_list.remove_specs_unsatisfied_by dependencies
|
||||||
end
|
end
|
||||||
|
##
|
||||||
|
# Creates an AvailableSet to install from based on +dep_or_name+ and
|
||||||
|
# +version+
|
||||||
|
|
||||||
|
def available_set_for dep_or_name, version # :nodoc:
|
||||||
|
if String === dep_or_name then
|
||||||
|
find_spec_by_name_and_version dep_or_name, version, @prerelease
|
||||||
|
else
|
||||||
|
dep = dep_or_name.dup
|
||||||
|
dep.prerelease = @prerelease
|
||||||
|
@available = find_gems_with_sources dep
|
||||||
|
end
|
||||||
|
|
||||||
|
@available.pick_best!
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Indicated, based on the requested domain, if local
|
||||||
|
# gems should be considered.
|
||||||
|
|
||||||
|
def consider_local?
|
||||||
|
@domain == :both or @domain == :local
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Indicated, based on the requested domain, if remote
|
||||||
|
# gems should be considered.
|
||||||
|
|
||||||
|
def consider_remote?
|
||||||
|
@domain == :both or @domain == :remote
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Returns a list of pairs of gemspecs and source_uris that match
|
||||||
|
# Gem::Dependency +dep+ from both local (Dir.pwd) and remote (Gem.sources)
|
||||||
|
# sources. Gems are sorted with newer gems preferred over older gems, and
|
||||||
|
# local gems preferred over remote gems.
|
||||||
|
|
||||||
|
def find_gems_with_sources dep # :nodoc:
|
||||||
|
set = Gem::AvailableSet.new
|
||||||
|
|
||||||
|
if consider_local?
|
||||||
|
sl = Gem::Source::Local.new
|
||||||
|
|
||||||
|
if spec = sl.find_gem(dep.name)
|
||||||
|
if dep.matches_spec? spec
|
||||||
|
set.add spec, sl
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if consider_remote?
|
||||||
|
begin
|
||||||
|
found, errors = Gem::SpecFetcher.fetcher.spec_for_dependency dep
|
||||||
|
|
||||||
|
if @errors
|
||||||
|
@errors += errors
|
||||||
|
else
|
||||||
|
@errors = errors
|
||||||
|
end
|
||||||
|
|
||||||
|
set << found
|
||||||
|
|
||||||
|
rescue Gem::RemoteFetcher::FetchError => e
|
||||||
|
# FIX if there is a problem talking to the network, we either need to always tell
|
||||||
|
# the user (no really_verbose) or fail hard, not silently tell them that we just
|
||||||
|
# couldn't find their requested gem.
|
||||||
|
if Gem.configuration.really_verbose then
|
||||||
|
say "Error fetching remote data:\t\t#{e.message}"
|
||||||
|
say "Falling back to local-only install"
|
||||||
|
end
|
||||||
|
@domain = :local
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
set
|
||||||
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# Finds a spec and the source_uri it came from for gem +gem_name+ and
|
# Finds a spec and the source_uri it came from for gem +gem_name+ and
|
||||||
# +version+. Returns an Array of specs and sources required for
|
# +version+. Returns an Array of specs and sources required for
|
||||||
# installation of the gem.
|
# installation of the gem.
|
||||||
|
|
||||||
def find_spec_by_name_and_version(gem_name,
|
def find_spec_by_name_and_version gem_name,
|
||||||
version = Gem::Requirement.default,
|
version = Gem::Requirement.default,
|
||||||
prerelease = false)
|
prerelease = false
|
||||||
|
|
||||||
set = Gem::AvailableSet.new
|
set = Gem::AvailableSet.new
|
||||||
|
|
||||||
|
@ -303,6 +283,59 @@ class Gem::DependencyInstaller
|
||||||
@available = set
|
@available = set
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Gathers all dependencies necessary for the installation from local and
|
||||||
|
# remote sources unless the ignore_dependencies was given.
|
||||||
|
#--
|
||||||
|
# TODO remove, no longer used
|
||||||
|
|
||||||
|
def gather_dependencies # :nodoc:
|
||||||
|
specs = @available.all_specs
|
||||||
|
|
||||||
|
# these gems were listed by the user, always install them
|
||||||
|
keep_names = specs.map { |spec| spec.full_name }
|
||||||
|
|
||||||
|
if @dev_shallow
|
||||||
|
@toplevel_specs = keep_names
|
||||||
|
end
|
||||||
|
|
||||||
|
dependency_list = Gem::DependencyList.new @development
|
||||||
|
dependency_list.add(*specs)
|
||||||
|
to_do = specs.dup
|
||||||
|
add_found_dependencies to_do, dependency_list unless @ignore_dependencies
|
||||||
|
|
||||||
|
# REFACTOR maybe abstract away using Gem::Specification.include? so
|
||||||
|
# that this isn't dependent only on the currently installed gems
|
||||||
|
dependency_list.specs.reject! { |spec|
|
||||||
|
not keep_names.include?(spec.full_name) and
|
||||||
|
Gem::Specification.include?(spec)
|
||||||
|
}
|
||||||
|
|
||||||
|
unless dependency_list.ok? or @ignore_dependencies or @force then
|
||||||
|
reason = dependency_list.why_not_ok?.map { |k,v|
|
||||||
|
"#{k} requires #{v.join(", ")}"
|
||||||
|
}.join("; ")
|
||||||
|
raise Gem::DependencyError, "Unable to resolve dependencies: #{reason}"
|
||||||
|
end
|
||||||
|
|
||||||
|
@gems_to_install = dependency_list.dependency_order.reverse
|
||||||
|
end
|
||||||
|
|
||||||
|
def in_background what # :nodoc:
|
||||||
|
fork_happened = false
|
||||||
|
if @build_docs_in_background and Process.respond_to?(:fork)
|
||||||
|
begin
|
||||||
|
Process.fork do
|
||||||
|
yield
|
||||||
|
end
|
||||||
|
fork_happened = true
|
||||||
|
say "#{what} in a background process."
|
||||||
|
rescue NotImplementedError
|
||||||
|
end
|
||||||
|
end
|
||||||
|
yield unless fork_happened
|
||||||
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# Installs the gem +dep_or_name+ and all its dependencies. Returns an Array
|
# Installs the gem +dep_or_name+ and all its dependencies. Returns an Array
|
||||||
# of installed gem specifications.
|
# of installed gem specifications.
|
||||||
|
@ -318,61 +351,30 @@ class Gem::DependencyInstaller
|
||||||
# separately.
|
# separately.
|
||||||
|
|
||||||
def install dep_or_name, version = Gem::Requirement.default
|
def install dep_or_name, version = Gem::Requirement.default
|
||||||
available_set_for dep_or_name, version
|
request_set = resolve_dependencies dep_or_name, version
|
||||||
|
|
||||||
@installed_gems = []
|
@installed_gems = []
|
||||||
|
|
||||||
gather_dependencies
|
options = {
|
||||||
|
:bin_dir => @bin_dir,
|
||||||
|
:build_args => @build_args,
|
||||||
|
:env_shebang => @env_shebang,
|
||||||
|
:force => @force,
|
||||||
|
:format_executable => @format_executable,
|
||||||
|
:ignore_dependencies => @ignore_dependencies,
|
||||||
|
:security_policy => @security_policy,
|
||||||
|
:user_install => @user_install,
|
||||||
|
:wrappers => @wrappers,
|
||||||
|
:install_as_default => @install_as_default
|
||||||
|
}
|
||||||
|
options[:install_dir] = @install_dir if @only_install_dir
|
||||||
|
|
||||||
# REFACTOR is the last gem always the one that the user requested?
|
request_set.install options do |_, installer|
|
||||||
# This code assumes that but is that actually validated by the code?
|
@installed_gems << installer.spec if installer
|
||||||
|
|
||||||
last = @gems_to_install.size - 1
|
|
||||||
@gems_to_install.each_with_index do |spec, index|
|
|
||||||
# REFACTOR more current spec set hardcoding, should be abstracted?
|
|
||||||
next if Gem::Specification.include?(spec) and index != last
|
|
||||||
|
|
||||||
# TODO: make this sorta_verbose so other users can benefit from it
|
|
||||||
say "Installing gem #{spec.full_name}" if Gem.configuration.really_verbose
|
|
||||||
|
|
||||||
source = @available.source_for spec
|
|
||||||
|
|
||||||
begin
|
|
||||||
# REFACTOR make the fetcher to use configurable
|
|
||||||
local_gem_path = source.download spec, @cache_dir
|
|
||||||
rescue Gem::RemoteFetcher::FetchError
|
|
||||||
# TODO I doubt all fetch errors are recoverable, we should at least
|
|
||||||
# report the errors probably.
|
|
||||||
next if @force
|
|
||||||
raise
|
|
||||||
end
|
|
||||||
|
|
||||||
if @development
|
|
||||||
if @dev_shallow
|
|
||||||
is_dev = @toplevel_specs.include? spec.full_name
|
|
||||||
else
|
|
||||||
is_dev = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
inst = Gem::Installer.new local_gem_path,
|
|
||||||
:bin_dir => @bin_dir,
|
|
||||||
:development => is_dev,
|
|
||||||
:env_shebang => @env_shebang,
|
|
||||||
:force => @force,
|
|
||||||
:format_executable => @format_executable,
|
|
||||||
:ignore_dependencies => @ignore_dependencies,
|
|
||||||
:install_dir => @install_dir,
|
|
||||||
:security_policy => @security_policy,
|
|
||||||
:user_install => @user_install,
|
|
||||||
:wrappers => @wrappers,
|
|
||||||
:build_args => @build_args
|
|
||||||
|
|
||||||
spec = inst.install
|
|
||||||
|
|
||||||
@installed_gems << spec
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@installed_gems.sort!
|
||||||
|
|
||||||
# Since this is currently only called for docs, we can be lazy and just say
|
# Since this is currently only called for docs, we can be lazy and just say
|
||||||
# it's documentation. Ideally the hook adder could decide whether to be in
|
# it's documentation. Ideally the hook adder could decide whether to be in
|
||||||
# the background or not, and what to call it.
|
# the background or not, and what to call it.
|
||||||
|
@ -385,18 +387,34 @@ class Gem::DependencyInstaller
|
||||||
@installed_gems
|
@installed_gems
|
||||||
end
|
end
|
||||||
|
|
||||||
def in_background what
|
def install_development_deps # :nodoc:
|
||||||
fork_happened = false
|
if @development and @dev_shallow then
|
||||||
if @build_docs_in_background and Process.respond_to?(:fork)
|
:shallow
|
||||||
begin
|
elsif @development then
|
||||||
Process.fork do
|
:all
|
||||||
yield
|
else
|
||||||
end
|
:none
|
||||||
fork_happened = true
|
|
||||||
say "#{what} in a background process."
|
|
||||||
rescue NotImplementedError
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
yield unless fork_happened
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def resolve_dependencies dep_or_name, version # :nodoc:
|
||||||
|
as = available_set_for dep_or_name, version
|
||||||
|
|
||||||
|
request_set = as.to_request_set install_development_deps
|
||||||
|
request_set.soft_missing = @force
|
||||||
|
|
||||||
|
installer_set = Gem::DependencyResolver::InstallerSet.new @domain
|
||||||
|
installer_set.always_install.concat request_set.always_install
|
||||||
|
installer_set.ignore_installed = @only_install_dir
|
||||||
|
|
||||||
|
if @ignore_dependencies then
|
||||||
|
installer_set.ignore_dependencies = true
|
||||||
|
request_set.soft_missing = true
|
||||||
|
end
|
||||||
|
|
||||||
|
request_set.resolve Gem::DependencyResolver.compose_sets(as, installer_set)
|
||||||
|
|
||||||
|
request_set
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,575 +1,240 @@
|
||||||
require 'rubygems'
|
require 'rubygems'
|
||||||
require 'rubygems/dependency'
|
require 'rubygems/dependency'
|
||||||
require 'rubygems/exceptions'
|
require 'rubygems/exceptions'
|
||||||
|
require 'rubygems/util/list'
|
||||||
|
|
||||||
require 'uri'
|
require 'uri'
|
||||||
require 'net/http'
|
require 'net/http'
|
||||||
|
|
||||||
module Gem
|
##
|
||||||
|
# Given a set of Gem::Dependency objects as +needed+ and a way to query the
|
||||||
|
# set of available specs via +set+, calculates a set of ActivationRequest
|
||||||
|
# objects which indicate all the specs that should be activated to meet the
|
||||||
|
# all the requirements.
|
||||||
|
|
||||||
# Raised when a DependencyConflict reaches the toplevel.
|
class Gem::DependencyResolver
|
||||||
# Indicates which dependencies were incompatible.
|
|
||||||
#
|
|
||||||
class DependencyResolutionError < Gem::Exception
|
|
||||||
def initialize(conflict)
|
|
||||||
@conflict = conflict
|
|
||||||
a, b = conflicting_dependencies
|
|
||||||
|
|
||||||
super "unable to resolve conflicting dependencies '#{a}' and '#{b}'"
|
##
|
||||||
end
|
# Contains all the conflicts encountered while doing resolution
|
||||||
|
|
||||||
attr_reader :conflict
|
attr_reader :conflicts
|
||||||
|
|
||||||
def conflicting_dependencies
|
attr_accessor :development
|
||||||
@conflict.conflicting_dependencies
|
|
||||||
end
|
attr_reader :missing
|
||||||
|
|
||||||
|
##
|
||||||
|
# When a missing dependency, don't stop. Just go on and record what was
|
||||||
|
# missing.
|
||||||
|
|
||||||
|
attr_accessor :soft_missing
|
||||||
|
|
||||||
|
def self.compose_sets *sets
|
||||||
|
Gem::DependencyResolver::ComposedSet.new(*sets)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Raised when a dependency requests a gem for which there is
|
##
|
||||||
# no spec.
|
# Provide a DependencyResolver that queries only against the already
|
||||||
#
|
# installed gems.
|
||||||
class UnsatisfiableDepedencyError < Gem::Exception
|
|
||||||
def initialize(dep)
|
|
||||||
super "unable to find any gem matching dependency '#{dep}'"
|
|
||||||
|
|
||||||
@dependency = dep
|
def self.for_current_gems needed
|
||||||
end
|
new needed, Gem::DependencyResolver::CurrentSet.new
|
||||||
|
|
||||||
attr_reader :dependency
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Raised when dependencies conflict and create the inability to
|
##
|
||||||
# find a valid possible spec for a request.
|
# Create DependencyResolver object which will resolve the tree starting
|
||||||
|
# with +needed+ Depedency objects.
|
||||||
#
|
#
|
||||||
class ImpossibleDependenciesError < Gem::Exception
|
# +set+ is an object that provides where to look for specifications to
|
||||||
def initialize(request, conflicts)
|
# satisify the Dependencies. This defaults to IndexSet, which will query
|
||||||
s = conflicts.size == 1 ? "" : "s"
|
# rubygems.org.
|
||||||
super "detected #{conflicts.size} conflict#{s} with dependency '#{request.dependency}'"
|
|
||||||
@request = request
|
|
||||||
@conflicts = conflicts
|
|
||||||
end
|
|
||||||
|
|
||||||
def dependency
|
def initialize needed, set = nil
|
||||||
@request.dependency
|
@set = set || Gem::DependencyResolver::IndexSet.new
|
||||||
end
|
@needed = needed
|
||||||
|
|
||||||
attr_reader :conflicts
|
@conflicts = nil
|
||||||
|
@development = false
|
||||||
|
@missing = []
|
||||||
|
@soft_missing = false
|
||||||
end
|
end
|
||||||
|
|
||||||
# Given a set of Gem::Dependency objects as +needed+ and a way
|
def requests s, act, reqs=nil
|
||||||
# to query the set of available specs via +set+, calculates
|
s.dependencies.reverse_each do |d|
|
||||||
# a set of ActivationRequest objects which indicate all the specs
|
next if d.type == :development and not @development
|
||||||
# that should be activated to meet the all the requirements.
|
reqs = Gem::List.new Gem::DependencyResolver::DependencyRequest.new(d, act), reqs
|
||||||
#
|
|
||||||
class DependencyResolver
|
|
||||||
|
|
||||||
# Represents a specification retrieved via the rubygems.org
|
|
||||||
# API. This is used to avoid having to load the full
|
|
||||||
# Specification object when all we need is the name, version,
|
|
||||||
# and dependencies.
|
|
||||||
#
|
|
||||||
class APISpecification
|
|
||||||
attr_reader :set # :nodoc:
|
|
||||||
|
|
||||||
def initialize(set, api_data)
|
|
||||||
@set = set
|
|
||||||
@name = api_data[:name]
|
|
||||||
@version = Gem::Version.new api_data[:number]
|
|
||||||
@dependencies = api_data[:dependencies].map do |name, ver|
|
|
||||||
Gem::Dependency.new name, ver.split(/\s*,\s*/)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
attr_reader :name, :version, :dependencies
|
|
||||||
|
|
||||||
def == other # :nodoc:
|
|
||||||
self.class === other and
|
|
||||||
@set == other.set and
|
|
||||||
@name == other.name and
|
|
||||||
@version == other.version and
|
|
||||||
@dependencies == other.dependencies
|
|
||||||
end
|
|
||||||
|
|
||||||
def full_name
|
|
||||||
"#{@name}-#{@version}"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# The global rubygems pool, available via the rubygems.org API.
|
@set.prefetch reqs
|
||||||
# Returns instances of APISpecification.
|
|
||||||
#
|
|
||||||
class APISet
|
|
||||||
def initialize
|
|
||||||
@data = Hash.new { |h,k| h[k] = [] }
|
|
||||||
@dep_uri = URI 'https://rubygems.org/api/v1/dependencies'
|
|
||||||
end
|
|
||||||
|
|
||||||
# Return data for all versions of the gem +name+.
|
reqs
|
||||||
#
|
end
|
||||||
def versions(name)
|
|
||||||
if @data.key?(name)
|
|
||||||
return @data[name]
|
|
||||||
end
|
|
||||||
|
|
||||||
uri = @dep_uri + "?gems=#{name}"
|
##
|
||||||
str = Gem::RemoteFetcher.fetcher.fetch_path uri
|
# Proceed with resolution! Returns an array of ActivationRequest objects.
|
||||||
|
|
||||||
Marshal.load(str).each do |ver|
|
def resolve
|
||||||
@data[ver[:name]] << ver
|
@conflicts = []
|
||||||
end
|
|
||||||
|
|
||||||
@data[name]
|
needed = nil
|
||||||
end
|
|
||||||
|
|
||||||
# Return an array of APISpecification objects matching
|
@needed.reverse_each do |n|
|
||||||
# DependencyRequest +req+.
|
needed = Gem::List.new(Gem::DependencyResolver::DependencyRequest.new(n, nil), needed)
|
||||||
#
|
|
||||||
def find_all(req)
|
|
||||||
res = []
|
|
||||||
|
|
||||||
versions(req.name).each do |ver|
|
|
||||||
if req.dependency.match? req.name, ver[:number]
|
|
||||||
res << APISpecification.new(self, ver)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
res
|
|
||||||
end
|
|
||||||
|
|
||||||
# A hint run by the resolver to allow the Set to fetch
|
|
||||||
# data for DependencyRequests +reqs+.
|
|
||||||
#
|
|
||||||
def prefetch(reqs)
|
|
||||||
names = reqs.map { |r| r.dependency.name }
|
|
||||||
needed = names.find_all { |d| !@data.key?(d) }
|
|
||||||
|
|
||||||
return if needed.empty?
|
|
||||||
|
|
||||||
uri = @dep_uri + "?gems=#{needed.sort.join ','}"
|
|
||||||
str = Gem::RemoteFetcher.fetcher.fetch_path uri
|
|
||||||
|
|
||||||
Marshal.load(str).each do |ver|
|
|
||||||
@data[ver[:name]] << ver
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Represents a possible Specification object returned
|
res = resolve_for needed, nil
|
||||||
# from IndexSet. Used to delay needed to download full
|
|
||||||
# Specification objects when only the +name+ and +version+
|
|
||||||
# are needed.
|
|
||||||
#
|
|
||||||
class IndexSpecification
|
|
||||||
def initialize(set, name, version, source, plat)
|
|
||||||
@set = set
|
|
||||||
@name = name
|
|
||||||
@version = version
|
|
||||||
@source = source
|
|
||||||
@platform = plat
|
|
||||||
|
|
||||||
@spec = nil
|
raise Gem::DependencyResolutionError, res if
|
||||||
end
|
res.kind_of? Gem::DependencyResolver::DependencyConflict
|
||||||
|
|
||||||
attr_reader :name, :version, :source
|
res.to_a
|
||||||
|
end
|
||||||
|
|
||||||
def full_name
|
##
|
||||||
"#{@name}-#{@version}"
|
# The meat of the algorithm. Given +needed+ DependencyRequest objects and
|
||||||
end
|
# +specs+ being a list to ActivationRequest, calculate a new list of
|
||||||
|
# ActivationRequest objects.
|
||||||
|
|
||||||
def spec
|
def resolve_for needed, specs
|
||||||
@spec ||= @set.load_spec(@name, @version, @source)
|
while needed
|
||||||
end
|
dep = needed.value
|
||||||
|
needed = needed.tail
|
||||||
|
|
||||||
def dependencies
|
# If there is already a spec activated for the requested name...
|
||||||
spec.dependencies
|
if specs && existing = specs.find { |s| dep.name == s.name }
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# The global rubygems pool represented via the traditional
|
# then we're done since this new dep matches the
|
||||||
# source index.
|
# existing spec.
|
||||||
#
|
next if dep.matches_spec? existing
|
||||||
class IndexSet
|
|
||||||
def initialize
|
|
||||||
@f = Gem::SpecFetcher.fetcher
|
|
||||||
|
|
||||||
@all = Hash.new { |h,k| h[k] = [] }
|
# There is a conflict! We return the conflict
|
||||||
|
# object which will be seen by the caller and be
|
||||||
|
# handled at the right level.
|
||||||
|
|
||||||
list, _ = @f.available_specs(:released)
|
# If the existing activation indicates that there
|
||||||
list.each do |uri, specs|
|
# are other possibles for it, then issue the conflict
|
||||||
specs.each do |n|
|
# on the dep for the activation itself. Otherwise, issue
|
||||||
@all[n.name] << [uri, n]
|
# it on the requester's request itself.
|
||||||
end
|
#
|
||||||
end
|
if existing.others_possible?
|
||||||
|
conflict =
|
||||||
@specs = {}
|
Gem::DependencyResolver::DependencyConflict.new dep, existing
|
||||||
end
|
|
||||||
|
|
||||||
# Return an array of IndexSpecification objects matching
|
|
||||||
# DependencyRequest +req+.
|
|
||||||
#
|
|
||||||
def find_all(req)
|
|
||||||
res = []
|
|
||||||
|
|
||||||
name = req.dependency.name
|
|
||||||
|
|
||||||
@all[name].each do |uri, n|
|
|
||||||
if req.dependency.match? n
|
|
||||||
res << IndexSpecification.new(self, n.name, n.version,
|
|
||||||
uri, n.platform)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
res
|
|
||||||
end
|
|
||||||
|
|
||||||
# No prefetching needed since we load the whole index in
|
|
||||||
# initially.
|
|
||||||
#
|
|
||||||
def prefetch(gems)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Called from IndexSpecification to get a true Specification
|
|
||||||
# object.
|
|
||||||
#
|
|
||||||
def load_spec(name, ver, source)
|
|
||||||
key = "#{name}-#{ver}"
|
|
||||||
@specs[key] ||= source.fetch_spec(Gem::NameTuple.new(name, ver))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# A set which represents the installed gems. Respects
|
|
||||||
# all the normal settings that control where to look
|
|
||||||
# for installed gems.
|
|
||||||
#
|
|
||||||
class CurrentSet
|
|
||||||
def find_all(req)
|
|
||||||
req.dependency.matching_specs
|
|
||||||
end
|
|
||||||
|
|
||||||
def prefetch(gems)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Create DependencyResolver object which will resolve
|
|
||||||
# the tree starting with +needed+ Depedency objects.
|
|
||||||
#
|
|
||||||
# +set+ is an object that provides where to look for
|
|
||||||
# specifications to satisify the Dependencies. This
|
|
||||||
# defaults to IndexSet, which will query rubygems.org.
|
|
||||||
#
|
|
||||||
def initialize(needed, set=IndexSet.new)
|
|
||||||
@set = set || IndexSet.new # Allow nil to mean IndexSet
|
|
||||||
@needed = needed
|
|
||||||
|
|
||||||
@conflicts = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
# Provide a DependencyResolver that queries only against
|
|
||||||
# the already installed gems.
|
|
||||||
#
|
|
||||||
def self.for_current_gems(needed)
|
|
||||||
new needed, CurrentSet.new
|
|
||||||
end
|
|
||||||
|
|
||||||
# Contains all the conflicts encountered while doing resolution
|
|
||||||
#
|
|
||||||
attr_reader :conflicts
|
|
||||||
|
|
||||||
# Proceed with resolution! Returns an array of ActivationRequest
|
|
||||||
# objects.
|
|
||||||
#
|
|
||||||
def resolve
|
|
||||||
@conflicts = []
|
|
||||||
|
|
||||||
needed = @needed.map { |n| DependencyRequest.new(n, nil) }
|
|
||||||
|
|
||||||
res = resolve_for needed, []
|
|
||||||
|
|
||||||
if res.kind_of? DependencyConflict
|
|
||||||
raise DependencyResolutionError.new(res)
|
|
||||||
end
|
|
||||||
|
|
||||||
res
|
|
||||||
end
|
|
||||||
|
|
||||||
# Used internally to indicate that a dependency conflicted
|
|
||||||
# with a spec that would be activated.
|
|
||||||
#
|
|
||||||
class DependencyConflict
|
|
||||||
def initialize(dependency, activated, failed_dep=dependency)
|
|
||||||
@dependency = dependency
|
|
||||||
@activated = activated
|
|
||||||
@failed_dep = failed_dep
|
|
||||||
end
|
|
||||||
|
|
||||||
attr_reader :dependency, :activated
|
|
||||||
|
|
||||||
# Return the Specification that listed the dependency
|
|
||||||
#
|
|
||||||
def requester
|
|
||||||
@failed_dep.requester
|
|
||||||
end
|
|
||||||
|
|
||||||
def for_spec?(spec)
|
|
||||||
@dependency.name == spec.name
|
|
||||||
end
|
|
||||||
|
|
||||||
# Return the 2 dependency objects that conflicted
|
|
||||||
#
|
|
||||||
def conflicting_dependencies
|
|
||||||
[@failed_dep.dependency, @activated.request.dependency]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Used Internally. Wraps a Depedency object to also track
|
|
||||||
# which spec contained the Dependency.
|
|
||||||
#
|
|
||||||
class DependencyRequest
|
|
||||||
def initialize(dep, act)
|
|
||||||
@dependency = dep
|
|
||||||
@requester = act
|
|
||||||
end
|
|
||||||
|
|
||||||
attr_reader :dependency, :requester
|
|
||||||
|
|
||||||
def name
|
|
||||||
@dependency.name
|
|
||||||
end
|
|
||||||
|
|
||||||
def matches_spec?(spec)
|
|
||||||
@dependency.matches_spec? spec
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_s
|
|
||||||
@dependency.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
def ==(other)
|
|
||||||
case other
|
|
||||||
when Dependency
|
|
||||||
@dependency == other
|
|
||||||
when DependencyRequest
|
|
||||||
@dependency == other.dependency && @requester == other.requester
|
|
||||||
else
|
else
|
||||||
false
|
depreq = existing.request.requester.request
|
||||||
|
conflict =
|
||||||
|
Gem::DependencyResolver::DependencyConflict.new depreq, existing, dep
|
||||||
end
|
end
|
||||||
end
|
@conflicts << conflict
|
||||||
end
|
|
||||||
|
|
||||||
# Specifies a Specification object that should be activated.
|
return conflict
|
||||||
# Also contains a dependency that was used to introduce this
|
|
||||||
# activation.
|
|
||||||
#
|
|
||||||
class ActivationRequest
|
|
||||||
def initialize(spec, req, others_possible=true)
|
|
||||||
@spec = spec
|
|
||||||
@request = req
|
|
||||||
@others_possible = others_possible
|
|
||||||
end
|
end
|
||||||
|
|
||||||
attr_reader :spec, :request
|
# Get a list of all specs that satisfy dep
|
||||||
|
possible = @set.find_all dep
|
||||||
|
|
||||||
# Indicate if this activation is one of a set of possible
|
case possible.size
|
||||||
# requests for the same Dependency request.
|
when 0
|
||||||
#
|
@missing << dep
|
||||||
def others_possible?
|
|
||||||
@others_possible
|
|
||||||
end
|
|
||||||
|
|
||||||
# Return the ActivationRequest that contained the dependency
|
unless @soft_missing
|
||||||
# that we were activated for.
|
|
||||||
#
|
|
||||||
def parent
|
|
||||||
@request.requester
|
|
||||||
end
|
|
||||||
|
|
||||||
def name
|
|
||||||
@spec.name
|
|
||||||
end
|
|
||||||
|
|
||||||
def full_name
|
|
||||||
@spec.full_name
|
|
||||||
end
|
|
||||||
|
|
||||||
def version
|
|
||||||
@spec.version
|
|
||||||
end
|
|
||||||
|
|
||||||
def full_spec
|
|
||||||
Gem::Specification === @spec ? @spec : @spec.spec
|
|
||||||
end
|
|
||||||
|
|
||||||
def download(path)
|
|
||||||
if @spec.respond_to? :source
|
|
||||||
source = @spec.source
|
|
||||||
else
|
|
||||||
source = Gem.sources.first
|
|
||||||
end
|
|
||||||
|
|
||||||
Gem.ensure_gem_subdirectories path
|
|
||||||
|
|
||||||
source.download full_spec, path
|
|
||||||
end
|
|
||||||
|
|
||||||
def ==(other)
|
|
||||||
case other
|
|
||||||
when Gem::Specification
|
|
||||||
@spec == other
|
|
||||||
when ActivationRequest
|
|
||||||
@spec == other.spec && @request == other.request
|
|
||||||
else
|
|
||||||
false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# Indicates if the requested gem has already been installed.
|
|
||||||
|
|
||||||
def installed?
|
|
||||||
this_spec = full_spec
|
|
||||||
|
|
||||||
Gem::Specification.any? do |s|
|
|
||||||
s == this_spec
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def requests(s, act)
|
|
||||||
reqs = []
|
|
||||||
s.dependencies.each do |d|
|
|
||||||
next unless d.type == :runtime
|
|
||||||
reqs << DependencyRequest.new(d, act)
|
|
||||||
end
|
|
||||||
|
|
||||||
@set.prefetch(reqs)
|
|
||||||
|
|
||||||
reqs
|
|
||||||
end
|
|
||||||
|
|
||||||
# The meat of the algorithm. Given +needed+ DependencyRequest objects
|
|
||||||
# and +specs+ being a list to ActivationRequest, calculate a new list
|
|
||||||
# of ActivationRequest objects.
|
|
||||||
#
|
|
||||||
def resolve_for(needed, specs)
|
|
||||||
until needed.empty?
|
|
||||||
dep = needed.shift
|
|
||||||
|
|
||||||
# If there is already a spec activated for the requested name...
|
|
||||||
if existing = specs.find { |s| dep.name == s.name }
|
|
||||||
|
|
||||||
# then we're done since this new dep matches the
|
|
||||||
# existing spec.
|
|
||||||
next if dep.matches_spec? existing
|
|
||||||
|
|
||||||
# There is a conflict! We return the conflict
|
|
||||||
# object which will be seen by the caller and be
|
|
||||||
# handled at the right level.
|
|
||||||
|
|
||||||
# If the existing activation indicates that there
|
|
||||||
# are other possibles for it, then issue the conflict
|
|
||||||
# on the dep for the activation itself. Otherwise, issue
|
|
||||||
# it on the requester's request itself.
|
|
||||||
#
|
|
||||||
if existing.others_possible?
|
|
||||||
conflict = DependencyConflict.new(dep, existing)
|
|
||||||
else
|
|
||||||
depreq = existing.request.requester.request
|
|
||||||
conflict = DependencyConflict.new(depreq, existing, dep)
|
|
||||||
end
|
|
||||||
@conflicts << conflict
|
|
||||||
|
|
||||||
return conflict
|
|
||||||
end
|
|
||||||
|
|
||||||
# Get a list of all specs that satisfy dep
|
|
||||||
possible = @set.find_all(dep)
|
|
||||||
|
|
||||||
case possible.size
|
|
||||||
when 0
|
|
||||||
# If there are none, then our work here is done.
|
# If there are none, then our work here is done.
|
||||||
raise UnsatisfiableDepedencyError.new(dep)
|
raise Gem::UnsatisfiableDependencyError, dep
|
||||||
when 1
|
|
||||||
# If there is one, then we just add it to specs
|
|
||||||
# and process the specs dependencies by adding
|
|
||||||
# them to needed.
|
|
||||||
|
|
||||||
spec = possible.first
|
|
||||||
act = ActivationRequest.new(spec, dep, false)
|
|
||||||
|
|
||||||
specs << act
|
|
||||||
|
|
||||||
# Put the deps for at the beginning of needed
|
|
||||||
# rather than the end to match the depth first
|
|
||||||
# searching done by the multiple case code below.
|
|
||||||
#
|
|
||||||
# This keeps the error messages consistent.
|
|
||||||
needed = requests(spec, act) + needed
|
|
||||||
else
|
|
||||||
# There are multiple specs for this dep. This is
|
|
||||||
# the case that this class is built to handle.
|
|
||||||
|
|
||||||
# Sort them so that we try the highest versions
|
|
||||||
# first.
|
|
||||||
possible = possible.sort_by { |s| s.version }
|
|
||||||
|
|
||||||
# We track the conflicts seen so that we can report them
|
|
||||||
# to help the user figure out how to fix the situation.
|
|
||||||
conflicts = []
|
|
||||||
|
|
||||||
# To figure out which to pick, we keep resolving
|
|
||||||
# given each one being activated and if there isn't
|
|
||||||
# a conflict, we know we've found a full set.
|
|
||||||
#
|
|
||||||
# We use an until loop rather than #reverse_each
|
|
||||||
# to keep the stack short since we're using a recursive
|
|
||||||
# algorithm.
|
|
||||||
#
|
|
||||||
until possible.empty?
|
|
||||||
s = possible.pop
|
|
||||||
|
|
||||||
# Recursively call #resolve_for with this spec
|
|
||||||
# and add it's dependencies into the picture...
|
|
||||||
|
|
||||||
act = ActivationRequest.new(s, dep)
|
|
||||||
|
|
||||||
try = requests(s, act) + needed
|
|
||||||
|
|
||||||
res = resolve_for(try, specs + [act])
|
|
||||||
|
|
||||||
# While trying to resolve these dependencies, there may
|
|
||||||
# be a conflict!
|
|
||||||
|
|
||||||
if res.kind_of? DependencyConflict
|
|
||||||
# The conflict might be created not by this invocation
|
|
||||||
# but rather one up the stack, so if we can't attempt
|
|
||||||
# to resolve this conflict (conflict isn't with the spec +s+)
|
|
||||||
# then just return it so the caller can try to sort it out.
|
|
||||||
return res unless res.for_spec? s
|
|
||||||
|
|
||||||
# Otherwise, this is a conflict that we can attempt to fix
|
|
||||||
conflicts << [s, res]
|
|
||||||
|
|
||||||
# Optimization:
|
|
||||||
#
|
|
||||||
# Because the conflict indicates the dependency that trigger
|
|
||||||
# it, we can prune possible based on this new information.
|
|
||||||
#
|
|
||||||
# This cuts down on the number of iterations needed.
|
|
||||||
possible.delete_if { |x| !res.dependency.matches_spec? x }
|
|
||||||
else
|
|
||||||
# No conflict, return the specs
|
|
||||||
return res
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# We tried all possibles and nothing worked, so we let the user
|
|
||||||
# know and include as much information about the problem since
|
|
||||||
# the user is going to have to take action to fix this.
|
|
||||||
raise ImpossibleDependenciesError.new(dep, conflicts)
|
|
||||||
end
|
end
|
||||||
end
|
when 1
|
||||||
|
# If there is one, then we just add it to specs
|
||||||
|
# and process the specs dependencies by adding
|
||||||
|
# them to needed.
|
||||||
|
|
||||||
specs
|
spec = possible.first
|
||||||
|
act = Gem::DependencyResolver::ActivationRequest.new spec, dep, false
|
||||||
|
|
||||||
|
specs = Gem::List.prepend specs, act
|
||||||
|
|
||||||
|
# Put the deps for at the beginning of needed
|
||||||
|
# rather than the end to match the depth first
|
||||||
|
# searching done by the multiple case code below.
|
||||||
|
#
|
||||||
|
# This keeps the error messages consistent.
|
||||||
|
needed = requests(spec, act, needed)
|
||||||
|
else
|
||||||
|
# There are multiple specs for this dep. This is
|
||||||
|
# the case that this class is built to handle.
|
||||||
|
|
||||||
|
# Sort them so that we try the highest versions
|
||||||
|
# first.
|
||||||
|
possible = possible.sort_by { |s| [s.source, s.version] }
|
||||||
|
|
||||||
|
# We track the conflicts seen so that we can report them
|
||||||
|
# to help the user figure out how to fix the situation.
|
||||||
|
conflicts = []
|
||||||
|
|
||||||
|
# To figure out which to pick, we keep resolving
|
||||||
|
# given each one being activated and if there isn't
|
||||||
|
# a conflict, we know we've found a full set.
|
||||||
|
#
|
||||||
|
# We use an until loop rather than #reverse_each
|
||||||
|
# to keep the stack short since we're using a recursive
|
||||||
|
# algorithm.
|
||||||
|
#
|
||||||
|
until possible.empty?
|
||||||
|
s = possible.pop
|
||||||
|
|
||||||
|
# Recursively call #resolve_for with this spec
|
||||||
|
# and add it's dependencies into the picture...
|
||||||
|
|
||||||
|
act = Gem::DependencyResolver::ActivationRequest.new s, dep
|
||||||
|
|
||||||
|
try = requests(s, act, needed)
|
||||||
|
|
||||||
|
res = resolve_for try, Gem::List.prepend(specs, act)
|
||||||
|
|
||||||
|
# While trying to resolve these dependencies, there may
|
||||||
|
# be a conflict!
|
||||||
|
|
||||||
|
if res.kind_of? Gem::DependencyResolver::DependencyConflict
|
||||||
|
# The conflict might be created not by this invocation
|
||||||
|
# but rather one up the stack, so if we can't attempt
|
||||||
|
# to resolve this conflict (conflict isn't with the spec +s+)
|
||||||
|
# then just return it so the caller can try to sort it out.
|
||||||
|
return res unless res.for_spec? s
|
||||||
|
|
||||||
|
# Otherwise, this is a conflict that we can attempt to fix
|
||||||
|
conflicts << [s, res]
|
||||||
|
|
||||||
|
# Optimization:
|
||||||
|
#
|
||||||
|
# Because the conflict indicates the dependency that trigger
|
||||||
|
# it, we can prune possible based on this new information.
|
||||||
|
#
|
||||||
|
# This cuts down on the number of iterations needed.
|
||||||
|
possible.delete_if { |x| !res.dependency.matches_spec? x }
|
||||||
|
else
|
||||||
|
# No conflict, return the specs
|
||||||
|
return res
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# We tried all possibles and nothing worked, so we let the user
|
||||||
|
# know and include as much information about the problem since
|
||||||
|
# the user is going to have to take action to fix this.
|
||||||
|
raise Gem::ImpossibleDependenciesError.new(dep, conflicts)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
specs
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
require 'rubygems/dependency_resolver/api_set'
|
||||||
|
require 'rubygems/dependency_resolver/api_specification'
|
||||||
|
require 'rubygems/dependency_resolver/activation_request'
|
||||||
|
require 'rubygems/dependency_resolver/composed_set'
|
||||||
|
require 'rubygems/dependency_resolver/current_set'
|
||||||
|
require 'rubygems/dependency_resolver/dependency_conflict'
|
||||||
|
require 'rubygems/dependency_resolver/dependency_request'
|
||||||
|
require 'rubygems/dependency_resolver/index_set'
|
||||||
|
require 'rubygems/dependency_resolver/index_specification'
|
||||||
|
require 'rubygems/dependency_resolver/installed_specification'
|
||||||
|
require 'rubygems/dependency_resolver/installer_set'
|
||||||
|
|
||||||
|
|
109
lib/rubygems/dependency_resolver/activation_request.rb
Normal file
109
lib/rubygems/dependency_resolver/activation_request.rb
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
##
|
||||||
|
# Specifies a Specification object that should be activated.
|
||||||
|
# Also contains a dependency that was used to introduce this
|
||||||
|
# activation.
|
||||||
|
|
||||||
|
class Gem::DependencyResolver::ActivationRequest
|
||||||
|
|
||||||
|
attr_reader :request
|
||||||
|
|
||||||
|
attr_reader :spec
|
||||||
|
|
||||||
|
def initialize spec, req, others_possible = true
|
||||||
|
@spec = spec
|
||||||
|
@request = req
|
||||||
|
@others_possible = others_possible
|
||||||
|
end
|
||||||
|
|
||||||
|
def == other
|
||||||
|
case other
|
||||||
|
when Gem::Specification
|
||||||
|
@spec == other
|
||||||
|
when Gem::DependencyResolver::ActivationRequest
|
||||||
|
@spec == other.spec && @request == other.request
|
||||||
|
else
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def download path
|
||||||
|
if @spec.respond_to? :source
|
||||||
|
source = @spec.source
|
||||||
|
else
|
||||||
|
source = Gem.sources.first
|
||||||
|
end
|
||||||
|
|
||||||
|
Gem.ensure_gem_subdirectories path
|
||||||
|
|
||||||
|
source.download full_spec, path
|
||||||
|
end
|
||||||
|
|
||||||
|
def full_name
|
||||||
|
@spec.full_name
|
||||||
|
end
|
||||||
|
|
||||||
|
def full_spec
|
||||||
|
Gem::Specification === @spec ? @spec : @spec.spec
|
||||||
|
end
|
||||||
|
|
||||||
|
def inspect # :nodoc:
|
||||||
|
others_possible = nil
|
||||||
|
others_possible = ' (others possible)' if @others_possible
|
||||||
|
|
||||||
|
'#<%s for %p from %s%s>' % [
|
||||||
|
self.class, @spec, @request, others_possible
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Indicates if the requested gem has already been installed.
|
||||||
|
|
||||||
|
def installed?
|
||||||
|
this_spec = full_spec
|
||||||
|
|
||||||
|
Gem::Specification.any? do |s|
|
||||||
|
s == this_spec
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def name
|
||||||
|
@spec.name
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Indicate if this activation is one of a set of possible
|
||||||
|
# requests for the same Dependency request.
|
||||||
|
|
||||||
|
def others_possible?
|
||||||
|
@others_possible
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Return the ActivationRequest that contained the dependency
|
||||||
|
# that we were activated for.
|
||||||
|
|
||||||
|
def parent
|
||||||
|
@request.requester
|
||||||
|
end
|
||||||
|
|
||||||
|
def pretty_print q # :nodoc:
|
||||||
|
q.group 2, '[Activation request', ']' do
|
||||||
|
q.breakable
|
||||||
|
q.pp @spec
|
||||||
|
|
||||||
|
q.breakable
|
||||||
|
q.text ' for '
|
||||||
|
q.pp @request
|
||||||
|
|
||||||
|
|
||||||
|
q.breakable
|
||||||
|
q.text ' (other possible)' if @others_possible
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def version
|
||||||
|
@spec.version
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
65
lib/rubygems/dependency_resolver/api_set.rb
Normal file
65
lib/rubygems/dependency_resolver/api_set.rb
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
##
|
||||||
|
# The global rubygems pool, available via the rubygems.org API.
|
||||||
|
# Returns instances of APISpecification.
|
||||||
|
|
||||||
|
class Gem::DependencyResolver::APISet
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@data = Hash.new { |h,k| h[k] = [] }
|
||||||
|
@dep_uri = URI 'https://rubygems.org/api/v1/dependencies'
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Return an array of APISpecification objects matching
|
||||||
|
# DependencyRequest +req+.
|
||||||
|
|
||||||
|
def find_all req
|
||||||
|
res = []
|
||||||
|
|
||||||
|
versions(req.name).each do |ver|
|
||||||
|
if req.dependency.match? req.name, ver[:number]
|
||||||
|
res << Gem::DependencyResolver::APISpecification.new(self, ver)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
res
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# A hint run by the resolver to allow the Set to fetch
|
||||||
|
# data for DependencyRequests +reqs+.
|
||||||
|
|
||||||
|
def prefetch reqs
|
||||||
|
names = reqs.map { |r| r.dependency.name }
|
||||||
|
needed = names.find_all { |d| !@data.key?(d) }
|
||||||
|
|
||||||
|
return if needed.empty?
|
||||||
|
|
||||||
|
uri = @dep_uri + "?gems=#{needed.sort.join ','}"
|
||||||
|
str = Gem::RemoteFetcher.fetcher.fetch_path uri
|
||||||
|
|
||||||
|
Marshal.load(str).each do |ver|
|
||||||
|
@data[ver[:name]] << ver
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Return data for all versions of the gem +name+.
|
||||||
|
|
||||||
|
def versions name
|
||||||
|
if @data.key?(name)
|
||||||
|
return @data[name]
|
||||||
|
end
|
||||||
|
|
||||||
|
uri = @dep_uri + "?gems=#{name}"
|
||||||
|
str = Gem::RemoteFetcher.fetcher.fetch_path uri
|
||||||
|
|
||||||
|
Marshal.load(str).each do |ver|
|
||||||
|
@data[ver[:name]] << ver
|
||||||
|
end
|
||||||
|
|
||||||
|
@data[name]
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
36
lib/rubygems/dependency_resolver/api_specification.rb
Normal file
36
lib/rubygems/dependency_resolver/api_specification.rb
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
##
|
||||||
|
# Represents a specification retrieved via the rubygems.org
|
||||||
|
# API. This is used to avoid having to load the full
|
||||||
|
# Specification object when all we need is the name, version,
|
||||||
|
# and dependencies.
|
||||||
|
|
||||||
|
class Gem::DependencyResolver::APISpecification
|
||||||
|
|
||||||
|
attr_reader :dependencies
|
||||||
|
attr_reader :name
|
||||||
|
attr_reader :set # :nodoc:
|
||||||
|
attr_reader :version
|
||||||
|
|
||||||
|
def initialize(set, api_data)
|
||||||
|
@set = set
|
||||||
|
@name = api_data[:name]
|
||||||
|
@version = Gem::Version.new api_data[:number]
|
||||||
|
@dependencies = api_data[:dependencies].map do |name, ver|
|
||||||
|
Gem::Dependency.new name, ver.split(/\s*,\s*/)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def == other # :nodoc:
|
||||||
|
self.class === other and
|
||||||
|
@set == other.set and
|
||||||
|
@name == other.name and
|
||||||
|
@version == other.version and
|
||||||
|
@dependencies == other.dependencies
|
||||||
|
end
|
||||||
|
|
||||||
|
def full_name
|
||||||
|
"#{@name}-#{@version}"
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
18
lib/rubygems/dependency_resolver/composed_set.rb
Normal file
18
lib/rubygems/dependency_resolver/composed_set.rb
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
class Gem::DependencyResolver::ComposedSet
|
||||||
|
|
||||||
|
def initialize *sets
|
||||||
|
@sets = sets
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_all req
|
||||||
|
res = []
|
||||||
|
@sets.each { |s| res += s.find_all(req) }
|
||||||
|
res
|
||||||
|
end
|
||||||
|
|
||||||
|
def prefetch reqs
|
||||||
|
@sets.each { |s| s.prefetch(reqs) }
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
16
lib/rubygems/dependency_resolver/current_set.rb
Normal file
16
lib/rubygems/dependency_resolver/current_set.rb
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
##
|
||||||
|
# A set which represents the installed gems. Respects
|
||||||
|
# all the normal settings that control where to look
|
||||||
|
# for installed gems.
|
||||||
|
|
||||||
|
class Gem::DependencyResolver::CurrentSet
|
||||||
|
|
||||||
|
def find_all req
|
||||||
|
req.dependency.matching_specs
|
||||||
|
end
|
||||||
|
|
||||||
|
def prefetch gems
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
85
lib/rubygems/dependency_resolver/dependency_conflict.rb
Normal file
85
lib/rubygems/dependency_resolver/dependency_conflict.rb
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
##
|
||||||
|
# Used internally to indicate that a dependency conflicted
|
||||||
|
# with a spec that would be activated.
|
||||||
|
|
||||||
|
class Gem::DependencyResolver::DependencyConflict
|
||||||
|
|
||||||
|
attr_reader :activated
|
||||||
|
|
||||||
|
attr_reader :dependency
|
||||||
|
|
||||||
|
def initialize(dependency, activated, failed_dep=dependency)
|
||||||
|
@dependency = dependency
|
||||||
|
@activated = activated
|
||||||
|
@failed_dep = failed_dep
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Return the 2 dependency objects that conflicted
|
||||||
|
|
||||||
|
def conflicting_dependencies
|
||||||
|
[@failed_dep.dependency, @activated.request.dependency]
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Explanation of the conflict used by exceptions to print useful messages
|
||||||
|
|
||||||
|
def explanation
|
||||||
|
activated = @activated.spec.full_name
|
||||||
|
requirement = @failed_dep.dependency.requirement
|
||||||
|
|
||||||
|
" Activated %s instead of (%s) via:\n %s\n" % [
|
||||||
|
activated, requirement, request_path.join(', ')
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
def for_spec?(spec)
|
||||||
|
@dependency.name == spec.name
|
||||||
|
end
|
||||||
|
|
||||||
|
def pretty_print q # :nodoc:
|
||||||
|
q.group 2, '[Dependency conflict: ', ']' do
|
||||||
|
q.breakable
|
||||||
|
|
||||||
|
q.text 'activated '
|
||||||
|
q.pp @activated
|
||||||
|
|
||||||
|
q.breakable
|
||||||
|
q.text ' dependency '
|
||||||
|
q.pp @dependency
|
||||||
|
|
||||||
|
q.breakable
|
||||||
|
if @dependency == @failed_dep then
|
||||||
|
q.text ' failed'
|
||||||
|
else
|
||||||
|
q.text ' failed dependency '
|
||||||
|
q.pp @failed_dep
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Path of specifications that requested this dependency
|
||||||
|
|
||||||
|
def request_path
|
||||||
|
current = requester
|
||||||
|
path = []
|
||||||
|
|
||||||
|
while current do
|
||||||
|
path << current.spec.full_name
|
||||||
|
|
||||||
|
current = current.request.requester
|
||||||
|
end
|
||||||
|
|
||||||
|
path
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Return the Specification that listed the dependency
|
||||||
|
|
||||||
|
def requester
|
||||||
|
@failed_dep.requester
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
51
lib/rubygems/dependency_resolver/dependency_request.rb
Normal file
51
lib/rubygems/dependency_resolver/dependency_request.rb
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
##
|
||||||
|
# Used Internally. Wraps a Dependency object to also track which spec
|
||||||
|
# contained the Dependency.
|
||||||
|
|
||||||
|
class Gem::DependencyResolver::DependencyRequest
|
||||||
|
|
||||||
|
attr_reader :dependency
|
||||||
|
|
||||||
|
attr_reader :requester
|
||||||
|
|
||||||
|
def initialize(dep, act)
|
||||||
|
@dependency = dep
|
||||||
|
@requester = act
|
||||||
|
end
|
||||||
|
|
||||||
|
def ==(other)
|
||||||
|
case other
|
||||||
|
when Gem::Dependency
|
||||||
|
@dependency == other
|
||||||
|
when Gem::DependencyResolver::DependencyRequest
|
||||||
|
@dependency == other.dependency && @requester == other.requester
|
||||||
|
else
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def matches_spec?(spec)
|
||||||
|
@dependency.matches_spec? spec
|
||||||
|
end
|
||||||
|
|
||||||
|
def name
|
||||||
|
@dependency.name
|
||||||
|
end
|
||||||
|
|
||||||
|
def pretty_print q # :nodoc:
|
||||||
|
q.group 2, '[Dependency request ', ']' do
|
||||||
|
q.breakable
|
||||||
|
q.text @dependency.to_s
|
||||||
|
|
||||||
|
q.breakable
|
||||||
|
q.text ' requested by '
|
||||||
|
q.pp @requester
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s # :nodoc:
|
||||||
|
@dependency.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
59
lib/rubygems/dependency_resolver/index_set.rb
Normal file
59
lib/rubygems/dependency_resolver/index_set.rb
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
##
|
||||||
|
# The global rubygems pool represented via the traditional
|
||||||
|
# source index.
|
||||||
|
|
||||||
|
class Gem::DependencyResolver::IndexSet
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@f = Gem::SpecFetcher.fetcher
|
||||||
|
|
||||||
|
@all = Hash.new { |h,k| h[k] = [] }
|
||||||
|
|
||||||
|
list, = @f.available_specs :released
|
||||||
|
|
||||||
|
list.each do |uri, specs|
|
||||||
|
specs.each do |n|
|
||||||
|
@all[n.name] << [uri, n]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@specs = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Return an array of IndexSpecification objects matching
|
||||||
|
# DependencyRequest +req+.
|
||||||
|
|
||||||
|
def find_all req
|
||||||
|
res = []
|
||||||
|
|
||||||
|
name = req.dependency.name
|
||||||
|
|
||||||
|
@all[name].each do |uri, n|
|
||||||
|
if req.dependency.match? n
|
||||||
|
res << Gem::DependencyResolver::IndexSpecification.new(
|
||||||
|
self, n.name, n.version, uri, n.platform)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
res
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Called from IndexSpecification to get a true Specification
|
||||||
|
# object.
|
||||||
|
|
||||||
|
def load_spec name, ver, source
|
||||||
|
key = "#{name}-#{ver}"
|
||||||
|
@specs[key] ||= source.fetch_spec(Gem::NameTuple.new(name, ver))
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# No prefetching needed since we load the whole index in
|
||||||
|
# initially.
|
||||||
|
|
||||||
|
def prefetch gems
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
53
lib/rubygems/dependency_resolver/index_specification.rb
Normal file
53
lib/rubygems/dependency_resolver/index_specification.rb
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
##
|
||||||
|
# Represents a possible Specification object returned
|
||||||
|
# from IndexSet. Used to delay needed to download full
|
||||||
|
# Specification objects when only the +name+ and +version+
|
||||||
|
# are needed.
|
||||||
|
|
||||||
|
class Gem::DependencyResolver::IndexSpecification
|
||||||
|
|
||||||
|
attr_reader :name
|
||||||
|
|
||||||
|
attr_reader :source
|
||||||
|
|
||||||
|
attr_reader :version
|
||||||
|
|
||||||
|
def initialize set, name, version, source, plat
|
||||||
|
@set = set
|
||||||
|
@name = name
|
||||||
|
@version = version
|
||||||
|
@source = source
|
||||||
|
@platform = plat
|
||||||
|
|
||||||
|
@spec = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def dependencies
|
||||||
|
spec.dependencies
|
||||||
|
end
|
||||||
|
|
||||||
|
def full_name
|
||||||
|
"#{@name}-#{@version}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def inspect # :nodoc:
|
||||||
|
'#<%s %s source %s>' % [self.class, full_name, @source]
|
||||||
|
end
|
||||||
|
|
||||||
|
def pretty_print q # :nodoc:
|
||||||
|
q.group 2, '[Index specification', ']' do
|
||||||
|
q.breakable
|
||||||
|
q.text full_name
|
||||||
|
|
||||||
|
q.breakable
|
||||||
|
q.text ' source '
|
||||||
|
q.pp @source
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def spec
|
||||||
|
@spec ||= @set.load_spec(@name, @version, @source)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
38
lib/rubygems/dependency_resolver/installed_specification.rb
Normal file
38
lib/rubygems/dependency_resolver/installed_specification.rb
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
class Gem::DependencyResolver::InstalledSpecification
|
||||||
|
|
||||||
|
attr_reader :spec
|
||||||
|
|
||||||
|
def initialize set, spec, source=nil
|
||||||
|
@set = set
|
||||||
|
@source = source
|
||||||
|
@spec = spec
|
||||||
|
end
|
||||||
|
|
||||||
|
def == other # :nodoc:
|
||||||
|
self.class === other and
|
||||||
|
@set == other.set and
|
||||||
|
@spec == other.spec
|
||||||
|
end
|
||||||
|
|
||||||
|
def dependencies
|
||||||
|
@spec.dependencies
|
||||||
|
end
|
||||||
|
|
||||||
|
def full_name
|
||||||
|
"#{@spec.name}-#{@spec.version}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def name
|
||||||
|
@spec.name
|
||||||
|
end
|
||||||
|
|
||||||
|
def source
|
||||||
|
@source ||= Gem::Source::Installed.new
|
||||||
|
end
|
||||||
|
|
||||||
|
def version
|
||||||
|
@spec.version
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
130
lib/rubygems/dependency_resolver/installer_set.rb
Normal file
130
lib/rubygems/dependency_resolver/installer_set.rb
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
class Gem::DependencyResolver::InstallerSet
|
||||||
|
|
||||||
|
##
|
||||||
|
# List of Gem::Specification objects that must always be installed.
|
||||||
|
|
||||||
|
attr_reader :always_install
|
||||||
|
|
||||||
|
##
|
||||||
|
# Only install gems in the always_install list
|
||||||
|
|
||||||
|
attr_accessor :ignore_dependencies
|
||||||
|
|
||||||
|
##
|
||||||
|
# Do not look in the installed set when finding specifications. This is
|
||||||
|
# used by the --install-dir option to `gem install`
|
||||||
|
|
||||||
|
attr_accessor :ignore_installed
|
||||||
|
|
||||||
|
def initialize domain
|
||||||
|
@domain = domain
|
||||||
|
|
||||||
|
@f = Gem::SpecFetcher.fetcher
|
||||||
|
|
||||||
|
@all = Hash.new { |h,k| h[k] = [] }
|
||||||
|
@always_install = []
|
||||||
|
@ignore_dependencies = false
|
||||||
|
@ignore_installed = false
|
||||||
|
@loaded_remote_specs = []
|
||||||
|
@specs = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Should local gems should be considered?
|
||||||
|
|
||||||
|
def consider_local?
|
||||||
|
@domain == :both or @domain == :local
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Should remote gems should be considered?
|
||||||
|
|
||||||
|
def consider_remote?
|
||||||
|
@domain == :both or @domain == :remote
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Returns an array of IndexSpecification objects matching DependencyRequest
|
||||||
|
# +req+.
|
||||||
|
|
||||||
|
def find_all req
|
||||||
|
res = []
|
||||||
|
|
||||||
|
dep = req.dependency
|
||||||
|
|
||||||
|
return res if @ignore_dependencies and
|
||||||
|
@always_install.none? { |spec| dep.matches_spec? spec }
|
||||||
|
|
||||||
|
name = dep.name
|
||||||
|
|
||||||
|
dep.matching_specs.each do |gemspec|
|
||||||
|
next if @always_install.include? gemspec
|
||||||
|
|
||||||
|
res << Gem::DependencyResolver::InstalledSpecification.new(self, gemspec)
|
||||||
|
end unless @ignore_installed
|
||||||
|
|
||||||
|
if consider_local? then
|
||||||
|
local_source = Gem::Source::Local.new
|
||||||
|
|
||||||
|
if spec = local_source.find_gem(name, dep.requirement) then
|
||||||
|
res << Gem::DependencyResolver::IndexSpecification.new(
|
||||||
|
self, spec.name, spec.version, local_source, spec.platform)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if consider_remote? then
|
||||||
|
load_remote_specs dep
|
||||||
|
|
||||||
|
@all[name].each do |remote_source, n|
|
||||||
|
if dep.match? n then
|
||||||
|
res << Gem::DependencyResolver::IndexSpecification.new(
|
||||||
|
self, n.name, n.version, remote_source, n.platform)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
res
|
||||||
|
end
|
||||||
|
|
||||||
|
def inspect # :nodoc:
|
||||||
|
'#<%s domain: %s specs: %p>' % [ self.class, @domain, @specs.keys ]
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Loads remote prerelease specs if +dep+ is a prerelease dependency
|
||||||
|
|
||||||
|
def load_remote_specs dep
|
||||||
|
types = [:released]
|
||||||
|
types << :prerelease if dep.prerelease?
|
||||||
|
|
||||||
|
types.each do |type|
|
||||||
|
next if @loaded_remote_specs.include? type
|
||||||
|
@loaded_remote_specs << type
|
||||||
|
|
||||||
|
list, = @f.available_specs type
|
||||||
|
|
||||||
|
list.each do |uri, specs|
|
||||||
|
specs.each do |n|
|
||||||
|
@all[n.name] << [uri, n]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Called from IndexSpecification to get a true Specification
|
||||||
|
# object.
|
||||||
|
|
||||||
|
def load_spec name, ver, source
|
||||||
|
key = "#{name}-#{ver}"
|
||||||
|
@specs[key] ||= source.fetch_spec Gem::NameTuple.new name, ver
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# No prefetching needed since we load the whole index in initially.
|
||||||
|
|
||||||
|
def prefetch(reqs)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
|
@ -16,6 +16,28 @@ class Gem::DependencyError < Gem::Exception; end
|
||||||
|
|
||||||
class Gem::DependencyRemovalException < Gem::Exception; end
|
class Gem::DependencyRemovalException < Gem::Exception; end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Raised by Gem::DependencyResolver when a Gem::DependencyConflict reaches the
|
||||||
|
# toplevel. Indicates which dependencies were incompatible through #conflict
|
||||||
|
# and #conflicting_dependencies
|
||||||
|
|
||||||
|
class Gem::DependencyResolutionError < Gem::Exception
|
||||||
|
|
||||||
|
attr_reader :conflict
|
||||||
|
|
||||||
|
def initialize conflict
|
||||||
|
@conflict = conflict
|
||||||
|
a, b = conflicting_dependencies
|
||||||
|
|
||||||
|
super "unable to resolve conflicting dependencies '#{a}' and '#{b}'"
|
||||||
|
end
|
||||||
|
|
||||||
|
def conflicting_dependencies
|
||||||
|
@conflict.conflicting_dependencies
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# Raised when attempting to uninstall a gem that isn't in GEM_HOME.
|
# Raised when attempting to uninstall a gem that isn't in GEM_HOME.
|
||||||
|
|
||||||
|
@ -65,6 +87,42 @@ class Gem::SpecificGemNotFoundException < Gem::GemNotFoundException
|
||||||
attr_reader :name, :version, :errors
|
attr_reader :name, :version, :errors
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Raised by Gem::DependencyResolver when dependencies conflict and create the
|
||||||
|
# inability to find a valid possible spec for a request.
|
||||||
|
|
||||||
|
class Gem::ImpossibleDependenciesError < Gem::Exception
|
||||||
|
|
||||||
|
attr_reader :conflicts
|
||||||
|
attr_reader :request
|
||||||
|
|
||||||
|
def initialize request, conflicts
|
||||||
|
@request = request
|
||||||
|
@conflicts = conflicts
|
||||||
|
|
||||||
|
super build_message
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_message # :nodoc:
|
||||||
|
requester = @request.requester
|
||||||
|
requester = requester ? requester.spec.full_name : 'The user'
|
||||||
|
dependency = @request.dependency
|
||||||
|
|
||||||
|
message = "#{requester} requires #{dependency} but it conflicted:\n"
|
||||||
|
|
||||||
|
@conflicts.each do |_, conflict|
|
||||||
|
message << conflict.explanation
|
||||||
|
end
|
||||||
|
|
||||||
|
message
|
||||||
|
end
|
||||||
|
|
||||||
|
def dependency
|
||||||
|
@request.dependency
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
class Gem::InstallError < Gem::Exception; end
|
class Gem::InstallError < Gem::Exception; end
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -107,3 +165,26 @@ class Gem::SystemExitException < SystemExit
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Raised by DependencyResolver when a dependency requests a gem for which
|
||||||
|
# there is no spec.
|
||||||
|
|
||||||
|
class Gem::UnsatisfiableDependencyError < Gem::Exception
|
||||||
|
|
||||||
|
attr_reader :dependency
|
||||||
|
|
||||||
|
def initialize dep
|
||||||
|
requester = dep.requester ? dep.requester.request : '(unknown)'
|
||||||
|
|
||||||
|
super "Unable to resolve dependency: #{requester} requires #{dep}"
|
||||||
|
|
||||||
|
@dependency = dep
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Backwards compatible typo'd exception class for early RubyGems 2.0.x
|
||||||
|
|
||||||
|
Gem::UnsatisfiableDepedencyError = Gem::UnsatisfiableDependencyError # :nodoc:
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,7 @@ class Gem::Ext::Builder
|
||||||
|
|
||||||
# try to find make program from Ruby configure arguments first
|
# try to find make program from Ruby configure arguments first
|
||||||
RbConfig::CONFIG['configure_args'] =~ /with-make-prog\=(\w+)/
|
RbConfig::CONFIG['configure_args'] =~ /with-make-prog\=(\w+)/
|
||||||
make_program = $1 || ENV['MAKE'] || ENV['make']
|
make_program = ENV['MAKE'] || ENV['make'] || $1
|
||||||
unless make_program then
|
unless make_program then
|
||||||
make_program = (/mswin/ =~ RUBY_PLATFORM) ? 'nmake' : 'make'
|
make_program = (/mswin/ =~ RUBY_PLATFORM) ? 'nmake' : 'make'
|
||||||
end
|
end
|
||||||
|
|
|
@ -33,17 +33,11 @@ class Gem::GemRunner
|
||||||
##
|
##
|
||||||
# Run the gem command with the following arguments.
|
# Run the gem command with the following arguments.
|
||||||
|
|
||||||
def run(args)
|
def run args
|
||||||
if args.include?('--')
|
build_args = extract_build_args args
|
||||||
# We need to preserve the original ARGV to use for passing gem options
|
|
||||||
# to source gems. If there is a -- in the line, strip all options after
|
|
||||||
# it...its for the source building process.
|
|
||||||
# TODO use slice!
|
|
||||||
build_args = args[args.index("--") + 1...args.length]
|
|
||||||
args = args[0...args.index("--")]
|
|
||||||
end
|
|
||||||
|
|
||||||
do_configuration args
|
do_configuration args
|
||||||
|
|
||||||
cmd = @command_manager_class.instance
|
cmd = @command_manager_class.instance
|
||||||
|
|
||||||
cmd.command_names.each do |command_name|
|
cmd.command_names.each do |command_name|
|
||||||
|
@ -60,6 +54,20 @@ class Gem::GemRunner
|
||||||
cmd.run Gem.configuration.args, build_args
|
cmd.run Gem.configuration.args, build_args
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Separates the build arguments (those following <code>--</code>) from the
|
||||||
|
# other arguments in the list.
|
||||||
|
|
||||||
|
def extract_build_args args # :nodoc:
|
||||||
|
return [] unless offset = args.index('--')
|
||||||
|
|
||||||
|
build_args = args.slice!(offset...args.length)
|
||||||
|
|
||||||
|
build_args.shift
|
||||||
|
|
||||||
|
build_args
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def do_configuration(args)
|
def do_configuration(args)
|
||||||
|
|
|
@ -1,11 +1,17 @@
|
||||||
require 'rubygems/remote_fetcher'
|
require 'rubygems/remote_fetcher'
|
||||||
|
|
||||||
|
##
|
||||||
|
# Utility methods for using the RubyGems API.
|
||||||
|
|
||||||
module Gem::GemcutterUtilities
|
module Gem::GemcutterUtilities
|
||||||
|
|
||||||
# TODO: move to Gem::Command
|
# TODO: move to Gem::Command
|
||||||
OptionParser.accept Symbol do |value|
|
OptionParser.accept Symbol do |value|
|
||||||
value.to_sym
|
value.to_sym
|
||||||
end
|
end
|
||||||
|
|
||||||
|
attr_writer :host
|
||||||
|
|
||||||
##
|
##
|
||||||
# Add the --key option
|
# Add the --key option
|
||||||
|
|
||||||
|
@ -17,6 +23,9 @@ module Gem::GemcutterUtilities
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# The API key from the command options or from the user's configuration.
|
||||||
|
|
||||||
def api_key
|
def api_key
|
||||||
if options[:key] then
|
if options[:key] then
|
||||||
verify_api_key options[:key]
|
verify_api_key options[:key]
|
||||||
|
@ -27,6 +36,47 @@ module Gem::GemcutterUtilities
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# The host to connect to either from the RUBYGEMS_HOST environment variable
|
||||||
|
# or from the user's configuration
|
||||||
|
|
||||||
|
def host
|
||||||
|
configured_host = Gem.host unless
|
||||||
|
Gem.configuration.disable_default_gem_server
|
||||||
|
|
||||||
|
@host ||=
|
||||||
|
begin
|
||||||
|
env_rubygems_host = ENV['RUBYGEMS_HOST']
|
||||||
|
env_rubygems_host = nil if
|
||||||
|
env_rubygems_host and env_rubygems_host.empty?
|
||||||
|
|
||||||
|
env_rubygems_host|| configured_host
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Creates an RubyGems API to +host+ and +path+ with the given HTTP +method+.
|
||||||
|
|
||||||
|
def rubygems_api_request(method, path, host = nil, &block)
|
||||||
|
require 'net/http'
|
||||||
|
|
||||||
|
self.host = host if host
|
||||||
|
unless self.host
|
||||||
|
alert_error "You must specify a gem server"
|
||||||
|
terminate_interaction 1 # TODO: question this
|
||||||
|
end
|
||||||
|
|
||||||
|
uri = URI.parse "#{self.host}/#{path}"
|
||||||
|
|
||||||
|
request_method = Net::HTTP.const_get method.to_s.capitalize
|
||||||
|
|
||||||
|
Gem::RemoteFetcher.fetcher.request(uri, request_method, &block)
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Signs in with the RubyGems API at +sign_in_host+ and sets the rubygems API
|
||||||
|
# key.
|
||||||
|
|
||||||
def sign_in sign_in_host = self.host
|
def sign_in sign_in_host = self.host
|
||||||
return if Gem.configuration.rubygems_api_key
|
return if Gem.configuration.rubygems_api_key
|
||||||
|
|
||||||
|
@ -55,53 +105,9 @@ module Gem::GemcutterUtilities
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
attr_writer :host
|
##
|
||||||
def host
|
# Retrieves the pre-configured API key +key+ or terminates interaction with
|
||||||
configured_host = Gem.host unless
|
# an error.
|
||||||
Gem.configuration.disable_default_gem_server
|
|
||||||
|
|
||||||
@host ||=
|
|
||||||
begin
|
|
||||||
env_rubygems_host = ENV['RUBYGEMS_HOST']
|
|
||||||
env_rubygems_host = nil if
|
|
||||||
env_rubygems_host and env_rubygems_host.empty?
|
|
||||||
|
|
||||||
env_rubygems_host|| configured_host
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def rubygems_api_request(method, path, host = nil, &block)
|
|
||||||
require 'net/http'
|
|
||||||
|
|
||||||
self.host = host if host
|
|
||||||
unless self.host
|
|
||||||
alert_error "You must specify a gem server"
|
|
||||||
terminate_interaction 1 # TODO: question this
|
|
||||||
end
|
|
||||||
|
|
||||||
uri = URI.parse "#{self.host}/#{path}"
|
|
||||||
|
|
||||||
request_method = Net::HTTP.const_get method.to_s.capitalize
|
|
||||||
|
|
||||||
Gem::RemoteFetcher.fetcher.request(uri, request_method, &block)
|
|
||||||
end
|
|
||||||
|
|
||||||
def with_response resp, error_prefix = nil
|
|
||||||
case resp
|
|
||||||
when Net::HTTPSuccess then
|
|
||||||
if block_given? then
|
|
||||||
yield resp
|
|
||||||
else
|
|
||||||
say resp.body
|
|
||||||
end
|
|
||||||
else
|
|
||||||
message = resp.body
|
|
||||||
message = "#{error_prefix}: #{message}" if error_prefix
|
|
||||||
|
|
||||||
say message
|
|
||||||
terminate_interaction 1 # TODO: question this
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def verify_api_key(key)
|
def verify_api_key(key)
|
||||||
if Gem.configuration.api_keys.key? key then
|
if Gem.configuration.api_keys.key? key then
|
||||||
|
@ -112,4 +118,29 @@ module Gem::GemcutterUtilities
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# If +response+ is an HTTP Success (2XX) response, yields the response if a
|
||||||
|
# block was given or shows the response body to the user.
|
||||||
|
#
|
||||||
|
# If the response was not successful, shows an error to the user including
|
||||||
|
# the +error_prefix+ and the response body.
|
||||||
|
|
||||||
|
def with_response response, error_prefix = nil
|
||||||
|
case response
|
||||||
|
when Net::HTTPSuccess then
|
||||||
|
if block_given? then
|
||||||
|
yield response
|
||||||
|
else
|
||||||
|
say response.body
|
||||||
|
end
|
||||||
|
else
|
||||||
|
message = response.body
|
||||||
|
message = "#{error_prefix}: #{message}" if error_prefix
|
||||||
|
|
||||||
|
say message
|
||||||
|
terminate_interaction 1 # TODO: question this
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
12
lib/rubygems/install_default_message.rb
Normal file
12
lib/rubygems/install_default_message.rb
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
require 'rubygems'
|
||||||
|
require 'rubygems/user_interaction'
|
||||||
|
|
||||||
|
##
|
||||||
|
# A post-install hook that displays "Successfully installed
|
||||||
|
# some_gem-1.0 as a default gem"
|
||||||
|
|
||||||
|
Gem.post_install do |installer|
|
||||||
|
ui = Gem::DefaultUserInteraction.ui
|
||||||
|
ui.say "Successfully installed #{installer.spec.full_name} as a default gem"
|
||||||
|
end
|
||||||
|
|
|
@ -26,6 +26,9 @@ module Gem::InstallUpdateOptions
|
||||||
OptionParser.accept Gem::Security::Policy do |value|
|
OptionParser.accept Gem::Security::Policy do |value|
|
||||||
require 'rubygems/security'
|
require 'rubygems/security'
|
||||||
|
|
||||||
|
raise OptionParser::InvalidArgument, 'OpenSSL not installed' unless
|
||||||
|
defined?(Gem::Security::HighSecurity)
|
||||||
|
|
||||||
value = Gem::Security::Policies[value]
|
value = Gem::Security::Policies[value]
|
||||||
valid = Gem::Security::Policies.keys.sort
|
valid = Gem::Security::Policies.keys.sort
|
||||||
message = "#{value} (#{valid.join ', '} are valid)"
|
message = "#{value} (#{valid.join ', '} are valid)"
|
||||||
|
|
|
@ -212,16 +212,21 @@ class Gem::Installer
|
||||||
FileUtils.rm_rf gem_dir
|
FileUtils.rm_rf gem_dir
|
||||||
|
|
||||||
FileUtils.mkdir_p gem_dir
|
FileUtils.mkdir_p gem_dir
|
||||||
|
|
||||||
extract_files
|
if @options[:install_as_default]
|
||||||
|
extract_bin
|
||||||
build_extensions
|
write_default_spec
|
||||||
write_build_info_file
|
else
|
||||||
run_post_build_hooks
|
extract_files
|
||||||
|
|
||||||
generate_bin
|
build_extensions
|
||||||
write_spec
|
write_build_info_file
|
||||||
write_cache_file
|
run_post_build_hooks
|
||||||
|
|
||||||
|
generate_bin
|
||||||
|
write_spec
|
||||||
|
write_cache_file
|
||||||
|
end
|
||||||
|
|
||||||
say spec.post_install_message unless spec.post_install_message.nil?
|
say spec.post_install_message unless spec.post_install_message.nil?
|
||||||
|
|
||||||
|
@ -326,6 +331,14 @@ class Gem::Installer
|
||||||
File.join gem_home, "specifications", "#{spec.full_name}.gemspec"
|
File.join gem_home, "specifications", "#{spec.full_name}.gemspec"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# The location of of the default spec file for default gems.
|
||||||
|
#
|
||||||
|
|
||||||
|
def default_spec_file
|
||||||
|
File.join gem_home, "specifications/default", "#{spec.full_name}.gemspec"
|
||||||
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# Writes the .gemspec specification (in Ruby) to the gem home's
|
# Writes the .gemspec specification (in Ruby) to the gem home's
|
||||||
# specifications directory.
|
# specifications directory.
|
||||||
|
@ -336,6 +349,16 @@ class Gem::Installer
|
||||||
file.fsync rescue nil # for filesystems without fsync(2)
|
file.fsync rescue nil # for filesystems without fsync(2)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Writes the full .gemspec specification (in Ruby) to the gem home's
|
||||||
|
# specifications/default directory.
|
||||||
|
|
||||||
|
def write_default_spec
|
||||||
|
File.open(default_spec_file, "w") do |file|
|
||||||
|
file.puts spec.to_ruby
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# Creates windows .bat files for easy running of commands
|
# Creates windows .bat files for easy running of commands
|
||||||
|
@ -538,13 +561,13 @@ class Gem::Installer
|
||||||
:bin_dir => nil,
|
:bin_dir => nil,
|
||||||
:env_shebang => false,
|
:env_shebang => false,
|
||||||
:force => false,
|
:force => false,
|
||||||
:install_dir => Gem.dir,
|
|
||||||
:only_install_dir => false
|
:only_install_dir => false
|
||||||
}.merge options
|
}.merge options
|
||||||
|
|
||||||
@env_shebang = options[:env_shebang]
|
@env_shebang = options[:env_shebang]
|
||||||
@force = options[:force]
|
@force = options[:force]
|
||||||
@gem_home = options[:install_dir]
|
@install_dir = options[:install_dir]
|
||||||
|
@gem_home = options[:install_dir] || Gem.dir
|
||||||
@ignore_dependencies = options[:ignore_dependencies]
|
@ignore_dependencies = options[:ignore_dependencies]
|
||||||
@format_executable = options[:format_executable]
|
@format_executable = options[:format_executable]
|
||||||
@security_policy = options[:security_policy]
|
@security_policy = options[:security_policy]
|
||||||
|
@ -715,6 +738,15 @@ EOF
|
||||||
def extract_files
|
def extract_files
|
||||||
@package.extract_files gem_dir
|
@package.extract_files gem_dir
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Extracts only the bin/ files from the gem into the gem directory.
|
||||||
|
# This is used by default gems to allow a gem-aware stub to function
|
||||||
|
# without the full gem installed.
|
||||||
|
|
||||||
|
def extract_bin
|
||||||
|
@package.extract_files gem_dir, "bin/*"
|
||||||
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# Prefix and suffix the program filename the same as ruby.
|
# Prefix and suffix the program filename the same as ruby.
|
||||||
|
@ -756,7 +788,11 @@ EOF
|
||||||
|
|
||||||
ensure_loadable_spec
|
ensure_loadable_spec
|
||||||
|
|
||||||
Gem.ensure_gem_subdirectories gem_home
|
if options[:install_as_default]
|
||||||
|
Gem.ensure_default_gem_subdirectories gem_home
|
||||||
|
else
|
||||||
|
Gem.ensure_gem_subdirectories gem_home
|
||||||
|
end
|
||||||
|
|
||||||
return true if @force
|
return true if @force
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,20 @@ class Gem::NameTuple
|
||||||
new nil, Gem::Version.new(0), nil
|
new nil, Gem::Version.new(0), nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Returns the full name (name-version) of this Gem. Platform information is
|
||||||
|
# included if it is not the default Ruby platform. This mimics the behavior
|
||||||
|
# of Gem::Specification#full_name.
|
||||||
|
|
||||||
|
def full_name
|
||||||
|
case @platform
|
||||||
|
when nil, 'ruby', ''
|
||||||
|
"#{@name}-#{@version}"
|
||||||
|
else
|
||||||
|
"#{@name}-#{@version}-#{@platform}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# Indicate if this NameTuple matches the current platform.
|
# Indicate if this NameTuple matches the current platform.
|
||||||
|
|
||||||
|
@ -59,12 +73,7 @@ class Gem::NameTuple
|
||||||
# Return the name that the gemspec file would be
|
# Return the name that the gemspec file would be
|
||||||
|
|
||||||
def spec_name
|
def spec_name
|
||||||
case @platform
|
"#{full_name}.gemspec"
|
||||||
when nil, 'ruby', ''
|
|
||||||
"#{@name}-#{@version}.gemspec"
|
|
||||||
else
|
|
||||||
"#{@name}-#{@version}-#{@platform}.gemspec"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -74,10 +83,12 @@ class Gem::NameTuple
|
||||||
[@name, @version, @platform]
|
[@name, @version, @platform]
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_s
|
def inspect # :nodoc:
|
||||||
"#<Gem::NameTuple #{@name}, #{@version}, #{@platform}>"
|
"#<Gem::NameTuple #{@name}, #{@version}, #{@platform}>"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
alias to_s inspect # :nodoc:
|
||||||
|
|
||||||
def <=> other
|
def <=> other
|
||||||
to_a <=> other.to_a
|
to_a <=> other.to_a
|
||||||
end
|
end
|
||||||
|
|
|
@ -280,11 +280,16 @@ EOM
|
||||||
algorithms = if @checksums then
|
algorithms = if @checksums then
|
||||||
@checksums.keys
|
@checksums.keys
|
||||||
else
|
else
|
||||||
[Gem::Security::DIGEST_NAME]
|
[Gem::Security::DIGEST_NAME].compact
|
||||||
end
|
end
|
||||||
|
|
||||||
algorithms.each do |algorithm|
|
algorithms.each do |algorithm|
|
||||||
digester = OpenSSL::Digest.new algorithm
|
digester =
|
||||||
|
if defined?(OpenSSL::Digest) then
|
||||||
|
OpenSSL::Digest.new algorithm
|
||||||
|
else
|
||||||
|
Digest.const_get(algorithm).new
|
||||||
|
end
|
||||||
|
|
||||||
digester << entry.read(16384) until entry.eof?
|
digester << entry.read(16384) until entry.eof?
|
||||||
|
|
||||||
|
@ -298,8 +303,11 @@ EOM
|
||||||
|
|
||||||
##
|
##
|
||||||
# Extracts the files in this package into +destination_dir+
|
# Extracts the files in this package into +destination_dir+
|
||||||
|
#
|
||||||
|
# If +pattern+ is specified, only entries matching that glob will be
|
||||||
|
# extracted.
|
||||||
|
|
||||||
def extract_files destination_dir
|
def extract_files destination_dir, pattern = "*"
|
||||||
verify unless @spec
|
verify unless @spec
|
||||||
|
|
||||||
FileUtils.mkdir_p destination_dir
|
FileUtils.mkdir_p destination_dir
|
||||||
|
@ -310,7 +318,7 @@ EOM
|
||||||
reader.each do |entry|
|
reader.each do |entry|
|
||||||
next unless entry.full_name == 'data.tar.gz'
|
next unless entry.full_name == 'data.tar.gz'
|
||||||
|
|
||||||
extract_tar_gz entry, destination_dir
|
extract_tar_gz entry, destination_dir, pattern
|
||||||
|
|
||||||
return # ignore further entries
|
return # ignore further entries
|
||||||
end
|
end
|
||||||
|
@ -324,10 +332,15 @@ EOM
|
||||||
# If an entry in the archive contains a relative path above
|
# If an entry in the archive contains a relative path above
|
||||||
# +destination_dir+ or an absolute path is encountered an exception is
|
# +destination_dir+ or an absolute path is encountered an exception is
|
||||||
# raised.
|
# raised.
|
||||||
|
#
|
||||||
|
# If +pattern+ is specified, only entries matching that glob will be
|
||||||
|
# extracted.
|
||||||
|
|
||||||
def extract_tar_gz io, destination_dir # :nodoc:
|
def extract_tar_gz io, destination_dir, pattern = "*" # :nodoc:
|
||||||
open_tar_gz io do |tar|
|
open_tar_gz io do |tar|
|
||||||
tar.each do |entry|
|
tar.each do |entry|
|
||||||
|
next unless File.fnmatch pattern, entry.full_name
|
||||||
|
|
||||||
destination = install_location entry.full_name, destination_dir
|
destination = install_location entry.full_name, destination_dir
|
||||||
|
|
||||||
FileUtils.rm_rf destination
|
FileUtils.rm_rf destination
|
||||||
|
@ -428,12 +441,13 @@ EOM
|
||||||
# certificate and key are not present only checksum generation is set up.
|
# certificate and key are not present only checksum generation is set up.
|
||||||
|
|
||||||
def setup_signer
|
def setup_signer
|
||||||
|
passphrase = ENV['GEM_PRIVATE_KEY_PASSPHRASE']
|
||||||
if @spec.signing_key then
|
if @spec.signing_key then
|
||||||
@signer = Gem::Security::Signer.new @spec.signing_key, @spec.cert_chain
|
@signer = Gem::Security::Signer.new @spec.signing_key, @spec.cert_chain, passphrase
|
||||||
@spec.signing_key = nil
|
@spec.signing_key = nil
|
||||||
@spec.cert_chain = @signer.cert_chain.map { |cert| cert.to_s }
|
@spec.cert_chain = @signer.cert_chain.map { |cert| cert.to_s }
|
||||||
else
|
else
|
||||||
@signer = Gem::Security::Signer.new nil, nil
|
@signer = Gem::Security::Signer.new nil, nil, passphrase
|
||||||
@spec.cert_chain = @signer.cert_chain.map { |cert| cert.to_pem } if
|
@spec.cert_chain = @signer.cert_chain.map { |cert| cert.to_pem } if
|
||||||
@signer.cert_chain
|
@signer.cert_chain
|
||||||
end
|
end
|
||||||
|
@ -509,28 +523,39 @@ EOM
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Verifies +entry+ in a .gem file.
|
||||||
|
|
||||||
|
def verify_entry entry
|
||||||
|
file_name = entry.full_name
|
||||||
|
@files << file_name
|
||||||
|
|
||||||
|
case file_name
|
||||||
|
when /\.sig$/ then
|
||||||
|
@signatures[$`] = entry.read if @security_policy
|
||||||
|
return
|
||||||
|
else
|
||||||
|
digest entry
|
||||||
|
end
|
||||||
|
|
||||||
|
case file_name
|
||||||
|
when /^metadata(.gz)?$/ then
|
||||||
|
load_spec entry
|
||||||
|
when 'data.tar.gz' then
|
||||||
|
verify_gz entry
|
||||||
|
end
|
||||||
|
rescue => e
|
||||||
|
message = "package is corrupt, exception while verifying: " +
|
||||||
|
"#{e.message} (#{e.class})"
|
||||||
|
raise Gem::Package::FormatError.new message, @gem
|
||||||
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# Verifies the files of the +gem+
|
# Verifies the files of the +gem+
|
||||||
|
|
||||||
def verify_files gem
|
def verify_files gem
|
||||||
gem.each do |entry|
|
gem.each do |entry|
|
||||||
file_name = entry.full_name
|
verify_entry entry
|
||||||
@files << file_name
|
|
||||||
|
|
||||||
case file_name
|
|
||||||
when /\.sig$/ then
|
|
||||||
@signatures[$`] = entry.read if @security_policy
|
|
||||||
next
|
|
||||||
else
|
|
||||||
digest entry
|
|
||||||
end
|
|
||||||
|
|
||||||
case file_name
|
|
||||||
when /^metadata(.gz)?$/ then
|
|
||||||
load_spec entry
|
|
||||||
when 'data.tar.gz' then
|
|
||||||
verify_gz entry
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
unless @spec then
|
unless @spec then
|
||||||
|
|
|
@ -71,7 +71,7 @@ class Gem::Package::TarTestCase < Gem::TestCase
|
||||||
SP(Z(to_oct(sum, 6)))
|
SP(Z(to_oct(sum, 6)))
|
||||||
end
|
end
|
||||||
|
|
||||||
def header(type, fname, dname, length, mode, checksum = nil)
|
def header(type, fname, dname, length, mode, mtime, checksum = nil)
|
||||||
checksum ||= " " * 8
|
checksum ||= " " * 8
|
||||||
|
|
||||||
arr = [ # struct tarfile_entry_posix
|
arr = [ # struct tarfile_entry_posix
|
||||||
|
@ -80,7 +80,7 @@ class Gem::Package::TarTestCase < Gem::TestCase
|
||||||
Z(to_oct(0, 7)), # char uid[8]; ditto
|
Z(to_oct(0, 7)), # char uid[8]; ditto
|
||||||
Z(to_oct(0, 7)), # char gid[8]; ditto
|
Z(to_oct(0, 7)), # char gid[8]; ditto
|
||||||
Z(to_oct(length, 11)), # char size[12]; 0 padded, octal, null
|
Z(to_oct(length, 11)), # char size[12]; 0 padded, octal, null
|
||||||
Z(to_oct(0, 11)), # char mtime[12]; 0 padded, octal, null
|
Z(to_oct(mtime, 11)), # char mtime[12]; 0 padded, octal, null
|
||||||
checksum, # char checksum[8]; 0 padded, octal, null, space
|
checksum, # char checksum[8]; 0 padded, octal, null, space
|
||||||
type, # char typeflag[1]; file: "0" dir: "5"
|
type, # char typeflag[1]; file: "0" dir: "5"
|
||||||
"\0" * 100, # char linkname[100]; ASCII + (Z unless filled)
|
"\0" * 100, # char linkname[100]; ASCII + (Z unless filled)
|
||||||
|
@ -105,16 +105,16 @@ class Gem::Package::TarTestCase < Gem::TestCase
|
||||||
ret
|
ret
|
||||||
end
|
end
|
||||||
|
|
||||||
def tar_dir_header(name, prefix, mode)
|
def tar_dir_header(name, prefix, mode, mtime)
|
||||||
h = header("5", name, prefix, 0, mode)
|
h = header("5", name, prefix, 0, mode, mtime)
|
||||||
checksum = calc_checksum(h)
|
checksum = calc_checksum(h)
|
||||||
header("5", name, prefix, 0, mode, checksum)
|
header("5", name, prefix, 0, mode, mtime, checksum)
|
||||||
end
|
end
|
||||||
|
|
||||||
def tar_file_header(fname, dname, mode, length)
|
def tar_file_header(fname, dname, mode, length, mtime)
|
||||||
h = header("0", fname, dname, length, mode)
|
h = header("0", fname, dname, length, mode, mtime)
|
||||||
checksum = calc_checksum(h)
|
checksum = calc_checksum(h)
|
||||||
header("0", fname, dname, length, mode, checksum)
|
header("0", fname, dname, length, mode, mtime, checksum)
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_oct(n, pad_size)
|
def to_oct(n, pad_size)
|
||||||
|
@ -130,7 +130,7 @@ class Gem::Package::TarTestCase < Gem::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
def util_dir_entry
|
def util_dir_entry
|
||||||
util_entry tar_dir_header("foo", "bar", 0)
|
util_entry tar_dir_header("foo", "bar", 0, Time.now)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
# See LICENSE.txt for additional licensing information.
|
# See LICENSE.txt for additional licensing information.
|
||||||
#++
|
#++
|
||||||
|
|
||||||
|
require 'digest'
|
||||||
|
|
||||||
##
|
##
|
||||||
# Allows writing of tar files
|
# Allows writing of tar files
|
||||||
|
|
||||||
|
@ -121,7 +123,8 @@ class Gem::Package::TarWriter
|
||||||
@io.pos = init_pos
|
@io.pos = init_pos
|
||||||
|
|
||||||
header = Gem::Package::TarHeader.new :name => name, :mode => mode,
|
header = Gem::Package::TarHeader.new :name => name, :mode => mode,
|
||||||
:size => size, :prefix => prefix
|
:size => size, :prefix => prefix,
|
||||||
|
:mtime => Time.now
|
||||||
|
|
||||||
@io.write header
|
@io.write header
|
||||||
@io.pos = final_pos
|
@io.pos = final_pos
|
||||||
|
@ -140,7 +143,15 @@ class Gem::Package::TarWriter
|
||||||
def add_file_digest name, mode, digest_algorithms # :yields: io
|
def add_file_digest name, mode, digest_algorithms # :yields: io
|
||||||
digests = digest_algorithms.map do |digest_algorithm|
|
digests = digest_algorithms.map do |digest_algorithm|
|
||||||
digest = digest_algorithm.new
|
digest = digest_algorithm.new
|
||||||
[digest.name, digest]
|
digest_name =
|
||||||
|
if digest.respond_to? :name then
|
||||||
|
digest.name
|
||||||
|
else
|
||||||
|
/::([^:]+)$/ =~ digest_algorithm.name
|
||||||
|
$1
|
||||||
|
end
|
||||||
|
|
||||||
|
[digest_name, digest]
|
||||||
end
|
end
|
||||||
|
|
||||||
digests = Hash[*digests.flatten]
|
digests = Hash[*digests.flatten]
|
||||||
|
@ -165,22 +176,32 @@ class Gem::Package::TarWriter
|
||||||
def add_file_signed name, mode, signer
|
def add_file_signed name, mode, signer
|
||||||
digest_algorithms = [
|
digest_algorithms = [
|
||||||
signer.digest_algorithm,
|
signer.digest_algorithm,
|
||||||
OpenSSL::Digest::SHA512,
|
Digest::SHA512,
|
||||||
].uniq
|
].compact.uniq
|
||||||
|
|
||||||
digests = add_file_digest name, mode, digest_algorithms do |io|
|
digests = add_file_digest name, mode, digest_algorithms do |io|
|
||||||
yield io
|
yield io
|
||||||
end
|
end
|
||||||
|
|
||||||
signature_digest = digests.values.find do |digest|
|
signature_digest = digests.values.compact.find do |digest|
|
||||||
digest.name == signer.digest_name
|
digest_name =
|
||||||
|
if digest.respond_to? :name then
|
||||||
|
digest.name
|
||||||
|
else
|
||||||
|
/::([^:]+)$/ =~ digest.class.name
|
||||||
|
$1
|
||||||
|
end
|
||||||
|
|
||||||
|
digest_name == signer.digest_name
|
||||||
end
|
end
|
||||||
|
|
||||||
signature = signer.sign signature_digest.digest
|
if signer.key then
|
||||||
|
signature = signer.sign signature_digest.digest
|
||||||
|
|
||||||
add_file_simple "#{name}.sig", 0444, signature.length do |io|
|
add_file_simple "#{name}.sig", 0444, signature.length do |io|
|
||||||
io.write signature
|
io.write signature
|
||||||
end if signature
|
end
|
||||||
|
end
|
||||||
|
|
||||||
digests
|
digests
|
||||||
end
|
end
|
||||||
|
@ -195,7 +216,8 @@ class Gem::Package::TarWriter
|
||||||
name, prefix = split_name name
|
name, prefix = split_name name
|
||||||
|
|
||||||
header = Gem::Package::TarHeader.new(:name => name, :mode => mode,
|
header = Gem::Package::TarHeader.new(:name => name, :mode => mode,
|
||||||
:size => size, :prefix => prefix).to_s
|
:size => size, :prefix => prefix,
|
||||||
|
:mtime => Time.now).to_s
|
||||||
|
|
||||||
@io.write header
|
@io.write header
|
||||||
os = BoundedStream.new @io, size
|
os = BoundedStream.new @io, size
|
||||||
|
@ -256,7 +278,8 @@ class Gem::Package::TarWriter
|
||||||
|
|
||||||
header = Gem::Package::TarHeader.new :name => name, :mode => mode,
|
header = Gem::Package::TarHeader.new :name => name, :mode => mode,
|
||||||
:typeflag => "5", :size => 0,
|
:typeflag => "5", :size => 0,
|
||||||
:prefix => prefix
|
:prefix => prefix,
|
||||||
|
:mtime => Time.now
|
||||||
|
|
||||||
@io.write header
|
@io.write header
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,10 @@ class Gem::PathSupport
|
||||||
# Array of paths to search for Gems.
|
# Array of paths to search for Gems.
|
||||||
attr_reader :path
|
attr_reader :path
|
||||||
|
|
||||||
|
##
|
||||||
|
# Directory with spec cache
|
||||||
|
attr_reader :spec_cache_dir # :nodoc:
|
||||||
|
|
||||||
##
|
##
|
||||||
#
|
#
|
||||||
# Constructor. Takes a single argument which is to be treated like a
|
# Constructor. Takes a single argument which is to be treated like a
|
||||||
|
@ -28,6 +32,10 @@ class Gem::PathSupport
|
||||||
end
|
end
|
||||||
|
|
||||||
self.path = env["GEM_PATH"] || ENV["GEM_PATH"]
|
self.path = env["GEM_PATH"] || ENV["GEM_PATH"]
|
||||||
|
|
||||||
|
@spec_cache_dir =
|
||||||
|
env["GEM_SPEC_CACHE"] || ENV["GEM_SPEC_CACHE"] ||
|
||||||
|
Gem.default_spec_cache_dir
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
@ -2,6 +2,8 @@ require "rubygems/deprecate"
|
||||||
|
|
||||||
##
|
##
|
||||||
# Available list of platforms for targeting Gem installations.
|
# Available list of platforms for targeting Gem installations.
|
||||||
|
#
|
||||||
|
# See `gem help platform` for information on platform matching.
|
||||||
|
|
||||||
class Gem::Platform
|
class Gem::Platform
|
||||||
|
|
||||||
|
@ -129,12 +131,16 @@ class Gem::Platform
|
||||||
# Does +other+ match this platform? Two platforms match if they have the
|
# Does +other+ match this platform? Two platforms match if they have the
|
||||||
# same CPU, or either has a CPU of 'universal', they have the same OS, and
|
# same CPU, or either has a CPU of 'universal', they have the same OS, and
|
||||||
# they have the same version, or either has no version.
|
# they have the same version, or either has no version.
|
||||||
|
#
|
||||||
|
# Additionally, the platform will match if the local CPU is 'arm' and the
|
||||||
|
# other CPU starts with "arm" (for generic ARM family support).
|
||||||
|
|
||||||
def ===(other)
|
def ===(other)
|
||||||
return nil unless Gem::Platform === other
|
return nil unless Gem::Platform === other
|
||||||
|
|
||||||
# cpu
|
# cpu
|
||||||
(@cpu == 'universal' or other.cpu == 'universal' or @cpu == other.cpu) and
|
(@cpu == 'universal' or other.cpu == 'universal' or @cpu == other.cpu or
|
||||||
|
(@cpu == 'arm' and other.cpu =~ /\Aarm/)) and
|
||||||
|
|
||||||
# os
|
# os
|
||||||
@os == other.os and
|
@os == other.os and
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
require 'rubygems'
|
require 'rubygems'
|
||||||
|
require 'rubygems/request'
|
||||||
|
require 'rubygems/uri_formatter'
|
||||||
require 'rubygems/user_interaction'
|
require 'rubygems/user_interaction'
|
||||||
require 'uri'
|
|
||||||
require 'resolv'
|
require 'resolv'
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -71,17 +72,7 @@ class Gem::RemoteFetcher
|
||||||
|
|
||||||
Socket.do_not_reverse_lookup = true
|
Socket.do_not_reverse_lookup = true
|
||||||
|
|
||||||
@connections = {}
|
@proxy = proxy
|
||||||
@requests = Hash.new 0
|
|
||||||
@proxy_uri =
|
|
||||||
case proxy
|
|
||||||
when :no_proxy then nil
|
|
||||||
when nil then get_proxy_from_env
|
|
||||||
when URI::HTTP then proxy
|
|
||||||
else URI.parse(proxy)
|
|
||||||
end
|
|
||||||
@user_agent = user_agent
|
|
||||||
@env_no_proxy = get_no_proxy_from_env
|
|
||||||
|
|
||||||
@dns = dns
|
@dns = dns
|
||||||
end
|
end
|
||||||
|
@ -200,7 +191,7 @@ class Gem::RemoteFetcher
|
||||||
source_uri.path
|
source_uri.path
|
||||||
end
|
end
|
||||||
|
|
||||||
source_path = unescape source_path
|
source_path = Gem::UriFormatter.new(source_path).unescape
|
||||||
|
|
||||||
begin
|
begin
|
||||||
FileUtils.cp source_path, local_gem_path unless
|
FileUtils.cp source_path, local_gem_path unless
|
||||||
|
@ -319,125 +310,6 @@ class Gem::RemoteFetcher
|
||||||
response['content-length'].to_i
|
response['content-length'].to_i
|
||||||
end
|
end
|
||||||
|
|
||||||
def escape(str)
|
|
||||||
return unless str
|
|
||||||
@uri_parser ||= uri_escaper
|
|
||||||
@uri_parser.escape str
|
|
||||||
end
|
|
||||||
|
|
||||||
def unescape(str)
|
|
||||||
return unless str
|
|
||||||
@uri_parser ||= uri_escaper
|
|
||||||
@uri_parser.unescape str
|
|
||||||
end
|
|
||||||
|
|
||||||
def uri_escaper
|
|
||||||
URI::Parser.new
|
|
||||||
rescue NameError
|
|
||||||
URI
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# Returns list of no_proxy entries (if any) from the environment
|
|
||||||
|
|
||||||
def get_no_proxy_from_env
|
|
||||||
env_no_proxy = ENV['no_proxy'] || ENV['NO_PROXY']
|
|
||||||
|
|
||||||
return [] if env_no_proxy.nil? or env_no_proxy.empty?
|
|
||||||
|
|
||||||
env_no_proxy.split(/\s*,\s*/)
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# Returns an HTTP proxy URI if one is set in the environment variables.
|
|
||||||
|
|
||||||
def get_proxy_from_env
|
|
||||||
env_proxy = ENV['http_proxy'] || ENV['HTTP_PROXY']
|
|
||||||
|
|
||||||
return nil if env_proxy.nil? or env_proxy.empty?
|
|
||||||
|
|
||||||
uri = URI.parse(normalize_uri(env_proxy))
|
|
||||||
|
|
||||||
if uri and uri.user.nil? and uri.password.nil? then
|
|
||||||
# Probably we have http_proxy_* variables?
|
|
||||||
uri.user = escape(ENV['http_proxy_user'] || ENV['HTTP_PROXY_USER'])
|
|
||||||
uri.password = escape(ENV['http_proxy_pass'] || ENV['HTTP_PROXY_PASS'])
|
|
||||||
end
|
|
||||||
|
|
||||||
uri
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# Normalize the URI by adding "http://" if it is missing.
|
|
||||||
|
|
||||||
def normalize_uri(uri)
|
|
||||||
(uri =~ /^(https?|ftp|file):/i) ? uri : "http://#{uri}"
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# Creates or an HTTP connection based on +uri+, or retrieves an existing
|
|
||||||
# connection, using a proxy if needed.
|
|
||||||
|
|
||||||
def connection_for(uri)
|
|
||||||
net_http_args = [uri.host, uri.port]
|
|
||||||
|
|
||||||
if @proxy_uri and not no_proxy?(uri.host) then
|
|
||||||
net_http_args += [
|
|
||||||
@proxy_uri.host,
|
|
||||||
@proxy_uri.port,
|
|
||||||
@proxy_uri.user,
|
|
||||||
@proxy_uri.password
|
|
||||||
]
|
|
||||||
end
|
|
||||||
|
|
||||||
connection_id = [Thread.current.object_id, *net_http_args].join ':'
|
|
||||||
@connections[connection_id] ||= Net::HTTP.new(*net_http_args)
|
|
||||||
connection = @connections[connection_id]
|
|
||||||
|
|
||||||
if https?(uri) and not connection.started? then
|
|
||||||
configure_connection_for_https(connection)
|
|
||||||
end
|
|
||||||
|
|
||||||
connection.start unless connection.started?
|
|
||||||
|
|
||||||
connection
|
|
||||||
rescue defined?(OpenSSL::SSL) ? OpenSSL::SSL::SSLError : Errno::EHOSTDOWN,
|
|
||||||
Errno::EHOSTDOWN => e
|
|
||||||
raise FetchError.new(e.message, uri)
|
|
||||||
end
|
|
||||||
|
|
||||||
def configure_connection_for_https(connection)
|
|
||||||
require 'net/https'
|
|
||||||
connection.use_ssl = true
|
|
||||||
connection.verify_mode =
|
|
||||||
Gem.configuration.ssl_verify_mode || OpenSSL::SSL::VERIFY_PEER
|
|
||||||
store = OpenSSL::X509::Store.new
|
|
||||||
if Gem.configuration.ssl_ca_cert
|
|
||||||
if File.directory? Gem.configuration.ssl_ca_cert
|
|
||||||
store.add_path Gem.configuration.ssl_ca_cert
|
|
||||||
else
|
|
||||||
store.add_file Gem.configuration.ssl_ca_cert
|
|
||||||
end
|
|
||||||
else
|
|
||||||
store.set_default_paths
|
|
||||||
add_rubygems_trusted_certs(store)
|
|
||||||
end
|
|
||||||
connection.cert_store = store
|
|
||||||
rescue LoadError => e
|
|
||||||
raise unless (e.respond_to?(:path) && e.path == 'openssl') ||
|
|
||||||
e.message =~ / -- openssl$/
|
|
||||||
|
|
||||||
raise Gem::Exception.new(
|
|
||||||
'Unable to require openssl, install OpenSSL and rebuild ruby (preferred) or use non-HTTPS sources')
|
|
||||||
end
|
|
||||||
|
|
||||||
def add_rubygems_trusted_certs(store)
|
|
||||||
pattern = File.expand_path("./ssl_certs/*.pem", File.dirname(__FILE__))
|
|
||||||
Dir.glob(pattern).each do |ssl_cert_file|
|
|
||||||
store.add_file ssl_cert_file
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def correct_for_windows_path(path)
|
def correct_for_windows_path(path)
|
||||||
if path[0].chr == '/' && path[1].chr =~ /[a-z]/i && path[2].chr == ':'
|
if path[0].chr == '/' && path[1].chr =~ /[a-z]/i && path[2].chr == ':'
|
||||||
path = path[1..-1]
|
path = path[1..-1]
|
||||||
|
@ -446,136 +318,13 @@ class Gem::RemoteFetcher
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def no_proxy? host
|
|
||||||
host = host.downcase
|
|
||||||
@env_no_proxy.each do |pattern|
|
|
||||||
pattern = pattern.downcase
|
|
||||||
return true if host[-pattern.length, pattern.length ] == pattern
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Performs a Net::HTTP request of type +request_class+ on +uri+ returning
|
# Performs a Net::HTTP request of type +request_class+ on +uri+ returning
|
||||||
# a Net::HTTP response object. request maintains a table of persistent
|
# a Net::HTTP response object. request maintains a table of persistent
|
||||||
# connections to reduce connect overhead.
|
# connections to reduce connect overhead.
|
||||||
|
|
||||||
def request(uri, request_class, last_modified = nil)
|
def request(uri, request_class, last_modified = nil)
|
||||||
request = request_class.new uri.request_uri
|
Gem::Request.new(uri, request_class, last_modified, @proxy).fetch
|
||||||
|
|
||||||
unless uri.nil? || uri.user.nil? || uri.user.empty? then
|
|
||||||
request.basic_auth uri.user, uri.password
|
|
||||||
end
|
|
||||||
|
|
||||||
request.add_field 'User-Agent', @user_agent
|
|
||||||
request.add_field 'Connection', 'keep-alive'
|
|
||||||
request.add_field 'Keep-Alive', '30'
|
|
||||||
|
|
||||||
if last_modified then
|
|
||||||
last_modified = last_modified.utc
|
|
||||||
request.add_field 'If-Modified-Since', last_modified.rfc2822
|
|
||||||
end
|
|
||||||
|
|
||||||
yield request if block_given?
|
|
||||||
|
|
||||||
connection = connection_for uri
|
|
||||||
|
|
||||||
retried = false
|
|
||||||
bad_response = false
|
|
||||||
|
|
||||||
begin
|
|
||||||
@requests[connection.object_id] += 1
|
|
||||||
|
|
||||||
say "#{request.method} #{uri}" if
|
|
||||||
Gem.configuration.really_verbose
|
|
||||||
|
|
||||||
file_name = File.basename(uri.path)
|
|
||||||
# perform download progress reporter only for gems
|
|
||||||
if request.response_body_permitted? && file_name =~ /\.gem$/
|
|
||||||
reporter = ui.download_reporter
|
|
||||||
response = connection.request(request) do |incomplete_response|
|
|
||||||
if Net::HTTPOK === incomplete_response
|
|
||||||
reporter.fetch(file_name, incomplete_response.content_length)
|
|
||||||
downloaded = 0
|
|
||||||
data = ''
|
|
||||||
|
|
||||||
incomplete_response.read_body do |segment|
|
|
||||||
data << segment
|
|
||||||
downloaded += segment.length
|
|
||||||
reporter.update(downloaded)
|
|
||||||
end
|
|
||||||
reporter.done
|
|
||||||
if incomplete_response.respond_to? :body=
|
|
||||||
incomplete_response.body = data
|
|
||||||
else
|
|
||||||
incomplete_response.instance_variable_set(:@body, data)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
response = connection.request request
|
|
||||||
end
|
|
||||||
|
|
||||||
say "#{response.code} #{response.message}" if
|
|
||||||
Gem.configuration.really_verbose
|
|
||||||
|
|
||||||
rescue Net::HTTPBadResponse
|
|
||||||
say "bad response" if Gem.configuration.really_verbose
|
|
||||||
|
|
||||||
reset connection
|
|
||||||
|
|
||||||
raise FetchError.new('too many bad responses', uri) if bad_response
|
|
||||||
|
|
||||||
bad_response = true
|
|
||||||
retry
|
|
||||||
# HACK work around EOFError bug in Net::HTTP
|
|
||||||
# NOTE Errno::ECONNABORTED raised a lot on Windows, and make impossible
|
|
||||||
# to install gems.
|
|
||||||
rescue EOFError, Timeout::Error,
|
|
||||||
Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE
|
|
||||||
|
|
||||||
requests = @requests[connection.object_id]
|
|
||||||
say "connection reset after #{requests} requests, retrying" if
|
|
||||||
Gem.configuration.really_verbose
|
|
||||||
|
|
||||||
raise FetchError.new('too many connection resets', uri) if retried
|
|
||||||
|
|
||||||
reset connection
|
|
||||||
|
|
||||||
retried = true
|
|
||||||
retry
|
|
||||||
end
|
|
||||||
|
|
||||||
response
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# Resets HTTP connection +connection+.
|
|
||||||
|
|
||||||
def reset(connection)
|
|
||||||
@requests.delete connection.object_id
|
|
||||||
|
|
||||||
connection.finish
|
|
||||||
connection.start
|
|
||||||
end
|
|
||||||
|
|
||||||
def user_agent
|
|
||||||
ua = "RubyGems/#{Gem::VERSION} #{Gem::Platform.local}"
|
|
||||||
|
|
||||||
ruby_version = RUBY_VERSION
|
|
||||||
ruby_version += 'dev' if RUBY_PATCHLEVEL == -1
|
|
||||||
|
|
||||||
ua << " Ruby/#{ruby_version} (#{RUBY_RELEASE_DATE}"
|
|
||||||
if RUBY_PATCHLEVEL >= 0 then
|
|
||||||
ua << " patchlevel #{RUBY_PATCHLEVEL}"
|
|
||||||
elsif defined?(RUBY_REVISION) then
|
|
||||||
ua << " revision #{RUBY_REVISION}"
|
|
||||||
end
|
|
||||||
ua << ")"
|
|
||||||
|
|
||||||
ua << " #{RUBY_ENGINE}" if defined?(RUBY_ENGINE) and RUBY_ENGINE != 'ruby'
|
|
||||||
|
|
||||||
ua
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def https?(uri)
|
def https?(uri)
|
||||||
|
|
262
lib/rubygems/request.rb
Normal file
262
lib/rubygems/request.rb
Normal file
|
@ -0,0 +1,262 @@
|
||||||
|
require 'net/http'
|
||||||
|
require 'time'
|
||||||
|
require 'rubygems/user_interaction'
|
||||||
|
|
||||||
|
class Gem::Request
|
||||||
|
|
||||||
|
include Gem::UserInteraction
|
||||||
|
|
||||||
|
attr_reader :proxy_uri
|
||||||
|
|
||||||
|
def initialize(uri, request_class, last_modified, proxy)
|
||||||
|
@uri = uri
|
||||||
|
@request_class = request_class
|
||||||
|
@last_modified = last_modified
|
||||||
|
@requests = Hash.new 0
|
||||||
|
@connections = {}
|
||||||
|
@user_agent = user_agent
|
||||||
|
|
||||||
|
@proxy_uri =
|
||||||
|
case proxy
|
||||||
|
when :no_proxy then nil
|
||||||
|
when nil then get_proxy_from_env
|
||||||
|
when URI::HTTP then proxy
|
||||||
|
else URI.parse(proxy)
|
||||||
|
end
|
||||||
|
@env_no_proxy = get_no_proxy_from_env
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_rubygems_trusted_certs(store)
|
||||||
|
pattern = File.expand_path("./ssl_certs/*.pem", File.dirname(__FILE__))
|
||||||
|
Dir.glob(pattern).each do |ssl_cert_file|
|
||||||
|
store.add_file ssl_cert_file
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def configure_connection_for_https(connection)
|
||||||
|
require 'net/https'
|
||||||
|
connection.use_ssl = true
|
||||||
|
connection.verify_mode =
|
||||||
|
Gem.configuration.ssl_verify_mode || OpenSSL::SSL::VERIFY_PEER
|
||||||
|
store = OpenSSL::X509::Store.new
|
||||||
|
|
||||||
|
if Gem.configuration.ssl_client_cert then
|
||||||
|
pem = File.read Gem.configuration.ssl_client_cert
|
||||||
|
connection.cert = OpenSSL::X509::Certificate.new pem
|
||||||
|
connection.key = OpenSSL::PKey::RSA.new pem
|
||||||
|
end
|
||||||
|
|
||||||
|
if Gem.configuration.ssl_ca_cert
|
||||||
|
if File.directory? Gem.configuration.ssl_ca_cert
|
||||||
|
store.add_path Gem.configuration.ssl_ca_cert
|
||||||
|
else
|
||||||
|
store.add_file Gem.configuration.ssl_ca_cert
|
||||||
|
end
|
||||||
|
else
|
||||||
|
store.set_default_paths
|
||||||
|
add_rubygems_trusted_certs(store)
|
||||||
|
end
|
||||||
|
connection.cert_store = store
|
||||||
|
rescue LoadError => e
|
||||||
|
raise unless (e.respond_to?(:path) && e.path == 'openssl') ||
|
||||||
|
e.message =~ / -- openssl$/
|
||||||
|
|
||||||
|
raise Gem::Exception.new(
|
||||||
|
'Unable to require openssl, install OpenSSL and rebuild ruby (preferred) or use non-HTTPS sources')
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Creates or an HTTP connection based on +uri+, or retrieves an existing
|
||||||
|
# connection, using a proxy if needed.
|
||||||
|
|
||||||
|
def connection_for(uri)
|
||||||
|
net_http_args = [uri.host, uri.port]
|
||||||
|
|
||||||
|
if @proxy_uri and not no_proxy?(uri.host) then
|
||||||
|
net_http_args += [
|
||||||
|
@proxy_uri.host,
|
||||||
|
@proxy_uri.port,
|
||||||
|
@proxy_uri.user,
|
||||||
|
@proxy_uri.password
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
connection_id = [Thread.current.object_id, *net_http_args].join ':'
|
||||||
|
@connections[connection_id] ||= Net::HTTP.new(*net_http_args)
|
||||||
|
connection = @connections[connection_id]
|
||||||
|
|
||||||
|
if https?(uri) and not connection.started? then
|
||||||
|
configure_connection_for_https(connection)
|
||||||
|
end
|
||||||
|
|
||||||
|
connection.start unless connection.started?
|
||||||
|
|
||||||
|
connection
|
||||||
|
rescue defined?(OpenSSL::SSL) ? OpenSSL::SSL::SSLError : Errno::EHOSTDOWN,
|
||||||
|
Errno::EHOSTDOWN => e
|
||||||
|
raise Gem::RemoteFetcher::FetchError.new(e.message, uri)
|
||||||
|
end
|
||||||
|
|
||||||
|
def fetch
|
||||||
|
request = @request_class.new @uri.request_uri
|
||||||
|
|
||||||
|
unless @uri.nil? || @uri.user.nil? || @uri.user.empty? then
|
||||||
|
request.basic_auth @uri.user, @uri.password
|
||||||
|
end
|
||||||
|
|
||||||
|
request.add_field 'User-Agent', @user_agent
|
||||||
|
request.add_field 'Connection', 'keep-alive'
|
||||||
|
request.add_field 'Keep-Alive', '30'
|
||||||
|
|
||||||
|
if @last_modified then
|
||||||
|
@last_modified = @last_modified.utc
|
||||||
|
request.add_field 'If-Modified-Since', @last_modified.rfc2822
|
||||||
|
end
|
||||||
|
|
||||||
|
yield request if block_given?
|
||||||
|
|
||||||
|
connection = connection_for @uri
|
||||||
|
|
||||||
|
retried = false
|
||||||
|
bad_response = false
|
||||||
|
|
||||||
|
begin
|
||||||
|
@requests[connection.object_id] += 1
|
||||||
|
|
||||||
|
say "#{request.method} #{@uri}" if
|
||||||
|
Gem.configuration.really_verbose
|
||||||
|
|
||||||
|
file_name = File.basename(@uri.path)
|
||||||
|
# perform download progress reporter only for gems
|
||||||
|
if request.response_body_permitted? && file_name =~ /\.gem$/
|
||||||
|
reporter = ui.download_reporter
|
||||||
|
response = connection.request(request) do |incomplete_response|
|
||||||
|
if Net::HTTPOK === incomplete_response
|
||||||
|
reporter.fetch(file_name, incomplete_response.content_length)
|
||||||
|
downloaded = 0
|
||||||
|
data = ''
|
||||||
|
|
||||||
|
incomplete_response.read_body do |segment|
|
||||||
|
data << segment
|
||||||
|
downloaded += segment.length
|
||||||
|
reporter.update(downloaded)
|
||||||
|
end
|
||||||
|
reporter.done
|
||||||
|
if incomplete_response.respond_to? :body=
|
||||||
|
incomplete_response.body = data
|
||||||
|
else
|
||||||
|
incomplete_response.instance_variable_set(:@body, data)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
response = connection.request request
|
||||||
|
end
|
||||||
|
|
||||||
|
say "#{response.code} #{response.message}" if
|
||||||
|
Gem.configuration.really_verbose
|
||||||
|
|
||||||
|
rescue Net::HTTPBadResponse
|
||||||
|
say "bad response" if Gem.configuration.really_verbose
|
||||||
|
|
||||||
|
reset connection
|
||||||
|
|
||||||
|
raise Gem::RemoteFetcher::FetchError.new('too many bad responses', @uri) if bad_response
|
||||||
|
|
||||||
|
bad_response = true
|
||||||
|
retry
|
||||||
|
# HACK work around EOFError bug in Net::HTTP
|
||||||
|
# NOTE Errno::ECONNABORTED raised a lot on Windows, and make impossible
|
||||||
|
# to install gems.
|
||||||
|
rescue EOFError, Timeout::Error,
|
||||||
|
Errno::ECONNABORTED, Errno::ECONNRESET, Errno::EPIPE
|
||||||
|
|
||||||
|
requests = @requests[connection.object_id]
|
||||||
|
say "connection reset after #{requests} requests, retrying" if
|
||||||
|
Gem.configuration.really_verbose
|
||||||
|
|
||||||
|
raise Gem::RemoteFetcher::FetchError.new('too many connection resets', @uri) if retried
|
||||||
|
|
||||||
|
reset connection
|
||||||
|
|
||||||
|
retried = true
|
||||||
|
retry
|
||||||
|
end
|
||||||
|
|
||||||
|
response
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Returns list of no_proxy entries (if any) from the environment
|
||||||
|
|
||||||
|
def get_no_proxy_from_env
|
||||||
|
env_no_proxy = ENV['no_proxy'] || ENV['NO_PROXY']
|
||||||
|
|
||||||
|
return [] if env_no_proxy.nil? or env_no_proxy.empty?
|
||||||
|
|
||||||
|
env_no_proxy.split(/\s*,\s*/)
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Returns an HTTP proxy URI if one is set in the environment variables.
|
||||||
|
|
||||||
|
def get_proxy_from_env
|
||||||
|
env_proxy = ENV['http_proxy'] || ENV['HTTP_PROXY']
|
||||||
|
|
||||||
|
return nil if env_proxy.nil? or env_proxy.empty?
|
||||||
|
|
||||||
|
uri = URI(Gem::UriFormatter.new(env_proxy).normalize)
|
||||||
|
|
||||||
|
if uri and uri.user.nil? and uri.password.nil? then
|
||||||
|
# Probably we have http_proxy_* variables?
|
||||||
|
uri.user = Gem::UriFormatter.new(ENV['http_proxy_user'] || ENV['HTTP_PROXY_USER']).escape
|
||||||
|
uri.password = Gem::UriFormatter.new(ENV['http_proxy_pass'] || ENV['HTTP_PROXY_PASS']).escape
|
||||||
|
end
|
||||||
|
|
||||||
|
uri
|
||||||
|
end
|
||||||
|
|
||||||
|
def https?(uri)
|
||||||
|
uri.scheme.downcase == 'https'
|
||||||
|
end
|
||||||
|
|
||||||
|
def no_proxy? host
|
||||||
|
host = host.downcase
|
||||||
|
@env_no_proxy.each do |pattern|
|
||||||
|
pattern = pattern.downcase
|
||||||
|
return true if host[-pattern.length, pattern.length ] == pattern
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Resets HTTP connection +connection+.
|
||||||
|
|
||||||
|
def reset(connection)
|
||||||
|
@requests.delete connection.object_id
|
||||||
|
|
||||||
|
connection.finish
|
||||||
|
connection.start
|
||||||
|
end
|
||||||
|
|
||||||
|
def user_agent
|
||||||
|
ua = "RubyGems/#{Gem::VERSION} #{Gem::Platform.local}"
|
||||||
|
|
||||||
|
ruby_version = RUBY_VERSION
|
||||||
|
ruby_version += 'dev' if RUBY_PATCHLEVEL == -1
|
||||||
|
|
||||||
|
ua << " Ruby/#{ruby_version} (#{RUBY_RELEASE_DATE}"
|
||||||
|
if RUBY_PATCHLEVEL >= 0 then
|
||||||
|
ua << " patchlevel #{RUBY_PATCHLEVEL}"
|
||||||
|
elsif defined?(RUBY_REVISION) then
|
||||||
|
ua << " revision #{RUBY_REVISION}"
|
||||||
|
end
|
||||||
|
ua << ")"
|
||||||
|
|
||||||
|
ua << " #{RUBY_ENGINE}" if defined?(RUBY_ENGINE) and RUBY_ENGINE != 'ruby'
|
||||||
|
|
||||||
|
ua
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
|
@ -5,178 +5,176 @@ require 'rubygems/dependency_list'
|
||||||
require 'rubygems/installer'
|
require 'rubygems/installer'
|
||||||
require 'tsort'
|
require 'tsort'
|
||||||
|
|
||||||
module Gem
|
class Gem::RequestSet
|
||||||
class RequestSet
|
|
||||||
|
|
||||||
include TSort
|
include TSort
|
||||||
|
|
||||||
def initialize(*deps)
|
##
|
||||||
@dependencies = deps
|
# Array of gems to install even if already installed
|
||||||
|
|
||||||
yield self if block_given?
|
attr_reader :always_install
|
||||||
|
|
||||||
|
attr_reader :dependencies
|
||||||
|
|
||||||
|
attr_accessor :development
|
||||||
|
|
||||||
|
##
|
||||||
|
# Treat missing dependencies as silent errors
|
||||||
|
|
||||||
|
attr_accessor :soft_missing
|
||||||
|
|
||||||
|
def initialize *deps
|
||||||
|
@dependencies = deps
|
||||||
|
|
||||||
|
@always_install = []
|
||||||
|
@development = false
|
||||||
|
@soft_missing = false
|
||||||
|
|
||||||
|
yield self if block_given?
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Declare that a gem of name +name+ with +reqs+ requirements is needed.
|
||||||
|
|
||||||
|
def gem name, *reqs
|
||||||
|
@dependencies << Gem::Dependency.new(name, reqs)
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Add +deps+ Gem::Dependency objects to the set.
|
||||||
|
|
||||||
|
def import deps
|
||||||
|
@dependencies += deps
|
||||||
|
end
|
||||||
|
|
||||||
|
def install options, &block
|
||||||
|
if dir = options[:install_dir]
|
||||||
|
return install_into dir, false, options, &block
|
||||||
end
|
end
|
||||||
|
|
||||||
attr_reader :dependencies
|
cache_dir = options[:cache_dir] || Gem.dir
|
||||||
|
|
||||||
# Declare that a gem of name +name+ with +reqs+ requirements
|
specs = []
|
||||||
# is needed.
|
|
||||||
#
|
sorted_requests.each do |req|
|
||||||
def gem(name, *reqs)
|
if req.installed? and
|
||||||
@dependencies << Gem::Dependency.new(name, reqs)
|
@always_install.none? { |spec| spec == req.spec.spec } then
|
||||||
|
yield req, nil if block_given?
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
path = req.download cache_dir
|
||||||
|
|
||||||
|
inst = Gem::Installer.new path, options
|
||||||
|
|
||||||
|
yield req, inst if block_given?
|
||||||
|
|
||||||
|
specs << inst.install
|
||||||
end
|
end
|
||||||
|
|
||||||
# Add +deps+ Gem::Depedency objects to the set.
|
specs
|
||||||
#
|
end
|
||||||
def import(deps)
|
|
||||||
@dependencies += deps
|
def install_into dir, force = true, options = {}
|
||||||
|
existing = force ? [] : specs_in(dir)
|
||||||
|
existing.delete_if { |s| @always_install.include? s }
|
||||||
|
|
||||||
|
dir = File.expand_path dir
|
||||||
|
|
||||||
|
installed = []
|
||||||
|
|
||||||
|
sorted_requests.each do |req|
|
||||||
|
if existing.find { |s| s.full_name == req.spec.full_name }
|
||||||
|
yield req, nil if block_given?
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
path = req.download(dir)
|
||||||
|
|
||||||
|
unless path then # already installed
|
||||||
|
yield req, nil if block_given?
|
||||||
|
next
|
||||||
|
end
|
||||||
|
|
||||||
|
options[:install_dir] = dir
|
||||||
|
options[:only_install_dir] = true
|
||||||
|
|
||||||
|
inst = Gem::Installer.new path, options
|
||||||
|
|
||||||
|
yield req, inst if block_given?
|
||||||
|
|
||||||
|
inst.install
|
||||||
|
|
||||||
|
installed << req
|
||||||
end
|
end
|
||||||
|
|
||||||
# Resolve the requested dependencies and return an Array of
|
installed
|
||||||
# Specification objects to be activated.
|
end
|
||||||
#
|
|
||||||
def resolve(set=nil)
|
##
|
||||||
r = Gem::DependencyResolver.new(@dependencies, set)
|
# Load a dependency management file.
|
||||||
@requests = r.resolve
|
|
||||||
|
def load_gemdeps path
|
||||||
|
gf = Gem::RequestSet::GemDepedencyAPI.new self, path
|
||||||
|
gf.load
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Resolve the requested dependencies and return an Array of Specification
|
||||||
|
# objects to be activated.
|
||||||
|
|
||||||
|
def resolve set = nil
|
||||||
|
resolver = Gem::DependencyResolver.new @dependencies, set
|
||||||
|
resolver.development = @development
|
||||||
|
resolver.soft_missing = @soft_missing
|
||||||
|
|
||||||
|
@requests = resolver.resolve
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Resolve the requested dependencies against the gems available via Gem.path
|
||||||
|
# and return an Array of Specification objects to be activated.
|
||||||
|
|
||||||
|
def resolve_current
|
||||||
|
resolve Gem::DependencyResolver::CurrentSet.new
|
||||||
|
end
|
||||||
|
|
||||||
|
def sorted_requests
|
||||||
|
@sorted ||= strongly_connected_components.flatten
|
||||||
|
end
|
||||||
|
|
||||||
|
def specs
|
||||||
|
@specs ||= @requests.map { |r| r.full_spec }
|
||||||
|
end
|
||||||
|
|
||||||
|
def specs_in dir
|
||||||
|
Dir["#{dir}/specifications/*.gemspec"].map do |g|
|
||||||
|
Gem::Specification.load g
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Resolve the requested dependencies against the gems
|
def tsort_each_node &block # :nodoc:
|
||||||
# available via Gem.path and return an Array of Specification
|
@requests.each(&block)
|
||||||
# objects to be activated.
|
end
|
||||||
#
|
|
||||||
def resolve_current
|
|
||||||
resolve DependencyResolver::CurrentSet.new
|
|
||||||
end
|
|
||||||
|
|
||||||
# Load a dependency management file.
|
def tsort_each_child node # :nodoc:
|
||||||
#
|
node.spec.dependencies.each do |dep|
|
||||||
def load_gemdeps(path)
|
next if dep.type == :development and not @development
|
||||||
gf = GemDepedencyAPI.new(self, path)
|
|
||||||
gf.load
|
|
||||||
end
|
|
||||||
|
|
||||||
def specs
|
match = @requests.find { |r| dep.match? r.spec.name, r.spec.version }
|
||||||
@specs ||= @requests.map { |r| r.full_spec }
|
if match
|
||||||
end
|
begin
|
||||||
|
yield match
|
||||||
def tsort_each_node(&block)
|
rescue TSort::Cyclic
|
||||||
@requests.each(&block)
|
end
|
||||||
end
|
else
|
||||||
|
unless @soft_missing
|
||||||
def tsort_each_child(node)
|
|
||||||
node.spec.dependencies.each do |dep|
|
|
||||||
next if dep.type == :development
|
|
||||||
|
|
||||||
match = @requests.find { |r| dep.match? r.spec.name, r.spec.version }
|
|
||||||
if match
|
|
||||||
begin
|
|
||||||
yield match
|
|
||||||
rescue TSort::Cyclic
|
|
||||||
end
|
|
||||||
else
|
|
||||||
raise Gem::DependencyError, "Unresolved depedency found during sorting - #{dep}"
|
raise Gem::DependencyError, "Unresolved depedency found during sorting - #{dep}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def sorted_requests
|
|
||||||
@sorted ||= strongly_connected_components.flatten
|
|
||||||
end
|
|
||||||
|
|
||||||
def specs_in(dir)
|
|
||||||
Dir["#{dir}/specifications/*.gemspec"].map do |g|
|
|
||||||
Gem::Specification.load g
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def install_into(dir, force=true, &b)
|
|
||||||
existing = force ? [] : specs_in(dir)
|
|
||||||
|
|
||||||
dir = File.expand_path dir
|
|
||||||
|
|
||||||
installed = []
|
|
||||||
|
|
||||||
sorted_requests.each do |req|
|
|
||||||
if existing.find { |s| s.full_name == req.spec.full_name }
|
|
||||||
b.call req, nil if b
|
|
||||||
next
|
|
||||||
end
|
|
||||||
|
|
||||||
path = req.download(dir)
|
|
||||||
|
|
||||||
inst = Gem::Installer.new path, :install_dir => dir,
|
|
||||||
:only_install_dir => true
|
|
||||||
|
|
||||||
b.call req, inst if b
|
|
||||||
|
|
||||||
inst.install
|
|
||||||
|
|
||||||
installed << req
|
|
||||||
end
|
|
||||||
|
|
||||||
installed
|
|
||||||
end
|
|
||||||
|
|
||||||
def install(options, &b)
|
|
||||||
if dir = options[:install_dir]
|
|
||||||
return install_into(dir, false, &b)
|
|
||||||
end
|
|
||||||
|
|
||||||
cache_dir = options[:cache_dir] || Gem.dir
|
|
||||||
|
|
||||||
specs = []
|
|
||||||
|
|
||||||
sorted_requests.each do |req|
|
|
||||||
if req.installed?
|
|
||||||
b.call req, nil if b
|
|
||||||
next
|
|
||||||
end
|
|
||||||
|
|
||||||
path = req.download cache_dir
|
|
||||||
|
|
||||||
inst = Gem::Installer.new path, options
|
|
||||||
|
|
||||||
b.call req, inst if b
|
|
||||||
|
|
||||||
specs << inst.install
|
|
||||||
end
|
|
||||||
|
|
||||||
specs
|
|
||||||
end
|
|
||||||
|
|
||||||
# A semi-compatible DSL for Bundler's Gemfile format
|
|
||||||
#
|
|
||||||
class GemDepedencyAPI
|
|
||||||
def initialize(set, path)
|
|
||||||
@set = set
|
|
||||||
@path = path
|
|
||||||
end
|
|
||||||
|
|
||||||
def load
|
|
||||||
instance_eval File.read(@path).untaint, @path, 1
|
|
||||||
end
|
|
||||||
|
|
||||||
# DSL
|
|
||||||
|
|
||||||
def source(url)
|
|
||||||
end
|
|
||||||
|
|
||||||
def gem(name, *reqs)
|
|
||||||
# Ignore the opts for now.
|
|
||||||
reqs.pop if reqs.last.kind_of?(Hash)
|
|
||||||
|
|
||||||
@set.gem name, *reqs
|
|
||||||
end
|
|
||||||
|
|
||||||
def platform(what)
|
|
||||||
if what == :ruby
|
|
||||||
yield
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
alias_method :platforms, :platform
|
|
||||||
|
|
||||||
def group(*what)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
require 'rubygems/request_set/gem_dependency_api'
|
||||||
|
|
39
lib/rubygems/request_set/gem_dependency_api.rb
Normal file
39
lib/rubygems/request_set/gem_dependency_api.rb
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
##
|
||||||
|
# A semi-compatible DSL for Bundler's Gemfile format
|
||||||
|
|
||||||
|
class Gem::RequestSet::GemDepedencyAPI
|
||||||
|
|
||||||
|
def initialize set, path
|
||||||
|
@set = set
|
||||||
|
@path = path
|
||||||
|
end
|
||||||
|
|
||||||
|
def load
|
||||||
|
instance_eval File.read(@path).untaint, @path, 1
|
||||||
|
end
|
||||||
|
|
||||||
|
# :category: Bundler Gemfile DSL
|
||||||
|
|
||||||
|
def gem name, *reqs
|
||||||
|
# Ignore the opts for now.
|
||||||
|
reqs.pop if reqs.last.kind_of?(Hash)
|
||||||
|
|
||||||
|
@set.gem name, *reqs
|
||||||
|
end
|
||||||
|
|
||||||
|
def group *what
|
||||||
|
end
|
||||||
|
|
||||||
|
def platform what
|
||||||
|
if what == :ruby
|
||||||
|
yield
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
alias :platforms :platform
|
||||||
|
|
||||||
|
def source url
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
|
@ -12,20 +12,6 @@ begin
|
||||||
rescue LoadError => e
|
rescue LoadError => e
|
||||||
raise unless (e.respond_to?(:path) && e.path == 'openssl') ||
|
raise unless (e.respond_to?(:path) && e.path == 'openssl') ||
|
||||||
e.message =~ / -- openssl$/
|
e.message =~ / -- openssl$/
|
||||||
|
|
||||||
module OpenSSL # :nodoc:
|
|
||||||
class Digest # :nodoc:
|
|
||||||
class SHA1 # :nodoc:
|
|
||||||
def name
|
|
||||||
'SHA1'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
module PKey # :nodoc:
|
|
||||||
class RSA # :nodoc:
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -352,23 +338,38 @@ module Gem::Security
|
||||||
##
|
##
|
||||||
# Digest algorithm used to sign gems
|
# Digest algorithm used to sign gems
|
||||||
|
|
||||||
DIGEST_ALGORITHM = OpenSSL::Digest::SHA1
|
DIGEST_ALGORITHM =
|
||||||
|
if defined?(OpenSSL::Digest) then
|
||||||
|
OpenSSL::Digest::SHA1
|
||||||
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# Used internally to select the signing digest from all computed digests
|
# Used internally to select the signing digest from all computed digests
|
||||||
|
|
||||||
DIGEST_NAME = DIGEST_ALGORITHM.new.name # :nodoc:
|
DIGEST_NAME = # :nodoc:
|
||||||
|
if DIGEST_ALGORITHM then
|
||||||
|
DIGEST_ALGORITHM.new.name
|
||||||
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# Algorithm for creating the key pair used to sign gems
|
# Algorithm for creating the key pair used to sign gems
|
||||||
|
|
||||||
KEY_ALGORITHM = OpenSSL::PKey::RSA
|
KEY_ALGORITHM =
|
||||||
|
if defined?(OpenSSL::PKey) then
|
||||||
|
OpenSSL::PKey::RSA
|
||||||
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# Length of keys created by KEY_ALGORITHM
|
# Length of keys created by KEY_ALGORITHM
|
||||||
|
|
||||||
KEY_LENGTH = 2048
|
KEY_LENGTH = 2048
|
||||||
|
|
||||||
|
##
|
||||||
|
# Cipher used to encrypt the key pair used to sign gems.
|
||||||
|
# Must be in the list returned by OpenSSL::Cipher.ciphers
|
||||||
|
|
||||||
|
KEY_CIPHER = OpenSSL::Cipher.new('aes256') if defined?(OpenSSL::Cipher)
|
||||||
|
|
||||||
##
|
##
|
||||||
# One year in seconds
|
# One year in seconds
|
||||||
|
|
||||||
|
@ -563,13 +564,18 @@ module Gem::Security
|
||||||
|
|
||||||
##
|
##
|
||||||
# Writes +pemmable+, which must respond to +to_pem+ to +path+ with the given
|
# Writes +pemmable+, which must respond to +to_pem+ to +path+ with the given
|
||||||
# +permissions+.
|
# +permissions+. If passed +cipher+ and +passphrase+ those arguments will be
|
||||||
|
# passed to +to_pem+.
|
||||||
|
|
||||||
def self.write pemmable, path, permissions = 0600
|
def self.write pemmable, path, permissions = 0600, passphrase = nil, cipher = KEY_CIPHER
|
||||||
path = File.expand_path path
|
path = File.expand_path path
|
||||||
|
|
||||||
open path, 'wb', permissions do |io|
|
open path, 'wb', permissions do |io|
|
||||||
io.write pemmable.to_pem
|
if passphrase and cipher
|
||||||
|
io.write pemmable.to_pem cipher, passphrase
|
||||||
|
else
|
||||||
|
io.write pemmable.to_pem
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
path
|
path
|
||||||
|
@ -579,8 +585,11 @@ module Gem::Security
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
require 'rubygems/security/policy'
|
if defined?(OpenSSL::SSL) then
|
||||||
require 'rubygems/security/policies'
|
require 'rubygems/security/policy'
|
||||||
require 'rubygems/security/signer'
|
require 'rubygems/security/policies'
|
||||||
require 'rubygems/security/trust_dir'
|
require 'rubygems/security/trust_dir'
|
||||||
|
end
|
||||||
|
|
||||||
|
require 'rubygems/security/signer'
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
require 'rubygems/user_interaction'
|
||||||
|
|
||||||
##
|
##
|
||||||
# A Gem::Security::Policy object encapsulates the settings for verifying
|
# A Gem::Security::Policy object encapsulates the settings for verifying
|
||||||
# signed gem files. This is the base class. You can either declare an
|
# signed gem files. This is the base class. You can either declare an
|
||||||
|
@ -6,6 +8,8 @@
|
||||||
|
|
||||||
class Gem::Security::Policy
|
class Gem::Security::Policy
|
||||||
|
|
||||||
|
include Gem::UserInteraction
|
||||||
|
|
||||||
attr_reader :name
|
attr_reader :name
|
||||||
|
|
||||||
attr_accessor :only_signed
|
attr_accessor :only_signed
|
||||||
|
@ -175,6 +179,19 @@ class Gem::Security::Policy
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Extracts the email or subject from +certificate+
|
||||||
|
|
||||||
|
def subject certificate # :nodoc:
|
||||||
|
certificate.extensions.each do |extension|
|
||||||
|
next unless extension.oid == 'subjectAltName'
|
||||||
|
|
||||||
|
return extension.value
|
||||||
|
end
|
||||||
|
|
||||||
|
certificate.subject.to_s
|
||||||
|
end
|
||||||
|
|
||||||
def inspect # :nodoc:
|
def inspect # :nodoc:
|
||||||
("[Policy: %s - data: %p signer: %p chain: %p root: %p " +
|
("[Policy: %s - data: %p signer: %p chain: %p root: %p " +
|
||||||
"signed-only: %p trusted-only: %p]") % [
|
"signed-only: %p trusted-only: %p]") % [
|
||||||
|
@ -184,16 +201,21 @@ class Gem::Security::Policy
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# Verifies the certificate +chain+ is valid, the +digests+ match the
|
# For +full_name+, verifies the certificate +chain+ is valid, the +digests+
|
||||||
# signatures +signatures+ created by the signer depending on the +policy+
|
# match the signatures +signatures+ created by the signer depending on the
|
||||||
# settings.
|
# +policy+ settings.
|
||||||
#
|
#
|
||||||
# If +key+ is given it is used to validate the signing certificate.
|
# If +key+ is given it is used to validate the signing certificate.
|
||||||
|
|
||||||
def verify chain, key = nil, digests = {}, signatures = {}
|
def verify chain, key = nil, digests = {}, signatures = {},
|
||||||
if @only_signed and signatures.empty? then
|
full_name = '(unknown)'
|
||||||
raise Gem::Security::Exception,
|
if signatures.empty? then
|
||||||
"unsigned gems are not allowed by the #{name} policy"
|
if @only_signed then
|
||||||
|
raise Gem::Security::Exception,
|
||||||
|
"unsigned gems are not allowed by the #{name} policy"
|
||||||
|
else
|
||||||
|
alert_warning "#{full_name} is not signed"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
opt = @opt
|
opt = @opt
|
||||||
|
@ -222,7 +244,11 @@ class Gem::Security::Policy
|
||||||
|
|
||||||
check_root chain, time if @verify_root
|
check_root chain, time if @verify_root
|
||||||
|
|
||||||
check_trust chain, digester, trust_dir if @only_trusted
|
if @only_trusted then
|
||||||
|
check_trust chain, digester, trust_dir
|
||||||
|
else
|
||||||
|
alert_warning "#{subject signer} is not trusted for #{full_name}"
|
||||||
|
end
|
||||||
|
|
||||||
signatures.each do |file, _|
|
signatures.each do |file, _|
|
||||||
digest = signer_digests[file]
|
digest = signer_digests[file]
|
||||||
|
@ -252,7 +278,7 @@ class Gem::Security::Policy
|
||||||
OpenSSL::X509::Certificate.new cert_pem
|
OpenSSL::X509::Certificate.new cert_pem
|
||||||
end
|
end
|
||||||
|
|
||||||
verify chain, nil, digests, signatures
|
verify chain, nil, digests, signatures, spec.full_name
|
||||||
|
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
|
@ -29,7 +29,7 @@ class Gem::Security::Signer
|
||||||
# +chain+ containing X509 certificates, encoding certificates or paths to
|
# +chain+ containing X509 certificates, encoding certificates or paths to
|
||||||
# certificates.
|
# certificates.
|
||||||
|
|
||||||
def initialize key, cert_chain
|
def initialize key, cert_chain, passphrase = nil
|
||||||
@cert_chain = cert_chain
|
@cert_chain = cert_chain
|
||||||
@key = key
|
@key = key
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ 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 if
|
@key = OpenSSL::PKey::RSA.new File.read(@key), passphrase if
|
||||||
@key and not OpenSSL::PKey::RSA === @key
|
@key and not OpenSSL::PKey::RSA === @key
|
||||||
|
|
||||||
if @cert_chain then
|
if @cert_chain then
|
||||||
|
|
|
@ -25,14 +25,21 @@ class Gem::Source
|
||||||
end
|
end
|
||||||
|
|
||||||
def <=>(other)
|
def <=>(other)
|
||||||
if !@uri
|
case other
|
||||||
return 0 unless other.uri
|
when Gem::Source::Installed, Gem::Source::Local then
|
||||||
return -1
|
-1
|
||||||
|
when Gem::Source then
|
||||||
|
if !@uri
|
||||||
|
return 0 unless other.uri
|
||||||
|
return -1
|
||||||
|
end
|
||||||
|
|
||||||
|
return 1 if !other.uri
|
||||||
|
|
||||||
|
@uri.to_s <=> other.uri.to_s
|
||||||
|
else
|
||||||
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
return 1 if !other.uri
|
|
||||||
|
|
||||||
@uri.to_s <=> other.uri.to_s
|
|
||||||
end
|
end
|
||||||
|
|
||||||
include Comparable
|
include Comparable
|
||||||
|
@ -58,8 +65,7 @@ class Gem::Source
|
||||||
def cache_dir(uri)
|
def cache_dir(uri)
|
||||||
# Correct for windows paths
|
# Correct for windows paths
|
||||||
escaped_path = uri.path.sub(/^\/([a-z]):\//i, '/\\1-/')
|
escaped_path = uri.path.sub(/^\/([a-z]):\//i, '/\\1-/')
|
||||||
root = File.join Gem.user_home, '.gem', 'specs'
|
File.join Gem.spec_cache_dir, "#{uri.host}%#{uri.port}", File.dirname(escaped_path)
|
||||||
File.join root, "#{uri.host}%#{uri.port}", File.dirname(escaped_path)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_cache?
|
def update_cache?
|
||||||
|
@ -141,4 +147,14 @@ class Gem::Source
|
||||||
fetcher = Gem::RemoteFetcher.fetcher
|
fetcher = Gem::RemoteFetcher.fetcher
|
||||||
fetcher.download spec, @uri.to_s, dir
|
fetcher.download spec, @uri.to_s, dir
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def pretty_print q # :nodoc:
|
||||||
|
q.group 2, '[Remote:', ']' do
|
||||||
|
q.breakable
|
||||||
|
q.text @uri.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
require 'rubygems/source/installed'
|
||||||
|
|
28
lib/rubygems/source/installed.rb
Normal file
28
lib/rubygems/source/installed.rb
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
class Gem::Source::Installed < Gem::Source
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Installed sources sort before all other sources
|
||||||
|
|
||||||
|
def <=> other
|
||||||
|
case other
|
||||||
|
when Gem::Source::Installed then
|
||||||
|
0
|
||||||
|
when Gem::Source then
|
||||||
|
1
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# We don't need to download an installed gem
|
||||||
|
|
||||||
|
def download spec, path
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
122
lib/rubygems/source/local.rb
Normal file
122
lib/rubygems/source/local.rb
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
require 'rubygems/source'
|
||||||
|
|
||||||
|
class Gem::Source::Local < Gem::Source
|
||||||
|
def initialize
|
||||||
|
@uri = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Local sorts before Gem::Source and after Gem::Source::Installed
|
||||||
|
|
||||||
|
def <=> other
|
||||||
|
case other
|
||||||
|
when Gem::Source::Installed then
|
||||||
|
-1
|
||||||
|
when Gem::Source::Local then
|
||||||
|
0
|
||||||
|
when Gem::Source then
|
||||||
|
1
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def inspect # :nodoc:
|
||||||
|
"#<%s specs: %p>" % [self.class, @specs.keys]
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_specs(type)
|
||||||
|
names = []
|
||||||
|
|
||||||
|
@specs = {}
|
||||||
|
|
||||||
|
Dir["*.gem"].each do |file|
|
||||||
|
begin
|
||||||
|
pkg = Gem::Package.new(file)
|
||||||
|
rescue SystemCallError, Gem::Package::FormatError
|
||||||
|
# ignore
|
||||||
|
else
|
||||||
|
tup = pkg.spec.name_tuple
|
||||||
|
@specs[tup] = [File.expand_path(file), pkg]
|
||||||
|
|
||||||
|
case type
|
||||||
|
when :released
|
||||||
|
unless pkg.spec.version.prerelease?
|
||||||
|
names << pkg.spec.name_tuple
|
||||||
|
end
|
||||||
|
when :prerelease
|
||||||
|
if pkg.spec.version.prerelease?
|
||||||
|
names << pkg.spec.name_tuple
|
||||||
|
end
|
||||||
|
when :latest
|
||||||
|
tup = pkg.spec.name_tuple
|
||||||
|
|
||||||
|
cur = names.find { |x| x.name == tup.name }
|
||||||
|
if !cur
|
||||||
|
names << tup
|
||||||
|
elsif cur.version < tup.version
|
||||||
|
names.delete cur
|
||||||
|
names << tup
|
||||||
|
end
|
||||||
|
else
|
||||||
|
names << pkg.spec.name_tuple
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
names
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_gem(gem_name, version=Gem::Requirement.default,
|
||||||
|
prerelease=false)
|
||||||
|
load_specs :complete
|
||||||
|
|
||||||
|
found = []
|
||||||
|
|
||||||
|
@specs.each do |n, data|
|
||||||
|
if n.name == gem_name
|
||||||
|
s = data[1].spec
|
||||||
|
|
||||||
|
if version.satisfied_by?(s.version)
|
||||||
|
if prerelease
|
||||||
|
found << s
|
||||||
|
elsif !s.version.prerelease?
|
||||||
|
found << s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
found.sort_by { |s| s.version }.last
|
||||||
|
end
|
||||||
|
|
||||||
|
def fetch_spec(name)
|
||||||
|
load_specs :complete
|
||||||
|
|
||||||
|
if data = @specs[name]
|
||||||
|
data.last.spec
|
||||||
|
else
|
||||||
|
raise Gem::Exception, "Unable to find spec for '#{name}'"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def download(spec, cache_dir=nil)
|
||||||
|
load_specs :complete
|
||||||
|
|
||||||
|
@specs.each do |name, data|
|
||||||
|
return data[0] if data[1].spec == spec
|
||||||
|
end
|
||||||
|
|
||||||
|
raise Gem::Exception, "Unable to find file for '#{spec.full_name}'"
|
||||||
|
end
|
||||||
|
|
||||||
|
def pretty_print q # :nodoc:
|
||||||
|
q.group 2, '[Local gems:', ']' do
|
||||||
|
q.breakable
|
||||||
|
q.seplist @specs.keys do |v|
|
||||||
|
q.text v.full_name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
28
lib/rubygems/source/specific_file.rb
Normal file
28
lib/rubygems/source/specific_file.rb
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
class Gem::Source::SpecificFile < Gem::Source
|
||||||
|
def initialize(file)
|
||||||
|
@uri = nil
|
||||||
|
@path = ::File.expand_path(file)
|
||||||
|
|
||||||
|
@package = Gem::Package.new @path
|
||||||
|
@spec = @package.spec
|
||||||
|
@name = @spec.name_tuple
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_reader :spec
|
||||||
|
|
||||||
|
def load_specs(*a)
|
||||||
|
[@name]
|
||||||
|
end
|
||||||
|
|
||||||
|
def fetch_spec(name)
|
||||||
|
return @spec if name == @name
|
||||||
|
raise Gem::Exception, "Unable to find '#{name}'"
|
||||||
|
@spec
|
||||||
|
end
|
||||||
|
|
||||||
|
def download(spec, dir=nil)
|
||||||
|
return @path if spec == @spec
|
||||||
|
raise Gem::Exception, "Unable to download '#{spec.full_name}'"
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -1,92 +1,5 @@
|
||||||
require 'rubygems/source'
|
require 'rubygems/source'
|
||||||
|
require 'rubygems/source_local'
|
||||||
|
|
||||||
class Gem::Source::Local < Gem::Source
|
# TODO warn upon require, this file is deprecated.
|
||||||
def initialize
|
|
||||||
@uri = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def load_specs(type)
|
|
||||||
names = []
|
|
||||||
|
|
||||||
@specs = {}
|
|
||||||
|
|
||||||
Dir["*.gem"].each do |file|
|
|
||||||
begin
|
|
||||||
pkg = Gem::Package.new(file)
|
|
||||||
rescue SystemCallError, Gem::Package::FormatError
|
|
||||||
# ignore
|
|
||||||
else
|
|
||||||
tup = pkg.spec.name_tuple
|
|
||||||
@specs[tup] = [File.expand_path(file), pkg]
|
|
||||||
|
|
||||||
case type
|
|
||||||
when :released
|
|
||||||
unless pkg.spec.version.prerelease?
|
|
||||||
names << pkg.spec.name_tuple
|
|
||||||
end
|
|
||||||
when :prerelease
|
|
||||||
if pkg.spec.version.prerelease?
|
|
||||||
names << pkg.spec.name_tuple
|
|
||||||
end
|
|
||||||
when :latest
|
|
||||||
tup = pkg.spec.name_tuple
|
|
||||||
|
|
||||||
cur = names.find { |x| x.name == tup.name }
|
|
||||||
if !cur
|
|
||||||
names << tup
|
|
||||||
elsif cur.version < tup.version
|
|
||||||
names.delete cur
|
|
||||||
names << tup
|
|
||||||
end
|
|
||||||
else
|
|
||||||
names << pkg.spec.name_tuple
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
names
|
|
||||||
end
|
|
||||||
|
|
||||||
def find_gem(gem_name, version=Gem::Requirement.default,
|
|
||||||
prerelease=false)
|
|
||||||
load_specs :complete
|
|
||||||
|
|
||||||
found = []
|
|
||||||
|
|
||||||
@specs.each do |n, data|
|
|
||||||
if n.name == gem_name
|
|
||||||
s = data[1].spec
|
|
||||||
|
|
||||||
if version.satisfied_by?(s.version)
|
|
||||||
if prerelease
|
|
||||||
found << s
|
|
||||||
elsif !s.version.prerelease?
|
|
||||||
found << s
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
found.sort_by { |s| s.version }.last
|
|
||||||
end
|
|
||||||
|
|
||||||
def fetch_spec(name)
|
|
||||||
load_specs :complete
|
|
||||||
|
|
||||||
if data = @specs[name]
|
|
||||||
data.last.spec
|
|
||||||
else
|
|
||||||
raise Gem::Exception, "Unable to find spec for '#{name}'"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def download(spec, cache_dir=nil)
|
|
||||||
load_specs :complete
|
|
||||||
|
|
||||||
@specs.each do |name, data|
|
|
||||||
return data[0] if data[1].spec == spec
|
|
||||||
end
|
|
||||||
|
|
||||||
raise Gem::Exception, "Unable to find file for '#{spec.full_name}'"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
|
@ -1,28 +1,4 @@
|
||||||
class Gem::Source::SpecificFile < Gem::Source
|
require 'rubygems/source/specific_file'
|
||||||
def initialize(file)
|
|
||||||
@uri = nil
|
|
||||||
@path = ::File.expand_path(file)
|
|
||||||
|
|
||||||
@package = Gem::Package.new @path
|
# TODO warn upon require, this file is deprecated.
|
||||||
@spec = @package.spec
|
|
||||||
@name = @spec.name_tuple
|
|
||||||
end
|
|
||||||
|
|
||||||
attr_reader :spec
|
|
||||||
|
|
||||||
def load_specs(*a)
|
|
||||||
[@name]
|
|
||||||
end
|
|
||||||
|
|
||||||
def fetch_spec(name)
|
|
||||||
return @spec if name == @name
|
|
||||||
raise Gem::Exception, "Unable to find '#{name}'"
|
|
||||||
@spec
|
|
||||||
end
|
|
||||||
|
|
||||||
def download(spec, dir=nil)
|
|
||||||
return @path if spec == @spec
|
|
||||||
raise Gem::Exception, "Unable to download '#{spec.full_name}'"
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
|
@ -38,7 +38,6 @@ class Gem::SpecFetcher
|
||||||
end
|
end
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
@dir = File.join Gem.user_home, '.gem', 'specs'
|
|
||||||
@update_cache = File.stat(Gem.user_home).uid == Process.uid
|
@update_cache = File.stat(Gem.user_home).uid == Process.uid
|
||||||
|
|
||||||
@specs = {}
|
@specs = {}
|
||||||
|
|
|
@ -5,10 +5,13 @@
|
||||||
# See LICENSE.txt for permissions.
|
# See LICENSE.txt for permissions.
|
||||||
#++
|
#++
|
||||||
|
|
||||||
|
|
||||||
require 'rubygems/version'
|
require 'rubygems/version'
|
||||||
require 'rubygems/requirement'
|
require 'rubygems/requirement'
|
||||||
require 'rubygems/platform'
|
require 'rubygems/platform'
|
||||||
require 'rubygems/deprecate'
|
require 'rubygems/deprecate'
|
||||||
|
require 'rubygems/basic_specification'
|
||||||
|
require 'rubygems/stub_specification'
|
||||||
|
|
||||||
# :stopdoc:
|
# :stopdoc:
|
||||||
# date.rb can't be loaded for `make install` due to miniruby
|
# date.rb can't be loaded for `make install` due to miniruby
|
||||||
|
@ -45,7 +48,7 @@ class Date; end
|
||||||
#
|
#
|
||||||
# s.metadata = { "bugtracker" => "http://somewhere.com/blah" }
|
# s.metadata = { "bugtracker" => "http://somewhere.com/blah" }
|
||||||
|
|
||||||
class Gem::Specification
|
class Gem::Specification < Gem::BasicSpecification
|
||||||
|
|
||||||
# REFACTOR: Consider breaking out this version stuff into a separate
|
# REFACTOR: Consider breaking out this version stuff into a separate
|
||||||
# module. There's enough special stuff around it that it may justify
|
# module. There's enough special stuff around it that it may justify
|
||||||
|
@ -107,6 +110,10 @@ class Gem::Specification
|
||||||
today = Time.now.utc
|
today = Time.now.utc
|
||||||
TODAY = Time.utc(today.year, today.month, today.day)
|
TODAY = Time.utc(today.year, today.month, today.day)
|
||||||
|
|
||||||
|
LOAD_CACHE = {}
|
||||||
|
|
||||||
|
private_constant :LOAD_CACHE if defined? private_constant
|
||||||
|
|
||||||
# :startdoc:
|
# :startdoc:
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -156,6 +163,17 @@ class Gem::Specification
|
||||||
:version => nil,
|
:version => nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Dupable = { }
|
||||||
|
|
||||||
|
@@default_value.each do |k,v|
|
||||||
|
case v
|
||||||
|
when Time, Numeric, Symbol, true, false, nil
|
||||||
|
Dupable[k] = false
|
||||||
|
else
|
||||||
|
Dupable[k] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
@@attributes = @@default_value.keys.sort_by { |s| s.to_s }
|
@@attributes = @@default_value.keys.sort_by { |s| s.to_s }
|
||||||
@@array_attributes = @@default_value.reject { |k,v| v != [] }.keys
|
@@array_attributes = @@default_value.reject { |k,v| v != [] }.keys
|
||||||
@@nil_attributes, @@non_nil_attributes = @@default_value.keys.partition { |k|
|
@@nil_attributes, @@non_nil_attributes = @@default_value.keys.partition { |k|
|
||||||
|
@ -583,11 +601,6 @@ class Gem::Specification
|
||||||
|
|
||||||
attr_writer :default_executable
|
attr_writer :default_executable
|
||||||
|
|
||||||
##
|
|
||||||
# Path this gemspec was loaded from. This attribute is not persisted.
|
|
||||||
|
|
||||||
attr_reader :loaded_from
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Allows deinstallation of gems with legacy platforms.
|
# Allows deinstallation of gems with legacy platforms.
|
||||||
|
|
||||||
|
@ -615,44 +628,9 @@ class Gem::Specification
|
||||||
|
|
||||||
attr_accessor :specification_version
|
attr_accessor :specification_version
|
||||||
|
|
||||||
class << self
|
|
||||||
def default_specifications_dir
|
|
||||||
File.join(Gem.default_dir, "specifications", "default")
|
|
||||||
end
|
|
||||||
|
|
||||||
def each_spec(search_dirs) # :nodoc:
|
|
||||||
search_dirs.each { |dir|
|
|
||||||
Dir[File.join(dir, "*.gemspec")].each { |path|
|
|
||||||
spec = Gem::Specification.load path.untaint
|
|
||||||
# #load returns nil if the spec is bad, so we just ignore
|
|
||||||
# it at this stage
|
|
||||||
yield(spec) if spec
|
|
||||||
}
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
def each_default(&block) # :nodoc:
|
|
||||||
each_spec([default_specifications_dir],
|
|
||||||
&block)
|
|
||||||
end
|
|
||||||
|
|
||||||
def each_normal(&block) # :nodoc:
|
|
||||||
each_spec(dirs, &block)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def self._all # :nodoc:
|
def self._all # :nodoc:
|
||||||
unless defined?(@@all) && @@all then
|
unless defined?(@@all) && @@all then
|
||||||
|
@@all = stubs.map(&:to_spec)
|
||||||
specs = {}
|
|
||||||
each_default do |spec|
|
|
||||||
specs[spec.full_name] ||= spec
|
|
||||||
end
|
|
||||||
each_normal do |spec|
|
|
||||||
specs[spec.full_name] ||= spec
|
|
||||||
end
|
|
||||||
|
|
||||||
@@all = specs.values
|
|
||||||
|
|
||||||
# After a reset, make sure already loaded specs
|
# After a reset, make sure already loaded specs
|
||||||
# are still marked as activated.
|
# are still marked as activated.
|
||||||
|
@ -660,13 +638,58 @@ class Gem::Specification
|
||||||
Gem.loaded_specs.each_value{|s| specs[s] = true}
|
Gem.loaded_specs.each_value{|s| specs[s] = true}
|
||||||
@@all.each{|s| s.activated = true if specs[s]}
|
@@all.each{|s| s.activated = true if specs[s]}
|
||||||
|
|
||||||
_resort!
|
_resort!(@@all)
|
||||||
end
|
end
|
||||||
@@all
|
@@all
|
||||||
end
|
end
|
||||||
|
|
||||||
def self._resort! # :nodoc:
|
def self._clear_load_cache # :nodoc:
|
||||||
@@all.sort! { |a, b|
|
LOAD_CACHE.clear
|
||||||
|
end
|
||||||
|
|
||||||
|
# :nodoc:
|
||||||
|
def self.each_gemspec(dirs)
|
||||||
|
dirs.each do |dir|
|
||||||
|
Dir[File.join(dir, "*.gemspec")].each do |path|
|
||||||
|
yield path.untaint
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# :nodoc:
|
||||||
|
def self.each_stub(dirs)
|
||||||
|
each_gemspec(dirs) do |path|
|
||||||
|
stub = Gem::StubSpecification.new(path)
|
||||||
|
yield stub if stub.valid?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# :nodoc:
|
||||||
|
def self.each_spec(dirs)
|
||||||
|
each_gemspec(dirs) do |path|
|
||||||
|
spec = self.load path
|
||||||
|
yield spec if spec
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Returns a Gem::StubSpecification for every installed gem
|
||||||
|
|
||||||
|
def self.stubs
|
||||||
|
@@stubs ||= begin
|
||||||
|
stubs = {}
|
||||||
|
each_stub([default_specifications_dir] + dirs) do |stub|
|
||||||
|
stubs[stub.full_name] ||= stub
|
||||||
|
end
|
||||||
|
|
||||||
|
stubs = stubs.values
|
||||||
|
_resort!(stubs)
|
||||||
|
stubs
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self._resort!(specs) # :nodoc:
|
||||||
|
specs.sort! { |a, b|
|
||||||
names = a.name <=> b.name
|
names = a.name <=> b.name
|
||||||
next names if names.nonzero?
|
next names if names.nonzero?
|
||||||
b.version <=> a.version
|
b.version <=> a.version
|
||||||
|
@ -677,7 +700,9 @@ class Gem::Specification
|
||||||
# Loads the default specifications. It should be called only once.
|
# Loads the default specifications. It should be called only once.
|
||||||
|
|
||||||
def self.load_defaults
|
def self.load_defaults
|
||||||
each_default do |spec|
|
each_spec([default_specifications_dir]) do |spec|
|
||||||
|
# #load returns nil if the spec is bad, so we just ignore
|
||||||
|
# it at this stage
|
||||||
Gem.register_default_spec(spec)
|
Gem.register_default_spec(spec)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -700,7 +725,9 @@ class Gem::Specification
|
||||||
return if _all.include? spec
|
return if _all.include? spec
|
||||||
|
|
||||||
_all << spec
|
_all << spec
|
||||||
_resort!
|
stubs << spec
|
||||||
|
_resort!(_all)
|
||||||
|
_resort!(stubs)
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -843,9 +870,10 @@ class Gem::Specification
|
||||||
# amongst the specs that are not activated.
|
# amongst the specs that are not activated.
|
||||||
|
|
||||||
def self.find_inactive_by_path path
|
def self.find_inactive_by_path path
|
||||||
self.find { |spec|
|
stub = stubs.find { |s|
|
||||||
spec.contains_requirable_file? path unless spec.activated?
|
s.contains_requirable_file? path unless s.activated?
|
||||||
}
|
}
|
||||||
|
stub && stub.to_spec
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -937,6 +965,9 @@ class Gem::Specification
|
||||||
file = file.dup.untaint
|
file = file.dup.untaint
|
||||||
return unless File.file?(file)
|
return unless File.file?(file)
|
||||||
|
|
||||||
|
spec = LOAD_CACHE[file]
|
||||||
|
return spec if spec
|
||||||
|
|
||||||
code = if defined? Encoding
|
code = if defined? Encoding
|
||||||
File.read file, :mode => 'r:UTF-8:-'
|
File.read file, :mode => 'r:UTF-8:-'
|
||||||
else
|
else
|
||||||
|
@ -950,6 +981,7 @@ class Gem::Specification
|
||||||
|
|
||||||
if Gem::Specification === spec
|
if Gem::Specification === spec
|
||||||
spec.loaded_from = file.to_s
|
spec.loaded_from = file.to_s
|
||||||
|
LOAD_CACHE[file] = spec
|
||||||
return spec
|
return spec
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1013,6 +1045,7 @@ class Gem::Specification
|
||||||
raise "wtf: #{spec.full_name} not in #{all_names.inspect}" unless
|
raise "wtf: #{spec.full_name} not in #{all_names.inspect}" unless
|
||||||
_all.include? spec
|
_all.include? spec
|
||||||
_all.delete spec
|
_all.delete spec
|
||||||
|
stubs.delete_if { |s| s.full_name == spec.full_name }
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -1037,6 +1070,8 @@ class Gem::Specification
|
||||||
@@dirs = nil
|
@@dirs = nil
|
||||||
Gem.pre_reset_hooks.each { |hook| hook.call }
|
Gem.pre_reset_hooks.each { |hook| hook.call }
|
||||||
@@all = nil
|
@@all = nil
|
||||||
|
@@stubs = nil
|
||||||
|
_clear_load_cache
|
||||||
unresolved = unresolved_deps
|
unresolved = unresolved_deps
|
||||||
unless unresolved.empty? then
|
unless unresolved.empty? then
|
||||||
w = "W" + "ARN"
|
w = "W" + "ARN"
|
||||||
|
@ -1280,20 +1315,6 @@ class Gem::Specification
|
||||||
@authors ||= []
|
@authors ||= []
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
|
||||||
# Returns the full path to the base gem directory.
|
|
||||||
#
|
|
||||||
# eg: /usr/local/lib/ruby/gems/1.8
|
|
||||||
|
|
||||||
def base_dir
|
|
||||||
return Gem.dir unless loaded_from
|
|
||||||
@base_dir ||= if default_gem? then
|
|
||||||
File.dirname File.dirname File.dirname loaded_from
|
|
||||||
else
|
|
||||||
File.dirname File.dirname loaded_from
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Returns the full path to installed gem's bin directory.
|
# Returns the full path to installed gem's bin directory.
|
||||||
#
|
#
|
||||||
|
@ -1367,19 +1388,6 @@ class Gem::Specification
|
||||||
conflicts
|
conflicts
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
|
||||||
# Return true if this spec can require +file+.
|
|
||||||
|
|
||||||
def contains_requirable_file? file
|
|
||||||
root = full_gem_path
|
|
||||||
suffixes = Gem.suffixes
|
|
||||||
|
|
||||||
require_paths.any? do |lib|
|
|
||||||
base = "#{root}/#{lib}/#{file}"
|
|
||||||
suffixes.any? { |suf| File.file? "#{base}#{suf}" }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# The date this gem was created. Lazily defaults to TODAY.
|
# The date this gem was created. Lazily defaults to TODAY.
|
||||||
|
|
||||||
|
@ -1623,35 +1631,14 @@ class Gem::Specification
|
||||||
spec
|
spec
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
# :nodoc:
|
||||||
# The full path to the gem (install path + full name).
|
def find_full_gem_path
|
||||||
|
super || File.expand_path(File.join(gems_dir, original_name))
|
||||||
def full_gem_path
|
|
||||||
# TODO: This is a heavily used method by gems, so we'll need
|
|
||||||
# to aleast just alias it to #gem_dir rather than remove it.
|
|
||||||
|
|
||||||
# TODO: also, shouldn't it default to full_name if it hasn't been written?
|
|
||||||
return @full_gem_path if defined?(@full_gem_path) && @full_gem_path
|
|
||||||
|
|
||||||
@full_gem_path = File.expand_path File.join(gems_dir, full_name)
|
|
||||||
@full_gem_path.untaint
|
|
||||||
|
|
||||||
return @full_gem_path if File.directory? @full_gem_path
|
|
||||||
|
|
||||||
@full_gem_path = File.expand_path File.join(gems_dir, original_name)
|
|
||||||
end
|
end
|
||||||
|
private :find_full_gem_path
|
||||||
##
|
|
||||||
# Returns the full name (name-version) of this Gem. Platform information
|
|
||||||
# is included (name-version-platform) if it is specified and not the
|
|
||||||
# default Ruby platform.
|
|
||||||
|
|
||||||
def full_name
|
def full_name
|
||||||
@full_name ||= if platform == Gem::Platform::RUBY or platform.nil? then
|
@full_name ||= super
|
||||||
"#{@name}-#{@version}".untaint
|
|
||||||
else
|
|
||||||
"#{@name}-#{@version}-#{platform}".untaint
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -1662,15 +1649,6 @@ class Gem::Specification
|
||||||
@gem_dir ||= File.expand_path File.join(gems_dir, full_name)
|
@gem_dir ||= File.expand_path File.join(gems_dir, full_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
|
||||||
# Returns the full path to the gems directory containing this spec's
|
|
||||||
# gem directory. eg: /usr/local/lib/ruby/1.8/gems
|
|
||||||
|
|
||||||
def gems_dir
|
|
||||||
# TODO: this logic seems terribly broken, but tests fail if just base_dir
|
|
||||||
@gems_dir ||= File.join(loaded_from && base_dir || Gem.dir, "gems")
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# Deprecated and ignored, defaults to true.
|
# Deprecated and ignored, defaults to true.
|
||||||
#
|
#
|
||||||
|
@ -1703,9 +1681,7 @@ class Gem::Specification
|
||||||
# :startdoc:
|
# :startdoc:
|
||||||
|
|
||||||
def hash # :nodoc:
|
def hash # :nodoc:
|
||||||
@@attributes.inject(0) { |hash_code, (name, _)|
|
name.hash ^ version.hash
|
||||||
hash_code ^ self.send(name).hash
|
|
||||||
}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def init_with coder # :nodoc:
|
def init_with coder # :nodoc:
|
||||||
|
@ -1720,7 +1696,7 @@ class Gem::Specification
|
||||||
def initialize name = nil, version = nil
|
def initialize name = nil, version = nil
|
||||||
@loaded = false
|
@loaded = false
|
||||||
@activated = false
|
@activated = false
|
||||||
@loaded_from = nil
|
self.loaded_from = nil
|
||||||
@original_platform = nil
|
@original_platform = nil
|
||||||
|
|
||||||
@@nil_attributes.each do |key|
|
@@nil_attributes.each do |key|
|
||||||
|
@ -1729,11 +1705,7 @@ class Gem::Specification
|
||||||
|
|
||||||
@@non_nil_attributes.each do |key|
|
@@non_nil_attributes.each do |key|
|
||||||
default = default_value(key)
|
default = default_value(key)
|
||||||
value = case default
|
value = Dupable[key] ? default.dup : default
|
||||||
when Time, Numeric, Symbol, true, false, nil then default
|
|
||||||
else default.dup
|
|
||||||
end
|
|
||||||
|
|
||||||
instance_variable_set "@#{key}", value
|
instance_variable_set "@#{key}", value
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1828,27 +1800,30 @@ class Gem::Specification
|
||||||
@licenses ||= []
|
@licenses ||= []
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
def filename= path
|
||||||
# Set the location a Specification was loaded from. +obj+ is converted
|
super
|
||||||
# to a String.
|
|
||||||
|
|
||||||
def loaded_from= path
|
|
||||||
@loaded_from = path.to_s
|
|
||||||
|
|
||||||
# reset everything @loaded_from depends upon
|
|
||||||
@base_dir = nil
|
|
||||||
@bin_dir = nil
|
@bin_dir = nil
|
||||||
@cache_dir = nil
|
@cache_dir = nil
|
||||||
@cache_file = nil
|
@cache_file = nil
|
||||||
@doc_dir = nil
|
@doc_dir = nil
|
||||||
@full_gem_path = nil
|
|
||||||
@gem_dir = nil
|
@gem_dir = nil
|
||||||
@gems_dir = nil
|
|
||||||
@ri_dir = nil
|
@ri_dir = nil
|
||||||
@spec_dir = nil
|
@spec_dir = nil
|
||||||
@spec_file = nil
|
@spec_file = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Path this gemspec was loaded from. This attribute is not persisted.
|
||||||
|
|
||||||
|
alias loaded_from filename
|
||||||
|
|
||||||
|
##
|
||||||
|
# Set the location a Specification was loaded from. +obj+ is converted
|
||||||
|
# to a String.
|
||||||
|
|
||||||
|
alias loaded_from= filename=
|
||||||
|
|
||||||
##
|
##
|
||||||
# Sets the rubygems_version to the current RubyGems version.
|
# Sets the rubygems_version to the current RubyGems version.
|
||||||
|
|
||||||
|
@ -1878,6 +1853,11 @@ class Gem::Specification
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Prevent ruby hitting spec.method_missing when [[spec]].flatten is called
|
||||||
|
def to_ary # :nodoc:
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# Normalize the list of files so that:
|
# Normalize the list of files so that:
|
||||||
# * All file lists have redundancies removed.
|
# * All file lists have redundancies removed.
|
||||||
|
@ -2093,6 +2073,13 @@ class Gem::Specification
|
||||||
[@name, @version, @new_platform == Gem::Platform::RUBY ? -1 : 1]
|
[@name, @version, @new_platform == Gem::Platform::RUBY ? -1 : 1]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Used by Gem::DependencyResolver to order Gem::Specification objects
|
||||||
|
|
||||||
|
def source # :nodoc:
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# Returns the full path to the directory containing this spec's
|
# Returns the full path to the directory containing this spec's
|
||||||
# gemspec file. eg: /usr/local/lib/ruby/gems/1.8/specifications
|
# gemspec file. eg: /usr/local/lib/ruby/gems/1.8/specifications
|
||||||
|
@ -2172,6 +2159,7 @@ class Gem::Specification
|
||||||
mark_version
|
mark_version
|
||||||
result = []
|
result = []
|
||||||
result << "# -*- encoding: utf-8 -*-"
|
result << "# -*- encoding: utf-8 -*-"
|
||||||
|
result << "#{Gem::StubSpecification::PREFIX}#{name} #{version} #{platform} #{require_paths.join("\0")}"
|
||||||
result << nil
|
result << nil
|
||||||
result << "Gem::Specification.new do |s|"
|
result << "Gem::Specification.new do |s|"
|
||||||
|
|
||||||
|
@ -2259,6 +2247,13 @@ class Gem::Specification
|
||||||
"#<Gem::Specification name=#{@name} version=#{@version}>"
|
"#<Gem::Specification name=#{@name} version=#{@version}>"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Returns self
|
||||||
|
|
||||||
|
def to_spec
|
||||||
|
self
|
||||||
|
end
|
||||||
|
|
||||||
def to_yaml(opts = {}) # :nodoc:
|
def to_yaml(opts = {}) # :nodoc:
|
||||||
if YAML.const_defined?(:ENGINE) && !YAML::ENGINE.syck? then
|
if YAML.const_defined?(:ENGINE) && !YAML::ENGINE.syck? then
|
||||||
# Because the user can switch the YAML engine behind our
|
# Because the user can switch the YAML engine behind our
|
||||||
|
@ -2559,11 +2554,6 @@ class Gem::Specification
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def default_gem?
|
|
||||||
loaded_from &&
|
|
||||||
File.dirname(loaded_from) == self.class.default_specifications_dir
|
|
||||||
end
|
|
||||||
|
|
||||||
extend Gem::Deprecate
|
extend Gem::Deprecate
|
||||||
|
|
||||||
# TODO:
|
# TODO:
|
||||||
|
|
112
lib/rubygems/stub_specification.rb
Normal file
112
lib/rubygems/stub_specification.rb
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
module Gem
|
||||||
|
# Gem::StubSpecification reads the stub: line from the gemspec
|
||||||
|
# This prevents us having to eval the entire gemspec in order to
|
||||||
|
# find out certain information.
|
||||||
|
class StubSpecification < BasicSpecification
|
||||||
|
# :nodoc:
|
||||||
|
PREFIX = "# stub: "
|
||||||
|
|
||||||
|
# :nodoc:
|
||||||
|
class StubLine
|
||||||
|
attr_reader :parts
|
||||||
|
|
||||||
|
def initialize(data)
|
||||||
|
@parts = data[PREFIX.length..-1].split(" ")
|
||||||
|
end
|
||||||
|
|
||||||
|
def name
|
||||||
|
@parts[0]
|
||||||
|
end
|
||||||
|
|
||||||
|
def version
|
||||||
|
Gem::Version.new @parts[1]
|
||||||
|
end
|
||||||
|
|
||||||
|
def platform
|
||||||
|
Gem::Platform.new @parts[2]
|
||||||
|
end
|
||||||
|
|
||||||
|
def require_paths
|
||||||
|
@parts[3..-1].join(" ").split("\0")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(filename)
|
||||||
|
self.filename = filename
|
||||||
|
@data = nil
|
||||||
|
@spec = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Name of the gem
|
||||||
|
|
||||||
|
def name
|
||||||
|
@name ||= data.name
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Version of the gem
|
||||||
|
|
||||||
|
def version
|
||||||
|
@version ||= data.version
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Platform of the gem
|
||||||
|
|
||||||
|
def platform
|
||||||
|
@platform ||= data.platform
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Require paths of the gem
|
||||||
|
|
||||||
|
def require_paths
|
||||||
|
@require_paths ||= data.require_paths
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# The full Gem::Specification for this gem, loaded from evalling its gemspec
|
||||||
|
|
||||||
|
def to_spec
|
||||||
|
@spec ||= Gem::Specification.load(filename)
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# True when this gem has been activated
|
||||||
|
|
||||||
|
def activated?
|
||||||
|
loaded = Gem.loaded_specs[name]
|
||||||
|
loaded && loaded.version == version
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Is this StubSpecification valid? i.e. have we found a stub line, OR does
|
||||||
|
# the filename contain a valid gemspec?
|
||||||
|
|
||||||
|
def valid?
|
||||||
|
data
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
##
|
||||||
|
# If the gemspec contains a stubline, returns a StubLine instance. Otherwise
|
||||||
|
# returns the full Gem::Specification.
|
||||||
|
|
||||||
|
def data
|
||||||
|
unless @data
|
||||||
|
File.open(filename, "r:UTF-8:-") do |file|
|
||||||
|
begin
|
||||||
|
file.readline # discard encoding line
|
||||||
|
stubline = file.readline.chomp
|
||||||
|
@data = StubLine.new(stubline) if stubline.start_with?(PREFIX)
|
||||||
|
rescue EOFError
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@data ||= to_spec
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -78,6 +78,23 @@ end
|
||||||
|
|
||||||
class Gem::TestCase < MiniTest::Unit::TestCase
|
class Gem::TestCase < MiniTest::Unit::TestCase
|
||||||
|
|
||||||
|
def assert_activate expected, *specs
|
||||||
|
specs.each do |spec|
|
||||||
|
case spec
|
||||||
|
when String then
|
||||||
|
Gem::Specification.find_by_name(spec).activate
|
||||||
|
when Gem::Specification then
|
||||||
|
spec.activate
|
||||||
|
else
|
||||||
|
flunk spec.inspect
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
loaded = Gem.loaded_specs.values.map(&:full_name)
|
||||||
|
|
||||||
|
assert_equal expected.sort, loaded.sort if expected
|
||||||
|
end
|
||||||
|
|
||||||
# TODO: move to minitest
|
# TODO: move to minitest
|
||||||
def assert_path_exists path, msg = nil
|
def assert_path_exists path, msg = nil
|
||||||
msg = message(msg) { "Expected path '#{path}' to exist" }
|
msg = message(msg) { "Expected path '#{path}' to exist" }
|
||||||
|
@ -200,6 +217,7 @@ class Gem::TestCase < MiniTest::Unit::TestCase
|
||||||
|
|
||||||
@gemhome = File.join @tempdir, 'gemhome'
|
@gemhome = File.join @tempdir, 'gemhome'
|
||||||
@userhome = File.join @tempdir, 'userhome'
|
@userhome = File.join @tempdir, 'userhome'
|
||||||
|
ENV["GEM_SPEC_CACHE"] = File.join @tempdir, 'spec_cache'
|
||||||
|
|
||||||
@orig_ruby = if ENV['RUBY'] then
|
@orig_ruby = if ENV['RUBY'] then
|
||||||
ruby = Gem.instance_variable_get :@ruby
|
ruby = Gem.instance_variable_get :@ruby
|
||||||
|
@ -221,6 +239,9 @@ class Gem::TestCase < MiniTest::Unit::TestCase
|
||||||
FileUtils.mkdir_p @gemhome
|
FileUtils.mkdir_p @gemhome
|
||||||
FileUtils.mkdir_p @userhome
|
FileUtils.mkdir_p @userhome
|
||||||
|
|
||||||
|
@orig_gem_private_key_passphrase = ENV['GEM_PRIVATE_KEY_PASSPHRASE']
|
||||||
|
ENV['GEM_PRIVATE_KEY_PASSPHRASE'] = PRIVATE_KEY_PASSPHRASE
|
||||||
|
|
||||||
@default_dir = File.join @tempdir, 'default'
|
@default_dir = File.join @tempdir, 'default'
|
||||||
@default_spec_dir = File.join @default_dir, "specifications", "default"
|
@default_spec_dir = File.join @default_dir, "specifications", "default"
|
||||||
Gem.instance_variable_set :@default_dir, @default_dir
|
Gem.instance_variable_set :@default_dir, @default_dir
|
||||||
|
@ -266,39 +287,6 @@ class Gem::TestCase < MiniTest::Unit::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
@marshal_version = "#{Marshal::MAJOR_VERSION}.#{Marshal::MINOR_VERSION}"
|
@marshal_version = "#{Marshal::MAJOR_VERSION}.#{Marshal::MINOR_VERSION}"
|
||||||
|
|
||||||
# TODO: move to installer test cases
|
|
||||||
Gem.post_build_hooks.clear
|
|
||||||
Gem.post_install_hooks.clear
|
|
||||||
Gem.done_installing_hooks.clear
|
|
||||||
Gem.post_reset_hooks.clear
|
|
||||||
Gem.post_uninstall_hooks.clear
|
|
||||||
Gem.pre_install_hooks.clear
|
|
||||||
Gem.pre_reset_hooks.clear
|
|
||||||
Gem.pre_uninstall_hooks.clear
|
|
||||||
|
|
||||||
# TODO: move to installer test cases
|
|
||||||
Gem.post_build do |installer|
|
|
||||||
@post_build_hook_arg = installer
|
|
||||||
true
|
|
||||||
end
|
|
||||||
|
|
||||||
Gem.post_install do |installer|
|
|
||||||
@post_install_hook_arg = installer
|
|
||||||
end
|
|
||||||
|
|
||||||
Gem.post_uninstall do |uninstaller|
|
|
||||||
@post_uninstall_hook_arg = uninstaller
|
|
||||||
end
|
|
||||||
|
|
||||||
Gem.pre_install do |installer|
|
|
||||||
@pre_install_hook_arg = installer
|
|
||||||
true
|
|
||||||
end
|
|
||||||
|
|
||||||
Gem.pre_uninstall do |uninstaller|
|
|
||||||
@pre_uninstall_hook_arg = uninstaller
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -332,6 +320,47 @@ class Gem::TestCase < MiniTest::Unit::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
Gem.instance_variable_set :@default_dir, nil
|
Gem.instance_variable_set :@default_dir, nil
|
||||||
|
|
||||||
|
ENV['GEM_PRIVATE_KEY_PASSPHRASE'] = @orig_gem_private_key_passphrase
|
||||||
|
|
||||||
|
Gem::Specification._clear_load_cache
|
||||||
|
end
|
||||||
|
|
||||||
|
def common_installer_setup
|
||||||
|
common_installer_teardown
|
||||||
|
|
||||||
|
Gem.post_build do |installer|
|
||||||
|
@post_build_hook_arg = installer
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
Gem.post_install do |installer|
|
||||||
|
@post_install_hook_arg = installer
|
||||||
|
end
|
||||||
|
|
||||||
|
Gem.post_uninstall do |uninstaller|
|
||||||
|
@post_uninstall_hook_arg = uninstaller
|
||||||
|
end
|
||||||
|
|
||||||
|
Gem.pre_install do |installer|
|
||||||
|
@pre_install_hook_arg = installer
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
Gem.pre_uninstall do |uninstaller|
|
||||||
|
@pre_uninstall_hook_arg = uninstaller
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def common_installer_teardown
|
||||||
|
Gem.post_build_hooks.clear
|
||||||
|
Gem.post_install_hooks.clear
|
||||||
|
Gem.done_installing_hooks.clear
|
||||||
|
Gem.post_reset_hooks.clear
|
||||||
|
Gem.post_uninstall_hooks.clear
|
||||||
|
Gem.pre_install_hooks.clear
|
||||||
|
Gem.pre_reset_hooks.clear
|
||||||
|
Gem.pre_uninstall_hooks.clear
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -560,6 +589,21 @@ class Gem::TestCase < MiniTest::Unit::TestCase
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def loaded_spec_names
|
||||||
|
Gem.loaded_specs.values.map(&:full_name).sort
|
||||||
|
end
|
||||||
|
|
||||||
|
def unresolved_names
|
||||||
|
Gem::Specification.unresolved_deps.values.map(&:to_s).sort
|
||||||
|
end
|
||||||
|
|
||||||
|
def save_loaded_features
|
||||||
|
old_loaded_features = $LOADED_FEATURES.dup
|
||||||
|
yield
|
||||||
|
ensure
|
||||||
|
$LOADED_FEATURES.replace old_loaded_features
|
||||||
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# Create a new spec (or gem if passed an array of files) and set it
|
# Create a new spec (or gem if passed an array of files) and set it
|
||||||
# up properly. Use this instead of util_spec and util_gem.
|
# up properly. Use this instead of util_spec and util_gem.
|
||||||
|
@ -1005,6 +1049,24 @@ Also, a list:
|
||||||
Gem::Dependency.new name, *requirements
|
Gem::Dependency.new name, *requirements
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Constructs a Gem::DependencyResolver::DependencyRequest from a
|
||||||
|
# Gem::Dependency +dep+, a +from_name+ and +from_version+ requesting the
|
||||||
|
# dependency and a +parent+ DependencyRequest
|
||||||
|
|
||||||
|
def dependency_request dep, from_name, from_version, parent = nil
|
||||||
|
remote = Gem::Source.new @uri
|
||||||
|
|
||||||
|
parent ||= Gem::DependencyResolver::DependencyRequest.new \
|
||||||
|
dep, nil
|
||||||
|
|
||||||
|
spec = Gem::DependencyResolver::IndexSpecification.new \
|
||||||
|
nil, from_name, from_version, remote, Gem::Platform::RUBY
|
||||||
|
activation = Gem::DependencyResolver::ActivationRequest.new spec, parent
|
||||||
|
|
||||||
|
Gem::DependencyResolver::DependencyRequest.new dep, activation
|
||||||
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# Constructs a new Gem::Requirement.
|
# Constructs a new Gem::Requirement.
|
||||||
|
|
||||||
|
@ -1074,18 +1136,18 @@ Also, a list:
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# Loads an RSA private key named +key_name+ in <tt>test/rubygems/</tt>
|
# Loads an RSA private key named +key_name+ with +passphrase+ in <tt>test/rubygems/</tt>
|
||||||
|
|
||||||
def self.load_key key_name
|
def self.load_key key_name, passphrase = nil
|
||||||
key_file = key_path key_name
|
key_file = key_path key_name
|
||||||
|
|
||||||
key = File.read key_file
|
key = File.read key_file
|
||||||
|
|
||||||
OpenSSL::PKey::RSA.new key
|
OpenSSL::PKey::RSA.new key, passphrase
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# Returns the path tot he key named +key_name+ from <tt>test/rubygems</tt>
|
# Returns the path to the key named +key_name+ from <tt>test/rubygems</tt>
|
||||||
|
|
||||||
def self.key_path key_name
|
def self.key_path key_name
|
||||||
File.expand_path "../../../test/rubygems/#{key_name}_key.pem", __FILE__
|
File.expand_path "../../../test/rubygems/#{key_name}_key.pem", __FILE__
|
||||||
|
@ -1094,17 +1156,24 @@ Also, a list:
|
||||||
# :stopdoc:
|
# :stopdoc:
|
||||||
# only available in RubyGems tests
|
# only available in RubyGems tests
|
||||||
|
|
||||||
begin
|
PRIVATE_KEY_PASSPHRASE = 'Foo bar'
|
||||||
PRIVATE_KEY = load_key 'private'
|
|
||||||
PRIVATE_KEY_PATH = key_path 'private'
|
|
||||||
PUBLIC_KEY = PRIVATE_KEY.public_key
|
|
||||||
|
|
||||||
PUBLIC_CERT = load_cert 'public'
|
begin
|
||||||
PUBLIC_CERT_PATH = cert_path 'public'
|
PRIVATE_KEY = load_key 'private'
|
||||||
|
PRIVATE_KEY_PATH = key_path 'private'
|
||||||
|
|
||||||
|
# ENCRYPTED_PRIVATE_KEY is PRIVATE_KEY encrypted with PRIVATE_KEY_PASSPHRASE
|
||||||
|
ENCRYPTED_PRIVATE_KEY = load_key 'encrypted_private', PRIVATE_KEY_PASSPHRASE
|
||||||
|
ENCRYPTED_PRIVATE_KEY_PATH = key_path 'encrypted_private'
|
||||||
|
|
||||||
|
PUBLIC_KEY = PRIVATE_KEY.public_key
|
||||||
|
|
||||||
|
PUBLIC_CERT = load_cert 'public'
|
||||||
|
PUBLIC_CERT_PATH = cert_path 'public'
|
||||||
rescue Errno::ENOENT
|
rescue Errno::ENOENT
|
||||||
PRIVATE_KEY = nil
|
PRIVATE_KEY = nil
|
||||||
PUBLIC_KEY = nil
|
PUBLIC_KEY = nil
|
||||||
PUBLIC_CERT = nil
|
PUBLIC_CERT = nil
|
||||||
end
|
end if defined?(OpenSSL::SSL)
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -43,14 +43,15 @@ class Gem::Uninstaller
|
||||||
|
|
||||||
def initialize(gem, options = {})
|
def initialize(gem, options = {})
|
||||||
# TODO document the valid options
|
# TODO document the valid options
|
||||||
@gem = gem
|
@gem = gem
|
||||||
@version = options[:version] || Gem::Requirement.default
|
@version = options[:version] || Gem::Requirement.default
|
||||||
@gem_home = File.expand_path(options[:install_dir] || Gem.dir)
|
@gem_home = File.expand_path(options[:install_dir] || Gem.dir)
|
||||||
@force_executables = options[:executables]
|
@force_executables = options[:executables]
|
||||||
@force_all = options[:all]
|
@force_all = options[:all]
|
||||||
@force_ignore = options[:ignore]
|
@force_ignore = options[:ignore]
|
||||||
@bin_dir = options[:bin_dir]
|
@bin_dir = options[:bin_dir]
|
||||||
@format_executable = options[:format_executable]
|
@format_executable = options[:format_executable]
|
||||||
|
@abort_on_dependent = options[:abort_on_dependent]
|
||||||
|
|
||||||
# Indicate if development dependencies should be checked when
|
# Indicate if development dependencies should be checked when
|
||||||
# uninstalling. (default: false)
|
# uninstalling. (default: false)
|
||||||
|
@ -143,7 +144,7 @@ class Gem::Uninstaller
|
||||||
@spec = spec
|
@spec = spec
|
||||||
|
|
||||||
unless dependencies_ok? spec
|
unless dependencies_ok? spec
|
||||||
unless ask_if_ok(spec)
|
if abort_on_dependent? || !ask_if_ok(spec)
|
||||||
raise Gem::DependencyRemovalException,
|
raise Gem::DependencyRemovalException,
|
||||||
"Uninstallation aborted due to dependent gem(s)"
|
"Uninstallation aborted due to dependent gem(s)"
|
||||||
end
|
end
|
||||||
|
@ -290,6 +291,10 @@ class Gem::Uninstaller
|
||||||
deplist.ok_to_remove?(spec.full_name, @check_dev)
|
deplist.ok_to_remove?(spec.full_name, @check_dev)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def abort_on_dependent?
|
||||||
|
@abort_on_dependent
|
||||||
|
end
|
||||||
|
|
||||||
def ask_if_ok(spec)
|
def ask_if_ok(spec)
|
||||||
msg = ['']
|
msg = ['']
|
||||||
msg << 'You have requested to uninstall the gem:'
|
msg << 'You have requested to uninstall the gem:'
|
||||||
|
|
39
lib/rubygems/uri_formatter.rb
Normal file
39
lib/rubygems/uri_formatter.rb
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
require 'uri'
|
||||||
|
|
||||||
|
class Gem::UriFormatter
|
||||||
|
attr_reader :uri
|
||||||
|
|
||||||
|
def initialize uri
|
||||||
|
@uri = uri
|
||||||
|
end
|
||||||
|
|
||||||
|
def escape
|
||||||
|
return unless @uri
|
||||||
|
escaper.escape @uri
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Normalize the URI by adding "http://" if it is missing.
|
||||||
|
|
||||||
|
def normalize
|
||||||
|
(@uri =~ /^(https?|ftp|file):/i) ? @uri : "http://#{@uri}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def unescape
|
||||||
|
return unless @uri
|
||||||
|
escaper.unescape @uri
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def escaper
|
||||||
|
@uri_parser ||=
|
||||||
|
begin
|
||||||
|
URI::Parser.new
|
||||||
|
rescue NameError
|
||||||
|
URI
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
44
lib/rubygems/util/list.rb
Normal file
44
lib/rubygems/util/list.rb
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
module Gem
|
||||||
|
List = Struct.new(:value, :tail)
|
||||||
|
|
||||||
|
class List
|
||||||
|
def each
|
||||||
|
n = self
|
||||||
|
while n
|
||||||
|
yield n.value
|
||||||
|
n = n.tail
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_a
|
||||||
|
ary = []
|
||||||
|
n = self
|
||||||
|
while n
|
||||||
|
ary.unshift n.value
|
||||||
|
n = n.tail
|
||||||
|
end
|
||||||
|
|
||||||
|
ary
|
||||||
|
end
|
||||||
|
|
||||||
|
def find
|
||||||
|
n = self
|
||||||
|
while n
|
||||||
|
v = n.value
|
||||||
|
return v if yield(v)
|
||||||
|
n = n.tail
|
||||||
|
end
|
||||||
|
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def prepend(value)
|
||||||
|
List.new value, self
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.prepend(list, value)
|
||||||
|
return List.new(value) unless list
|
||||||
|
List.new value, list
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -147,13 +147,16 @@ class Gem::Version
|
||||||
|
|
||||||
# FIX: These are only used once, in .correct?. Do they deserve to be
|
# FIX: These are only used once, in .correct?. Do they deserve to be
|
||||||
# constants?
|
# constants?
|
||||||
VERSION_PATTERN = '[0-9]+(\.[0-9a-zA-Z]+)*' # :nodoc:
|
VERSION_PATTERN = '[0-9]+(\.[0-9a-zA-Z]+)*(-[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?' # :nodoc:
|
||||||
ANCHORED_VERSION_PATTERN = /\A\s*(#{VERSION_PATTERN})*\s*\z/ # :nodoc:
|
ANCHORED_VERSION_PATTERN = /\A\s*(#{VERSION_PATTERN})*\s*\z/ # :nodoc:
|
||||||
|
|
||||||
##
|
##
|
||||||
# A string representation of this Version.
|
# A string representation of this Version.
|
||||||
|
|
||||||
attr_reader :version
|
def version
|
||||||
|
@version.dup
|
||||||
|
end
|
||||||
|
|
||||||
alias to_s version
|
alias to_s version
|
||||||
|
|
||||||
##
|
##
|
||||||
|
@ -183,6 +186,12 @@ class Gem::Version
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@@all = {}
|
||||||
|
|
||||||
|
def self.new version
|
||||||
|
@@all[version] ||= super
|
||||||
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
# Constructs a Version from the +version+ string. A version string is a
|
# Constructs a Version from the +version+ string. A version string is a
|
||||||
# series of digits or ASCII letters separated by dots.
|
# series of digits or ASCII letters separated by dots.
|
||||||
|
@ -191,7 +200,8 @@ class Gem::Version
|
||||||
raise ArgumentError, "Malformed version number string #{version}" unless
|
raise ArgumentError, "Malformed version number string #{version}" unless
|
||||||
self.class.correct?(version)
|
self.class.correct?(version)
|
||||||
|
|
||||||
@version = version.to_s.dup.strip
|
@version = version.to_s.strip.gsub("-",".pre.")
|
||||||
|
@segments = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|
|
@ -42,6 +42,7 @@ module Gem::VersionOption
|
||||||
add_option("--[no-]prerelease",
|
add_option("--[no-]prerelease",
|
||||||
"Allow prerelease versions of a gem", *wrap) do |value, options|
|
"Allow prerelease versions of a gem", *wrap) do |value, options|
|
||||||
options[:prerelease] = value
|
options[:prerelease] = value
|
||||||
|
options[:explicit_prerelease] = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -50,14 +51,19 @@ module Gem::VersionOption
|
||||||
|
|
||||||
def add_version_option(task = command, *wrap)
|
def add_version_option(task = command, *wrap)
|
||||||
OptionParser.accept Gem::Requirement do |value|
|
OptionParser.accept Gem::Requirement do |value|
|
||||||
Gem::Requirement.new value
|
Gem::Requirement.new(*value.split(/\s*,\s*/))
|
||||||
end
|
end
|
||||||
|
|
||||||
add_option('-v', '--version VERSION', Gem::Requirement,
|
add_option('-v', '--version VERSION', Gem::Requirement,
|
||||||
"Specify version of gem to #{task}", *wrap) do
|
"Specify version of gem to #{task}", *wrap) do
|
||||||
|value, options|
|
|value, options|
|
||||||
options[:version] = value
|
options[:version] = value
|
||||||
options[:prerelease] = true if value.prerelease?
|
|
||||||
|
explicit_prerelease_set = !options[:explicit_prerelease].nil?
|
||||||
|
options[:explicit_prerelease] = false unless explicit_prerelease_set
|
||||||
|
|
||||||
|
options[:prerelease] = value.prerelease? unless
|
||||||
|
options[:explicit_prerelease]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -43,3 +43,26 @@ ySjIblqVQkPuzebv3Ror6ZnVDukn96Mg7kP4u6zgxOeqlJGRe1M949SS9Vudjl8X
|
||||||
SF4aZUUB9pQGhsqQJVqaz2OlhGOp9D0q54xko/rekjAIcuDjl1mdX4F2WRrzpUmZ
|
SF4aZUUB9pQGhsqQJVqaz2OlhGOp9D0q54xko/rekjAIcuDjl1mdX4F2WRrzpUmZ
|
||||||
uY/bPeOBYiVsOYVe
|
uY/bPeOBYiVsOYVe
|
||||||
-----END CERTIFICATE-----
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDtTCCAp2gAwIBAgIJANz6ehBcVuuiMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
|
||||||
|
BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
|
||||||
|
aWRnaXRzIFB0eSBMdGQwHhcNMTMwNTAxMTQ0NTQxWhcNMjMwMzEwMTQ0NTQxWjBF
|
||||||
|
MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50
|
||||||
|
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
|
||||||
|
CgKCAQEAzlpZwhEYoALOEKU4lmMw5l3YI/gadzDOoELtdcidvVvovKK8IIOTDwbA
|
||||||
|
3XcjwV0UPGEPOK4Uk1aD0EKkOQVg8ivSre2a3FFGffs2kXck+doJMzAA+pf8tvFk
|
||||||
|
QsETVOurOp74GN+er2xbbRSDVxQKq6d+QTe1E60btyXQS5M1Nt5SvLn8dazZJgvv
|
||||||
|
3yzJQ1IOQl+xeEO0WVVhPIx5Mx3VtjjcDyl8aewPkYkzia6UOrAyQZnl5sIzWGOb
|
||||||
|
kYKCNeKjTPepzlbMx0dN6jBupPYGNB+4FYY9GezInjGbRP5np5382wd3EWwsVzic
|
||||||
|
Nau8kXHTL2r7GzNvoy0p//iPCqx9FQIDAQABo4GnMIGkMB0GA1UdDgQWBBS7B027
|
||||||
|
H/ZIkW3ngm1SrR0X/aTCwDB1BgNVHSMEbjBsgBS7B027H/ZIkW3ngm1SrR0X/aTC
|
||||||
|
wKFJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNV
|
||||||
|
BAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJANz6ehBcVuuiMAwGA1UdEwQF
|
||||||
|
MAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAC0glUrUiylTfuOWlwkQvi74oiYC5CzW
|
||||||
|
Jfusg6o/Gg1XEuJhaHiYMsK/do16gSc6Za3934rHQbYu3mesyFkCWF9kD4J6/hEO
|
||||||
|
OQL8xmmgN7wS6GXy6oIODpny0MgnFrV4gd1aEx69NIfL/wXaM8Gw2sj1TnuGLs8+
|
||||||
|
HFmWLRRH3WSR7ZLnqYzPVJwhHu8vtZBL9HZk1J6xyq00Nwi2Cz5WdiHamgaza3TS
|
||||||
|
OgBdWwDeSClwhrTJni4d30dbq+eNMByIZ7QNGBQivpFzDxeNV/2UBrTU0CilKG5Q
|
||||||
|
j7ZwknfKeA4xUTd8TMK3vKab5JJCfjbXOTHZQsYUcEEGSjOMS8/YVQs=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
49
test/rubygems/client.pem
Normal file
49
test/rubygems/client.pem
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDgTCCAmmgAwIBAgICEAIwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQVUx
|
||||||
|
EzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMg
|
||||||
|
UHR5IEx0ZDAeFw0xMzA1MDExNTAxMzFaFw0yMzAzMTAxNTAxMzFaMEUxCzAJBgNV
|
||||||
|
BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
|
||||||
|
aWRnaXRzIFB0eSBMdGQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCw
|
||||||
|
+lcrpdWcloQCgAlxcM3GjvBxZ3yjzi6SgXKRBSi54i0J1LXxznWKcJ5P/O1+j+7i
|
||||||
|
LjHK+OWqsa0+EbKTwSu+0tx20h0z++YJF9GWEoCwT5aH1kor/0+EQLgYnxBaF8GC
|
||||||
|
2xAbkRkWmbSu2aLDIey3lg7lqAazYqdS2wH0UjSDjFKDLxz9LwpfFm0yGL3DgwLW
|
||||||
|
+dobYkgt1A6F/8Pz6D2FjwYKcM8JE6w7KJSJDUvXcv2E18wmhZ/qF/MtFAF4coB1
|
||||||
|
f5ALnz8YqY6eyDF5aY/VfaHZvXdirLlMH6/miie9GBVMnJWF0ah5ssbsMvcpmnDJ
|
||||||
|
qkiYju2e1oLFEE7zztU/AgMBAAGjezB5MAkGA1UdEwQCMAAwLAYJYIZIAYb4QgEN
|
||||||
|
BB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBTcOELj
|
||||||
|
hSUdiLrdRF3CFZDZkWaGzDAfBgNVHSMEGDAWgBS7B027H/ZIkW3ngm1SrR0X/aTC
|
||||||
|
wDANBgkqhkiG9w0BAQUFAAOCAQEAlQMzHlnT6L1qqA4hL6tABPbiMsVwXyKCcfNB
|
||||||
|
zBn82Wkxgbg7Mp31fbR6/qvGeXOtaX6IdPdgtVf8nh1NURk0MmFBP+gfnwfNBD+m
|
||||||
|
Q1cldDt9kY2LGIrPii40xbugF1/xqEYcZMgXU08aEvQ2IHX46J8wZoqMa2KhrU8/
|
||||||
|
mzY0F+UEFOGWtKDgUzz3dyBPsdzVrX+SXULwH0lqZX8Nsw5LyfrlVt3xQvS5Ogm4
|
||||||
|
kYlt8kqhF8lUS3WTbuADrIs3NaDPRWSs1iLRRFgosgUtHN7tkrkrVaHeBo0KbAJG
|
||||||
|
mMqtxSY0XZI9WBxffP9UtoY3EiTWNVWLtuCN3OSvryP6NDe4BA==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEowIBAAKCAQEAsPpXK6XVnJaEAoAJcXDNxo7wcWd8o84ukoFykQUoueItCdS1
|
||||||
|
8c51inCeT/ztfo/u4i4xyvjlqrGtPhGyk8ErvtLcdtIdM/vmCRfRlhKAsE+Wh9ZK
|
||||||
|
K/9PhEC4GJ8QWhfBgtsQG5EZFpm0rtmiwyHst5YO5agGs2KnUtsB9FI0g4xSgy8c
|
||||||
|
/S8KXxZtMhi9w4MC1vnaG2JILdQOhf/D8+g9hY8GCnDPCROsOyiUiQ1L13L9hNfM
|
||||||
|
JoWf6hfzLRQBeHKAdX+QC58/GKmOnsgxeWmP1X2h2b13Yqy5TB+v5oonvRgVTJyV
|
||||||
|
hdGoebLG7DL3KZpwyapImI7tntaCxRBO887VPwIDAQABAoIBAFOpdG3gzlNg3/Ti
|
||||||
|
nBQxdEVqKwYhGs3A2UlOwl8F5lPBNPNRx9UQeYZBaMV9VrQezJnFpqpB8Sg5KCGQ
|
||||||
|
ci/hAJIL0kalW0LI0Nz5ko10H7u5U/rQ9W1JG0j041JYV32Pf14husKdXBPQA5co
|
||||||
|
sQW30tSSrmYogUpp15mWiJz8A3EvqiCTlQv5JwwMFGnjVl8+HNfuLghK/vqY/Eb9
|
||||||
|
YmwTKxPFejqN7E0Mud2ylNiuPTSLwBy8UvV9uxOlDc6lMyZjVRO0woiEzrjw5dKF
|
||||||
|
yf5tUkICRcPkekcx+XtpGrCMlRLl770bZBZX+YNmbYXVWhFp09cNR+U0KZqPNcDp
|
||||||
|
jg73vXECgYEA3huOKzfHGt3qUdMlEHd1FvQhW9fYIrmUSnuVYQJOnY8lFfKfmrOH
|
||||||
|
gpwOIHDNiVHYlhAJaNocCLYx4hWHgZXarY7NKxmlY2+Vp8mcCIf2Cw3Kr/sFklUJ
|
||||||
|
KpiRxqEPGR7U4C/E31kkH/C+w7m9Zh3ndhltU2Pki9/Eq0lk8YClMMkCgYEAy/vU
|
||||||
|
jxzviIk8bll5uCIuXJyCfao7ywaZABbL6a20kdVGKrHj57O/OJ2WZVwBihhB7OS+
|
||||||
|
QsKC/J8LrUJkobOFtQvQ8O23uep5rB6kqCkXsXCG4SCl2L5xZySBp/qhiqbuMwvp
|
||||||
|
EAWPSIA6UNoR0J2rDYVmq6jtY526wQf5ivE8IccCgYEAphfzJAyNH2FOZixArmS2
|
||||||
|
shiUjasG3UjsRRrP5YClK5wtPpF2m2if8KMkyUux2HvVPLr3XmqkxjsBaLFy6QwY
|
||||||
|
QOvmL9H45Tg/sP7KaXLLIw8IQLu2OezPcwQvF1u//6gXxyLR1bhClIQjFBjlMuUv
|
||||||
|
/xgasl6kPZlz6Cd1jkgGwEkCgYAI1IT2EQWZfn9cM4leXDRvk+LeN8FQ35897r6z
|
||||||
|
Be78JSRdcsfv3ssXU1MQXjQ+2x/3dkt6LltnPidOP8KFcXUHSlSoKVI7vRe5SLZO
|
||||||
|
BUFeUAW2tygWwt+73Eu0jtfxXZqQISLcq7DxLYPYvifpRPoDotO3+J8WIdzUwFig
|
||||||
|
GCNHPwKBgHqXOyRef7ykVUCptMf61/BvNU8NP1f9PkKQBMYQZC39UwqEQ675QBUh
|
||||||
|
hSG9t/kyc44zUVmBeKIlWHVyLQ83Dv+ennz/D9t7tstet0VMKvALNdiVT0sjFKN7
|
||||||
|
1VINygCeFkqrlTXlOwFcRSo1gHn3/JIrhSgRuYKHSf0GZOcN6d9l
|
||||||
|
-----END RSA PRIVATE KEY-----
|
30
test/rubygems/encrypted_private_key.pem
Normal file
30
test/rubygems/encrypted_private_key.pem
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
Proc-Type: 4,ENCRYPTED
|
||||||
|
DEK-Info: AES-256-CBC,1AC78C928C296A1D7C70D525B0F1051C
|
||||||
|
|
||||||
|
QL7dLRBmNpbSYsq+4niIdtP9LpJYQxG9tXaTKjQfgkYtLbDzhQMhxpKcJwCTtZUK
|
||||||
|
kJWxt7AOq8JwvvH69kp8fEULR5IThSPyFTjnLxtg1ZpMJZfHyfjAtveBO+Z4pCWA
|
||||||
|
Z6xrLI7RoFEVuSEgAkNYlb2JY4Z26nfCakvciEpHOkeYEYsneBQkr7Zf/IcKuKwd
|
||||||
|
wjOMzuLwvF3+cYaxcoHViRsuwyI6YrToJvPtin0xJlJczWalVSQciwjuDDGfjzow
|
||||||
|
J2o1O5UZc+VnEItpIbLWriRQPGP5ezOiTUCCxN+v/lignaeykfk+apAiKliKl2w6
|
||||||
|
eyxfBAIt8yE3RyhE3mX+AZN8sX+mfduEXCcAziZLSTYm3Lfq90eKGs+cUMFmwz1N
|
||||||
|
NvFVfIHpiRSzKlrJlvd38SRbSbQfvS2OEo+e0f4ZW7cKCXayczwF0gQQY9VZ23bn
|
||||||
|
Sk1CGuA2ugn+cd9T/yrSTtgz1EDpZxp7HYE242DiJb7wUY30nAqgYZ//ug6HGBJA
|
||||||
|
OYQldiinj6lWr0i/jEdKknUKIZTQQ+aH0c+hvbsagQRoVFZUCG6RFbKtWHRxL/a0
|
||||||
|
teMT1SFeab6pulh3/VfdLzdBKVvHaY3bpujAmOg4lq0O2MQWMGvIPdso9iTBoAJm
|
||||||
|
TrLR/YO0RfvnfC0uM2YHXcLlhgsBUiGQUNnk6EZ5qK2aEiZuaCecpsCYEt2uhO9W
|
||||||
|
HF7CpAh3T1OUY33HEw/4KdvMG+5uwK+4D1JatKHsU0Umpp2+2C9T6W/iSLXndg0L
|
||||||
|
Xr8NFu9ziXdEe4tZy/9VDo4QOnqFhSBXxkimGrdnUrbTxH4nwUzmv4VRnbAXTEJM
|
||||||
|
XkVat7zZ1dvUf+iJXiRxjo6BbwXtL6+ZmL1aYbnbN8HrQdhuFN/QD/OzhYj7f9Yn
|
||||||
|
sTSQUleAK1+sppcTs6tiEdxWBgnKUeQNCXEBXG4twy7rd2ymamvunBTaoywebcaG
|
||||||
|
RTnK8eyOkoDeVEFZx+EI2TrG2PaA0Zuq+7IYqID+6/asa4K/3J/ChXqjIAgqUcML
|
||||||
|
56DlF5DCTvaRRUwftARaOqJZ+VxoW62i30nP/oD35xh++Esf8YgxhPeg1Gjzozx7
|
||||||
|
ZC1GZ5f44EvDJyFlXUUNtNy3dC3cSdUUM6oYvDLrPI3wVEw3QgLUJ+Tc8lA5Gx7M
|
||||||
|
wW2i/Y6JqlVUabvkaKe4d+w8eo219Bnfo7D199TppbEXOob6AaC2CJranActTfrm
|
||||||
|
fFrWQKJrdWz1mWZT3efoBpxVAds8fYk2hNaXL6LQepOAF6ObbS4hHcRHbI7HIdVB
|
||||||
|
6GNUfVWlrISZ6thj84way/niR1ikXUFipN5gCRERc0+brXK4OCnksyLqYgvMI74Z
|
||||||
|
5lW8HfuX4FNp/Gd5uU+tbYnNy6nIqa8oZScLp0Kjg9tPKjjrDbZS2LJ8kxf7q9lb
|
||||||
|
YbxhzMy+uKwdmxIB4fKjWZTgPX4MwjA8FAaMncyvA64rxGnfyLExmOOZWSXqZQ8z
|
||||||
|
y+xoqA239Wob98mJn+oluneMKwSAM3ActGTmp5X5jHVk++yEcJN9uGYAa3UohKlm
|
||||||
|
/wgpQ79yfBywju2rZR0hQXN0ExBdE/UnJucJMv/iB5fxlkJlkNJPwFgq8iMbzQLu
|
||||||
|
-----END RSA PRIVATE KEY-----
|
49
test/rubygems/invalid_client.pem
Normal file
49
test/rubygems/invalid_client.pem
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIDgTCCAmmgAwIBAgICEAIwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCQVUx
|
||||||
|
EzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMg
|
||||||
|
UHR5IEx0ZDAeFw0xMzA1MDExNTAxMzFaFw0yMzAzMTAxNTAxMzFaMEUxCzAJBgNV
|
||||||
|
BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
|
||||||
|
aWRnaXRzIFB0eXXXdGQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCw
|
||||||
|
+lcrpdWcloQCgAlxcM3GjvBxZ3yjzi6SgXKRBSi54i0J1LXxznWKcJ5P/O1+j+7i
|
||||||
|
LjHK+OWqsa0+EbKTwSu+0tx20h0z++YJF9GWEoCwT5aH1kor/0+EQLgYnxBaF8GC
|
||||||
|
2xAbkRkWmbSu2aLDIey3lg7lqAazYqdS2wH0UjSDjFKDLxz9LwpfFm0yGL3DgwLW
|
||||||
|
+dobYkgt1A6F/8Pz6D2FjwYKcM8JE6w7KJSJDUvXcv2E18wmhZ/qF/MtFAF4coB1
|
||||||
|
f5ALnz8YqY6eyDF5aY/VfaHZvXdirLlMH6/miie9GBVMnJWF0ah5ssbsMvcpmnDJ
|
||||||
|
qkiYju2e1oLFEE7zztU/AgMBAAGjezB5MAkGA1UdEwQCMAAwLAYJYIZIAYb4QgEN
|
||||||
|
BB8WHU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBTcOELj
|
||||||
|
hSUdiLrdRF3CFZDZkWaGzDAfBgNVHSMEGDAWgBS7B027H/ZIkW3ngm1SrR0X/aTC
|
||||||
|
wDANBgkqhkiG9w0BAQUFAAOCAQEAlQMzHlnT6L1qqA4hL6tABPbiMsVwXyKCcfNB
|
||||||
|
zBn82Wkxgbg7Mp31fbR6/qvGeXOtaX6IdPdgtVf8nh1NURk0MmFBP+gfnwfNBD+m
|
||||||
|
Q1cldDt9kY2LGIrPii40xbugF1/xqEYcZMgXU08aEvQ2IHX46J8wZoqMa2KhrU8/
|
||||||
|
mzY0F+UEFOGWtKDgUzz3dyBPsdzVrX+SXULwH0lqZX8Nsw5LyfrlVt3xQvS5Ogm4
|
||||||
|
kYlt8kqhF8lUS3WTbuADrIs3NaDPRWSs1iLRRFgosgUtHN7tkrkrVaHeBo0KbAJG
|
||||||
|
mMqtxSY0XZI9WBxffP9UtoY3EiTWNVWLtuCN3OSvryP6NDe4BA==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
||||||
|
-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEowIBAAKCAQEAsPpXK6XVnJaEAoAJcXDNxo7wcWd8o84ukoFykQUoueItCdS1
|
||||||
|
8c51inCeT/ztfo/u4i4xyvjlqrGtPhGyk8ErvtLcdtIdM/vmCRfRlhKAsE+Wh9ZK
|
||||||
|
K/9PhEC4GJ8QWhfBgtsQG5EZFpm0rtmiwyHst5YO5agGs2KnUtsB9FI0g4xSgy8c
|
||||||
|
/S8KXxZtMhi9w4MC1vnaG2JILdQOhf/D8+g9hY8GCnDPCROsOyiUiQ1L13L9hNfM
|
||||||
|
JoWf6hfzLRQBeHKAdX+QC58/GKmOnsgxeWmP1X2h2b13Yqy5TB+v5oonvRgVTJyV
|
||||||
|
hdGoebLG7DL3KZpwyapImI7tntaCxRBO887VPwIDAQABAoIBAFOpdG3gzlNg3/Ti
|
||||||
|
nBQxdEVqKwYhGs3A2UlOwl8F5lPBNPNRx9UQeYZBaMV9VrQezJnFpqpB8Sg5KCGQ
|
||||||
|
ci/hAJIL0kalW0LI0Nz5ko10H7u5U/rQ9W1JG0j041JYV32Pf14husKdXBPQA5co
|
||||||
|
sQW30tSSrmYogUpp15mWiJz8A3EvqiCTlQv5JwwMFGnjVl8+HNfuLghK/vqY/Eb9
|
||||||
|
YmwTKxPFejqN7E0Mud2ylNiuPTSLwBy8UvV9uxOlDc6lMyZjVRO0woiEzrjw5dKF
|
||||||
|
yf5tUkICRcPkekcx+XtpGrCMlRLl770bZBZX+YNmbYXVWhFp09cNR+U0KZqPNcDp
|
||||||
|
jg73vXECgYEA3huOKzfHGt3qUdMlEHd1FvQhW9fYIrmUSnuVYQJOnY8lFfKfmrOH
|
||||||
|
gpwOIHDNiVHYlhAJaNocCLYx4hWHgZXarY7NKxmlY2+Vp8mcCIf2Cw3Kr/sFklUJ
|
||||||
|
KpiRxqEPGR7U4C/E31kkH/C+w7m9Zh3ndhltU2Pki9/Eq0lk8YClMMkCgYEAy/vU
|
||||||
|
jxzviIk8bll5uCIuXJyCfao7ywaZABbL6a20kdVGKrHj57O/OJ2WZVwBihhB7OS+
|
||||||
|
QsKC/J8LrUJkobOFtQvQ8O23uep5rB6kqCkXsXCG4SCl2L5xZySBp/qhiqbuMwvp
|
||||||
|
EAWPSIA6UNoR0J2rDYVmq6jtY526wQf5ivE8IccCgYEAphfzJAyNH2FOZixArmS2
|
||||||
|
shiUjasG3UjsRRrP5YClK5wtPpF2m2if8KMkyUux2HvVPLr3XmqkxjsBaLFy6QwY
|
||||||
|
QOvmL9H45Tg/sP7KaXLLIw8IQLu2OezPcwQvF1u//6gXxyLR1bhClIQjFBjlMuUv
|
||||||
|
/xgasl6kPZlz6Cd1jkgGwEkCgYAI1IT2EQWZfn9cM4leXDRvk+LeN8FQ35897r6z
|
||||||
|
Be78JSRdcsfv3ssXU1MQXjQ+2x/3dkt6LltnPidOP8KFcXUHSlSoKVI7vRe5SLZO
|
||||||
|
BUFeUAW2tygWwt+73Eu0jtfxXZqQISLcq7DxLYPYvifpRPoDotO3+J8WIdzUwFig
|
||||||
|
GCNHPwKBgHqXOyRef7ykVUCptMf61/BvNU8NP1f9PkKQBMYQZC39UwqEQ675QBUh
|
||||||
|
hSG9t/kyc44zUVmBeKIlWHVyLQ83Dv+ennz/D9t7tstet0VMKvALNdiVT0sjFKN7
|
||||||
|
1VINygCeFkqrlTXlOwFcRSo1gHn3/JIrhSgRuYKHSf0GZOcN6d9l
|
||||||
|
-----END RSA PRIVATE KEY-----
|
9
test/rubygems/specifications/bar-0.0.2.gemspec
Normal file
9
test/rubygems/specifications/bar-0.0.2.gemspec
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
|
Gem::Specification.new do |s|
|
||||||
|
s.name = "bar"
|
||||||
|
s.version = "0.0.2"
|
||||||
|
s.platform = "ruby"
|
||||||
|
s.require_paths = ["lib"]
|
||||||
|
s.summary = "A very bar gem"
|
||||||
|
end
|
BIN
test/rubygems/specifications/foo-0.0.1.gemspec
Normal file
BIN
test/rubygems/specifications/foo-0.0.1.gemspec
Normal file
Binary file not shown.
|
@ -15,6 +15,7 @@ class TestGem < Gem::TestCase
|
||||||
|
|
||||||
def setup
|
def setup
|
||||||
super
|
super
|
||||||
|
common_installer_setup
|
||||||
|
|
||||||
ENV.delete 'RUBYGEMS_GEMDEPS'
|
ENV.delete 'RUBYGEMS_GEMDEPS'
|
||||||
@additional = %w[a b].map { |d| File.join @tempdir, d }
|
@additional = %w[a b].map { |d| File.join @tempdir, d }
|
||||||
|
@ -22,119 +23,6 @@ class TestGem < Gem::TestCase
|
||||||
util_remove_interrupt_command
|
util_remove_interrupt_command
|
||||||
end
|
end
|
||||||
|
|
||||||
def assert_activate expected, *specs
|
|
||||||
specs.each do |spec|
|
|
||||||
case spec
|
|
||||||
when String then
|
|
||||||
Gem::Specification.find_by_name(spec).activate
|
|
||||||
when Gem::Specification then
|
|
||||||
spec.activate
|
|
||||||
else
|
|
||||||
flunk spec.inspect
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
loaded = Gem.loaded_specs.values.map(&:full_name)
|
|
||||||
|
|
||||||
assert_equal expected.sort, loaded.sort if expected
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_self_activate
|
|
||||||
foo = util_spec 'foo', '1'
|
|
||||||
|
|
||||||
assert_activate %w[foo-1], foo
|
|
||||||
end
|
|
||||||
|
|
||||||
def loaded_spec_names
|
|
||||||
Gem.loaded_specs.values.map(&:full_name).sort
|
|
||||||
end
|
|
||||||
|
|
||||||
def unresolved_names
|
|
||||||
Gem::Specification.unresolved_deps.values.map(&:to_s).sort
|
|
||||||
end
|
|
||||||
|
|
||||||
# TODO: move these to specification
|
|
||||||
def test_self_activate_via_require
|
|
||||||
a1 = new_spec "a", "1", "b" => "= 1"
|
|
||||||
b1 = new_spec "b", "1", nil, "lib/b/c.rb"
|
|
||||||
b2 = new_spec "b", "2", nil, "lib/b/c.rb"
|
|
||||||
|
|
||||||
install_specs a1, b1, b2
|
|
||||||
|
|
||||||
a1.activate
|
|
||||||
save_loaded_features do
|
|
||||||
require "b/c"
|
|
||||||
end
|
|
||||||
|
|
||||||
assert_equal %w(a-1 b-1), loaded_spec_names
|
|
||||||
end
|
|
||||||
|
|
||||||
# TODO: move these to specification
|
|
||||||
def test_self_activate_deep_unambiguous
|
|
||||||
a1 = new_spec "a", "1", "b" => "= 1"
|
|
||||||
b1 = new_spec "b", "1", "c" => "= 1"
|
|
||||||
b2 = new_spec "b", "2", "c" => "= 2"
|
|
||||||
c1 = new_spec "c", "1"
|
|
||||||
c2 = new_spec "c", "2"
|
|
||||||
|
|
||||||
install_specs a1, b1, b2, c1, c2
|
|
||||||
|
|
||||||
a1.activate
|
|
||||||
assert_equal %w(a-1 b-1 c-1), loaded_spec_names
|
|
||||||
end
|
|
||||||
|
|
||||||
def save_loaded_features
|
|
||||||
old_loaded_features = $LOADED_FEATURES.dup
|
|
||||||
yield
|
|
||||||
ensure
|
|
||||||
$LOADED_FEATURES.replace old_loaded_features
|
|
||||||
end
|
|
||||||
|
|
||||||
# TODO: move these to specification
|
|
||||||
def test_self_activate_ambiguous_direct
|
|
||||||
save_loaded_features do
|
|
||||||
a1 = new_spec "a", "1", "b" => "> 0"
|
|
||||||
b1 = new_spec("b", "1", { "c" => ">= 1" }, "lib/d.rb")
|
|
||||||
b2 = new_spec("b", "2", { "c" => ">= 2" }, "lib/d.rb")
|
|
||||||
c1 = new_spec "c", "1"
|
|
||||||
c2 = new_spec "c", "2"
|
|
||||||
|
|
||||||
Gem::Specification.reset
|
|
||||||
install_specs a1, b1, b2, c1, c2
|
|
||||||
|
|
||||||
a1.activate
|
|
||||||
assert_equal %w(a-1), loaded_spec_names
|
|
||||||
assert_equal ["b (> 0)"], unresolved_names
|
|
||||||
|
|
||||||
require "d"
|
|
||||||
|
|
||||||
assert_equal %w(a-1 b-2 c-2), loaded_spec_names
|
|
||||||
assert_equal [], unresolved_names
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# TODO: move these to specification
|
|
||||||
def test_self_activate_ambiguous_indirect
|
|
||||||
save_loaded_features do
|
|
||||||
a1 = new_spec "a", "1", "b" => "> 0"
|
|
||||||
b1 = new_spec "b", "1", "c" => ">= 1"
|
|
||||||
b2 = new_spec "b", "2", "c" => ">= 2"
|
|
||||||
c1 = new_spec "c", "1", nil, "lib/d.rb"
|
|
||||||
c2 = new_spec "c", "2", nil, "lib/d.rb"
|
|
||||||
|
|
||||||
install_specs a1, b1, b2, c1, c2
|
|
||||||
|
|
||||||
a1.activate
|
|
||||||
assert_equal %w(a-1), loaded_spec_names
|
|
||||||
assert_equal ["b (> 0)"], unresolved_names
|
|
||||||
|
|
||||||
require "d"
|
|
||||||
|
|
||||||
assert_equal %w(a-1 b-2 c-2), loaded_spec_names
|
|
||||||
assert_equal [], unresolved_names
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_self_finish_resolve
|
def test_self_finish_resolve
|
||||||
save_loaded_features do
|
save_loaded_features do
|
||||||
a1 = new_spec "a", "1", "b" => "> 0"
|
a1 = new_spec "a", "1", "b" => "> 0"
|
||||||
|
@ -157,36 +45,6 @@ class TestGem < Gem::TestCase
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_self_activate_via_require_wtf
|
|
||||||
save_loaded_features do
|
|
||||||
a1 = new_spec "a", "1", "b" => "> 0", "d" => "> 0" # this
|
|
||||||
b1 = new_spec "b", "1", { "c" => ">= 1" }, "lib/b.rb"
|
|
||||||
b2 = new_spec "b", "2", { "c" => ">= 2" }, "lib/b.rb" # this
|
|
||||||
c1 = new_spec "c", "1"
|
|
||||||
c2 = new_spec "c", "2" # this
|
|
||||||
d1 = new_spec "d", "1", { "c" => "< 2" }, "lib/d.rb"
|
|
||||||
d2 = new_spec "d", "2", { "c" => "< 2" }, "lib/d.rb" # this
|
|
||||||
|
|
||||||
install_specs a1, b1, b2, c1, c2, d1, d2
|
|
||||||
|
|
||||||
a1.activate
|
|
||||||
|
|
||||||
assert_equal %w(a-1), loaded_spec_names
|
|
||||||
assert_equal ["b (> 0)", "d (> 0)"], unresolved_names
|
|
||||||
|
|
||||||
require "b"
|
|
||||||
|
|
||||||
e = assert_raises Gem::LoadError do
|
|
||||||
require "d"
|
|
||||||
end
|
|
||||||
|
|
||||||
assert_equal "unable to find a version of 'd' to activate", e.message
|
|
||||||
|
|
||||||
assert_equal %w(a-1 b-2 c-2), loaded_spec_names
|
|
||||||
assert_equal ["d (> 0)"], unresolved_names
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_self_finish_resolve_wtf
|
def test_self_finish_resolve_wtf
|
||||||
save_loaded_features do
|
save_loaded_features do
|
||||||
a1 = new_spec "a", "1", "b" => "> 0", "d" => "> 0" # this
|
a1 = new_spec "a", "1", "b" => "> 0", "d" => "> 0" # this
|
||||||
|
@ -211,94 +69,6 @@ class TestGem < Gem::TestCase
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO: move these to specification
|
|
||||||
def test_self_activate_ambiguous_unrelated
|
|
||||||
save_loaded_features do
|
|
||||||
a1 = new_spec "a", "1", "b" => "> 0"
|
|
||||||
b1 = new_spec "b", "1", "c" => ">= 1"
|
|
||||||
b2 = new_spec "b", "2", "c" => ">= 2"
|
|
||||||
c1 = new_spec "c", "1"
|
|
||||||
c2 = new_spec "c", "2"
|
|
||||||
d1 = new_spec "d", "1", nil, "lib/d.rb"
|
|
||||||
|
|
||||||
install_specs a1, b1, b2, c1, c2, d1
|
|
||||||
|
|
||||||
a1.activate
|
|
||||||
assert_equal %w(a-1), loaded_spec_names
|
|
||||||
assert_equal ["b (> 0)"], unresolved_names
|
|
||||||
|
|
||||||
require "d"
|
|
||||||
|
|
||||||
assert_equal %w(a-1 d-1), loaded_spec_names
|
|
||||||
assert_equal ["b (> 0)"], unresolved_names
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# TODO: move these to specification
|
|
||||||
def test_self_activate_ambiguous_indirect_conflict
|
|
||||||
save_loaded_features do
|
|
||||||
a1 = new_spec "a", "1", "b" => "> 0"
|
|
||||||
a2 = new_spec "a", "2", "b" => "> 0"
|
|
||||||
b1 = new_spec "b", "1", "c" => ">= 1"
|
|
||||||
b2 = new_spec "b", "2", "c" => ">= 2"
|
|
||||||
c1 = new_spec "c", "1", nil, "lib/d.rb"
|
|
||||||
c2 = new_spec("c", "2", { "a" => "1" }, "lib/d.rb") # conflicts with a-2
|
|
||||||
|
|
||||||
install_specs a1, a2, b1, b2, c1, c2
|
|
||||||
|
|
||||||
a2.activate
|
|
||||||
assert_equal %w(a-2), loaded_spec_names
|
|
||||||
assert_equal ["b (> 0)"], unresolved_names
|
|
||||||
|
|
||||||
require "d"
|
|
||||||
|
|
||||||
assert_equal %w(a-2 b-1 c-1), loaded_spec_names
|
|
||||||
assert_equal [], unresolved_names
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# TODO: move these to specification
|
|
||||||
def test_require_already_activated
|
|
||||||
save_loaded_features do
|
|
||||||
a1 = new_spec "a", "1", nil, "lib/d.rb"
|
|
||||||
|
|
||||||
install_specs a1 # , a2, b1, b2, c1, c2
|
|
||||||
|
|
||||||
a1.activate
|
|
||||||
assert_equal %w(a-1), loaded_spec_names
|
|
||||||
assert_equal [], unresolved_names
|
|
||||||
|
|
||||||
assert require "d"
|
|
||||||
|
|
||||||
assert_equal %w(a-1), loaded_spec_names
|
|
||||||
assert_equal [], unresolved_names
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# TODO: move these to specification
|
|
||||||
def test_require_already_activated_indirect_conflict
|
|
||||||
save_loaded_features do
|
|
||||||
a1 = new_spec "a", "1", "b" => "> 0"
|
|
||||||
a2 = new_spec "a", "2", "b" => "> 0"
|
|
||||||
b1 = new_spec "b", "1", "c" => ">= 1"
|
|
||||||
b2 = new_spec "b", "2", "c" => ">= 2"
|
|
||||||
c1 = new_spec "c", "1", nil, "lib/d.rb"
|
|
||||||
c2 = new_spec("c", "2", { "a" => "1" }, "lib/d.rb") # conflicts with a-2
|
|
||||||
|
|
||||||
install_specs a1, a2, b1, b2, c1, c2
|
|
||||||
|
|
||||||
a1.activate
|
|
||||||
c1.activate
|
|
||||||
assert_equal %w(a-1 c-1), loaded_spec_names
|
|
||||||
assert_equal ["b (> 0)"], unresolved_names
|
|
||||||
|
|
||||||
assert require "d"
|
|
||||||
|
|
||||||
assert_equal %w(a-1 c-1), loaded_spec_names
|
|
||||||
assert_equal ["b (> 0)"], unresolved_names
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_require_missing
|
def test_require_missing
|
||||||
save_loaded_features do
|
save_loaded_features do
|
||||||
assert_raises ::LoadError do
|
assert_raises ::LoadError do
|
||||||
|
@ -321,221 +91,6 @@ class TestGem < Gem::TestCase
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO: move these to specification
|
|
||||||
def test_self_activate_loaded
|
|
||||||
foo = util_spec 'foo', '1'
|
|
||||||
|
|
||||||
assert foo.activate
|
|
||||||
refute foo.activate
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# [A] depends on
|
|
||||||
# [B] >= 1.0 (satisfied by 2.0)
|
|
||||||
# [C] depends on nothing
|
|
||||||
|
|
||||||
def test_self_activate_unrelated
|
|
||||||
a = util_spec 'a', '1.0', 'b' => '>= 1.0'
|
|
||||||
util_spec 'b', '1.0'
|
|
||||||
c = util_spec 'c', '1.0'
|
|
||||||
|
|
||||||
assert_activate %w[b-1.0 c-1.0 a-1.0], a, c, "b"
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# [A] depends on
|
|
||||||
# [B] >= 1.0 (satisfied by 2.0)
|
|
||||||
# [C] = 1.0 depends on
|
|
||||||
# [B] ~> 1.0
|
|
||||||
#
|
|
||||||
# and should resolve using b-1.0
|
|
||||||
# TODO: move these to specification
|
|
||||||
|
|
||||||
def test_self_activate_over
|
|
||||||
a = util_spec 'a', '1.0', 'b' => '>= 1.0', 'c' => '= 1.0'
|
|
||||||
util_spec 'b', '1.0'
|
|
||||||
util_spec 'b', '1.1'
|
|
||||||
util_spec 'b', '2.0'
|
|
||||||
util_spec 'c', '1.0', 'b' => '~> 1.0'
|
|
||||||
|
|
||||||
a.activate
|
|
||||||
|
|
||||||
assert_equal %w[a-1.0 c-1.0], loaded_spec_names
|
|
||||||
assert_equal ["b (>= 1.0, ~> 1.0)"], unresolved_names
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# [A] depends on
|
|
||||||
# [B] ~> 1.0 (satisfied by 1.1)
|
|
||||||
# [C] = 1.0 depends on
|
|
||||||
# [B] = 1.0
|
|
||||||
#
|
|
||||||
# and should resolve using b-1.0
|
|
||||||
#
|
|
||||||
# TODO: this is not under, but over... under would require depth
|
|
||||||
# first resolve through a dependency that is later pruned.
|
|
||||||
|
|
||||||
def test_self_activate_under
|
|
||||||
a, _ = util_spec 'a', '1.0', 'b' => '~> 1.0', 'c' => '= 1.0'
|
|
||||||
util_spec 'b', '1.0'
|
|
||||||
util_spec 'b', '1.1'
|
|
||||||
c, _ = util_spec 'c', '1.0', 'b' => '= 1.0'
|
|
||||||
|
|
||||||
assert_activate %w[b-1.0 c-1.0 a-1.0], a, c, "b"
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# [A1] depends on
|
|
||||||
# [B] > 0 (satisfied by 2.0)
|
|
||||||
# [B1] depends on
|
|
||||||
# [C] > 0 (satisfied by 1.0)
|
|
||||||
# [B2] depends on nothing!
|
|
||||||
# [C1] depends on nothing
|
|
||||||
|
|
||||||
def test_self_activate_dropped
|
|
||||||
a1, = util_spec 'a', '1', 'b' => nil
|
|
||||||
util_spec 'b', '1', 'c' => nil
|
|
||||||
util_spec 'b', '2'
|
|
||||||
util_spec 'c', '1'
|
|
||||||
|
|
||||||
assert_activate %w[b-2 a-1], a1, "b"
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# [A] depends on
|
|
||||||
# [B] >= 1.0 (satisfied by 1.1) depends on
|
|
||||||
# [Z]
|
|
||||||
# [C] >= 1.0 depends on
|
|
||||||
# [B] = 1.0
|
|
||||||
#
|
|
||||||
# and should backtrack to resolve using b-1.0, pruning Z from the
|
|
||||||
# resolve.
|
|
||||||
|
|
||||||
def test_self_activate_raggi_the_edgecase_generator
|
|
||||||
a, _ = util_spec 'a', '1.0', 'b' => '>= 1.0', 'c' => '>= 1.0'
|
|
||||||
util_spec 'b', '1.0'
|
|
||||||
util_spec 'b', '1.1', 'z' => '>= 1.0'
|
|
||||||
c, _ = util_spec 'c', '1.0', 'b' => '= 1.0'
|
|
||||||
|
|
||||||
assert_activate %w[b-1.0 c-1.0 a-1.0], a, c, "b"
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_self_activate_conflict
|
|
||||||
util_spec 'b', '1.0'
|
|
||||||
util_spec 'b', '2.0'
|
|
||||||
|
|
||||||
gem "b", "= 1.0"
|
|
||||||
|
|
||||||
assert_raises Gem::LoadError do
|
|
||||||
gem "b", "= 2.0"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# [A] depends on
|
|
||||||
# [C] = 1.0 depends on
|
|
||||||
# [B] = 2.0
|
|
||||||
# [B] ~> 1.0 (satisfied by 1.0)
|
|
||||||
|
|
||||||
def test_self_activate_checks_dependencies
|
|
||||||
a, _ = util_spec 'a', '1.0'
|
|
||||||
a.add_dependency 'c', '= 1.0'
|
|
||||||
a.add_dependency 'b', '~> 1.0'
|
|
||||||
|
|
||||||
util_spec 'b', '1.0'
|
|
||||||
util_spec 'b', '2.0'
|
|
||||||
c, _ = util_spec 'c', '1.0', 'b' => '= 2.0'
|
|
||||||
|
|
||||||
e = assert_raises Gem::LoadError do
|
|
||||||
assert_activate nil, a, c, "b"
|
|
||||||
end
|
|
||||||
|
|
||||||
expected = "can't satisfy 'b (~> 1.0)', already activated 'b-2.0'"
|
|
||||||
assert_equal expected, e.message
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# [A] depends on
|
|
||||||
# [B] ~> 1.0 (satisfied by 1.0)
|
|
||||||
# [C] = 1.0 depends on
|
|
||||||
# [B] = 2.0
|
|
||||||
|
|
||||||
def test_self_activate_divergent
|
|
||||||
a, _ = util_spec 'a', '1.0', 'b' => '~> 1.0', 'c' => '= 1.0'
|
|
||||||
util_spec 'b', '1.0'
|
|
||||||
util_spec 'b', '2.0'
|
|
||||||
c, _ = util_spec 'c', '1.0', 'b' => '= 2.0'
|
|
||||||
|
|
||||||
e = assert_raises Gem::LoadError do
|
|
||||||
assert_activate nil, a, c, "b"
|
|
||||||
end
|
|
||||||
|
|
||||||
assert_match(/Unable to activate c-1.0,/, e.message)
|
|
||||||
assert_match(/because b-1.0 conflicts with b .= 2.0/, e.message)
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# DOC
|
|
||||||
|
|
||||||
def test_self_activate_platform_alternate
|
|
||||||
@x1_m = util_spec 'x', '1' do |s|
|
|
||||||
s.platform = Gem::Platform.new %w[cpu my_platform 1]
|
|
||||||
end
|
|
||||||
|
|
||||||
@x1_o = util_spec 'x', '1' do |s|
|
|
||||||
s.platform = Gem::Platform.new %w[cpu other_platform 1]
|
|
||||||
end
|
|
||||||
|
|
||||||
@w1 = util_spec 'w', '1', 'x' => nil
|
|
||||||
|
|
||||||
util_set_arch 'cpu-my_platform1'
|
|
||||||
|
|
||||||
assert_activate %w[x-1-cpu-my_platform-1 w-1], @w1, @x1_m
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# DOC
|
|
||||||
|
|
||||||
def test_self_activate_platform_bump
|
|
||||||
@y1 = util_spec 'y', '1'
|
|
||||||
|
|
||||||
@y1_1_p = util_spec 'y', '1.1' do |s|
|
|
||||||
s.platform = Gem::Platform.new %w[cpu my_platform 1]
|
|
||||||
end
|
|
||||||
|
|
||||||
@z1 = util_spec 'z', '1', 'y' => nil
|
|
||||||
|
|
||||||
assert_activate %w[y-1 z-1], @z1, @y1
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# [C] depends on
|
|
||||||
# [A] = 1.a
|
|
||||||
# [B] = 1.0 depends on
|
|
||||||
# [A] >= 0 (satisfied by 1.a)
|
|
||||||
|
|
||||||
def test_self_activate_prerelease
|
|
||||||
@c1_pre = util_spec 'c', '1.a', "a" => "1.a", "b" => "1"
|
|
||||||
@a1_pre = util_spec 'a', '1.a'
|
|
||||||
@b1 = util_spec 'b', '1' do |s|
|
|
||||||
s.add_dependency 'a'
|
|
||||||
s.add_development_dependency 'aa'
|
|
||||||
end
|
|
||||||
|
|
||||||
assert_activate %w[a-1.a b-1 c-1.a], @c1_pre, @a1_pre, @b1
|
|
||||||
end
|
|
||||||
|
|
||||||
##
|
|
||||||
# DOC
|
|
||||||
|
|
||||||
def test_self_activate_old_required
|
|
||||||
e1, = util_spec 'e', '1', 'd' => '= 1'
|
|
||||||
@d1 = util_spec 'd', '1'
|
|
||||||
@d2 = util_spec 'd', '2'
|
|
||||||
|
|
||||||
assert_activate %w[d-1 e-1], e1, "d"
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_self_bin_path_no_exec_name
|
def test_self_bin_path_no_exec_name
|
||||||
e = assert_raises ArgumentError do
|
e = assert_raises ArgumentError do
|
||||||
Gem.bin_path 'a'
|
Gem.bin_path 'a'
|
||||||
|
@ -1557,6 +1112,34 @@ class TestGem < Gem::TestCase
|
||||||
|
|
||||||
assert_equal '["a-1", "b-1", "c-1"]', out.strip
|
assert_equal '["a-1", "b-1", "c-1"]', out.strip
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_register_default_spec
|
||||||
|
Gem.clear_default_specs
|
||||||
|
|
||||||
|
old_style = Gem::Specification.new do |spec|
|
||||||
|
spec.files = ["foo.rb", "bar.rb"]
|
||||||
|
end
|
||||||
|
|
||||||
|
Gem.register_default_spec old_style
|
||||||
|
|
||||||
|
assert_equal old_style, Gem.find_unresolved_default_spec("foo.rb")
|
||||||
|
assert_equal old_style, Gem.find_unresolved_default_spec("bar.rb")
|
||||||
|
assert_equal nil, Gem.find_unresolved_default_spec("baz.rb")
|
||||||
|
|
||||||
|
Gem.clear_default_specs
|
||||||
|
|
||||||
|
new_style = Gem::Specification.new do |spec|
|
||||||
|
spec.files = ["lib/foo.rb", "ext/bar.rb", "bin/exec", "README"]
|
||||||
|
spec.require_paths = ["lib", "ext"]
|
||||||
|
end
|
||||||
|
|
||||||
|
Gem.register_default_spec new_style
|
||||||
|
|
||||||
|
assert_equal new_style, Gem.find_unresolved_default_spec("foo.rb")
|
||||||
|
assert_equal new_style, Gem.find_unresolved_default_spec("bar.rb")
|
||||||
|
assert_equal nil, Gem.find_unresolved_default_spec("exec")
|
||||||
|
assert_equal nil, Gem.find_unresolved_default_spec("README")
|
||||||
|
end
|
||||||
|
|
||||||
def with_plugin(path)
|
def with_plugin(path)
|
||||||
test_plugin_path = File.expand_path("test/rubygems/plugin/#{path}",
|
test_plugin_path = File.expand_path("test/rubygems/plugin/#{path}",
|
||||||
|
@ -1634,4 +1217,3 @@ class TestGem < Gem::TestCase
|
||||||
File.join Gem.dir, "cache"
|
File.join Gem.dir, "cache"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,8 @@ require 'rubygems/test_case'
|
||||||
require 'rubygems/commands/cert_command'
|
require 'rubygems/commands/cert_command'
|
||||||
require 'rubygems/fix_openssl_warnings' if RUBY_VERSION < "1.9"
|
require 'rubygems/fix_openssl_warnings' if RUBY_VERSION < "1.9"
|
||||||
|
|
||||||
unless defined? OpenSSL then
|
unless defined?(OpenSSL::SSL) then
|
||||||
warn "`gem cert` tests are being skipped, module OpenSSL not found"
|
warn 'Skipping `gem cert` tests. openssl not found.'
|
||||||
end
|
end
|
||||||
|
|
||||||
class TestGemCommandsCertCommand < Gem::TestCase
|
class TestGemCommandsCertCommand < Gem::TestCase
|
||||||
|
@ -98,14 +98,22 @@ Added '/CN=alternate/DC=example'
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_execute_build
|
def test_execute_build
|
||||||
|
passphrase = 'Foo bar'
|
||||||
|
|
||||||
@cmd.handle_options %W[--build nobody@example.com]
|
@cmd.handle_options %W[--build nobody@example.com]
|
||||||
|
|
||||||
use_ui @ui do
|
@build_ui = Gem::MockGemUi.new "#{passphrase}\n#{passphrase}"
|
||||||
|
|
||||||
|
use_ui @build_ui do
|
||||||
@cmd.execute
|
@cmd.execute
|
||||||
end
|
end
|
||||||
|
|
||||||
output = @ui.output.split "\n"
|
output = @build_ui.output.split "\n"
|
||||||
|
|
||||||
|
assert_equal "Passphrase for your Private Key: ",
|
||||||
|
output.shift
|
||||||
|
assert_equal "Please repeat the passphrase for your Private Key: ",
|
||||||
|
output.shift
|
||||||
assert_equal "Certificate: #{File.join @tempdir, 'gem-public_cert.pem'}",
|
assert_equal "Certificate: #{File.join @tempdir, 'gem-public_cert.pem'}",
|
||||||
output.shift
|
output.shift
|
||||||
assert_equal "Private Key: #{File.join @tempdir, 'gem-private_key.pem'}",
|
assert_equal "Private Key: #{File.join @tempdir, 'gem-private_key.pem'}",
|
||||||
|
@ -115,12 +123,43 @@ Added '/CN=alternate/DC=example'
|
||||||
output.shift
|
output.shift
|
||||||
|
|
||||||
assert_empty output
|
assert_empty output
|
||||||
assert_empty @ui.error
|
assert_empty @build_ui.error
|
||||||
|
|
||||||
assert_path_exists File.join(@tempdir, 'gem-private_key.pem')
|
assert_path_exists File.join(@tempdir, 'gem-private_key.pem')
|
||||||
assert_path_exists File.join(@tempdir, 'gem-public_cert.pem')
|
assert_path_exists File.join(@tempdir, 'gem-public_cert.pem')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_execute_build_bad_passphrase_confirmation
|
||||||
|
passphrase = 'Foo bar'
|
||||||
|
passphrase_confirmation = 'Fu bar'
|
||||||
|
|
||||||
|
@cmd.handle_options %W[--build nobody@example.com]
|
||||||
|
|
||||||
|
@build_ui = Gem::MockGemUi.new "#{passphrase}\n#{passphrase_confirmation}"
|
||||||
|
|
||||||
|
use_ui @build_ui do
|
||||||
|
e = assert_raises Gem::CommandLineError do
|
||||||
|
@cmd.execute
|
||||||
|
end
|
||||||
|
|
||||||
|
output = @build_ui.output.split "\n"
|
||||||
|
|
||||||
|
assert_equal "Passphrase for your Private Key: ",
|
||||||
|
output.shift
|
||||||
|
assert_equal "Please repeat the passphrase for your Private Key: ",
|
||||||
|
output.shift
|
||||||
|
|
||||||
|
assert_empty output
|
||||||
|
|
||||||
|
assert_equal "Passphrase and passphrase confirmation don't match",
|
||||||
|
e.message
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
refute_path_exists File.join(@tempdir, 'gem-private_key.pem')
|
||||||
|
refute_path_exists File.join(@tempdir, 'gem-public_cert.pem')
|
||||||
|
end
|
||||||
|
|
||||||
def test_execute_build_key
|
def test_execute_build_key
|
||||||
@cmd.handle_options %W[
|
@cmd.handle_options %W[
|
||||||
--build nobody@example.com
|
--build nobody@example.com
|
||||||
|
@ -135,21 +174,32 @@ Added '/CN=alternate/DC=example'
|
||||||
|
|
||||||
assert_equal "Certificate: #{File.join @tempdir, 'gem-public_cert.pem'}",
|
assert_equal "Certificate: #{File.join @tempdir, 'gem-public_cert.pem'}",
|
||||||
output.shift
|
output.shift
|
||||||
assert_equal "Private Key: #{File.join @tempdir, 'gem-private_key.pem'}",
|
|
||||||
output.shift
|
|
||||||
|
|
||||||
assert_equal "Don't forget to move the key file to somewhere private!",
|
assert_empty output
|
||||||
|
assert_empty @ui.error
|
||||||
|
|
||||||
|
assert_path_exists File.join(@tempdir, 'gem-public_cert.pem')
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_execute_build_encrypted_key
|
||||||
|
@cmd.handle_options %W[
|
||||||
|
--build nobody@example.com
|
||||||
|
--private-key #{ENCRYPTED_PRIVATE_KEY_PATH}
|
||||||
|
]
|
||||||
|
|
||||||
|
use_ui @ui do
|
||||||
|
@cmd.execute
|
||||||
|
end
|
||||||
|
|
||||||
|
output = @ui.output.split "\n"
|
||||||
|
|
||||||
|
assert_equal "Certificate: #{File.join @tempdir, 'gem-public_cert.pem'}",
|
||||||
output.shift
|
output.shift
|
||||||
|
|
||||||
assert_empty output
|
assert_empty output
|
||||||
assert_empty @ui.error
|
assert_empty @ui.error
|
||||||
|
|
||||||
assert_path_exists File.join(@tempdir, 'gem-public_cert.pem')
|
assert_path_exists File.join(@tempdir, 'gem-public_cert.pem')
|
||||||
|
|
||||||
private_key_file = File.join @tempdir, 'gem-private_key.pem'
|
|
||||||
assert_path_exists private_key_file
|
|
||||||
|
|
||||||
assert_equal PRIVATE_KEY.to_pem, File.read(private_key_file)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_execute_certificate
|
def test_execute_certificate
|
||||||
|
@ -203,6 +253,17 @@ Added '/CN=alternate/DC=example'
|
||||||
assert_equal PRIVATE_KEY.to_pem, @cmd.options[:key].to_pem
|
assert_equal PRIVATE_KEY.to_pem, @cmd.options[:key].to_pem
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_execute_encrypted_private_key
|
||||||
|
use_ui @ui do
|
||||||
|
@cmd.send :handle_options, %W[--private-key #{ENCRYPTED_PRIVATE_KEY_PATH}]
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_equal '', @ui.output
|
||||||
|
assert_equal '', @ui.error
|
||||||
|
|
||||||
|
assert_equal ENCRYPTED_PRIVATE_KEY.to_pem, @cmd.options[:key].to_pem
|
||||||
|
end
|
||||||
|
|
||||||
def test_execute_remove
|
def test_execute_remove
|
||||||
@trust_dir.trust_cert PUBLIC_CERT
|
@trust_dir.trust_cert PUBLIC_CERT
|
||||||
|
|
||||||
|
@ -307,6 +368,35 @@ Removed '/CN=alternate/DC=example'
|
||||||
assert_equal mask, File.stat(path).mode unless win_platform?
|
assert_equal mask, File.stat(path).mode unless win_platform?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_execute_sign_encrypted_key
|
||||||
|
path = File.join @tempdir, 'cert.pem'
|
||||||
|
Gem::Security.write ALTERNATE_CERT, path, 0600
|
||||||
|
|
||||||
|
assert_equal '/CN=alternate/DC=example', ALTERNATE_CERT.issuer.to_s
|
||||||
|
|
||||||
|
@cmd.handle_options %W[
|
||||||
|
--private-key #{ENCRYPTED_PRIVATE_KEY_PATH}
|
||||||
|
--certificate #{PUBLIC_CERT_FILE}
|
||||||
|
|
||||||
|
--sign #{path}
|
||||||
|
]
|
||||||
|
|
||||||
|
use_ui @ui do
|
||||||
|
@cmd.execute
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_equal '', @ui.output
|
||||||
|
assert_equal '', @ui.error
|
||||||
|
|
||||||
|
cert = OpenSSL::X509::Certificate.new File.read path
|
||||||
|
|
||||||
|
assert_equal '/CN=nobody/DC=example', cert.issuer.to_s
|
||||||
|
|
||||||
|
mask = 0100600 & (~File.umask)
|
||||||
|
|
||||||
|
assert_equal mask, File.stat(path).mode unless win_platform?
|
||||||
|
end
|
||||||
|
|
||||||
def test_execute_sign_default
|
def test_execute_sign_default
|
||||||
FileUtils.mkdir_p File.join Gem.user_home, '.gem'
|
FileUtils.mkdir_p File.join Gem.user_home, '.gem'
|
||||||
|
|
||||||
|
@ -339,6 +429,38 @@ Removed '/CN=alternate/DC=example'
|
||||||
assert_equal mask, File.stat(path).mode unless win_platform?
|
assert_equal mask, File.stat(path).mode unless win_platform?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_execute_sign_default_encrypted_key
|
||||||
|
FileUtils.mkdir_p File.join(Gem.user_home, '.gem')
|
||||||
|
|
||||||
|
private_key_path = File.join Gem.user_home, '.gem', 'gem-private_key.pem'
|
||||||
|
Gem::Security.write ENCRYPTED_PRIVATE_KEY, private_key_path, 0600, PRIVATE_KEY_PASSPHRASE
|
||||||
|
|
||||||
|
public_cert_path = File.join Gem.user_home, '.gem', 'gem-public_cert.pem'
|
||||||
|
Gem::Security.write PUBLIC_CERT, public_cert_path
|
||||||
|
|
||||||
|
path = File.join @tempdir, 'cert.pem'
|
||||||
|
Gem::Security.write ALTERNATE_CERT, path, 0600
|
||||||
|
|
||||||
|
assert_equal '/CN=alternate/DC=example', ALTERNATE_CERT.issuer.to_s
|
||||||
|
|
||||||
|
@cmd.handle_options %W[--sign #{path}]
|
||||||
|
|
||||||
|
use_ui @ui do
|
||||||
|
@cmd.execute
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_equal '', @ui.output
|
||||||
|
assert_equal '', @ui.error
|
||||||
|
|
||||||
|
cert = OpenSSL::X509::Certificate.new File.read path
|
||||||
|
|
||||||
|
assert_equal '/CN=nobody/DC=example', cert.issuer.to_s
|
||||||
|
|
||||||
|
mask = 0100600 & (~File.umask)
|
||||||
|
|
||||||
|
assert_equal mask, File.stat(path).mode unless win_platform?
|
||||||
|
end
|
||||||
|
|
||||||
def test_execute_sign_no_cert
|
def test_execute_sign_no_cert
|
||||||
FileUtils.mkdir_p File.join Gem.user_home, '.gem'
|
FileUtils.mkdir_p File.join Gem.user_home, '.gem'
|
||||||
|
|
||||||
|
@ -509,6 +631,24 @@ ERROR: --private-key not specified and ~/.gem/gem-private_key.pem does not exis
|
||||||
assert_equal [ALTERNATE_CERT_FILE, CHILD_CERT_FILE], @cmd.options[:sign]
|
assert_equal [ALTERNATE_CERT_FILE, CHILD_CERT_FILE], @cmd.options[:sign]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_handle_options_sign_encrypted_key
|
||||||
|
@cmd.handle_options %W[
|
||||||
|
--private-key #{ALTERNATE_KEY_FILE}
|
||||||
|
--private-key #{ENCRYPTED_PRIVATE_KEY_PATH}
|
||||||
|
|
||||||
|
--certificate #{ALTERNATE_CERT_FILE}
|
||||||
|
--certificate #{PUBLIC_CERT_FILE}
|
||||||
|
|
||||||
|
--sign #{ALTERNATE_CERT_FILE}
|
||||||
|
--sign #{CHILD_CERT_FILE}
|
||||||
|
]
|
||||||
|
|
||||||
|
assert_equal ENCRYPTED_PRIVATE_KEY.to_pem, @cmd.options[:key].to_pem
|
||||||
|
assert_equal PUBLIC_CERT.to_pem, @cmd.options[:issuer_cert].to_pem
|
||||||
|
|
||||||
|
assert_equal [ALTERNATE_CERT_FILE, CHILD_CERT_FILE], @cmd.options[:sign]
|
||||||
|
end
|
||||||
|
|
||||||
def test_handle_options_sign_nonexistent
|
def test_handle_options_sign_nonexistent
|
||||||
nonexistent = File.join @tempdir, 'nonexistent'
|
nonexistent = File.join @tempdir, 'nonexistent'
|
||||||
e = assert_raises OptionParser::InvalidArgument do
|
e = assert_raises OptionParser::InvalidArgument do
|
||||||
|
@ -525,5 +665,5 @@ ERROR: --private-key not specified and ~/.gem/gem-private_key.pem does not exis
|
||||||
e.message
|
e.message
|
||||||
end
|
end
|
||||||
|
|
||||||
end if defined? OpenSSL
|
end if defined?(OpenSSL::SSL)
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,21 @@ class TestGemCommandsCleanupCommand < Gem::TestCase
|
||||||
install_gem @a_2
|
install_gem @a_2
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_handle_options_d
|
||||||
|
@cmd.handle_options %w[-d]
|
||||||
|
assert @cmd.options[:dryrun]
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_handle_options_dry_run
|
||||||
|
@cmd.handle_options %w[--dryrun]
|
||||||
|
assert @cmd.options[:dryrun]
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_handle_options_n
|
||||||
|
@cmd.handle_options %w[-n]
|
||||||
|
assert @cmd.options[:dryrun]
|
||||||
|
end
|
||||||
|
|
||||||
def test_execute
|
def test_execute
|
||||||
@cmd.options[:args] = %w[a]
|
@cmd.options[:args] = %w[a]
|
||||||
|
|
||||||
|
|
|
@ -140,10 +140,10 @@ lib/foo.rb
|
||||||
@cmd.execute
|
@cmd.execute
|
||||||
end
|
end
|
||||||
|
|
||||||
expected = %W[
|
expected = [
|
||||||
#{Gem::ConfigMap[:bindir]}/default_command
|
File.join(Gem::ConfigMap[:bindir], 'default_command'),
|
||||||
#{Gem::ConfigMap[:rubylibdir]}/default/gem.rb
|
File.join(Gem::ConfigMap[:rubylibdir], 'default/gem.rb'),
|
||||||
#{Gem::ConfigMap[:archdir]}/default_gem.so
|
File.join(Gem::ConfigMap[:archdir], 'default_gem.so')
|
||||||
].sort.join "\n"
|
].sort.join "\n"
|
||||||
|
|
||||||
assert_equal expected, @ui.output.chomp
|
assert_equal expected, @ui.output.chomp
|
||||||
|
|
|
@ -11,6 +11,7 @@ class TestGemCommandsEnvironmentCommand < Gem::TestCase
|
||||||
|
|
||||||
def test_execute
|
def test_execute
|
||||||
orig_sources = Gem.sources.dup
|
orig_sources = Gem.sources.dup
|
||||||
|
orig_path, ENV['PATH'] = ENV['PATH'], '/usr/local/bin:/usr/bin:/bin'
|
||||||
Gem.sources.replace %w[http://gems.example.com]
|
Gem.sources.replace %w[http://gems.example.com]
|
||||||
Gem.configuration['gemcutter_key'] = 'blah'
|
Gem.configuration['gemcutter_key'] = 'blah'
|
||||||
|
|
||||||
|
@ -36,10 +37,17 @@ class TestGemCommandsEnvironmentCommand < Gem::TestCase
|
||||||
assert_match %r|"gemcutter_key" => "\*\*\*\*"|, @ui.output
|
assert_match %r|"gemcutter_key" => "\*\*\*\*"|, @ui.output
|
||||||
assert_match %r|:verbose => |, @ui.output
|
assert_match %r|:verbose => |, @ui.output
|
||||||
assert_match %r|REMOTE SOURCES:|, @ui.output
|
assert_match %r|REMOTE SOURCES:|, @ui.output
|
||||||
assert_equal '', @ui.error
|
|
||||||
|
assert_match %r|- SHELL PATH:|, @ui.output
|
||||||
|
assert_match %r|- /usr/local/bin$|, @ui.output
|
||||||
|
assert_match %r|- /usr/bin$|, @ui.output
|
||||||
|
assert_match %r|- /bin$|, @ui.output
|
||||||
|
|
||||||
|
assert_empty @ui.error
|
||||||
|
|
||||||
ensure
|
ensure
|
||||||
Gem.sources.replace orig_sources
|
Gem.sources.replace orig_sources
|
||||||
|
ENV['PATH'] = orig_path
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_execute_gemdir
|
def test_execute_gemdir
|
||||||
|
|
|
@ -36,9 +36,12 @@ class TestGemCommandsHelpCommand < Gem::TestCase
|
||||||
mgr.command_names.each do |cmd|
|
mgr.command_names.each do |cmd|
|
||||||
assert_match(/\s+#{cmd}\s+\S+/, out)
|
assert_match(/\s+#{cmd}\s+\S+/, out)
|
||||||
end
|
end
|
||||||
assert_equal '', err
|
|
||||||
|
|
||||||
refute_match 'No command found for ', out
|
if defined?(OpenSSL::SSL) then
|
||||||
|
assert_empty err
|
||||||
|
|
||||||
|
refute_match 'No command found for ', out
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ class TestGemCommandsInstallCommand < Gem::TestCase
|
||||||
|
|
||||||
def setup
|
def setup
|
||||||
super
|
super
|
||||||
|
common_installer_setup
|
||||||
|
|
||||||
@cmd = Gem::Commands::InstallCommand.new
|
@cmd = Gem::Commands::InstallCommand.new
|
||||||
@cmd.options[:document] = []
|
@cmd.options[:document] = []
|
||||||
|
@ -167,8 +168,7 @@ class TestGemCommandsInstallCommand < Gem::TestCase
|
||||||
|
|
||||||
# This is needed because we need to exercise the cache path
|
# This is needed because we need to exercise the cache path
|
||||||
# within SpecFetcher
|
# within SpecFetcher
|
||||||
path = File.join Gem.user_home, '.gem', 'specs', "not-there.nothing%80",
|
path = File.join Gem.spec_cache_dir, "not-there.nothing%80", "latest_specs.4.8"
|
||||||
"latest_specs.4.8"
|
|
||||||
|
|
||||||
FileUtils.mkdir_p File.dirname(path)
|
FileUtils.mkdir_p File.dirname(path)
|
||||||
|
|
||||||
|
@ -632,67 +632,6 @@ ERROR: Possible alternatives: non_existent_with_hint
|
||||||
assert_equal x, e
|
assert_equal x, e
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_execute_installs_dependencies
|
|
||||||
r, r_gem = util_gem 'r', '1', 'q' => '= 1'
|
|
||||||
q, q_gem = util_gem 'q', '1'
|
|
||||||
|
|
||||||
util_setup_fake_fetcher
|
|
||||||
util_setup_spec_fetcher r, q
|
|
||||||
|
|
||||||
Gem::Specification.reset
|
|
||||||
|
|
||||||
@fetcher.data["#{@gem_repo}gems/#{q.file_name}"] = read_binary(q_gem)
|
|
||||||
@fetcher.data["#{@gem_repo}gems/#{r.file_name}"] = read_binary(r_gem)
|
|
||||||
|
|
||||||
@cmd.options[:args] = ["r"]
|
|
||||||
|
|
||||||
e = nil
|
|
||||||
use_ui @ui do
|
|
||||||
e = assert_raises Gem::SystemExitException do
|
|
||||||
capture_io do
|
|
||||||
@cmd.execute
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
out = @ui.output.split "\n"
|
|
||||||
assert_equal "2 gems installed", out.shift
|
|
||||||
assert out.empty?, out.inspect
|
|
||||||
|
|
||||||
assert_equal %w[q-1 r-1], @cmd.installed_specs.map { |spec| spec.full_name }
|
|
||||||
|
|
||||||
assert_equal 0, e.exit_code
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_execute_satisfy_deps_of_local_from_sources
|
|
||||||
r, r_gem = util_gem 'r', '1', 'q' => '= 1'
|
|
||||||
q, q_gem = util_gem 'q', '1'
|
|
||||||
|
|
||||||
util_setup_fake_fetcher
|
|
||||||
util_setup_spec_fetcher r, q
|
|
||||||
|
|
||||||
Gem::Specification.reset
|
|
||||||
|
|
||||||
@fetcher.data["#{@gem_repo}gems/#{q.file_name}"] = read_binary(q_gem)
|
|
||||||
|
|
||||||
@cmd.options[:args] = [r_gem]
|
|
||||||
|
|
||||||
use_ui @ui do
|
|
||||||
e = assert_raises Gem::SystemExitException do
|
|
||||||
capture_io do
|
|
||||||
@cmd.execute
|
|
||||||
end
|
|
||||||
end
|
|
||||||
assert_equal 0, e.exit_code
|
|
||||||
end
|
|
||||||
|
|
||||||
assert_equal %w[q-1 r-1], @cmd.installed_specs.map { |spec| spec.full_name }
|
|
||||||
|
|
||||||
out = @ui.output.split "\n"
|
|
||||||
assert_equal "2 gems installed", out.shift
|
|
||||||
assert out.empty?, out.inspect
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_execute_uses_from_a_gemdeps
|
def test_execute_uses_from_a_gemdeps
|
||||||
util_setup_fake_fetcher
|
util_setup_fake_fetcher
|
||||||
util_setup_spec_fetcher
|
util_setup_spec_fetcher
|
||||||
|
@ -950,4 +889,3 @@ ERROR: Possible alternatives: non_existent_with_hint
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ class TestGemCommandsOwnerCommand < Gem::TestCase
|
||||||
def setup
|
def setup
|
||||||
super
|
super
|
||||||
|
|
||||||
|
ENV["RUBYGEMS_HOST"] = nil
|
||||||
@fetcher = Gem::FakeFetcher.new
|
@fetcher = Gem::FakeFetcher.new
|
||||||
Gem::RemoteFetcher.fetcher = @fetcher
|
Gem::RemoteFetcher.fetcher = @fetcher
|
||||||
Gem.configuration.rubygems_api_key = "ed244fbf2b1a52e012da8616c512fa47f9aa5250"
|
Gem.configuration.rubygems_api_key = "ed244fbf2b1a52e012da8616c512fa47f9aa5250"
|
||||||
|
@ -34,6 +35,36 @@ EOF
|
||||||
assert_match %r{- user2@example.com}, @ui.output
|
assert_match %r{- user2@example.com}, @ui.output
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_show_owners_setting_up_host_through_env_var
|
||||||
|
response = "- email: user1@example.com\n"
|
||||||
|
host = "http://rubygems.example"
|
||||||
|
ENV["RUBYGEMS_HOST"] = host
|
||||||
|
|
||||||
|
@fetcher.data["#{host}/api/v1/gems/freewill/owners.yaml"] = [response, 200, 'OK']
|
||||||
|
|
||||||
|
use_ui @ui do
|
||||||
|
@cmd.show_owners("freewill")
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_match %r{Owners for gem: freewill}, @ui.output
|
||||||
|
assert_match %r{- user1@example.com}, @ui.output
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_show_owners_setting_up_host
|
||||||
|
response = "- email: user1@example.com\n"
|
||||||
|
host = "http://rubygems.example"
|
||||||
|
@cmd.host = host
|
||||||
|
|
||||||
|
@fetcher.data["#{host}/api/v1/gems/freewill/owners.yaml"] = [response, 200, 'OK']
|
||||||
|
|
||||||
|
use_ui @ui do
|
||||||
|
@cmd.show_owners("freewill")
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_match %r{Owners for gem: freewill}, @ui.output
|
||||||
|
assert_match %r{- user1@example.com}, @ui.output
|
||||||
|
end
|
||||||
|
|
||||||
def test_show_owners_denied
|
def test_show_owners_denied
|
||||||
response = "You don't have permission to push to this gem"
|
response = "You don't have permission to push to this gem"
|
||||||
@fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners.yaml"] = [response, 403, 'Forbidden']
|
@fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners.yaml"] = [response, 403, 'Forbidden']
|
||||||
|
@ -87,6 +118,24 @@ EOF
|
||||||
assert_match response, @ui.output
|
assert_match response, @ui.output
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_add_owner_with_host_option_through_execute
|
||||||
|
host = "http://rubygems.example"
|
||||||
|
add_owner_response = "Owner added successfully."
|
||||||
|
show_owners_response = "- email: user1@example.com\n"
|
||||||
|
@fetcher.data["#{host}/api/v1/gems/freewill/owners"] = [add_owner_response, 200, 'OK']
|
||||||
|
@fetcher.data["#{host}/api/v1/gems/freewill/owners.yaml"] = [show_owners_response, 200, 'OK']
|
||||||
|
|
||||||
|
@cmd.handle_options %W[--host #{host} --add user-new1@example.com freewill]
|
||||||
|
|
||||||
|
use_ui @ui do
|
||||||
|
@cmd.execute
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_match add_owner_response, @ui.output
|
||||||
|
assert_match %r{Owners for gem: freewill}, @ui.output
|
||||||
|
assert_match %r{- user1@example.com}, @ui.output
|
||||||
|
end
|
||||||
|
|
||||||
def test_add_owners_key
|
def test_add_owners_key
|
||||||
response = "Owner added successfully."
|
response = "Owner added successfully."
|
||||||
@fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners"] = [response, 200, 'OK']
|
@fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners"] = [response, 200, 'OK']
|
||||||
|
|
|
@ -80,6 +80,32 @@ class TestGemCommandsPristineCommand < Gem::TestCase
|
||||||
assert_empty out, out.inspect
|
assert_empty out, out.inspect
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_execute_env_shebang
|
||||||
|
a = quick_spec 'a' do |s|
|
||||||
|
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
|
||||||
|
|
||||||
|
install_gem a
|
||||||
|
|
||||||
|
gem_exec = File.join @gemhome, 'bin', 'foo'
|
||||||
|
|
||||||
|
FileUtils.rm gem_exec
|
||||||
|
|
||||||
|
@cmd.handle_options %w[--all --env-shebang]
|
||||||
|
|
||||||
|
use_ui @ui do
|
||||||
|
@cmd.execute
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_path_exists gem_exec
|
||||||
|
|
||||||
|
assert_match '/usr/bin/env', File.read(gem_exec)
|
||||||
|
end
|
||||||
|
|
||||||
def test_execute_no_extension
|
def test_execute_no_extension
|
||||||
a = quick_spec 'a' do |s| s.extensions << 'ext/a/extconf.rb' end
|
a = quick_spec 'a' do |s| s.extensions << 'ext/a/extconf.rb' end
|
||||||
|
|
||||||
|
|
|
@ -194,7 +194,7 @@ beta-gems.example.com is not a URI
|
||||||
assert_equal expected, @ui.output
|
assert_equal expected, @ui.output
|
||||||
assert_equal '', @ui.error
|
assert_equal '', @ui.error
|
||||||
|
|
||||||
dir = File.join Gem.user_home, '.gem', 'specs'
|
dir = Gem.spec_cache_dir
|
||||||
refute File.exist?(dir), 'cache dir removed'
|
refute File.exist?(dir), 'cache dir removed'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ class TestGemCommandsUpdateCommand < Gem::TestCase
|
||||||
|
|
||||||
def setup
|
def setup
|
||||||
super
|
super
|
||||||
|
common_installer_setup
|
||||||
|
|
||||||
@cmd = Gem::Commands::UpdateCommand.new
|
@cmd = Gem::Commands::UpdateCommand.new
|
||||||
|
|
||||||
|
@ -253,7 +254,7 @@ class TestGemCommandsUpdateCommand < Gem::TestCase
|
||||||
out = @ui.output.split "\n"
|
out = @ui.output.split "\n"
|
||||||
assert_equal "Updating installed gems", out.shift
|
assert_equal "Updating installed gems", out.shift
|
||||||
assert_equal "Updating #{@a2.name}", out.shift
|
assert_equal "Updating #{@a2.name}", out.shift
|
||||||
assert_equal "Gems updated: #{@c2.name} #{@b2.name} #{@a2.name}",
|
assert_equal "Gems updated: #{@a2.name} #{@b2.name} #{@c2.name}",
|
||||||
out.shift
|
out.shift
|
||||||
|
|
||||||
assert_empty out
|
assert_empty out
|
||||||
|
|
|
@ -201,6 +201,10 @@ ERROR: Your gem push credentials file located at:
|
||||||
|
|
||||||
has file permissions of 0644 but 0600 is required.
|
has file permissions of 0644 but 0600 is required.
|
||||||
|
|
||||||
|
To fix this error run:
|
||||||
|
|
||||||
|
\tchmod 0600 #{@cfg.credentials_path}
|
||||||
|
|
||||||
You should reset your credentials at:
|
You should reset your credentials at:
|
||||||
|
|
||||||
\thttps://rubygems.org/profile/edit
|
\thttps://rubygems.org/profile/edit
|
||||||
|
@ -428,6 +432,14 @@ if you believe they were disclosed to a third party.
|
||||||
assert_equal('/home/me/certs', @cfg.ssl_ca_cert)
|
assert_equal('/home/me/certs', @cfg.ssl_ca_cert)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_load_ssl_client_cert_from_config
|
||||||
|
File.open @temp_conf, 'w' do |fp|
|
||||||
|
fp.puts ":ssl_client_cert: /home/me/mine.pem"
|
||||||
|
end
|
||||||
|
util_config_file
|
||||||
|
assert_equal('/home/me/mine.pem', @cfg.ssl_client_cert)
|
||||||
|
end
|
||||||
|
|
||||||
def util_config_file(args = @cfg_args)
|
def util_config_file(args = @cfg_args)
|
||||||
@cfg = Gem::ConfigFile.new args
|
@cfg = Gem::ConfigFile.new args
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,6 +6,7 @@ class TestGemDependencyInstaller < Gem::TestCase
|
||||||
|
|
||||||
def setup
|
def setup
|
||||||
super
|
super
|
||||||
|
common_installer_setup
|
||||||
|
|
||||||
@gems_dir = File.join @tempdir, 'gems'
|
@gems_dir = File.join @tempdir, 'gems'
|
||||||
@cache_dir = File.join @gemhome, 'cache'
|
@cache_dir = File.join @gemhome, 'cache'
|
||||||
|
@ -172,7 +173,8 @@ class TestGemDependencyInstaller < Gem::TestCase
|
||||||
|
|
||||||
FileUtils.mv @a1_gem, @tempdir
|
FileUtils.mv @a1_gem, @tempdir
|
||||||
FileUtils.mv @b1_gem, @tempdir
|
FileUtils.mv @b1_gem, @tempdir
|
||||||
FileUtils.mv e1_gem, @tempdir
|
FileUtils.mv e1_gem, @tempdir
|
||||||
|
|
||||||
inst = nil
|
inst = nil
|
||||||
|
|
||||||
Dir.chdir @tempdir do
|
Dir.chdir @tempdir do
|
||||||
|
@ -180,40 +182,15 @@ class TestGemDependencyInstaller < Gem::TestCase
|
||||||
inst.install 'b'
|
inst.install 'b'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
assert_equal %w[b-1], inst.installed_gems.map { |s| s.full_name },
|
||||||
|
'sanity check'
|
||||||
|
|
||||||
Dir.chdir @tempdir do
|
Dir.chdir @tempdir do
|
||||||
inst = Gem::DependencyInstaller.new
|
inst = Gem::DependencyInstaller.new
|
||||||
inst.install 'e'
|
inst.install 'e'
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_equal %w[e-1 a-1], inst.installed_gems.map { |s| s.full_name }
|
assert_equal %w[a-1 e-1], inst.installed_gems.map { |s| s.full_name }
|
||||||
end
|
|
||||||
|
|
||||||
def test_install_ignore_satified_deps
|
|
||||||
util_setup_gems
|
|
||||||
|
|
||||||
_, e1_gem = util_gem 'e', '1' do |s|
|
|
||||||
s.add_dependency 'b'
|
|
||||||
end
|
|
||||||
|
|
||||||
util_clear_gems
|
|
||||||
|
|
||||||
FileUtils.mv @a1_gem, @tempdir
|
|
||||||
FileUtils.mv @b1_gem, @tempdir
|
|
||||||
FileUtils.mv e1_gem, @tempdir
|
|
||||||
|
|
||||||
Dir.chdir @tempdir do
|
|
||||||
i = Gem::DependencyInstaller.new :ignore_dependencies => true
|
|
||||||
i.install 'b'
|
|
||||||
end
|
|
||||||
|
|
||||||
inst = nil
|
|
||||||
|
|
||||||
Dir.chdir @tempdir do
|
|
||||||
inst = Gem::DependencyInstaller.new :minimal_deps => true
|
|
||||||
inst.install 'e'
|
|
||||||
end
|
|
||||||
|
|
||||||
assert_equal %w[e-1], inst.installed_gems.map { |s| s.full_name }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_install_cache_dir
|
def test_install_cache_dir
|
||||||
|
@ -246,15 +223,18 @@ class TestGemDependencyInstaller < Gem::TestCase
|
||||||
Gem::Specification.reset
|
Gem::Specification.reset
|
||||||
|
|
||||||
FileUtils.mv @a1_gem, @tempdir
|
FileUtils.mv @a1_gem, @tempdir
|
||||||
FileUtils.mv a2_gem, @tempdir # not in index
|
FileUtils.mv a2_gem, @tempdir # not in index
|
||||||
FileUtils.mv @b1_gem, @tempdir
|
FileUtils.mv @b1_gem, @tempdir
|
||||||
inst = nil
|
inst = nil
|
||||||
|
|
||||||
Dir.chdir @tempdir do
|
Dir.chdir @tempdir do
|
||||||
inst = Gem::DependencyInstaller.new
|
inst = Gem::DependencyInstaller.new
|
||||||
inst.install 'a', Gem::Requirement.create("= 2")
|
inst.install 'a', req("= 2")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
assert_equal %w[a-2], inst.installed_gems.map { |s| s.full_name },
|
||||||
|
'sanity check'
|
||||||
|
|
||||||
FileUtils.rm File.join(@tempdir, a2.file_name)
|
FileUtils.rm File.join(@tempdir, a2.file_name)
|
||||||
|
|
||||||
Dir.chdir @tempdir do
|
Dir.chdir @tempdir do
|
||||||
|
@ -282,19 +262,18 @@ class TestGemDependencyInstaller < Gem::TestCase
|
||||||
Gem::Specification.reset
|
Gem::Specification.reset
|
||||||
|
|
||||||
FileUtils.mv @a1_gem, @tempdir
|
FileUtils.mv @a1_gem, @tempdir
|
||||||
FileUtils.mv a2_gem, @tempdir # not in index
|
FileUtils.mv a2_gem, @tempdir # not in index
|
||||||
FileUtils.mv @b1_gem, @tempdir
|
FileUtils.mv @b1_gem, @tempdir
|
||||||
FileUtils.mv a3_gem, @tempdir
|
FileUtils.mv a3_gem, @tempdir
|
||||||
|
|
||||||
inst = nil
|
|
||||||
|
|
||||||
Dir.chdir @tempdir do
|
Dir.chdir @tempdir do
|
||||||
inst = Gem::DependencyInstaller.new
|
Gem::DependencyInstaller.new.install 'a', req("= 2")
|
||||||
inst.install 'a', Gem::Requirement.create("= 2")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
FileUtils.rm File.join(@tempdir, a2.file_name)
|
FileUtils.rm File.join(@tempdir, a2.file_name)
|
||||||
|
|
||||||
|
inst = nil
|
||||||
|
|
||||||
Dir.chdir @tempdir do
|
Dir.chdir @tempdir do
|
||||||
inst = Gem::DependencyInstaller.new
|
inst = Gem::DependencyInstaller.new
|
||||||
inst.install 'b'
|
inst.install 'b'
|
||||||
|
@ -488,6 +467,42 @@ class TestGemDependencyInstaller < Gem::TestCase
|
||||||
assert_equal %w[a-1], inst.installed_gems.map { |s| s.full_name }
|
assert_equal %w[a-1], inst.installed_gems.map { |s| s.full_name }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_install_minimal_deps
|
||||||
|
util_setup_gems
|
||||||
|
|
||||||
|
_, e1_gem = util_gem 'e', '1' do |s|
|
||||||
|
s.add_dependency 'b'
|
||||||
|
end
|
||||||
|
|
||||||
|
_, b2_gem = util_gem 'b', '2' do |s|
|
||||||
|
s.add_dependency 'a'
|
||||||
|
end
|
||||||
|
|
||||||
|
util_clear_gems
|
||||||
|
|
||||||
|
FileUtils.mv @a1_gem, @tempdir
|
||||||
|
FileUtils.mv @b1_gem, @tempdir
|
||||||
|
FileUtils.mv b2_gem, @tempdir
|
||||||
|
FileUtils.mv e1_gem, @tempdir
|
||||||
|
|
||||||
|
inst = nil
|
||||||
|
|
||||||
|
Dir.chdir @tempdir do
|
||||||
|
inst = Gem::DependencyInstaller.new :ignore_dependencies => true
|
||||||
|
inst.install 'b', req('= 1')
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_equal %w[b-1], inst.installed_gems.map { |s| s.full_name },
|
||||||
|
'sanity check'
|
||||||
|
|
||||||
|
Dir.chdir @tempdir do
|
||||||
|
inst = Gem::DependencyInstaller.new :minimal_deps => true
|
||||||
|
inst.install 'e'
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_equal %w[a-1 e-1], inst.installed_gems.map { |s| s.full_name }
|
||||||
|
end
|
||||||
|
|
||||||
def test_install_env_shebang
|
def test_install_env_shebang
|
||||||
util_setup_gems
|
util_setup_gems
|
||||||
|
|
||||||
|
@ -627,12 +642,12 @@ class TestGemDependencyInstaller < Gem::TestCase
|
||||||
inst = nil
|
inst = nil
|
||||||
|
|
||||||
Dir.chdir @tempdir do
|
Dir.chdir @tempdir do
|
||||||
e = assert_raises Gem::DependencyError do
|
e = assert_raises Gem::UnsatisfiableDependencyError do
|
||||||
inst = Gem::DependencyInstaller.new :domain => :local
|
inst = Gem::DependencyInstaller.new :domain => :local
|
||||||
inst.install 'b'
|
inst.install 'b'
|
||||||
end
|
end
|
||||||
|
|
||||||
expected = "Unable to resolve dependencies: b requires a (>= 0)"
|
expected = "Unable to resolve dependency: b (= 1) requires a (>= 0)"
|
||||||
assert_equal expected, e.message
|
assert_equal expected, e.message
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -910,12 +925,13 @@ class TestGemDependencyInstaller < Gem::TestCase
|
||||||
gems = set.sorted
|
gems = set.sorted
|
||||||
|
|
||||||
assert_equal 2, gems.length
|
assert_equal 2, gems.length
|
||||||
local = gems.first
|
|
||||||
|
remote, local = gems
|
||||||
|
|
||||||
assert_equal 'a-1', local.spec.full_name, 'local spec'
|
assert_equal 'a-1', local.spec.full_name, 'local spec'
|
||||||
assert_equal File.join(@tempdir, @a1.file_name),
|
assert_equal File.join(@tempdir, @a1.file_name),
|
||||||
local.source.download(local.spec), 'local path'
|
local.source.download(local.spec), 'local path'
|
||||||
|
|
||||||
remote = gems.last
|
|
||||||
assert_equal 'a-1', remote.spec.full_name, 'remote spec'
|
assert_equal 'a-1', remote.spec.full_name, 'remote spec'
|
||||||
assert_equal Gem::Source.new(@gem_repo), remote.source, 'remote path'
|
assert_equal Gem::Source.new(@gem_repo), remote.source, 'remote path'
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,9 @@ class TestGemDependencyResolver < Gem::TestCase
|
||||||
exp = expected.sort_by { |s| s.full_name }
|
exp = expected.sort_by { |s| s.full_name }
|
||||||
act = actual.map { |a| a.spec }.sort_by { |s| s.full_name }
|
act = actual.map { |a| a.spec }.sort_by { |s| s.full_name }
|
||||||
|
|
||||||
assert_equal exp, act
|
msg = "Set of gems was not the same: #{exp.map { |x| x.full_name}.inspect} != #{act.map { |x| x.full_name}.inspect}"
|
||||||
|
|
||||||
|
assert_equal exp, act, msg
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_no_overlap_specificly
|
def test_no_overlap_specificly
|
||||||
|
@ -177,7 +179,8 @@ class TestGemDependencyResolver < Gem::TestCase
|
||||||
r.resolve
|
r.resolve
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_equal "unable to find any gem matching dependency 'a (>= 0)'", e.message
|
assert_equal "Unable to resolve dependency: (unknown) requires a (>= 0)",
|
||||||
|
e.message
|
||||||
|
|
||||||
assert_equal "a (>= 0)", e.dependency.to_s
|
assert_equal "a (>= 0)", e.dependency.to_s
|
||||||
end
|
end
|
||||||
|
@ -215,7 +218,7 @@ class TestGemDependencyResolver < Gem::TestCase
|
||||||
r.resolve
|
r.resolve
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_equal "detected 1 conflict with dependency 'c (>= 2)'", e.message
|
assert_match "a-1 requires c (>= 2) but it conflicted", e.message
|
||||||
|
|
||||||
assert_equal "c (>= 2)", e.dependency.to_s
|
assert_equal "c (>= 2)", e.dependency.to_s
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
require 'rubygems/test_case'
|
||||||
|
require 'rubygems/dependency_resolver'
|
||||||
|
|
||||||
|
class TestGemDependencyResolverDependencyConflict < Gem::TestCase
|
||||||
|
|
||||||
|
def test_explanation
|
||||||
|
root =
|
||||||
|
dependency_request dep('net-ssh', '>= 2.0.13'), 'rye', '0.9.8'
|
||||||
|
child =
|
||||||
|
dependency_request dep('net-ssh', '>= 2.6.5'), 'net-ssh', '2.2.2', root
|
||||||
|
|
||||||
|
conflict =
|
||||||
|
Gem::DependencyResolver::DependencyConflict.new child, child.requester
|
||||||
|
|
||||||
|
expected = <<-EXPECTED
|
||||||
|
Activated net-ssh-2.2.2 instead of (>= 2.6.5) via:
|
||||||
|
net-ssh-2.2.2, rye-0.9.8
|
||||||
|
EXPECTED
|
||||||
|
|
||||||
|
assert_equal expected, conflict.explanation
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_request_path
|
||||||
|
root =
|
||||||
|
dependency_request dep('net-ssh', '>= 2.0.13'), 'rye', '0.9.8'
|
||||||
|
child =
|
||||||
|
dependency_request dep('net-ssh', '>= 2.6.5'), 'net-ssh', '2.2.2', root
|
||||||
|
|
||||||
|
conflict =
|
||||||
|
Gem::DependencyResolver::DependencyConflict.new child, nil
|
||||||
|
|
||||||
|
assert_equal %w[net-ssh-2.2.2 rye-0.9.8], conflict.request_path
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
|
@ -7,6 +7,7 @@ class TestGemGemRunner < Gem::TestCase
|
||||||
super
|
super
|
||||||
|
|
||||||
@orig_args = Gem::Command.build_args
|
@orig_args = Gem::Command.build_args
|
||||||
|
@runner = Gem::GemRunner.new
|
||||||
end
|
end
|
||||||
|
|
||||||
def teardown
|
def teardown
|
||||||
|
@ -41,23 +42,26 @@ class TestGemGemRunner < Gem::TestCase
|
||||||
assert_equal %w[--commands], Gem::Command.extra_args
|
assert_equal %w[--commands], Gem::Command.extra_args
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_build_args_are_handled
|
def test_extract_build_args
|
||||||
Gem.clear_paths
|
args = %w[]
|
||||||
|
assert_equal [], @runner.extract_build_args(args)
|
||||||
|
assert_equal %w[], args
|
||||||
|
|
||||||
cls = Class.new(Gem::Command) do
|
args = %w[foo]
|
||||||
def execute
|
assert_equal [], @runner.extract_build_args(args)
|
||||||
end
|
assert_equal %w[foo], args
|
||||||
end
|
|
||||||
|
|
||||||
test_obj = cls.new :ba_test
|
args = %w[--foo]
|
||||||
|
assert_equal [], @runner.extract_build_args(args)
|
||||||
|
assert_equal %w[--foo], args
|
||||||
|
|
||||||
cmds = Gem::CommandManager.new
|
args = %w[--foo --]
|
||||||
cmds.register_command :ba_test, test_obj
|
assert_equal [], @runner.extract_build_args(args)
|
||||||
|
assert_equal %w[--foo], args
|
||||||
|
|
||||||
runner = Gem::GemRunner.new :command_manager => cmds
|
args = %w[--foo -- --bar]
|
||||||
runner.run(%W[ba_test -- --build_arg1 --build_arg2])
|
assert_equal %w[--bar], @runner.extract_build_args(args)
|
||||||
|
assert_equal %w[--foo], args
|
||||||
assert_equal %w[--build_arg1 --build_arg2], test_obj.options[:build_args]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
41
test/rubygems/test_gem_impossible_dependencies_error.rb
Normal file
41
test/rubygems/test_gem_impossible_dependencies_error.rb
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
require 'rubygems/test_case'
|
||||||
|
|
||||||
|
class TestGemImpossibleDependenciesError < Gem::TestCase
|
||||||
|
|
||||||
|
def test_message_conflict
|
||||||
|
request = dependency_request dep('net-ssh', '>= 2.0.13'), 'rye', '0.9.8'
|
||||||
|
|
||||||
|
conflicts = []
|
||||||
|
|
||||||
|
# These conflicts are lies as their dependencies does not have the correct
|
||||||
|
# requested-by entries, but they are suitable for testing the message.
|
||||||
|
# See #485 to construct a correct conflict.
|
||||||
|
net_ssh_2_2_2 =
|
||||||
|
dependency_request dep('net-ssh', '>= 2.6.5'), 'net-ssh', '2.2.2', request
|
||||||
|
net_ssh_2_6_5 =
|
||||||
|
dependency_request dep('net-ssh', '~> 2.2.2'), 'net-ssh', '2.6.5', request
|
||||||
|
|
||||||
|
conflict1 = Gem::DependencyResolver::DependencyConflict.new \
|
||||||
|
net_ssh_2_6_5, net_ssh_2_6_5.requester
|
||||||
|
|
||||||
|
conflict2 = Gem::DependencyResolver::DependencyConflict.new \
|
||||||
|
net_ssh_2_2_2, net_ssh_2_2_2.requester
|
||||||
|
|
||||||
|
conflicts << [net_ssh_2_6_5.requester.spec, conflict1]
|
||||||
|
conflicts << [net_ssh_2_2_2.requester.spec, conflict2]
|
||||||
|
|
||||||
|
error = Gem::ImpossibleDependenciesError.new request, conflicts
|
||||||
|
|
||||||
|
expected = <<-EXPECTED
|
||||||
|
rye-0.9.8 requires net-ssh (>= 2.0.13) but it conflicted:
|
||||||
|
Activated net-ssh-2.6.5 instead of (~> 2.2.2) via:
|
||||||
|
net-ssh-2.6.5, rye-0.9.8
|
||||||
|
Activated net-ssh-2.2.2 instead of (>= 2.6.5) via:
|
||||||
|
net-ssh-2.2.2, rye-0.9.8
|
||||||
|
EXPECTED
|
||||||
|
|
||||||
|
assert_equal expected, error.message
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
|
@ -22,12 +22,13 @@ class TestGemInstallUpdateOptions < Gem::InstallerTestCase
|
||||||
--rdoc
|
--rdoc
|
||||||
--ri
|
--ri
|
||||||
-E
|
-E
|
||||||
-P HighSecurity
|
|
||||||
-f
|
-f
|
||||||
-i /install_to
|
-i /install_to
|
||||||
-w
|
-w
|
||||||
]
|
]
|
||||||
|
|
||||||
|
args.concat %w[-P HighSecurity] if defined?(OpenSSL::SSL)
|
||||||
|
|
||||||
assert @cmd.handles?(args)
|
assert @cmd.handles?(args)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -100,6 +101,8 @@ class TestGemInstallUpdateOptions < Gem::InstallerTestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_security_policy
|
def test_security_policy
|
||||||
|
skip 'openssl is missing' unless defined?(OpenSSL::SSL)
|
||||||
|
|
||||||
@cmd.handle_options %w[-P HighSecurity]
|
@cmd.handle_options %w[-P HighSecurity]
|
||||||
|
|
||||||
assert_equal Gem::Security::HighSecurity, @cmd.options[:security_policy]
|
assert_equal Gem::Security::HighSecurity, @cmd.options[:security_policy]
|
||||||
|
|
|
@ -4,6 +4,7 @@ class TestGemInstaller < Gem::InstallerTestCase
|
||||||
|
|
||||||
def setup
|
def setup
|
||||||
super
|
super
|
||||||
|
common_installer_setup
|
||||||
|
|
||||||
if __name__ =~ /^test_install(_|$)/ then
|
if __name__ =~ /^test_install(_|$)/ then
|
||||||
FileUtils.rm_r @spec.gem_dir
|
FileUtils.rm_r @spec.gem_dir
|
||||||
|
@ -14,6 +15,8 @@ class TestGemInstaller < Gem::InstallerTestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
def teardown
|
def teardown
|
||||||
|
common_installer_teardown
|
||||||
|
|
||||||
super
|
super
|
||||||
|
|
||||||
Gem.configuration = @config
|
Gem.configuration = @config
|
||||||
|
@ -300,6 +303,8 @@ gem 'other', version
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_ensure_loadable_spec_security_policy
|
def test_ensure_loadable_spec_security_policy
|
||||||
|
skip 'openssl is missing' unless defined?(OpenSSL::SSL)
|
||||||
|
|
||||||
_, a_gem = util_gem 'a', 2 do |s|
|
_, a_gem = util_gem 'a', 2 do |s|
|
||||||
s.add_dependency 'garbage ~> 5'
|
s.add_dependency 'garbage ~> 5'
|
||||||
end
|
end
|
||||||
|
@ -1341,7 +1346,7 @@ gem 'other', version
|
||||||
assert File.exist?(File.join(dest, 'bin', 'executable'))
|
assert File.exist?(File.join(dest, 'bin', 'executable'))
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_write_build_args
|
def test_write_build_info_file
|
||||||
refute_path_exists @spec.build_info_file
|
refute_path_exists @spec.build_info_file
|
||||||
|
|
||||||
@installer.build_args = %w[
|
@installer.build_args = %w[
|
||||||
|
@ -1357,7 +1362,7 @@ gem 'other', version
|
||||||
assert_equal expected, File.read(@spec.build_info_file)
|
assert_equal expected, File.read(@spec.build_info_file)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_write_build_args_empty
|
def test_write_build_info_file_empty
|
||||||
refute_path_exists @spec.build_info_file
|
refute_path_exists @spec.build_info_file
|
||||||
|
|
||||||
@installer.write_build_info_file
|
@installer.write_build_info_file
|
||||||
|
@ -1429,6 +1434,30 @@ gem 'other', version
|
||||||
assert_match %r!/gemhome/gems/a-2$!, @installer.dir
|
assert_match %r!/gemhome/gems/a-2$!, @installer.dir
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_default_gem
|
||||||
|
FileUtils.rm_f File.join(Gem.dir, 'specifications')
|
||||||
|
|
||||||
|
@installer.wrappers = true
|
||||||
|
@installer.options[:install_as_default] = true
|
||||||
|
@installer.gem_dir = util_gem_dir @spec
|
||||||
|
@installer.generate_bin
|
||||||
|
|
||||||
|
use_ui @ui do
|
||||||
|
@installer.install
|
||||||
|
end
|
||||||
|
|
||||||
|
assert File.directory? util_inst_bindir
|
||||||
|
installed_exec = File.join util_inst_bindir, 'executable'
|
||||||
|
assert File.exist? installed_exec
|
||||||
|
|
||||||
|
assert File.directory? File.join(Gem.dir, 'specifications')
|
||||||
|
assert File.directory? File.join(Gem.dir, 'specifications', 'default')
|
||||||
|
|
||||||
|
default_spec = eval File.read File.join(Gem.dir, 'specifications', 'default', 'a-2.gemspec')
|
||||||
|
assert_equal Gem::Version.new("2"), default_spec.version
|
||||||
|
assert_equal ['bin/executable'], default_spec.files
|
||||||
|
end
|
||||||
|
|
||||||
def old_ruby_required
|
def old_ruby_required
|
||||||
spec = quick_spec 'old_ruby_required', '1' do |s|
|
spec = quick_spec 'old_ruby_required', '1' do |s|
|
||||||
s.required_ruby_version = '= 1.4.6'
|
s.required_ruby_version = '= 1.4.6'
|
||||||
|
|
|
@ -2,6 +2,21 @@ require 'rubygems/test_case'
|
||||||
require 'rubygems/name_tuple'
|
require 'rubygems/name_tuple'
|
||||||
|
|
||||||
class TestGemNameTuple < Gem::TestCase
|
class TestGemNameTuple < Gem::TestCase
|
||||||
|
|
||||||
|
def test_full_name
|
||||||
|
n = Gem::NameTuple.new "a", Gem::Version.new(0), "ruby"
|
||||||
|
assert_equal "a-0", n.full_name
|
||||||
|
|
||||||
|
n = Gem::NameTuple.new "a", Gem::Version.new(0), nil
|
||||||
|
assert_equal "a-0", n.full_name
|
||||||
|
|
||||||
|
n = Gem::NameTuple.new "a", Gem::Version.new(0), ""
|
||||||
|
assert_equal "a-0", n.full_name
|
||||||
|
|
||||||
|
n = Gem::NameTuple.new "a", Gem::Version.new(0), "other"
|
||||||
|
assert_equal "a-0-other", n.full_name
|
||||||
|
end
|
||||||
|
|
||||||
def test_platform_normalization
|
def test_platform_normalization
|
||||||
n = Gem::NameTuple.new "a", Gem::Version.new(0), "ruby"
|
n = Gem::NameTuple.new "a", Gem::Version.new(0), "ruby"
|
||||||
assert_equal "ruby", n.platform
|
assert_equal "ruby", n.platform
|
||||||
|
@ -12,4 +27,11 @@ class TestGemNameTuple < Gem::TestCase
|
||||||
n = Gem::NameTuple.new "a", Gem::Version.new(0), ""
|
n = Gem::NameTuple.new "a", Gem::Version.new(0), ""
|
||||||
assert_equal "ruby", n.platform
|
assert_equal "ruby", n.platform
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_spec_name
|
||||||
|
n = Gem::NameTuple.new "a", Gem::Version.new(0), "ruby"
|
||||||
|
assert_equal "a-0.gemspec", n.spec_name
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -89,16 +89,19 @@ class TestGemPackage < Gem::Package::TarTestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
expected = {
|
expected = {
|
||||||
'SHA1' => {
|
|
||||||
'metadata.gz' => metadata_sha1,
|
|
||||||
'data.tar.gz' => data_digests['SHA1'].hexdigest,
|
|
||||||
},
|
|
||||||
'SHA512' => {
|
'SHA512' => {
|
||||||
'metadata.gz' => metadata_sha512,
|
'metadata.gz' => metadata_sha512,
|
||||||
'data.tar.gz' => data_digests['SHA512'].hexdigest,
|
'data.tar.gz' => data_digests['SHA512'].hexdigest,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if defined?(OpenSSL::Digest) then
|
||||||
|
expected['SHA1'] = {
|
||||||
|
'metadata.gz' => metadata_sha1,
|
||||||
|
'data.tar.gz' => data_digests['SHA1'].hexdigest,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
assert_equal expected, YAML.load(checksums)
|
assert_equal expected, YAML.load(checksums)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -162,11 +165,56 @@ class TestGemPackage < Gem::Package::TarTestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_build_auto_signed
|
def test_build_auto_signed
|
||||||
|
skip 'openssl is missing' unless defined?(OpenSSL::SSL)
|
||||||
|
|
||||||
FileUtils.mkdir_p File.join(Gem.user_home, '.gem')
|
FileUtils.mkdir_p File.join(Gem.user_home, '.gem')
|
||||||
|
|
||||||
private_key_path = File.join Gem.user_home, '.gem', 'gem-private_key.pem'
|
private_key_path = File.join Gem.user_home, '.gem', 'gem-private_key.pem'
|
||||||
Gem::Security.write PRIVATE_KEY, private_key_path
|
Gem::Security.write PRIVATE_KEY, private_key_path
|
||||||
|
|
||||||
|
public_cert_path = File.join Gem.user_home, '.gem', 'gem-public_cert.pem'
|
||||||
|
FileUtils.cp PUBLIC_CERT_PATH, public_cert_path
|
||||||
|
|
||||||
|
spec = Gem::Specification.new 'build', '1'
|
||||||
|
spec.summary = 'build'
|
||||||
|
spec.authors = 'build'
|
||||||
|
spec.files = ['lib/code.rb']
|
||||||
|
|
||||||
|
FileUtils.mkdir 'lib'
|
||||||
|
|
||||||
|
open 'lib/code.rb', 'w' do |io|
|
||||||
|
io.write '# lib/code.rb'
|
||||||
|
end
|
||||||
|
|
||||||
|
package = Gem::Package.new spec.file_name
|
||||||
|
package.spec = spec
|
||||||
|
|
||||||
|
package.build
|
||||||
|
|
||||||
|
assert_equal Gem::VERSION, spec.rubygems_version
|
||||||
|
assert_path_exists spec.file_name
|
||||||
|
|
||||||
|
reader = Gem::Package.new spec.file_name
|
||||||
|
assert reader.verify
|
||||||
|
|
||||||
|
assert_equal [PUBLIC_CERT.to_pem], reader.spec.cert_chain
|
||||||
|
|
||||||
|
assert_equal %w[metadata.gz metadata.gz.sig
|
||||||
|
data.tar.gz data.tar.gz.sig
|
||||||
|
checksums.yaml.gz checksums.yaml.gz.sig],
|
||||||
|
reader.files
|
||||||
|
|
||||||
|
assert_equal %w[lib/code.rb], reader.contents
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_build_auto_signed_encrypted_key
|
||||||
|
skip 'openssl is missing' unless defined?(OpenSSL::SSL)
|
||||||
|
|
||||||
|
FileUtils.mkdir_p File.join(Gem.user_home, '.gem')
|
||||||
|
|
||||||
|
private_key_path = File.join Gem.user_home, '.gem', 'gem-private_key.pem'
|
||||||
|
FileUtils.cp ENCRYPTED_PRIVATE_KEY_PATH, private_key_path
|
||||||
|
|
||||||
public_cert_path = File.join Gem.user_home, '.gem', 'gem-public_cert.pem'
|
public_cert_path = File.join Gem.user_home, '.gem', 'gem-public_cert.pem'
|
||||||
Gem::Security.write PUBLIC_CERT, public_cert_path
|
Gem::Security.write PUBLIC_CERT, public_cert_path
|
||||||
|
|
||||||
|
@ -216,6 +264,8 @@ class TestGemPackage < Gem::Package::TarTestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_build_signed
|
def test_build_signed
|
||||||
|
skip 'openssl is missing' unless defined?(OpenSSL::SSL)
|
||||||
|
|
||||||
spec = Gem::Specification.new 'build', '1'
|
spec = Gem::Specification.new 'build', '1'
|
||||||
spec.summary = 'build'
|
spec.summary = 'build'
|
||||||
spec.authors = 'build'
|
spec.authors = 'build'
|
||||||
|
@ -250,6 +300,43 @@ class TestGemPackage < Gem::Package::TarTestCase
|
||||||
assert_equal %w[lib/code.rb], reader.contents
|
assert_equal %w[lib/code.rb], reader.contents
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_build_signed_encryped_key
|
||||||
|
skip 'openssl is missing' unless defined?(OpenSSL::SSL)
|
||||||
|
|
||||||
|
spec = Gem::Specification.new 'build', '1'
|
||||||
|
spec.summary = 'build'
|
||||||
|
spec.authors = 'build'
|
||||||
|
spec.files = ['lib/code.rb']
|
||||||
|
spec.cert_chain = [PUBLIC_CERT.to_pem]
|
||||||
|
spec.signing_key = ENCRYPTED_PRIVATE_KEY
|
||||||
|
|
||||||
|
FileUtils.mkdir 'lib'
|
||||||
|
|
||||||
|
open 'lib/code.rb', 'w' do |io|
|
||||||
|
io.write '# lib/code.rb'
|
||||||
|
end
|
||||||
|
|
||||||
|
package = Gem::Package.new spec.file_name
|
||||||
|
package.spec = spec
|
||||||
|
|
||||||
|
package.build
|
||||||
|
|
||||||
|
assert_equal Gem::VERSION, spec.rubygems_version
|
||||||
|
assert_path_exists spec.file_name
|
||||||
|
|
||||||
|
reader = Gem::Package.new spec.file_name
|
||||||
|
assert reader.verify
|
||||||
|
|
||||||
|
assert_equal spec, reader.spec
|
||||||
|
|
||||||
|
assert_equal %w[metadata.gz metadata.gz.sig
|
||||||
|
data.tar.gz data.tar.gz.sig
|
||||||
|
checksums.yaml.gz checksums.yaml.gz.sig],
|
||||||
|
reader.files
|
||||||
|
|
||||||
|
assert_equal %w[lib/code.rb], reader.contents
|
||||||
|
end
|
||||||
|
|
||||||
def test_contents
|
def test_contents
|
||||||
package = Gem::Package.new @gem
|
package = Gem::Package.new @gem
|
||||||
|
|
||||||
|
@ -446,7 +533,7 @@ class TestGemPackage < Gem::Package::TarTestCase
|
||||||
io.write metadata_gz
|
io.write metadata_gz
|
||||||
end
|
end
|
||||||
|
|
||||||
digest = OpenSSL::Digest::SHA1.new
|
digest = Digest::SHA1.new
|
||||||
digest << metadata_gz
|
digest << metadata_gz
|
||||||
|
|
||||||
checksums = {
|
checksums = {
|
||||||
|
@ -478,7 +565,8 @@ class TestGemPackage < Gem::Package::TarTestCase
|
||||||
def test_verify_corrupt
|
def test_verify_corrupt
|
||||||
Tempfile.open 'corrupt' do |io|
|
Tempfile.open 'corrupt' do |io|
|
||||||
data = Gem.gzip 'a' * 10
|
data = Gem.gzip 'a' * 10
|
||||||
io.write tar_file_header('metadata.gz', "\000x", 0644, data.length)
|
io.write \
|
||||||
|
tar_file_header('metadata.gz', "\000x", 0644, data.length, Time.now)
|
||||||
io.write data
|
io.write data
|
||||||
io.rewind
|
io.rewind
|
||||||
|
|
||||||
|
@ -517,6 +605,8 @@ class TestGemPackage < Gem::Package::TarTestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_verify_security_policy
|
def test_verify_security_policy
|
||||||
|
skip 'openssl is missing' unless defined?(OpenSSL::SSL)
|
||||||
|
|
||||||
package = Gem::Package.new @gem
|
package = Gem::Package.new @gem
|
||||||
package.security_policy = Gem::Security::HighSecurity
|
package.security_policy = Gem::Security::HighSecurity
|
||||||
|
|
||||||
|
@ -532,6 +622,8 @@ class TestGemPackage < Gem::Package::TarTestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_verify_security_policy_low_security
|
def test_verify_security_policy_low_security
|
||||||
|
skip 'openssl is missing' unless defined?(OpenSSL::SSL)
|
||||||
|
|
||||||
@spec.cert_chain = [PUBLIC_CERT.to_pem]
|
@spec.cert_chain = [PUBLIC_CERT.to_pem]
|
||||||
@spec.signing_key = PRIVATE_KEY
|
@spec.signing_key = PRIVATE_KEY
|
||||||
|
|
||||||
|
@ -550,6 +642,8 @@ class TestGemPackage < Gem::Package::TarTestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_verify_security_policy_checksum_missing
|
def test_verify_security_policy_checksum_missing
|
||||||
|
skip 'openssl is missing' unless defined?(OpenSSL::SSL)
|
||||||
|
|
||||||
@spec.cert_chain = [PUBLIC_CERT.to_pem]
|
@spec.cert_chain = [PUBLIC_CERT.to_pem]
|
||||||
@spec.signing_key = PRIVATE_KEY
|
@spec.signing_key = PRIVATE_KEY
|
||||||
|
|
||||||
|
@ -605,6 +699,21 @@ class TestGemPackage < Gem::Package::TarTestCase
|
||||||
e.message
|
e.message
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# end #verify tests
|
||||||
|
|
||||||
|
def test_verify_entry
|
||||||
|
entry = Object.new
|
||||||
|
def entry.full_name() raise ArgumentError, 'whatever' end
|
||||||
|
|
||||||
|
package = Gem::Package.new @gem
|
||||||
|
|
||||||
|
e = assert_raises Gem::Package::FormatError do
|
||||||
|
package.verify_entry entry
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_equal "package is corrupt, exception while verifying: whatever (ArgumentError) in #{@gem}", e.message
|
||||||
|
end
|
||||||
|
|
||||||
def test_spec
|
def test_spec
|
||||||
package = Gem::Package.new @gem
|
package = Gem::Package.new @gem
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,8 @@ class TestGemPackageOld < Gem::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_contents_security_policy
|
def test_contents_security_policy
|
||||||
|
skip 'openssl is missing' unless defined?(OpenSSL::SSL)
|
||||||
|
|
||||||
@package.security_policy = Gem::Security::AlmostNoSecurity
|
@package.security_policy = Gem::Security::AlmostNoSecurity
|
||||||
|
|
||||||
assert_raises Gem::Security::Exception do
|
assert_raises Gem::Security::Exception do
|
||||||
|
@ -40,6 +42,8 @@ class TestGemPackageOld < Gem::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_extract_files_security_policy
|
def test_extract_files_security_policy
|
||||||
|
skip 'openssl is missing' unless defined?(OpenSSL::SSL)
|
||||||
|
|
||||||
@package.security_policy = Gem::Security::AlmostNoSecurity
|
@package.security_policy = Gem::Security::AlmostNoSecurity
|
||||||
|
|
||||||
assert_raises Gem::Security::Exception do
|
assert_raises Gem::Security::Exception do
|
||||||
|
@ -52,6 +56,8 @@ class TestGemPackageOld < Gem::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_spec_security_policy
|
def test_spec_security_policy
|
||||||
|
skip 'openssl is missing' unless defined?(OpenSSL::SSL)
|
||||||
|
|
||||||
@package.security_policy = Gem::Security::AlmostNoSecurity
|
@package.security_policy = Gem::Security::AlmostNoSecurity
|
||||||
|
|
||||||
assert_raises Gem::Security::Exception do
|
assert_raises Gem::Security::Exception do
|
||||||
|
@ -60,6 +66,8 @@ class TestGemPackageOld < Gem::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_verify
|
def test_verify
|
||||||
|
skip 'openssl is missing' unless defined?(OpenSSL::SSL)
|
||||||
|
|
||||||
assert @package.verify
|
assert @package.verify
|
||||||
|
|
||||||
@package.security_policy = Gem::Security::NoSecurity
|
@package.security_policy = Gem::Security::NoSecurity
|
||||||
|
|
|
@ -4,8 +4,8 @@ require 'rubygems/package'
|
||||||
class TestGemPackageTarReader < Gem::Package::TarTestCase
|
class TestGemPackageTarReader < Gem::Package::TarTestCase
|
||||||
|
|
||||||
def test_each_entry
|
def test_each_entry
|
||||||
tar = tar_dir_header "foo", "bar", 0
|
tar = tar_dir_header "foo", "bar", 0, Time.now
|
||||||
tar << tar_file_header("bar", "baz", 0, 0)
|
tar << tar_file_header("bar", "baz", 0, 0, Time.now)
|
||||||
|
|
||||||
io = TempIO.new tar
|
io = TempIO.new tar
|
||||||
|
|
||||||
|
@ -25,8 +25,9 @@ class TestGemPackageTarReader < Gem::Package::TarTestCase
|
||||||
def test_rewind
|
def test_rewind
|
||||||
content = ('a'..'z').to_a.join(" ")
|
content = ('a'..'z').to_a.join(" ")
|
||||||
|
|
||||||
str = tar_file_header("lib/foo", "", 010644, content.size) + content +
|
str =
|
||||||
"\0" * (512 - content.size)
|
tar_file_header("lib/foo", "", 010644, content.size, Time.now) +
|
||||||
|
content + "\0" * (512 - content.size)
|
||||||
str << "\0" * 1024
|
str << "\0" * 1024
|
||||||
|
|
||||||
Gem::Package::TarReader.new(TempIO.new(str)) do |tar_reader|
|
Gem::Package::TarReader.new(TempIO.new(str)) do |tar_reader|
|
||||||
|
@ -43,8 +44,8 @@ class TestGemPackageTarReader < Gem::Package::TarTestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_seek
|
def test_seek
|
||||||
tar = tar_dir_header "foo", "bar", 0
|
tar = tar_dir_header "foo", "bar", 0, Time.now
|
||||||
tar << tar_file_header("bar", "baz", 0, 0)
|
tar << tar_file_header("bar", "baz", 0, 0, Time.now)
|
||||||
|
|
||||||
io = TempIO.new tar
|
io = TempIO.new tar
|
||||||
|
|
||||||
|
@ -60,8 +61,8 @@ class TestGemPackageTarReader < Gem::Package::TarTestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_seek_missing
|
def test_seek_missing
|
||||||
tar = tar_dir_header "foo", "bar", 0
|
tar = tar_dir_header "foo", "bar", 0, Time.now
|
||||||
tar << tar_file_header("bar", "baz", 0, 0)
|
tar << tar_file_header("bar", "baz", 0, 0, Time.now)
|
||||||
|
|
||||||
io = TempIO.new tar
|
io = TempIO.new tar
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ class TestGemPackageTarReaderEntry < Gem::Package::TarTestCase
|
||||||
@contents = ('a'..'z').to_a.join * 100
|
@contents = ('a'..'z').to_a.join * 100
|
||||||
|
|
||||||
@tar = ''
|
@tar = ''
|
||||||
@tar << tar_file_header("lib/foo", "", 0, @contents.size)
|
@tar << tar_file_header("lib/foo", "", 0, @contents.size, Time.now)
|
||||||
@tar << @contents
|
@tar << @contents
|
||||||
@tar << "\0" * (512 - (@tar.size % 512))
|
@tar << "\0" * (512 - (@tar.size % 512))
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
require 'rubygems/package/tar_test_case'
|
require 'rubygems/package/tar_test_case'
|
||||||
require 'rubygems/package/tar_writer'
|
require 'rubygems/package/tar_writer'
|
||||||
|
require 'minitest/mock'
|
||||||
|
|
||||||
class TestGemPackageTarWriter < Gem::Package::TarTestCase
|
class TestGemPackageTarWriter < Gem::Package::TarTestCase
|
||||||
|
|
||||||
|
@ -18,112 +19,130 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_add_file
|
def test_add_file
|
||||||
@tar_writer.add_file 'x', 0644 do |f| f.write 'a' * 10 end
|
Time.stub :now, Time.at(1458518157) do
|
||||||
|
@tar_writer.add_file 'x', 0644 do |f| f.write 'a' * 10 end
|
||||||
|
|
||||||
assert_headers_equal(tar_file_header('x', '', 0644, 10),
|
assert_headers_equal(tar_file_header('x', '', 0644, 10, Time.now),
|
||||||
@io.string[0, 512])
|
@io.string[0, 512])
|
||||||
|
end
|
||||||
assert_equal "aaaaaaaaaa#{"\0" * 502}", @io.string[512, 512]
|
assert_equal "aaaaaaaaaa#{"\0" * 502}", @io.string[512, 512]
|
||||||
assert_equal 1024, @io.pos
|
assert_equal 1024, @io.pos
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_add_file_digest
|
def test_add_file_digest
|
||||||
digest_algorithms = OpenSSL::Digest::SHA1, OpenSSL::Digest::SHA512
|
digest_algorithms = Digest::SHA1, Digest::SHA512
|
||||||
|
|
||||||
digests = @tar_writer.add_file_digest 'x', 0644, digest_algorithms do |io|
|
Time.stub :now, Time.at(1458518157) do
|
||||||
io.write 'a' * 10
|
digests = @tar_writer.add_file_digest 'x', 0644, digest_algorithms do |io|
|
||||||
end
|
io.write 'a' * 10
|
||||||
|
end
|
||||||
|
|
||||||
assert_equal '3495ff69d34671d1e15b33a63c1379fdedd3a32a',
|
assert_equal '3495ff69d34671d1e15b33a63c1379fdedd3a32a',
|
||||||
digests['SHA1'].hexdigest
|
digests['SHA1'].hexdigest
|
||||||
assert_equal '4714870aff6c97ca09d135834fdb58a6389a50c1' \
|
assert_equal '4714870aff6c97ca09d135834fdb58a6389a50c1' \
|
||||||
'1fef8ec4afef466fb60a23ac6b7a9c92658f14df' \
|
'1fef8ec4afef466fb60a23ac6b7a9c92658f14df' \
|
||||||
'4993d6b40a4e4d8424196afc347e97640d68de61' \
|
'4993d6b40a4e4d8424196afc347e97640d68de61' \
|
||||||
'e1cf14b0',
|
'e1cf14b0',
|
||||||
digests['SHA512'].hexdigest
|
digests['SHA512'].hexdigest
|
||||||
|
|
||||||
assert_headers_equal(tar_file_header('x', '', 0644, 10),
|
assert_headers_equal(tar_file_header('x', '', 0644, 10, Time.now),
|
||||||
@io.string[0, 512])
|
@io.string[0, 512])
|
||||||
|
end
|
||||||
assert_equal "aaaaaaaaaa#{"\0" * 502}", @io.string[512, 512]
|
assert_equal "aaaaaaaaaa#{"\0" * 502}", @io.string[512, 512]
|
||||||
assert_equal 1024, @io.pos
|
assert_equal 1024, @io.pos
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_add_file_digest_multiple
|
def test_add_file_digest_multiple
|
||||||
digest_algorithms = [OpenSSL::Digest::SHA1, OpenSSL::Digest::SHA512]
|
digest_algorithms = [Digest::SHA1, Digest::SHA512]
|
||||||
|
|
||||||
digests = @tar_writer.add_file_digest 'x', 0644, digest_algorithms do |io|
|
Time.stub :now, Time.at(1458518157) do
|
||||||
io.write 'a' * 10
|
digests = @tar_writer.add_file_digest 'x', 0644, digest_algorithms do |io|
|
||||||
|
io.write 'a' * 10
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_equal '3495ff69d34671d1e15b33a63c1379fdedd3a32a',
|
||||||
|
digests['SHA1'].hexdigest
|
||||||
|
assert_equal '4714870aff6c97ca09d135834fdb58a6389a50c1' \
|
||||||
|
'1fef8ec4afef466fb60a23ac6b7a9c92658f14df' \
|
||||||
|
'4993d6b40a4e4d8424196afc347e97640d68de61' \
|
||||||
|
'e1cf14b0',
|
||||||
|
digests['SHA512'].hexdigest
|
||||||
|
|
||||||
|
assert_headers_equal(tar_file_header('x', '', 0644, 10, Time.now),
|
||||||
|
@io.string[0, 512])
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_equal '3495ff69d34671d1e15b33a63c1379fdedd3a32a',
|
|
||||||
digests['SHA1'].hexdigest
|
|
||||||
assert_equal '4714870aff6c97ca09d135834fdb58a6389a50c1' \
|
|
||||||
'1fef8ec4afef466fb60a23ac6b7a9c92658f14df' \
|
|
||||||
'4993d6b40a4e4d8424196afc347e97640d68de61' \
|
|
||||||
'e1cf14b0',
|
|
||||||
digests['SHA512'].hexdigest
|
|
||||||
|
|
||||||
assert_headers_equal(tar_file_header('x', '', 0644, 10),
|
|
||||||
@io.string[0, 512])
|
|
||||||
assert_equal "aaaaaaaaaa#{"\0" * 502}", @io.string[512, 512]
|
assert_equal "aaaaaaaaaa#{"\0" * 502}", @io.string[512, 512]
|
||||||
assert_equal 1024, @io.pos
|
assert_equal 1024, @io.pos
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_add_file_signer
|
def test_add_file_signer
|
||||||
|
skip 'openssl is missing' unless defined?(OpenSSL::SSL)
|
||||||
|
|
||||||
signer = Gem::Security::Signer.new PRIVATE_KEY, [PUBLIC_CERT]
|
signer = Gem::Security::Signer.new PRIVATE_KEY, [PUBLIC_CERT]
|
||||||
|
|
||||||
@tar_writer.add_file_signed 'x', 0644, signer do |io|
|
Time.stub :now, Time.at(1458518157) do
|
||||||
io.write 'a' * 10
|
@tar_writer.add_file_signed 'x', 0644, signer do |io|
|
||||||
|
io.write 'a' * 10
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_headers_equal(tar_file_header('x', '', 0644, 10, Time.now),
|
||||||
|
@io.string[0, 512])
|
||||||
|
|
||||||
|
|
||||||
|
assert_equal "aaaaaaaaaa#{"\0" * 502}", @io.string[512, 512]
|
||||||
|
|
||||||
|
digest = signer.digest_algorithm.new
|
||||||
|
digest.update 'a' * 10
|
||||||
|
|
||||||
|
signature = signer.sign digest.digest
|
||||||
|
|
||||||
|
assert_headers_equal(tar_file_header('x.sig', '', 0444, signature.length,
|
||||||
|
Time.now),
|
||||||
|
@io.string[1024, 512])
|
||||||
|
assert_equal "#{signature}#{"\0" * (512 - signature.length)}",
|
||||||
|
@io.string[1536, 512]
|
||||||
|
|
||||||
|
assert_equal 2048, @io.pos
|
||||||
end
|
end
|
||||||
|
|
||||||
assert_headers_equal(tar_file_header('x', '', 0644, 10),
|
|
||||||
@io.string[0, 512])
|
|
||||||
assert_equal "aaaaaaaaaa#{"\0" * 502}", @io.string[512, 512]
|
|
||||||
|
|
||||||
digest = signer.digest_algorithm.new
|
|
||||||
digest.update 'a' * 10
|
|
||||||
|
|
||||||
signature = signer.sign digest.digest
|
|
||||||
|
|
||||||
assert_headers_equal(tar_file_header('x.sig', '', 0444, signature.length),
|
|
||||||
@io.string[1024, 512])
|
|
||||||
assert_equal "#{signature}#{"\0" * (512 - signature.length)}",
|
|
||||||
@io.string[1536, 512]
|
|
||||||
|
|
||||||
assert_equal 2048, @io.pos
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_add_file_signer_empty
|
def test_add_file_signer_empty
|
||||||
signer = Gem::Security::Signer.new nil, nil
|
signer = Gem::Security::Signer.new nil, nil
|
||||||
|
|
||||||
@tar_writer.add_file_signed 'x', 0644, signer do |io|
|
Time.stub :now, Time.at(1458518157) do
|
||||||
io.write 'a' * 10
|
|
||||||
end
|
|
||||||
|
|
||||||
assert_headers_equal(tar_file_header('x', '', 0644, 10),
|
@tar_writer.add_file_signed 'x', 0644, signer do |io|
|
||||||
|
io.write 'a' * 10
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_headers_equal(tar_file_header('x', '', 0644, 10, Time.now),
|
||||||
@io.string[0, 512])
|
@io.string[0, 512])
|
||||||
|
end
|
||||||
assert_equal "aaaaaaaaaa#{"\0" * 502}", @io.string[512, 512]
|
assert_equal "aaaaaaaaaa#{"\0" * 502}", @io.string[512, 512]
|
||||||
|
|
||||||
digest = signer.digest_algorithm.new
|
|
||||||
digest.update 'a' * 10
|
|
||||||
|
|
||||||
assert_equal 1024, @io.pos
|
assert_equal 1024, @io.pos
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_add_file_simple
|
def test_add_file_simple
|
||||||
@tar_writer.add_file_simple 'x', 0644, 10 do |io| io.write "a" * 10 end
|
Time.stub :now, Time.at(1458518157) do
|
||||||
|
@tar_writer.add_file_simple 'x', 0644, 10 do |io| io.write "a" * 10 end
|
||||||
|
|
||||||
assert_headers_equal(tar_file_header('x', '', 0644, 10),
|
assert_headers_equal(tar_file_header('x', '', 0644, 10, Time.now),
|
||||||
@io.string[0, 512])
|
@io.string[0, 512])
|
||||||
|
end
|
||||||
|
|
||||||
assert_equal "aaaaaaaaaa#{"\0" * 502}", @io.string[512, 512]
|
assert_equal "aaaaaaaaaa#{"\0" * 502}", @io.string[512, 512]
|
||||||
assert_equal 1024, @io.pos
|
assert_equal 1024, @io.pos
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_add_file_simple_padding
|
def test_add_file_simple_padding
|
||||||
@tar_writer.add_file_simple 'x', 0, 100
|
Time.stub :now, Time.at(1458518157) do
|
||||||
|
@tar_writer.add_file_simple 'x', 0, 100
|
||||||
|
|
||||||
assert_headers_equal tar_file_header('x', '', 0, 100),
|
assert_headers_equal tar_file_header('x', '', 0, 100, Time.now),
|
||||||
@io.string[0, 512]
|
@io.string[0, 512]
|
||||||
|
end
|
||||||
|
|
||||||
assert_equal "\0" * 512, @io.string[512, 512]
|
assert_equal "\0" * 512, @io.string[512, 512]
|
||||||
end
|
end
|
||||||
|
@ -182,11 +201,14 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_mkdir
|
def test_mkdir
|
||||||
@tar_writer.mkdir 'foo', 0644
|
Time.stub :now, Time.at(1458518157) do
|
||||||
|
@tar_writer.mkdir 'foo', 0644
|
||||||
|
|
||||||
assert_headers_equal tar_dir_header('foo', '', 0644),
|
assert_headers_equal tar_dir_header('foo', '', 0644, Time.now),
|
||||||
@io.string[0, 512]
|
@io.string[0, 512]
|
||||||
assert_equal 512, @io.pos
|
|
||||||
|
assert_equal 512, @io.pos
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_split_name
|
def test_split_name
|
||||||
|
|
|
@ -64,4 +64,21 @@ class TestGemPathSupport < Gem::TestCase
|
||||||
def util_path
|
def util_path
|
||||||
ENV["GEM_PATH"].split(File::PATH_SEPARATOR)
|
ENV["GEM_PATH"].split(File::PATH_SEPARATOR)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_initialize_spec
|
||||||
|
ENV["GEM_SPEC_CACHE"] = nil
|
||||||
|
|
||||||
|
ps = Gem::PathSupport.new
|
||||||
|
assert_equal Gem.default_spec_cache_dir, ps.spec_cache_dir
|
||||||
|
|
||||||
|
ENV["GEM_SPEC_CACHE"] = 'bar'
|
||||||
|
|
||||||
|
ps = Gem::PathSupport.new
|
||||||
|
assert_equal ENV["GEM_SPEC_CACHE"], ps.spec_cache_dir
|
||||||
|
|
||||||
|
ENV["GEM_SPEC_CACHE"] = File.join @tempdir, 'spec_cache'
|
||||||
|
|
||||||
|
ps = Gem::PathSupport.new "GEM_SPEC_CACHE" => "foo"
|
||||||
|
assert_equal "foo", ps.spec_cache_dir
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -186,6 +186,24 @@ class TestGemPlatform < Gem::TestCase
|
||||||
assert((x86_darwin8 === Gem::Platform.local), 'universal =~ x86')
|
assert((x86_darwin8 === Gem::Platform.local), 'universal =~ x86')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_equals3_cpu_arm
|
||||||
|
arm = Gem::Platform.new 'arm-linux'
|
||||||
|
armv5 = Gem::Platform.new 'armv5-linux'
|
||||||
|
armv7 = Gem::Platform.new 'armv7-linux'
|
||||||
|
|
||||||
|
util_set_arch 'armv5-linux'
|
||||||
|
assert((arm === Gem::Platform.local), 'arm === armv5')
|
||||||
|
assert((armv5 === Gem::Platform.local), 'armv5 === armv5')
|
||||||
|
refute((armv7 === Gem::Platform.local), 'armv7 === armv5')
|
||||||
|
refute((Gem::Platform.local === arm), 'armv5 === arm')
|
||||||
|
|
||||||
|
util_set_arch 'armv7-linux'
|
||||||
|
assert((arm === Gem::Platform.local), 'arm === armv7')
|
||||||
|
refute((armv5 === Gem::Platform.local), 'armv5 === armv7')
|
||||||
|
assert((armv7 === Gem::Platform.local), 'armv7 === armv7')
|
||||||
|
refute((Gem::Platform.local === arm), 'armv7 === arm')
|
||||||
|
end
|
||||||
|
|
||||||
def test_equals3_version
|
def test_equals3_version
|
||||||
util_set_arch 'i686-darwin8'
|
util_set_arch 'i686-darwin8'
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,13 @@
|
||||||
require 'rubygems/test_case'
|
require 'rubygems/test_case'
|
||||||
require 'ostruct'
|
|
||||||
require 'webrick'
|
require 'webrick'
|
||||||
require 'webrick/https'
|
begin
|
||||||
|
require 'webrick/https'
|
||||||
|
rescue LoadError => e
|
||||||
|
raise unless (e.respond_to?(:path) && e.path == 'openssl') ||
|
||||||
|
e.message =~ / -- openssl$/
|
||||||
|
end
|
||||||
|
|
||||||
require 'rubygems/remote_fetcher'
|
require 'rubygems/remote_fetcher'
|
||||||
require 'rubygems/package'
|
require 'rubygems/package'
|
||||||
require 'minitest/mock'
|
require 'minitest/mock'
|
||||||
|
@ -128,19 +134,7 @@ gems:
|
||||||
|
|
||||||
refute_nil fetcher
|
refute_nil fetcher
|
||||||
assert_kind_of Gem::RemoteFetcher, fetcher
|
assert_kind_of Gem::RemoteFetcher, fetcher
|
||||||
assert_equal proxy_uri, fetcher.instance_variable_get(:@proxy_uri).to_s
|
assert_equal proxy_uri, fetcher.instance_variable_get(:@proxy).to_s
|
||||||
end
|
|
||||||
|
|
||||||
def test_self_fetcher_with_proxy_URI
|
|
||||||
proxy_uri = URI.parse 'http://proxy.example.com'
|
|
||||||
Gem.configuration[:http_proxy] = proxy_uri
|
|
||||||
Gem::RemoteFetcher.fetcher = nil
|
|
||||||
|
|
||||||
fetcher = Gem::RemoteFetcher.fetcher
|
|
||||||
refute_nil fetcher
|
|
||||||
|
|
||||||
assert_kind_of Gem::RemoteFetcher, fetcher
|
|
||||||
assert_equal proxy_uri, fetcher.instance_variable_get(:@proxy_uri)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_fetch_size_bad_uri
|
def test_fetch_size_bad_uri
|
||||||
|
@ -155,7 +149,7 @@ gems:
|
||||||
|
|
||||||
def test_fetch_size_socket_error
|
def test_fetch_size_socket_error
|
||||||
fetcher = Gem::RemoteFetcher.new nil
|
fetcher = Gem::RemoteFetcher.new nil
|
||||||
def fetcher.connection_for(uri)
|
def fetcher.request(uri, request_class, last_modified = nil)
|
||||||
raise SocketError, "tarded"
|
raise SocketError, "tarded"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -414,70 +408,6 @@ gems:
|
||||||
assert_equal @a2.file_name, File.basename(gem)
|
assert_equal @a2.file_name, File.basename(gem)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_explicit_proxy
|
|
||||||
use_ui @ui do
|
|
||||||
fetcher = Gem::RemoteFetcher.new @proxy_uri
|
|
||||||
assert_equal PROXY_DATA.size, fetcher.fetch_size(@server_uri)
|
|
||||||
assert_data_from_proxy fetcher.fetch_path(@server_uri)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_explicit_proxy_with_user_auth
|
|
||||||
use_ui @ui do
|
|
||||||
uri = URI.parse @proxy_uri
|
|
||||||
uri.user, uri.password = 'foo', 'bar'
|
|
||||||
fetcher = Gem::RemoteFetcher.new uri.to_s
|
|
||||||
proxy = fetcher.instance_variable_get("@proxy_uri")
|
|
||||||
assert_equal 'foo', proxy.user
|
|
||||||
assert_equal 'bar', proxy.password
|
|
||||||
assert_data_from_proxy fetcher.fetch_path(@server_uri)
|
|
||||||
end
|
|
||||||
|
|
||||||
use_ui @ui do
|
|
||||||
uri = URI.parse @proxy_uri
|
|
||||||
uri.user, uri.password = 'domain%5Cuser', 'bar'
|
|
||||||
fetcher = Gem::RemoteFetcher.new uri.to_s
|
|
||||||
proxy = fetcher.instance_variable_get("@proxy_uri")
|
|
||||||
assert_equal 'domain\user', fetcher.unescape(proxy.user)
|
|
||||||
assert_equal 'bar', proxy.password
|
|
||||||
assert_data_from_proxy fetcher.fetch_path(@server_uri)
|
|
||||||
end
|
|
||||||
|
|
||||||
use_ui @ui do
|
|
||||||
uri = URI.parse @proxy_uri
|
|
||||||
uri.user, uri.password = 'user', 'my%20pass'
|
|
||||||
fetcher = Gem::RemoteFetcher.new uri.to_s
|
|
||||||
proxy = fetcher.instance_variable_get("@proxy_uri")
|
|
||||||
assert_equal 'user', proxy.user
|
|
||||||
assert_equal 'my pass', fetcher.unescape(proxy.password)
|
|
||||||
assert_data_from_proxy fetcher.fetch_path(@server_uri)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_explicit_proxy_with_user_auth_in_env
|
|
||||||
use_ui @ui do
|
|
||||||
ENV['http_proxy'] = @proxy_uri
|
|
||||||
ENV['http_proxy_user'] = 'foo'
|
|
||||||
ENV['http_proxy_pass'] = 'bar'
|
|
||||||
fetcher = Gem::RemoteFetcher.new nil
|
|
||||||
proxy = fetcher.instance_variable_get("@proxy_uri")
|
|
||||||
assert_equal 'foo', proxy.user
|
|
||||||
assert_equal 'bar', proxy.password
|
|
||||||
assert_data_from_proxy fetcher.fetch_path(@server_uri)
|
|
||||||
end
|
|
||||||
|
|
||||||
use_ui @ui do
|
|
||||||
ENV['http_proxy'] = @proxy_uri
|
|
||||||
ENV['http_proxy_user'] = 'foo\user'
|
|
||||||
ENV['http_proxy_pass'] = 'my bar'
|
|
||||||
fetcher = Gem::RemoteFetcher.new nil
|
|
||||||
proxy = fetcher.instance_variable_get("@proxy_uri")
|
|
||||||
assert_equal 'foo\user', fetcher.unescape(proxy.user)
|
|
||||||
assert_equal 'my bar', fetcher.unescape(proxy.password)
|
|
||||||
assert_data_from_proxy fetcher.fetch_path(@server_uri)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_fetch_path_gzip
|
def test_fetch_path_gzip
|
||||||
fetcher = Gem::RemoteFetcher.new nil
|
fetcher = Gem::RemoteFetcher.new nil
|
||||||
|
|
||||||
|
@ -560,22 +490,6 @@ gems:
|
||||||
assert_equal nil, fetcher.fetch_path(URI.parse(@gem_repo), Time.at(0))
|
assert_equal nil, fetcher.fetch_path(URI.parse(@gem_repo), Time.at(0))
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_get_proxy_from_env_auto_normalizes
|
|
||||||
fetcher = Gem::RemoteFetcher.new(nil)
|
|
||||||
ENV['HTTP_PROXY'] = 'fakeurl:12345'
|
|
||||||
|
|
||||||
assert_equal('http://fakeurl:12345', fetcher.get_proxy_from_env.to_s)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_get_proxy_from_env_empty
|
|
||||||
ENV['HTTP_PROXY'] = ''
|
|
||||||
ENV.delete 'http_proxy'
|
|
||||||
|
|
||||||
fetcher = Gem::RemoteFetcher.new nil
|
|
||||||
|
|
||||||
assert_equal nil, fetcher.send(:get_proxy_from_env)
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_implicit_no_proxy
|
def test_implicit_no_proxy
|
||||||
use_ui @ui do
|
use_ui @ui do
|
||||||
ENV['http_proxy'] = 'http://fakeurl:12345'
|
ENV['http_proxy'] = 'http://fakeurl:12345'
|
||||||
|
@ -611,9 +525,7 @@ gems:
|
||||||
fetcher = Gem::RemoteFetcher.new nil
|
fetcher = Gem::RemoteFetcher.new nil
|
||||||
url = 'http://gems.example.com/redirect'
|
url = 'http://gems.example.com/redirect'
|
||||||
|
|
||||||
conn = Object.new
|
def fetcher.request(uri, request_class, last_modified = nil)
|
||||||
def conn.started?() true end
|
|
||||||
def conn.request(req)
|
|
||||||
url = 'http://gems.example.com/redirect'
|
url = 'http://gems.example.com/redirect'
|
||||||
unless defined? @requested then
|
unless defined? @requested then
|
||||||
@requested = true
|
@requested = true
|
||||||
|
@ -627,9 +539,6 @@ gems:
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
conn = { "#{Thread.current.object_id}:gems.example.com:80" => conn }
|
|
||||||
fetcher.instance_variable_set :@connections, conn
|
|
||||||
|
|
||||||
data = fetcher.fetch_http URI.parse(url)
|
data = fetcher.fetch_http URI.parse(url)
|
||||||
|
|
||||||
assert_equal 'real_path', data
|
assert_equal 'real_path', data
|
||||||
|
@ -639,18 +548,13 @@ gems:
|
||||||
fetcher = Gem::RemoteFetcher.new nil
|
fetcher = Gem::RemoteFetcher.new nil
|
||||||
url = 'http://gems.example.com/redirect'
|
url = 'http://gems.example.com/redirect'
|
||||||
|
|
||||||
conn = Object.new
|
def fetcher.request(uri, request_class, last_modified = nil)
|
||||||
def conn.started?() true end
|
|
||||||
def conn.request(req)
|
|
||||||
url = 'http://gems.example.com/redirect'
|
url = 'http://gems.example.com/redirect'
|
||||||
res = Net::HTTPMovedPermanently.new nil, 301, nil
|
res = Net::HTTPMovedPermanently.new nil, 301, nil
|
||||||
res.add_field 'Location', url
|
res.add_field 'Location', url
|
||||||
res
|
res
|
||||||
end
|
end
|
||||||
|
|
||||||
conn = { "#{Thread.current.object_id}:gems.example.com:80" => conn }
|
|
||||||
fetcher.instance_variable_set :@connections, conn
|
|
||||||
|
|
||||||
e = assert_raises Gem::RemoteFetcher::FetchError do
|
e = assert_raises Gem::RemoteFetcher::FetchError do
|
||||||
fetcher.fetch_http URI.parse(url)
|
fetcher.fetch_http URI.parse(url)
|
||||||
end
|
end
|
||||||
|
@ -658,14 +562,6 @@ gems:
|
||||||
assert_equal "too many redirects (#{url})", e.message
|
assert_equal "too many redirects (#{url})", e.message
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_normalize_uri
|
|
||||||
assert_equal 'FILE://example/', @fetcher.normalize_uri('FILE://example/')
|
|
||||||
assert_equal 'FTP://example/', @fetcher.normalize_uri('FTP://example/')
|
|
||||||
assert_equal 'HTTP://example/', @fetcher.normalize_uri('HTTP://example/')
|
|
||||||
assert_equal 'HTTPS://example/', @fetcher.normalize_uri('HTTPS://example/')
|
|
||||||
assert_equal 'http://example/', @fetcher.normalize_uri('example/')
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_observe_no_proxy_env_single_host
|
def test_observe_no_proxy_env_single_host
|
||||||
use_ui @ui do
|
use_ui @ui do
|
||||||
ENV["http_proxy"] = @proxy_uri
|
ENV["http_proxy"] = @proxy_uri
|
||||||
|
@ -684,117 +580,6 @@ gems:
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_request
|
|
||||||
uri = URI.parse "#{@gem_repo}/specs.#{Gem.marshal_version}"
|
|
||||||
util_stub_connection_for :body => :junk, :code => 200
|
|
||||||
|
|
||||||
response = @fetcher.request uri, Net::HTTP::Get
|
|
||||||
|
|
||||||
assert_equal 200, response.code
|
|
||||||
assert_equal :junk, response.body
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_request_head
|
|
||||||
uri = URI.parse "#{@gem_repo}/specs.#{Gem.marshal_version}"
|
|
||||||
util_stub_connection_for :body => '', :code => 200
|
|
||||||
response = @fetcher.request uri, Net::HTTP::Head
|
|
||||||
|
|
||||||
assert_equal 200, response.code
|
|
||||||
assert_equal '', response.body
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_request_unmodified
|
|
||||||
uri = URI.parse "#{@gem_repo}/specs.#{Gem.marshal_version}"
|
|
||||||
conn = util_stub_connection_for :body => '', :code => 304
|
|
||||||
|
|
||||||
t = Time.now
|
|
||||||
response = @fetcher.request uri, Net::HTTP::Head, t
|
|
||||||
|
|
||||||
assert_equal 304, response.code
|
|
||||||
assert_equal '', response.body
|
|
||||||
|
|
||||||
assert_equal t.rfc2822, conn.payload['if-modified-since']
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_user_agent
|
|
||||||
ua = @fetcher.user_agent
|
|
||||||
|
|
||||||
assert_match %r%^RubyGems/\S+ \S+ Ruby/\S+ \(.*?\)%, ua
|
|
||||||
assert_match %r%RubyGems/#{Regexp.escape Gem::VERSION}%, ua
|
|
||||||
assert_match %r% #{Regexp.escape Gem::Platform.local.to_s} %, ua
|
|
||||||
assert_match %r%Ruby/#{Regexp.escape RUBY_VERSION}%, ua
|
|
||||||
assert_match %r%\(#{Regexp.escape RUBY_RELEASE_DATE} %, ua
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_user_agent_engine
|
|
||||||
util_save_version
|
|
||||||
|
|
||||||
Object.send :remove_const, :RUBY_ENGINE if defined?(RUBY_ENGINE)
|
|
||||||
Object.send :const_set, :RUBY_ENGINE, 'vroom'
|
|
||||||
|
|
||||||
ua = @fetcher.user_agent
|
|
||||||
|
|
||||||
assert_match %r%\) vroom%, ua
|
|
||||||
ensure
|
|
||||||
util_restore_version
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_user_agent_engine_ruby
|
|
||||||
util_save_version
|
|
||||||
|
|
||||||
Object.send :remove_const, :RUBY_ENGINE if defined?(RUBY_ENGINE)
|
|
||||||
Object.send :const_set, :RUBY_ENGINE, 'ruby'
|
|
||||||
|
|
||||||
ua = @fetcher.user_agent
|
|
||||||
|
|
||||||
assert_match %r%\)%, ua
|
|
||||||
ensure
|
|
||||||
util_restore_version
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_user_agent_patchlevel
|
|
||||||
util_save_version
|
|
||||||
|
|
||||||
Object.send :remove_const, :RUBY_PATCHLEVEL
|
|
||||||
Object.send :const_set, :RUBY_PATCHLEVEL, 5
|
|
||||||
|
|
||||||
ua = @fetcher.user_agent
|
|
||||||
|
|
||||||
assert_match %r% patchlevel 5\)%, ua
|
|
||||||
ensure
|
|
||||||
util_restore_version
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_user_agent_revision
|
|
||||||
util_save_version
|
|
||||||
|
|
||||||
Object.send :remove_const, :RUBY_PATCHLEVEL
|
|
||||||
Object.send :const_set, :RUBY_PATCHLEVEL, -1
|
|
||||||
Object.send :remove_const, :RUBY_REVISION if defined?(RUBY_REVISION)
|
|
||||||
Object.send :const_set, :RUBY_REVISION, 6
|
|
||||||
|
|
||||||
ua = @fetcher.user_agent
|
|
||||||
|
|
||||||
assert_match %r% revision 6\)%, ua
|
|
||||||
assert_match %r%Ruby/#{Regexp.escape RUBY_VERSION}dev%, ua
|
|
||||||
ensure
|
|
||||||
util_restore_version
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_user_agent_revision_missing
|
|
||||||
util_save_version
|
|
||||||
|
|
||||||
Object.send :remove_const, :RUBY_PATCHLEVEL
|
|
||||||
Object.send :const_set, :RUBY_PATCHLEVEL, -1
|
|
||||||
Object.send :remove_const, :RUBY_REVISION if defined?(RUBY_REVISION)
|
|
||||||
|
|
||||||
ua = @fetcher.user_agent
|
|
||||||
|
|
||||||
assert_match %r%\(#{Regexp.escape RUBY_RELEASE_DATE}\)%, ua
|
|
||||||
ensure
|
|
||||||
util_restore_version
|
|
||||||
end
|
|
||||||
|
|
||||||
def test_yaml_error_on_size
|
def test_yaml_error_on_size
|
||||||
use_ui @ui do
|
use_ui @ui do
|
||||||
self.class.enable_yaml = false
|
self.class.enable_yaml = false
|
||||||
|
@ -811,6 +596,42 @@ gems:
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_ssl_client_cert_auth_connection
|
||||||
|
skip 'openssl is missing' unless defined?(OpenSSL::SSL)
|
||||||
|
|
||||||
|
ssl_server = self.class.start_ssl_server({
|
||||||
|
:SSLVerifyClient =>
|
||||||
|
OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT})
|
||||||
|
|
||||||
|
temp_ca_cert = File.join(DIR, 'ca_cert.pem')
|
||||||
|
temp_client_cert = File.join(DIR, 'client.pem')
|
||||||
|
|
||||||
|
with_configured_fetcher(
|
||||||
|
":ssl_ca_cert: #{temp_ca_cert}\n" +
|
||||||
|
":ssl_client_cert: #{temp_client_cert}\n") do |fetcher|
|
||||||
|
fetcher.fetch_path("https://localhost:#{ssl_server.config[:Port]}/yaml")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_do_not_allow_invalid_client_cert_auth_connection
|
||||||
|
skip 'openssl is missing' unless defined?(OpenSSL::SSL)
|
||||||
|
|
||||||
|
ssl_server = self.class.start_ssl_server({
|
||||||
|
:SSLVerifyClient =>
|
||||||
|
OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT})
|
||||||
|
|
||||||
|
temp_ca_cert = File.join(DIR, 'ca_cert.pem')
|
||||||
|
temp_client_cert = File.join(DIR, 'invalid_client.pem')
|
||||||
|
|
||||||
|
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
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def test_do_not_allow_insecure_ssl_connection_by_default
|
def test_do_not_allow_insecure_ssl_connection_by_default
|
||||||
ssl_server = self.class.start_ssl_server
|
ssl_server = self.class.start_ssl_server
|
||||||
with_configured_fetcher do |fetcher|
|
with_configured_fetcher do |fetcher|
|
||||||
|
@ -850,18 +671,6 @@ gems:
|
||||||
Gem.configuration = nil
|
Gem.configuration = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def util_stub_connection_for hash
|
|
||||||
def @fetcher.connection= conn
|
|
||||||
@conn = conn
|
|
||||||
end
|
|
||||||
|
|
||||||
def @fetcher.connection_for uri
|
|
||||||
@conn
|
|
||||||
end
|
|
||||||
|
|
||||||
@fetcher.connection = Conn.new OpenStruct.new(hash)
|
|
||||||
end
|
|
||||||
|
|
||||||
def assert_error(exception_class=Exception)
|
def assert_error(exception_class=Exception)
|
||||||
got_exception = false
|
got_exception = false
|
||||||
|
|
||||||
|
@ -882,20 +691,6 @@ gems:
|
||||||
assert_match(/0\.4\.2/, data, "Data is not from proxy")
|
assert_match(/0\.4\.2/, data, "Data is not from proxy")
|
||||||
end
|
end
|
||||||
|
|
||||||
class Conn
|
|
||||||
attr_accessor :payload
|
|
||||||
|
|
||||||
def initialize(response)
|
|
||||||
@response = response
|
|
||||||
self.payload = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def request(req)
|
|
||||||
self.payload = req
|
|
||||||
@response
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class NilLog < WEBrick::Log
|
class NilLog < WEBrick::Log
|
||||||
def log(level, data) #Do nothing
|
def log(level, data) #Do nothing
|
||||||
end
|
end
|
||||||
|
@ -913,9 +708,11 @@ gems:
|
||||||
end
|
end
|
||||||
|
|
||||||
DIR = File.expand_path(File.dirname(__FILE__))
|
DIR = File.expand_path(File.dirname(__FILE__))
|
||||||
DH_PARAM = OpenSSL::PKey::DH.new(128)
|
|
||||||
|
|
||||||
def start_ssl_server(config = {})
|
def start_ssl_server(config = {})
|
||||||
|
raise MiniTest::Skip, 'openssl not installed' unless
|
||||||
|
defined?(OpenSSL::SSL)
|
||||||
|
|
||||||
null_logger = NilLog.new
|
null_logger = NilLog.new
|
||||||
server = WEBrick::HTTPServer.new({
|
server = WEBrick::HTTPServer.new({
|
||||||
:Port => 0,
|
:Port => 0,
|
||||||
|
@ -934,7 +731,7 @@ gems:
|
||||||
server.mount_proc("/insecure_redirect") { |req, res|
|
server.mount_proc("/insecure_redirect") { |req, res|
|
||||||
res.set_redirect(WEBrick::HTTPStatus::MovedPermanently, req.query['to'])
|
res.set_redirect(WEBrick::HTTPStatus::MovedPermanently, req.query['to'])
|
||||||
}
|
}
|
||||||
server.ssl_context.tmp_dh_callback = proc { DH_PARAM }
|
server.ssl_context.tmp_dh_callback = proc { OpenSSL::PKey::DH.new 128 }
|
||||||
t = Thread.new do
|
t = Thread.new do
|
||||||
begin
|
begin
|
||||||
server.start
|
server.start
|
||||||
|
@ -953,8 +750,6 @@ gems:
|
||||||
server
|
server
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def start_server(port, data)
|
def start_server(port, data)
|
||||||
|
@ -1015,24 +810,5 @@ gems:
|
||||||
assert_equal "/home/skillet", @fetcher.correct_for_windows_path(path)
|
assert_equal "/home/skillet", @fetcher.correct_for_windows_path(path)
|
||||||
end
|
end
|
||||||
|
|
||||||
def util_save_version
|
|
||||||
@orig_RUBY_ENGINE = RUBY_ENGINE if defined? RUBY_ENGINE
|
|
||||||
@orig_RUBY_PATCHLEVEL = RUBY_PATCHLEVEL
|
|
||||||
@orig_RUBY_REVISION = RUBY_REVISION if defined? RUBY_REVISION
|
|
||||||
end
|
|
||||||
|
|
||||||
def util_restore_version
|
|
||||||
Object.send :remove_const, :RUBY_ENGINE if defined?(RUBY_ENGINE)
|
|
||||||
Object.send :const_set, :RUBY_ENGINE, @orig_RUBY_ENGINE if
|
|
||||||
defined?(@orig_RUBY_ENGINE)
|
|
||||||
|
|
||||||
Object.send :remove_const, :RUBY_PATCHLEVEL
|
|
||||||
Object.send :const_set, :RUBY_PATCHLEVEL, @orig_RUBY_PATCHLEVEL
|
|
||||||
|
|
||||||
Object.send :remove_const, :RUBY_REVISION if defined?(RUBY_REVISION)
|
|
||||||
Object.send :const_set, :RUBY_REVISION, @orig_RUBY_REVISION if
|
|
||||||
defined?(@orig_RUBY_REVISION)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
239
test/rubygems/test_gem_request.rb
Normal file
239
test/rubygems/test_gem_request.rb
Normal file
|
@ -0,0 +1,239 @@
|
||||||
|
require 'rubygems/test_case'
|
||||||
|
require 'rubygems/request'
|
||||||
|
require 'ostruct'
|
||||||
|
|
||||||
|
class TestGemRequest < Gem::TestCase
|
||||||
|
|
||||||
|
def setup
|
||||||
|
@proxies = %w[http_proxy HTTP_PROXY http_proxy_user HTTP_PROXY_USER http_proxy_pass HTTP_PROXY_PASS no_proxy NO_PROXY]
|
||||||
|
@old_proxies = @proxies.map {|k| ENV[k] }
|
||||||
|
@proxies.each {|k| ENV[k] = nil }
|
||||||
|
|
||||||
|
super
|
||||||
|
|
||||||
|
@proxy_uri = "http://localhost:1234"
|
||||||
|
|
||||||
|
@request = Gem::Request.new nil, nil, nil, nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def teardown
|
||||||
|
super
|
||||||
|
Gem.configuration[:http_proxy] = nil
|
||||||
|
@proxies.each_with_index {|k, i| ENV[k] = @old_proxies[i] }
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_initialize_proxy
|
||||||
|
proxy_uri = 'http://proxy.example.com'
|
||||||
|
|
||||||
|
request = Gem::Request.new nil, nil, nil, proxy_uri
|
||||||
|
|
||||||
|
assert_equal proxy_uri, request.proxy_uri.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_initialize_proxy_URI
|
||||||
|
proxy_uri = 'http://proxy.example.com'
|
||||||
|
|
||||||
|
request = Gem::Request.new nil, nil, nil, URI(proxy_uri)
|
||||||
|
|
||||||
|
assert_equal proxy_uri, request.proxy_uri.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_initialize_proxy_ENV
|
||||||
|
ENV['http_proxy'] = @proxy_uri
|
||||||
|
ENV['http_proxy_user'] = 'foo'
|
||||||
|
ENV['http_proxy_pass'] = 'bar'
|
||||||
|
|
||||||
|
request = Gem::Request.new nil, nil, nil, nil
|
||||||
|
|
||||||
|
proxy = request.proxy_uri
|
||||||
|
|
||||||
|
assert_equal 'foo', proxy.user
|
||||||
|
assert_equal 'bar', proxy.password
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_get_proxy_from_env_domain
|
||||||
|
ENV['http_proxy'] = @proxy_uri
|
||||||
|
ENV['http_proxy_user'] = 'foo\user'
|
||||||
|
ENV['http_proxy_pass'] = 'my bar'
|
||||||
|
|
||||||
|
proxy = @request.get_proxy_from_env
|
||||||
|
|
||||||
|
assert_equal 'foo\user', Gem::UriFormatter.new(proxy.user).unescape
|
||||||
|
assert_equal 'my bar', Gem::UriFormatter.new(proxy.password).unescape
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_get_proxy_from_env_normalize
|
||||||
|
ENV['HTTP_PROXY'] = 'fakeurl:12345'
|
||||||
|
|
||||||
|
assert_equal 'http://fakeurl:12345', @request.get_proxy_from_env.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_get_proxy_from_env_empty
|
||||||
|
ENV['HTTP_PROXY'] = ''
|
||||||
|
ENV.delete 'http_proxy'
|
||||||
|
|
||||||
|
assert_nil @request.get_proxy_from_env
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_fetch
|
||||||
|
uri = URI.parse "#{@gem_repo}/specs.#{Gem.marshal_version}"
|
||||||
|
@request = Gem::Request.new(uri, Net::HTTP::Get, nil, nil)
|
||||||
|
util_stub_connection_for :body => :junk, :code => 200
|
||||||
|
|
||||||
|
response = @request.fetch
|
||||||
|
|
||||||
|
assert_equal 200, response.code
|
||||||
|
assert_equal :junk, response.body
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_fetch_head
|
||||||
|
uri = URI.parse "#{@gem_repo}/specs.#{Gem.marshal_version}"
|
||||||
|
@request = Gem::Request.new(uri, Net::HTTP::Get, nil, nil)
|
||||||
|
util_stub_connection_for :body => '', :code => 200
|
||||||
|
|
||||||
|
response = @request.fetch
|
||||||
|
|
||||||
|
assert_equal 200, response.code
|
||||||
|
assert_equal '', response.body
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_fetch_unmodified
|
||||||
|
uri = URI.parse "#{@gem_repo}/specs.#{Gem.marshal_version}"
|
||||||
|
t = Time.now
|
||||||
|
@request = Gem::Request.new(uri, Net::HTTP::Get, t, nil)
|
||||||
|
conn = util_stub_connection_for :body => '', :code => 304
|
||||||
|
|
||||||
|
response = @request.fetch
|
||||||
|
|
||||||
|
assert_equal 304, response.code
|
||||||
|
assert_equal '', response.body
|
||||||
|
|
||||||
|
assert_equal t.rfc2822, conn.payload['if-modified-since']
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_user_agent
|
||||||
|
ua = Gem::Request.new(nil, nil, nil, nil).user_agent
|
||||||
|
|
||||||
|
assert_match %r%^RubyGems/\S+ \S+ Ruby/\S+ \(.*?\)%, ua
|
||||||
|
assert_match %r%RubyGems/#{Regexp.escape Gem::VERSION}%, ua
|
||||||
|
assert_match %r% #{Regexp.escape Gem::Platform.local.to_s} %, ua
|
||||||
|
assert_match %r%Ruby/#{Regexp.escape RUBY_VERSION}%, ua
|
||||||
|
assert_match %r%\(#{Regexp.escape RUBY_RELEASE_DATE} %, ua
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_user_agent_engine
|
||||||
|
util_save_version
|
||||||
|
|
||||||
|
Object.send :remove_const, :RUBY_ENGINE if defined?(RUBY_ENGINE)
|
||||||
|
Object.send :const_set, :RUBY_ENGINE, 'vroom'
|
||||||
|
|
||||||
|
ua = Gem::Request.new(nil, nil, nil, nil).user_agent
|
||||||
|
|
||||||
|
assert_match %r%\) vroom%, ua
|
||||||
|
ensure
|
||||||
|
util_restore_version
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_user_agent_engine_ruby
|
||||||
|
util_save_version
|
||||||
|
|
||||||
|
Object.send :remove_const, :RUBY_ENGINE if defined?(RUBY_ENGINE)
|
||||||
|
Object.send :const_set, :RUBY_ENGINE, 'ruby'
|
||||||
|
|
||||||
|
ua = Gem::Request.new(nil, nil, nil, nil).user_agent
|
||||||
|
|
||||||
|
assert_match %r%\)%, ua
|
||||||
|
ensure
|
||||||
|
util_restore_version
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_user_agent_patchlevel
|
||||||
|
util_save_version
|
||||||
|
|
||||||
|
Object.send :remove_const, :RUBY_PATCHLEVEL
|
||||||
|
Object.send :const_set, :RUBY_PATCHLEVEL, 5
|
||||||
|
|
||||||
|
ua = Gem::Request.new(nil, nil, nil, nil).user_agent
|
||||||
|
|
||||||
|
assert_match %r% patchlevel 5\)%, ua
|
||||||
|
ensure
|
||||||
|
util_restore_version
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_user_agent_revision
|
||||||
|
util_save_version
|
||||||
|
|
||||||
|
Object.send :remove_const, :RUBY_PATCHLEVEL
|
||||||
|
Object.send :const_set, :RUBY_PATCHLEVEL, -1
|
||||||
|
Object.send :remove_const, :RUBY_REVISION if defined?(RUBY_REVISION)
|
||||||
|
Object.send :const_set, :RUBY_REVISION, 6
|
||||||
|
|
||||||
|
ua = Gem::Request.new(nil, nil, nil, nil).user_agent
|
||||||
|
|
||||||
|
assert_match %r% revision 6\)%, ua
|
||||||
|
assert_match %r%Ruby/#{Regexp.escape RUBY_VERSION}dev%, ua
|
||||||
|
ensure
|
||||||
|
util_restore_version
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_user_agent_revision_missing
|
||||||
|
util_save_version
|
||||||
|
|
||||||
|
Object.send :remove_const, :RUBY_PATCHLEVEL
|
||||||
|
Object.send :const_set, :RUBY_PATCHLEVEL, -1
|
||||||
|
Object.send :remove_const, :RUBY_REVISION if defined?(RUBY_REVISION)
|
||||||
|
|
||||||
|
ua = Gem::Request.new(nil, nil, nil, nil).user_agent
|
||||||
|
|
||||||
|
assert_match %r%\(#{Regexp.escape RUBY_RELEASE_DATE}\)%, ua
|
||||||
|
ensure
|
||||||
|
util_restore_version
|
||||||
|
end
|
||||||
|
|
||||||
|
def util_restore_version
|
||||||
|
Object.send :remove_const, :RUBY_ENGINE if defined?(RUBY_ENGINE)
|
||||||
|
Object.send :const_set, :RUBY_ENGINE, @orig_RUBY_ENGINE if
|
||||||
|
defined?(@orig_RUBY_ENGINE)
|
||||||
|
|
||||||
|
Object.send :remove_const, :RUBY_PATCHLEVEL
|
||||||
|
Object.send :const_set, :RUBY_PATCHLEVEL, @orig_RUBY_PATCHLEVEL
|
||||||
|
|
||||||
|
Object.send :remove_const, :RUBY_REVISION if defined?(RUBY_REVISION)
|
||||||
|
Object.send :const_set, :RUBY_REVISION, @orig_RUBY_REVISION if
|
||||||
|
defined?(@orig_RUBY_REVISION)
|
||||||
|
end
|
||||||
|
|
||||||
|
def util_save_version
|
||||||
|
@orig_RUBY_ENGINE = RUBY_ENGINE if defined? RUBY_ENGINE
|
||||||
|
@orig_RUBY_PATCHLEVEL = RUBY_PATCHLEVEL
|
||||||
|
@orig_RUBY_REVISION = RUBY_REVISION if defined? RUBY_REVISION
|
||||||
|
end
|
||||||
|
|
||||||
|
def util_stub_connection_for hash
|
||||||
|
def @request.connection= conn
|
||||||
|
@conn = conn
|
||||||
|
end
|
||||||
|
|
||||||
|
def @request.connection_for uri
|
||||||
|
@conn
|
||||||
|
end
|
||||||
|
|
||||||
|
@request.connection = Conn.new OpenStruct.new(hash)
|
||||||
|
end
|
||||||
|
|
||||||
|
class Conn
|
||||||
|
attr_accessor :payload
|
||||||
|
|
||||||
|
def initialize(response)
|
||||||
|
@response = response
|
||||||
|
self.payload = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def request(req)
|
||||||
|
self.payload = req
|
||||||
|
@response
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue