aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordrbrain <drbrain@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2013-07-09 23:21:36 +0000
committerdrbrain <drbrain@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2013-07-09 23:21:36 +0000
commit47f0248b0858898dd24d1e654cedf174059ca677 (patch)
tree493e84160f8609db408d88349f0624a3ff92c3c2
parentcd9f9e471977447a991ced4ea38efb2309459ef5 (diff)
downloadruby-47f0248b0858898dd24d1e654cedf174059ca677.tar.gz
* 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
-rw-r--r--ChangeLog5
-rw-r--r--lib/rubygems.rb58
-rw-r--r--lib/rubygems/available_set.rb68
-rw-r--r--lib/rubygems/basic_specification.rb139
-rw-r--r--lib/rubygems/commands/cert_command.rb40
-rw-r--r--lib/rubygems/commands/cleanup_command.rb4
-rw-r--r--lib/rubygems/commands/environment_command.rb21
-rw-r--r--lib/rubygems/commands/help_command.rb20
-rw-r--r--lib/rubygems/commands/install_command.rb17
-rw-r--r--lib/rubygems/commands/owner_command.rb6
-rw-r--r--lib/rubygems/commands/pristine_command.rb19
-rw-r--r--lib/rubygems/commands/sources_command.rb2
-rw-r--r--lib/rubygems/commands/uninstall_command.rb6
-rw-r--r--lib/rubygems/config_file.rb17
-rwxr-xr-xlib/rubygems/core_ext/kernel_require.rb2
-rw-r--r--lib/rubygems/defaults.rb8
-rw-r--r--lib/rubygems/dependency.rb6
-rw-r--r--lib/rubygems/dependency_installer.rb342
-rw-r--r--lib/rubygems/dependency_resolver.rb717
-rw-r--r--lib/rubygems/dependency_resolver/activation_request.rb109
-rw-r--r--lib/rubygems/dependency_resolver/api_set.rb65
-rw-r--r--lib/rubygems/dependency_resolver/api_specification.rb36
-rw-r--r--lib/rubygems/dependency_resolver/composed_set.rb18
-rw-r--r--lib/rubygems/dependency_resolver/current_set.rb16
-rw-r--r--lib/rubygems/dependency_resolver/dependency_conflict.rb85
-rw-r--r--lib/rubygems/dependency_resolver/dependency_request.rb51
-rw-r--r--lib/rubygems/dependency_resolver/index_set.rb59
-rw-r--r--lib/rubygems/dependency_resolver/index_specification.rb53
-rw-r--r--lib/rubygems/dependency_resolver/installed_specification.rb38
-rw-r--r--lib/rubygems/dependency_resolver/installer_set.rb130
-rw-r--r--lib/rubygems/exceptions.rb81
-rw-r--r--lib/rubygems/ext/builder.rb2
-rw-r--r--lib/rubygems/gem_runner.rb26
-rw-r--r--lib/rubygems/gemcutter_utilities.rb111
-rw-r--r--lib/rubygems/install_default_message.rb12
-rw-r--r--lib/rubygems/install_update_options.rb3
-rw-r--r--lib/rubygems/installer.rb62
-rw-r--r--lib/rubygems/name_tuple.rb25
-rw-r--r--lib/rubygems/package.rb73
-rw-r--r--lib/rubygems/package/tar_test_case.rb18
-rw-r--r--lib/rubygems/package/tar_writer.rb47
-rw-r--r--lib/rubygems/path_support.rb8
-rw-r--r--lib/rubygems/platform.rb8
-rw-r--r--lib/rubygems/remote_fetcher.rb261
-rw-r--r--lib/rubygems/request.rb262
-rw-r--r--lib/rubygems/request_set.rb248
-rw-r--r--lib/rubygems/request_set/gem_dependency_api.rb39
-rw-r--r--lib/rubygems/security.rb55
-rw-r--r--lib/rubygems/security/policy.rb44
-rw-r--r--lib/rubygems/security/signer.rb4
-rw-r--r--lib/rubygems/source.rb32
-rw-r--r--lib/rubygems/source/installed.rb28
-rw-r--r--lib/rubygems/source/local.rb122
-rw-r--r--lib/rubygems/source/specific_file.rb28
-rw-r--r--lib/rubygems/source_local.rb91
-rw-r--r--lib/rubygems/source_specific_file.rb28
-rw-r--r--lib/rubygems/spec_fetcher.rb1
-rw-r--r--lib/rubygems/specification.rb256
-rw-r--r--lib/rubygems/stub_specification.rb112
-rw-r--r--lib/rubygems/test_case.rb155
-rw-r--r--lib/rubygems/uninstaller.rb23
-rw-r--r--lib/rubygems/uri_formatter.rb39
-rw-r--r--lib/rubygems/util/list.rb44
-rw-r--r--lib/rubygems/version.rb16
-rw-r--r--lib/rubygems/version_option.rb10
-rw-r--r--test/rubygems/ca_cert.pem23
-rw-r--r--test/rubygems/client.pem49
-rw-r--r--test/rubygems/encrypted_private_key.pem30
-rw-r--r--test/rubygems/invalid_client.pem49
-rw-r--r--test/rubygems/specifications/bar-0.0.2.gemspec9
-rw-r--r--test/rubygems/specifications/foo-0.0.1.gemspecbin0 -> 269 bytes
-rw-r--r--test/rubygems/test_gem.rb476
-rw-r--r--test/rubygems/test_gem_commands_cert_command.rb168
-rw-r--r--test/rubygems/test_gem_commands_cleanup_command.rb15
-rw-r--r--test/rubygems/test_gem_commands_contents_command.rb8
-rw-r--r--test/rubygems/test_gem_commands_environment_command.rb10
-rw-r--r--test/rubygems/test_gem_commands_help_command.rb7
-rw-r--r--test/rubygems/test_gem_commands_install_command.rb66
-rw-r--r--test/rubygems/test_gem_commands_owner_command.rb49
-rw-r--r--test/rubygems/test_gem_commands_pristine_command.rb26
-rw-r--r--test/rubygems/test_gem_commands_sources_command.rb2
-rw-r--r--test/rubygems/test_gem_commands_update_command.rb3
-rw-r--r--test/rubygems/test_gem_config_file.rb12
-rw-r--r--test/rubygems/test_gem_dependency_installer.rb100
-rw-r--r--test/rubygems/test_gem_dependency_resolver.rb9
-rw-r--r--test/rubygems/test_gem_dependency_resolver_dependency_conflict.rb36
-rw-r--r--test/rubygems/test_gem_gem_runner.rb30
-rw-r--r--test/rubygems/test_gem_impossible_dependencies_error.rb41
-rw-r--r--test/rubygems/test_gem_install_update_options.rb5
-rw-r--r--test/rubygems/test_gem_installer.rb33
-rw-r--r--test/rubygems/test_gem_name_tuple.rb22
-rw-r--r--test/rubygems/test_gem_package.rb121
-rw-r--r--test/rubygems/test_gem_package_old.rb8
-rw-r--r--test/rubygems/test_gem_package_tar_reader.rb17
-rw-r--r--test/rubygems/test_gem_package_tar_reader_entry.rb2
-rw-r--r--test/rubygems/test_gem_package_tar_writer.rb134
-rw-r--r--test/rubygems/test_gem_path_support.rb17
-rw-r--r--test/rubygems/test_gem_platform.rb18
-rw-r--r--test/rubygems/test_gem_remote_fetcher.rb328
-rw-r--r--test/rubygems/test_gem_request.rb239
-rw-r--r--test/rubygems/test_gem_security.rb58
-rw-r--r--test/rubygems/test_gem_security_policy.rb43
-rw-r--r--test/rubygems/test_gem_security_signer.rb14
-rw-r--r--test/rubygems/test_gem_security_trust_dir.rb6
-rw-r--r--test/rubygems/test_gem_source.rb8
-rw-r--r--test/rubygems/test_gem_source_local.rb8
-rw-r--r--test/rubygems/test_gem_source_specific_file.rb2
-rw-r--r--test/rubygems/test_gem_specification.rb451
-rw-r--r--test/rubygems/test_gem_stub_specification.rb30
-rw-r--r--test/rubygems/test_gem_uninstaller.rb14
-rw-r--r--test/rubygems/test_gem_uri_formatter.rb20
-rw-r--r--test/rubygems/test_gem_version.rb15
-rw-r--r--test/rubygems/test_gem_version_option.rb64
113 files changed, 4890 insertions, 2536 deletions
diff --git a/ChangeLog b/ChangeLog
index 0f1bd4f9db..e07f69817f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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
diff --git a/lib/rubygems.rb b/lib/rubygems.rb
index 2c8dbee2a2..dfc1006fd4 100644
--- a/lib/rubygems.rb
+++ b/lib/rubygems.rb
@@ -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
diff --git a/lib/rubygems/available_set.rb b/lib/rubygems/available_set.rb
index 80539feee9..bb0b3a3abe 100644
--- a/lib/rubygems/available_set.rb
+++ b/lib/rubygems/available_set.rb
@@ -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?
diff --git a/lib/rubygems/basic_specification.rb b/lib/rubygems/basic_specification.rb
new file mode 100644
index 0000000000..2e47f32986
--- /dev/null
+++ b/lib/rubygems/basic_specification.rb
@@ -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
diff --git a/lib/rubygems/commands/cert_command.rb b/lib/rubygems/commands/cert_command.rb
index 5a9320f9c4..99e9690755 100644
--- a/lib/rubygems/commands/cert_command.rb
+++ b/lib/rubygems/commands/cert_command.rb
@@ -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,16 +121,31 @@ 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"
- cert = Gem::Security.create_cert_email name, key
+ 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
- key_path = Gem::Security.write key, "gem-private_key.pem"
+ cert = Gem::Security.create_cert_email name, key
cert_path = Gem::Security.write cert, "gem-public_cert.pem"
say "Certificate: #{cert_path}"
- say "Private Key: #{key_path}"
- say "Don't forget to move the key file to somewhere private!"
+
+ if key_path
+ say "Private Key: #{key_path}"
+ say "Don't forget to move the key file to somewhere private!"
+ end
end
def certificates_matching filter
@@ -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)
diff --git a/lib/rubygems/commands/cleanup_command.rb b/lib/rubygems/commands/cleanup_command.rb
index 61f189e449..cacfe89404 100644
--- a/lib/rubygems/commands/cleanup_command.rb
+++ b/lib/rubygems/commands/cleanup_command.rb
@@ -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
-
diff --git a/lib/rubygems/commands/environment_command.rb b/lib/rubygems/commands/environment_command.rb
index 40e71cf094..39d52e7f01 100644
--- a/lib/rubygems/commands/environment_command.rb
+++ b/lib/rubygems/commands/environment_command.rb
@@ -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
diff --git a/lib/rubygems/commands/help_command.rb b/lib/rubygems/commands/help_command.rb
index 7f1fb486e0..9f0ebc087f 100644
--- a/lib/rubygems/commands/help_command.rb
+++ b/lib/rubygems/commands/help_command.rb
@@ -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"
diff --git a/lib/rubygems/commands/install_command.rb b/lib/rubygems/commands/install_command.rb
index 0b58fa665e..ac78764af5 100644
--- a/lib/rubygems/commands/install_command.rb
+++ b/lib/rubygems/commands/install_command.rb
@@ -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
@@ -39,6 +37,12 @@ class Gem::Commands::InstallCommand < Gem::Command
'install the listed gems') do |v,o|
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
@@ -153,7 +157,14 @@ to write the specification by hand. For example:
alert_error "Can't use --version w/ multiple gems. Use name:ver instead."
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]
diff --git a/lib/rubygems/commands/owner_command.rb b/lib/rubygems/commands/owner_command.rb
index 11e6e026fd..c1c0a570d6 100644
--- a/lib/rubygems/commands/owner_command.rb
+++ b/lib/rubygems/commands/owner_command.rb
@@ -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
diff --git a/lib/rubygems/commands/pristine_command.rb b/lib/rubygems/commands/pristine_command.rb
index f22fe11769..0a7f315a0a 100644
--- a/lib/rubygems/commands/pristine_command.rb
+++ b/lib/rubygems/commands/pristine_command.rb
@@ -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
- install_defaults = Gem::ConfigFile::PLATFORM_DEFAULTS['install']
- installer_env_shebang = install_defaults.to_s['--env-shebang']
+ env_shebang =
+ if options.include? :env_shebang then
+ options[:env_shebang]
+ else
+ install_defaults = Gem::ConfigFile::PLATFORM_DEFAULTS['install']
+ 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
diff --git a/lib/rubygems/commands/sources_command.rb b/lib/rubygems/commands/sources_command.rb
index f4cc3e57ae..82bb1f62eb 100644
--- a/lib/rubygems/commands/sources_command.rb
+++ b/lib/rubygems/commands/sources_command.rb
@@ -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
diff --git a/lib/rubygems/commands/uninstall_command.rb b/lib/rubygems/commands/uninstall_command.rb
index 56aa8ee57f..68d170acb1 100644
--- a/lib/rubygems/commands/uninstall_command.rb
+++ b/lib/rubygems/commands/uninstall_command.rb
@@ -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
diff --git a/lib/rubygems/config_file.rb b/lib/rubygems/config_file.rb
index 244e845e6f..d0699dcb29 100644
--- a/lib/rubygems/config_file.rb
+++ b/lib/rubygems/config_file.rb
@@ -141,6 +141,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
diff --git a/lib/rubygems/core_ext/kernel_require.rb b/lib/rubygems/core_ext/kernel_require.rb
index 71062410cb..b9172e26c0 100755
--- a/lib/rubygems/core_ext/kernel_require.rb
+++ b/lib/rubygems/core_ext/kernel_require.rb
@@ -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
}
diff --git a/lib/rubygems/defaults.rb b/lib/rubygems/defaults.rb
index cc8dc722fc..9b5bea21da 100644
--- a/lib/rubygems/defaults.rb
+++ b/lib/rubygems/defaults.rb
@@ -15,6 +15,14 @@ module Gem
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
diff --git a/lib/rubygems/dependency.rb b/lib/rubygems/dependency.rb
index 1e3cc168a8..a96d67c3e5 100644
--- a/lib/rubygems/dependency.rb
+++ b/lib/rubygems/dependency.rb
@@ -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|
diff --git a/lib/rubygems/dependency_installer.rb b/lib/rubygems/dependency_installer.rb
index 6f19a310f7..2523d1b2ae 100644
--- a/lib/rubygems/dependency_installer.rb
+++ b/lib/rubygems/dependency_installer.rb
@@ -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,9 +28,31 @@ 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.
#
# Options are:
@@ -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,13 +110,61 @@ 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
+ ##
+ #--
+ # TODO remove, no longer used
+
+ def add_found_dependencies to_do, dependency_list # :nodoc:
+ seen = {}
+ dependencies = Hash.new { |h, name| h[name] = Gem::Dependency.new name }
+
+ until to_do.empty? do
+ spec = to_do.shift
+
+ # HACK why is spec nil?
+ next if spec.nil? or seen[spec.name]
+ seen[spec.name] = true
+
+ deps = spec.runtime_dependencies
+
+ if @development
+ if @dev_shallow
+ if @toplevel_specs.include? spec.full_name
+ deps |= spec.development_dependencies
+ end
+ else
+ deps |= spec.development_dependencies
+ end
+ end
+
+ deps.each do |dep|
+ dependencies[dep.name] = dependencies[dep.name].merge dep
+
+ if @minimal_deps
+ next if Gem::Specification.any? do |installed_spec|
+ dep.name == installed_spec.name and
+ dep.requirement.satisfied_by? installed_spec.version
+ end
+ end
+
+ results = find_gems_with_sources(dep)
+ results.sorted.each do |t|
+ to_do.push t.spec
+ end
+
+ results.remove_installed! dep
+
+ @available << results
+ results.inject_into_list dependency_list
+ end
+ end
+
+ dependency_list.remove_specs_unsatisfied_by dependencies
+ end
##
# Creates an AvailableSet to install from based on +dep_or_name+ and
# +version+
@@ -138,7 +203,7 @@ class Gem::DependencyInstaller
# sources. Gems are sorted with newer gems preferred over older gems, and
# local gems preferred over remote gems.
- def find_gems_with_sources(dep)
+ def find_gems_with_sources dep # :nodoc:
set = Gem::AvailableSet.new
if consider_local?
@@ -179,10 +244,52 @@ class Gem::DependencyInstaller
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,
+ version = Gem::Requirement.default,
+ prerelease = false
+
+ set = Gem::AvailableSet.new
+
+ if consider_local?
+ if gem_name =~ /\.gem$/ and File.file? gem_name then
+ src = Gem::Source::SpecificFile.new(gem_name)
+ set.add src.spec, src
+ else
+ local = Gem::Source::Local.new
+
+ if s = local.find_gem(gem_name, version)
+ set.add s, local
+ end
+ end
+ end
+
+ if set.empty?
+ dep = Gem::Dependency.new gem_name, version
+ # HACK Dependency objects should be immutable
+ dep.prerelease = true if prerelease
+
+ set = find_gems_with_sources(dep)
+ set.match_platform!
+ end
+
+ if set.empty?
+ raise Gem::SpecificGemNotFoundException.new(gem_name, version, @errors)
+ end
+
+ @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
+ def gather_dependencies # :nodoc:
specs = @available.all_specs
# these gems were listed by the user, always install them
@@ -214,93 +321,19 @@ class Gem::DependencyInstaller
@gems_to_install = dependency_list.dependency_order.reverse
end
- def add_found_dependencies to_do, dependency_list
- seen = {}
- dependencies = Hash.new { |h, name| h[name] = Gem::Dependency.new name }
-
- until to_do.empty? do
- spec = to_do.shift
-
- # HACK why is spec nil?
- next if spec.nil? or seen[spec.name]
- seen[spec.name] = true
-
- deps = spec.runtime_dependencies
-
- if @development
- if @dev_shallow
- if @toplevel_specs.include? spec.full_name
- deps |= spec.development_dependencies
- end
- else
- deps |= spec.development_dependencies
- end
- end
-
- deps.each do |dep|
- dependencies[dep.name] = dependencies[dep.name].merge dep
-
- if @minimal_deps
- next if Gem::Specification.any? do |installed_spec|
- dep.name == installed_spec.name and
- dep.requirement.satisfied_by? installed_spec.version
- end
- end
-
- results = find_gems_with_sources(dep)
-
- results.sorted.each do |t|
- to_do.push t.spec
- end
-
- results.remove_installed! dep
-
- @available << results
- results.inject_into_list dependency_list
- end
- end
-
- dependency_list.remove_specs_unsatisfied_by dependencies
- 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,
- version = Gem::Requirement.default,
- prerelease = false)
-
- set = Gem::AvailableSet.new
-
- if consider_local?
- if gem_name =~ /\.gem$/ and File.file? gem_name then
- src = Gem::Source::SpecificFile.new(gem_name)
- set.add src.spec, src
- else
- local = Gem::Source::Local.new
-
- if s = local.find_gem(gem_name, version)
- set.add s, local
+ 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
-
- if set.empty?
- dep = Gem::Dependency.new gem_name, version
- # HACK Dependency objects should be immutable
- dep.prerelease = true if prerelease
-
- set = find_gems_with_sources(dep)
- set.match_platform!
- end
-
- if set.empty?
- raise Gem::SpecificGemNotFoundException.new(gem_name, version, @errors)
- end
-
- @available = set
+ yield unless fork_happened
end
##
@@ -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
+ options = {
+ :bin_dir => @bin_dir,
+ :build_args => @build_args,
+ :env_shebang => @env_shebang,
+ :force => @force,
+ :format_executable => @format_executable,
+ :ignore_dependencies => @ignore_dependencies,
+ :security_policy => @security_policy,
+ :user_install => @user_install,
+ :wrappers => @wrappers,
+ :install_as_default => @install_as_default
+ }
+ options[:install_dir] = @install_dir if @only_install_dir
- inst = Gem::Installer.new local_gem_path,
- :bin_dir => @bin_dir,
- :development => is_dev,
- :env_shebang => @env_shebang,
- :force => @force,
- :format_executable => @format_executable,
- :ignore_dependencies => @ignore_dependencies,
- :install_dir => @install_dir,
- :security_policy => @security_policy,
- :user_install => @user_install,
- :wrappers => @wrappers,
- :build_args => @build_args
-
- spec = inst.install
-
- @installed_gems << spec
+ 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
- end
+ def install_development_deps # :nodoc:
+ if @development and @dev_shallow then
+ :shallow
+ elsif @development then
+ :all
+ else
+ :none
end
- yield unless fork_happened
end
+
+ def resolve_dependencies dep_or_name, version # :nodoc:
+ as = available_set_for dep_or_name, version
+
+ request_set = as.to_request_set install_development_deps
+ request_set.soft_missing = @force
+
+ installer_set = Gem::DependencyResolver::InstallerSet.new @domain
+ installer_set.always_install.concat request_set.always_install
+ installer_set.ignore_installed = @only_install_dir
+
+ if @ignore_dependencies then
+ installer_set.ignore_dependencies = true
+ request_set.soft_missing = true
+ end
+
+ request_set.resolve Gem::DependencyResolver.compose_sets(as, installer_set)
+
+ request_set
+ end
+
end
diff --git a/lib/rubygems/dependency_resolver.rb b/lib/rubygems/dependency_resolver.rb
index 66f55eb9ad..e5c05972d8 100644
--- a/lib/rubygems/dependency_resolver.rb
+++ b/lib/rubygems/dependency_resolver.rb
@@ -1,575 +1,240 @@
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
+class Gem::DependencyResolver
- super "unable to resolve conflicting dependencies '#{a}' and '#{b}'"
- end
+ ##
+ # Contains all the conflicts encountered while doing resolution
- attr_reader :conflict
+ attr_reader :conflicts
- def conflicting_dependencies
- @conflict.conflicting_dependencies
- end
- end
+ attr_accessor :development
- # 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}'"
+ attr_reader :missing
- @dependency = dep
- end
+ ##
+ # When a missing dependency, don't stop. Just go on and record what was
+ # missing.
- attr_reader :dependency
- end
+ attr_accessor :soft_missing
- # 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 self.compose_sets *sets
+ Gem::DependencyResolver::ComposedSet.new(*sets)
+ end
- def dependency
- @request.dependency
- end
+ ##
+ # Provide a DependencyResolver that queries only against the already
+ # installed gems.
- attr_reader :conflicts
+ def self.for_current_gems needed
+ new needed, Gem::DependencyResolver::CurrentSet.new
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.
+ ##
+ # Create DependencyResolver object which will resolve the tree starting
+ # with +needed+ Depedency objects.
#
- 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
-
- # Contains all the conflicts encountered while doing resolution
- #
- attr_reader :conflicts
-
- # Proceed with resolution! Returns an array of ActivationRequest
- # objects.
- #
- def resolve
- @conflicts = []
-
- needed = @needed.map { |n| DependencyRequest.new(n, nil) }
-
- res = resolve_for needed, []
-
- if res.kind_of? DependencyConflict
- raise DependencyResolutionError.new(res)
- end
-
- res
- end
-
- # Used internally to indicate that a dependency conflicted
- # with a spec that would be activated.
- #
- class DependencyConflict
- def initialize(dependency, activated, failed_dep=dependency)
- @dependency = dependency
- @activated = activated
- @failed_dep = failed_dep
- end
-
- attr_reader :dependency, :activated
-
- # Return the Specification that listed the dependency
- #
- def requester
- @failed_dep.requester
- end
-
- def for_spec?(spec)
- @dependency.name == spec.name
- end
+ # +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
- # Return the 2 dependency objects that conflicted
- #
- def conflicting_dependencies
- [@failed_dep.dependency, @activated.request.dependency]
- end
+ 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
- # 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
+ @set.prefetch reqs
- attr_reader :dependency, :requester
+ reqs
+ end
- def name
- @dependency.name
- end
+ ##
+ # Proceed with resolution! Returns an array of ActivationRequest objects.
- def matches_spec?(spec)
- @dependency.matches_spec? spec
- end
+ def resolve
+ @conflicts = []
- def to_s
- @dependency.to_s
- end
+ needed = nil
- def ==(other)
- case other
- when Dependency
- @dependency == other
- when DependencyRequest
- @dependency == other.dependency && @requester == other.requester
- else
- false
- end
- end
+ @needed.reverse_each do |n|
+ needed = Gem::List.new(Gem::DependencyResolver::DependencyRequest.new(n, nil), needed)
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
+ res = resolve_for needed, nil
- # Indicate if this activation is one of a set of possible
- # requests for the same Dependency request.
- #
- def others_possible?
- @others_possible
- end
+ raise Gem::DependencyResolutionError, res if
+ res.kind_of? Gem::DependencyResolver::DependencyConflict
- # 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
+ res.to_a
+ end
- def download(path)
- if @spec.respond_to? :source
- source = @spec.source
+ ##
+ # 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 specs && existing = specs.find { |s| dep.name == s.name }
+
+ # then we're done since this new dep matches the
+ # existing spec.
+ next if dep.matches_spec? existing
+
+ # There is a conflict! We return the conflict
+ # object which will be seen by the caller and be
+ # handled at the right level.
+
+ # If the existing activation indicates that there
+ # are other possibles for it, then issue the conflict
+ # on the dep for the activation itself. Otherwise, issue
+ # it on the requester's request itself.
+ #
+ if existing.others_possible?
+ conflict =
+ Gem::DependencyResolver::DependencyConflict.new dep, existing
else
- source = Gem.sources.first
+ depreq = existing.request.requester.request
+ conflict =
+ Gem::DependencyResolver::DependencyConflict.new depreq, existing, dep
end
+ @conflicts << conflict
- 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
+ return conflict
end
- ##
- # Indicates if the requested gem has already been installed.
+ # Get a list of all specs that satisfy dep
+ possible = @set.find_all dep
- def installed?
- this_spec = full_spec
+ case possible.size
+ when 0
+ @missing << dep
- Gem::Specification.any? do |s|
- s == this_spec
+ unless @soft_missing
+ # If there are none, then our work here is done.
+ raise Gem::UnsatisfiableDependencyError, dep
end
- end
- end
-
- def requests(s, act)
- reqs = []
- s.dependencies.each do |d|
- next unless d.type == :runtime
- reqs << DependencyRequest.new(d, act)
- end
-
- @set.prefetch(reqs)
-
- reqs
- end
-
- # The meat of the algorithm. Given +needed+ DependencyRequest objects
- # and +specs+ being a list to ActivationRequest, calculate a new list
- # of ActivationRequest objects.
- #
- def resolve_for(needed, specs)
- until needed.empty?
- dep = needed.shift
-
- # If there is already a spec activated for the requested name...
- if existing = specs.find { |s| dep.name == s.name }
-
- # then we're done since this new dep matches the
- # existing spec.
- next if dep.matches_spec? existing
-
- # There is a conflict! We return the conflict
- # object which will be seen by the caller and be
- # handled at the right level.
-
- # If the existing activation indicates that there
- # are other possibles for it, then issue the conflict
- # on the dep for the activation itself. Otherwise, issue
- # it on the requester's request itself.
- #
- if existing.others_possible?
- conflict = DependencyConflict.new(dep, existing)
+ 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 = Gem::DependencyResolver::ActivationRequest.new spec, dep, false
+
+ specs = Gem::List.prepend specs, act
+
+ # Put the deps for at the beginning of needed
+ # rather than the end to match the depth first
+ # searching done by the multiple case code below.
+ #
+ # This keeps the error messages consistent.
+ needed = requests(spec, act, needed)
+ else
+ # There are multiple specs for this dep. This is
+ # the case that this class is built to handle.
+
+ # Sort them so that we try the highest versions
+ # first.
+ possible = possible.sort_by { |s| [s.source, s.version] }
+
+ # We track the conflicts seen so that we can report them
+ # to help the user figure out how to fix the situation.
+ conflicts = []
+
+ # To figure out which to pick, we keep resolving
+ # given each one being activated and if there isn't
+ # a conflict, we know we've found a full set.
+ #
+ # We use an until loop rather than #reverse_each
+ # to keep the stack short since we're using a recursive
+ # algorithm.
+ #
+ until possible.empty?
+ s = possible.pop
+
+ # Recursively call #resolve_for with this spec
+ # and add it's dependencies into the picture...
+
+ act = Gem::DependencyResolver::ActivationRequest.new s, dep
+
+ try = requests(s, act, needed)
+
+ res = resolve_for try, Gem::List.prepend(specs, act)
+
+ # While trying to resolve these dependencies, there may
+ # be a conflict!
+
+ if res.kind_of? Gem::DependencyResolver::DependencyConflict
+ # The conflict might be created not by this invocation
+ # but rather one up the stack, so if we can't attempt
+ # to resolve this conflict (conflict isn't with the spec +s+)
+ # then just return it so the caller can try to sort it out.
+ return res unless res.for_spec? s
+
+ # Otherwise, this is a conflict that we can attempt to fix
+ conflicts << [s, res]
+
+ # Optimization:
+ #
+ # Because the conflict indicates the dependency that trigger
+ # it, we can prune possible based on this new information.
+ #
+ # This cuts down on the number of iterations needed.
+ possible.delete_if { |x| !res.dependency.matches_spec? x }
else
- depreq = existing.request.requester.request
- conflict = DependencyConflict.new(depreq, existing, dep)
+ # No conflict, return the specs
+ return res
end
- @conflicts << conflict
-
- return conflict
end
- # Get a list of all specs that satisfy dep
- possible = @set.find_all(dep)
-
- case possible.size
- when 0
- # If there are none, then our work here is done.
- raise UnsatisfiableDepedencyError.new(dep)
- when 1
- # If there is one, then we just add it to specs
- # and process the specs dependencies by adding
- # them to needed.
-
- spec = possible.first
- act = ActivationRequest.new(spec, dep, false)
-
- specs << act
-
- # Put the deps for at the beginning of needed
- # rather than the end to match the depth first
- # searching done by the multiple case code below.
- #
- # This keeps the error messages consistent.
- needed = requests(spec, act) + needed
- else
- # There are multiple specs for this dep. This is
- # the case that this class is built to handle.
-
- # Sort them so that we try the highest versions
- # first.
- possible = possible.sort_by { |s| s.version }
-
- # We track the conflicts seen so that we can report them
- # to help the user figure out how to fix the situation.
- conflicts = []
-
- # To figure out which to pick, we keep resolving
- # given each one being activated and if there isn't
- # a conflict, we know we've found a full set.
- #
- # We use an until loop rather than #reverse_each
- # to keep the stack short since we're using a recursive
- # algorithm.
- #
- until possible.empty?
- s = possible.pop
-
- # Recursively call #resolve_for with this spec
- # and add it's dependencies into the picture...
-
- act = ActivationRequest.new(s, dep)
-
- try = requests(s, act) + needed
-
- res = resolve_for(try, specs + [act])
-
- # While trying to resolve these dependencies, there may
- # be a conflict!
-
- if res.kind_of? DependencyConflict
- # The conflict might be created not by this invocation
- # but rather one up the stack, so if we can't attempt
- # to resolve this conflict (conflict isn't with the spec +s+)
- # then just return it so the caller can try to sort it out.
- return res unless res.for_spec? s
-
- # Otherwise, this is a conflict that we can attempt to fix
- conflicts << [s, res]
-
- # Optimization:
- #
- # Because the conflict indicates the dependency that trigger
- # it, we can prune possible based on this new information.
- #
- # This cuts down on the number of iterations needed.
- possible.delete_if { |x| !res.dependency.matches_spec? x }
- else
- # No conflict, return the specs
- return res
- end
- end
-
- # We tried all possibles and nothing worked, so we let the user
- # know and include as much information about the problem since
- # the user is going to have to take action to fix this.
- raise ImpossibleDependenciesError.new(dep, conflicts)
- end
+ # We tried all possibles and nothing worked, so we let the user
+ # know and include as much information about the problem since
+ # the user is going to have to take action to fix this.
+ raise Gem::ImpossibleDependenciesError.new(dep, conflicts)
end
-
- specs
end
+
+ specs
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'
+
diff --git a/lib/rubygems/dependency_resolver/activation_request.rb b/lib/rubygems/dependency_resolver/activation_request.rb
new file mode 100644
index 0000000000..25af6378ac
--- /dev/null
+++ b/lib/rubygems/dependency_resolver/activation_request.rb
@@ -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
+
diff --git a/lib/rubygems/dependency_resolver/api_set.rb b/lib/rubygems/dependency_resolver/api_set.rb
new file mode 100644
index 0000000000..469c005a09
--- /dev/null
+++ b/lib/rubygems/dependency_resolver/api_set.rb
@@ -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
+
diff --git a/lib/rubygems/dependency_resolver/api_specification.rb b/lib/rubygems/dependency_resolver/api_specification.rb
new file mode 100644
index 0000000000..5ad07396cf
--- /dev/null
+++ b/lib/rubygems/dependency_resolver/api_specification.rb
@@ -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
+
diff --git a/lib/rubygems/dependency_resolver/composed_set.rb b/lib/rubygems/dependency_resolver/composed_set.rb
new file mode 100644
index 0000000000..fb38128bb0
--- /dev/null
+++ b/lib/rubygems/dependency_resolver/composed_set.rb
@@ -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
+
diff --git a/lib/rubygems/dependency_resolver/current_set.rb b/lib/rubygems/dependency_resolver/current_set.rb
new file mode 100644
index 0000000000..13bc490e9e
--- /dev/null
+++ b/lib/rubygems/dependency_resolver/current_set.rb
@@ -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
+
diff --git a/lib/rubygems/dependency_resolver/dependency_conflict.rb b/lib/rubygems/dependency_resolver/dependency_conflict.rb
new file mode 100644
index 0000000000..1755d910c3
--- /dev/null
+++ b/lib/rubygems/dependency_resolver/dependency_conflict.rb
@@ -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
+
diff --git a/lib/rubygems/dependency_resolver/dependency_request.rb b/lib/rubygems/dependency_resolver/dependency_request.rb
new file mode 100644
index 0000000000..05e447c3be
--- /dev/null
+++ b/lib/rubygems/dependency_resolver/dependency_request.rb
@@ -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
+
diff --git a/lib/rubygems/dependency_resolver/index_set.rb b/lib/rubygems/dependency_resolver/index_set.rb
new file mode 100644
index 0000000000..fcf919d81b
--- /dev/null
+++ b/lib/rubygems/dependency_resolver/index_set.rb
@@ -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
+
diff --git a/lib/rubygems/dependency_resolver/index_specification.rb b/lib/rubygems/dependency_resolver/index_specification.rb
new file mode 100644
index 0000000000..371018ba44
--- /dev/null
+++ b/lib/rubygems/dependency_resolver/index_specification.rb
@@ -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
+
diff --git a/lib/rubygems/dependency_resolver/installed_specification.rb b/lib/rubygems/dependency_resolver/installed_specification.rb
new file mode 100644
index 0000000000..af167572bf
--- /dev/null
+++ b/lib/rubygems/dependency_resolver/installed_specification.rb
@@ -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
+
diff --git a/lib/rubygems/dependency_resolver/installer_set.rb b/lib/rubygems/dependency_resolver/installer_set.rb
new file mode 100644
index 0000000000..7de052df77
--- /dev/null
+++ b/lib/rubygems/dependency_resolver/installer_set.rb
@@ -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
+
diff --git a/lib/rubygems/exceptions.rb b/lib/rubygems/exceptions.rb
index ff389b320b..13cb5c7f6e 100644
--- a/lib/rubygems/exceptions.rb
+++ b/lib/rubygems/exceptions.rb
@@ -17,6 +17,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.
class Gem::GemNotInHomeException < Gem::Exception
@@ -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:
+
diff --git a/lib/rubygems/ext/builder.rb b/lib/rubygems/ext/builder.rb
index ab454b4ba1..74689bb5e2 100644
--- a/lib/rubygems/ext/builder.rb
+++ b/lib/rubygems/ext/builder.rb
@@ -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
diff --git a/lib/rubygems/gem_runner.rb b/lib/rubygems/gem_runner.rb
index 8060e15312..7a3fd6b116 100644
--- a/lib/rubygems/gem_runner.rb
+++ b/lib/rubygems/gem_runner.rb
@@ -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)
diff --git a/lib/rubygems/gemcutter_utilities.rb b/lib/rubygems/gemcutter_utilities.rb
index 04d7cd300f..6446cc9799 100644
--- a/lib/rubygems/gemcutter_utilities.rb
+++ b/lib/rubygems/gemcutter_utilities.rb
@@ -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,47 +105,36 @@ 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'
+ ##
+ # Retrieves the pre-configured API key +key+ or terminates interaction with
+ # an error.
- self.host = host if host
- unless self.host
- alert_error "You must specify a gem server"
+ def verify_api_key(key)
+ if Gem.configuration.api_keys.key? key then
+ Gem.configuration.api_keys[key]
+ else
+ alert_error "No such API key. Please add it to your configuration (done automatically on initial `gem push`)."
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
+ ##
+ # 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 resp
+ yield response
else
- say resp.body
+ say response.body
end
else
- message = resp.body
+ message = response.body
message = "#{error_prefix}: #{message}" if error_prefix
say message
@@ -103,13 +142,5 @@ module Gem::GemcutterUtilities
end
end
- def verify_api_key(key)
- if Gem.configuration.api_keys.key? key then
- Gem.configuration.api_keys[key]
- else
- alert_error "No such API key. Please add it to your configuration (done automatically on initial `gem push`)."
- terminate_interaction 1 # TODO: question this
- end
- end
-
end
+
diff --git a/lib/rubygems/install_default_message.rb b/lib/rubygems/install_default_message.rb
new file mode 100644
index 0000000000..458ba3da96
--- /dev/null
+++ b/lib/rubygems/install_default_message.rb
@@ -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
+
diff --git a/lib/rubygems/install_update_options.rb b/lib/rubygems/install_update_options.rb
index ffa8f910df..d3f55cd5ea 100644
--- a/lib/rubygems/install_update_options.rb
+++ b/lib/rubygems/install_update_options.rb
@@ -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)"
diff --git a/lib/rubygems/installer.rb b/lib/rubygems/installer.rb
index 939bc9693d..c6fad3c492 100644
--- a/lib/rubygems/installer.rb
+++ b/lib/rubygems/installer.rb
@@ -212,16 +212,21 @@ class Gem::Installer
FileUtils.rm_rf gem_dir
FileUtils.mkdir_p gem_dir
-
- extract_files
-
- build_extensions
- write_build_info_file
- run_post_build_hooks
-
- generate_bin
- write_spec
- write_cache_file
+
+ if @options[:install_as_default]
+ extract_bin
+ write_default_spec
+ else
+ extract_files
+
+ build_extensions
+ write_build_info_file
+ run_post_build_hooks
+
+ generate_bin
+ write_spec
+ write_cache_file
+ end
say spec.post_install_message unless spec.post_install_message.nil?
@@ -327,6 +332,14 @@ class Gem::Installer
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.
@@ -336,6 +349,16 @@ class Gem::Installer
file.fsync rescue nil # for filesystems without fsync(2)
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]
@@ -715,6 +738,15 @@ EOF
def extract_files
@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
- Gem.ensure_gem_subdirectories gem_home
+ if options[:install_as_default]
+ Gem.ensure_default_gem_subdirectories gem_home
+ else
+ Gem.ensure_gem_subdirectories gem_home
+ end
return true if @force
diff --git a/lib/rubygems/name_tuple.rb b/lib/rubygems/name_tuple.rb
index d16fad26f1..f16ab369fa 100644
--- a/lib/rubygems/name_tuple.rb
+++ b/lib/rubygems/name_tuple.rb
@@ -43,6 +43,20 @@ class Gem::NameTuple
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.
def match_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
diff --git a/lib/rubygems/package.rb b/lib/rubygems/package.rb
index 957446257d..76f45a99d8 100644
--- a/lib/rubygems/package.rb
+++ b/lib/rubygems/package.rb
@@ -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,27 +524,38 @@ EOM
end
##
+ # Verifies +entry+ in a .gem file.
+
+ def verify_entry entry
+ file_name = entry.full_name
+ @files << file_name
+
+ case file_name
+ when /\.sig$/ then
+ @signatures[$`] = entry.read if @security_policy
+ return
+ else
+ digest entry
+ end
+
+ case file_name
+ when /^metadata(.gz)?$/ then
+ load_spec entry
+ when 'data.tar.gz' then
+ verify_gz entry
+ end
+ rescue => e
+ message = "package is corrupt, exception while verifying: " +
+ "#{e.message} (#{e.class})"
+ raise Gem::Package::FormatError.new message, @gem
+ end
+
+ ##
# Verifies the files of the +gem+
def verify_files gem
gem.each do |entry|
- file_name = entry.full_name
- @files << file_name
-
- case file_name
- when /\.sig$/ then
- @signatures[$`] = entry.read if @security_policy
- next
- else
- digest entry
- end
-
- case file_name
- when /^metadata(.gz)?$/ then
- load_spec entry
- when 'data.tar.gz' then
- verify_gz entry
- end
+ verify_entry entry
end
unless @spec then
diff --git a/lib/rubygems/package/tar_test_case.rb b/lib/rubygems/package/tar_test_case.rb
index 4601f1328f..5253e32f36 100644
--- a/lib/rubygems/package/tar_test_case.rb
+++ b/lib/rubygems/package/tar_test_case.rb
@@ -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
diff --git a/lib/rubygems/package/tar_writer.rb b/lib/rubygems/package/tar_writer.rb
index f2c11e3544..e1b38ad6b5 100644
--- a/lib/rubygems/package/tar_writer.rb
+++ b/lib/rubygems/package/tar_writer.rb
@@ -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
- signature = signer.sign signature_digest.digest
+ if signer.key then
+ signature = signer.sign signature_digest.digest
- add_file_simple "#{name}.sig", 0444, signature.length do |io|
- io.write signature
- end if signature
+ add_file_simple "#{name}.sig", 0444, signature.length do |io|
+ io.write 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
diff --git a/lib/rubygems/path_support.rb b/lib/rubygems/path_support.rb
index 7195b322ef..5a16d7a6df 100644
--- a/lib/rubygems/path_support.rb
+++ b/lib/rubygems/path_support.rb
@@ -13,6 +13,10 @@ class Gem::PathSupport
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
# hashtable, or defaults to ENV, the system environment.
@@ -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
diff --git a/lib/rubygems/platform.rb b/lib/rubygems/platform.rb
index 4a4674b72f..21345d73df 100644
--- a/lib/rubygems/platform.rb
+++ b/lib/rubygems/platform.rb
@@ -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
diff --git a/lib/rubygems/remote_fetcher.rb b/lib/rubygems/remote_fetcher.rb
index ec052b50da..c10f9ebae8 100644
--- a/lib/rubygems/remote_fetcher.rb
+++ b/lib/rubygems/remote_fetcher.rb
@@ -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)
diff --git a/lib/rubygems/request.rb b/lib/rubygems/request.rb
new file mode 100644
index 0000000000..60e31838f9
--- /dev/null
+++ b/lib/rubygems/request.rb
@@ -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
+
diff --git a/lib/rubygems/request_set.rb b/lib/rubygems/request_set.rb
index 6c52b90c40..748c320c28 100644
--- a/lib/rubygems/request_set.rb
+++ b/lib/rubygems/request_set.rb
@@ -5,178 +5,176 @@ require 'rubygems/dependency_list'
require 'rubygems/installer'
require 'tsort'
-module Gem
- class RequestSet
+class Gem::RequestSet
- include TSort
+ include TSort
- def initialize(*deps)
- @dependencies = deps
+ ##
+ # Array of gems to install even if already installed
- yield self if block_given?
- end
+ attr_reader :always_install
- attr_reader :dependencies
+ attr_reader :dependencies
- # Declare that a gem of name +name+ with +reqs+ requirements
- # is needed.
- #
- def gem(name, *reqs)
- @dependencies << Gem::Dependency.new(name, reqs)
- end
+ attr_accessor :development
- # Add +deps+ Gem::Depedency objects to the set.
- #
- def import(deps)
- @dependencies += deps
- end
+ ##
+ # Treat missing dependencies as silent errors
- # 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
- end
+ attr_accessor :soft_missing
- # 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
- end
+ def initialize *deps
+ @dependencies = deps
- # Load a dependency management file.
- #
- def load_gemdeps(path)
- gf = GemDepedencyAPI.new(self, path)
- gf.load
- end
+ @always_install = []
+ @development = false
+ @soft_missing = false
- def specs
- @specs ||= @requests.map { |r| r.full_spec }
- end
+ yield self if block_given?
+ end
- def tsort_each_node(&block)
- @requests.each(&block)
- end
+ ##
+ # Declare that a gem of name +name+ with +reqs+ requirements is needed.
- 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 gem name, *reqs
+ @dependencies << Gem::Dependency.new(name, reqs)
+ end
+
+ ##
+ # Add +deps+ Gem::Dependency objects to the set.
+
+ def import deps
+ @dependencies += deps
+ end
- def sorted_requests
- @sorted ||= strongly_connected_components.flatten
+ def install options, &block
+ if dir = options[:install_dir]
+ return install_into dir, false, options, &block
end
- def specs_in(dir)
- Dir["#{dir}/specifications/*.gemspec"].map do |g|
- Gem::Specification.load g
+ 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
- end
- def install_into(dir, force=true, &b)
- existing = force ? [] : specs_in(dir)
+ path = req.download cache_dir
- dir = File.expand_path dir
+ inst = Gem::Installer.new path, options
- installed = []
+ yield req, inst if block_given?
- sorted_requests.each do |req|
- if existing.find { |s| s.full_name == req.spec.full_name }
- b.call req, nil if b
- next
- end
+ specs << inst.install
+ end
- path = req.download(dir)
+ specs
+ end
- inst = Gem::Installer.new path, :install_dir => dir,
- :only_install_dir => true
+ def install_into dir, force = true, options = {}
+ existing = force ? [] : specs_in(dir)
+ existing.delete_if { |s| @always_install.include? s }
- b.call req, inst if b
+ dir = File.expand_path dir
- inst.install
+ installed = []
- installed << req
+ sorted_requests.each do |req|
+ if existing.find { |s| s.full_name == req.spec.full_name }
+ yield req, nil if block_given?
+ next
end
- installed
- end
+ path = req.download(dir)
- def install(options, &b)
- if dir = options[:install_dir]
- return install_into(dir, false, &b)
+ unless path then # already installed
+ yield req, nil if block_given?
+ next
end
- cache_dir = options[:cache_dir] || Gem.dir
+ options[:install_dir] = dir
+ options[:only_install_dir] = true
- specs = []
+ inst = Gem::Installer.new path, options
- sorted_requests.each do |req|
- if req.installed?
- b.call req, nil if b
- next
- end
+ yield req, inst if block_given?
- path = req.download cache_dir
+ inst.install
- inst = Gem::Installer.new path, options
+ installed << req
+ end
- b.call req, inst if b
+ installed
+ end
- specs << inst.install
- end
+ ##
+ # Load a dependency management file.
- specs
- end
+ def load_gemdeps path
+ gf = Gem::RequestSet::GemDepedencyAPI.new self, path
+ gf.load
+ end
- # A semi-compatible DSL for Bundler's Gemfile format
- #
- class GemDepedencyAPI
- def initialize(set, path)
- @set = set
- @path = path
- end
+ ##
+ # Resolve the requested dependencies and return an Array of Specification
+ # objects to be activated.
- def load
- instance_eval File.read(@path).untaint, @path, 1
- end
+ def resolve set = nil
+ resolver = Gem::DependencyResolver.new @dependencies, set
+ resolver.development = @development
+ resolver.soft_missing = @soft_missing
- # DSL
+ @requests = resolver.resolve
+ end
- def source(url)
- end
+ ##
+ # Resolve the requested dependencies against the gems available via Gem.path
+ # and return an Array of Specification objects to be activated.
- def gem(name, *reqs)
- # Ignore the opts for now.
- reqs.pop if reqs.last.kind_of?(Hash)
+ def resolve_current
+ resolve Gem::DependencyResolver::CurrentSet.new
+ end
- @set.gem name, *reqs
- end
+ def sorted_requests
+ @sorted ||= strongly_connected_components.flatten
+ end
- def platform(what)
- if what == :ruby
- yield
- end
- end
+ def specs
+ @specs ||= @requests.map { |r| r.full_spec }
+ end
+
+ def specs_in dir
+ Dir["#{dir}/specifications/*.gemspec"].map do |g|
+ Gem::Specification.load g
+ end
+ end
+
+ def tsort_each_node &block # :nodoc:
+ @requests.each(&block)
+ end
- alias_method :platforms, :platform
+ def tsort_each_child node # :nodoc:
+ node.spec.dependencies.each do |dep|
+ next if dep.type == :development and not @development
- def group(*what)
+ 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'
diff --git a/lib/rubygems/request_set/gem_dependency_api.rb b/lib/rubygems/request_set/gem_dependency_api.rb
new file mode 100644
index 0000000000..178ed1b59d
--- /dev/null
+++ b/lib/rubygems/request_set/gem_dependency_api.rb
@@ -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
+
diff --git a/lib/rubygems/security.rb b/lib/rubygems/security.rb
index bed47ab9f3..ef0ff12cda 100644
--- a/lib/rubygems/security.rb
+++ b/lib/rubygems/security.rb
@@ -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,17 +338,26 @@ 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
@@ -370,6 +365,12 @@ module Gem::Security
KEY_LENGTH = 2048
##
+ # Cipher used to encrypt the key pair used to sign gems.
+ # Must be in the list returned by OpenSSL::Cipher.ciphers
+
+ KEY_CIPHER = OpenSSL::Cipher.new('aes256') if defined?(OpenSSL::Cipher)
+
+ ##
# One year in seconds
ONE_YEAR = 86400 * 365
@@ -563,13 +564,18 @@ 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|
- io.write pemmable.to_pem
+ if passphrase and cipher
+ io.write pemmable.to_pem cipher, passphrase
+ else
+ io.write pemmable.to_pem
+ end
end
path
@@ -579,8 +585,11 @@ module Gem::Security
end
-require 'rubygems/security/policy'
-require 'rubygems/security/policies'
+if defined?(OpenSSL::SSL) then
+ require 'rubygems/security/policy'
+ require 'rubygems/security/policies'
+ require 'rubygems/security/trust_dir'
+end
+
require 'rubygems/security/signer'
-require 'rubygems/security/trust_dir'
diff --git a/lib/rubygems/security/policy.rb b/lib/rubygems/security/policy.rb
index 467ee932b5..98e41b812c 100644
--- a/lib/rubygems/security/policy.rb
+++ b/lib/rubygems/security/policy.rb
@@ -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
- raise Gem::Security::Exception,
- "unsigned gems are not allowed by the #{name} policy"
+ 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
diff --git a/lib/rubygems/security/signer.rb b/lib/rubygems/security/signer.rb
index 78455c0732..231f2fe604 100644
--- a/lib/rubygems/security/signer.rb
+++ b/lib/rubygems/security/signer.rb
@@ -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
diff --git a/lib/rubygems/source.rb b/lib/rubygems/source.rb
index 96d57870e2..8322ac33d4 100644
--- a/lib/rubygems/source.rb
+++ b/lib/rubygems/source.rb
@@ -25,14 +25,21 @@ class Gem::Source
end
def <=>(other)
- if !@uri
- return 0 unless other.uri
- return -1
- end
+ case other
+ when Gem::Source::Installed, Gem::Source::Local then
+ -1
+ when Gem::Source then
+ if !@uri
+ return 0 unless other.uri
+ return -1
+ end
- return 1 if !other.uri
+ return 1 if !other.uri
- @uri.to_s <=> other.uri.to_s
+ @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'
diff --git a/lib/rubygems/source/installed.rb b/lib/rubygems/source/installed.rb
new file mode 100644
index 0000000000..7709778791
--- /dev/null
+++ b/lib/rubygems/source/installed.rb
@@ -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
+
diff --git a/lib/rubygems/source/local.rb b/lib/rubygems/source/local.rb
new file mode 100644
index 0000000000..7392ff0e8e
--- /dev/null
+++ b/lib/rubygems/source/local.rb
@@ -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
diff --git a/lib/rubygems/source/specific_file.rb b/lib/rubygems/source/specific_file.rb
new file mode 100644
index 0000000000..d296e617cc
--- /dev/null
+++ b/lib/rubygems/source/specific_file.rb
@@ -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
diff --git a/lib/rubygems/source_local.rb b/lib/rubygems/source_local.rb
index 44b170c4a4..0808f4694a 100644
--- a/lib/rubygems/source_local.rb
+++ b/lib/rubygems/source_local.rb
@@ -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
diff --git a/lib/rubygems/source_specific_file.rb b/lib/rubygems/source_specific_file.rb
index d296e617cc..f785c2667c 100644
--- a/lib/rubygems/source_specific_file.rb
+++ b/lib/rubygems/source_specific_file.rb
@@ -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
diff --git a/lib/rubygems/spec_fetcher.rb b/lib/rubygems/spec_fetcher.rb
index 3d484d1c13..967ab4492a 100644
--- a/lib/rubygems/spec_fetcher.rb
+++ b/lib/rubygems/spec_fetcher.rb
@@ -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 = {}
diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb
index 08614cd9eb..596f6ff69f 100644
--- a/lib/rubygems/specification.rb
+++ b/lib/rubygems/specification.rb
@@ -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|
@@ -584,11 +602,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.
attr_writer :original_platform # :nodoc:
@@ -615,58 +628,68 @@ class Gem::Specification
attr_accessor :specification_version
- class << self
- def default_specifications_dir
- File.join(Gem.default_dir, "specifications", "default")
+ def self._all # :nodoc:
+ unless defined?(@@all) && @@all then
+ @@all = stubs.map(&:to_spec)
+
+ # After a reset, make sure already loaded specs
+ # are still marked as activated.
+ specs = {}
+ Gem.loaded_specs.each_value{|s| specs[s] = true}
+ @@all.each{|s| s.activated = true if specs[s]}
+
+ _resort!(@@all)
end
+ @@all
+ 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
- }
- }
+ 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
- def each_default(&block) # :nodoc:
- each_spec([default_specifications_dir],
- &block)
+ # :nodoc:
+ def self.each_stub(dirs)
+ each_gemspec(dirs) do |path|
+ stub = Gem::StubSpecification.new(path)
+ yield stub if stub.valid?
end
+ end
- def each_normal(&block) # :nodoc:
- each_spec(dirs, &block)
+ # :nodoc:
+ def self.each_spec(dirs)
+ each_gemspec(dirs) do |path|
+ spec = self.load path
+ yield spec if spec
end
end
- def self._all # :nodoc:
- unless defined?(@@all) && @@all then
+ ##
+ # Returns a Gem::StubSpecification for every installed gem
- specs = {}
- each_default do |spec|
- specs[spec.full_name] ||= spec
+ def self.stubs
+ @@stubs ||= begin
+ stubs = {}
+ each_stub([default_specifications_dir] + dirs) do |stub|
+ stubs[stub.full_name] ||= stub
end
- each_normal do |spec|
- specs[spec.full_name] ||= spec
- end
-
- @@all = specs.values
-
- # After a reset, make sure already loaded specs
- # are still marked as activated.
- specs = {}
- Gem.loaded_specs.each_value{|s| specs[s] = true}
- @@all.each{|s| s.activated = true if specs[s]}
- _resort!
+ stubs = stubs.values
+ _resort!(stubs)
+ stubs
end
- @@all
end
- def self._resort! # :nodoc:
- @@all.sort! { |a, b|
+ 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"
@@ -1281,20 +1316,6 @@ class Gem::Specification
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.
#
# NOTE: do not confuse this with +bindir+, which is just 'bin', not
@@ -1368,19 +1389,6 @@ class Gem::Specification
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.
def date
@@ -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
##
@@ -1663,15 +1650,6 @@ class Gem::Specification
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.
#
# Formerly used to indicate this gem was RDoc-capable.
@@ -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,28 +1800,31 @@ 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.
def mark_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.
@@ -2094,6 +2074,13 @@ class Gem::Specification
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:
diff --git a/lib/rubygems/stub_specification.rb b/lib/rubygems/stub_specification.rb
new file mode 100644
index 0000000000..0a6e70bb15
--- /dev/null
+++ b/lib/rubygems/stub_specification.rb
@@ -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
diff --git a/lib/rubygems/test_case.rb b/lib/rubygems/test_case.rb
index e92e5d868a..df5908ea2f 100644
--- a/lib/rubygems/test_case.rb
+++ b/lib/rubygems/test_case.rb
@@ -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.
@@ -1006,6 +1050,24 @@ Also, a list:
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.
def req *requirements
@@ -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,17 +1156,24 @@ 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'
- PUBLIC_KEY = PRIVATE_KEY.public_key
+ 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_CERT = load_cert 'public'
- PUBLIC_CERT_PATH = cert_path 'public'
+ PUBLIC_KEY = PRIVATE_KEY.public_key
+
+ PUBLIC_CERT = load_cert 'public'
+ PUBLIC_CERT_PATH = cert_path 'public'
rescue Errno::ENOENT
PRIVATE_KEY = nil
PUBLIC_KEY = nil
PUBLIC_CERT = nil
- end
+ end if defined?(OpenSSL::SSL)
end
diff --git a/lib/rubygems/uninstaller.rb b/lib/rubygems/uninstaller.rb
index d672b9dec1..143ab6df26 100644
--- a/lib/rubygems/uninstaller.rb
+++ b/lib/rubygems/uninstaller.rb
@@ -43,14 +43,15 @@ class Gem::Uninstaller
def initialize(gem, options = {})
# TODO document the valid options
- @gem = gem
- @version = options[:version] || Gem::Requirement.default
- @gem_home = File.expand_path(options[:install_dir] || Gem.dir)
- @force_executables = options[:executables]
- @force_all = options[:all]
- @force_ignore = options[:ignore]
- @bin_dir = options[:bin_dir]
- @format_executable = options[:format_executable]
+ @gem = gem
+ @version = options[:version] || Gem::Requirement.default
+ @gem_home = File.expand_path(options[:install_dir] || Gem.dir)
+ @force_executables = options[:executables]
+ @force_all = options[:all]
+ @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:'
diff --git a/lib/rubygems/uri_formatter.rb b/lib/rubygems/uri_formatter.rb
new file mode 100644
index 0000000000..deac3bfe75
--- /dev/null
+++ b/lib/rubygems/uri_formatter.rb
@@ -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
+
diff --git a/lib/rubygems/util/list.rb b/lib/rubygems/util/list.rb
new file mode 100644
index 0000000000..9f540adcc1
--- /dev/null
+++ b/lib/rubygems/util/list.rb
@@ -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
diff --git a/lib/rubygems/version.rb b/lib/rubygems/version.rb
index e983751c17..fa9bbc5a9d 100644
--- a/lib/rubygems/version.rb
+++ b/lib/rubygems/version.rb
@@ -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
##
diff --git a/lib/rubygems/version_option.rb b/lib/rubygems/version_option.rb
index a3de4dc9e7..a0755d5020 100644
--- a/lib/rubygems/version_option.rb
+++ b/lib/rubygems/version_option.rb
@@ -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
diff --git a/test/rubygems/ca_cert.pem b/test/rubygems/ca_cert.pem
index 5acdcf8f32..5207531bc2 100644
--- a/test/rubygems/ca_cert.pem
+++ b/test/rubygems/ca_cert.pem
@@ -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-----
diff --git a/test/rubygems/client.pem b/test/rubygems/client.pem
new file mode 100644
index 0000000000..63a52c574a
--- /dev/null
+++ b/test/rubygems/client.pem
@@ -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-----
diff --git a/test/rubygems/encrypted_private_key.pem b/test/rubygems/encrypted_private_key.pem
new file mode 100644
index 0000000000..6acfda6ce4
--- /dev/null
+++ b/test/rubygems/encrypted_private_key.pem
@@ -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-----
diff --git a/test/rubygems/invalid_client.pem b/test/rubygems/invalid_client.pem
new file mode 100644
index 0000000000..e0d39519d9
--- /dev/null
+++ b/test/rubygems/invalid_client.pem
@@ -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-----
diff --git a/test/rubygems/specifications/bar-0.0.2.gemspec b/test/rubygems/specifications/bar-0.0.2.gemspec
new file mode 100644
index 0000000000..ceefa4ed16
--- /dev/null
+++ b/test/rubygems/specifications/bar-0.0.2.gemspec
@@ -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
diff --git a/test/rubygems/specifications/foo-0.0.1.gemspec b/test/rubygems/specifications/foo-0.0.1.gemspec
new file mode 100644
index 0000000000..7fbc56429f
--- /dev/null
+++ b/test/rubygems/specifications/foo-0.0.1.gemspec
Binary files differ
diff --git a/test/rubygems/test_gem.rb b/test/rubygems/test_gem.rb
index fda83767f2..a4e5313c5d 100644
--- a/test/rubygems/test_gem.rb
+++ b/test/rubygems/test_gem.rb
@@ -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'
@@ -1557,6 +1112,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}",
@@ -1634,4 +1217,3 @@ class TestGem < Gem::TestCase
File.join Gem.dir, "cache"
end
end
-
diff --git a/test/rubygems/test_gem_commands_cert_command.rb b/test/rubygems/test_gem_commands_cert_command.rb
index 75db6c4e15..a158a4442b 100644
--- a/test/rubygems/test_gem_commands_cert_command.rb
+++ b/test/rubygems/test_gem_commands_cert_command.rb
@@ -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!",
- output.shift
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
- private_key_file = File.join @tempdir, 'gem-private_key.pem'
- assert_path_exists private_key_file
+ assert_empty output
+ assert_empty @ui.error
- assert_equal PRIVATE_KEY.to_pem, File.read(private_key_file)
+ assert_path_exists File.join(@tempdir, 'gem-public_cert.pem')
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)
diff --git a/test/rubygems/test_gem_commands_cleanup_command.rb b/test/rubygems/test_gem_commands_cleanup_command.rb
index 26b1fe7b0c..6fd06d26e0 100644
--- a/test/rubygems/test_gem_commands_cleanup_command.rb
+++ b/test/rubygems/test_gem_commands_cleanup_command.rb
@@ -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]
diff --git a/test/rubygems/test_gem_commands_contents_command.rb b/test/rubygems/test_gem_commands_contents_command.rb
index 60df53f53b..35c9631959 100644
--- a/test/rubygems/test_gem_commands_contents_command.rb
+++ b/test/rubygems/test_gem_commands_contents_command.rb
@@ -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
diff --git a/test/rubygems/test_gem_commands_environment_command.rb b/test/rubygems/test_gem_commands_environment_command.rb
index 439057df9d..a97ed4360f 100644
--- a/test/rubygems/test_gem_commands_environment_command.rb
+++ b/test/rubygems/test_gem_commands_environment_command.rb
@@ -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
diff --git a/test/rubygems/test_gem_commands_help_command.rb b/test/rubygems/test_gem_commands_help_command.rb
index a10e575a5a..c3e143f619 100644
--- a/test/rubygems/test_gem_commands_help_command.rb
+++ b/test/rubygems/test_gem_commands_help_command.rb
@@ -36,9 +36,12 @@ class TestGemCommandsHelpCommand < Gem::TestCase
mgr.command_names.each do |cmd|
assert_match(/\s+#{cmd}\s+\S+/, out)
end
- assert_equal '', err
- refute_match 'No command found for ', out
+ if defined?(OpenSSL::SSL) then
+ assert_empty err
+
+ refute_match 'No command found for ', out
+ end
end
end
diff --git a/test/rubygems/test_gem_commands_install_command.rb b/test/rubygems/test_gem_commands_install_command.rb
index 28b4e474ba..8bfb633070 100644
--- a/test/rubygems/test_gem_commands_install_command.rb
+++ b/test/rubygems/test_gem_commands_install_command.rb
@@ -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
-
diff --git a/test/rubygems/test_gem_commands_owner_command.rb b/test/rubygems/test_gem_commands_owner_command.rb
index dfbc2572dc..5d7b66137e 100644
--- a/test/rubygems/test_gem_commands_owner_command.rb
+++ b/test/rubygems/test_gem_commands_owner_command.rb
@@ -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']
diff --git a/test/rubygems/test_gem_commands_pristine_command.rb b/test/rubygems/test_gem_commands_pristine_command.rb
index 778ce2ee1f..425f0eae1e 100644
--- a/test/rubygems/test_gem_commands_pristine_command.rb
+++ b/test/rubygems/test_gem_commands_pristine_command.rb
@@ -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
diff --git a/test/rubygems/test_gem_commands_sources_command.rb b/test/rubygems/test_gem_commands_sources_command.rb
index 8dc2f91ed4..1e5afca009 100644
--- a/test/rubygems/test_gem_commands_sources_command.rb
+++ b/test/rubygems/test_gem_commands_sources_command.rb
@@ -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
diff --git a/test/rubygems/test_gem_commands_update_command.rb b/test/rubygems/test_gem_commands_update_command.rb
index 006d821210..d1c1b20c6e 100644
--- a/test/rubygems/test_gem_commands_update_command.rb
+++ b/test/rubygems/test_gem_commands_update_command.rb
@@ -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
diff --git a/test/rubygems/test_gem_config_file.rb b/test/rubygems/test_gem_config_file.rb
index 13f1c34b50..e9cd33579d 100644
--- a/test/rubygems/test_gem_config_file.rb
+++ b/test/rubygems/test_gem_config_file.rb
@@ -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
diff --git a/test/rubygems/test_gem_dependency_installer.rb b/test/rubygems/test_gem_dependency_installer.rb
index 2b95597d4b..af6b922fe3 100644
--- a/test/rubygems/test_gem_dependency_installer.rb
+++ b/test/rubygems/test_gem_dependency_installer.rb
@@ -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'
@@ -172,7 +173,8 @@ class TestGemDependencyInstaller < Gem::TestCase
FileUtils.mv @a1_gem, @tempdir
FileUtils.mv @b1_gem, @tempdir
- FileUtils.mv e1_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
- 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
+ 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 = Gem::DependencyInstaller.new
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
@@ -246,15 +223,18 @@ class TestGemDependencyInstaller < Gem::TestCase
Gem::Specification.reset
FileUtils.mv @a1_gem, @tempdir
- FileUtils.mv a2_gem, @tempdir # not in index
+ FileUtils.mv a2_gem, @tempdir # not in index
FileUtils.mv @b1_gem, @tempdir
inst = nil
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
@@ -282,19 +262,18 @@ class TestGemDependencyInstaller < Gem::TestCase
Gem::Specification.reset
FileUtils.mv @a1_gem, @tempdir
- FileUtils.mv a2_gem, @tempdir # not in index
+ FileUtils.mv a2_gem, @tempdir # not in index
FileUtils.mv @b1_gem, @tempdir
- FileUtils.mv a3_gem, @tempdir
-
- inst = nil
+ FileUtils.mv a3_gem, @tempdir
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'
diff --git a/test/rubygems/test_gem_dependency_resolver.rb b/test/rubygems/test_gem_dependency_resolver.rb
index 08d8e62f2b..3f65c23aa9 100644
--- a/test/rubygems/test_gem_dependency_resolver.rb
+++ b/test/rubygems/test_gem_dependency_resolver.rb
@@ -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
diff --git a/test/rubygems/test_gem_dependency_resolver_dependency_conflict.rb b/test/rubygems/test_gem_dependency_resolver_dependency_conflict.rb
new file mode 100644
index 0000000000..56a34d3001
--- /dev/null
+++ b/test/rubygems/test_gem_dependency_resolver_dependency_conflict.rb
@@ -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
+
diff --git a/test/rubygems/test_gem_gem_runner.rb b/test/rubygems/test_gem_gem_runner.rb
index 6680a784c9..85ff725345 100644
--- a/test/rubygems/test_gem_gem_runner.rb
+++ b/test/rubygems/test_gem_gem_runner.rb
@@ -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
-
- cls = Class.new(Gem::Command) do
- def execute
- end
- end
+ def test_extract_build_args
+ args = %w[]
+ assert_equal [], @runner.extract_build_args(args)
+ assert_equal %w[], 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])
+ args = %w[--foo --]
+ assert_equal [], @runner.extract_build_args(args)
+ assert_equal %w[--foo], args
- 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
diff --git a/test/rubygems/test_gem_impossible_dependencies_error.rb b/test/rubygems/test_gem_impossible_dependencies_error.rb
new file mode 100644
index 0000000000..9a0472fa36
--- /dev/null
+++ b/test/rubygems/test_gem_impossible_dependencies_error.rb
@@ -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
+
diff --git a/test/rubygems/test_gem_install_update_options.rb b/test/rubygems/test_gem_install_update_options.rb
index 18d3569417..3f63896999 100644
--- a/test/rubygems/test_gem_install_update_options.rb
+++ b/test/rubygems/test_gem_install_update_options.rb
@@ -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]
diff --git a/test/rubygems/test_gem_installer.rb b/test/rubygems/test_gem_installer.rb
index e513fe6da3..fbc7cd05d4 100644
--- a/test/rubygems/test_gem_installer.rb
+++ b/test/rubygems/test_gem_installer.rb
@@ -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'
diff --git a/test/rubygems/test_gem_name_tuple.rb b/test/rubygems/test_gem_name_tuple.rb
index 62b4801f71..170a9c2ae0 100644
--- a/test/rubygems/test_gem_name_tuple.rb
+++ b/test/rubygems/test_gem_name_tuple.rb
@@ -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
+
diff --git a/test/rubygems/test_gem_package.rb b/test/rubygems/test_gem_package.rb
index bb78829bf9..55bb32af04 100644
--- a/test/rubygems/test_gem_package.rb
+++ b/test/rubygems/test_gem_package.rb
@@ -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,12 +165,57 @@ 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
spec = Gem::Specification.new 'build', '1'
@@ -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
diff --git a/test/rubygems/test_gem_package_old.rb b/test/rubygems/test_gem_package_old.rb
index 05f7ae3dec..6236dbbaf2 100644
--- a/test/rubygems/test_gem_package_old.rb
+++ b/test/rubygems/test_gem_package_old.rb
@@ -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
diff --git a/test/rubygems/test_gem_package_tar_reader.rb b/test/rubygems/test_gem_package_tar_reader.rb
index d1af22d3be..5e0474c253 100644
--- a/test/rubygems/test_gem_package_tar_reader.rb
+++ b/test/rubygems/test_gem_package_tar_reader.rb
@@ -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
diff --git a/test/rubygems/test_gem_package_tar_reader_entry.rb b/test/rubygems/test_gem_package_tar_reader_entry.rb
index 92da220fa6..3c1bf7291a 100644
--- a/test/rubygems/test_gem_package_tar_reader_entry.rb
+++ b/test/rubygems/test_gem_package_tar_reader_entry.rb
@@ -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))
diff --git a/test/rubygems/test_gem_package_tar_writer.rb b/test/rubygems/test_gem_package_tar_writer.rb
index 40c6982929..2505d7ced1 100644
--- a/test/rubygems/test_gem_package_tar_writer.rb
+++ b/test/rubygems/test_gem_package_tar_writer.rb
@@ -1,5 +1,6 @@
require 'rubygems/package/tar_test_case'
require 'rubygems/package/tar_writer'
+require 'minitest/mock'
class TestGemPackageTarWriter < Gem::Package::TarTestCase
@@ -18,112 +19,130 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
end
def test_add_file
- @tar_writer.add_file 'x', 0644 do |f| f.write 'a' * 10 end
+ Time.stub :now, Time.at(1458518157) do
+ @tar_writer.add_file 'x', 0644 do |f| f.write 'a' * 10 end
- assert_headers_equal(tar_file_header('x', '', 0644, 10),
+ assert_headers_equal(tar_file_header('x', '', 0644, 10, Time.now),
@io.string[0, 512])
+ 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
- digests = @tar_writer.add_file_digest 'x', 0644, digest_algorithms do |io|
- io.write 'a' * 10
- end
+ Time.stub :now, Time.at(1458518157) do
+ digests = @tar_writer.add_file_digest 'x', 0644, digest_algorithms do |io|
+ io.write 'a' * 10
+ end
- assert_equal '3495ff69d34671d1e15b33a63c1379fdedd3a32a',
- digests['SHA1'].hexdigest
- assert_equal '4714870aff6c97ca09d135834fdb58a6389a50c1' \
- '1fef8ec4afef466fb60a23ac6b7a9c92658f14df' \
- '4993d6b40a4e4d8424196afc347e97640d68de61' \
- 'e1cf14b0',
- digests['SHA512'].hexdigest
+ assert_equal '3495ff69d34671d1e15b33a63c1379fdedd3a32a',
+ digests['SHA1'].hexdigest
+ assert_equal '4714870aff6c97ca09d135834fdb58a6389a50c1' \
+ '1fef8ec4afef466fb60a23ac6b7a9c92658f14df' \
+ '4993d6b40a4e4d8424196afc347e97640d68de61' \
+ '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]
- digests = @tar_writer.add_file_digest 'x', 0644, digest_algorithms do |io|
- io.write 'a' * 10
- end
+ Time.stub :now, Time.at(1458518157) do
+ digests = @tar_writer.add_file_digest 'x', 0644, digest_algorithms do |io|
+ io.write 'a' * 10
+ end
- assert_equal '3495ff69d34671d1e15b33a63c1379fdedd3a32a',
- digests['SHA1'].hexdigest
- assert_equal '4714870aff6c97ca09d135834fdb58a6389a50c1' \
- '1fef8ec4afef466fb60a23ac6b7a9c92658f14df' \
- '4993d6b40a4e4d8424196afc347e97640d68de61' \
- 'e1cf14b0',
- digests['SHA512'].hexdigest
+ assert_equal '3495ff69d34671d1e15b33a63c1379fdedd3a32a',
+ digests['SHA1'].hexdigest
+ assert_equal '4714870aff6c97ca09d135834fdb58a6389a50c1' \
+ '1fef8ec4afef466fb60a23ac6b7a9c92658f14df' \
+ '4993d6b40a4e4d8424196afc347e97640d68de61' \
+ 'e1cf14b0',
+ digests['SHA512'].hexdigest
- assert_headers_equal(tar_file_header('x', '', 0644, 10),
- @io.string[0, 512])
+ assert_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]
- @tar_writer.add_file_signed 'x', 0644, signer do |io|
- io.write 'a' * 10
- end
+ 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, Time.now),
+ @io.string[0, 512])
- assert_headers_equal(tar_file_header('x', '', 0644, 10),
- @io.string[0, 512])
- assert_equal "aaaaaaaaaa#{"\0" * 502}", @io.string[512, 512]
- digest = signer.digest_algorithm.new
- digest.update 'a' * 10
+ assert_equal "aaaaaaaaaa#{"\0" * 502}", @io.string[512, 512]
- signature = signer.sign digest.digest
+ digest = signer.digest_algorithm.new
+ digest.update 'a' * 10
- assert_headers_equal(tar_file_header('x.sig', '', 0444, signature.length),
- @io.string[1024, 512])
- assert_equal "#{signature}#{"\0" * (512 - signature.length)}",
- @io.string[1536, 512]
+ signature = signer.sign digest.digest
+
+ assert_headers_equal(tar_file_header('x.sig', '', 0444, signature.length,
+ Time.now),
+ @io.string[1024, 512])
+ assert_equal "#{signature}#{"\0" * (512 - signature.length)}",
+ @io.string[1536, 512]
+
+ assert_equal 2048, @io.pos
+ end
- assert_equal 2048, @io.pos
end
def test_add_file_signer_empty
signer = Gem::Security::Signer.new nil, nil
- @tar_writer.add_file_signed 'x', 0644, signer do |io|
- io.write 'a' * 10
- end
+ Time.stub :now, Time.at(1458518157) do
- assert_headers_equal(tar_file_header('x', '', 0644, 10),
+ @tar_writer.add_file_signed 'x', 0644, signer do |io|
+ io.write 'a' * 10
+ end
+
+ assert_headers_equal(tar_file_header('x', '', 0644, 10, Time.now),
@io.string[0, 512])
+ 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
- @tar_writer.add_file_simple 'x', 0644, 10 do |io| io.write "a" * 10 end
+ Time.stub :now, Time.at(1458518157) do
+ @tar_writer.add_file_simple 'x', 0644, 10 do |io| io.write "a" * 10 end
- assert_headers_equal(tar_file_header('x', '', 0644, 10),
+ assert_headers_equal(tar_file_header('x', '', 0644, 10, Time.now),
@io.string[0, 512])
+ end
assert_equal "aaaaaaaaaa#{"\0" * 502}", @io.string[512, 512]
assert_equal 1024, @io.pos
end
def test_add_file_simple_padding
- @tar_writer.add_file_simple 'x', 0, 100
+ Time.stub :now, Time.at(1458518157) do
+ @tar_writer.add_file_simple 'x', 0, 100
- assert_headers_equal tar_file_header('x', '', 0, 100),
+ assert_headers_equal tar_file_header('x', '', 0, 100, Time.now),
@io.string[0, 512]
+ end
assert_equal "\0" * 512, @io.string[512, 512]
end
@@ -182,11 +201,14 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
end
def test_mkdir
- @tar_writer.mkdir 'foo', 0644
+ Time.stub :now, Time.at(1458518157) do
+ @tar_writer.mkdir 'foo', 0644
- assert_headers_equal tar_dir_header('foo', '', 0644),
- @io.string[0, 512]
- assert_equal 512, @io.pos
+ 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
diff --git a/test/rubygems/test_gem_path_support.rb b/test/rubygems/test_gem_path_support.rb
index bffc66f7dc..879cc98b5f 100644
--- a/test/rubygems/test_gem_path_support.rb
+++ b/test/rubygems/test_gem_path_support.rb
@@ -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
diff --git a/test/rubygems/test_gem_platform.rb b/test/rubygems/test_gem_platform.rb
index 1112a013f5..5966710dad 100644
--- a/test/rubygems/test_gem_platform.rb
+++ b/test/rubygems/test_gem_platform.rb
@@ -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'
diff --git a/test/rubygems/test_gem_remote_fetcher.rb b/test/rubygems/test_gem_remote_fetcher.rb
index d3cc388db4..7ffb638436 100644
--- a/test/rubygems/test_gem_remote_fetcher.rb
+++ b/test/rubygems/test_gem_remote_fetcher.rb
@@ -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
diff --git a/test/rubygems/test_gem_request.rb b/test/rubygems/test_gem_request.rb
new file mode 100644
index 0000000000..869c6730a9
--- /dev/null
+++ b/test/rubygems/test_gem_request.rb
@@ -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
+
diff --git a/test/rubygems/test_gem_security.rb b/test/rubygems/test_gem_security.rb
index 737881d7bc..2e6a56fc8f 100644
--- a/test/rubygems/test_gem_security.rb
+++ b/test/rubygems/test_gem_security.rb
@@ -2,6 +2,10 @@ require 'rubygems/test_case'
require 'rubygems/security'
require 'rubygems/fix_openssl_warnings' if RUBY_VERSION < "1.9"
+unless defined?(OpenSSL::SSL) then
+ warn 'Skipping Gem::Security tests. openssl not found.'
+end
+
class TestGemSecurity < Gem::TestCase
CHILD_KEY = load_key 'child'
@@ -246,5 +250,57 @@ class TestGemSecurity < Gem::TestCase
assert_equal expected, trust_dir.dir
end
-end
+ def test_class_write
+ key = @SEC.create_key 256
+
+ path = File.join @tempdir, 'test-private_key.pem'
+
+ @SEC.write key, path
+
+ assert_path_exists path
+
+ key_from_file = File.read path
+
+ assert_equal key.to_pem, key_from_file
+ end
+
+ def test_class_write_encrypted
+ key = @SEC.create_key 256
+
+ path = File.join @tempdir, 'test-private_encrypted_key.pem'
+
+ passphrase = 'It should be long.'
+
+ @SEC.write key, path, 0600, passphrase
+
+ assert_path_exists path
+
+ key_from_file = OpenSSL::PKey::RSA.new File.read(path), passphrase
+
+ assert_equal key.to_pem, key_from_file.to_pem
+ end
+
+ def test_class_write_encrypted_cipher
+ key = @SEC.create_key 256
+
+ path = File.join @tempdir, 'test-private_encrypted__with_non_default_cipher_key.pem'
+
+ passphrase = 'It should be long.'
+
+ cipher = OpenSSL::Cipher.new('aes192')
+
+ @SEC.write key, path, 0600, passphrase, cipher
+
+ assert_path_exists path
+
+ key_file_contents = File.read(path)
+
+ assert key_file_contents.split("\n")[2].match(cipher.name)
+
+ key_from_file = OpenSSL::PKey::RSA.new key_file_contents, passphrase
+
+ assert_equal key.to_pem, key_from_file.to_pem
+ end
+
+end if defined?(OpenSSL::SSL)
diff --git a/test/rubygems/test_gem_security_policy.rb b/test/rubygems/test_gem_security_policy.rb
index 1ce93fbd95..5b960c4d48 100644
--- a/test/rubygems/test_gem_security_policy.rb
+++ b/test/rubygems/test_gem_security_policy.rb
@@ -2,6 +2,10 @@
require 'rubygems/test_case'
+unless defined?(OpenSSL::SSL) then
+ warn 'Skipping Gem::Security::Policy tests. openssl not found.'
+end
+
class TestGemSecurityPolicy < Gem::TestCase
ALTERNATE_KEY = load_key 'alternate'
@@ -11,6 +15,7 @@ class TestGemSecurityPolicy < Gem::TestCase
INVALIDCHILD_KEY = load_key 'invalidchild'
ALTERNATE_CERT = load_cert 'alternate'
+ CA_CERT = load_cert 'ca'
CHILD_CERT = load_cert 'child'
EXPIRED_CERT = load_cert 'expired'
FUTURE_CERT = load_cert 'future'
@@ -285,6 +290,11 @@ class TestGemSecurityPolicy < Gem::TestCase
"(root of signing cert #{CHILD_CERT.subject})", e.message
end
+ def test_subject
+ assert_equal 'email:nobody@example', @no.subject(PUBLIC_CERT)
+ assert_equal '/C=JP/O=JIN.GR.JP/OU=RRR/CN=CA', @no.subject(CA_CERT)
+ end
+
def test_verify
Gem::Security.trust_dir.trust_cert PUBLIC_CERT
@@ -325,6 +335,22 @@ class TestGemSecurityPolicy < Gem::TestCase
assert_equal 'missing digest for 0', e.message
end
+ def test_verify_no_signatures
+ Gem::Security.trust_dir.trust_cert PUBLIC_CERT
+
+ digests, = dummy_signatures
+
+ use_ui @ui do
+ @no.verify [PUBLIC_CERT], nil, digests, {}, 'some_gem'
+ end
+
+ assert_match "WARNING: some_gem is not signed\n", @ui.error
+
+ assert_raises Gem::Security::Exception do
+ @almost_no.verify [PUBLIC_CERT], nil, digests, {}
+ end
+ end
+
def test_verify_not_enough_signatures
Gem::Security.trust_dir.trust_cert PUBLIC_CERT
@@ -341,6 +367,21 @@ class TestGemSecurityPolicy < Gem::TestCase
assert_equal 'missing digest for 1', e.message
end
+ def test_verify_no_trust
+ digests, signatures = dummy_signatures
+
+ use_ui @ui do
+ @low.verify [PUBLIC_CERT], nil, digests, signatures, 'some_gem'
+ end
+
+ assert_equal "WARNING: email:nobody@example is not trusted for some_gem\n",
+ @ui.error
+
+ assert_raises Gem::Security::Exception do
+ @medium.verify [PUBLIC_CERT], nil, digests, signatures
+ end
+ end
+
def test_verify_wrong_digest_type
Gem::Security.trust_dir.trust_cert PUBLIC_CERT
@@ -484,5 +525,5 @@ class TestGemSecurityPolicy < Gem::TestCase
return digests, signatures
end
-end
+end if defined?(OpenSSL::SSL)
diff --git a/test/rubygems/test_gem_security_signer.rb b/test/rubygems/test_gem_security_signer.rb
index 59c5089fec..c984382947 100644
--- a/test/rubygems/test_gem_security_signer.rb
+++ b/test/rubygems/test_gem_security_signer.rb
@@ -1,5 +1,9 @@
require 'rubygems/test_case'
+unless defined?(OpenSSL::SSL) then
+ warn 'Skipping Gem::Security::Signer tests. openssl not found.'
+end
+
class TestGemSecuritySigner < Gem::TestCase
ALTERNATE_KEY = load_key 'alternate'
@@ -72,6 +76,14 @@ class TestGemSecuritySigner < Gem::TestCase
assert_equal PRIVATE_KEY.to_s, signer.key.to_s
end
+ def test_initialize_encrypted_key_path
+ key_file = ENCRYPTED_PRIVATE_KEY_PATH
+
+ signer = Gem::Security::Signer.new key_file, nil, PRIVATE_KEY_PASSPHRASE
+
+ assert_equal ENCRYPTED_PRIVATE_KEY.to_s, signer.key.to_s
+ end
+
def test_load_cert_chain
Gem::Security.trust_dir.trust_cert PUBLIC_CERT
@@ -186,5 +198,5 @@ c7NM7KZZjj7G++SXjYTEI1PHSA7aFQ/i/+qSUvx+Pg==
end
end
-end
+end if defined?(OpenSSL::SSL)
diff --git a/test/rubygems/test_gem_security_trust_dir.rb b/test/rubygems/test_gem_security_trust_dir.rb
index 56c2feb44c..7b0d450bd6 100644
--- a/test/rubygems/test_gem_security_trust_dir.rb
+++ b/test/rubygems/test_gem_security_trust_dir.rb
@@ -1,5 +1,9 @@
require 'rubygems/test_case'
+unless defined?(OpenSSL::SSL) then
+ warn 'Skipping Gem::Security::TrustDir tests. openssl not found.'
+end
+
class TestGemSecurityTrustDir < Gem::TestCase
CHILD_CERT = load_cert 'child'
@@ -90,5 +94,5 @@ class TestGemSecurityTrustDir < Gem::TestCase
assert_equal mask, File.stat(@dest_dir).mode unless win_platform?
end
-end
+end if defined?(OpenSSL::SSL)
diff --git a/test/rubygems/test_gem_source.rb b/test/rubygems/test_gem_source.rb
index 2629f180a9..1303978a8e 100644
--- a/test/rubygems/test_gem_source.rb
+++ b/test/rubygems/test_gem_source.rb
@@ -63,7 +63,7 @@ class TestGemSource < Gem::TestCase
def test_cache_dir_escapes_windows_paths
uri = URI.parse("file:///C:/WINDOWS/Temp/gem_repo")
- root = File.join Gem.user_home, '.gem', 'specs'
+ root = Gem.spec_cache_dir
cache_dir = @source.cache_dir(uri).gsub(root, '')
assert cache_dir !~ /:/, "#{cache_dir} should not contain a :"
end
@@ -123,7 +123,7 @@ class TestGemSource < Gem::TestCase
expected = @released
assert_equal expected, @source.load_specs(:released)
- cache_dir = File.join Gem.user_home, '.gem', 'specs', 'gems.example.com%80'
+ cache_dir = File.join Gem.spec_cache_dir, 'gems.example.com%80'
assert File.exist?(cache_dir), "#{cache_dir} does not exist"
cache_file = File.join cache_dir, "specs.#{Gem.marshal_version}"
@@ -138,7 +138,7 @@ class TestGemSource < Gem::TestCase
@fetcher.data["#{@gem_repo}latest_specs.#{Gem.marshal_version}"] =
' ' * Marshal.dump(@latest_specs).length
- cache_dir = File.join Gem.user_home, '.gem', 'specs', 'gems.example.com%80'
+ cache_dir = File.join Gem.spec_cache_dir, 'gems.example.com%80'
FileUtils.mkdir_p cache_dir
@@ -160,7 +160,7 @@ class TestGemSource < Gem::TestCase
@fetcher.data["#{@gem_repo}latest_specs.#{Gem.marshal_version}.gz"] =
util_gzip(Marshal.dump(@latest_specs))
- cache_dir = File.join Gem.user_home, '.gem', 'specs', 'gems.example.com%80'
+ cache_dir = File.join Gem.spec_cache_dir, 'gems.example.com%80'
FileUtils.mkdir_p cache_dir
diff --git a/test/rubygems/test_gem_source_local.rb b/test/rubygems/test_gem_source_local.rb
index 54ce3d51be..8e901cfd21 100644
--- a/test/rubygems/test_gem_source_local.rb
+++ b/test/rubygems/test_gem_source_local.rb
@@ -1,5 +1,5 @@
require 'rubygems/test_case'
-require 'rubygems/source_local'
+require 'rubygems/source/local'
require 'fileutils'
@@ -76,8 +76,8 @@ class TestGemSourceLocal < Gem::TestCase
uri = URI.parse "http://gems.example/foo"
s = Gem::Source.new uri
- assert_equal(-1, (@sl <=> s))
- assert_equal 1, (s <=> @sl)
- assert_equal 0, (@sl <=> @sl)
+ assert_equal(-1, s <=> @sl)
+ assert_equal 0, @sl <=> @sl
+ assert_equal 1, @sl <=> s
end
end
diff --git a/test/rubygems/test_gem_source_specific_file.rb b/test/rubygems/test_gem_source_specific_file.rb
index 7ffcf482dc..1d8351781f 100644
--- a/test/rubygems/test_gem_source_specific_file.rb
+++ b/test/rubygems/test_gem_source_specific_file.rb
@@ -1,5 +1,5 @@
require 'rubygems/test_case'
-require 'rubygems/source_specific_file'
+require 'rubygems/source/specific_file'
class TestGemSourceSpecificFile < Gem::TestCase
def setup
diff --git a/test/rubygems/test_gem_specification.rb b/test/rubygems/test_gem_specification.rb
index 9b2ae82fe3..556115bdc6 100644
--- a/test/rubygems/test_gem_specification.rb
+++ b/test/rubygems/test_gem_specification.rb
@@ -38,10 +38,8 @@ Gem::Specification.new do |s|
end
EOF
- def setup
- super
-
- @a1 = quick_spec 'a', '1' do |s|
+ def make_spec_c1
+ @c1 = quick_spec 'a', '1' do |s|
s.executable = 'exec'
s.extensions << 'ext/a/extconf.rb'
s.test_file = 'test/suite.rb'
@@ -56,6 +54,22 @@ end
s.mark_version
s.files = %w[lib/code.rb]
end
+ end
+
+ def setup
+ super
+
+ @a1 = quick_spec 'a', '1' do |s|
+ s.executable = 'exec'
+ s.extensions << 'ext/a/extconf.rb'
+ s.test_file = 'test/suite.rb'
+ s.requirements << 'A working computer'
+ s.rubyforge_project = 'example'
+ s.license = 'MIT'
+
+ s.mark_version
+ s.files = %w[lib/code.rb]
+ end
@a2 = quick_spec 'a', '2' do |s|
s.files = %w[lib/code.rb]
@@ -66,6 +80,371 @@ end
load 'rubygems/syck_hack.rb'
end
+ def test_self_activate
+ foo = util_spec 'foo', '1'
+
+ assert_activate %w[foo-1], foo
+ end
+
+ 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
+
+ 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_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
+
+ 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
+
+ ##
+ # [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_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
+
+ ##
+ # 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
+
+ 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
+
+ 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_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 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
+
def test_self_attribute_names
expected_value = %w[
authors
@@ -915,7 +1294,7 @@ dependencies: []
end
def test_base_dir_not_loaded
- @a1.instance_variable_set :@loaded_from, nil
+ @a1.instance_variable_set :@filename, nil
assert_equal Gem.dir, @a1.base_dir
end
@@ -924,7 +1303,7 @@ dependencies: []
default_dir =
File.join Gem::Specification.default_specifications_dir, @a1.spec_name
- @a1.instance_variable_set :@loaded_from, default_dir
+ @a1.instance_variable_set :@filename, default_dir
assert_equal Gem.default_dir, @a1.base_dir
end
@@ -1022,19 +1401,60 @@ dependencies: []
assert_equal %w[lib], @a1.require_paths
end
+ 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
+
+ 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_requirements
assert_equal ['A working computer'], @a1.requirements
end
def test_runtime_dependencies_legacy
+ make_spec_c1
# legacy gems don't have a type
- @a1.runtime_dependencies.each do |dep|
+ @c1.runtime_dependencies.each do |dep|
dep.instance_variable_set :@type, nil
end
expected = %w[rake jabber4r pqa]
- assert_equal expected, @a1.runtime_dependencies.map { |d| d.name }
+ assert_equal expected, @c1.runtime_dependencies.map { |d| d.name }
end
def test_spaceship_name
@@ -1088,11 +1508,13 @@ dependencies: []
@a2.add_runtime_dependency 'b', '1'
@a2.dependencies.first.instance_variable_set :@type, nil
@a2.required_rubygems_version = Gem::Requirement.new '> 0'
+ @a2.require_paths << "lib/a/ext"
ruby_code = @a2.to_ruby
expected = <<-SPEC
# -*- encoding: utf-8 -*-
+# stub: a 2 ruby lib\0lib/a/ext
Gem::Specification.new do |s|
s.name = "a"
@@ -1105,7 +1527,7 @@ Gem::Specification.new do |s|
s.email = "example@example.com"
s.files = ["lib/code.rb"]
s.homepage = "http://example.com"
- s.require_paths = ["lib"]
+ s.require_paths = ["lib", "lib/a/ext"]
s.rubygems_version = "#{Gem::VERSION}"
s.summary = "this is a summary"
@@ -1140,6 +1562,7 @@ end
expected = <<-SPEC
# -*- encoding: utf-8 -*-
+# stub: a 2 ruby lib
Gem::Specification.new do |s|
s.name = "a"
@@ -1179,14 +1602,17 @@ end
end
def test_to_ruby_fancy
- @a1.platform = Gem::Platform.local
- ruby_code = @a1.to_ruby
+ make_spec_c1
+
+ @c1.platform = Gem::Platform.local
+ ruby_code = @c1.to_ruby
local = Gem::Platform.local
expected_platform = "[#{local.cpu.inspect}, #{local.os.inspect}, #{local.version.inspect}]"
expected = <<-SPEC
# -*- encoding: utf-8 -*-
+# stub: a 1 x86-darwin-8 lib
Gem::Specification.new do |s|
s.name = "a"
@@ -1234,7 +1660,7 @@ end
same_spec = eval ruby_code
- assert_equal @a1, same_spec
+ assert_equal @c1, same_spec
end
def test_to_ruby_legacy
@@ -1886,6 +2312,7 @@ end
def test_metadata_specs
valid_ruby_spec = <<-EOF
# -*- encoding: utf-8 -*-
+# stub: m 1 ruby lib
Gem::Specification.new do |s|
s.name = "m"
diff --git a/test/rubygems/test_gem_stub_specification.rb b/test/rubygems/test_gem_stub_specification.rb
new file mode 100644
index 0000000000..6feb96eb4f
--- /dev/null
+++ b/test/rubygems/test_gem_stub_specification.rb
@@ -0,0 +1,30 @@
+require "rubygems/test_case"
+require "rubygems/stub_specification"
+
+class TestStubSpecification < Gem::TestCase
+ SPECIFICATIONS = File.expand_path(File.join("..", "specifications"), __FILE__)
+ FOO = File.join SPECIFICATIONS, "foo-0.0.1.gemspec"
+ BAR = File.join SPECIFICATIONS, "bar-0.0.2.gemspec"
+
+ def test_basic
+ stub = Gem::StubSpecification.new(FOO)
+ assert_equal "foo", stub.name
+ assert_equal Gem::Version.new("0.0.1"), stub.version
+ assert_equal Gem::Platform.new("mswin32"), stub.platform
+ assert_equal ["lib", "lib/f oo/ext"], stub.require_paths
+ end
+
+ def test_missing_stubline
+ stub = Gem::StubSpecification.new(BAR)
+ assert_equal "bar", stub.name
+ assert_equal Gem::Version.new("0.0.2"), stub.version
+ assert_equal Gem::Platform.new("ruby"), stub.platform
+ assert_equal ["lib"], stub.require_paths
+ end
+
+ def test_to_spec
+ stub = Gem::StubSpecification.new(FOO)
+ assert stub.to_spec.is_a?(Gem::Specification)
+ assert_equal "foo", stub.to_spec.name
+ end
+end
diff --git a/test/rubygems/test_gem_uninstaller.rb b/test/rubygems/test_gem_uninstaller.rb
index 948318a5e3..1739614c67 100644
--- a/test/rubygems/test_gem_uninstaller.rb
+++ b/test/rubygems/test_gem_uninstaller.rb
@@ -5,6 +5,7 @@ class TestGemUninstaller < Gem::InstallerTestCase
def setup
super
+ common_installer_setup
build_rake_in do
use_ui ui do
@@ -375,6 +376,19 @@ class TestGemUninstaller < Gem::InstallerTestCase
assert_equal "Successfully uninstalled q-1.0", lines.shift
end
+ def test_uninstall_doesnt_prompt_and_raises_when_abort_on_dependent_set
+ quick_gem 'r', '1' do |s| s.add_dependency 'q', '= 1' end
+ quick_gem 'q', '1'
+
+ un = Gem::Uninstaller.new('q', :abort_on_dependent => true)
+ ui = Gem::MockGemUi.new("y\n")
+
+ assert_raises Gem::DependencyRemovalException do
+ use_ui ui do
+ un.uninstall
+ end
+ end
+ end
def test_uninstall_prompt_includes_dep_type
quick_gem 'r', '1' do |s|
diff --git a/test/rubygems/test_gem_uri_formatter.rb b/test/rubygems/test_gem_uri_formatter.rb
new file mode 100644
index 0000000000..b185797a3a
--- /dev/null
+++ b/test/rubygems/test_gem_uri_formatter.rb
@@ -0,0 +1,20 @@
+require 'rubygems/test_case'
+require 'rubygems/uri_formatter'
+
+class TestGemUriFormatter < Gem::TestCase
+
+ def test_normalize_uri
+ assert_equal 'FILE://example/',
+ Gem::UriFormatter.new('FILE://example/').normalize
+ assert_equal 'FTP://example/',
+ Gem::UriFormatter.new('FTP://example/').normalize
+ assert_equal 'HTTP://example/',
+ Gem::UriFormatter.new('HTTP://example/').normalize
+ assert_equal 'HTTPS://example/',
+ Gem::UriFormatter.new('HTTPS://example/').normalize
+ assert_equal 'http://example/',
+ Gem::UriFormatter.new('example/').normalize
+ end
+
+end
+
diff --git a/test/rubygems/test_gem_version.rb b/test/rubygems/test_gem_version.rb
index da3b87dbca..2ba196e48d 100644
--- a/test/rubygems/test_gem_version.rb
+++ b/test/rubygems/test_gem_version.rb
@@ -122,6 +122,15 @@ class TestGemVersion < Gem::TestCase
assert_equal "5.2.4", v("5.2.4").to_s
end
+ def test_semver
+ assert_less_than "1.0.0-alpha", "1.0.0-alpha.1"
+ assert_less_than "1.0.0-alpha.1", "1.0.0-beta.2"
+ assert_less_than "1.0.0-beta.2", "1.0.0-beta.11"
+ assert_less_than "1.0.0-beta.11", "1.0.0-rc.1"
+ assert_less_than "1.0.0-rc1", "1.0.0"
+ assert_less_than "1.0.0-1", "1"
+ end
+
# Asserts that +version+ is a prerelease.
def assert_prerelease version
@@ -161,6 +170,12 @@ class TestGemVersion < Gem::TestCase
assert second.eql?(first), "#{second} is eql? #{first}"
end
+ def assert_less_than left, right
+ l = v(left)
+ r = v(right)
+ assert l < r, "#{left} not less than #{right}"
+ end
+
# Refute the assumption that +version+ is a prerelease.
def refute_prerelease version
diff --git a/test/rubygems/test_gem_version_option.rb b/test/rubygems/test_gem_version_option.rb
index cbe819c22e..d6035ab800 100644
--- a/test/rubygems/test_gem_version_option.rb
+++ b/test/rubygems/test_gem_version_option.rb
@@ -80,7 +80,69 @@ class TestGemVersionOption < Gem::TestCase
@cmd.handle_options %w[--version >1]
- expected = { :version => Gem::Requirement.new('> 1'), :args => [] }
+ expected = {
+ :args => [],
+ :explicit_prerelease => false,
+ :prerelease => false,
+ :version => Gem::Requirement.new('> 1'),
+ }
+
+ assert_equal expected, @cmd.options
+ end
+
+ def test_version_option_compound
+ @cmd.add_version_option
+
+ @cmd.handle_options ['--version', '< 1, > 0.9']
+
+ expected = {
+ :args => [],
+ :explicit_prerelease => false,
+ :prerelease => false,
+ :version => Gem::Requirement.new('< 1', '> 0.9'),
+ }
+
+ assert_equal expected, @cmd.options
+ end
+
+ def test_version_option_explicit_prerelease
+ @cmd.add_prerelease_option
+ @cmd.add_version_option
+
+ @cmd.handle_options %w[--pre --version >1]
+
+ expected = {
+ :args => [],
+ :explicit_prerelease => true,
+ :prerelease => true,
+ :version => Gem::Requirement.new('> 1'),
+ }
+
+ assert_equal expected, @cmd.options
+ end
+
+ def test_version_option_twice
+ @cmd.add_version_option
+
+ @cmd.handle_options %w[--version >1.a]
+
+ expected = {
+ :args => [],
+ :explicit_prerelease => false,
+ :prerelease => true,
+ :version => Gem::Requirement.new('> 1.a'),
+ }
+
+ assert_equal expected, @cmd.options
+
+ @cmd.handle_options %w[--version >1]
+
+ expected = {
+ :args => [],
+ :explicit_prerelease => false,
+ :prerelease => false,
+ :version => Gem::Requirement.new('> 1'),
+ }
assert_equal expected, @cmd.options
end