1
0
Fork 0
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:
drbrain 2013-07-09 23:21:36 +00:00
parent cd9f9e4719
commit 47f0248b08
113 changed files with 4964 additions and 2610 deletions

View file

@ -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>
* lib/rubygems/ext/ext_conf_builder.rb: Remove siteconf file after

View file

@ -8,7 +8,7 @@
require 'rbconfig'
module Gem
VERSION = '2.0.4'
VERSION = '2.1.0'
end
# Must be first since it unloads the prelude from 1.9.2
@ -143,6 +143,14 @@ module Gem
specifications
]
##
# Subdirectories in a gem repository for default gems
REPOSITORY_DEFAULT_GEM_SUBDIRECTORIES = %w[
gems
specifications/default
]
@@win_platform = nil
@configuration = nil
@ -379,6 +387,10 @@ module Gem
paths.path
end
def self.spec_cache_dir
paths.spec_cache_dir
end
##
# Quietly ensure the Gem directory +dir+ contains all the proper
# 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.
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
File.umask old_umask | 002
@ -398,7 +427,7 @@ module Gem
options[:mode] = mode if mode
REPOSITORY_SUBDIRECTORIES.each do |name|
subdirs.each do |name|
subdir = File.join dir, name
next if File.exist? subdir
FileUtils.mkdir_p subdir, options rescue nil
@ -971,10 +1000,33 @@ module Gem
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)
new_format, prefix_pattern = nil
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
end
end

View file

@ -1,4 +1,7 @@
class Gem::AvailableSet
include Enumerable
Tuple = Struct.new(:spec, :source)
def initialize
@ -36,6 +39,28 @@ class Gem::AvailableSet
self
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?
@set.empty?
end
@ -66,6 +91,49 @@ class Gem::AvailableSet
f.source
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!
return self if empty?

View 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

View file

@ -1,6 +1,11 @@
require 'rubygems/command'
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
@ -21,7 +26,8 @@ class Gem::Commands::CertCommand < Gem::Command
OptionParser.accept OpenSSL::PKey::RSA do |key_file|
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
raise OptionParser::InvalidArgument, "#{key_file}: does not exist"
rescue OpenSSL::PKey::RSAError
@ -115,17 +121,32 @@ class Gem::Commands::CertCommand < Gem::Command
end
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"
passphrase_confirmation = ask_for_password 'Please repeat the passphrase for your Private Key:'
say "\n"
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
key_path = Gem::Security.write key, "gem-private_key.pem"
cert_path = Gem::Security.write cert, "gem-public_cert.pem"
say "Certificate: #{cert_path}"
if key_path
say "Private Key: #{key_path}"
say "Don't forget to move the key file to somewhere private!"
end
end
def certificates_matching filter
return enum_for __method__, filter unless block_given?
@ -198,7 +219,8 @@ For further reading on signing gems see `ri Gem::Security`.
def load_default_key
key_file = File.join Gem.default_key_path
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
alert_error \
"--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
end
end
end if defined?(OpenSSL::SSL)

View file

@ -9,7 +9,8 @@ class Gem::Commands::CleanupCommand < Gem::Command
'Clean up old versions of installed gems in the local repository',
: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
end
@ -162,4 +163,3 @@ are not removed.
end
end

View file

@ -99,6 +99,8 @@ lib/rubygems/defaults/operating_system.rb
out << " - EXECUTABLE DIRECTORY: #{Gem.bindir}\n"
out << " - SPEC CACHE DIRECTORY: #{Gem.spec_cache_dir}\n"
out << " - RUBYGEMS PLATFORMS:\n"
Gem.platforms.each do |platform|
out << " - #{platform}\n"
@ -107,11 +109,9 @@ lib/rubygems/defaults/operating_system.rb
out << " - GEM PATHS:\n"
out << " - #{Gem.dir}\n"
path = Gem.path.dup
path.delete Gem.dir
path.each do |p|
out << " - #{p}\n"
end
gem_path = Gem.path.dup
gem_path.delete Gem.dir
add_path out, gem_path
out << " - GEM CONFIGURATION:\n"
Gem.configuration.each do |name, value|
@ -124,6 +124,11 @@ lib/rubygems/defaults/operating_system.rb
out << " - #{s}\n"
end
out << " - SHELL PATH:\n"
shell_path = ENV['PATH'].split(File::PATH_SEPARATOR)
add_path out, shell_path
else
raise Gem::CommandLineError, "Unknown environment option [#{arg}]"
end
@ -131,5 +136,11 @@ lib/rubygems/defaults/operating_system.rb
true
end
def add_path out, path
path.each do |component|
out << " - #{component}\n"
end
end
end

View file

@ -46,6 +46,10 @@ Some examples of 'gem' usage.
* Update all gems on your system:
gem update
* Update your local version of RubyGems
gem update --system
EOF
PLATFORMS = <<-'EOF'
@ -55,8 +59,9 @@ your current platform by running `gem environment`.
RubyGems matches platforms as follows:
* The CPU must match exactly, unless one of the platforms has
"universal" as the CPU.
* The CPU must match exactly unless one of the platforms has
"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 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
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:
x86-freebsd # Any FreeBSD version on an x86 CPU
universal-darwin-8 # Darwin 8 only gems that run on any CPU
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
Gem::Platform::CURRENT. This will correctly mark the gem with your ruby's
@ -119,7 +133,7 @@ platform.
if command then
command.summary
else
"[No command found for #{cmd_name}, bug?]"
"[No command found for #{cmd_name}]"
end
summary = wrap(summary, summary_width).split "\n"

View file

@ -4,8 +4,6 @@ require 'rubygems/dependency_installer'
require 'rubygems/local_remote_options'
require 'rubygems/validator'
require 'rubygems/version_option'
require 'rubygems/install_message' # must come before rdoc for messaging
require 'rubygems/rdoc'
##
# Gem installer command line tool
@ -40,6 +38,12 @@ class Gem::Commands::InstallCommand < Gem::Command
o[:gemdeps] = v
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
end
@ -154,6 +158,13 @@ to write the specification by hand. For example:
terminate_interaction 1
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|
gem_version ||= options[:version]

View file

@ -31,9 +31,15 @@ class Gem::Commands::OwnerCommand < Gem::Command
add_option '-r', '--remove EMAIL', 'Remove an owner' do |value, options|
options[:remove] << value
end
add_option '-h', '--host HOST', 'Use another gemcutter-compatible host' do |value, options|
options[:host] = value
end
end
def execute
@host = options[:host]
sign_in
name = get_one_gem_name

View file

@ -30,6 +30,12 @@ class Gem::Commands::PristineCommand < Gem::Command
options[:only_executables] = value
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')
end
@ -104,16 +110,21 @@ with extensions.
Gem::RemoteFetcher.fetcher.download_to_cache dep
end
# TODO use installer options
env_shebang =
if options.include? :env_shebang then
options[:env_shebang]
else
install_defaults = Gem::ConfigFile::PLATFORM_DEFAULTS['install']
installer_env_shebang = install_defaults.to_s['--env-shebang']
install_defaults.to_s['--env-shebang']
end
installer = Gem::Installer.new(gem,
:wrappers => true,
:force => true,
:install_dir => spec.base_dir,
:env_shebang => installer_env_shebang,
:env_shebang => env_shebang,
:build_args => spec.build_args)
if options[:only_executables] then
installer.generate_bin
else

View file

@ -48,7 +48,7 @@ class Gem::Commands::SourcesCommand < Gem::Command
options[:update])
if options[:clear_all] then
path = File.join Gem.user_home, '.gem', 'specs'
path = Gem.spec_cache_dir
FileUtils.rm_rf path
unless File.exist? path then

View file

@ -67,6 +67,12 @@ class Gem::Commands::UninstallCommand < Gem::Command
options[:force] = value
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_platform_option
end

View file

@ -140,6 +140,11 @@ class Gem::ConfigFile
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
# 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_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
@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.
To fix this error run:
\tchmod 0600 #{credentials_path}
You should reset your credentials at:
\thttps://rubygems.org/profile/edit
@ -309,6 +319,9 @@ if you believe they were disclosed to a third party.
@rubygems_api_key = api_key
end
YAMLErrors = [ArgumentError]
YAMLErrors << Psych::SyntaxError if defined?(Psych::SyntaxError)
def load_file(filename)
Gem.load_yaml
@ -321,8 +334,8 @@ if you believe they were disclosed to a third party.
return {}
end
return content
rescue ArgumentError
warn "Failed to load #{filename}"
rescue *YAMLErrors => e
warn "Failed to load #{filename}, #{e.to_s}"
rescue Errno::EACCES
warn "Failed to load #{filename} due to permissions problem."
end

View file

@ -57,7 +57,7 @@ module Kernel
#--
# 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
}

View file

@ -14,6 +14,14 @@ module Gem
%w[https://rubygems.org/]
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
# specified in the environment

View file

@ -203,6 +203,8 @@ class Gem::Dependency
requirement.satisfied_by? version
end
alias === =~
# DOC: this method needs either documented or :nodoc'd
def match? obj, version=nil
@ -250,10 +252,10 @@ class Gem::Dependency
# DOC: this method needs either documented or :nodoc'd
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 ===
requirement.satisfied_by? spec.version
}
}.map(&:to_spec)
if platform_only
matches.reject! { |spec|

View file

@ -1,11 +1,12 @@
require 'rubygems'
require 'rubygems/dependency_list'
require 'rubygems/dependency_resolver'
require 'rubygems/package'
require 'rubygems/installer'
require 'rubygems/spec_fetcher'
require 'rubygems/user_interaction'
require 'rubygems/source_local'
require 'rubygems/source_specific_file'
require 'rubygems/source/local'
require 'rubygems/source/specific_file'
require 'rubygems/available_set'
##
@ -15,15 +16,7 @@ class Gem::DependencyInstaller
include Gem::UserInteraction
attr_reader :gems_to_install
attr_reader :installed_gems
##
# Documentation types. For use by the Gem.done_installing hook
attr_reader :document
DEFAULT_OPTIONS = {
DEFAULT_OPTIONS = { # :nodoc:
:env_shebang => false,
:document => %w[ri],
:domain => :both, # HACK dup
@ -35,8 +28,30 @@ class Gem::DependencyInstaller
:wrappers => true,
:build_args => nil,
:build_docs_in_background => false,
:install_as_default => false
}.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.
#
@ -56,7 +71,8 @@ class Gem::DependencyInstaller
# :wrappers:: 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
if options[:install_dir] then
@ -82,6 +98,7 @@ class Gem::DependencyInstaller
@wrappers = options[:wrappers]
@build_args = options[:build_args]
@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
# we absolutely must.
@ -93,128 +110,14 @@ class Gem::DependencyInstaller
@cache_dir = options[:cache_dir] || @install_dir
# Set with any errors that SpecFetcher finds while search through
# gemspecs for a dep
@errors = nil
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:
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
def add_found_dependencies to_do, dependency_list # :nodoc:
seen = {}
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
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
# +version+. Returns an Array of specs and sources required for
# 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,
prerelease = false)
prerelease = false
set = Gem::AvailableSet.new
@ -303,6 +283,59 @@ class Gem::DependencyInstaller
@available = set
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
# of installed gem specifications.
@ -318,61 +351,30 @@ class Gem::DependencyInstaller
# separately.
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 = []
gather_dependencies
# REFACTOR is the last gem always the one that the user requested?
# This code assumes that but is that actually validated by the code?
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,
options = {
:bin_dir => @bin_dir,
:development => is_dev,
:build_args => @build_args,
: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
:install_as_default => @install_as_default
}
options[:install_dir] = @install_dir if @only_install_dir
spec = inst.install
@installed_gems << spec
request_set.install options do |_, installer|
@installed_gems << installer.spec if installer
end
@installed_gems.sort!
# 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
# the background or not, and what to call it.
@ -385,18 +387,34 @@ class Gem::DependencyInstaller
@installed_gems
end
def in_background what
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
def install_development_deps # :nodoc:
if @development and @dev_shallow then
:shallow
elsif @development then
:all
else
:none
end
end
yield unless fork_happened
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

View file

@ -1,461 +1,107 @@
require 'rubygems'
require 'rubygems/dependency'
require 'rubygems/exceptions'
require 'rubygems/util/list'
require 'uri'
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.
# 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
attr_reader :conflict
def conflicting_dependencies
@conflict.conflicting_dependencies
end
end
# Raised when a dependency requests a gem for which there is
# no spec.
#
class UnsatisfiableDepedencyError < Gem::Exception
def initialize(dep)
super "unable to find any gem matching dependency '#{dep}'"
@dependency = dep
end
attr_reader :dependency
end
# Raised when dependencies conflict and create the inability to
# find a valid possible spec for a request.
#
class ImpossibleDependenciesError < Gem::Exception
def initialize(request, conflicts)
s = conflicts.size == 1 ? "" : "s"
super "detected #{conflicts.size} conflict#{s} with dependency '#{request.dependency}'"
@request = request
@conflicts = conflicts
end
def dependency
@request.dependency
end
attr_reader :conflicts
end
# 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.
#
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
# The global rubygems pool, available via the rubygems.org API.
# 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+.
#
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
# 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 << 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
# 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 IndexSpecification
def initialize(set, name, version, source, plat)
@set = set
@name = name
@version = version
@source = source
@platform = plat
@spec = nil
end
attr_reader :name, :version, :source
def full_name
"#{@name}-#{@version}"
end
def spec
@spec ||= @set.load_spec(@name, @version, @source)
end
def dependencies
spec.dependencies
end
end
# The global rubygems pool represented via the traditional
# source index.
#
class 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 << 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
class Gem::DependencyResolver
##
# Contains all the conflicts encountered while doing resolution
#
attr_reader :conflicts
# Proceed with resolution! Returns an array of ActivationRequest
# objects.
#
def resolve
@conflicts = []
attr_accessor :development
needed = @needed.map { |n| DependencyRequest.new(n, nil) }
attr_reader :missing
res = resolve_for needed, []
##
# When a missing dependency, don't stop. Just go on and record what was
# missing.
if res.kind_of? DependencyConflict
raise DependencyResolutionError.new(res)
end
attr_accessor :soft_missing
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
false
end
end
end
# Specifies a Specification object that should be activated.
# 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
attr_reader :spec, :request
# 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 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
def self.compose_sets *sets
Gem::DependencyResolver::ComposedSet.new(*sets)
end
##
# Indicates if the requested gem has already been installed.
# Provide a DependencyResolver that queries only against the already
# installed gems.
def installed?
this_spec = full_spec
Gem::Specification.any? do |s|
s == this_spec
end
end
def self.for_current_gems needed
new needed, Gem::DependencyResolver::CurrentSet.new
end
def requests(s, act)
reqs = []
s.dependencies.each do |d|
next unless d.type == :runtime
reqs << DependencyRequest.new(d, act)
##
# 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 = nil
@set = set || Gem::DependencyResolver::IndexSet.new
@needed = needed
@conflicts = nil
@development = false
@missing = []
@soft_missing = false
end
@set.prefetch(reqs)
def requests s, act, reqs=nil
s.dependencies.reverse_each do |d|
next if d.type == :development and not @development
reqs = Gem::List.new Gem::DependencyResolver::DependencyRequest.new(d, act), reqs
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
##
# Proceed with resolution! Returns an array of ActivationRequest objects.
def resolve
@conflicts = []
needed = nil
@needed.reverse_each do |n|
needed = Gem::List.new(Gem::DependencyResolver::DependencyRequest.new(n, nil), needed)
end
res = resolve_for needed, nil
raise Gem::DependencyResolutionError, res if
res.kind_of? Gem::DependencyResolver::DependencyConflict
res.to_a
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
while needed
dep = needed.value
needed = needed.tail
# If there is already a spec activated for the requested name...
if existing = specs.find { |s| dep.name == s.name }
if specs && existing = specs.find { |s| dep.name == s.name }
# then we're done since this new dep matches the
# existing spec.
@ -471,10 +117,12 @@ module Gem
# it on the requester's request itself.
#
if existing.others_possible?
conflict = DependencyConflict.new(dep, existing)
conflict =
Gem::DependencyResolver::DependencyConflict.new dep, existing
else
depreq = existing.request.requester.request
conflict = DependencyConflict.new(depreq, existing, dep)
conflict =
Gem::DependencyResolver::DependencyConflict.new depreq, existing, dep
end
@conflicts << conflict
@ -482,35 +130,39 @@ module Gem
end
# Get a list of all specs that satisfy dep
possible = @set.find_all(dep)
possible = @set.find_all dep
case possible.size
when 0
@missing << dep
unless @soft_missing
# If there are none, then our work here is done.
raise UnsatisfiableDepedencyError.new(dep)
raise Gem::UnsatisfiableDependencyError, dep
end
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)
act = Gem::DependencyResolver::ActivationRequest.new spec, dep, false
specs << act
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
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 }
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.
@ -530,16 +182,16 @@ module Gem
# Recursively call #resolve_for with this spec
# and add it's dependencies into the picture...
act = ActivationRequest.new(s, dep)
act = Gem::DependencyResolver::ActivationRequest.new s, dep
try = requests(s, act) + needed
try = requests(s, act, needed)
res = resolve_for(try, specs + [act])
res = resolve_for try, Gem::List.prepend(specs, act)
# While trying to resolve these dependencies, there may
# be a conflict!
if res.kind_of? DependencyConflict
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+)
@ -565,11 +217,24 @@ module Gem
# 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)
raise Gem::ImpossibleDependenciesError.new(dep, conflicts)
end
end
specs
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'

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View file

@ -16,6 +16,28 @@ class Gem::DependencyError < 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.
@ -65,6 +87,42 @@ class Gem::SpecificGemNotFoundException < Gem::GemNotFoundException
attr_reader :name, :version, :errors
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
##
@ -107,3 +165,26 @@ class Gem::SystemExitException < SystemExit
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:

View file

@ -18,7 +18,7 @@ class Gem::Ext::Builder
# try to find make program from Ruby configure arguments first
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
make_program = (/mswin/ =~ RUBY_PLATFORM) ? 'nmake' : 'make'
end

View file

@ -33,17 +33,11 @@ class Gem::GemRunner
##
# Run the gem command with the following arguments.
def run(args)
if args.include?('--')
# 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
def run args
build_args = extract_build_args args
do_configuration args
cmd = @command_manager_class.instance
cmd.command_names.each do |command_name|
@ -60,6 +54,20 @@ class Gem::GemRunner
cmd.run Gem.configuration.args, build_args
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
def do_configuration(args)

View file

@ -1,11 +1,17 @@
require 'rubygems/remote_fetcher'
##
# Utility methods for using the RubyGems API.
module Gem::GemcutterUtilities
# TODO: move to Gem::Command
OptionParser.accept Symbol do |value|
value.to_sym
end
attr_writer :host
##
# Add the --key option
@ -17,6 +23,9 @@ module Gem::GemcutterUtilities
end
end
##
# The API key from the command options or from the user's configuration.
def api_key
if options[:key] then
verify_api_key options[:key]
@ -27,6 +36,47 @@ module Gem::GemcutterUtilities
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
return if Gem.configuration.rubygems_api_key
@ -55,53 +105,9 @@ module Gem::GemcutterUtilities
end
end
attr_writer :host
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
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
##
# Retrieves the pre-configured API key +key+ or terminates interaction with
# an error.
def verify_api_key(key)
if Gem.configuration.api_keys.key? key then
@ -112,4 +118,29 @@ module Gem::GemcutterUtilities
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

View 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

View file

@ -26,6 +26,9 @@ module Gem::InstallUpdateOptions
OptionParser.accept Gem::Security::Policy do |value|
require 'rubygems/security'
raise OptionParser::InvalidArgument, 'OpenSSL not installed' unless
defined?(Gem::Security::HighSecurity)
value = Gem::Security::Policies[value]
valid = Gem::Security::Policies.keys.sort
message = "#{value} (#{valid.join ', '} are valid)"

View file

@ -213,6 +213,10 @@ class Gem::Installer
FileUtils.mkdir_p gem_dir
if @options[:install_as_default]
extract_bin
write_default_spec
else
extract_files
build_extensions
@ -222,6 +226,7 @@ class Gem::Installer
generate_bin
write_spec
write_cache_file
end
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"
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
# specifications directory.
@ -337,6 +350,16 @@ class Gem::Installer
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
@ -538,13 +561,13 @@ class Gem::Installer
:bin_dir => nil,
:env_shebang => false,
:force => false,
:install_dir => Gem.dir,
:only_install_dir => false
}.merge options
@env_shebang = options[:env_shebang]
@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]
@format_executable = options[:format_executable]
@security_policy = options[:security_policy]
@ -716,6 +739,15 @@ EOF
@package.extract_files gem_dir
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.
@ -756,7 +788,11 @@ EOF
ensure_loadable_spec
if options[:install_as_default]
Gem.ensure_default_gem_subdirectories gem_home
else
Gem.ensure_gem_subdirectories gem_home
end
return true if @force

View file

@ -42,6 +42,20 @@ class Gem::NameTuple
new nil, Gem::Version.new(0), nil
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.
@ -59,12 +73,7 @@ class Gem::NameTuple
# Return the name that the gemspec file would be
def spec_name
case @platform
when nil, 'ruby', ''
"#{@name}-#{@version}.gemspec"
else
"#{@name}-#{@version}-#{@platform}.gemspec"
end
"#{full_name}.gemspec"
end
##
@ -74,10 +83,12 @@ class Gem::NameTuple
[@name, @version, @platform]
end
def to_s
def inspect # :nodoc:
"#<Gem::NameTuple #{@name}, #{@version}, #{@platform}>"
end
alias to_s inspect # :nodoc:
def <=> other
to_a <=> other.to_a
end

View file

@ -280,11 +280,16 @@ EOM
algorithms = if @checksums then
@checksums.keys
else
[Gem::Security::DIGEST_NAME]
[Gem::Security::DIGEST_NAME].compact
end
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?
@ -298,8 +303,11 @@ EOM
##
# 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
FileUtils.mkdir_p destination_dir
@ -310,7 +318,7 @@ EOM
reader.each do |entry|
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
end
@ -324,10 +332,15 @@ EOM
# If an entry in the archive contains a relative path above
# +destination_dir+ or an absolute path is encountered an exception is
# 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|
tar.each do |entry|
next unless File.fnmatch pattern, entry.full_name
destination = install_location entry.full_name, destination_dir
FileUtils.rm_rf destination
@ -428,12 +441,13 @@ EOM
# certificate and key are not present only checksum generation is set up.
def setup_signer
passphrase = ENV['GEM_PRIVATE_KEY_PASSPHRASE']
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.cert_chain = @signer.cert_chain.map { |cert| cert.to_s }
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
@signer.cert_chain
end
@ -510,17 +524,16 @@ EOM
end
##
# Verifies the files of the +gem+
# Verifies +entry+ in a .gem file.
def verify_files gem
gem.each do |entry|
def verify_entry entry
file_name = entry.full_name
@files << file_name
case file_name
when /\.sig$/ then
@signatures[$`] = entry.read if @security_policy
next
return
else
digest entry
end
@ -531,6 +544,18 @@ EOM
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+
def verify_files gem
gem.each do |entry|
verify_entry entry
end
unless @spec then

View file

@ -71,7 +71,7 @@ class Gem::Package::TarTestCase < Gem::TestCase
SP(Z(to_oct(sum, 6)))
end
def header(type, fname, dname, length, mode, checksum = nil)
def header(type, fname, dname, length, mode, mtime, checksum = nil)
checksum ||= " " * 8
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 gid[8]; ditto
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
type, # char typeflag[1]; file: "0" dir: "5"
"\0" * 100, # char linkname[100]; ASCII + (Z unless filled)
@ -105,16 +105,16 @@ class Gem::Package::TarTestCase < Gem::TestCase
ret
end
def tar_dir_header(name, prefix, mode)
h = header("5", name, prefix, 0, mode)
def tar_dir_header(name, prefix, mode, mtime)
h = header("5", name, prefix, 0, mode, mtime)
checksum = calc_checksum(h)
header("5", name, prefix, 0, mode, checksum)
header("5", name, prefix, 0, mode, mtime, checksum)
end
def tar_file_header(fname, dname, mode, length)
h = header("0", fname, dname, length, mode)
def tar_file_header(fname, dname, mode, length, mtime)
h = header("0", fname, dname, length, mode, mtime)
checksum = calc_checksum(h)
header("0", fname, dname, length, mode, checksum)
header("0", fname, dname, length, mode, mtime, checksum)
end
def to_oct(n, pad_size)
@ -130,7 +130,7 @@ class Gem::Package::TarTestCase < Gem::TestCase
end
def util_dir_entry
util_entry tar_dir_header("foo", "bar", 0)
util_entry tar_dir_header("foo", "bar", 0, Time.now)
end
end

View file

@ -4,6 +4,8 @@
# See LICENSE.txt for additional licensing information.
#++
require 'digest'
##
# Allows writing of tar files
@ -121,7 +123,8 @@ class Gem::Package::TarWriter
@io.pos = init_pos
header = Gem::Package::TarHeader.new :name => name, :mode => mode,
:size => size, :prefix => prefix
:size => size, :prefix => prefix,
:mtime => Time.now
@io.write header
@io.pos = final_pos
@ -140,7 +143,15 @@ class Gem::Package::TarWriter
def add_file_digest name, mode, digest_algorithms # :yields: io
digests = digest_algorithms.map do |digest_algorithm|
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
digests = Hash[*digests.flatten]
@ -165,22 +176,32 @@ class Gem::Package::TarWriter
def add_file_signed name, mode, signer
digest_algorithms = [
signer.digest_algorithm,
OpenSSL::Digest::SHA512,
].uniq
Digest::SHA512,
].compact.uniq
digests = add_file_digest name, mode, digest_algorithms do |io|
yield io
end
signature_digest = digests.values.find do |digest|
digest.name == signer.digest_name
signature_digest = digests.values.compact.find do |digest|
digest_name =
if digest.respond_to? :name then
digest.name
else
/::([^:]+)$/ =~ digest.class.name
$1
end
digest_name == signer.digest_name
end
if signer.key then
signature = signer.sign signature_digest.digest
add_file_simple "#{name}.sig", 0444, signature.length do |io|
io.write signature
end if signature
end
end
digests
end
@ -195,7 +216,8 @@ class Gem::Package::TarWriter
name, prefix = split_name name
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
os = BoundedStream.new @io, size
@ -256,7 +278,8 @@ class Gem::Package::TarWriter
header = Gem::Package::TarHeader.new :name => name, :mode => mode,
:typeflag => "5", :size => 0,
:prefix => prefix
:prefix => prefix,
:mtime => Time.now
@io.write header

View file

@ -12,6 +12,10 @@ class Gem::PathSupport
# Array of paths to search for Gems.
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
@ -28,6 +32,10 @@ class Gem::PathSupport
end
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
private

View file

@ -2,6 +2,8 @@ require "rubygems/deprecate"
##
# Available list of platforms for targeting Gem installations.
#
# See `gem help platform` for information on platform matching.
class Gem::Platform
@ -129,12 +131,16 @@ class Gem::Platform
# 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
# 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)
return nil unless Gem::Platform === other
# 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 == other.os and

View file

@ -1,6 +1,7 @@
require 'rubygems'
require 'rubygems/request'
require 'rubygems/uri_formatter'
require 'rubygems/user_interaction'
require 'uri'
require 'resolv'
##
@ -71,17 +72,7 @@ class Gem::RemoteFetcher
Socket.do_not_reverse_lookup = true
@connections = {}
@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
@proxy = proxy
@dns = dns
end
@ -200,7 +191,7 @@ class Gem::RemoteFetcher
source_uri.path
end
source_path = unescape source_path
source_path = Gem::UriFormatter.new(source_path).unescape
begin
FileUtils.cp source_path, local_gem_path unless
@ -319,125 +310,6 @@ class Gem::RemoteFetcher
response['content-length'].to_i
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)
if path[0].chr == '/' && path[1].chr =~ /[a-z]/i && path[2].chr == ':'
path = path[1..-1]
@ -446,136 +318,13 @@ class Gem::RemoteFetcher
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
# a Net::HTTP response object. request maintains a table of persistent
# connections to reduce connect overhead.
def request(uri, request_class, last_modified = nil)
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 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
Gem::Request.new(uri, request_class, last_modified, @proxy).fetch
end
def https?(uri)

262
lib/rubygems/request.rb Normal file
View 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

View file

@ -5,91 +5,79 @@ require 'rubygems/dependency_list'
require 'rubygems/installer'
require 'tsort'
module Gem
class RequestSet
class Gem::RequestSet
include TSort
def initialize(*deps)
##
# Array of gems to install even if already installed
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
attr_reader :dependencies
##
# Declare that a gem of name +name+ with +reqs+ requirements is needed.
# Declare that a gem of name +name+ with +reqs+ requirements
# is needed.
#
def gem(name, *reqs)
def gem name, *reqs
@dependencies << Gem::Dependency.new(name, reqs)
end
# Add +deps+ Gem::Depedency objects to the set.
#
def import(deps)
##
# Add +deps+ Gem::Dependency objects to the set.
def import deps
@dependencies += deps
end
# Resolve the requested dependencies and return an Array of
# Specification objects to be activated.
#
def resolve(set=nil)
r = Gem::DependencyResolver.new(@dependencies, set)
@requests = r.resolve
def install options, &block
if dir = options[:install_dir]
return install_into dir, false, options, &block
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 DependencyResolver::CurrentSet.new
cache_dir = options[:cache_dir] || Gem.dir
specs = []
sorted_requests.each do |req|
if req.installed? and
@always_install.none? { |spec| spec == req.spec.spec } then
yield req, nil if block_given?
next
end
# Load a dependency management file.
#
def load_gemdeps(path)
gf = GemDepedencyAPI.new(self, path)
gf.load
path = req.download cache_dir
inst = Gem::Installer.new path, options
yield req, inst if block_given?
specs << inst.install
end
def specs
@specs ||= @requests.map { |r| r.full_spec }
specs
end
def tsort_each_node(&block)
@requests.each(&block)
end
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}"
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)
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
@ -97,16 +85,23 @@ module Gem
sorted_requests.each do |req|
if existing.find { |s| s.full_name == req.spec.full_name }
b.call req, nil if b
yield req, nil if block_given?
next
end
path = req.download(dir)
inst = Gem::Installer.new path, :install_dir => dir,
:only_install_dir => true
unless path then # already installed
yield req, nil if block_given?
next
end
b.call req, inst if b
options[:install_dir] = dir
options[:only_install_dir] = true
inst = Gem::Installer.new path, options
yield req, inst if block_given?
inst.install
@ -116,67 +111,70 @@ module Gem
installed
end
def install(options, &b)
if dir = options[:install_dir]
return install_into(dir, false, &b)
##
# Load a dependency management file.
def load_gemdeps path
gf = Gem::RequestSet::GemDepedencyAPI.new self, path
gf.load
end
cache_dir = options[:cache_dir] || Gem.dir
##
# Resolve the requested dependencies and return an Array of Specification
# objects to be activated.
specs = []
def resolve set = nil
resolver = Gem::DependencyResolver.new @dependencies, set
resolver.development = @development
resolver.soft_missing = @soft_missing
sorted_requests.each do |req|
if req.installed?
b.call req, nil if b
next
@requests = resolver.resolve
end
path = req.download cache_dir
##
# Resolve the requested dependencies against the gems available via Gem.path
# and return an Array of Specification objects to be activated.
inst = Gem::Installer.new path, options
b.call req, inst if b
specs << inst.install
def resolve_current
resolve Gem::DependencyResolver::CurrentSet.new
end
specs
def sorted_requests
@sorted ||= strongly_connected_components.flatten
end
# A semi-compatible DSL for Bundler's Gemfile format
#
class GemDepedencyAPI
def initialize(set, path)
@set = set
@path = path
def specs
@specs ||= @requests.map { |r| r.full_spec }
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
def specs_in dir
Dir["#{dir}/specifications/*.gemspec"].map do |g|
Gem::Specification.load g
end
end
alias_method :platforms, :platform
def tsort_each_node &block # :nodoc:
@requests.each(&block)
end
def group(*what)
def tsort_each_child node # :nodoc:
node.spec.dependencies.each do |dep|
next if dep.type == :development and not @development
match = @requests.find { |r| dep.match? r.spec.name, r.spec.version }
if match
begin
yield match
rescue TSort::Cyclic
end
else
unless @soft_missing
raise Gem::DependencyError, "Unresolved depedency found during sorting - #{dep}"
end
end
end
end
end
require 'rubygems/request_set/gem_dependency_api'

View 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

View file

@ -12,20 +12,6 @@ begin
rescue LoadError => e
raise unless (e.respond_to?(:path) && e.path == '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
##
@ -352,23 +338,38 @@ module Gem::Security
##
# 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
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
KEY_ALGORITHM = OpenSSL::PKey::RSA
KEY_ALGORITHM =
if defined?(OpenSSL::PKey) then
OpenSSL::PKey::RSA
end
##
# Length of keys created by KEY_ALGORITHM
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
@ -563,14 +564,19 @@ module Gem::Security
##
# 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
open path, 'wb', permissions do |io|
if passphrase and cipher
io.write pemmable.to_pem cipher, passphrase
else
io.write pemmable.to_pem
end
end
path
end
@ -579,8 +585,11 @@ module Gem::Security
end
require 'rubygems/security/policy'
require 'rubygems/security/policies'
require 'rubygems/security/signer'
require 'rubygems/security/trust_dir'
if defined?(OpenSSL::SSL) then
require 'rubygems/security/policy'
require 'rubygems/security/policies'
require 'rubygems/security/trust_dir'
end
require 'rubygems/security/signer'

View file

@ -1,3 +1,5 @@
require 'rubygems/user_interaction'
##
# A Gem::Security::Policy object encapsulates the settings for verifying
# signed gem files. This is the base class. You can either declare an
@ -6,6 +8,8 @@
class Gem::Security::Policy
include Gem::UserInteraction
attr_reader :name
attr_accessor :only_signed
@ -175,6 +179,19 @@ class Gem::Security::Policy
true
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:
("[Policy: %s - data: %p signer: %p chain: %p root: %p " +
"signed-only: %p trusted-only: %p]") % [
@ -184,16 +201,21 @@ class Gem::Security::Policy
end
##
# Verifies the certificate +chain+ is valid, the +digests+ match the
# signatures +signatures+ created by the signer depending on the +policy+
# settings.
# For +full_name+, verifies the certificate +chain+ is valid, the +digests+
# match the signatures +signatures+ created by the signer depending on the
# +policy+ settings.
#
# If +key+ is given it is used to validate the signing certificate.
def verify chain, key = nil, digests = {}, signatures = {}
if @only_signed and signatures.empty? then
def verify chain, key = nil, digests = {}, signatures = {},
full_name = '(unknown)'
if signatures.empty? then
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
opt = @opt
@ -222,7 +244,11 @@ class Gem::Security::Policy
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, _|
digest = signer_digests[file]
@ -252,7 +278,7 @@ class Gem::Security::Policy
OpenSSL::X509::Certificate.new cert_pem
end
verify chain, nil, digests, signatures
verify chain, nil, digests, signatures, spec.full_name
true
end

View file

@ -29,7 +29,7 @@ class Gem::Security::Signer
# +chain+ containing X509 certificates, encoding certificates or paths to
# certificates.
def initialize key, cert_chain
def initialize key, cert_chain, passphrase = nil
@cert_chain = cert_chain
@key = key
@ -46,7 +46,7 @@ class Gem::Security::Signer
@digest_algorithm = Gem::Security::DIGEST_ALGORITHM
@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
if @cert_chain then

View file

@ -25,6 +25,10 @@ class Gem::Source
end
def <=>(other)
case other
when Gem::Source::Installed, Gem::Source::Local then
-1
when Gem::Source then
if !@uri
return 0 unless other.uri
return -1
@ -33,6 +37,9 @@ class Gem::Source
return 1 if !other.uri
@uri.to_s <=> other.uri.to_s
else
nil
end
end
include Comparable
@ -58,8 +65,7 @@ class Gem::Source
def cache_dir(uri)
# Correct for windows paths
escaped_path = uri.path.sub(/^\/([a-z]):\//i, '/\\1-/')
root = File.join Gem.user_home, '.gem', 'specs'
File.join root, "#{uri.host}%#{uri.port}", File.dirname(escaped_path)
File.join Gem.spec_cache_dir, "#{uri.host}%#{uri.port}", File.dirname(escaped_path)
end
def update_cache?
@ -141,4 +147,14 @@ class Gem::Source
fetcher = Gem::RemoteFetcher.fetcher
fetcher.download spec, @uri.to_s, dir
end
def pretty_print q # :nodoc:
q.group 2, '[Remote:', ']' do
q.breakable
q.text @uri.to_s
end
end
end
require 'rubygems/source/installed'

View 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

View 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

View 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

View file

@ -1,92 +1,5 @@
require 'rubygems/source'
require 'rubygems/source_local'
class Gem::Source::Local < Gem::Source
def initialize
@uri = nil
end
# TODO warn upon require, this file is deprecated.
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

View file

@ -1,28 +1,4 @@
class Gem::Source::SpecificFile < Gem::Source
def initialize(file)
@uri = nil
@path = ::File.expand_path(file)
require 'rubygems/source/specific_file'
@package = Gem::Package.new @path
@spec = @package.spec
@name = @spec.name_tuple
end
# TODO warn upon require, this file is deprecated.
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

View file

@ -38,7 +38,6 @@ class Gem::SpecFetcher
end
def initialize
@dir = File.join Gem.user_home, '.gem', 'specs'
@update_cache = File.stat(Gem.user_home).uid == Process.uid
@specs = {}

View file

@ -5,10 +5,13 @@
# See LICENSE.txt for permissions.
#++
require 'rubygems/version'
require 'rubygems/requirement'
require 'rubygems/platform'
require 'rubygems/deprecate'
require 'rubygems/basic_specification'
require 'rubygems/stub_specification'
# :stopdoc:
# 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" }
class Gem::Specification
class Gem::Specification < Gem::BasicSpecification
# REFACTOR: Consider breaking out this version stuff into a separate
# 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.utc(today.year, today.month, today.day)
LOAD_CACHE = {}
private_constant :LOAD_CACHE if defined? private_constant
# :startdoc:
##
@ -156,6 +163,17 @@ class Gem::Specification
: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 }
@@array_attributes = @@default_value.reject { |k,v| v != [] }.keys
@@nil_attributes, @@non_nil_attributes = @@default_value.keys.partition { |k|
@ -583,11 +601,6 @@ class Gem::Specification
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.
@ -615,44 +628,9 @@ class Gem::Specification
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:
unless defined?(@@all) && @@all then
specs = {}
each_default do |spec|
specs[spec.full_name] ||= spec
end
each_normal do |spec|
specs[spec.full_name] ||= spec
end
@@all = specs.values
@@all = stubs.map(&:to_spec)
# After a reset, make sure already loaded specs
# are still marked as activated.
@ -660,13 +638,58 @@ class Gem::Specification
Gem.loaded_specs.each_value{|s| specs[s] = true}
@@all.each{|s| s.activated = true if specs[s]}
_resort!
_resort!(@@all)
end
@@all
end
def self._resort! # :nodoc:
@@all.sort! { |a, b|
def self._clear_load_cache # :nodoc:
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
next names if names.nonzero?
b.version <=> a.version
@ -677,7 +700,9 @@ class Gem::Specification
# Loads the default specifications. It should be called only once.
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)
end
end
@ -700,7 +725,9 @@ class Gem::Specification
return if _all.include? spec
_all << spec
_resort!
stubs << spec
_resort!(_all)
_resort!(stubs)
end
##
@ -843,9 +870,10 @@ class Gem::Specification
# amongst the specs that are not activated.
def self.find_inactive_by_path path
self.find { |spec|
spec.contains_requirable_file? path unless spec.activated?
stub = stubs.find { |s|
s.contains_requirable_file? path unless s.activated?
}
stub && stub.to_spec
end
##
@ -937,6 +965,9 @@ class Gem::Specification
file = file.dup.untaint
return unless File.file?(file)
spec = LOAD_CACHE[file]
return spec if spec
code = if defined? Encoding
File.read file, :mode => 'r:UTF-8:-'
else
@ -950,6 +981,7 @@ class Gem::Specification
if Gem::Specification === spec
spec.loaded_from = file.to_s
LOAD_CACHE[file] = spec
return spec
end
@ -1013,6 +1045,7 @@ class Gem::Specification
raise "wtf: #{spec.full_name} not in #{all_names.inspect}" unless
_all.include? spec
_all.delete spec
stubs.delete_if { |s| s.full_name == spec.full_name }
end
##
@ -1037,6 +1070,8 @@ class Gem::Specification
@@dirs = nil
Gem.pre_reset_hooks.each { |hook| hook.call }
@@all = nil
@@stubs = nil
_clear_load_cache
unresolved = unresolved_deps
unless unresolved.empty? then
w = "W" + "ARN"
@ -1280,20 +1315,6 @@ class Gem::Specification
@authors ||= []
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.
#
@ -1367,19 +1388,6 @@ class Gem::Specification
conflicts
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.
@ -1623,35 +1631,14 @@ class Gem::Specification
spec
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.
# 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)
# :nodoc:
def find_full_gem_path
super || File.expand_path(File.join(gems_dir, original_name))
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.
private :find_full_gem_path
def full_name
@full_name ||= if platform == Gem::Platform::RUBY or platform.nil? then
"#{@name}-#{@version}".untaint
else
"#{@name}-#{@version}-#{platform}".untaint
end
@full_name ||= super
end
##
@ -1662,15 +1649,6 @@ class Gem::Specification
@gem_dir ||= File.expand_path File.join(gems_dir, full_name)
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.
#
@ -1703,9 +1681,7 @@ class Gem::Specification
# :startdoc:
def hash # :nodoc:
@@attributes.inject(0) { |hash_code, (name, _)|
hash_code ^ self.send(name).hash
}
name.hash ^ version.hash
end
def init_with coder # :nodoc:
@ -1720,7 +1696,7 @@ class Gem::Specification
def initialize name = nil, version = nil
@loaded = false
@activated = false
@loaded_from = nil
self.loaded_from = nil
@original_platform = nil
@@nil_attributes.each do |key|
@ -1729,11 +1705,7 @@ class Gem::Specification
@@non_nil_attributes.each do |key|
default = default_value(key)
value = case default
when Time, Numeric, Symbol, true, false, nil then default
else default.dup
end
value = Dupable[key] ? default.dup : default
instance_variable_set "@#{key}", value
end
@ -1828,27 +1800,30 @@ class Gem::Specification
@licenses ||= []
end
##
# Set the location a Specification was loaded from. +obj+ is converted
# to a String.
def filename= path
super
def loaded_from= path
@loaded_from = path.to_s
# reset everything @loaded_from depends upon
@base_dir = nil
@bin_dir = nil
@cache_dir = nil
@cache_file = nil
@doc_dir = nil
@full_gem_path = nil
@gem_dir = nil
@gems_dir = nil
@ri_dir = nil
@spec_dir = nil
@spec_file = nil
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.
@ -1878,6 +1853,11 @@ class Gem::Specification
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:
# * All file lists have redundancies removed.
@ -2093,6 +2073,13 @@ class Gem::Specification
[@name, @version, @new_platform == Gem::Platform::RUBY ? -1 : 1]
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
# gemspec file. eg: /usr/local/lib/ruby/gems/1.8/specifications
@ -2172,6 +2159,7 @@ class Gem::Specification
mark_version
result = []
result << "# -*- encoding: utf-8 -*-"
result << "#{Gem::StubSpecification::PREFIX}#{name} #{version} #{platform} #{require_paths.join("\0")}"
result << nil
result << "Gem::Specification.new do |s|"
@ -2259,6 +2247,13 @@ class Gem::Specification
"#<Gem::Specification name=#{@name} version=#{@version}>"
end
##
# Returns self
def to_spec
self
end
def to_yaml(opts = {}) # :nodoc:
if YAML.const_defined?(:ENGINE) && !YAML::ENGINE.syck? then
# Because the user can switch the YAML engine behind our
@ -2559,11 +2554,6 @@ class Gem::Specification
end
end
def default_gem?
loaded_from &&
File.dirname(loaded_from) == self.class.default_specifications_dir
end
extend Gem::Deprecate
# TODO:

View 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

View file

@ -78,6 +78,23 @@ end
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
def assert_path_exists path, msg = nil
msg = message(msg) { "Expected path '#{path}' to exist" }
@ -200,6 +217,7 @@ class Gem::TestCase < MiniTest::Unit::TestCase
@gemhome = File.join @tempdir, 'gemhome'
@userhome = File.join @tempdir, 'userhome'
ENV["GEM_SPEC_CACHE"] = File.join @tempdir, 'spec_cache'
@orig_ruby = if ENV['RUBY'] then
ruby = Gem.instance_variable_get :@ruby
@ -221,6 +239,9 @@ class Gem::TestCase < MiniTest::Unit::TestCase
FileUtils.mkdir_p @gemhome
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_spec_dir = File.join @default_dir, "specifications", "default"
Gem.instance_variable_set :@default_dir, @default_dir
@ -266,39 +287,6 @@ class Gem::TestCase < MiniTest::Unit::TestCase
end
@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
##
@ -332,6 +320,47 @@ class Gem::TestCase < MiniTest::Unit::TestCase
end
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
##
@ -560,6 +589,21 @@ class Gem::TestCase < MiniTest::Unit::TestCase
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
# up properly. Use this instead of util_spec and util_gem.
@ -1005,6 +1049,24 @@ Also, a list:
Gem::Dependency.new name, *requirements
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.
@ -1074,18 +1136,18 @@ Also, a list:
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.read key_file
OpenSSL::PKey::RSA.new key
OpenSSL::PKey::RSA.new key, passphrase
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
File.expand_path "../../../test/rubygems/#{key_name}_key.pem", __FILE__
@ -1094,9 +1156,16 @@ Also, a list:
# :stopdoc:
# only available in RubyGems tests
PRIVATE_KEY_PASSPHRASE = 'Foo bar'
begin
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'
@ -1105,6 +1174,6 @@ Also, a list:
PRIVATE_KEY = nil
PUBLIC_KEY = nil
PUBLIC_CERT = nil
end
end if defined?(OpenSSL::SSL)
end

View file

@ -51,6 +51,7 @@ class Gem::Uninstaller
@force_ignore = options[:ignore]
@bin_dir = options[:bin_dir]
@format_executable = options[:format_executable]
@abort_on_dependent = options[:abort_on_dependent]
# Indicate if development dependencies should be checked when
# uninstalling. (default: false)
@ -143,7 +144,7 @@ class Gem::Uninstaller
@spec = spec
unless dependencies_ok? spec
unless ask_if_ok(spec)
if abort_on_dependent? || !ask_if_ok(spec)
raise Gem::DependencyRemovalException,
"Uninstallation aborted due to dependent gem(s)"
end
@ -290,6 +291,10 @@ class Gem::Uninstaller
deplist.ok_to_remove?(spec.full_name, @check_dev)
end
def abort_on_dependent?
@abort_on_dependent
end
def ask_if_ok(spec)
msg = ['']
msg << 'You have requested to uninstall the gem:'

View 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
View 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

View file

@ -147,13 +147,16 @@ class Gem::Version
# FIX: These are only used once, in .correct?. Do they deserve to be
# 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:
##
# A string representation of this Version.
attr_reader :version
def version
@version.dup
end
alias to_s version
##
@ -183,6 +186,12 @@ class Gem::Version
end
end
@@all = {}
def self.new version
@@all[version] ||= super
end
##
# Constructs a Version from the +version+ string. A version string is a
# series of digits or ASCII letters separated by dots.
@ -191,7 +200,8 @@ class Gem::Version
raise ArgumentError, "Malformed version number string #{version}" unless
self.class.correct?(version)
@version = version.to_s.dup.strip
@version = version.to_s.strip.gsub("-",".pre.")
@segments = nil
end
##

View file

@ -42,6 +42,7 @@ module Gem::VersionOption
add_option("--[no-]prerelease",
"Allow prerelease versions of a gem", *wrap) do |value, options|
options[:prerelease] = value
options[:explicit_prerelease] = true
end
end
@ -50,14 +51,19 @@ module Gem::VersionOption
def add_version_option(task = command, *wrap)
OptionParser.accept Gem::Requirement do |value|
Gem::Requirement.new value
Gem::Requirement.new(*value.split(/\s*,\s*/))
end
add_option('-v', '--version VERSION', Gem::Requirement,
"Specify version of gem to #{task}", *wrap) do
|value, options|
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

View file

@ -43,3 +43,26 @@ ySjIblqVQkPuzebv3Ror6ZnVDukn96Mg7kP4u6zgxOeqlJGRe1M949SS9Vudjl8X
SF4aZUUB9pQGhsqQJVqaz2OlhGOp9D0q54xko/rekjAIcuDjl1mdX4F2WRrzpUmZ
uY/bPeOBYiVsOYVe
-----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
View 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-----

View 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-----

View 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-----

View 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

Binary file not shown.

View file

@ -15,6 +15,7 @@ class TestGem < Gem::TestCase
def setup
super
common_installer_setup
ENV.delete 'RUBYGEMS_GEMDEPS'
@additional = %w[a b].map { |d| File.join @tempdir, d }
@ -22,119 +23,6 @@ class TestGem < Gem::TestCase
util_remove_interrupt_command
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
save_loaded_features do
a1 = new_spec "a", "1", "b" => "> 0"
@ -157,36 +45,6 @@ class TestGem < Gem::TestCase
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
save_loaded_features do
a1 = new_spec "a", "1", "b" => "> 0", "d" => "> 0" # this
@ -211,94 +69,6 @@ class TestGem < Gem::TestCase
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
save_loaded_features do
assert_raises ::LoadError do
@ -321,221 +91,6 @@ class TestGem < Gem::TestCase
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
e = assert_raises ArgumentError do
Gem.bin_path 'a'
@ -1558,6 +1113,34 @@ class TestGem < Gem::TestCase
assert_equal '["a-1", "b-1", "c-1"]', out.strip
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)
test_plugin_path = File.expand_path("test/rubygems/plugin/#{path}",
@@project_dir)
@ -1634,4 +1217,3 @@ class TestGem < Gem::TestCase
File.join Gem.dir, "cache"
end
end

View file

@ -2,8 +2,8 @@ require 'rubygems/test_case'
require 'rubygems/commands/cert_command'
require 'rubygems/fix_openssl_warnings' if RUBY_VERSION < "1.9"
unless defined? OpenSSL then
warn "`gem cert` tests are being skipped, module OpenSSL not found"
unless defined?(OpenSSL::SSL) then
warn 'Skipping `gem cert` tests. openssl not found.'
end
class TestGemCommandsCertCommand < Gem::TestCase
@ -98,14 +98,22 @@ Added '/CN=alternate/DC=example'
end
def test_execute_build
passphrase = 'Foo bar'
@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
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'}",
output.shift
assert_equal "Private Key: #{File.join @tempdir, 'gem-private_key.pem'}",
@ -115,12 +123,43 @@ Added '/CN=alternate/DC=example'
output.shift
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-public_cert.pem')
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
@cmd.handle_options %W[
--build nobody@example.com
@ -135,21 +174,32 @@ Added '/CN=alternate/DC=example'
assert_equal "Certificate: #{File.join @tempdir, 'gem-public_cert.pem'}",
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
assert_empty output
assert_empty @ui.error
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
def test_execute_certificate
@ -203,6 +253,17 @@ Added '/CN=alternate/DC=example'
assert_equal PRIVATE_KEY.to_pem, @cmd.options[:key].to_pem
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
@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?
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
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?
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
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]
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
nonexistent = File.join @tempdir, 'nonexistent'
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
end
end if defined? OpenSSL
end if defined?(OpenSSL::SSL)

View file

@ -15,6 +15,21 @@ class TestGemCommandsCleanupCommand < Gem::TestCase
install_gem @a_2
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
@cmd.options[:args] = %w[a]

View file

@ -140,10 +140,10 @@ lib/foo.rb
@cmd.execute
end
expected = %W[
#{Gem::ConfigMap[:bindir]}/default_command
#{Gem::ConfigMap[:rubylibdir]}/default/gem.rb
#{Gem::ConfigMap[:archdir]}/default_gem.so
expected = [
File.join(Gem::ConfigMap[:bindir], 'default_command'),
File.join(Gem::ConfigMap[:rubylibdir], 'default/gem.rb'),
File.join(Gem::ConfigMap[:archdir], 'default_gem.so')
].sort.join "\n"
assert_equal expected, @ui.output.chomp

View file

@ -11,6 +11,7 @@ class TestGemCommandsEnvironmentCommand < Gem::TestCase
def test_execute
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.configuration['gemcutter_key'] = 'blah'
@ -36,10 +37,17 @@ class TestGemCommandsEnvironmentCommand < Gem::TestCase
assert_match %r|"gemcutter_key" => "\*\*\*\*"|, @ui.output
assert_match %r|:verbose => |, @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
Gem.sources.replace orig_sources
ENV['PATH'] = orig_path
end
def test_execute_gemdir

View file

@ -36,11 +36,14 @@ class TestGemCommandsHelpCommand < Gem::TestCase
mgr.command_names.each do |cmd|
assert_match(/\s+#{cmd}\s+\S+/, out)
end
assert_equal '', err
if defined?(OpenSSL::SSL) then
assert_empty err
refute_match 'No command found for ', out
end
end
end
def test_gem_no_args_shows_help
util_gem do |out, err|

View file

@ -5,6 +5,7 @@ class TestGemCommandsInstallCommand < Gem::TestCase
def setup
super
common_installer_setup
@cmd = Gem::Commands::InstallCommand.new
@cmd.options[:document] = []
@ -167,8 +168,7 @@ class TestGemCommandsInstallCommand < Gem::TestCase
# This is needed because we need to exercise the cache path
# within SpecFetcher
path = File.join Gem.user_home, '.gem', 'specs', "not-there.nothing%80",
"latest_specs.4.8"
path = File.join Gem.spec_cache_dir, "not-there.nothing%80", "latest_specs.4.8"
FileUtils.mkdir_p File.dirname(path)
@ -632,67 +632,6 @@ ERROR: Possible alternatives: non_existent_with_hint
assert_equal x, e
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
util_setup_fake_fetcher
util_setup_spec_fetcher
@ -950,4 +889,3 @@ ERROR: Possible alternatives: non_existent_with_hint
end

View file

@ -6,6 +6,7 @@ class TestGemCommandsOwnerCommand < Gem::TestCase
def setup
super
ENV["RUBYGEMS_HOST"] = nil
@fetcher = Gem::FakeFetcher.new
Gem::RemoteFetcher.fetcher = @fetcher
Gem.configuration.rubygems_api_key = "ed244fbf2b1a52e012da8616c512fa47f9aa5250"
@ -34,6 +35,36 @@ EOF
assert_match %r{- user2@example.com}, @ui.output
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
response = "You don't have permission to push to this gem"
@fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners.yaml"] = [response, 403, 'Forbidden']
@ -87,6 +118,24 @@ EOF
assert_match response, @ui.output
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
response = "Owner added successfully."
@fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners"] = [response, 200, 'OK']

View file

@ -80,6 +80,32 @@ class TestGemCommandsPristineCommand < Gem::TestCase
assert_empty out, out.inspect
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
a = quick_spec 'a' do |s| s.extensions << 'ext/a/extconf.rb' end

View file

@ -194,7 +194,7 @@ beta-gems.example.com is not a URI
assert_equal expected, @ui.output
assert_equal '', @ui.error
dir = File.join Gem.user_home, '.gem', 'specs'
dir = Gem.spec_cache_dir
refute File.exist?(dir), 'cache dir removed'
end

View file

@ -11,6 +11,7 @@ class TestGemCommandsUpdateCommand < Gem::TestCase
def setup
super
common_installer_setup
@cmd = Gem::Commands::UpdateCommand.new
@ -253,7 +254,7 @@ class TestGemCommandsUpdateCommand < Gem::TestCase
out = @ui.output.split "\n"
assert_equal "Updating installed gems", 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
assert_empty out

View file

@ -201,6 +201,10 @@ ERROR: Your gem push credentials file located at:
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:
\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)
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)
@cfg = Gem::ConfigFile.new args
end

View file

@ -6,6 +6,7 @@ class TestGemDependencyInstaller < Gem::TestCase
def setup
super
common_installer_setup
@gems_dir = File.join @tempdir, 'gems'
@cache_dir = File.join @gemhome, 'cache'
@ -173,6 +174,7 @@ class TestGemDependencyInstaller < Gem::TestCase
FileUtils.mv @a1_gem, @tempdir
FileUtils.mv @b1_gem, @tempdir
FileUtils.mv e1_gem, @tempdir
inst = nil
Dir.chdir @tempdir do
@ -180,40 +182,15 @@ class TestGemDependencyInstaller < Gem::TestCase
inst.install 'b'
end
assert_equal %w[b-1], inst.installed_gems.map { |s| s.full_name },
'sanity check'
Dir.chdir @tempdir do
inst = Gem::DependencyInstaller.new
inst.install 'e'
end
assert_equal %w[e-1 a-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 }
assert_equal %w[a-1 e-1], inst.installed_gems.map { |s| s.full_name }
end
def test_install_cache_dir
@ -252,9 +229,12 @@ class TestGemDependencyInstaller < Gem::TestCase
Dir.chdir @tempdir do
inst = Gem::DependencyInstaller.new
inst.install 'a', Gem::Requirement.create("= 2")
inst.install 'a', req("= 2")
end
assert_equal %w[a-2], inst.installed_gems.map { |s| s.full_name },
'sanity check'
FileUtils.rm File.join(@tempdir, a2.file_name)
Dir.chdir @tempdir do
@ -286,15 +266,14 @@ class TestGemDependencyInstaller < Gem::TestCase
FileUtils.mv @b1_gem, @tempdir
FileUtils.mv a3_gem, @tempdir
inst = nil
Dir.chdir @tempdir do
inst = Gem::DependencyInstaller.new
inst.install 'a', Gem::Requirement.create("= 2")
Gem::DependencyInstaller.new.install 'a', req("= 2")
end
FileUtils.rm File.join(@tempdir, a2.file_name)
inst = nil
Dir.chdir @tempdir do
inst = Gem::DependencyInstaller.new
inst.install 'b'
@ -488,6 +467,42 @@ class TestGemDependencyInstaller < Gem::TestCase
assert_equal %w[a-1], inst.installed_gems.map { |s| s.full_name }
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
util_setup_gems
@ -627,12 +642,12 @@ class TestGemDependencyInstaller < Gem::TestCase
inst = nil
Dir.chdir @tempdir do
e = assert_raises Gem::DependencyError do
e = assert_raises Gem::UnsatisfiableDependencyError do
inst = Gem::DependencyInstaller.new :domain => :local
inst.install 'b'
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
end
@ -910,12 +925,13 @@ class TestGemDependencyInstaller < Gem::TestCase
gems = set.sorted
assert_equal 2, gems.length
local = gems.first
remote, local = gems
assert_equal 'a-1', local.spec.full_name, 'local spec'
assert_equal File.join(@tempdir, @a1.file_name),
local.source.download(local.spec), 'local path'
remote = gems.last
assert_equal 'a-1', remote.spec.full_name, 'remote spec'
assert_equal Gem::Source.new(@gem_repo), remote.source, 'remote path'

View file

@ -15,7 +15,9 @@ class TestGemDependencyResolver < Gem::TestCase
exp = expected.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
def test_no_overlap_specificly
@ -177,7 +179,8 @@ class TestGemDependencyResolver < Gem::TestCase
r.resolve
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
end
@ -215,7 +218,7 @@ class TestGemDependencyResolver < Gem::TestCase
r.resolve
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

View file

@ -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

View file

@ -7,6 +7,7 @@ class TestGemGemRunner < Gem::TestCase
super
@orig_args = Gem::Command.build_args
@runner = Gem::GemRunner.new
end
def teardown
@ -41,23 +42,26 @@ class TestGemGemRunner < Gem::TestCase
assert_equal %w[--commands], Gem::Command.extra_args
end
def test_build_args_are_handled
Gem.clear_paths
def test_extract_build_args
args = %w[]
assert_equal [], @runner.extract_build_args(args)
assert_equal %w[], args
cls = Class.new(Gem::Command) do
def execute
end
end
args = %w[foo]
assert_equal [], @runner.extract_build_args(args)
assert_equal %w[foo], args
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
cmds.register_command :ba_test, test_obj
args = %w[--foo --]
assert_equal [], @runner.extract_build_args(args)
assert_equal %w[--foo], args
runner = Gem::GemRunner.new :command_manager => cmds
runner.run(%W[ba_test -- --build_arg1 --build_arg2])
assert_equal %w[--build_arg1 --build_arg2], test_obj.options[:build_args]
args = %w[--foo -- --bar]
assert_equal %w[--bar], @runner.extract_build_args(args)
assert_equal %w[--foo], args
end
end

View 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

View file

@ -22,12 +22,13 @@ class TestGemInstallUpdateOptions < Gem::InstallerTestCase
--rdoc
--ri
-E
-P HighSecurity
-f
-i /install_to
-w
]
args.concat %w[-P HighSecurity] if defined?(OpenSSL::SSL)
assert @cmd.handles?(args)
end
@ -100,6 +101,8 @@ class TestGemInstallUpdateOptions < Gem::InstallerTestCase
end
def test_security_policy
skip 'openssl is missing' unless defined?(OpenSSL::SSL)
@cmd.handle_options %w[-P HighSecurity]
assert_equal Gem::Security::HighSecurity, @cmd.options[:security_policy]

View file

@ -4,6 +4,7 @@ class TestGemInstaller < Gem::InstallerTestCase
def setup
super
common_installer_setup
if __name__ =~ /^test_install(_|$)/ then
FileUtils.rm_r @spec.gem_dir
@ -14,6 +15,8 @@ class TestGemInstaller < Gem::InstallerTestCase
end
def teardown
common_installer_teardown
super
Gem.configuration = @config
@ -300,6 +303,8 @@ gem 'other', version
end
def test_ensure_loadable_spec_security_policy
skip 'openssl is missing' unless defined?(OpenSSL::SSL)
_, a_gem = util_gem 'a', 2 do |s|
s.add_dependency 'garbage ~> 5'
end
@ -1341,7 +1346,7 @@ gem 'other', version
assert File.exist?(File.join(dest, 'bin', 'executable'))
end
def test_write_build_args
def test_write_build_info_file
refute_path_exists @spec.build_info_file
@installer.build_args = %w[
@ -1357,7 +1362,7 @@ gem 'other', version
assert_equal expected, File.read(@spec.build_info_file)
end
def test_write_build_args_empty
def test_write_build_info_file_empty
refute_path_exists @spec.build_info_file
@installer.write_build_info_file
@ -1429,6 +1434,30 @@ gem 'other', version
assert_match %r!/gemhome/gems/a-2$!, @installer.dir
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
spec = quick_spec 'old_ruby_required', '1' do |s|
s.required_ruby_version = '= 1.4.6'

View file

@ -2,6 +2,21 @@ require 'rubygems/test_case'
require 'rubygems/name_tuple'
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
n = Gem::NameTuple.new "a", Gem::Version.new(0), "ruby"
assert_equal "ruby", n.platform
@ -12,4 +27,11 @@ class TestGemNameTuple < Gem::TestCase
n = Gem::NameTuple.new "a", Gem::Version.new(0), ""
assert_equal "ruby", n.platform
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

View file

@ -89,16 +89,19 @@ class TestGemPackage < Gem::Package::TarTestCase
end
expected = {
'SHA1' => {
'metadata.gz' => metadata_sha1,
'data.tar.gz' => data_digests['SHA1'].hexdigest,
},
'SHA512' => {
'metadata.gz' => metadata_sha512,
'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)
end
@ -162,11 +165,56 @@ class TestGemPackage < Gem::Package::TarTestCase
end
def test_build_auto_signed
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'
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'
Gem::Security.write PUBLIC_CERT, public_cert_path
@ -216,6 +264,8 @@ class TestGemPackage < Gem::Package::TarTestCase
end
def test_build_signed
skip 'openssl is missing' unless defined?(OpenSSL::SSL)
spec = Gem::Specification.new 'build', '1'
spec.summary = 'build'
spec.authors = 'build'
@ -250,6 +300,43 @@ class TestGemPackage < Gem::Package::TarTestCase
assert_equal %w[lib/code.rb], reader.contents
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
package = Gem::Package.new @gem
@ -446,7 +533,7 @@ class TestGemPackage < Gem::Package::TarTestCase
io.write metadata_gz
end
digest = OpenSSL::Digest::SHA1.new
digest = Digest::SHA1.new
digest << metadata_gz
checksums = {
@ -478,7 +565,8 @@ class TestGemPackage < Gem::Package::TarTestCase
def test_verify_corrupt
Tempfile.open 'corrupt' do |io|
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.rewind
@ -517,6 +605,8 @@ class TestGemPackage < Gem::Package::TarTestCase
end
def test_verify_security_policy
skip 'openssl is missing' unless defined?(OpenSSL::SSL)
package = Gem::Package.new @gem
package.security_policy = Gem::Security::HighSecurity
@ -532,6 +622,8 @@ class TestGemPackage < Gem::Package::TarTestCase
end
def test_verify_security_policy_low_security
skip 'openssl is missing' unless defined?(OpenSSL::SSL)
@spec.cert_chain = [PUBLIC_CERT.to_pem]
@spec.signing_key = PRIVATE_KEY
@ -550,6 +642,8 @@ class TestGemPackage < Gem::Package::TarTestCase
end
def test_verify_security_policy_checksum_missing
skip 'openssl is missing' unless defined?(OpenSSL::SSL)
@spec.cert_chain = [PUBLIC_CERT.to_pem]
@spec.signing_key = PRIVATE_KEY
@ -605,6 +699,21 @@ class TestGemPackage < Gem::Package::TarTestCase
e.message
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
package = Gem::Package.new @gem

View file

@ -21,6 +21,8 @@ class TestGemPackageOld < Gem::TestCase
end
def test_contents_security_policy
skip 'openssl is missing' unless defined?(OpenSSL::SSL)
@package.security_policy = Gem::Security::AlmostNoSecurity
assert_raises Gem::Security::Exception do
@ -40,6 +42,8 @@ class TestGemPackageOld < Gem::TestCase
end
def test_extract_files_security_policy
skip 'openssl is missing' unless defined?(OpenSSL::SSL)
@package.security_policy = Gem::Security::AlmostNoSecurity
assert_raises Gem::Security::Exception do
@ -52,6 +56,8 @@ class TestGemPackageOld < Gem::TestCase
end
def test_spec_security_policy
skip 'openssl is missing' unless defined?(OpenSSL::SSL)
@package.security_policy = Gem::Security::AlmostNoSecurity
assert_raises Gem::Security::Exception do
@ -60,6 +66,8 @@ class TestGemPackageOld < Gem::TestCase
end
def test_verify
skip 'openssl is missing' unless defined?(OpenSSL::SSL)
assert @package.verify
@package.security_policy = Gem::Security::NoSecurity

View file

@ -4,8 +4,8 @@ require 'rubygems/package'
class TestGemPackageTarReader < Gem::Package::TarTestCase
def test_each_entry
tar = tar_dir_header "foo", "bar", 0
tar << tar_file_header("bar", "baz", 0, 0)
tar = tar_dir_header "foo", "bar", 0, Time.now
tar << tar_file_header("bar", "baz", 0, 0, Time.now)
io = TempIO.new tar
@ -25,8 +25,9 @@ class TestGemPackageTarReader < Gem::Package::TarTestCase
def test_rewind
content = ('a'..'z').to_a.join(" ")
str = tar_file_header("lib/foo", "", 010644, content.size) + content +
"\0" * (512 - content.size)
str =
tar_file_header("lib/foo", "", 010644, content.size, Time.now) +
content + "\0" * (512 - content.size)
str << "\0" * 1024
Gem::Package::TarReader.new(TempIO.new(str)) do |tar_reader|
@ -43,8 +44,8 @@ class TestGemPackageTarReader < Gem::Package::TarTestCase
end
def test_seek
tar = tar_dir_header "foo", "bar", 0
tar << tar_file_header("bar", "baz", 0, 0)
tar = tar_dir_header "foo", "bar", 0, Time.now
tar << tar_file_header("bar", "baz", 0, 0, Time.now)
io = TempIO.new tar
@ -60,8 +61,8 @@ class TestGemPackageTarReader < Gem::Package::TarTestCase
end
def test_seek_missing
tar = tar_dir_header "foo", "bar", 0
tar << tar_file_header("bar", "baz", 0, 0)
tar = tar_dir_header "foo", "bar", 0, Time.now
tar << tar_file_header("bar", "baz", 0, 0, Time.now)
io = TempIO.new tar

View file

@ -9,7 +9,7 @@ class TestGemPackageTarReaderEntry < Gem::Package::TarTestCase
@contents = ('a'..'z').to_a.join * 100
@tar = ''
@tar << tar_file_header("lib/foo", "", 0, @contents.size)
@tar << tar_file_header("lib/foo", "", 0, @contents.size, Time.now)
@tar << @contents
@tar << "\0" * (512 - (@tar.size % 512))

View file

@ -1,5 +1,6 @@
require 'rubygems/package/tar_test_case'
require 'rubygems/package/tar_writer'
require 'minitest/mock'
class TestGemPackageTarWriter < Gem::Package::TarTestCase
@ -18,17 +19,20 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
end
def test_add_file
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])
end
assert_equal "aaaaaaaaaa#{"\0" * 502}", @io.string[512, 512]
assert_equal 1024, @io.pos
end
def test_add_file_digest
digest_algorithms = OpenSSL::Digest::SHA1, OpenSSL::Digest::SHA512
digest_algorithms = Digest::SHA1, Digest::SHA512
Time.stub :now, Time.at(1458518157) do
digests = @tar_writer.add_file_digest 'x', 0644, digest_algorithms do |io|
io.write 'a' * 10
end
@ -41,15 +45,17 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
'e1cf14b0',
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])
end
assert_equal "aaaaaaaaaa#{"\0" * 502}", @io.string[512, 512]
assert_equal 1024, @io.pos
end
def test_add_file_digest_multiple
digest_algorithms = [OpenSSL::Digest::SHA1, OpenSSL::Digest::SHA512]
digest_algorithms = [Digest::SHA1, Digest::SHA512]
Time.stub :now, Time.at(1458518157) do
digests = @tar_writer.add_file_digest 'x', 0644, digest_algorithms do |io|
io.write 'a' * 10
end
@ -62,21 +68,27 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
'e1cf14b0',
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])
end
assert_equal "aaaaaaaaaa#{"\0" * 502}", @io.string[512, 512]
assert_equal 1024, @io.pos
end
def test_add_file_signer
skip 'openssl is missing' unless defined?(OpenSSL::SSL)
signer = Gem::Security::Signer.new PRIVATE_KEY, [PUBLIC_CERT]
Time.stub :now, Time.at(1458518157) do
@tar_writer.add_file_signed 'x', 0644, signer 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])
assert_equal "aaaaaaaaaa#{"\0" * 502}", @io.string[512, 512]
digest = signer.digest_algorithm.new
@ -84,7 +96,8 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
signature = signer.sign digest.digest
assert_headers_equal(tar_file_header('x.sig', '', 0444, signature.length),
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]
@ -92,38 +105,44 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
assert_equal 2048, @io.pos
end
end
def test_add_file_signer_empty
signer = Gem::Security::Signer.new nil, nil
Time.stub :now, Time.at(1458518157) do
@tar_writer.add_file_signed 'x', 0644, signer 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])
end
assert_equal "aaaaaaaaaa#{"\0" * 502}", @io.string[512, 512]
digest = signer.digest_algorithm.new
digest.update 'a' * 10
assert_equal 1024, @io.pos
end
def test_add_file_simple
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])
end
assert_equal "aaaaaaaaaa#{"\0" * 502}", @io.string[512, 512]
assert_equal 1024, @io.pos
end
def test_add_file_simple_padding
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]
end
assert_equal "\0" * 512, @io.string[512, 512]
end
@ -182,12 +201,15 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
end
def test_mkdir
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]
assert_equal 512, @io.pos
end
end
def test_split_name
assert_equal ['b' * 100, 'a' * 155],

View file

@ -64,4 +64,21 @@ class TestGemPathSupport < Gem::TestCase
def util_path
ENV["GEM_PATH"].split(File::PATH_SEPARATOR)
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

View file

@ -186,6 +186,24 @@ class TestGemPlatform < Gem::TestCase
assert((x86_darwin8 === Gem::Platform.local), 'universal =~ x86')
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
util_set_arch 'i686-darwin8'

View file

@ -1,7 +1,13 @@
require 'rubygems/test_case'
require 'ostruct'
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/package'
require 'minitest/mock'
@ -128,19 +134,7 @@ gems:
refute_nil fetcher
assert_kind_of Gem::RemoteFetcher, fetcher
assert_equal proxy_uri, fetcher.instance_variable_get(:@proxy_uri).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)
assert_equal proxy_uri, fetcher.instance_variable_get(:@proxy).to_s
end
def test_fetch_size_bad_uri
@ -155,7 +149,7 @@ gems:
def test_fetch_size_socket_error
fetcher = Gem::RemoteFetcher.new nil
def fetcher.connection_for(uri)
def fetcher.request(uri, request_class, last_modified = nil)
raise SocketError, "tarded"
end
@ -414,70 +408,6 @@ gems:
assert_equal @a2.file_name, File.basename(gem)
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
fetcher = Gem::RemoteFetcher.new nil
@ -560,22 +490,6 @@ gems:
assert_equal nil, fetcher.fetch_path(URI.parse(@gem_repo), Time.at(0))
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
use_ui @ui do
ENV['http_proxy'] = 'http://fakeurl:12345'
@ -611,9 +525,7 @@ gems:
fetcher = Gem::RemoteFetcher.new nil
url = 'http://gems.example.com/redirect'
conn = Object.new
def conn.started?() true end
def conn.request(req)
def fetcher.request(uri, request_class, last_modified = nil)
url = 'http://gems.example.com/redirect'
unless defined? @requested then
@requested = true
@ -627,9 +539,6 @@ gems:
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)
assert_equal 'real_path', data
@ -639,18 +548,13 @@ gems:
fetcher = Gem::RemoteFetcher.new nil
url = 'http://gems.example.com/redirect'
conn = Object.new
def conn.started?() true end
def conn.request(req)
def fetcher.request(uri, request_class, last_modified = nil)
url = 'http://gems.example.com/redirect'
res = Net::HTTPMovedPermanently.new nil, 301, nil
res.add_field 'Location', url
res
end
conn = { "#{Thread.current.object_id}:gems.example.com:80" => conn }
fetcher.instance_variable_set :@connections, conn
e = assert_raises Gem::RemoteFetcher::FetchError do
fetcher.fetch_http URI.parse(url)
end
@ -658,14 +562,6 @@ gems:
assert_equal "too many redirects (#{url})", e.message
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
use_ui @ui do
ENV["http_proxy"] = @proxy_uri
@ -684,117 +580,6 @@ gems:
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
use_ui @ui do
self.class.enable_yaml = false
@ -811,6 +596,42 @@ gems:
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
ssl_server = self.class.start_ssl_server
with_configured_fetcher do |fetcher|
@ -850,18 +671,6 @@ gems:
Gem.configuration = nil
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)
got_exception = false
@ -882,20 +691,6 @@ gems:
assert_match(/0\.4\.2/, data, "Data is not from proxy")
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
def log(level, data) #Do nothing
end
@ -913,9 +708,11 @@ gems:
end
DIR = File.expand_path(File.dirname(__FILE__))
DH_PARAM = OpenSSL::PKey::DH.new(128)
def start_ssl_server(config = {})
raise MiniTest::Skip, 'openssl not installed' unless
defined?(OpenSSL::SSL)
null_logger = NilLog.new
server = WEBrick::HTTPServer.new({
:Port => 0,
@ -934,7 +731,7 @@ gems:
server.mount_proc("/insecure_redirect") { |req, res|
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
begin
server.start
@ -953,8 +750,6 @@ gems:
server
end
private
def start_server(port, data)
@ -1015,24 +810,5 @@ gems:
assert_equal "/home/skillet", @fetcher.correct_for_windows_path(path)
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

View 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