зеркало из https://github.com/github/ruby.git
* lib/rubygems*: Updated to RubyGems 2.0
* test/rubygems*: ditto. * common.mk (prelude): Updated for RubyGems 2.0 source rearrangement. * tool/change_maker.rb: Allow invalid UTF-8 characters in source files. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37976 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
Родитель
3f606b7063
Коммит
9694bb8cac
10
ChangeLog
10
ChangeLog
|
@ -1,3 +1,13 @@
|
|||
Thu Nov 29 15:51:54 2012 Eric Hodel <drbrain@segment7.net>
|
||||
|
||||
* lib/rubygems*: Updated to RubyGems 2.0
|
||||
* test/rubygems*: ditto.
|
||||
|
||||
* common.mk (prelude): Updated for RubyGems 2.0 source rearrangement.
|
||||
|
||||
* tool/change_maker.rb: Allow invalid UTF-8 characters in source
|
||||
files.
|
||||
|
||||
Thu Nov 29 15:38:14 2012 Koichi Sasada <ko1@atdot.net>
|
||||
|
||||
* include/ruby/debug.h: provide rb_tracearg_*() APIs,
|
||||
|
|
|
@ -887,7 +887,8 @@ $(MINIPRELUDE_C): $(srcdir)/tool/compile_prelude.rb $(srcdir)/prelude.rb
|
|||
$(Q) $(BASERUBY) -I$(srcdir) $(srcdir)/tool/compile_prelude.rb $(srcdir)/prelude.rb $@
|
||||
|
||||
prelude.c: $(srcdir)/tool/compile_prelude.rb $(RBCONFIG) \
|
||||
$(srcdir)/lib/rubygems/defaults.rb $(srcdir)/lib/rubygems/custom_require.rb \
|
||||
$(srcdir)/lib/rubygems/defaults.rb \
|
||||
$(srcdir)/lib/rubygems/core_ext/kernel_gem.rb \
|
||||
$(PRELUDE_SCRIPTS) $(PREP)
|
||||
$(ECHO) generating $@
|
||||
$(Q) $(COMPILE_PRELUDE) $(PRELUDE_SCRIPTS) $@
|
||||
|
|
567
lib/rubygems.rb
567
lib/rubygems.rb
|
@ -5,32 +5,6 @@
|
|||
# See LICENSE.txt for permissions.
|
||||
#++
|
||||
|
||||
module Gem
|
||||
QUICKLOADER_SUCKAGE = RUBY_VERSION =~ /^1\.9\.1/
|
||||
|
||||
# Only MRI 1.9.2 has the custom prelude.
|
||||
GEM_PRELUDE_SUCKAGE = RUBY_VERSION =~ /^1\.9\.2/ && RUBY_ENGINE == "ruby"
|
||||
end
|
||||
|
||||
if Gem::GEM_PRELUDE_SUCKAGE and defined?(Gem::QuickLoader) then
|
||||
Gem::QuickLoader.remove
|
||||
|
||||
$LOADED_FEATURES.delete Gem::QuickLoader.path_to_full_rubygems_library
|
||||
|
||||
if $LOADED_FEATURES.any? do |path| path.end_with? '/rubygems.rb' end then
|
||||
# TODO path does not exist here
|
||||
raise LoadError, "another rubygems is already loaded from #{path}"
|
||||
end
|
||||
|
||||
class << Gem
|
||||
remove_method :try_activate if Gem.respond_to?(:try_activate, true)
|
||||
end
|
||||
end
|
||||
|
||||
require 'rubygems/defaults'
|
||||
require 'rbconfig'
|
||||
require "rubygems/deprecate"
|
||||
|
||||
##
|
||||
# RubyGems is the Ruby standard for publishing and managing third party
|
||||
# libraries.
|
||||
|
@ -72,8 +46,8 @@ require "rubygems/deprecate"
|
|||
# For RubyGems packagers, provide lib/rubygems/operating_system.rb and
|
||||
# override any defaults from lib/rubygems/defaults.rb.
|
||||
#
|
||||
# For Ruby implementers, provide lib/rubygems/#{RUBY_ENGINE}.rb and override
|
||||
# any defaults from lib/rubygems/defaults.rb.
|
||||
# For Ruby implementers, provide lib/rubygems/defaults/#{RUBY_ENGINE}.rb and
|
||||
# override any defaults from lib/rubygems/defaults.rb.
|
||||
#
|
||||
# If you need RubyGems to perform extra work on install or uninstall, your
|
||||
# defaults override file can set pre and post install and uninstall hooks.
|
||||
|
@ -83,8 +57,8 @@ require "rubygems/deprecate"
|
|||
# == Bugs
|
||||
#
|
||||
# You can submit bugs to the
|
||||
# {RubyGems bug tracker}[http://rubyforge.org/tracker/?atid=575&group_id=126]
|
||||
# on RubyForge
|
||||
# {RubyGems bug tracker}[https://github.com/rubygems/rubygems/issues]
|
||||
# on GitHub
|
||||
#
|
||||
# == Credits
|
||||
#
|
||||
|
@ -112,7 +86,8 @@ require "rubygems/deprecate"
|
|||
# * Daniel Berger -- djberg96(at)gmail.com
|
||||
# * Phil Hagelberg -- technomancy(at)gmail.com
|
||||
# * Ryan Davis -- ryand-ruby(at)zenspider.com
|
||||
# * Evan Phoenix -- evan@phx.io
|
||||
# * Evan Phoenix -- evan(at)fallingsnow.net
|
||||
# * Steve Klabnik -- steve(at)steveklabnik.com
|
||||
#
|
||||
# (If your name is missing, PLEASE let us know!)
|
||||
#
|
||||
|
@ -120,49 +95,22 @@ require "rubygems/deprecate"
|
|||
#
|
||||
# -The RubyGems Team
|
||||
|
||||
require 'rbconfig'
|
||||
|
||||
module Gem
|
||||
VERSION = '1.8.24'
|
||||
VERSION = '2.0.a'
|
||||
end
|
||||
|
||||
##
|
||||
# Raised when RubyGems is unable to load or activate a gem. Contains the
|
||||
# name and version requirements of the gem that either conflicts with
|
||||
# already activated gems or that RubyGems is otherwise unable to activate.
|
||||
# Must be first since it unloads the prelude from 1.9.2
|
||||
require 'rubygems/compatibility'
|
||||
|
||||
class LoadError < ::LoadError
|
||||
# Name of gem
|
||||
attr_accessor :name
|
||||
|
||||
# Version requirement of gem
|
||||
attr_accessor :requirement
|
||||
end
|
||||
|
||||
# :stopdoc:
|
||||
|
||||
RubyGemsVersion = VERSION
|
||||
|
||||
RbConfigPriorities = %w[
|
||||
EXEEXT RUBY_SO_NAME arch bindir datadir libdir ruby_install_name
|
||||
ruby_version rubylibprefix sitedir sitelibdir vendordir vendorlibdir
|
||||
]
|
||||
|
||||
unless defined?(ConfigMap)
|
||||
##
|
||||
# Configuration settings from ::RbConfig
|
||||
ConfigMap = Hash.new do |cm, key|
|
||||
cm[key] = RbConfig::CONFIG[key.to_s]
|
||||
end
|
||||
else
|
||||
RbConfigPriorities.each do |key|
|
||||
ConfigMap[key.to_sym] = RbConfig::CONFIG[key]
|
||||
end
|
||||
end
|
||||
|
||||
RubyGemsPackageVersion = VERSION
|
||||
require 'rubygems/defaults'
|
||||
require 'rubygems/deprecate'
|
||||
require 'rubygems/errors'
|
||||
|
||||
module Gem
|
||||
RUBYGEMS_DIR = File.dirname File.expand_path(__FILE__)
|
||||
|
||||
# :startdoc:
|
||||
|
||||
##
|
||||
# An Array of Regexps that match windows ruby platforms.
|
||||
|
||||
|
@ -175,11 +123,13 @@ module Gem
|
|||
/wince/i,
|
||||
]
|
||||
|
||||
@@source_index = nil
|
||||
GEM_DEP_FILES = %w!gem.deps.rb Gemfile Isolate!
|
||||
|
||||
@@win_platform = nil
|
||||
|
||||
@configuration = nil
|
||||
@loaded_specs = {}
|
||||
@path_to_default_spec_map = {}
|
||||
@platforms = []
|
||||
@ruby = nil
|
||||
@sources = nil
|
||||
|
@ -198,12 +148,13 @@ module Gem
|
|||
# activated. Returns false if it can't find the path in a gem.
|
||||
|
||||
def self.try_activate path
|
||||
# TODO: deprecate when 1.9.3 comes out.
|
||||
# finds the _latest_ version... regardless of loaded specs and their deps
|
||||
# if another gem had a requirement that would mean we shouldn't
|
||||
# activate the latest version, then either it would alreaby be activated
|
||||
# or if it was ambigious (and thus unresolved) the code in our custom
|
||||
# require will try to activate the more specific version.
|
||||
|
||||
# TODO: use find_all and bork if ambiguous
|
||||
|
||||
spec = Gem::Specification.find_by_path path
|
||||
spec = Gem::Specification.find_inactive_by_path path
|
||||
return false unless spec
|
||||
|
||||
begin
|
||||
|
@ -215,77 +166,43 @@ module Gem
|
|||
return true
|
||||
end
|
||||
|
||||
##
|
||||
# Activates an installed gem matching +dep+. The gem must satisfy
|
||||
# +requirements+.
|
||||
#
|
||||
# Returns true if the gem is activated, false if it is already
|
||||
# loaded, or an exception otherwise.
|
||||
#
|
||||
# Gem#activate adds the library paths in +dep+ to $LOAD_PATH. Before a Gem
|
||||
# is activated its required Gems are activated. If the version information
|
||||
# is omitted, the highest version Gem of the supplied name is loaded. If a
|
||||
# Gem is not found that meets the version requirements or a required Gem is
|
||||
# not found, a Gem::LoadError is raised.
|
||||
#
|
||||
# More information on version requirements can be found in the
|
||||
# Gem::Requirement and Gem::Version documentation.
|
||||
def self.needs
|
||||
rs = Gem::RequestSet.new
|
||||
|
||||
def self.activate(dep, *requirements)
|
||||
raise ArgumentError, "Deprecated use of Gem.activate(dep)" if
|
||||
Gem::Dependency === dep
|
||||
yield rs
|
||||
|
||||
Gem::Specification.find_by_name(dep, *requirements).activate
|
||||
finish_resolve rs
|
||||
end
|
||||
|
||||
def self.activate_dep dep, *requirements # :nodoc:
|
||||
dep.to_spec.activate
|
||||
def self.finish_resolve(request_set=Gem::RequestSet.new)
|
||||
request_set.import Gem::Specification.unresolved_deps.values
|
||||
|
||||
request_set.resolve_current.each do |s|
|
||||
s.full_spec.activate
|
||||
end
|
||||
end
|
||||
|
||||
def self.activate_spec spec # :nodoc:
|
||||
spec.activate
|
||||
end
|
||||
def self.detect_gemdeps
|
||||
if path = ENV['RUBYGEMS_GEMDEPS']
|
||||
path = path.dup.untaint
|
||||
|
||||
def self.unresolved_deps
|
||||
@unresolved_deps ||= Hash.new { |h, n| h[n] = Gem::Dependency.new n }
|
||||
end
|
||||
if path == "-"
|
||||
path = GEM_DEP_FILES.find { |f| File.exists?(f) }
|
||||
|
||||
##
|
||||
# An Array of all possible load paths for all versions of all gems in the
|
||||
# Gem installation.
|
||||
return unless path
|
||||
end
|
||||
|
||||
def self.all_load_paths
|
||||
result = []
|
||||
return unless File.exists? path
|
||||
|
||||
Gem.path.each do |gemdir|
|
||||
each_load_path all_partials(gemdir) do |load_path|
|
||||
result << load_path
|
||||
rs = Gem::RequestSet.new
|
||||
rs.load_gemdeps path
|
||||
|
||||
rs.resolve_current.map do |s|
|
||||
sp = s.full_spec
|
||||
sp.activate
|
||||
sp
|
||||
end
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
##
|
||||
# Return all the partial paths in +gemdir+.
|
||||
|
||||
def self.all_partials(gemdir)
|
||||
Dir[File.join(gemdir, "gems/*")]
|
||||
end
|
||||
|
||||
private_class_method :all_partials
|
||||
|
||||
##
|
||||
# See if a given gem is available.
|
||||
|
||||
def self.available?(dep, *requirements)
|
||||
requirements = Gem::Requirement.default if requirements.empty?
|
||||
|
||||
unless dep.respond_to?(:name) and dep.respond_to?(:requirement) then
|
||||
dep = Gem::Dependency.new dep, requirements
|
||||
end
|
||||
|
||||
not dep.matching_specs(true).empty?
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -343,11 +260,10 @@ module Gem
|
|||
# mainly used by the unit tests to provide test isolation.
|
||||
|
||||
def self.clear_paths
|
||||
@@source_index = nil
|
||||
@paths = nil
|
||||
@user_home = nil
|
||||
@searcher = nil
|
||||
Gem::Specification.reset
|
||||
Gem::Security.reset if const_defined? :Security
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -377,7 +293,8 @@ module Gem
|
|||
# package is not available as a gem, return nil.
|
||||
|
||||
def self.datadir(gem_name)
|
||||
# TODO: deprecate
|
||||
# TODO: deprecate and move to Gem::Specification
|
||||
# and drop the extra ", gem_name" which is uselessly redundant
|
||||
spec = @loaded_specs[gem_name]
|
||||
return nil if spec.nil?
|
||||
File.join spec.full_gem_path, "data", gem_name
|
||||
|
@ -391,10 +308,12 @@ module Gem
|
|||
Zlib::Deflate.deflate data
|
||||
end
|
||||
|
||||
# DOC: needs doc'd or :nodoc'd
|
||||
def self.paths
|
||||
@paths ||= Gem::PathSupport.new
|
||||
end
|
||||
|
||||
# DOC: needs doc'd or :nodoc'd
|
||||
def self.paths=(env)
|
||||
clear_paths
|
||||
@paths = Gem::PathSupport.new env
|
||||
|
@ -450,7 +369,7 @@ module Gem
|
|||
|
||||
require 'fileutils'
|
||||
|
||||
%w[cache doc gems specifications].each do |name|
|
||||
%w[cache build_info doc gems specifications].each do |name|
|
||||
subdir = File.join dir, name
|
||||
next if File.exist? subdir
|
||||
FileUtils.mkdir_p subdir rescue nil # in case of perms issues -- lame
|
||||
|
@ -566,13 +485,28 @@ module Gem
|
|||
Zlib::Inflate.inflate data
|
||||
end
|
||||
|
||||
##
|
||||
# Top level install helper method. Allows you to install gems interactively:
|
||||
#
|
||||
# % irb
|
||||
# >> Gem.install "minitest"
|
||||
# Fetching: minitest-3.0.1.gem (100%)
|
||||
# => [#<Gem::Specification:0x1013b4528 @name="minitest", ...>]
|
||||
|
||||
def self.install name, version = Gem::Requirement.default
|
||||
require "rubygems/dependency_installer"
|
||||
inst = Gem::DependencyInstaller.new
|
||||
inst.install name, version
|
||||
inst.installed_gems
|
||||
end
|
||||
|
||||
##
|
||||
# Get the default RubyGems API host. This is normally
|
||||
# <tt>https://rubygems.org</tt>.
|
||||
|
||||
def self.host
|
||||
# TODO: move to utils
|
||||
@host ||= "https://rubygems.org"
|
||||
@host ||= Gem::DEFAULT_HOST
|
||||
end
|
||||
|
||||
## Set the default RubyGems API host.
|
||||
|
@ -582,43 +516,6 @@ module Gem
|
|||
@host = host
|
||||
end
|
||||
|
||||
##
|
||||
# Return a list of all possible load paths for the latest version for all
|
||||
# gems in the Gem installation.
|
||||
|
||||
def self.latest_load_paths
|
||||
result = []
|
||||
|
||||
Gem.path.each do |gemdir|
|
||||
each_load_path(latest_partials(gemdir)) do |load_path|
|
||||
result << load_path
|
||||
end
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
##
|
||||
# Return only the latest partial paths in the given +gemdir+.
|
||||
|
||||
def self.latest_partials(gemdir)
|
||||
latest = {}
|
||||
all_partials(gemdir).each do |gp|
|
||||
base = File.basename gp
|
||||
|
||||
if base.to_s =~ /(.*)-((\d+\.)*\d+)/ then
|
||||
name, version = $1, $2
|
||||
ver = Gem::Version.new(version)
|
||||
if latest[name].nil? || ver > latest[name][0]
|
||||
latest[name] = [ver, gp]
|
||||
end
|
||||
end
|
||||
end
|
||||
latest.collect { |k,v| v[1] }
|
||||
end
|
||||
|
||||
private_class_method :latest_partials
|
||||
|
||||
##
|
||||
# The index to insert activated gem paths into the $LOAD_PATH.
|
||||
#
|
||||
|
@ -629,16 +526,6 @@ module Gem
|
|||
def self.load_path_insert_index
|
||||
index = $LOAD_PATH.index ConfigMap[:sitelibdir]
|
||||
|
||||
if QUICKLOADER_SUCKAGE then
|
||||
$LOAD_PATH.each_with_index do |path, i|
|
||||
if path.instance_variables.include?(:@gem_prelude_index) or
|
||||
path.instance_variables.include?('@gem_prelude_index') then
|
||||
index = i
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
index
|
||||
end
|
||||
|
||||
|
@ -711,27 +598,6 @@ module Gem
|
|||
"#{Marshal::MAJOR_VERSION}.#{Marshal::MINOR_VERSION}"
|
||||
end
|
||||
|
||||
##
|
||||
# Get the appropriate cache path.
|
||||
#
|
||||
# Pass a string to use a different base path, or nil/false (default) for
|
||||
# Gem.dir.
|
||||
#
|
||||
|
||||
def self.cache_dir(custom_dir=false)
|
||||
File.join(custom_dir || Gem.dir, "cache")
|
||||
end
|
||||
|
||||
##
|
||||
# Given a gem path, find the gem in cache.
|
||||
#
|
||||
# Pass a string as the second argument to use a different base path, or
|
||||
# nil/false (default) for Gem.dir.
|
||||
|
||||
def self.cache_gem(filename, user_dir=false)
|
||||
cache_dir(user_dir).add(filename)
|
||||
end
|
||||
|
||||
##
|
||||
# Set array of platforms this RubyGems supports (primarily for testing).
|
||||
|
||||
|
@ -769,6 +635,15 @@ module Gem
|
|||
@post_install_hooks << hook
|
||||
end
|
||||
|
||||
##
|
||||
# Adds a post-installs hook that will be passed a Gem::DependencyInstaller
|
||||
# and a list of installed specifications when
|
||||
# Gem::DependencyInstaller#install is complete
|
||||
|
||||
def self.done_installing(&hook)
|
||||
@done_installing_hooks << hook
|
||||
end
|
||||
|
||||
##
|
||||
# Adds a hook that will get run after Gem::Specification.reset is
|
||||
# run.
|
||||
|
@ -828,39 +703,10 @@ module Gem
|
|||
end
|
||||
|
||||
##
|
||||
# Promotes the load paths of the +gem_name+ over the load paths of
|
||||
# +over_name+. Useful for allowing one gem to override features in another
|
||||
# using #find_files.
|
||||
|
||||
def self.promote_load_path(gem_name, over_name)
|
||||
gem = Gem.loaded_specs[gem_name]
|
||||
over = Gem.loaded_specs[over_name]
|
||||
|
||||
raise ArgumentError, "gem #{gem_name} is not activated" if gem.nil?
|
||||
raise ArgumentError, "gem #{over_name} is not activated" if over.nil?
|
||||
|
||||
last_gem_path = Gem::Path.path(gem.full_gem_path).add(gem.require_paths.last)
|
||||
|
||||
over_paths = over.require_paths.map do |path|
|
||||
Gem::Path.path(over.full_gem_path).add(path).to_s
|
||||
end
|
||||
|
||||
over_paths.each do |path|
|
||||
$LOAD_PATH.delete path
|
||||
end
|
||||
|
||||
gem = $LOAD_PATH.index(last_gem_path) + 1
|
||||
|
||||
$LOAD_PATH.insert(gem, *over_paths)
|
||||
end
|
||||
|
||||
##
|
||||
# Refresh source_index from disk and clear searcher.
|
||||
# Refresh available gems from disk.
|
||||
|
||||
def self.refresh
|
||||
Gem::Specification.reset
|
||||
@source_index = nil
|
||||
@searcher = nil
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -870,50 +716,6 @@ module Gem
|
|||
File.open path, binary_mode do |f| f.read end
|
||||
end
|
||||
|
||||
##
|
||||
# Report a load error during activation. The message of load error
|
||||
# depends on whether it was a version mismatch or if there are not gems of
|
||||
# any version by the requested name.
|
||||
|
||||
def self.report_activate_error(gem)
|
||||
matches = Gem::Specification.find_by_name(gem.name)
|
||||
|
||||
if matches.empty? then
|
||||
error = Gem::LoadError.new(
|
||||
"Could not find RubyGem #{gem.name} (#{gem.requirement})\n")
|
||||
else
|
||||
error = Gem::LoadError.new(
|
||||
"RubyGem version error: " +
|
||||
"#{gem.name}(#{matches.first.version} not #{gem.requirement})\n")
|
||||
end
|
||||
|
||||
error.name = gem.name
|
||||
error.requirement = gem.requirement
|
||||
raise error
|
||||
end
|
||||
|
||||
private_class_method :report_activate_error
|
||||
|
||||
##
|
||||
# Full path to +libfile+ in +gemname+. Searches for the latest gem unless
|
||||
# +requirements+ is given.
|
||||
|
||||
def self.required_location(gemname, libfile, *requirements)
|
||||
requirements = Gem::Requirement.default if requirements.empty?
|
||||
|
||||
matches = Gem::Specification.find_all_by_name gemname, *requirements
|
||||
|
||||
return nil if matches.empty?
|
||||
|
||||
spec = matches.last
|
||||
spec.require_paths.each do |path|
|
||||
result = Gem::Path.path(spec.full_gem_path).add(path, libfile)
|
||||
return result if result.exist?
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
##
|
||||
# The path to the running Ruby interpreter.
|
||||
|
||||
|
@ -928,6 +730,7 @@ module Gem
|
|||
@ruby
|
||||
end
|
||||
|
||||
# DOC: needs doc'd or :nodoc'd
|
||||
def self.latest_spec_for name
|
||||
dependency = Gem::Dependency.new name
|
||||
fetcher = Gem::SpecFetcher.fetcher
|
||||
|
@ -942,13 +745,16 @@ module Gem
|
|||
match and fetcher.fetch_spec(*match)
|
||||
end
|
||||
|
||||
# DOC: needs doc'd or :nodoc'd
|
||||
def self.latest_version_for name
|
||||
spec = latest_spec_for name
|
||||
spec and spec.version
|
||||
end
|
||||
|
||||
# DOC: needs doc'd or :nodoc'd
|
||||
def self.latest_rubygems_version
|
||||
latest_version_for "rubygems-update"
|
||||
latest_version_for("rubygems-update") or
|
||||
raise "Can't find 'rubygems-update' in any repo. Check `gem source list`."
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -968,36 +774,34 @@ module Gem
|
|||
end
|
||||
|
||||
##
|
||||
# The GemPathSearcher object used to search for matching installed gems.
|
||||
# A Gem::Version for the currently running RubyGems
|
||||
|
||||
def self.searcher
|
||||
@searcher ||= Gem::GemPathSearcher.new
|
||||
def self.rubygems_version
|
||||
return @rubygems_version if defined? @rubygems_version
|
||||
@rubygems_version = Gem::Version.new Gem::VERSION
|
||||
end
|
||||
|
||||
##
|
||||
# Returns the Gem::SourceIndex of specifications that are in the Gem.path
|
||||
|
||||
def self.source_index
|
||||
@@source_index ||= Gem::Deprecate.skip_during do
|
||||
SourceIndex.new Gem::Specification.dirs
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Returns an Array of sources to fetch remote gems from. If the sources
|
||||
# list is empty, attempts to load the "sources" gem, then uses
|
||||
# default_sources if it is not installed.
|
||||
# Returns an Array of sources to fetch remote gems from. Uses
|
||||
# default_sources if the sources list is empty.
|
||||
|
||||
def self.sources
|
||||
@sources ||= default_sources
|
||||
@sources ||= Gem::SourceList.from(default_sources)
|
||||
end
|
||||
|
||||
##
|
||||
# Need to be able to set the sources without calling
|
||||
# Gem.sources.replace since that would cause an infinite loop.
|
||||
#
|
||||
# DOC: This comment is not documentation about the method itself, it's
|
||||
# more of a code comment about the implementation.
|
||||
|
||||
def self.sources= new_sources
|
||||
@sources = new_sources
|
||||
if !new_sources
|
||||
@sources = nil
|
||||
else
|
||||
@sources = Gem::SourceList.from(new_sources)
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -1007,12 +811,6 @@ module Gem
|
|||
@suffix_pattern ||= "{#{suffixes.join(',')}}"
|
||||
end
|
||||
|
||||
def self.loaded_path? path
|
||||
# TODO: ruby needs a feature to let us query what's loaded in 1.8 and 1.9
|
||||
re = /(^|\/)#{Regexp.escape path}#{Regexp.union(*Gem.suffixes)}$/
|
||||
$LOADED_FEATURES.any? { |s| s =~ re }
|
||||
end
|
||||
|
||||
##
|
||||
# Suffixes for require-able paths.
|
||||
|
||||
|
@ -1067,7 +865,7 @@ module Gem
|
|||
# The home directory for the user.
|
||||
|
||||
def self.user_home
|
||||
@user_home ||= find_home
|
||||
@user_home ||= find_home.untaint
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -1126,6 +924,9 @@ module Gem
|
|||
load_plugin_files files
|
||||
end
|
||||
|
||||
# FIX: Almost everywhere else we use the `def self.` way of defining class
|
||||
# methods, and then we switch over to `class << self` here. Pick one or the
|
||||
# other.
|
||||
class << self
|
||||
|
||||
##
|
||||
|
@ -1134,29 +935,73 @@ module Gem
|
|||
attr_reader :loaded_specs
|
||||
|
||||
##
|
||||
# The list of hooks to be run before Gem::Install#install finishes
|
||||
# installation
|
||||
# Register a Gem::Specification for default gem
|
||||
|
||||
def register_default_spec(spec)
|
||||
spec.files.each do |file|
|
||||
@path_to_default_spec_map[file] = spec
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Find a Gem::Specification of default gem from +path+
|
||||
|
||||
def find_unresolved_default_spec(path)
|
||||
Gem.suffixes.each do |suffix|
|
||||
spec = @path_to_default_spec_map["#{path}#{suffix}"]
|
||||
return spec if spec
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
##
|
||||
# Remove needless Gem::Specification of default gem from
|
||||
# unresolved default gem list
|
||||
|
||||
def remove_unresolved_default_spec(spec)
|
||||
spec.files.each do |file|
|
||||
@path_to_default_spec_map.delete(file)
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Clear default gem related varibles. It is for test
|
||||
|
||||
def clear_default_specs
|
||||
@path_to_default_spec_map.clear
|
||||
end
|
||||
|
||||
##
|
||||
# The list of hooks to be run after Gem::Installer#install extracts files
|
||||
# and builds extensions
|
||||
|
||||
attr_reader :post_build_hooks
|
||||
|
||||
##
|
||||
# The list of hooks to be run before Gem::Install#install does any work
|
||||
# The list of hooks to be run after Gem::Installer#install completes
|
||||
# installation
|
||||
|
||||
attr_reader :post_install_hooks
|
||||
|
||||
##
|
||||
# The list of hooks to be run after Gem::DependencyInstaller installs a
|
||||
# set of gems
|
||||
|
||||
attr_reader :done_installing_hooks
|
||||
|
||||
##
|
||||
# The list of hooks to be run after Gem::Specification.reset is run.
|
||||
|
||||
attr_reader :post_reset_hooks
|
||||
|
||||
##
|
||||
# The list of hooks to be run before Gem::Uninstall#uninstall does any
|
||||
# work
|
||||
# The list of hooks to be run after Gem::Uninstaller#uninstall completes
|
||||
# installation
|
||||
|
||||
attr_reader :post_uninstall_hooks
|
||||
|
||||
##
|
||||
# The list of hooks to be run after Gem::Install#install is finished
|
||||
# The list of hooks to be run before Gem::Installer#install does any work
|
||||
|
||||
attr_reader :pre_install_hooks
|
||||
|
||||
|
@ -1166,15 +1011,12 @@ module Gem
|
|||
attr_reader :pre_reset_hooks
|
||||
|
||||
##
|
||||
# The list of hooks to be run after Gem::Uninstall#uninstall is finished
|
||||
# The list of hooks to be run before Gem::Uninstaller#uninstall does any
|
||||
# work
|
||||
|
||||
attr_reader :pre_uninstall_hooks
|
||||
end
|
||||
|
||||
def self.cache # :nodoc:
|
||||
source_index
|
||||
end
|
||||
|
||||
##
|
||||
# Location of Marshal quick gemspecs on remote repositories
|
||||
|
||||
|
@ -1184,77 +1026,21 @@ module Gem
|
|||
autoload :Requirement, 'rubygems/requirement'
|
||||
autoload :Dependency, 'rubygems/dependency'
|
||||
autoload :DependencyList, 'rubygems/dependency_list'
|
||||
autoload :GemPathSearcher, 'rubygems/gem_path_searcher'
|
||||
autoload :SourceList, 'rubygems/source_list'
|
||||
autoload :SpecFetcher, 'rubygems/spec_fetcher'
|
||||
autoload :Specification, 'rubygems/specification'
|
||||
autoload :Cache, 'rubygems/source_index'
|
||||
autoload :SourceIndex, 'rubygems/source_index'
|
||||
autoload :PathSupport, 'rubygems/path_support'
|
||||
autoload :Platform, 'rubygems/platform'
|
||||
autoload :Builder, 'rubygems/builder'
|
||||
autoload :ConfigFile, 'rubygems/config_file'
|
||||
end
|
||||
autoload :DependencyResolver, 'rubygems/dependency_resolver'
|
||||
autoload :RequestSet, 'rubygems/request_set'
|
||||
|
||||
module Kernel
|
||||
|
||||
remove_method :gem if 'method' == defined? gem # from gem_prelude.rb on 1.9
|
||||
|
||||
##
|
||||
# Use Kernel#gem to activate a specific version of +gem_name+.
|
||||
#
|
||||
# +requirements+ is a list of version requirements that the
|
||||
# specified gem must match, most commonly "= example.version.number". See
|
||||
# Gem::Requirement for how to specify a version requirement.
|
||||
#
|
||||
# If you will be activating the latest version of a gem, there is no need to
|
||||
# call Kernel#gem, Kernel#require will do the right thing for you.
|
||||
#
|
||||
# Kernel#gem returns true if the gem was activated, otherwise false. If the
|
||||
# gem could not be found, didn't match the version requirements, or a
|
||||
# different version was already activated, an exception will be raised.
|
||||
#
|
||||
# Kernel#gem should be called *before* any require statements (otherwise
|
||||
# RubyGems may load a conflicting library version).
|
||||
#
|
||||
# In older RubyGems versions, the environment variable GEM_SKIP could be
|
||||
# used to skip activation of specified gems, for example to test out changes
|
||||
# that haven't been installed yet. Now RubyGems defers to -I and the
|
||||
# RUBYLIB environment variable to skip activation of a gem.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# GEM_SKIP=libA:libB ruby -I../libA -I../libB ./mycode.rb
|
||||
|
||||
def gem(gem_name, *requirements) # :doc:
|
||||
skip_list = (ENV['GEM_SKIP'] || "").split(/:/)
|
||||
raise Gem::LoadError, "skipping #{gem_name}" if skip_list.include? gem_name
|
||||
spec = Gem::Dependency.new(gem_name, *requirements).to_spec
|
||||
spec.activate if spec
|
||||
end
|
||||
|
||||
private :gem
|
||||
|
||||
end
|
||||
|
||||
##
|
||||
# Return the path to the data directory associated with the named package. If
|
||||
# the package is loaded as a gem, return the gem specific data directory.
|
||||
# Otherwise return a path to the share area as define by
|
||||
# "#{ConfigMap[:datadir]}/#{package_name}".
|
||||
|
||||
def RbConfig.datadir(package_name) # :nodoc:
|
||||
warn "#{Gem.location_of_caller.join ':'}:Warning: " \
|
||||
"RbConfig.datadir is deprecated and will be removed on or after " \
|
||||
"August 2011. " \
|
||||
"Use Gem::datadir."
|
||||
|
||||
require 'rbconfig/datadir'
|
||||
|
||||
Gem.datadir(package_name) || File.join(Gem::ConfigMap[:datadir], package_name)
|
||||
require "rubygems/specification"
|
||||
end
|
||||
|
||||
require 'rubygems/exceptions'
|
||||
|
||||
# REFACTOR: This should be pulled out into some kind of hacks file.
|
||||
gem_preluded = Gem::GEM_PRELUDE_SUCKAGE and defined? Gem
|
||||
unless gem_preluded then # TODO: remove guard after 1.9.2 dropped
|
||||
begin
|
||||
|
@ -1277,29 +1063,10 @@ unless gem_preluded then # TODO: remove guard after 1.9.2 dropped
|
|||
end
|
||||
|
||||
##
|
||||
# Enables the require hook for RubyGems.
|
||||
# Loads the default specs.
|
||||
Gem::Specification.load_defaults
|
||||
|
||||
require 'rubygems/custom_require'
|
||||
require 'rubygems/core_ext/kernel_gem'
|
||||
require 'rubygems/core_ext/kernel_require'
|
||||
|
||||
module Gem
|
||||
class << self
|
||||
extend Gem::Deprecate
|
||||
deprecate :activate_dep, "Specification#activate", 2011, 6
|
||||
deprecate :activate_spec, "Specification#activate", 2011, 6
|
||||
deprecate :cache, "Gem::source_index", 2011, 8
|
||||
deprecate :activate, "Specification#activate", 2011, 10
|
||||
deprecate :all_load_paths, :none, 2011, 10
|
||||
deprecate :all_partials, :none, 2011, 10
|
||||
deprecate :latest_load_paths, :none, 2011, 10
|
||||
deprecate :promote_load_path, :none, 2011, 10
|
||||
deprecate :available?, "Specification::find_by_name", 2011, 11
|
||||
deprecate :cache_dir, "Specification#cache_dir", 2011, 11
|
||||
deprecate :cache_gem, "Specification#cache_file", 2011, 11
|
||||
deprecate :default_system_source_cache_dir, :none, 2011, 11
|
||||
deprecate :default_user_source_cache_dir, :none, 2011, 11
|
||||
deprecate :report_activate_error, :none, 2011, 11
|
||||
deprecate :required_location, :none, 2011, 11
|
||||
deprecate :searcher, "Specification", 2011, 11
|
||||
deprecate :source_index, "Specification", 2011, 11
|
||||
end
|
||||
end
|
||||
Gem.detect_gemdeps
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
module Gem
|
||||
class AvailableSet
|
||||
Tuple = Struct.new(:spec, :source)
|
||||
|
||||
def initialize
|
||||
@set = []
|
||||
@sorted = nil
|
||||
end
|
||||
|
||||
attr_reader :set
|
||||
|
||||
def add(spec, source)
|
||||
@set << Tuple.new(spec, source)
|
||||
@sorted = nil
|
||||
self
|
||||
end
|
||||
|
||||
def <<(o)
|
||||
case o
|
||||
when AvailableSet
|
||||
s = o.set
|
||||
when Array
|
||||
s = o.map do |sp,so|
|
||||
if !sp.kind_of?(Specification) or !so.kind_of?(Source)
|
||||
raise TypeError, "Array must be in [[spec, source], ...] form"
|
||||
end
|
||||
|
||||
Tuple.new(sp,so)
|
||||
end
|
||||
else
|
||||
raise TypeError, "Must be an AvailableSet"
|
||||
end
|
||||
|
||||
@set += s
|
||||
@sorted = nil
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
def empty?
|
||||
@set.empty?
|
||||
end
|
||||
|
||||
def all_specs
|
||||
@set.map { |t| t.spec }
|
||||
end
|
||||
|
||||
def match_platform!
|
||||
@set.reject! { |t| !Gem::Platform.match(t.spec.platform) }
|
||||
@sorted = nil
|
||||
self
|
||||
end
|
||||
|
||||
def sorted
|
||||
@sorted ||= @set.sort do |a,b|
|
||||
i = b.spec <=> a.spec
|
||||
i != 0 ? i : (a.source <=> b.source)
|
||||
end
|
||||
end
|
||||
|
||||
def size
|
||||
@set.size
|
||||
end
|
||||
|
||||
def source_for(spec)
|
||||
f = @set.find { |t| t.spec == spec }
|
||||
f.source
|
||||
end
|
||||
|
||||
def pick_best!
|
||||
return self if empty?
|
||||
|
||||
@set = [sorted.first]
|
||||
@sorted = nil
|
||||
self
|
||||
end
|
||||
|
||||
def remove_installed!(dep)
|
||||
@set.reject! do |t|
|
||||
# already locally installed
|
||||
Gem::Specification.any? do |installed_spec|
|
||||
dep.name == installed_spec.name and
|
||||
dep.requirement.satisfied_by? installed_spec.version
|
||||
end
|
||||
end
|
||||
|
||||
@sorted = nil
|
||||
self
|
||||
end
|
||||
|
||||
def inject_into_list(dep_list)
|
||||
@set.each { |t| dep_list.add t.spec }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,99 +0,0 @@
|
|||
#--
|
||||
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
|
||||
# All rights reserved.
|
||||
# See LICENSE.txt for permissions.
|
||||
#++
|
||||
|
||||
require 'rubygems'
|
||||
require 'rubygems/user_interaction'
|
||||
|
||||
Gem.load_yaml
|
||||
|
||||
require 'rubygems/package'
|
||||
|
||||
##
|
||||
# The Builder class processes RubyGem specification files
|
||||
# to produce a .gem file.
|
||||
|
||||
class Gem::Builder
|
||||
|
||||
include Gem::UserInteraction
|
||||
|
||||
##
|
||||
# Constructs a builder instance for the provided specification
|
||||
#
|
||||
# spec:: [Gem::Specification] The specification instance
|
||||
|
||||
def initialize(spec)
|
||||
@spec = spec
|
||||
end
|
||||
|
||||
##
|
||||
# Builds the gem from the specification. Returns the name of the file
|
||||
# written.
|
||||
|
||||
def build(skip_validation=false)
|
||||
@spec.mark_version
|
||||
@spec.validate unless skip_validation
|
||||
@signer = sign
|
||||
write_package
|
||||
say success if Gem.configuration.verbose
|
||||
File.basename @spec.cache_file
|
||||
end
|
||||
|
||||
def success
|
||||
<<-EOM
|
||||
Successfully built RubyGem
|
||||
Name: #{@spec.name}
|
||||
Version: #{@spec.version}
|
||||
File: #{File.basename @spec.cache_file}
|
||||
EOM
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
##
|
||||
# If the signing key was specified, then load the file, and swap to the
|
||||
# public key (TODO: we should probably just omit the signing key in favor of
|
||||
# the signing certificate, but that's for the future, also the signature
|
||||
# algorithm should be configurable)
|
||||
|
||||
def sign
|
||||
signer = nil
|
||||
|
||||
if @spec.respond_to?(:signing_key) and @spec.signing_key then
|
||||
require 'rubygems/security'
|
||||
|
||||
signer = Gem::Security::Signer.new @spec.signing_key, @spec.cert_chain
|
||||
@spec.signing_key = nil
|
||||
@spec.cert_chain = signer.cert_chain.map { |cert| cert.to_s }
|
||||
end
|
||||
|
||||
signer
|
||||
end
|
||||
|
||||
def write_package
|
||||
file_name = File.basename @spec.cache_file
|
||||
open file_name, 'wb' do |gem_io|
|
||||
Gem::Package.open gem_io, 'w', @signer do |pkg|
|
||||
yaml = @spec.to_yaml
|
||||
pkg.metadata = yaml
|
||||
|
||||
@spec.files.each do |file|
|
||||
next if File.directory?(file)
|
||||
next if file == file_name # Don't add gem onto itself
|
||||
|
||||
stat = File.stat(file)
|
||||
mode = stat.mode & 0777
|
||||
size = stat.size
|
||||
|
||||
pkg.add_file_simple file, mode, size do |tar_io|
|
||||
tar_io.write open(file, "rb") { |f| f.read }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -9,7 +9,7 @@ require 'rubygems/user_interaction'
|
|||
|
||||
##
|
||||
# Base class for all Gem commands. When creating a new gem command, define
|
||||
# #new, #execute, #arguments, #defaults_str, #description and #usage
|
||||
# #initialize, #execute, #arguments, #defaults_str, #description and #usage
|
||||
# (as appropriate). See the above mentioned methods for details.
|
||||
#
|
||||
# A very good example to look at is Gem::Commands::ContentsCommand
|
||||
|
@ -150,8 +150,9 @@ class Gem::Command
|
|||
|
||||
def show_lookup_failure(gem_name, version, errors, domain)
|
||||
if errors and !errors.empty?
|
||||
alert_error "Could not find a valid gem '#{gem_name}' (#{version}), here is why:"
|
||||
errors.each { |x| say " #{x.wordy}" }
|
||||
msg = "Could not find a valid gem '#{gem_name}' (#{version}), here is why:\n"
|
||||
errors.each { |x| msg << " #{x.wordy}\n" }
|
||||
alert_error msg
|
||||
else
|
||||
alert_error "Could not find a valid gem '#{gem_name}' (#{version}) in any repository"
|
||||
end
|
||||
|
@ -179,6 +180,15 @@ class Gem::Command
|
|||
args.select { |arg| arg !~ /^-/ }
|
||||
end
|
||||
|
||||
##
|
||||
# Get all [gem, version] from the command line.
|
||||
#
|
||||
# An argument in the form gem:ver is pull apart into the gen name and version,
|
||||
# respectively.
|
||||
def get_all_gem_names_and_versions
|
||||
get_all_gem_names.map { |name| name.split(":", 2) }
|
||||
end
|
||||
|
||||
##
|
||||
# Get a single gem name from the command line. Fail if there is no gem name
|
||||
# or if there is more than one gem name given.
|
||||
|
@ -268,8 +278,18 @@ class Gem::Command
|
|||
# Invoke the command with the given list of arguments.
|
||||
|
||||
def invoke(*args)
|
||||
invoke_with_build_args args, nil
|
||||
end
|
||||
|
||||
##
|
||||
# Invoke the command with the given list of normal arguments
|
||||
# and additional build arguments.
|
||||
|
||||
def invoke_with_build_args(args, build_args)
|
||||
handle_options args
|
||||
|
||||
options[:build_args] = build_args
|
||||
|
||||
if options[:help] then
|
||||
show_help
|
||||
elsif @when_invoked then
|
||||
|
@ -344,7 +364,7 @@ class Gem::Command
|
|||
|
||||
def handle_options(args)
|
||||
args = add_extra_args(args)
|
||||
@options = @defaults.clone
|
||||
@options = Marshal.load Marshal.dump @defaults # deep copy
|
||||
parser.parse!(args)
|
||||
@options[:args] = args
|
||||
end
|
||||
|
@ -372,18 +392,23 @@ class Gem::Command
|
|||
|
||||
private
|
||||
|
||||
##
|
||||
# Create on demand parser.
|
||||
def add_parser_description # :nodoc:
|
||||
return unless description
|
||||
|
||||
def parser
|
||||
create_option_parser if @parser.nil?
|
||||
@parser
|
||||
end
|
||||
|
||||
def create_option_parser
|
||||
@parser = OptionParser.new
|
||||
formatted = description.split("\n\n").map do |chunk|
|
||||
wrap chunk, 80 - 4
|
||||
end.join "\n"
|
||||
|
||||
@parser.separator nil
|
||||
@parser.separator " Description:"
|
||||
formatted.split("\n").each do |line|
|
||||
@parser.separator " #{line.rstrip}"
|
||||
end
|
||||
end
|
||||
|
||||
def add_parser_options # :nodoc:
|
||||
@parser.separator nil
|
||||
|
||||
regular_options = @option_groups.delete :options
|
||||
|
||||
configure_options "", regular_options
|
||||
|
@ -392,45 +417,56 @@ class Gem::Command
|
|||
@parser.separator nil
|
||||
configure_options group_name, option_list
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Adds a section with +title+ and +content+ to the parser help view. Used
|
||||
# for adding command arguments and default arguments.
|
||||
|
||||
def add_parser_run_info title, content
|
||||
return if content.empty?
|
||||
|
||||
@parser.separator nil
|
||||
@parser.separator " #{title}:"
|
||||
content.split(/\n/).each do |line|
|
||||
@parser.separator " #{line}"
|
||||
end
|
||||
end
|
||||
|
||||
def add_parser_summary # :nodoc:
|
||||
return unless @summary
|
||||
|
||||
@parser.separator nil
|
||||
@parser.separator " Summary:"
|
||||
wrap(@summary, 80 - 4).split("\n").each do |line|
|
||||
@parser.separator " #{line.strip}"
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Create on demand parser.
|
||||
|
||||
def parser
|
||||
create_option_parser if @parser.nil?
|
||||
@parser
|
||||
end
|
||||
|
||||
##
|
||||
# Creates an option parser and fills it in with the help info for the
|
||||
# command.
|
||||
|
||||
def create_option_parser
|
||||
@parser = OptionParser.new
|
||||
|
||||
add_parser_options
|
||||
|
||||
@parser.separator nil
|
||||
configure_options "Common", Gem::Command.common_options
|
||||
|
||||
unless arguments.empty?
|
||||
@parser.separator nil
|
||||
@parser.separator " Arguments:"
|
||||
arguments.split(/\n/).each do |arg_desc|
|
||||
@parser.separator " #{arg_desc}"
|
||||
end
|
||||
end
|
||||
|
||||
if @summary then
|
||||
@parser.separator nil
|
||||
@parser.separator " Summary:"
|
||||
wrap(@summary, 80 - 4).split("\n").each do |line|
|
||||
@parser.separator " #{line.strip}"
|
||||
end
|
||||
end
|
||||
|
||||
if description then
|
||||
formatted = description.split("\n\n").map do |chunk|
|
||||
wrap chunk, 80 - 4
|
||||
end.join "\n"
|
||||
|
||||
@parser.separator nil
|
||||
@parser.separator " Description:"
|
||||
formatted.split("\n").each do |line|
|
||||
@parser.separator " #{line.rstrip}"
|
||||
end
|
||||
end
|
||||
|
||||
unless defaults_str.empty?
|
||||
@parser.separator nil
|
||||
@parser.separator " Defaults:"
|
||||
defaults_str.split(/\n/).each do |line|
|
||||
@parser.separator " #{line}"
|
||||
end
|
||||
end
|
||||
add_parser_run_info "Arguments", arguments
|
||||
add_parser_summary
|
||||
add_parser_description
|
||||
add_parser_run_info "Defaults", defaults_str
|
||||
end
|
||||
|
||||
def configure_options(header, option_list)
|
||||
|
@ -521,7 +557,7 @@ basic help message containing pointers to more information.
|
|||
http://localhost:8808/
|
||||
with info about installed gems
|
||||
Further information:
|
||||
http://rubygems.rubyforge.org
|
||||
http://guides.rubygems.org
|
||||
HELP
|
||||
|
||||
# :startdoc:
|
||||
|
@ -529,7 +565,7 @@ basic help message containing pointers to more information.
|
|||
end
|
||||
|
||||
##
|
||||
# This is where Commands will be placed in the namespace
|
||||
# \Commands will be placed in this namespace
|
||||
|
||||
module Gem::Commands
|
||||
end
|
||||
|
|
|
@ -18,12 +18,15 @@ require 'rubygems/user_interaction'
|
|||
# # file rubygems_plugin.rb
|
||||
# require 'rubygems/command_manager'
|
||||
#
|
||||
# Gem::CommandManager.instance.register_command :edit
|
||||
#
|
||||
# You should put the implementation of your command in rubygems/commands.
|
||||
#
|
||||
# # file rubygems/commands/edit_command.rb
|
||||
# class Gem::Commands::EditCommand < Gem::Command
|
||||
# # ...
|
||||
# end
|
||||
#
|
||||
# Gem::CommandManager.instance.register_command :edit
|
||||
#
|
||||
# See Gem::Command for instructions on writing gem commands.
|
||||
|
||||
class Gem::CommandManager
|
||||
|
@ -37,6 +40,14 @@ class Gem::CommandManager
|
|||
@command_manager ||= new
|
||||
end
|
||||
|
||||
##
|
||||
# Returns self. Allows a CommandManager instance to stand
|
||||
# in for the class itself.
|
||||
|
||||
def instance
|
||||
self
|
||||
end
|
||||
|
||||
##
|
||||
# Reset the authoritative instance of the command manager.
|
||||
|
||||
|
@ -63,6 +74,7 @@ class Gem::CommandManager
|
|||
register_command :install
|
||||
register_command :list
|
||||
register_command :lock
|
||||
register_command :mirror
|
||||
register_command :outdated
|
||||
register_command :owner
|
||||
register_command :pristine
|
||||
|
@ -78,13 +90,14 @@ class Gem::CommandManager
|
|||
register_command :unpack
|
||||
register_command :update
|
||||
register_command :which
|
||||
register_command :yank
|
||||
end
|
||||
|
||||
##
|
||||
# Register the Symbol +command+ as a gem command.
|
||||
|
||||
def register_command(command)
|
||||
@commands[command] = false
|
||||
def register_command(command, obj=false)
|
||||
@commands[command] = obj
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -95,7 +108,7 @@ class Gem::CommandManager
|
|||
end
|
||||
|
||||
##
|
||||
# Return the registered command from the command name.
|
||||
# Returns a Command instance for +command_name+
|
||||
|
||||
def [](command_name)
|
||||
command_name = command_name.intern
|
||||
|
@ -104,56 +117,69 @@ class Gem::CommandManager
|
|||
end
|
||||
|
||||
##
|
||||
# Return a sorted list of all command names (as strings).
|
||||
# Return a sorted list of all command names as strings.
|
||||
|
||||
def command_names
|
||||
@commands.keys.collect {|key| key.to_s}.sort
|
||||
end
|
||||
|
||||
##
|
||||
# Run the config specified by +args+.
|
||||
# Run the command specified by +args+.
|
||||
|
||||
def run(args)
|
||||
process_args(args)
|
||||
def run(args, build_args=nil)
|
||||
process_args(args, build_args)
|
||||
rescue StandardError, Timeout::Error => ex
|
||||
alert_error "While executing gem ... (#{ex.class})\n #{ex.to_s}"
|
||||
ui.errs.puts "\t#{ex.backtrace.join "\n\t"}" if
|
||||
Gem.configuration.backtrace
|
||||
ui.backtrace ex
|
||||
|
||||
if Gem.configuration.really_verbose and \
|
||||
ex.kind_of?(Gem::Exception) and ex.source_exception
|
||||
e = ex.source_exception
|
||||
|
||||
ui.errs.puts "Because of: (#{e.class})\n #{e.to_s}"
|
||||
ui.backtrace e
|
||||
end
|
||||
|
||||
terminate_interaction(1)
|
||||
rescue Interrupt
|
||||
alert_error "Interrupted"
|
||||
terminate_interaction(1)
|
||||
end
|
||||
|
||||
def process_args(args)
|
||||
def process_args(args, build_args=nil)
|
||||
args = args.to_str.split(/\s+/) if args.respond_to?(:to_str)
|
||||
if args.size == 0
|
||||
|
||||
if args.empty? then
|
||||
say Gem::Command::HELP
|
||||
terminate_interaction(1)
|
||||
terminate_interaction 1
|
||||
end
|
||||
case args[0]
|
||||
when '-h', '--help'
|
||||
|
||||
case args.first
|
||||
when '-h', '--help' then
|
||||
say Gem::Command::HELP
|
||||
terminate_interaction(0)
|
||||
when '-v', '--version'
|
||||
terminate_interaction 0
|
||||
when '-v', '--version' then
|
||||
say Gem::VERSION
|
||||
terminate_interaction(0)
|
||||
when /^-/
|
||||
alert_error "Invalid option: #{args[0]}. See 'gem --help'."
|
||||
terminate_interaction(1)
|
||||
terminate_interaction 0
|
||||
when /^-/ then
|
||||
alert_error "Invalid option: #{args.first}. See 'gem --help'."
|
||||
terminate_interaction 1
|
||||
else
|
||||
cmd_name = args.shift.downcase
|
||||
cmd = find_command(cmd_name)
|
||||
cmd.invoke(*args)
|
||||
cmd = find_command cmd_name
|
||||
cmd.invoke_with_build_args args, build_args
|
||||
end
|
||||
end
|
||||
|
||||
def find_command(cmd_name)
|
||||
possibilities = find_command_possibilities cmd_name
|
||||
|
||||
if possibilities.size > 1 then
|
||||
raise "Ambiguous command #{cmd_name} matches [#{possibilities.join(', ')}]"
|
||||
elsif possibilities.size < 1 then
|
||||
raise "Unknown command #{cmd_name}"
|
||||
raise Gem::CommandLineError,
|
||||
"Ambiguous command #{cmd_name} " \
|
||||
"matches [#{possibilities.join(', ')}]"
|
||||
elsif possibilities.empty? then
|
||||
raise Gem::CommandLineError, "Unknown command #{cmd_name}"
|
||||
end
|
||||
|
||||
self[possibilities.first]
|
||||
|
@ -162,7 +188,11 @@ class Gem::CommandManager
|
|||
def find_command_possibilities(cmd_name)
|
||||
len = cmd_name.length
|
||||
|
||||
command_names.select { |n| cmd_name == n[0, len] }
|
||||
found = command_names.select { |name| cmd_name == name[0, len] }
|
||||
|
||||
exact = found.find { |name| name == cmd_name }
|
||||
|
||||
exact ? [exact] : found
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -170,23 +200,20 @@ class Gem::CommandManager
|
|||
def load_and_instantiate(command_name)
|
||||
command_name = command_name.to_s
|
||||
const_name = command_name.capitalize.gsub(/_(.)/) { $1.upcase } << "Command"
|
||||
commands = Gem::Commands
|
||||
retried = false
|
||||
load_error = nil
|
||||
|
||||
begin
|
||||
commands.const_get(const_name).new
|
||||
rescue NameError
|
||||
raise if retried
|
||||
|
||||
retried = true
|
||||
begin
|
||||
require "rubygems/commands/#{command_name}_command"
|
||||
rescue Exception => e
|
||||
alert_error "Loading command: #{command_name} (#{e.class})\n #{e}"
|
||||
ui.errs.puts "\t#{e.backtrace.join "\n\t"}" if
|
||||
Gem.configuration.backtrace
|
||||
rescue LoadError => e
|
||||
load_error = e
|
||||
end
|
||||
retry
|
||||
Gem::Commands.const_get(const_name).new
|
||||
rescue Exception => e
|
||||
e = load_error if load_error
|
||||
|
||||
alert_error "Loading command: #{command_name} (#{e.class})\n\t#{e}"
|
||||
ui.backtrace e
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
require 'rubygems/command'
|
||||
require 'rubygems/builder'
|
||||
require 'rubygems/package'
|
||||
|
||||
class Gem::Commands::BuildCommand < Gem::Command
|
||||
|
||||
|
@ -22,11 +22,11 @@ class Gem::Commands::BuildCommand < Gem::Command
|
|||
def execute
|
||||
gemspec = get_one_gem_name
|
||||
|
||||
if File.exist? gemspec
|
||||
spec = load_gemspec gemspec
|
||||
if File.exist? gemspec then
|
||||
spec = Gem::Specification.load gemspec
|
||||
|
||||
if spec then
|
||||
Gem::Builder.new(spec).build options[:force]
|
||||
Gem::Package.build spec, options[:force]
|
||||
else
|
||||
alert_error "Error loading gemspec. Aborting."
|
||||
terminate_interaction 1
|
||||
|
@ -37,23 +37,5 @@ class Gem::Commands::BuildCommand < Gem::Command
|
|||
end
|
||||
end
|
||||
|
||||
def load_gemspec filename
|
||||
if yaml?(filename)
|
||||
open(filename) do |f|
|
||||
begin
|
||||
Gem::Specification.from_yaml(f)
|
||||
rescue Gem::EndOfYAMLException
|
||||
nil
|
||||
end
|
||||
end
|
||||
else
|
||||
Gem::Specification.load(filename) # can return nil
|
||||
end
|
||||
end
|
||||
|
||||
def yaml?(filename)
|
||||
line = open(filename) { |f| line = f.gets }
|
||||
result = line =~ %r{!ruby/object:Gem::Specification}
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -4,82 +4,224 @@ require 'rubygems/security'
|
|||
class Gem::Commands::CertCommand < Gem::Command
|
||||
|
||||
def initialize
|
||||
super 'cert', 'Manage RubyGems certificates and signing settings'
|
||||
super 'cert', 'Manage RubyGems certificates and signing settings',
|
||||
:add => [], :remove => [], :list => [], :build => [], :sign => []
|
||||
|
||||
add_option('-a', '--add CERT',
|
||||
'Add a trusted certificate.') do |value, options|
|
||||
cert = OpenSSL::X509::Certificate.new(File.read(value))
|
||||
Gem::Security.add_trusted_cert(cert)
|
||||
say "Added '#{cert.subject.to_s}'"
|
||||
end
|
||||
|
||||
add_option('-l', '--list',
|
||||
'List trusted certificates.') do |value, options|
|
||||
glob_str = File::join(Gem::Security::OPT[:trust_dir], '*.pem')
|
||||
Dir::glob(glob_str) do |path|
|
||||
begin
|
||||
cert = OpenSSL::X509::Certificate.new(File.read(path))
|
||||
# this could probably be formatted more gracefully
|
||||
say cert.subject.to_s
|
||||
rescue OpenSSL::X509::CertificateError
|
||||
next
|
||||
end
|
||||
OptionParser.accept OpenSSL::X509::Certificate do |certificate|
|
||||
begin
|
||||
OpenSSL::X509::Certificate.new File.read certificate
|
||||
rescue Errno::ENOENT
|
||||
raise OptionParser::InvalidArgument, "#{certificate}: does not exist"
|
||||
rescue OpenSSL::X509::CertificateError
|
||||
raise OptionParser::InvalidArgument,
|
||||
"#{certificate}: invalid X509 certificate"
|
||||
end
|
||||
end
|
||||
|
||||
add_option('-r', '--remove STRING',
|
||||
'Remove trusted certificates containing',
|
||||
'STRING.') do |value, options|
|
||||
trust_dir = Gem::Security::OPT[:trust_dir]
|
||||
glob_str = File::join(trust_dir, '*.pem')
|
||||
|
||||
Dir::glob(glob_str) do |path|
|
||||
begin
|
||||
cert = OpenSSL::X509::Certificate.new(File.read(path))
|
||||
if cert.subject.to_s.downcase.index(value)
|
||||
say "Removed '#{cert.subject.to_s}'"
|
||||
File.unlink(path)
|
||||
end
|
||||
rescue OpenSSL::X509::CertificateError
|
||||
next
|
||||
end
|
||||
OptionParser.accept OpenSSL::PKey::RSA do |key_file|
|
||||
begin
|
||||
key = OpenSSL::PKey::RSA.new File.read key_file
|
||||
rescue Errno::ENOENT
|
||||
raise OptionParser::InvalidArgument, "#{key_file}: does not exist"
|
||||
rescue OpenSSL::PKey::RSAError
|
||||
raise OptionParser::InvalidArgument, "#{key_file}: invalid RSA key"
|
||||
end
|
||||
|
||||
raise OptionParser::InvalidArgument,
|
||||
"#{key_file}: private key not found" unless key.private?
|
||||
|
||||
key
|
||||
end
|
||||
|
||||
add_option('-a', '--add CERT', OpenSSL::X509::Certificate,
|
||||
'Add a trusted certificate.') do |cert, options|
|
||||
options[:add] << cert
|
||||
end
|
||||
|
||||
add_option('-l', '--list [FILTER]',
|
||||
'List trusted certificates where the',
|
||||
'subject contains FILTER') do |filter, options|
|
||||
filter ||= ''
|
||||
|
||||
options[:list] << filter
|
||||
end
|
||||
|
||||
add_option('-r', '--remove FILTER',
|
||||
'Remove trusted certificates where the',
|
||||
'subject contains FILTER') do |filter, options|
|
||||
options[:remove] << filter
|
||||
end
|
||||
|
||||
add_option('-b', '--build EMAIL_ADDR',
|
||||
'Build private key and self-signed',
|
||||
'certificate for EMAIL_ADDR.') do |value, options|
|
||||
vals = Gem::Security.build_self_signed_cert(value)
|
||||
FileUtils.chmod 0600, vals[:key_path]
|
||||
say "Public Cert: #{vals[:cert_path]}"
|
||||
say "Private Key: #{vals[:key_path]}"
|
||||
say "Don't forget to move the key file to somewhere private..."
|
||||
'certificate for EMAIL_ADDR') do |email_address, options|
|
||||
options[:build] << email_address
|
||||
end
|
||||
|
||||
add_option('-C', '--certificate CERT',
|
||||
'Certificate for --sign command.') do |value, options|
|
||||
cert = OpenSSL::X509::Certificate.new(File.read(value))
|
||||
add_option('-C', '--certificate CERT', OpenSSL::X509::Certificate,
|
||||
'Signing certificate for --sign') do |cert, options|
|
||||
options[:issuer_cert] = cert
|
||||
end
|
||||
|
||||
add_option('-K', '--private-key KEY',
|
||||
'Private key for --sign command.') do |value, options|
|
||||
key = OpenSSL::PKey::RSA.new(File.read(value))
|
||||
options[:issuer_key] = key
|
||||
add_option('-K', '--private-key KEY', OpenSSL::PKey::RSA,
|
||||
'Key for --sign or --build') do |key, options|
|
||||
options[:key] = key
|
||||
end
|
||||
|
||||
add_option('-s', '--sign NEWCERT',
|
||||
'Sign a certificate with my key and',
|
||||
'certificate.') do |value, options|
|
||||
cert = OpenSSL::X509::Certificate.new(File.read(value))
|
||||
my_cert = options[:issuer_cert]
|
||||
my_key = options[:issuer_key]
|
||||
cert = Gem::Security.sign_cert(cert, my_key, my_cert)
|
||||
File.open(value, 'wb') { |file| file.write(cert.to_pem) }
|
||||
add_option('-s', '--sign CERT',
|
||||
'Signs CERT with the key from -K',
|
||||
'and the certificate from -C') do |cert_file, options|
|
||||
raise OptionParser::InvalidArgument, "#{cert_file}: does not exist" unless
|
||||
File.file? cert_file
|
||||
|
||||
options[:sign] << cert_file
|
||||
end
|
||||
end
|
||||
|
||||
def execute
|
||||
options[:add].each do |certificate|
|
||||
Gem::Security.trust_dir.trust_cert certificate
|
||||
|
||||
say "Added '#{certificate.subject}'"
|
||||
end
|
||||
|
||||
options[:remove].each do |filter|
|
||||
certificates_matching filter do |certificate, path|
|
||||
FileUtils.rm path
|
||||
say "Removed '#{certificate.subject}'"
|
||||
end
|
||||
end
|
||||
|
||||
options[:list].each do |filter|
|
||||
certificates_matching filter do |certificate, _|
|
||||
# this could probably be formatted more gracefully
|
||||
say certificate.subject.to_s
|
||||
end
|
||||
end
|
||||
|
||||
options[:build].each do |name|
|
||||
build name
|
||||
end
|
||||
|
||||
unless options[:sign].empty? then
|
||||
load_default_cert unless options[:issuer_cert]
|
||||
load_default_key unless options[:key]
|
||||
end
|
||||
|
||||
options[:sign].each do |cert_file|
|
||||
sign cert_file
|
||||
end
|
||||
end
|
||||
|
||||
def build name
|
||||
key = options[:key] || Gem::Security.create_key
|
||||
|
||||
cert = Gem::Security.create_cert_email name, key
|
||||
|
||||
key_path = Gem::Security.write key, "gem-private_key.pem"
|
||||
cert_path = Gem::Security.write cert, "gem-public_cert.pem"
|
||||
|
||||
say "Certificate: #{cert_path}"
|
||||
say "Private Key: #{key_path}"
|
||||
say "Don't forget to move the key file to somewhere private!"
|
||||
end
|
||||
|
||||
def certificates_matching filter
|
||||
return enum_for __method__, filter unless block_given?
|
||||
|
||||
Gem::Security.trusted_certificates.select do |certificate, _|
|
||||
subject = certificate.subject.to_s
|
||||
subject.downcase.index filter
|
||||
end.sort_by do |certificate, _|
|
||||
certificate.subject.to_a.map { |name, data,| [name, data] }
|
||||
end.each do |certificate, path|
|
||||
yield certificate, path
|
||||
end
|
||||
end
|
||||
|
||||
def description # :nodoc:
|
||||
<<-EOF
|
||||
The cert command manages signing keys and certificates for creating signed
|
||||
gems. Your signing certificate and private key are typically stored in
|
||||
~/.gem/gem-public_cert.pem and ~/.gem/gem-private_key.pem respectively.
|
||||
|
||||
To build a certificate for signing gems:
|
||||
|
||||
gem cert --build you@example
|
||||
|
||||
If you already have an RSA key, or are creating a new certificate for an
|
||||
existing key:
|
||||
|
||||
gem cert --build you@example --private-key /path/to/key.pem
|
||||
|
||||
If you wish to trust a certificate you can add it to the trust list with:
|
||||
|
||||
gem cert --add /path/to/cert.pem
|
||||
|
||||
You can list trusted certificates with:
|
||||
|
||||
gem cert --list
|
||||
|
||||
or:
|
||||
|
||||
gem cert --list cert_subject_substring
|
||||
|
||||
If you wish to remove a previously trusted certificate:
|
||||
|
||||
gem cert --remove cert_subject_substring
|
||||
|
||||
To sign another gem author's certificate:
|
||||
|
||||
gem cert --sign /path/to/other_cert.pem
|
||||
|
||||
For further reading on signing gems see `ri Gem::Security`.
|
||||
EOF
|
||||
end
|
||||
|
||||
def load_default_cert
|
||||
cert_file = File.join Gem.user_home, 'gem-public_cert.pem'
|
||||
cert = File.read cert_file
|
||||
options[:issuer_cert] = OpenSSL::X509::Certificate.new cert
|
||||
rescue Errno::ENOENT
|
||||
alert_error \
|
||||
"--certificate not specified and ~/.gem/gem-public_cert.pem does not exist"
|
||||
|
||||
terminate_interaction 1
|
||||
rescue OpenSSL::X509::CertificateError
|
||||
alert_error \
|
||||
"--certificate not specified and ~/.gem/gem-public_cert.pem is not valid"
|
||||
|
||||
terminate_interaction 1
|
||||
end
|
||||
|
||||
def load_default_key
|
||||
key_file = File.join Gem.user_home, 'gem-private_key.pem'
|
||||
key = File.read key_file
|
||||
options[:key] = OpenSSL::PKey::RSA.new key
|
||||
rescue Errno::ENOENT
|
||||
alert_error \
|
||||
"--private-key not specified and ~/.gem/gem-private_key.pem does not exist"
|
||||
|
||||
terminate_interaction 1
|
||||
rescue OpenSSL::PKey::RSAError
|
||||
alert_error \
|
||||
"--private-key not specified and ~/.gem/gem-private_key.pem is not valid"
|
||||
|
||||
terminate_interaction 1
|
||||
end
|
||||
|
||||
def sign cert_file
|
||||
cert = File.read cert_file
|
||||
cert = OpenSSL::X509::Certificate.new cert
|
||||
|
||||
permissions = File.stat(cert_file).mode & 0777
|
||||
|
||||
issuer_cert = options[:issuer_cert]
|
||||
issuer_key = options[:key]
|
||||
|
||||
cert = Gem::Security.sign cert, issuer_key, issuer_cert
|
||||
|
||||
Gem::Security.write cert, cert_file, permissions
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -8,13 +8,7 @@ class Gem::Commands::CheckCommand < Gem::Command
|
|||
|
||||
def initialize
|
||||
super 'check', 'Check installed gems',
|
||||
:verify => false, :alien => false
|
||||
|
||||
add_option( '--verify FILE',
|
||||
'Verify gem file against its internal',
|
||||
'checksum') do |value, options|
|
||||
options[:verify] = value
|
||||
end
|
||||
:alien => true
|
||||
|
||||
add_option('-a', '--alien', "Report 'unmanaged' or rogue files in the",
|
||||
"gem repository") do |value, options|
|
||||
|
@ -25,40 +19,21 @@ class Gem::Commands::CheckCommand < Gem::Command
|
|||
end
|
||||
|
||||
def execute
|
||||
if options[:alien]
|
||||
say "Performing the 'alien' operation"
|
||||
say
|
||||
gems = get_all_gem_names rescue []
|
||||
Gem::Validator.new.alien(gems).sort.each do |key, val|
|
||||
unless val.empty? then
|
||||
say "#{key} has #{val.size} problems"
|
||||
val.each do |error_entry|
|
||||
say " #{error_entry.path}:"
|
||||
say " #{error_entry.problem}"
|
||||
end
|
||||
else
|
||||
say "#{key} is error-free" if Gem.configuration.verbose
|
||||
end
|
||||
say
|
||||
end
|
||||
end
|
||||
say "Checking gems..."
|
||||
say
|
||||
gems = get_all_gem_names rescue []
|
||||
|
||||
if options[:verify]
|
||||
gem_name = options[:verify]
|
||||
unless gem_name
|
||||
alert_error "Must specify a .gem file with --verify NAME"
|
||||
return
|
||||
end
|
||||
unless File.exist?(gem_name)
|
||||
alert_error "Unknown file: #{gem_name}."
|
||||
return
|
||||
end
|
||||
say "Verifying gem: '#{gem_name}'"
|
||||
begin
|
||||
Gem::Validator.new.verify_gem_file(gem_name)
|
||||
rescue Exception
|
||||
alert_error "#{gem_name} is invalid."
|
||||
Gem::Validator.new.alien(gems).sort.each do |key, val|
|
||||
unless val.empty? then
|
||||
say "#{key} has #{val.size} problems"
|
||||
val.each do |error_entry|
|
||||
say " #{error_entry.path}:"
|
||||
say " #{error_entry.problem}"
|
||||
end
|
||||
else
|
||||
say "#{key} is error-free" if Gem.configuration.verbose
|
||||
end
|
||||
say
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -26,6 +26,9 @@ class Gem::Commands::CleanupCommand < Gem::Command
|
|||
<<-EOF
|
||||
The cleanup command removes old gems from GEM_HOME. If an older version is
|
||||
installed elsewhere in GEM_PATH the cleanup command won't touch it.
|
||||
|
||||
Older gems that are required to satisify the dependencies of gems
|
||||
are not removed.
|
||||
EOF
|
||||
end
|
||||
|
||||
|
@ -56,6 +59,8 @@ installed elsewhere in GEM_PATH the cleanup command won't touch it.
|
|||
primary_gems[spec.name].version != spec.version
|
||||
}
|
||||
|
||||
full = Gem::DependencyList.from_specs
|
||||
|
||||
deplist = Gem::DependencyList.new
|
||||
gems_to_cleanup.uniq.each do |spec| deplist.add spec end
|
||||
|
||||
|
@ -64,6 +69,8 @@ installed elsewhere in GEM_PATH the cleanup command won't touch it.
|
|||
original_path = Gem.path
|
||||
|
||||
deps.each do |spec|
|
||||
next unless full.ok_to_remove?(spec.full_name)
|
||||
|
||||
if options[:dryrun] then
|
||||
say "Dry Run Mode: Would uninstall #{spec.full_name}"
|
||||
else
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
require 'English'
|
||||
require 'rubygems/command'
|
||||
require 'rubygems/version_option'
|
||||
|
||||
|
@ -80,19 +81,36 @@ class Gem::Commands::ContentsCommand < Gem::Command
|
|||
terminate_interaction 1 if gem_names.length == 1
|
||||
end
|
||||
|
||||
gem_path = spec.full_gem_path
|
||||
extra = "/{#{spec.require_paths.join ','}}" if options[:lib_only]
|
||||
glob = "#{gem_path}#{extra}/**/*"
|
||||
files = Dir[glob]
|
||||
if spec.default_gem?
|
||||
files = spec.files.map do |file|
|
||||
case file
|
||||
when /\A#{spec.bindir}\//
|
||||
[Gem::ConfigMap[:bindir], $POSTMATCH]
|
||||
when /\.so\z/
|
||||
[Gem::ConfigMap[:archdir], file]
|
||||
else
|
||||
[Gem::ConfigMap[:rubylibdir], file]
|
||||
end
|
||||
end
|
||||
else
|
||||
gem_path = spec.full_gem_path
|
||||
extra = "/{#{spec.require_paths.join ','}}" if options[:lib_only]
|
||||
glob = "#{gem_path}#{extra}/**/*"
|
||||
prefix_re = /#{Regexp.escape(gem_path)}\//
|
||||
files = Dir[glob].map do |file|
|
||||
[gem_path, file.sub(prefix_re, "")]
|
||||
end
|
||||
end
|
||||
|
||||
gem_path = File.join gem_path, '' # add trailing / if missing
|
||||
files.sort.each do |prefix, basename|
|
||||
absolute_path = File.join(prefix, basename)
|
||||
next if File.directory? absolute_path
|
||||
|
||||
files.sort.each do |file|
|
||||
next if File.directory? file
|
||||
|
||||
file = file.sub gem_path, '' unless options[:prefix]
|
||||
|
||||
say file
|
||||
if options[:prefix]
|
||||
say absolute_path
|
||||
else
|
||||
say basename
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -71,14 +71,9 @@ class Gem::Commands::DependencyCommand < Gem::Command
|
|||
if remote? and not options[:reverse_dependencies] then
|
||||
fetcher = Gem::SpecFetcher.fetcher
|
||||
|
||||
# REFACTOR: fetcher.find_specs_matching => specs
|
||||
specs_and_sources = fetcher.find_matching(dependency,
|
||||
dependency.specific?, true,
|
||||
dependency.prerelease?)
|
||||
ss, _ = fetcher.spec_for_dependency dependency
|
||||
|
||||
specs.concat specs_and_sources.map { |spec_tuple, source_uri|
|
||||
fetcher.fetch_spec spec_tuple, URI.parse(source_uri)
|
||||
}
|
||||
ss.each { |s,o| specs << s }
|
||||
end
|
||||
|
||||
if specs.empty? then
|
||||
|
|
|
@ -24,33 +24,38 @@ class Gem::Commands::EnvironmentCommand < Gem::Command
|
|||
The RubyGems environment can be controlled through command line arguments,
|
||||
gemrc files, environment variables and built-in defaults.
|
||||
|
||||
Command line argument defaults and some RubyGems defaults can be set in
|
||||
~/.gemrc file for individual users and a /etc/gemrc for all users. A gemrc
|
||||
is a YAML file with the following YAML keys:
|
||||
Command line argument defaults and some RubyGems defaults can be set in a
|
||||
~/.gemrc file for individual users and a /etc/gemrc for all users. These
|
||||
files are YAML files with the following YAML keys:
|
||||
|
||||
:sources: A YAML array of remote gem repositories to install gems from
|
||||
:verbose: Verbosity of the gem command. false, true, and :really are the
|
||||
:verbose: Verbosity of the gem command. false, true, and :really are the
|
||||
levels
|
||||
:update_sources: Enable/disable automatic updating of repository metadata
|
||||
:backtrace: Print backtrace when RubyGems encounters an error
|
||||
:gempath: The paths in which to look for gems
|
||||
gem_command: A string containing arguments for the specified gem command
|
||||
:disable_default_gem_server: Force specification of gem server host on push
|
||||
<gem_command>: A string containing arguments for the specified gem command
|
||||
|
||||
Example:
|
||||
|
||||
:verbose: false
|
||||
install: --no-wrappers
|
||||
update: --no-wrappers
|
||||
:disable_default_gem_server: true
|
||||
|
||||
RubyGems' default local repository can be overridden with the GEM_PATH and
|
||||
GEM_HOME environment variables. GEM_HOME sets the default repository to
|
||||
install into. GEM_PATH allows multiple local repositories to be searched for
|
||||
GEM_HOME environment variables. GEM_HOME sets the default repository to
|
||||
install into. GEM_PATH allows multiple local repositories to be searched for
|
||||
gems.
|
||||
|
||||
If you are behind a proxy server, RubyGems uses the HTTP_PROXY,
|
||||
HTTP_PROXY_USER and HTTP_PROXY_PASS environment variables to discover the
|
||||
proxy server.
|
||||
|
||||
If you would like to push gems to a private gem server the RUBYGEMS_HOST
|
||||
environment variable can be set to the URI for that server.
|
||||
|
||||
If you are packaging RubyGems all of RubyGems' defaults are in
|
||||
lib/rubygems/defaults.rb. You may override these in
|
||||
lib/rubygems/defaults/operating_system.rb
|
||||
|
@ -74,7 +79,7 @@ lib/rubygems/defaults/operating_system.rb
|
|||
when /^gempath/, /^path/, /^GEM_PATH/ then
|
||||
out << Gem.path.join(File::PATH_SEPARATOR)
|
||||
when /^remotesources/ then
|
||||
out << Gem.sources.join("\n")
|
||||
out << Gem.sources.to_a.join("\n")
|
||||
when /^platform/ then
|
||||
out << Gem.platforms.join(File::PATH_SEPARATOR)
|
||||
when nil then
|
||||
|
|
|
@ -34,7 +34,6 @@ class Gem::Commands::FetchCommand < Gem::Command
|
|||
|
||||
def execute
|
||||
version = options[:version] || Gem::Requirement.default
|
||||
all = Gem::Requirement.default != version
|
||||
|
||||
platform = Gem.platforms.last
|
||||
gem_names = get_all_gem_names
|
||||
|
@ -43,32 +42,20 @@ class Gem::Commands::FetchCommand < Gem::Command
|
|||
dep = Gem::Dependency.new gem_name, version
|
||||
dep.prerelease = options[:prerelease]
|
||||
|
||||
specs_and_sources, errors =
|
||||
Gem::SpecFetcher.fetcher.fetch_with_errors(dep, all, true,
|
||||
dep.prerelease?)
|
||||
|
||||
specs_and_sources, errors = Gem::SpecFetcher.fetcher.spec_for_dependency dep
|
||||
if platform then
|
||||
filtered = specs_and_sources.select { |s,| s.platform == platform }
|
||||
specs_and_sources = filtered unless filtered.empty?
|
||||
end
|
||||
|
||||
spec, source_uri = specs_and_sources.sort_by { |s,| s.version }.last
|
||||
spec, source = specs_and_sources.sort_by { |s,| s.version }.first
|
||||
|
||||
if spec.nil? then
|
||||
show_lookup_failure gem_name, version, errors, options[:domain]
|
||||
next
|
||||
end
|
||||
|
||||
file = "#{spec.full_name}.gem"
|
||||
remote_path = URI.parse(source_uri) + "gems/#{file}"
|
||||
|
||||
fetch = Gem::RemoteFetcher.fetcher
|
||||
|
||||
gem = fetch.fetch_path remote_path.to_s
|
||||
|
||||
File.open file, "wb" do |f|
|
||||
f.write gem
|
||||
end
|
||||
source.download spec
|
||||
|
||||
say "Downloaded #{spec.full_name}"
|
||||
end
|
||||
|
|
|
@ -11,29 +11,16 @@ class Gem::Commands::GenerateIndexCommand < Gem::Command
|
|||
def initialize
|
||||
super 'generate_index',
|
||||
'Generates the index files for a gem server directory',
|
||||
:directory => '.', :build_legacy => true, :build_modern => true
|
||||
:directory => '.', :build_modern => true
|
||||
|
||||
add_option '-d', '--directory=DIRNAME',
|
||||
'repository base dir containing gems subdir' do |dir, options|
|
||||
options[:directory] = File.expand_path dir
|
||||
end
|
||||
|
||||
add_option '--[no-]legacy',
|
||||
'Generate Marshal.4.8' do |value, options|
|
||||
unless options[:build_modern] or value then
|
||||
raise OptionParser::InvalidOption, 'no indicies will be built'
|
||||
end
|
||||
|
||||
options[:build_legacy] = value
|
||||
end
|
||||
|
||||
add_option '--[no-]modern',
|
||||
'Generate indexes for RubyGems newer',
|
||||
'than 1.2.0' do |value, options|
|
||||
unless options[:build_legacy] or value then
|
||||
raise OptionParser::InvalidOption, 'no indicies will be built'
|
||||
end
|
||||
|
||||
'Generate indexes for RubyGems',
|
||||
'(always true)' do |value, options|
|
||||
options[:build_modern] = value
|
||||
end
|
||||
|
||||
|
@ -42,27 +29,10 @@ class Gem::Commands::GenerateIndexCommand < Gem::Command
|
|||
'since the last update' do |value, options|
|
||||
options[:update] = value
|
||||
end
|
||||
|
||||
add_option :RSS, '--rss-gems-host=GEM_HOST',
|
||||
'Host name where gems are served from,',
|
||||
'used for GUID and enclosure values' do |value, options|
|
||||
options[:rss_gems_host] = value
|
||||
end
|
||||
|
||||
add_option :RSS, '--rss-host=HOST',
|
||||
'Host name for more gems information,',
|
||||
'used for RSS feed link' do |value, options|
|
||||
options[:rss_host] = value
|
||||
end
|
||||
|
||||
add_option :RSS, '--rss-title=TITLE',
|
||||
'Set title for RSS feed' do |value, options|
|
||||
options[:rss_title] = value
|
||||
end
|
||||
end
|
||||
|
||||
def defaults_str # :nodoc:
|
||||
"--directory . --legacy --modern"
|
||||
"--directory . --modern"
|
||||
end
|
||||
|
||||
def description # :nodoc:
|
||||
|
@ -85,25 +55,15 @@ When done, it will generate a set of files like this:
|
|||
prerelease_specs.<version>.gz # prerelease specs index
|
||||
quick/Marshal.<version>/<gemname>.gemspec.rz # Marshal quick index file
|
||||
|
||||
# these files support legacy RubyGems
|
||||
Marshal.<version>
|
||||
Marshal.<version>.Z # Marshal full index
|
||||
|
||||
The .Z and .rz extension files are compressed with the inflate algorithm.
|
||||
The .rz extension files are compressed with the inflate algorithm.
|
||||
The Marshal version number comes from ruby's Marshal::MAJOR_VERSION and
|
||||
Marshal::MINOR_VERSION constants. It is used to ensure compatibility.
|
||||
|
||||
If --rss-host and --rss-gem-host are given an RSS feed will be generated at
|
||||
index.rss containing gems released in the last two days.
|
||||
EOF
|
||||
end
|
||||
|
||||
def execute
|
||||
if options[:update] and
|
||||
(options[:rss_host] or options[:rss_gems_host]) then
|
||||
alert_error '--update not compatible with RSS generation'
|
||||
terminate_interaction 1
|
||||
end
|
||||
# This is always true becasue it's the only way now.
|
||||
options[:build_modern] = true
|
||||
|
||||
if not File.exist?(options[:directory]) or
|
||||
not File.directory?(options[:directory]) then
|
||||
|
|
|
@ -37,7 +37,7 @@ Some examples of 'gem' usage.
|
|||
|
||||
* Create a gem:
|
||||
|
||||
See http://rubygems.rubyforge.org/wiki/wiki.pl?CreateAGemInTenMinutes
|
||||
See http://guides.rubygems.org/make-your-own-gem/
|
||||
|
||||
* See information about RubyGems:
|
||||
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
require 'rubygems/command'
|
||||
require 'rubygems/doc_manager'
|
||||
require 'rubygems/install_update_options'
|
||||
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
|
||||
|
@ -13,14 +14,14 @@ require 'rubygems/version_option'
|
|||
|
||||
class Gem::Commands::InstallCommand < Gem::Command
|
||||
|
||||
attr_reader :installed_specs # :nodoc:
|
||||
|
||||
include Gem::VersionOption
|
||||
include Gem::LocalRemoteOptions
|
||||
include Gem::InstallUpdateOptions
|
||||
|
||||
def initialize
|
||||
defaults = Gem::DependencyInstaller::DEFAULT_OPTIONS.merge({
|
||||
:generate_rdoc => true,
|
||||
:generate_ri => true,
|
||||
:format_executable => false,
|
||||
:version => Gem::Requirement.default,
|
||||
})
|
||||
|
@ -32,6 +33,14 @@ class Gem::Commands::InstallCommand < Gem::Command
|
|||
add_platform_option
|
||||
add_version_option
|
||||
add_prerelease_option "to be installed. (Only for listed gems)"
|
||||
|
||||
add_option(:"Install/Update", '-g', '--file FILE',
|
||||
'Read from a gem dependencies API file and',
|
||||
'install the listed gems') do |v,o|
|
||||
o[:gemdeps] = v
|
||||
end
|
||||
|
||||
@installed_specs = nil
|
||||
end
|
||||
|
||||
def arguments # :nodoc:
|
||||
|
@ -39,7 +48,7 @@ class Gem::Commands::InstallCommand < Gem::Command
|
|||
end
|
||||
|
||||
def defaults_str # :nodoc:
|
||||
"--both --version '#{Gem::Requirement.default}' --rdoc --ri --no-force\n" \
|
||||
"--both --version '#{Gem::Requirement.default}' --document --no-force\n" \
|
||||
"--install-dir #{Gem.dir}"
|
||||
end
|
||||
|
||||
|
@ -100,31 +109,73 @@ to write the specification by hand. For example:
|
|||
"#{program_name} GEMNAME [GEMNAME ...] [options] -- --build-flags"
|
||||
end
|
||||
|
||||
def execute
|
||||
if options[:include_dependencies] then
|
||||
alert "`gem install -y` is now default and will be removed"
|
||||
alert "use --ignore-dependencies to install only the gems you list"
|
||||
def install_from_gemdeps(gf)
|
||||
require 'rubygems/request_set'
|
||||
rs = Gem::RequestSet.new
|
||||
rs.load_gemdeps gf
|
||||
|
||||
rs.resolve
|
||||
|
||||
specs = rs.install options do |req, inst|
|
||||
s = req.full_spec
|
||||
|
||||
if inst
|
||||
say "Installing #{s.name} (#{s.version})"
|
||||
else
|
||||
say "Using #{s.name} (#{s.version})"
|
||||
end
|
||||
end
|
||||
|
||||
installed_gems = []
|
||||
@installed_specs = specs
|
||||
|
||||
raise Gem::SystemExitException, 0
|
||||
end
|
||||
|
||||
def execute
|
||||
if gf = options[:gemdeps] then
|
||||
install_from_gemdeps gf
|
||||
return
|
||||
end
|
||||
|
||||
@installed_specs = []
|
||||
|
||||
ENV.delete 'GEM_PATH' if options[:install_dir].nil? and RUBY_VERSION > '1.9'
|
||||
|
||||
if options[:install_dir] and options[:user_install]
|
||||
alert_error "Use --install-dir or --user-install but not both"
|
||||
terminate_interaction 1
|
||||
end
|
||||
|
||||
exit_code = 0
|
||||
|
||||
get_all_gem_names.each do |gem_name|
|
||||
if options[:version] != Gem::Requirement.default &&
|
||||
get_all_gem_names.size > 1 then
|
||||
alert_error "Can't use --version w/ multiple gems. Use name:ver instead."
|
||||
terminate_interaction 1
|
||||
end
|
||||
|
||||
|
||||
get_all_gem_names_and_versions.each do |gem_name, gem_version|
|
||||
gem_version ||= options[:version]
|
||||
|
||||
begin
|
||||
next if options[:conservative] and
|
||||
not Gem::Dependency.new(gem_name, options[:version]).matching_specs.empty?
|
||||
not Gem::Dependency.new(gem_name, gem_version).matching_specs.empty?
|
||||
|
||||
inst = Gem::DependencyInstaller.new options
|
||||
inst.install gem_name, options[:version]
|
||||
inst.install gem_name, Gem::Requirement.create(gem_version)
|
||||
|
||||
inst.installed_gems.each do |spec|
|
||||
say "Successfully installed #{spec.full_name}"
|
||||
@installed_specs.push(*inst.installed_gems)
|
||||
|
||||
next unless errs = inst.errors
|
||||
|
||||
errs.each do |x|
|
||||
next unless Gem::SourceFetchProblem === x
|
||||
|
||||
msg = "Unable to pull data from '#{x.source.uri}': #{x.error.message}"
|
||||
|
||||
alert_warning msg
|
||||
end
|
||||
|
||||
installed_gems.push(*inst.installed_gems)
|
||||
rescue Gem::InstallError => e
|
||||
alert_error "Error installing #{gem_name}:\n\t#{e.message}"
|
||||
exit_code |= 1
|
||||
|
@ -135,27 +186,9 @@ to write the specification by hand. For example:
|
|||
end
|
||||
end
|
||||
|
||||
unless installed_gems.empty? then
|
||||
gems = installed_gems.length == 1 ? 'gem' : 'gems'
|
||||
say "#{installed_gems.length} #{gems} installed"
|
||||
|
||||
# NOTE: *All* of the RI documents must be generated first. For some
|
||||
# reason, RI docs cannot be generated after any RDoc documents are
|
||||
# generated.
|
||||
|
||||
if options[:generate_ri] then
|
||||
installed_gems.each do |gem|
|
||||
Gem::DocManager.new(gem, options[:rdoc_args]).generate_ri
|
||||
end
|
||||
|
||||
Gem::DocManager.update_ri_cache
|
||||
end
|
||||
|
||||
if options[:generate_rdoc] then
|
||||
installed_gems.each do |gem|
|
||||
Gem::DocManager.new(gem, options[:rdoc_args]).generate_rdoc
|
||||
end
|
||||
end
|
||||
unless @installed_specs.empty? then
|
||||
gems = @installed_specs.length == 1 ? 'gem' : 'gems'
|
||||
say "#{@installed_specs.length} #{gems} installed"
|
||||
end
|
||||
|
||||
raise Gem::SystemExitException, exit_code
|
||||
|
|
|
@ -7,8 +7,9 @@ require 'rubygems/commands/query_command'
|
|||
|
||||
class Gem::Commands::ListCommand < Gem::Commands::QueryCommand
|
||||
|
||||
def initialize
|
||||
super 'list', 'Display gems whose name starts with STRING'
|
||||
def initialize(name = 'list',
|
||||
summary = 'Display gems whose name starts with STRING')
|
||||
super name, summary
|
||||
|
||||
remove_option('--name-matches')
|
||||
end
|
||||
|
@ -26,8 +27,9 @@ class Gem::Commands::ListCommand < Gem::Commands::QueryCommand
|
|||
end
|
||||
|
||||
def execute
|
||||
string = get_one_optional_argument || ''
|
||||
options[:name] = /^#{string}/i
|
||||
name = get_one_optional_argument || ''
|
||||
options[:name] = /^#{name}/i
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ generated.
|
|||
|
||||
Example:
|
||||
|
||||
gemlock rails-1.0.0 > lockdown.rb
|
||||
gem lock rails-1.0.0 > lockdown.rb
|
||||
|
||||
will produce in lockdown.rb:
|
||||
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
require 'rubygems/command'
|
||||
|
||||
class Gem::Commands::MirrorCommand < Gem::Command
|
||||
def initialize
|
||||
super('mirror', 'Mirror all gem files (requires rubygems-mirror)')
|
||||
begin
|
||||
Gem::Specification.find_by_name('rubygems-mirror').activate
|
||||
rescue Gem::LoadError
|
||||
# no-op
|
||||
end
|
||||
end
|
||||
|
||||
def execute
|
||||
alert_error "Install the rubygems-mirror gem for the mirror command"
|
||||
end
|
||||
|
||||
end
|
|
@ -19,12 +19,15 @@ class Gem::Commands::OutdatedCommand < Gem::Command
|
|||
Gem::Specification.outdated.sort.each do |name|
|
||||
local = Gem::Specification.find_all_by_name(name).max
|
||||
dep = Gem::Dependency.new local.name, ">= #{local.version}"
|
||||
remotes = Gem::SpecFetcher.fetcher.fetch dep
|
||||
remotes, _ = Gem::SpecFetcher.fetcher.spec_for_dependency dep
|
||||
|
||||
next if remotes.empty?
|
||||
|
||||
remote = remotes.last.first
|
||||
say "#{local.name} (#{local.version} < #{remote.version})"
|
||||
remotes.sort! { |a,b| a[0].version <=> b[0].version }
|
||||
|
||||
highest = remotes.last.first
|
||||
|
||||
say "#{local.name} (#{local.version} < #{highest.version})"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -14,6 +14,10 @@ class Gem::Commands::OwnerCommand < Gem::Command
|
|||
"GEM gem to manage owners for"
|
||||
end
|
||||
|
||||
def usage # :nodoc:
|
||||
"#{program_name} GEM"
|
||||
end
|
||||
|
||||
def initialize
|
||||
super 'owner', description
|
||||
add_proxy_option
|
||||
|
@ -63,12 +67,16 @@ class Gem::Commands::OwnerCommand < Gem::Command
|
|||
|
||||
def manage_owners method, name, owners
|
||||
owners.each do |owner|
|
||||
response = rubygems_api_request method, "api/v1/gems/#{name}/owners" do |request|
|
||||
request.set_form_data 'email' => owner
|
||||
request.add_field "Authorization", api_key
|
||||
end
|
||||
begin
|
||||
response = rubygems_api_request method, "api/v1/gems/#{name}/owners" do |request|
|
||||
request.set_form_data 'email' => owner
|
||||
request.add_field "Authorization", api_key
|
||||
end
|
||||
|
||||
with_response response
|
||||
with_response response
|
||||
rescue
|
||||
# ignore
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
require 'rubygems/command'
|
||||
require 'rubygems/format'
|
||||
require 'rubygems/package'
|
||||
require 'rubygems/installer'
|
||||
require 'rubygems/version_option'
|
||||
|
||||
|
@ -24,6 +24,11 @@ class Gem::Commands::PristineCommand < Gem::Command
|
|||
options[:extensions] = value
|
||||
end
|
||||
|
||||
add_option('--only-executables',
|
||||
'Only restore executables') do |value, options|
|
||||
options[:only_executables] = value
|
||||
end
|
||||
|
||||
add_version_option('restore to', 'pristine condition')
|
||||
end
|
||||
|
||||
|
@ -78,6 +83,11 @@ extensions.
|
|||
say "Restoring gems to pristine condition..."
|
||||
|
||||
specs.each do |spec|
|
||||
if spec.default_gem?
|
||||
say "Skipped #{spec.full_name}, it is a default gem"
|
||||
next
|
||||
end
|
||||
|
||||
unless spec.extensions.empty? or options[:extensions] then
|
||||
say "Skipped #{spec.full_name}, it needs to compile an extension"
|
||||
next
|
||||
|
@ -101,8 +111,13 @@ extensions.
|
|||
:wrappers => true,
|
||||
:force => true,
|
||||
:install_dir => spec.base_dir,
|
||||
:env_shebang => installer_env_shebang)
|
||||
installer.install
|
||||
:env_shebang => installer_env_shebang,
|
||||
:build_args => spec.build_args)
|
||||
if options[:only_executables] then
|
||||
installer.generate_bin
|
||||
else
|
||||
installer.install
|
||||
end
|
||||
|
||||
say "Restored #{spec.full_name}"
|
||||
end
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
require 'rubygems/command'
|
||||
require 'rubygems/local_remote_options'
|
||||
require 'rubygems/gemcutter_utilities'
|
||||
require 'rubygems/package'
|
||||
|
||||
class Gem::Commands::PushCommand < Gem::Command
|
||||
include Gem::LocalRemoteOptions
|
||||
|
@ -39,13 +40,23 @@ class Gem::Commands::PushCommand < Gem::Command
|
|||
def send_gem name
|
||||
args = [:post, "api/v1/gems"]
|
||||
|
||||
args << options[:host] if options[:host]
|
||||
|
||||
if Gem.latest_rubygems_version < Gem::Version.new(Gem::VERSION) then
|
||||
alert_error "Using beta/unreleased version of rubygems. Not pushing."
|
||||
terminate_interaction 1
|
||||
end
|
||||
|
||||
host = options[:host]
|
||||
unless host
|
||||
if gem_data = Gem::Package.new(name) then
|
||||
host = gem_data.spec.metadata['default_gem_server']
|
||||
end
|
||||
end
|
||||
|
||||
args << host if host
|
||||
|
||||
say "Pushing gem to #{host || Gem.host}..."
|
||||
|
||||
response = rubygems_api_request(*args) do |request|
|
||||
request.body = Gem.read_binary name
|
||||
request.add_field "Content-Length", request.body.size
|
||||
|
|
|
@ -21,6 +21,10 @@ class Gem::Commands::QueryCommand < Gem::Command
|
|||
options[:installed] = value
|
||||
end
|
||||
|
||||
add_option('-I', 'Equivalent to --no-installed') do |value, options|
|
||||
options[:installed] = false
|
||||
end
|
||||
|
||||
add_version_option command, "for use with --installed"
|
||||
|
||||
add_option('-n', '--name-matches REGEXP',
|
||||
|
@ -80,6 +84,7 @@ class Gem::Commands::QueryCommand < Gem::Command
|
|||
req = Gem::Requirement.default
|
||||
# TODO: deprecate for real
|
||||
dep = Gem::Deprecate.skip_during { Gem::Dependency.new name, req }
|
||||
dep.prerelease = prerelease
|
||||
|
||||
if local? then
|
||||
if prerelease and not both? then
|
||||
|
@ -97,7 +102,7 @@ class Gem::Commands::QueryCommand < Gem::Command
|
|||
}
|
||||
|
||||
spec_tuples = specs.map do |spec|
|
||||
[[spec.name, spec.version, spec.original_platform, spec], :local]
|
||||
[spec.name_tuple, spec]
|
||||
end
|
||||
|
||||
output_query_results spec_tuples
|
||||
|
@ -110,13 +115,27 @@ class Gem::Commands::QueryCommand < Gem::Command
|
|||
say
|
||||
end
|
||||
|
||||
all = options[:all]
|
||||
|
||||
fetcher = Gem::SpecFetcher.fetcher
|
||||
spec_tuples = fetcher.find_matching dep, all, false, prerelease
|
||||
|
||||
spec_tuples += fetcher.find_matching dep, false, false, true if
|
||||
prerelease and all
|
||||
type = if options[:all]
|
||||
if options[:prerelease]
|
||||
:complete
|
||||
else
|
||||
:released
|
||||
end
|
||||
elsif options[:prerelease]
|
||||
:prerelease
|
||||
else
|
||||
:latest
|
||||
end
|
||||
|
||||
if options[:name].source.empty?
|
||||
spec_tuples = fetcher.detect(type) { true }
|
||||
else
|
||||
spec_tuples = fetcher.detect(type) do |gem_name, ver, plat|
|
||||
options[:name] === gem_name
|
||||
end
|
||||
end
|
||||
|
||||
output_query_results spec_tuples
|
||||
end
|
||||
|
@ -135,32 +154,30 @@ class Gem::Commands::QueryCommand < Gem::Command
|
|||
output = []
|
||||
versions = Hash.new { |h,name| h[name] = [] }
|
||||
|
||||
spec_tuples.each do |spec_tuple, source_uri|
|
||||
versions[spec_tuple.first] << [spec_tuple, source_uri]
|
||||
spec_tuples.each do |spec_tuple, source|
|
||||
versions[spec_tuple.name] << [spec_tuple, source]
|
||||
end
|
||||
|
||||
versions = versions.sort_by do |(name,_),_|
|
||||
name.downcase
|
||||
versions = versions.sort_by do |(n,_),_|
|
||||
n.downcase
|
||||
end
|
||||
|
||||
versions.each do |gem_name, matching_tuples|
|
||||
matching_tuples = matching_tuples.sort_by do |(_, version,_),_|
|
||||
version
|
||||
end.reverse
|
||||
matching_tuples = matching_tuples.sort_by { |n,_| n.version }.reverse
|
||||
|
||||
platforms = Hash.new { |h,version| h[version] = [] }
|
||||
|
||||
matching_tuples.map do |(_, version, platform,_),_|
|
||||
platforms[version] << platform if platform
|
||||
matching_tuples.map do |n,_|
|
||||
platforms[n.version] << n.platform if n.platform
|
||||
end
|
||||
|
||||
seen = {}
|
||||
|
||||
matching_tuples.delete_if do |(_, version,_),_|
|
||||
if seen[version] then
|
||||
matching_tuples.delete_if do |n,_|
|
||||
if seen[n.version] then
|
||||
true
|
||||
else
|
||||
seen[version] = true
|
||||
seen[n.version] = true
|
||||
false
|
||||
end
|
||||
end
|
||||
|
@ -169,7 +186,7 @@ class Gem::Commands::QueryCommand < Gem::Command
|
|||
|
||||
if options[:versions] then
|
||||
list = if platforms.empty? or options[:details] then
|
||||
matching_tuples.map { |(_, version,_),_| version }.uniq
|
||||
matching_tuples.map { |n,_| n.version }.uniq
|
||||
else
|
||||
platforms.sort.reverse.map do |version, pls|
|
||||
if pls == [Gem::Platform::RUBY] then
|
||||
|
@ -188,12 +205,11 @@ class Gem::Commands::QueryCommand < Gem::Command
|
|||
if options[:details] then
|
||||
detail_tuple = matching_tuples.first
|
||||
|
||||
spec = if detail_tuple.first.length == 4 then
|
||||
detail_tuple.first.last
|
||||
else
|
||||
uri = URI.parse detail_tuple.last
|
||||
Gem::SpecFetcher.fetcher.fetch_spec detail_tuple.first, uri
|
||||
end
|
||||
spec = detail_tuple.last
|
||||
|
||||
unless spec.kind_of? Gem::Specification
|
||||
spec = spec.fetch_spec detail_tuple.first
|
||||
end
|
||||
|
||||
entry << "\n"
|
||||
|
||||
|
@ -243,9 +259,9 @@ class Gem::Commands::QueryCommand < Gem::Command
|
|||
entry << "\n" << " Installed at: #{loaded_from}"
|
||||
else
|
||||
label = 'Installed at'
|
||||
matching_tuples.each do |(_,version,_,s),|
|
||||
matching_tuples.each do |n,s|
|
||||
loaded_from = File.dirname File.dirname(s.loaded_from)
|
||||
entry << "\n" << " #{label} (#{version}): #{loaded_from}"
|
||||
entry << "\n" << " #{label} (#{n.version}): #{loaded_from}"
|
||||
label = ' ' * label.length
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
require 'rubygems/command'
|
||||
require 'rubygems/version_option'
|
||||
require 'rubygems/doc_manager'
|
||||
require 'rubygems/rdoc'
|
||||
|
||||
class Gem::Commands::RdocCommand < Gem::Command
|
||||
include Gem::VersionOption
|
||||
|
@ -8,7 +8,7 @@ class Gem::Commands::RdocCommand < Gem::Command
|
|||
def initialize
|
||||
super 'rdoc', 'Generates RDoc for pre-installed gems',
|
||||
:version => Gem::Requirement.default,
|
||||
:include_rdoc => true, :include_ri => true, :overwrite => false
|
||||
:include_rdoc => false, :include_ri => true, :overwrite => false
|
||||
|
||||
add_option('--all',
|
||||
'Generate RDoc/RI documentation for all',
|
||||
|
@ -39,7 +39,7 @@ class Gem::Commands::RdocCommand < Gem::Command
|
|||
end
|
||||
|
||||
def defaults_str # :nodoc:
|
||||
"--version '#{Gem::Requirement.default}' --rdoc --ri --no-overwrite"
|
||||
"--version '#{Gem::Requirement.default}' --ri --no-overwrite"
|
||||
end
|
||||
|
||||
def description # :nodoc:
|
||||
|
@ -54,37 +54,32 @@ The rdoc command builds RDoc and RI documentation for installed gems. Use
|
|||
end
|
||||
|
||||
def execute
|
||||
if options[:all] then
|
||||
specs = Gem::SourceIndex.from_installed_gems.collect { |name, spec|
|
||||
spec
|
||||
}
|
||||
else
|
||||
gem_name = get_one_gem_name
|
||||
dep = Gem::Dependency.new gem_name, options[:version]
|
||||
specs = Gem::SourceIndex.from_installed_gems.search dep
|
||||
specs = if options[:all] then
|
||||
Gem::Specification.to_a
|
||||
else
|
||||
get_all_gem_names.map do |name|
|
||||
Gem::Specification.find_by_name name, options[:version]
|
||||
end.flatten.uniq
|
||||
end
|
||||
|
||||
if specs.empty? then
|
||||
alert_error 'No matching gems found'
|
||||
terminate_interaction 1
|
||||
end
|
||||
|
||||
if specs.empty?
|
||||
raise "Failed to find gem #{gem_name} to generate RDoc for #{options[:version]}"
|
||||
end
|
||||
specs.each do |spec|
|
||||
doc = Gem::RDoc.new spec, options[:include_rdoc], options[:include_ri]
|
||||
|
||||
if options[:include_ri]
|
||||
specs.sort.each do |spec|
|
||||
doc = Gem::DocManager.new(spec)
|
||||
doc.generate_ri if options[:overwrite] || !doc.ri_installed?
|
||||
end
|
||||
doc.force = options[:overwrite]
|
||||
|
||||
Gem::DocManager.update_ri_cache
|
||||
end
|
||||
|
||||
if options[:include_rdoc]
|
||||
specs.sort.each do |spec|
|
||||
doc = Gem::DocManager.new(spec)
|
||||
doc.generate_rdoc if options[:overwrite] || !doc.rdoc_installed?
|
||||
begin
|
||||
doc.generate
|
||||
rescue Errno::ENOENT => e
|
||||
e.message =~ / - /
|
||||
alert_error "Unable to document #{spec.full_name}, #{$'} is missing, skipping"
|
||||
terminate_interaction 1 if specs.length == 1
|
||||
end
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,30 +1,16 @@
|
|||
require 'rubygems/command'
|
||||
require 'rubygems/commands/query_command'
|
||||
require 'rubygems/commands/list_command'
|
||||
|
||||
class Gem::Commands::SearchCommand < Gem::Commands::QueryCommand
|
||||
class Gem::Commands::SearchCommand < Gem::Commands::ListCommand
|
||||
|
||||
def initialize
|
||||
super 'search', 'Display all gems whose name contains STRING'
|
||||
|
||||
remove_option '--name-matches'
|
||||
end
|
||||
|
||||
def arguments # :nodoc:
|
||||
"STRING fragment of gem name to search for"
|
||||
@defaults[:domain] = :remote
|
||||
end
|
||||
|
||||
def defaults_str # :nodoc:
|
||||
"--local --no-details"
|
||||
end
|
||||
|
||||
def usage # :nodoc:
|
||||
"#{program_name} [STRING]"
|
||||
end
|
||||
|
||||
def execute
|
||||
string = get_one_optional_argument
|
||||
options[:name] = /#{string}/i
|
||||
super
|
||||
"--remote --no-details"
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -78,7 +78,7 @@ You can set up a shortcut to gem server documentation using the URL:
|
|||
end
|
||||
|
||||
def execute
|
||||
options[:gemdir] << Gem.dir if options[:gemdir].empty?
|
||||
options[:gemdir] = Gem.path if options[:gemdir].empty?
|
||||
Gem::Server.run options
|
||||
end
|
||||
|
||||
|
|
|
@ -5,14 +5,22 @@ require 'rubygems/command'
|
|||
# RubyGems checkout or tarball.
|
||||
|
||||
class Gem::Commands::SetupCommand < Gem::Command
|
||||
HISTORY_HEADER = /^===\s*[\d.]+\s*\/\s*\d{4}-\d{2}-\d{2}\s*$/
|
||||
VERSION_MATCHER = /^===\s*([\d.]+)\s*\/\s*\d{4}-\d{2}-\d{2}\s*$/
|
||||
|
||||
def initialize
|
||||
require 'tmpdir'
|
||||
|
||||
super 'setup', 'Install RubyGems',
|
||||
:format_executable => true, :rdoc => true, :ri => true,
|
||||
:format_executable => true, :document => %w[ri],
|
||||
:site_or_vendor => :sitelibdir,
|
||||
:destdir => '', :prefix => ''
|
||||
:destdir => '', :prefix => '', :previous_version => ''
|
||||
|
||||
add_option '--previous-version=VERSION',
|
||||
'Previous version of rubygems',
|
||||
'Used for changelog processing' do |version, options|
|
||||
options[:previous_version] = version
|
||||
end
|
||||
|
||||
add_option '--prefix=PREFIX',
|
||||
'Prefix path for installing RubyGems',
|
||||
|
@ -37,14 +45,37 @@ class Gem::Commands::SetupCommand < Gem::Command
|
|||
options[:format_executable] = value
|
||||
end
|
||||
|
||||
add_option '--[no-]document [TYPES]', Array,
|
||||
'Generate documentation for RubyGems.',
|
||||
'List the documentation types you wish to',
|
||||
'generate. For example: rdoc,ri' do |value, options|
|
||||
options[:document] = case value
|
||||
when nil then %w[rdoc ri]
|
||||
when false then []
|
||||
else value
|
||||
end
|
||||
end
|
||||
|
||||
add_option '--[no-]rdoc',
|
||||
'Generate RDoc documentation for RubyGems' do |value, options|
|
||||
options[:rdoc] = value
|
||||
if value then
|
||||
options[:document] << 'rdoc'
|
||||
else
|
||||
options[:document].delete 'rdoc'
|
||||
end
|
||||
|
||||
options[:document].uniq!
|
||||
end
|
||||
|
||||
add_option '--[no-]ri',
|
||||
'Generate RI documentation for RubyGems' do |value, options|
|
||||
options[:ri] = value
|
||||
if value then
|
||||
options[:document] << 'ri'
|
||||
else
|
||||
options[:document].delete 'ri'
|
||||
end
|
||||
|
||||
options[:document].uniq!
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -58,7 +89,7 @@ class Gem::Commands::SetupCommand < Gem::Command
|
|||
end
|
||||
|
||||
def defaults_str # :nodoc:
|
||||
"--format-executable --rdoc --ri"
|
||||
"--format-executable --document ri"
|
||||
end
|
||||
|
||||
def description # :nodoc:
|
||||
|
@ -110,7 +141,7 @@ By default, this RubyGems will install gem as:
|
|||
|
||||
uninstall_old_gemcutter
|
||||
|
||||
install_rdoc
|
||||
documentation_success = install_rdoc
|
||||
|
||||
say
|
||||
if @verbose then
|
||||
|
@ -118,14 +149,30 @@ By default, this RubyGems will install gem as:
|
|||
say
|
||||
end
|
||||
|
||||
if options[:previous_version].empty?
|
||||
options[:previous_version] = Gem::VERSION.sub(/[0-9]+$/, '0')
|
||||
end
|
||||
|
||||
options[:previous_version] = Gem::Version.new(options[:previous_version])
|
||||
|
||||
release_notes = File.join Dir.pwd, 'History.txt'
|
||||
|
||||
release_notes = if File.exist? release_notes then
|
||||
open release_notes do |io|
|
||||
text = io.gets '==='
|
||||
text << io.gets('===')
|
||||
text[0...-3].sub(/^# coding:.*?^=/m, '')
|
||||
history = File.read release_notes
|
||||
history = history.sub(/^# coding:.*?^=/m, '')
|
||||
|
||||
text = history.split(HISTORY_HEADER)
|
||||
text.shift # correct an off-by-one generated by split
|
||||
version_lines = history.scan(HISTORY_HEADER)
|
||||
versions = history.scan(VERSION_MATCHER).flatten.map { |x| Gem::Version.new(x) }
|
||||
|
||||
history_string = ""
|
||||
|
||||
until versions.length == 0 or versions.shift < options[:previous_version]
|
||||
history_string += version_lines.shift + text.shift
|
||||
end
|
||||
|
||||
history_string
|
||||
else
|
||||
"Oh-no! Unable to find release notes!"
|
||||
end
|
||||
|
@ -145,6 +192,31 @@ By default, this RubyGems will install gem as:
|
|||
say "to remove it by hand."
|
||||
say
|
||||
end
|
||||
|
||||
if documentation_success
|
||||
if options[:document].include? 'rdoc' then
|
||||
say "Rdoc documentation was installed. You may now invoke:"
|
||||
say " gem server"
|
||||
say "and then peruse beautifully formatted documentation for your gems"
|
||||
say "with your web browser."
|
||||
say "If you do not wish to install this documentation in the future, use the"
|
||||
say "--no-document flag, or set it as the default in your ~/.gemrc file. See"
|
||||
say "'gem help env' for details."
|
||||
say
|
||||
end
|
||||
|
||||
if options[:document].include? 'ri' then
|
||||
say "Ruby Interactive (ri) documentation was installed. ri is kind of like man "
|
||||
say "pages for ruby libraries. You may access it like this:"
|
||||
say " ri Classname"
|
||||
say " ri Classname.class_method"
|
||||
say " ri Classname#instance_method"
|
||||
say "If you do not wish to install this documentation in the future, use the"
|
||||
say "--no-document flag, or set it as the default in your ~/.gemrc file. See"
|
||||
say "'gem help env' for details."
|
||||
say
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def install_executables(bin_dir)
|
||||
|
@ -165,7 +237,7 @@ By default, this RubyGems will install gem as:
|
|||
end
|
||||
|
||||
dest_file = File.join bin_dir, bin_file_formatted
|
||||
bin_tmp_file = File.join Dir.tmpdir, bin_file
|
||||
bin_tmp_file = File.join Dir.tmpdir, "#{bin_file}.#{$$}"
|
||||
|
||||
begin
|
||||
bin = File.readlines bin_file
|
||||
|
@ -209,10 +281,7 @@ TEXT
|
|||
say "Installing RubyGems" if @verbose
|
||||
|
||||
Dir.chdir 'lib' do
|
||||
lib_files = Dir[File.join('**', '*rb')]
|
||||
|
||||
# Be sure to include our SSL ca bundles
|
||||
lib_files += Dir[File.join('**', '*pem')]
|
||||
lib_files = Dir[File.join('**', '*rb')]
|
||||
|
||||
lib_files.each do |lib_file|
|
||||
dest_file = File.join lib_dir, lib_file
|
||||
|
@ -229,6 +298,12 @@ TEXT
|
|||
rubygems_name = "rubygems-#{Gem::VERSION}"
|
||||
rubygems_doc_dir = File.join gem_doc_dir, rubygems_name
|
||||
|
||||
begin
|
||||
Gem.ensure_gem_subdirectories Gem.dir
|
||||
rescue SystemCallError
|
||||
# ignore
|
||||
end
|
||||
|
||||
if File.writable? gem_doc_dir and
|
||||
(not File.exist? rubygems_doc_dir or
|
||||
File.writable? rubygems_doc_dir) then
|
||||
|
@ -237,21 +312,26 @@ TEXT
|
|||
rm_rf dir
|
||||
end
|
||||
|
||||
if options[:ri] then
|
||||
ri_dir = File.join rubygems_doc_dir, 'ri'
|
||||
say "Installing #{rubygems_name} ri into #{ri_dir}" if @verbose
|
||||
run_rdoc '--ri', '--op', ri_dir
|
||||
require 'rubygems/rdoc'
|
||||
|
||||
fake_spec = Gem::Specification.new 'rubygems', Gem::VERSION
|
||||
def fake_spec.full_gem_path
|
||||
File.expand_path '../../../..', __FILE__
|
||||
end
|
||||
|
||||
if options[:rdoc] then
|
||||
rdoc_dir = File.join rubygems_doc_dir, 'rdoc'
|
||||
say "Installing #{rubygems_name} rdoc into #{rdoc_dir}" if @verbose
|
||||
run_rdoc '--op', rdoc_dir
|
||||
end
|
||||
generate_ri = options[:document].include? 'ri'
|
||||
generate_rdoc = options[:document].include? 'rdoc'
|
||||
|
||||
rdoc = Gem::RDoc.new fake_spec, generate_rdoc, generate_ri
|
||||
rdoc.generate
|
||||
|
||||
return true
|
||||
elsif @verbose then
|
||||
say "Skipping RDoc generation, #{gem_doc_dir} not writable"
|
||||
say "Set the GEM_HOME environment variable if you want RDoc generated"
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
def make_destination_dirs(install_destdir)
|
||||
|
@ -331,23 +411,6 @@ abort "#{deprecation_message}"
|
|||
end
|
||||
end
|
||||
|
||||
def run_rdoc(*args)
|
||||
begin
|
||||
gem 'rdoc'
|
||||
rescue Gem::LoadError
|
||||
end
|
||||
|
||||
require 'rdoc/rdoc'
|
||||
|
||||
args << '--main' << 'README.rdoc' << '--quiet'
|
||||
args << '.'
|
||||
args << 'README.rdoc' << 'UPGRADING.rdoc'
|
||||
args << 'LICENSE.txt' << 'MIT.txt' << 'History.txt'
|
||||
|
||||
r = RDoc::RDoc.new
|
||||
r.document args
|
||||
end
|
||||
|
||||
def uninstall_old_gemcutter
|
||||
require 'rubygems/uninstaller'
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ class Gem::Commands::SourcesCommand < Gem::Command
|
|||
options[:update])
|
||||
|
||||
if options[:clear_all] then
|
||||
path = Gem::SpecFetcher.fetcher.dir
|
||||
path = File.join Gem.user_home, '.gem', 'specs'
|
||||
FileUtils.rm_rf path
|
||||
|
||||
unless File.exist? path then
|
||||
|
@ -64,16 +64,19 @@ class Gem::Commands::SourcesCommand < Gem::Command
|
|||
end
|
||||
end
|
||||
|
||||
if options[:add] then
|
||||
source_uri = options[:add]
|
||||
uri = URI.parse source_uri
|
||||
if source_uri = options[:add] then
|
||||
source = Gem::Source.new source_uri
|
||||
|
||||
begin
|
||||
Gem::SpecFetcher.fetcher.load_specs uri, 'specs'
|
||||
Gem.sources << source_uri
|
||||
Gem.configuration.write
|
||||
if Gem.sources.include? source_uri then
|
||||
say "source #{source_uri} already present in the cache"
|
||||
else
|
||||
source.load_specs :released
|
||||
Gem.sources << source
|
||||
Gem.configuration.write
|
||||
|
||||
say "#{source_uri} added to sources"
|
||||
say "#{source_uri} added to sources"
|
||||
end
|
||||
rescue URI::Error, ArgumentError
|
||||
say "#{source_uri} is not a URI"
|
||||
terminate_interaction 1
|
||||
|
@ -97,12 +100,9 @@ class Gem::Commands::SourcesCommand < Gem::Command
|
|||
end
|
||||
|
||||
if options[:update] then
|
||||
fetcher = Gem::SpecFetcher.fetcher
|
||||
|
||||
Gem.sources.each do |update_uri|
|
||||
update_uri = URI.parse update_uri
|
||||
fetcher.load_specs update_uri, 'specs'
|
||||
fetcher.load_specs update_uri, 'latest_specs'
|
||||
Gem.sources.each_source do |src|
|
||||
src.load_specs :released
|
||||
src.load_specs :latest
|
||||
end
|
||||
|
||||
say "source cache successfully updated"
|
||||
|
@ -112,8 +112,8 @@ class Gem::Commands::SourcesCommand < Gem::Command
|
|||
say "*** CURRENT SOURCES ***"
|
||||
say
|
||||
|
||||
Gem.sources.each do |source|
|
||||
say source
|
||||
Gem.sources.each do |src|
|
||||
say src
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
require 'rubygems/command'
|
||||
require 'rubygems/local_remote_options'
|
||||
require 'rubygems/version_option'
|
||||
require 'rubygems/format'
|
||||
require 'rubygems/package'
|
||||
|
||||
class Gem::Commands::SpecificationCommand < Gem::Command
|
||||
|
||||
|
@ -17,6 +17,7 @@ class Gem::Commands::SpecificationCommand < Gem::Command
|
|||
|
||||
add_version_option('examine')
|
||||
add_platform_option
|
||||
add_prerelease_option
|
||||
|
||||
add_option('--all', 'Output specifications for all versions of',
|
||||
'the gem') do |value, options|
|
||||
|
@ -62,13 +63,13 @@ FIELD name of gemspec field to show
|
|||
"Please specify a gem name or file on the command line"
|
||||
end
|
||||
|
||||
case options[:version]
|
||||
case v = options[:version]
|
||||
when String
|
||||
req = Gem::Requirement.parse options[:version]
|
||||
req = Gem::Requirement.create v
|
||||
when Gem::Requirement
|
||||
req = options[:version]
|
||||
req = v
|
||||
else
|
||||
raise Gem::CommandLineError, "Unsupported version type: #{options[:version]}"
|
||||
raise Gem::CommandLineError, "Unsupported version type: '#{v}'"
|
||||
end
|
||||
|
||||
if !req.none? and options[:all]
|
||||
|
@ -79,7 +80,7 @@ FIELD name of gemspec field to show
|
|||
if options[:all]
|
||||
dep = Gem::Dependency.new gem
|
||||
else
|
||||
dep = Gem::Dependency.new gem, options[:version]
|
||||
dep = Gem::Dependency.new gem, req
|
||||
end
|
||||
|
||||
field = get_one_optional_argument
|
||||
|
@ -89,7 +90,7 @@ FIELD name of gemspec field to show
|
|||
|
||||
if local? then
|
||||
if File.exist? gem then
|
||||
specs << Gem::Format.from_file_by_path(gem).spec rescue nil
|
||||
specs << Gem::Package.new(gem).spec rescue nil
|
||||
end
|
||||
|
||||
if specs.empty? then
|
||||
|
@ -98,17 +99,14 @@ FIELD name of gemspec field to show
|
|||
end
|
||||
|
||||
if remote? then
|
||||
found = Gem::SpecFetcher.fetcher.fetch dep, true
|
||||
|
||||
if dep.prerelease? or options[:prerelease]
|
||||
found += Gem::SpecFetcher.fetcher.fetch dep, false, true, true
|
||||
end
|
||||
dep.prerelease = options[:prerelease]
|
||||
found, _ = Gem::SpecFetcher.fetcher.spec_for_dependency dep
|
||||
|
||||
specs.push(*found.map { |spec,| spec })
|
||||
end
|
||||
|
||||
if specs.empty? then
|
||||
alert_error "Unknown gem '#{gem}'"
|
||||
alert_error "No gem matching '#{dep}' found"
|
||||
terminate_interaction 1
|
||||
end
|
||||
|
||||
|
|
|
@ -13,7 +13,8 @@ class Gem::Commands::UninstallCommand < Gem::Command
|
|||
|
||||
def initialize
|
||||
super 'uninstall', 'Uninstall gems from the local repository',
|
||||
:version => Gem::Requirement.default, :user_install => true
|
||||
:version => Gem::Requirement.default, :user_install => true,
|
||||
:check_dev => false
|
||||
|
||||
add_option('-a', '--[no-]all',
|
||||
'Uninstall all matching versions'
|
||||
|
@ -27,6 +28,12 @@ class Gem::Commands::UninstallCommand < Gem::Command
|
|||
options[:ignore] = value
|
||||
end
|
||||
|
||||
add_option('-D', '--[no-]-check-development',
|
||||
'Check development dependencies while uninstalling',
|
||||
'(default: false)') do |value, options|
|
||||
options[:check_dev] = value
|
||||
end
|
||||
|
||||
add_option('-x', '--[no-]executables',
|
||||
'Uninstall applicable executables without',
|
||||
'confirmation') do |value, options|
|
||||
|
@ -54,6 +61,12 @@ class Gem::Commands::UninstallCommand < Gem::Command
|
|||
options[:format_executable] = value
|
||||
end
|
||||
|
||||
add_option('--[no-]force',
|
||||
'Uninstall all versions of the named gems',
|
||||
'ignoring dependencies') do |value, options|
|
||||
options[:force] = value
|
||||
end
|
||||
|
||||
add_version_option
|
||||
add_platform_option
|
||||
end
|
||||
|
@ -73,19 +86,23 @@ class Gem::Commands::UninstallCommand < Gem::Command
|
|||
end
|
||||
|
||||
def execute
|
||||
original_path = Gem.path
|
||||
# REFACTOR: stolen from cleanup_command
|
||||
deplist = Gem::DependencyList.new
|
||||
get_all_gem_names.uniq.each do |name|
|
||||
Gem::Specification.find_all_by_name(name).each do |spec|
|
||||
deplist.add spec
|
||||
end
|
||||
end
|
||||
|
||||
get_all_gem_names.each do |gem_name|
|
||||
deps = deplist.strongly_connected_components.flatten.reverse
|
||||
|
||||
deps.map(&:name).uniq.each do |gem_name|
|
||||
begin
|
||||
Gem::Uninstaller.new(gem_name, options).uninstall
|
||||
rescue Gem::InstallError => e
|
||||
alert e.message
|
||||
rescue Gem::GemNotInHomeException => e
|
||||
spec = e.spec
|
||||
alert("In order to remove #{spec.name}, please execute:\n" \
|
||||
"\tgem uninstall #{spec.name} --install-dir=#{spec.installation_path}")
|
||||
ensure
|
||||
Gem.use_paths(*original_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -69,8 +69,10 @@ class Gem::Commands::UnpackCommand < Gem::Command
|
|||
else
|
||||
basename = File.basename path, '.gem'
|
||||
target_dir = File.expand_path basename, options[:target]
|
||||
FileUtils.mkdir_p target_dir
|
||||
Gem::Installer.new(path, :unpack => true).unpack target_dir
|
||||
|
||||
package = Gem::Package.new path
|
||||
package.extract_files target_dir
|
||||
|
||||
say "Unpacked gem: '#{target_dir}'"
|
||||
end
|
||||
end
|
||||
|
@ -134,9 +136,11 @@ class Gem::Commands::UnpackCommand < Gem::Command
|
|||
##
|
||||
# Extracts the Gem::Specification and raw metadata from the .gem file at
|
||||
# +path+.
|
||||
#--
|
||||
# TODO move to Gem::Package as #raw_spec or something
|
||||
|
||||
def get_metadata path
|
||||
format = Gem::Format.from_file_by_path path
|
||||
format = Gem::Package.new path
|
||||
spec = format.spec
|
||||
|
||||
metadata = nil
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
require 'rubygems/command'
|
||||
require 'rubygems/command_manager'
|
||||
require 'rubygems/dependency_installer'
|
||||
require 'rubygems/install_update_options'
|
||||
require 'rubygems/local_remote_options'
|
||||
require 'rubygems/spec_fetcher'
|
||||
require 'rubygems/version_option'
|
||||
require 'rubygems/commands/install_command'
|
||||
require 'rubygems/install_message' # must come before rdoc for messaging
|
||||
require 'rubygems/rdoc'
|
||||
|
||||
class Gem::Commands::UpdateCommand < Gem::Command
|
||||
|
||||
|
@ -13,11 +15,9 @@ class Gem::Commands::UpdateCommand < Gem::Command
|
|||
include Gem::VersionOption
|
||||
|
||||
def initialize
|
||||
super 'update',
|
||||
'Update the named gems (or all installed gems) in the local repository',
|
||||
:generate_rdoc => true,
|
||||
:generate_ri => true,
|
||||
:force => false
|
||||
super 'update', 'Update installed gems to the latest version',
|
||||
:document => %w[rdoc ri],
|
||||
:force => false
|
||||
|
||||
add_install_update_options
|
||||
|
||||
|
@ -37,6 +37,9 @@ class Gem::Commands::UpdateCommand < Gem::Command
|
|||
add_local_remote_options
|
||||
add_platform_option
|
||||
add_prerelease_option "as update targets"
|
||||
|
||||
@updated = []
|
||||
@installer = Gem::DependencyInstaller.new options
|
||||
end
|
||||
|
||||
def arguments # :nodoc:
|
||||
|
@ -44,7 +47,7 @@ class Gem::Commands::UpdateCommand < Gem::Command
|
|||
end
|
||||
|
||||
def defaults_str # :nodoc:
|
||||
"--rdoc --ri --no-force --install-dir #{Gem.dir}"
|
||||
"--document --no-force --install-dir #{Gem.dir}"
|
||||
end
|
||||
|
||||
def usage # :nodoc:
|
||||
|
@ -52,9 +55,6 @@ class Gem::Commands::UpdateCommand < Gem::Command
|
|||
end
|
||||
|
||||
def execute
|
||||
@installer = Gem::DependencyInstaller.new options
|
||||
@updated = []
|
||||
|
||||
hig = {}
|
||||
|
||||
if options[:system] then
|
||||
|
@ -79,21 +79,7 @@ class Gem::Commands::UpdateCommand < Gem::Command
|
|||
if updated.empty? then
|
||||
say "Nothing to update"
|
||||
else
|
||||
say "Gems updated: #{updated.map { |spec| spec.name }.join ', '}"
|
||||
|
||||
if options[:generate_ri] then
|
||||
updated.each do |gem|
|
||||
Gem::DocManager.new(gem, options[:rdoc_args]).generate_ri
|
||||
end
|
||||
|
||||
Gem::DocManager.update_ri_cache
|
||||
end
|
||||
|
||||
if options[:generate_rdoc] then
|
||||
updated.each do |gem|
|
||||
Gem::DocManager.new(gem, options[:rdoc_args]).generate_rdoc
|
||||
end
|
||||
end
|
||||
say "Gems updated: #{updated.map { |spec| spec.name }.join ' '}"
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -112,7 +98,6 @@ class Gem::Commands::UpdateCommand < Gem::Command
|
|||
|
||||
@installer.installed_gems.each do |spec|
|
||||
@updated << spec
|
||||
say "Successfully installed #{spec.full_name}" if success
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -178,8 +163,9 @@ class Gem::Commands::UpdateCommand < Gem::Command
|
|||
|
||||
args = []
|
||||
args << '--prefix' << Gem.prefix if Gem.prefix
|
||||
args << '--no-rdoc' unless options[:generate_rdoc]
|
||||
args << '--no-ri' unless options[:generate_ri]
|
||||
# TODO use --document for >= 1.9 , --no-rdoc --no-ri < 1.9
|
||||
args << '--no-rdoc' unless options[:document].include? 'rdoc'
|
||||
args << '--no-ri' unless options[:document].include? 'ri'
|
||||
args << '--no-format-executable' if options[:no_format_executable]
|
||||
|
||||
update_dir = File.join Gem.dir, 'gems', "rubygems-update-#{version}"
|
||||
|
@ -205,20 +191,20 @@ class Gem::Commands::UpdateCommand < Gem::Command
|
|||
gem_names.all? { |name| /#{name}/ !~ l_spec.name }
|
||||
|
||||
dependency = Gem::Dependency.new l_spec.name, "> #{l_spec.version}"
|
||||
dependency.prerelease = options[:prerelease]
|
||||
|
||||
fetcher = Gem::SpecFetcher.fetcher
|
||||
spec_tuples = fetcher.find_matching dependency
|
||||
|
||||
matching_gems = spec_tuples.select do |(name, _, platform),|
|
||||
name == l_name and Gem::Platform.match platform
|
||||
spec_tuples, _ = fetcher.search_for_dependency dependency
|
||||
|
||||
matching_gems = spec_tuples.select do |g,_|
|
||||
g.name == l_name and g.match_platform?
|
||||
end
|
||||
|
||||
highest_remote_gem = matching_gems.sort_by do |(_, version),|
|
||||
version
|
||||
end.last
|
||||
highest_remote_gem = matching_gems.sort_by { |g,_| g.version }.last
|
||||
|
||||
highest_remote_gem ||= [[nil, Gem::Version.new(0), nil]] # "null" object
|
||||
highest_remote_ver = highest_remote_gem.first[1]
|
||||
highest_remote_gem ||= [Gem::NameTuple.null]
|
||||
highest_remote_ver = highest_remote_gem.first.version
|
||||
|
||||
if system or (l_spec.version < highest_remote_ver) then
|
||||
result << [l_spec.name, [l_spec.version, highest_remote_ver].max]
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
require 'rubygems/command'
|
||||
require 'rubygems/local_remote_options'
|
||||
require 'rubygems/version_option'
|
||||
require 'rubygems/gemcutter_utilities'
|
||||
|
||||
class Gem::Commands::YankCommand < Gem::Command
|
||||
include Gem::LocalRemoteOptions
|
||||
include Gem::VersionOption
|
||||
include Gem::GemcutterUtilities
|
||||
|
||||
def description # :nodoc:
|
||||
'Remove a specific gem version release from RubyGems.org'
|
||||
end
|
||||
|
||||
def arguments # :nodoc:
|
||||
"GEM name of gem"
|
||||
end
|
||||
|
||||
def usage # :nodoc:
|
||||
"#{program_name} GEM -v VERSION [-p PLATFORM] [--undo] [--key KEY_NAME]"
|
||||
end
|
||||
|
||||
def initialize
|
||||
super 'yank', description
|
||||
|
||||
add_version_option("remove")
|
||||
add_platform_option("remove")
|
||||
|
||||
add_option('--undo') do |value, options|
|
||||
options[:undo] = true
|
||||
end
|
||||
|
||||
add_option('-k', '--key KEY_NAME',
|
||||
'Use API key from your gem credentials file') do |value, options|
|
||||
options[:key] = value
|
||||
end
|
||||
end
|
||||
|
||||
def execute
|
||||
sign_in
|
||||
|
||||
version = get_version_from_requirements(options[:version])
|
||||
platform = get_platform_from_requirements(options)
|
||||
api_key = Gem.configuration.rubygems_api_key
|
||||
api_key = Gem.configuration.api_keys[options[:key].to_sym] if options[:key]
|
||||
|
||||
if version then
|
||||
if options[:undo] then
|
||||
unyank_gem(version, platform, api_key)
|
||||
else
|
||||
yank_gem(version, platform, api_key)
|
||||
end
|
||||
else
|
||||
say "A version argument is required: #{usage}"
|
||||
terminate_interaction
|
||||
end
|
||||
end
|
||||
|
||||
def yank_gem(version, platform, api_key)
|
||||
say "Yanking gem from #{self.host}..."
|
||||
yank_api_request(:delete, version, platform, "api/v1/gems/yank", api_key)
|
||||
end
|
||||
|
||||
def unyank_gem(version, platform, api_key)
|
||||
say "Unyanking gem from #{host}..."
|
||||
yank_api_request(:put, version, platform, "api/v1/gems/unyank", api_key)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def yank_api_request(method, version, platform, api, api_key)
|
||||
name = get_one_gem_name
|
||||
response = rubygems_api_request(method, api) do |request|
|
||||
request.add_field("Authorization", api_key)
|
||||
|
||||
data = {
|
||||
'gem_name' => name,
|
||||
'version' => version,
|
||||
}
|
||||
data['platform'] = platform if platform
|
||||
|
||||
request.set_form_data data
|
||||
end
|
||||
say response.body
|
||||
end
|
||||
|
||||
def get_version_from_requirements(requirements)
|
||||
requirements.requirements.first[1].version
|
||||
rescue
|
||||
nil
|
||||
end
|
||||
|
||||
def get_platform_from_requirements(requirements)
|
||||
Gem.platforms[1].to_s if requirements.key? :added_platform
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
# This file contains all sorts of little compatibility hacks that we've
|
||||
# had to introduce over the years. Quarantining them into one file helps
|
||||
# us know when we can get rid of them.
|
||||
|
||||
# Ruby 1.9.x has introduced some things that are awkward, and we need to
|
||||
# support them, so we define some constants to use later.
|
||||
module Gem
|
||||
# Only MRI 1.9.2 has the custom prelude.
|
||||
GEM_PRELUDE_SUCKAGE = RUBY_VERSION =~ /^1\.9\.2/ and RUBY_ENGINE == "ruby"
|
||||
end
|
||||
|
||||
# Gem::QuickLoader exists in the gem prelude code in ruby 1.9.2 itself.
|
||||
# We gotta get rid of it if it's there, before we do anything else.
|
||||
if Gem::GEM_PRELUDE_SUCKAGE and defined?(Gem::QuickLoader) then
|
||||
Gem::QuickLoader.remove
|
||||
|
||||
$LOADED_FEATURES.delete Gem::QuickLoader.path_to_full_rubygems_library
|
||||
|
||||
if $LOADED_FEATURES.any? do |path| path.end_with? '/rubygems.rb' end then
|
||||
# TODO path does not exist here
|
||||
raise LoadError, "another rubygems is already loaded from #{path}"
|
||||
end
|
||||
|
||||
class << Gem
|
||||
remove_method :try_activate if Gem.respond_to?(:try_activate, true)
|
||||
end
|
||||
end
|
||||
|
||||
module Gem
|
||||
RubyGemsVersion = VERSION
|
||||
|
||||
RbConfigPriorities = %w[
|
||||
EXEEXT RUBY_SO_NAME arch bindir datadir libdir ruby_install_name
|
||||
ruby_version rubylibprefix sitedir sitelibdir vendordir vendorlibdir
|
||||
rubylibdir
|
||||
]
|
||||
|
||||
unless defined?(ConfigMap)
|
||||
##
|
||||
# Configuration settings from ::RbConfig
|
||||
ConfigMap = Hash.new do |cm, key|
|
||||
cm[key] = RbConfig::CONFIG[key.to_s]
|
||||
end
|
||||
else
|
||||
RbConfigPriorities.each do |key|
|
||||
ConfigMap[key.to_sym] = RbConfig::CONFIG[key]
|
||||
end
|
||||
end
|
||||
|
||||
RubyGemsPackageVersion = VERSION
|
||||
end
|
|
@ -5,9 +5,9 @@
|
|||
#++
|
||||
|
||||
##
|
||||
# Gem::ConfigFile RubyGems options and gem command options from ~/.gemrc.
|
||||
# Gem::ConfigFile RubyGems options and gem command options from gemrc.
|
||||
#
|
||||
# ~/.gemrc is a YAML file that uses strings to match gem command arguments and
|
||||
# gemrc is a YAML file that uses strings to match gem command arguments and
|
||||
# symbols to match RubyGems options.
|
||||
#
|
||||
# Gem command arguments use a String key that matches the command name and
|
||||
|
@ -21,16 +21,19 @@
|
|||
# RubyGems options use symbol keys. Valid options are:
|
||||
#
|
||||
# +:backtrace+:: See #backtrace
|
||||
# +:benchmark+:: See #benchmark
|
||||
# +:sources+:: Sets Gem::sources
|
||||
# +:verbose+:: See #verbose
|
||||
|
||||
require 'rbconfig'
|
||||
#
|
||||
# gemrc files may exist in various locations and are read and merged in
|
||||
# the following order:
|
||||
#
|
||||
# - system wide (/etc/gemrc)
|
||||
# - per user (~/.gemrc)
|
||||
# - per environment (gemrc files listed in the GEMRC environment variable)
|
||||
|
||||
class Gem::ConfigFile
|
||||
|
||||
DEFAULT_BACKTRACE = false
|
||||
DEFAULT_BENCHMARK = false
|
||||
DEFAULT_BULK_THRESHOLD = 1000
|
||||
DEFAULT_VERBOSITY = true
|
||||
DEFAULT_UPDATE_SOURCES = true
|
||||
|
@ -96,11 +99,6 @@ class Gem::ConfigFile
|
|||
|
||||
attr_writer :backtrace
|
||||
|
||||
##
|
||||
# True if we are benchmarking this run.
|
||||
|
||||
attr_accessor :benchmark
|
||||
|
||||
##
|
||||
# Bulk threshold value. If the number of missing gems are above this
|
||||
# threshold value, then a bulk download technique is used. (deprecated)
|
||||
|
@ -131,6 +129,10 @@ class Gem::ConfigFile
|
|||
attr_reader :api_keys
|
||||
|
||||
##
|
||||
# True if we want to force specification of gem server when pushing a gem
|
||||
|
||||
attr_accessor :disable_default_gem_server
|
||||
|
||||
# openssl verify mode value, used for remote https connection
|
||||
|
||||
attr_reader :ssl_verify_mode
|
||||
|
@ -158,29 +160,29 @@ class Gem::ConfigFile
|
|||
# <tt>--debug</tt>::
|
||||
# Enable Ruby level debug messages. Handled early for the same reason as
|
||||
# --backtrace.
|
||||
#--
|
||||
# TODO: parse options upstream, pass in options directly
|
||||
|
||||
def initialize(arg_list)
|
||||
def initialize(args)
|
||||
@config_file_name = nil
|
||||
need_config_file_name = false
|
||||
|
||||
arg_list = arg_list.map do |arg|
|
||||
arg_list = []
|
||||
|
||||
args.each do |arg|
|
||||
if need_config_file_name then
|
||||
@config_file_name = arg
|
||||
need_config_file_name = false
|
||||
nil
|
||||
elsif arg =~ /^--config-file=(.*)/ then
|
||||
@config_file_name = $1
|
||||
nil
|
||||
elsif arg =~ /^--config-file$/ then
|
||||
need_config_file_name = true
|
||||
nil
|
||||
else
|
||||
arg
|
||||
arg_list << arg
|
||||
end
|
||||
end.compact
|
||||
end
|
||||
|
||||
@backtrace = DEFAULT_BACKTRACE
|
||||
@benchmark = DEFAULT_BENCHMARK
|
||||
@bulk_threshold = DEFAULT_BULK_THRESHOLD
|
||||
@verbose = DEFAULT_VERBOSITY
|
||||
@update_sources = DEFAULT_UPDATE_SOURCES
|
||||
|
@ -189,19 +191,25 @@ class Gem::ConfigFile
|
|||
platform_config = Marshal.load Marshal.dump(PLATFORM_DEFAULTS)
|
||||
system_config = load_file SYSTEM_WIDE_CONFIG_FILE
|
||||
user_config = load_file config_file_name.dup.untaint
|
||||
environment_config = (ENV['GEMRC'] || '').split(/[:;]/).inject({}) do |result, file|
|
||||
result.merge load_file file
|
||||
end
|
||||
|
||||
|
||||
@hash = operating_system_config.merge platform_config
|
||||
@hash = @hash.merge system_config
|
||||
@hash = @hash.merge user_config
|
||||
@hash = @hash.merge environment_config
|
||||
|
||||
# HACK these override command-line args, which is bad
|
||||
@backtrace = @hash[:backtrace] if @hash.key? :backtrace
|
||||
@benchmark = @hash[:benchmark] if @hash.key? :benchmark
|
||||
@bulk_threshold = @hash[:bulk_threshold] if @hash.key? :bulk_threshold
|
||||
@home = @hash[:gemhome] if @hash.key? :gemhome
|
||||
@path = @hash[:gempath] if @hash.key? :gempath
|
||||
@update_sources = @hash[:update_sources] if @hash.key? :update_sources
|
||||
@verbose = @hash[:verbose] if @hash.key? :verbose
|
||||
@backtrace = @hash[:backtrace] if @hash.key? :backtrace
|
||||
@bulk_threshold = @hash[:bulk_threshold] if @hash.key? :bulk_threshold
|
||||
@home = @hash[:gemhome] if @hash.key? :gemhome
|
||||
@path = @hash[:gempath] if @hash.key? :gempath
|
||||
@update_sources = @hash[:update_sources] if @hash.key? :update_sources
|
||||
@verbose = @hash[:verbose] if @hash.key? :verbose
|
||||
@disable_default_gem_server = @hash[:disable_default_gem_server] if @hash.key? :disable_default_gem_server
|
||||
|
||||
@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
|
||||
|
||||
|
@ -224,6 +232,7 @@ class Gem::ConfigFile
|
|||
else
|
||||
@hash
|
||||
end
|
||||
|
||||
if @api_keys.key? :rubygems_api_key then
|
||||
@rubygems_api_key = @api_keys[:rubygems_api_key]
|
||||
@api_keys[:rubygems] = @api_keys.delete :rubygems_api_key unless @api_keys.key? :rubygems
|
||||
|
@ -238,7 +247,8 @@ class Gem::ConfigFile
|
|||
|
||||
Gem.load_yaml
|
||||
|
||||
File.open(credentials_path, 'w') do |f|
|
||||
permissions = 0600 & (~File.umask)
|
||||
File.open(credentials_path, 'w', permissions) do |f|
|
||||
f.write config.to_yaml
|
||||
end
|
||||
|
||||
|
@ -249,13 +259,21 @@ class Gem::ConfigFile
|
|||
Gem.load_yaml
|
||||
|
||||
return {} unless filename and File.exist? filename
|
||||
|
||||
begin
|
||||
YAML.load(File.read(filename))
|
||||
content = YAML.load(File.read(filename))
|
||||
unless content.kind_of? Hash
|
||||
warn "Failed to load #{config_file_name} because it doesn't contain valid YAML hash"
|
||||
return {}
|
||||
end
|
||||
return content
|
||||
rescue ArgumentError
|
||||
warn "Failed to load #{config_file_name}"
|
||||
rescue Errno::EACCES
|
||||
warn "Failed to load #{config_file_name} due to permissions problem."
|
||||
end or {}
|
||||
end
|
||||
|
||||
{}
|
||||
end
|
||||
|
||||
# True if the backtrace option has been specified, or debug is on.
|
||||
|
@ -273,13 +291,11 @@ class Gem::ConfigFile
|
|||
hash = @hash.dup
|
||||
hash.delete :update_sources
|
||||
hash.delete :verbose
|
||||
hash.delete :benchmark
|
||||
hash.delete :backtrace
|
||||
hash.delete :bulk_threshold
|
||||
|
||||
yield :update_sources, @update_sources
|
||||
yield :verbose, @verbose
|
||||
yield :benchmark, @benchmark
|
||||
yield :backtrace, @backtrace
|
||||
yield :bulk_threshold, @bulk_threshold
|
||||
|
||||
|
@ -296,8 +312,6 @@ class Gem::ConfigFile
|
|||
case arg
|
||||
when /^--(backtrace|traceback)$/ then
|
||||
@backtrace = true
|
||||
when /^--bench(mark)?$/ then
|
||||
@benchmark = true
|
||||
when /^--debug$/ then
|
||||
$DEBUG = true
|
||||
else
|
||||
|
@ -309,25 +323,41 @@ class Gem::ConfigFile
|
|||
# Really verbose mode gives you extra output.
|
||||
def really_verbose
|
||||
case verbose
|
||||
when true, false, nil then false
|
||||
else true
|
||||
when true, false, nil then
|
||||
false
|
||||
else
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
# to_yaml only overwrites things you can't override on the command line.
|
||||
def to_yaml # :nodoc:
|
||||
yaml_hash = {}
|
||||
yaml_hash[:backtrace] = @hash.key?(:backtrace) ? @hash[:backtrace] :
|
||||
DEFAULT_BACKTRACE
|
||||
yaml_hash[:benchmark] = @hash.key?(:benchmark) ? @hash[:benchmark] :
|
||||
DEFAULT_BENCHMARK
|
||||
yaml_hash[:bulk_threshold] = @hash.key?(:bulk_threshold) ?
|
||||
@hash[:bulk_threshold] : DEFAULT_BULK_THRESHOLD
|
||||
yaml_hash[:sources] = Gem.sources
|
||||
yaml_hash[:update_sources] = @hash.key?(:update_sources) ?
|
||||
@hash[:update_sources] : DEFAULT_UPDATE_SOURCES
|
||||
yaml_hash[:verbose] = @hash.key?(:verbose) ? @hash[:verbose] :
|
||||
DEFAULT_VERBOSITY
|
||||
yaml_hash[:backtrace] = if @hash.key?(:backtrace)
|
||||
@hash[:backtrace]
|
||||
else
|
||||
DEFAULT_BACKTRACE
|
||||
end
|
||||
|
||||
yaml_hash[:bulk_threshold] = if @hash.key?(:bulk_threshold)
|
||||
@hash[:bulk_threshold]
|
||||
else
|
||||
DEFAULT_BULK_THRESHOLD
|
||||
end
|
||||
|
||||
yaml_hash[:sources] = Gem.sources.to_a
|
||||
|
||||
yaml_hash[:update_sources] = if @hash.key?(:update_sources)
|
||||
@hash[:update_sources]
|
||||
else
|
||||
DEFAULT_UPDATE_SOURCES
|
||||
end
|
||||
|
||||
yaml_hash[:verbose] = if @hash.key?(:verbose)
|
||||
@hash[:verbose]
|
||||
else
|
||||
DEFAULT_VERBOSITY
|
||||
end
|
||||
|
||||
keys = yaml_hash.keys.map { |key| key.to_s }
|
||||
keys << 'debug'
|
||||
|
@ -361,15 +391,13 @@ class Gem::ConfigFile
|
|||
|
||||
def ==(other) # :nodoc:
|
||||
self.class === other and
|
||||
@backtrace == other.backtrace and
|
||||
@benchmark == other.benchmark and
|
||||
@bulk_threshold == other.bulk_threshold and
|
||||
@verbose == other.verbose and
|
||||
@update_sources == other.update_sources and
|
||||
@hash == other.hash
|
||||
@backtrace == other.backtrace and
|
||||
@bulk_threshold == other.bulk_threshold and
|
||||
@verbose == other.verbose and
|
||||
@update_sources == other.update_sources and
|
||||
@hash == other.hash
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
attr_reader :hash
|
||||
protected :hash
|
||||
end
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
module Kernel
|
||||
|
||||
# REFACTOR: This should be pulled out into some kind of hacks file.
|
||||
remove_method :gem if 'method' == defined? gem # from gem_prelude.rb on 1.9
|
||||
|
||||
##
|
||||
# Use Kernel#gem to activate a specific version of +gem_name+.
|
||||
#
|
||||
# +requirements+ is a list of version requirements that the
|
||||
# specified gem must match, most commonly "= example.version.number". See
|
||||
# Gem::Requirement for how to specify a version requirement.
|
||||
#
|
||||
# If you will be activating the latest version of a gem, there is no need to
|
||||
# call Kernel#gem, Kernel#require will do the right thing for you.
|
||||
#
|
||||
# Kernel#gem returns true if the gem was activated, otherwise false. If the
|
||||
# gem could not be found, didn't match the version requirements, or a
|
||||
# different version was already activated, an exception will be raised.
|
||||
#
|
||||
# Kernel#gem should be called *before* any require statements (otherwise
|
||||
# RubyGems may load a conflicting library version).
|
||||
#
|
||||
# In older RubyGems versions, the environment variable GEM_SKIP could be
|
||||
# used to skip activation of specified gems, for example to test out changes
|
||||
# that haven't been installed yet. Now RubyGems defers to -I and the
|
||||
# RUBYLIB environment variable to skip activation of a gem.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# GEM_SKIP=libA:libB ruby -I../libA -I../libB ./mycode.rb
|
||||
|
||||
def gem(gem_name, *requirements) # :doc:
|
||||
skip_list = (ENV['GEM_SKIP'] || "").split(/:/)
|
||||
raise Gem::LoadError, "skipping #{gem_name}" if skip_list.include? gem_name
|
||||
|
||||
if gem_name.kind_of? Gem::Dependency
|
||||
unless Gem::Deprecate.skip
|
||||
warn "#{Gem.location_of_caller.join ':'}:Warning: Kernel.gem no longer "\
|
||||
"accepts a Gem::Dependency object, please pass the name "\
|
||||
"and requirements directly"
|
||||
end
|
||||
|
||||
requirements = gem_name.requirement
|
||||
gem_name = gem_name.name
|
||||
end
|
||||
|
||||
spec = Gem::Dependency.new(gem_name, *requirements).to_spec
|
||||
spec.activate if spec
|
||||
end
|
||||
|
||||
private :gem
|
||||
|
||||
end
|
|
@ -0,0 +1,119 @@
|
|||
#--
|
||||
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
|
||||
# All rights reserved.
|
||||
# See LICENSE.txt for permissions.
|
||||
#++
|
||||
|
||||
module Kernel
|
||||
|
||||
if defined?(gem_original_require) then
|
||||
# Ruby ships with a custom_require, override its require
|
||||
remove_method :require
|
||||
else
|
||||
##
|
||||
# The Kernel#require from before RubyGems was loaded.
|
||||
|
||||
alias gem_original_require require
|
||||
private :gem_original_require
|
||||
end
|
||||
|
||||
##
|
||||
# When RubyGems is required, Kernel#require is replaced with our own which
|
||||
# is capable of loading gems on demand.
|
||||
#
|
||||
# When you call <tt>require 'x'</tt>, this is what happens:
|
||||
# * If the file can be loaded from the existing Ruby loadpath, it
|
||||
# is.
|
||||
# * Otherwise, installed gems are searched for a file that matches.
|
||||
# If it's found in gem 'y', that gem is activated (added to the
|
||||
# loadpath).
|
||||
#
|
||||
# The normal <tt>require</tt> functionality of returning false if
|
||||
# that file has already been loaded is preserved.
|
||||
|
||||
def require path
|
||||
spec = Gem.find_unresolved_default_spec(path)
|
||||
if spec
|
||||
Gem.remove_unresolved_default_spec(spec)
|
||||
gem(spec.name)
|
||||
end
|
||||
|
||||
# If there are no unresolved deps, then we can use just try
|
||||
# normal require handle loading a gem from the rescue below.
|
||||
|
||||
if Gem::Specification.unresolved_deps.empty? then
|
||||
return gem_original_require(path)
|
||||
end
|
||||
|
||||
# If +path+ is for a gem that has already been loaded, don't
|
||||
# bother trying to find it in an unresolved gem, just go straight
|
||||
# to normal require.
|
||||
#--
|
||||
# TODO request access to the C implementation of this to speed up RubyGems
|
||||
|
||||
spec = Gem::Specification.find { |s|
|
||||
s.activated? and s.contains_requirable_file? path
|
||||
}
|
||||
|
||||
return gem_original_require(path) if spec
|
||||
|
||||
# Attempt to find +path+ in any unresolved gems...
|
||||
|
||||
found_specs = Gem::Specification.find_in_unresolved path
|
||||
|
||||
# If there are no directly unresolved gems, then try and find +path+
|
||||
# in any gems that are available via the currently unresolved gems.
|
||||
# For example, given:
|
||||
#
|
||||
# a => b => c => d
|
||||
#
|
||||
# If a and b are currently active with c being unresolved and d.rb is
|
||||
# requested, then find_in_unresolved_tree will find d.rb in d because
|
||||
# it's a dependency of c.
|
||||
#
|
||||
if found_specs.empty? then
|
||||
found_specs = Gem::Specification.find_in_unresolved_tree path
|
||||
|
||||
found_specs.each do |found_spec|
|
||||
found_spec.activate
|
||||
end
|
||||
|
||||
# We found +path+ directly in an unresolved gem. Now we figure out, of
|
||||
# the possible found specs, which one we should activate.
|
||||
else
|
||||
|
||||
# Check that all the found specs are just different
|
||||
# versions of the same gem
|
||||
names = found_specs.map(&:name).uniq
|
||||
|
||||
if names.size > 1 then
|
||||
raise Gem::LoadError, "#{path} found in multiple gems: #{names.join ', '}"
|
||||
end
|
||||
|
||||
# Ok, now find a gem that has no conflicts, starting
|
||||
# at the highest version.
|
||||
valid = found_specs.select { |s| s.conflicts.empty? }.last
|
||||
|
||||
unless valid then
|
||||
le = Gem::LoadError.new "unable to find a version of '#{names.first}' to activate"
|
||||
le.name = names.first
|
||||
raise le
|
||||
end
|
||||
|
||||
valid.activate
|
||||
end
|
||||
|
||||
gem_original_require path
|
||||
rescue LoadError => load_error
|
||||
if load_error.message.start_with?("Could not find") or
|
||||
(load_error.message.end_with?(path) and Gem.try_activate(path)) then
|
||||
return gem_original_require(path)
|
||||
end
|
||||
|
||||
raise load_error
|
||||
end
|
||||
|
||||
private :require
|
||||
|
||||
end
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
#--
|
||||
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
|
||||
# All rights reserved.
|
||||
# See LICENSE.txt for permissions.
|
||||
#++
|
||||
|
||||
module Kernel
|
||||
|
||||
if defined?(gem_original_require) then
|
||||
# Ruby ships with a custom_require, override its require
|
||||
remove_method :require
|
||||
else
|
||||
##
|
||||
# The Kernel#require from before RubyGems was loaded.
|
||||
|
||||
alias gem_original_require require
|
||||
private :gem_original_require
|
||||
end
|
||||
|
||||
##
|
||||
# When RubyGems is required, Kernel#require is replaced with our own which
|
||||
# is capable of loading gems on demand.
|
||||
#
|
||||
# When you call <tt>require 'x'</tt>, this is what happens:
|
||||
# * If the file can be loaded from the existing Ruby loadpath, it
|
||||
# is.
|
||||
# * Otherwise, installed gems are searched for a file that matches.
|
||||
# If it's found in gem 'y', that gem is activated (added to the
|
||||
# loadpath).
|
||||
#
|
||||
# The normal <tt>require</tt> functionality of returning false if
|
||||
# that file has already been loaded is preserved.
|
||||
|
||||
def require path
|
||||
if Gem.unresolved_deps.empty? then
|
||||
gem_original_require path
|
||||
else
|
||||
spec = Gem::Specification.find { |s|
|
||||
s.activated? and s.contains_requirable_file? path
|
||||
}
|
||||
|
||||
unless spec then
|
||||
found_specs = Gem::Specification.find_in_unresolved path
|
||||
unless found_specs.empty? then
|
||||
found_specs = [found_specs.last]
|
||||
else
|
||||
found_specs = Gem::Specification.find_in_unresolved_tree path
|
||||
end
|
||||
|
||||
found_specs.each do |found_spec|
|
||||
found_spec.activate
|
||||
end
|
||||
end
|
||||
|
||||
return gem_original_require path
|
||||
end
|
||||
rescue LoadError => load_error
|
||||
if load_error.message.start_with?("Could not find") or
|
||||
(load_error.message.end_with?(path) and Gem.try_activate(path)) then
|
||||
return gem_original_require(path)
|
||||
end
|
||||
|
||||
raise load_error
|
||||
end
|
||||
|
||||
private :require
|
||||
|
||||
end
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
module Gem
|
||||
|
||||
# TODO: move this whole file back into rubygems.rb
|
||||
DEFAULT_HOST = "https://rubygems.org"
|
||||
|
||||
@post_install_hooks ||= []
|
||||
@done_installing_hooks ||= []
|
||||
@post_uninstall_hooks ||= []
|
||||
@pre_uninstall_hooks ||= []
|
||||
@pre_install_hooks ||= []
|
||||
|
@ -61,7 +61,7 @@ module Gem
|
|||
# Default gem load path
|
||||
|
||||
def self.default_path
|
||||
if File.exist? Gem.user_home then
|
||||
if Gem.user_home && File.exist?(Gem.user_home) then
|
||||
[user_dir, default_dir]
|
||||
else
|
||||
[default_dir]
|
||||
|
@ -93,24 +93,6 @@ module Gem
|
|||
end
|
||||
end
|
||||
|
||||
##
|
||||
# The default system-wide source info cache directory
|
||||
|
||||
def self.default_system_source_cache_dir
|
||||
File.join(Gem.dir, 'source_cache')
|
||||
end
|
||||
|
||||
##
|
||||
# The default user-specific source info cache directory
|
||||
|
||||
def self.default_user_source_cache_dir
|
||||
#
|
||||
# NOTE Probably an argument for moving this to per-ruby supported dirs like
|
||||
# user_dir
|
||||
#
|
||||
File.join(Gem.user_home, '.gem', 'source_cache')
|
||||
end
|
||||
|
||||
##
|
||||
# A wrapper around RUBY_ENGINE const that may not be defined
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
require "rubygems/requirement"
|
||||
|
||||
##
|
||||
# The Dependency class holds a Gem name and a Gem::Requirement.
|
||||
|
||||
require "rubygems/requirement"
|
||||
|
||||
class Gem::Dependency
|
||||
|
||||
##
|
||||
|
@ -11,6 +11,9 @@ class Gem::Dependency
|
|||
# When this list is updated, be sure to change
|
||||
# Gem::Specification::CURRENT_SPECIFICATION_VERSION as well.
|
||||
|
||||
# REFACTOR: This type of constant, TYPES, indicates we might want
|
||||
# two classes, used via inheretance or duck typing.
|
||||
|
||||
TYPES = [
|
||||
:development,
|
||||
:runtime,
|
||||
|
@ -32,18 +35,23 @@ class Gem::Dependency
|
|||
# <tt>:runtime</tt>.
|
||||
|
||||
def initialize name, *requirements
|
||||
if Regexp === name then
|
||||
case name
|
||||
when String then # ok
|
||||
when Regexp then
|
||||
msg = ["NOTE: Dependency.new w/ a regexp is deprecated.",
|
||||
"Dependency.new called from #{Gem.location_of_caller.join(":")}"]
|
||||
warn msg.join("\n") unless Gem::Deprecate.skip
|
||||
else
|
||||
raise ArgumentError,
|
||||
"dependency name must be a String, was #{name.inspect}"
|
||||
end
|
||||
|
||||
type = Symbol === requirements.last ? requirements.pop : :runtime
|
||||
requirements = requirements.first if 1 == requirements.length # unpack
|
||||
|
||||
unless TYPES.include? type
|
||||
raise ArgumentError, "Valid types are #{TYPES.inspect}, "
|
||||
+ "not #{type.inspect}"
|
||||
raise ArgumentError, "Valid types are #{TYPES.inspect}, " +
|
||||
"not #{type.inspect}"
|
||||
end
|
||||
|
||||
@name = name
|
||||
|
@ -66,8 +74,13 @@ class Gem::Dependency
|
|||
end
|
||||
|
||||
def inspect # :nodoc:
|
||||
"<%s type=%p name=%p requirements=%p>" %
|
||||
[self.class, self.type, self.name, requirement.to_s]
|
||||
if @prerelease
|
||||
"<%s type=%p name=%p requirements=%p prerelease=ok>" %
|
||||
[self.class, self.type, self.name, requirement.to_s]
|
||||
else
|
||||
"<%s type=%p name=%p requirements=%p>" %
|
||||
[self.class, self.type, self.name, requirement.to_s]
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -77,6 +90,14 @@ class Gem::Dependency
|
|||
@prerelease || requirement.prerelease?
|
||||
end
|
||||
|
||||
##
|
||||
# Is this dependency simply asking for the latest version
|
||||
# of a gem?
|
||||
|
||||
def latest_version?
|
||||
@requirement.none?
|
||||
end
|
||||
|
||||
def pretty_print q # :nodoc:
|
||||
q.group 1, 'Gem::Dependency.new(', ')' do
|
||||
q.pp name
|
||||
|
@ -113,6 +134,8 @@ class Gem::Dependency
|
|||
# Children, define explicit marshal and unmarshal behavior for
|
||||
# public classes. Marshal formats are part of your public API.
|
||||
|
||||
# REFACTOR: See above
|
||||
|
||||
if defined?(@version_requirement) && @version_requirement
|
||||
version = @version_requirement.instance_variable_get :@version
|
||||
@version_requirement = nil
|
||||
|
@ -122,6 +145,7 @@ class Gem::Dependency
|
|||
@requirement = @version_requirements if defined?(@version_requirements)
|
||||
end
|
||||
|
||||
# DOC: this method needs documentation or :nodoc''d
|
||||
def requirements_list
|
||||
requirement.as_list
|
||||
end
|
||||
|
@ -179,13 +203,24 @@ class Gem::Dependency
|
|||
requirement.satisfied_by? version
|
||||
end
|
||||
|
||||
def match? name, version
|
||||
# DOC: this method needs either documented or :nodoc'd
|
||||
|
||||
def match? obj, version=nil
|
||||
if !version
|
||||
name = obj.name
|
||||
version = obj.version
|
||||
else
|
||||
name = obj
|
||||
end
|
||||
|
||||
return false unless self.name === name
|
||||
return true if requirement.none?
|
||||
|
||||
requirement.satisfied_by? Gem::Version.new(version)
|
||||
end
|
||||
|
||||
# DOC: this method needs either documented or :nodoc'd
|
||||
|
||||
def matches_spec? spec
|
||||
return false unless name === spec.name
|
||||
return true if requirement.none?
|
||||
|
@ -212,6 +247,8 @@ class Gem::Dependency
|
|||
self.class.new name, self_req.as_list.concat(other_req.as_list)
|
||||
end
|
||||
|
||||
# DOC: this method needs either documented or :nodoc'd
|
||||
|
||||
def matching_specs platform_only = false
|
||||
matches = Gem::Specification.find_all { |spec|
|
||||
self.name === spec.name and # TODO: == instead of ===
|
||||
|
@ -234,14 +271,26 @@ class Gem::Dependency
|
|||
@requirement.specific?
|
||||
end
|
||||
|
||||
# DOC: this method needs either documented or :nodoc'd
|
||||
|
||||
def to_specs
|
||||
matches = matching_specs true
|
||||
|
||||
# TODO: check Gem.activated_spec[self.name] in case matches falls outside
|
||||
|
||||
if matches.empty? then
|
||||
specs = Gem::Specification.all_names.join ", "
|
||||
error = Gem::LoadError.new "Could not find #{name} (#{requirement}) amongst [#{specs}]"
|
||||
specs = Gem::Specification.find_all { |s|
|
||||
s.name == name
|
||||
}.map { |x| x.full_name }
|
||||
|
||||
if specs.empty?
|
||||
total = Gem::Specification.to_a.size
|
||||
error = Gem::LoadError.new \
|
||||
"Could not find '#{name}' (#{requirement}) among #{total} total gem(s)"
|
||||
else
|
||||
error = Gem::LoadError.new \
|
||||
"Could not find '#{name}' (#{requirement}) - did find: [#{specs.join ','}]"
|
||||
end
|
||||
error.name = self.name
|
||||
error.requirement = self.requirement
|
||||
raise error
|
||||
|
@ -252,6 +301,8 @@ class Gem::Dependency
|
|||
matches
|
||||
end
|
||||
|
||||
# DOC: this method needs either documented or :nodoc'd
|
||||
|
||||
def to_spec
|
||||
matches = self.to_specs
|
||||
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
require 'rubygems'
|
||||
require 'rubygems/dependency_list'
|
||||
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/available_set'
|
||||
|
||||
##
|
||||
# Installs a gem along with all its dependencies from local and remote gems.
|
||||
|
@ -14,8 +18,14 @@ class Gem::DependencyInstaller
|
|||
attr_reader :gems_to_install
|
||||
attr_reader :installed_gems
|
||||
|
||||
##
|
||||
# Documentation types. For use by the Gem.done_installing hook
|
||||
|
||||
attr_reader :document
|
||||
|
||||
DEFAULT_OPTIONS = {
|
||||
:env_shebang => false,
|
||||
:document => %w[ri],
|
||||
:domain => :both, # HACK dup
|
||||
:force => false,
|
||||
:format_executable => false, # HACK dup
|
||||
|
@ -23,7 +33,8 @@ class Gem::DependencyInstaller
|
|||
:prerelease => false,
|
||||
:security_policy => nil, # HACK NoSecurity requires OpenSSL. AlmostNo? Low?
|
||||
:wrappers => true,
|
||||
}
|
||||
:build_docs_in_background => false,
|
||||
}.freeze
|
||||
|
||||
##
|
||||
# Creates a new installer instance.
|
||||
|
@ -47,6 +58,7 @@ class Gem::DependencyInstaller
|
|||
if options[:install_dir] then
|
||||
@gem_home = options[:install_dir]
|
||||
|
||||
# HACK shouldn't change the global settings
|
||||
Gem::Specification.dirs = @gem_home
|
||||
Gem.ensure_gem_subdirectories @gem_home
|
||||
options[:install_dir] = @gem_home # FIX: because we suck and reuse below
|
||||
|
@ -55,7 +67,9 @@ class Gem::DependencyInstaller
|
|||
options = DEFAULT_OPTIONS.merge options
|
||||
|
||||
@bin_dir = options[:bin_dir]
|
||||
@dev_shallow = options[:dev_shallow]
|
||||
@development = options[:development]
|
||||
@document = options[:document]
|
||||
@domain = options[:domain]
|
||||
@env_shebang = options[:env_shebang]
|
||||
@force = options[:force]
|
||||
|
@ -65,8 +79,14 @@ class Gem::DependencyInstaller
|
|||
@security_policy = options[:security_policy]
|
||||
@user_install = options[:user_install]
|
||||
@wrappers = options[:wrappers]
|
||||
@build_docs_in_background = options[:build_docs_in_background]
|
||||
|
||||
# Indicates that we should not try to update any deps unless
|
||||
# we absolutely must.
|
||||
@minimal_deps = options[:minimal_deps]
|
||||
|
||||
@installed_gems = []
|
||||
@toplevel_specs = nil
|
||||
|
||||
@install_dir = options[:install_dir] || Gem.dir
|
||||
@cache_dir = options[:cache_dir] || @install_dir
|
||||
|
@ -76,6 +96,24 @@ class Gem::DependencyInstaller
|
|||
@errors = nil
|
||||
end
|
||||
|
||||
attr_reader :errors
|
||||
|
||||
##
|
||||
# Indicated, based on the requested domain, if local
|
||||
# gems should be considered.
|
||||
|
||||
def consider_local?
|
||||
@domain == :both or @domain == :local
|
||||
end
|
||||
|
||||
##
|
||||
# Indicated, based on the requested domain, if remote
|
||||
# gems should be considered.
|
||||
|
||||
def consider_remote?
|
||||
@domain == :both or @domain == :remote
|
||||
end
|
||||
|
||||
##
|
||||
# Returns a list of pairs of gemspecs and source_uris that match
|
||||
# Gem::Dependency +dep+ from both local (Dir.pwd) and remote (Gem.sources)
|
||||
|
@ -83,35 +121,34 @@ class Gem::DependencyInstaller
|
|||
# local gems preferred over remote gems.
|
||||
|
||||
def find_gems_with_sources(dep)
|
||||
# Reset the errors
|
||||
@errors = nil
|
||||
gems_and_sources = []
|
||||
set = Gem::AvailableSet.new
|
||||
|
||||
if @domain == :both or @domain == :local then
|
||||
Dir[File.join(Dir.pwd, "#{dep.name}-[0-9]*.gem")].each do |gem_file|
|
||||
spec = Gem::Format.from_file_by_path(gem_file).spec
|
||||
gems_and_sources << [spec, gem_file] if spec.name == dep.name
|
||||
if consider_local?
|
||||
sl = Gem::Source::Local.new
|
||||
|
||||
if spec = sl.find_gem(dep.name)
|
||||
if dep.matches_spec? spec
|
||||
set.add spec, sl
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if @domain == :both or @domain == :remote then
|
||||
if consider_remote?
|
||||
begin
|
||||
# REFACTOR: all = dep.requirement.needs_all?
|
||||
requirements = dep.requirement.requirements.map do |req, ver|
|
||||
req
|
||||
found, errors = Gem::SpecFetcher.fetcher.spec_for_dependency dep
|
||||
|
||||
if @errors
|
||||
@errors += errors
|
||||
else
|
||||
@errors = errors
|
||||
end
|
||||
|
||||
all = !dep.prerelease? &&
|
||||
# we only need latest if there's one requirement and it is
|
||||
# guaranteed to match the newest specs
|
||||
(requirements.length > 1 or
|
||||
(requirements.first != ">=" and requirements.first != ">"))
|
||||
|
||||
found, @errors = Gem::SpecFetcher.fetcher.fetch_with_errors dep, all, true, dep.prerelease?
|
||||
|
||||
gems_and_sources.push(*found)
|
||||
set << found
|
||||
|
||||
rescue Gem::RemoteFetcher::FetchError => e
|
||||
# FIX if there is a problem talking to the network, we either need to always tell
|
||||
# the user (no really_verbose) or fail hard, not silently tell them that we just
|
||||
# couldn't find their requested gem.
|
||||
if Gem.configuration.really_verbose then
|
||||
say "Error fetching remote data:\t\t#{e.message}"
|
||||
say "Falling back to local-only install"
|
||||
|
@ -120,9 +157,7 @@ class Gem::DependencyInstaller
|
|||
end
|
||||
end
|
||||
|
||||
gems_and_sources.sort_by do |gem, source|
|
||||
[gem, source =~ /^http:\/\// ? 0 : 1] # local gems win
|
||||
end
|
||||
set
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -130,17 +165,22 @@ class Gem::DependencyInstaller
|
|||
# remote sources unless the ignore_dependencies was given.
|
||||
|
||||
def gather_dependencies
|
||||
specs = @specs_and_sources.map { |spec,_| spec }
|
||||
specs = @available.all_specs
|
||||
|
||||
# these gems were listed by the user, always install them
|
||||
keep_names = specs.map { |spec| spec.full_name }
|
||||
|
||||
if @dev_shallow
|
||||
@toplevel_specs = keep_names
|
||||
end
|
||||
|
||||
dependency_list = Gem::DependencyList.new @development
|
||||
dependency_list.add(*specs)
|
||||
to_do = specs.dup
|
||||
|
||||
add_found_dependencies to_do, dependency_list unless @ignore_dependencies
|
||||
|
||||
# REFACTOR maybe abstract away using Gem::Specification.include? so
|
||||
# that this isn't dependent only on the currently installed gems
|
||||
dependency_list.specs.reject! { |spec|
|
||||
not keep_names.include?(spec.full_name) and
|
||||
Gem::Specification.include?(spec)
|
||||
|
@ -162,32 +202,43 @@ class Gem::DependencyInstaller
|
|||
|
||||
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
|
||||
deps |= spec.development_dependencies if @development
|
||||
|
||||
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
|
||||
|
||||
results = find_gems_with_sources(dep).reverse
|
||||
|
||||
results.reject! do |dep_spec,|
|
||||
to_do.push dep_spec
|
||||
|
||||
# already locally installed
|
||||
Gem::Specification.any? do |installed_spec|
|
||||
dep.name == installed_spec.name and
|
||||
dep.requirement.satisfied_by? installed_spec.version
|
||||
end
|
||||
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.each do |dep_spec, source_uri|
|
||||
@specs_and_sources << [dep_spec, source_uri]
|
||||
results = find_gems_with_sources(dep)
|
||||
|
||||
dependency_list.add dep_spec
|
||||
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
|
||||
|
||||
|
@ -202,42 +253,36 @@ class Gem::DependencyInstaller
|
|||
def find_spec_by_name_and_version(gem_name,
|
||||
version = Gem::Requirement.default,
|
||||
prerelease = false)
|
||||
spec_and_source = nil
|
||||
|
||||
glob = if File::ALT_SEPARATOR then
|
||||
gem_name.gsub File::ALT_SEPARATOR, File::SEPARATOR
|
||||
else
|
||||
gem_name
|
||||
end
|
||||
set = Gem::AvailableSet.new
|
||||
|
||||
local_gems = Dir["#{glob}*"].sort.reverse
|
||||
if consider_local?
|
||||
if File.exists? gem_name
|
||||
src = Gem::Source::SpecificFile.new(gem_name)
|
||||
set.add src.spec, src
|
||||
else
|
||||
local = Gem::Source::Local.new
|
||||
|
||||
local_gems.each do |gem_file|
|
||||
next unless gem_file =~ /gem$/
|
||||
begin
|
||||
spec = Gem::Format.from_file_by_path(gem_file).spec
|
||||
spec_and_source = [spec, gem_file]
|
||||
break
|
||||
rescue SystemCallError, Gem::Package::FormatError
|
||||
if s = local.find_gem(gem_name, version)
|
||||
set.add s, local
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
unless spec_and_source then
|
||||
if set.empty?
|
||||
dep = Gem::Dependency.new gem_name, version
|
||||
# HACK Dependency objects should be immutable
|
||||
dep.prerelease = true if prerelease
|
||||
spec_and_sources = find_gems_with_sources(dep).reverse
|
||||
spec_and_source = spec_and_sources.find { |spec, source|
|
||||
Gem::Platform.match spec.platform
|
||||
}
|
||||
|
||||
set = find_gems_with_sources(dep)
|
||||
set.match_platform!
|
||||
end
|
||||
|
||||
if spec_and_source.nil? then
|
||||
raise Gem::GemNotFoundException.new(
|
||||
"Could not find a valid gem '#{gem_name}' (#{version}) locally or in a repository",
|
||||
gem_name, version, @errors)
|
||||
if set.empty?
|
||||
raise Gem::SpecificGemNotFoundException.new(gem_name, version, @errors)
|
||||
end
|
||||
|
||||
@specs_and_sources = [spec_and_source]
|
||||
@available = set
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -258,33 +303,49 @@ class Gem::DependencyInstaller
|
|||
if String === dep_or_name then
|
||||
find_spec_by_name_and_version dep_or_name, version, @prerelease
|
||||
else
|
||||
dep_or_name.prerelease = @prerelease
|
||||
@specs_and_sources = [find_gems_with_sources(dep_or_name).last]
|
||||
dep = dep_or_name.dup
|
||||
dep.prerelease = @prerelease
|
||||
@available = find_gems_with_sources(dep).pick_best!
|
||||
end
|
||||
|
||||
@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_uri = @specs_and_sources.assoc spec
|
||||
source = @available.source_for spec
|
||||
|
||||
begin
|
||||
local_gem_path = Gem::RemoteFetcher.fetcher.download spec, source_uri,
|
||||
@cache_dir
|
||||
# REFACTOR make the fetcher to use configurable
|
||||
local_gem_path = source.download spec, @cache_dir
|
||||
rescue Gem::RemoteFetcher::FetchError
|
||||
# TODO I doubt all fetch errors are recoverable, we should at least
|
||||
# report the errors probably.
|
||||
next if @force
|
||||
raise
|
||||
end
|
||||
|
||||
if @development
|
||||
if @dev_shallow
|
||||
is_dev = @toplevel_specs.include? spec.full_name
|
||||
else
|
||||
is_dev = true
|
||||
end
|
||||
end
|
||||
|
||||
inst = Gem::Installer.new local_gem_path,
|
||||
:bin_dir => @bin_dir,
|
||||
:development => @development,
|
||||
:development => is_dev,
|
||||
:env_shebang => @env_shebang,
|
||||
:force => @force,
|
||||
:format_executable => @format_executable,
|
||||
|
@ -299,6 +360,33 @@ class Gem::DependencyInstaller
|
|||
@installed_gems << spec
|
||||
end
|
||||
|
||||
# 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.
|
||||
in_background "Installing documentation" do
|
||||
start = Time.now
|
||||
Gem.done_installing_hooks.each do |hook|
|
||||
hook.call self, @installed_gems
|
||||
end
|
||||
finish = Time.now
|
||||
say "Done installing documentation for #{@installed_gems.map(&:name).join(', ')} (#{(finish-start).to_i} sec)."
|
||||
end unless Gem.done_installing_hooks.empty?
|
||||
|
||||
@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
|
||||
end
|
||||
yield unless fork_happened
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,6 +10,10 @@ require 'rubygems/deprecate'
|
|||
##
|
||||
# Gem::DependencyList is used for installing and uninstalling gems in the
|
||||
# correct order to avoid conflicts.
|
||||
#--
|
||||
# TODO: It appears that all but topo-sort functionality is being duplicated
|
||||
# (or is planned to be duplicated) elsewhere in rubygems. Is the majority of
|
||||
# this class necessary anymore? Especially #ok?, #why_not_ok?
|
||||
|
||||
class Gem::DependencyList
|
||||
attr_reader :specs
|
||||
|
@ -27,19 +31,10 @@ class Gem::DependencyList
|
|||
|
||||
def self.from_specs
|
||||
list = new
|
||||
list.add(*Gem::Specification.map)
|
||||
list.add(*Gem::Specification.to_a)
|
||||
list
|
||||
end
|
||||
|
||||
##
|
||||
# Creates a DependencyList from a Gem::SourceIndex +source_index+
|
||||
|
||||
def self.from_source_index(ignored=nil)
|
||||
warn "NOTE: DependencyList.from_source_index ignores it's arg" if ignored
|
||||
|
||||
from_specs
|
||||
end
|
||||
|
||||
##
|
||||
# Creates a new DependencyList. If +development+ is true, development
|
||||
# dependencies will be included.
|
||||
|
@ -143,7 +138,7 @@ class Gem::DependencyList
|
|||
# If removing the gemspec creates breaks a currently ok dependency, then it
|
||||
# is NOT ok to remove the gemspec.
|
||||
|
||||
def ok_to_remove?(full_name)
|
||||
def ok_to_remove?(full_name, check_dev=true)
|
||||
gem_to_remove = find_name full_name
|
||||
|
||||
siblings = @specs.find_all { |s|
|
||||
|
@ -154,7 +149,9 @@ class Gem::DependencyList
|
|||
deps = []
|
||||
|
||||
@specs.each do |spec|
|
||||
spec.dependencies.each do |dep|
|
||||
check = check_dev ? spec.dependencies : spec.runtime_dependencies
|
||||
|
||||
check.each do |dep|
|
||||
deps << dep if gem_to_remove.satisfies_requirement?(dep)
|
||||
end
|
||||
end
|
||||
|
@ -213,7 +210,7 @@ class Gem::DependencyList
|
|||
@specs.each(&block)
|
||||
end
|
||||
|
||||
def tsort_each_child(node, &block)
|
||||
def tsort_each_child(node)
|
||||
specs = @specs.sort.reverse
|
||||
|
||||
dependencies = node.runtime_dependencies
|
||||
|
@ -242,11 +239,6 @@ class Gem::DependencyList
|
|||
def active_count(specs, ignored)
|
||||
specs.count { |spec| ignored[spec.full_name].nil? }
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class Gem::DependencyList
|
||||
class << self
|
||||
extend Gem::Deprecate
|
||||
deprecate :from_source_index, "from_specs", 2011, 11
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,562 @@
|
|||
require 'rubygems'
|
||||
require 'rubygems/dependency'
|
||||
require 'rubygems/exceptions'
|
||||
|
||||
require 'uri'
|
||||
require 'net/http'
|
||||
|
||||
module Gem
|
||||
|
||||
# Raised when a DependencyConflict reaches the toplevel.
|
||||
# Indicates which dependencies were incompatible.
|
||||
#
|
||||
class DependencyResolutionError < Gem::Exception
|
||||
def initialize(conflict)
|
||||
@conflict = conflict
|
||||
a, b = conflicting_dependencies
|
||||
|
||||
super "unable to resolve conflicting dependencies '#{a}' and '#{b}'"
|
||||
end
|
||||
|
||||
attr_reader :conflict
|
||||
|
||||
def conflicting_dependencies
|
||||
@conflict.conflicting_dependencies
|
||||
end
|
||||
end
|
||||
|
||||
# Raised when a dependency requests a gem for which there is
|
||||
# no spec.
|
||||
#
|
||||
class UnsatisfiableDepedencyError < Gem::Exception
|
||||
def initialize(dep)
|
||||
super "unable to find any gem matching dependency '#{dep}'"
|
||||
|
||||
@dependency = dep
|
||||
end
|
||||
|
||||
attr_reader :dependency
|
||||
end
|
||||
|
||||
# Raised when dependencies conflict and create the inability to
|
||||
# find a valid possible spec for a request.
|
||||
#
|
||||
class ImpossibleDependenciesError < Gem::Exception
|
||||
def initialize(request, conflicts)
|
||||
s = conflicts.size == 1 ? "" : "s"
|
||||
super "detected #{conflicts.size} conflict#{s} with dependency '#{request.dependency}'"
|
||||
@request = request
|
||||
@conflicts = conflicts
|
||||
end
|
||||
|
||||
def dependency
|
||||
@request.dependency
|
||||
end
|
||||
|
||||
attr_reader :conflicts
|
||||
end
|
||||
|
||||
# Given a set of Gem::Dependency objects as +needed+ and a way
|
||||
# to query the set of available specs via +set+, calculates
|
||||
# a set of ActivationRequest objects which indicate all the specs
|
||||
# that should be activated to meet the all the requirements.
|
||||
#
|
||||
class DependencyResolver
|
||||
|
||||
# Represents a specification retrieved via the rubygems.org
|
||||
# API. This is used to avoid having to load the full
|
||||
# Specification object when all we need is the name, version,
|
||||
# and dependencies.
|
||||
#
|
||||
class APISpecification
|
||||
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 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] = [] }
|
||||
end
|
||||
|
||||
# Return data for all versions of the gem +name+.
|
||||
#
|
||||
def versions(name)
|
||||
if @data.key?(name)
|
||||
return @data[name]
|
||||
end
|
||||
|
||||
u = URI.parse "http://rubygems.org/api/v1/dependencies?gems=#{name}"
|
||||
str = Net::HTTP.get(u)
|
||||
|
||||
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?
|
||||
|
||||
u = URI.parse "http://rubygems.org/api/v1/dependencies?gems=#{needed.join ','}"
|
||||
str = Net::HTTP.get(u)
|
||||
|
||||
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
|
||||
|
||||
# Return the 2 dependency objects that conflicted
|
||||
#
|
||||
def conflicting_dependencies
|
||||
[@failed_dep.dependency, @activated.request.dependency]
|
||||
end
|
||||
end
|
||||
|
||||
# Used Internally. Wraps a Depedency object to also track
|
||||
# which spec contained the Dependency.
|
||||
#
|
||||
class DependencyRequest
|
||||
def initialize(dep, act)
|
||||
@dependency = dep
|
||||
@requester = act
|
||||
end
|
||||
|
||||
attr_reader :dependency, :requester
|
||||
|
||||
def name
|
||||
@dependency.name
|
||||
end
|
||||
|
||||
def matches_spec?(spec)
|
||||
@dependency.matches_spec? spec
|
||||
end
|
||||
|
||||
def to_s
|
||||
@dependency.to_s
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
case other
|
||||
when Dependency
|
||||
@dependency == other
|
||||
when DependencyRequest
|
||||
@dependency == other.dep && @requester == other.requester
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Specifies a Specification object that should be activated.
|
||||
# Also contains a dependency that was used to introduce this
|
||||
# activation.
|
||||
#
|
||||
class ActivationRequest
|
||||
def initialize(spec, req, others_possible=true)
|
||||
@spec = spec
|
||||
@request = req
|
||||
@others_possible = others_possible
|
||||
end
|
||||
|
||||
attr_reader :spec, :request
|
||||
|
||||
# Indicate if this activation is one of a set of possible
|
||||
# requests for the same Dependency request.
|
||||
#
|
||||
def others_possible?
|
||||
@others_possible
|
||||
end
|
||||
|
||||
# Return the ActivationRequest that contained the dependency
|
||||
# that we were activated for.
|
||||
#
|
||||
def parent
|
||||
@request.requester
|
||||
end
|
||||
|
||||
def name
|
||||
@spec.name
|
||||
end
|
||||
|
||||
def full_name
|
||||
@spec.full_name
|
||||
end
|
||||
|
||||
def version
|
||||
@spec.version
|
||||
end
|
||||
|
||||
def full_spec
|
||||
Gem::Specification === @spec ? @spec : @spec.spec
|
||||
end
|
||||
|
||||
def download(path)
|
||||
if @spec.respond_to? :source
|
||||
source = @spec.source
|
||||
else
|
||||
source = Gem.sources.first
|
||||
end
|
||||
|
||||
source.download full_spec, path
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
case other
|
||||
when Gem::Specification
|
||||
@spec == other
|
||||
when ActivationRequest
|
||||
@spec == other.spec && @request == other.request
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Indicates if the requested gem has already been installed.
|
||||
|
||||
def installed?
|
||||
this_spec = full_spec
|
||||
|
||||
Gem::Specification.any? do |s|
|
||||
s == this_spec
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def requests(s, act)
|
||||
reqs = []
|
||||
s.dependencies.each do |d|
|
||||
next unless d.type == :runtime
|
||||
reqs << DependencyRequest.new(d, act)
|
||||
end
|
||||
|
||||
@set.prefetch(reqs)
|
||||
|
||||
reqs
|
||||
end
|
||||
|
||||
# The meat of the algorithm. Given +needed+ DependencyRequest objects
|
||||
# and +specs+ being a list to ActivationRequest, calculate a new list
|
||||
# of ActivationRequest objects.
|
||||
#
|
||||
def resolve_for(needed, specs)
|
||||
until needed.empty?
|
||||
dep = needed.shift
|
||||
|
||||
# If there is already a spec activated for the requested name...
|
||||
if existing = specs.find { |s| dep.name == s.name }
|
||||
|
||||
# then we're done since this new dep matches the
|
||||
# existing spec.
|
||||
next if dep.matches_spec? existing
|
||||
|
||||
# There is a conflict! We return the conflict
|
||||
# object which will be seen by the caller and be
|
||||
# handled at the right level.
|
||||
|
||||
# If the existing activation indicates that there
|
||||
# are other possibles for it, then issue the conflict
|
||||
# on the dep for the activation itself. Otherwise, issue
|
||||
# it on the requester's request itself.
|
||||
#
|
||||
if existing.others_possible?
|
||||
conflict = DependencyConflict.new(dep, existing)
|
||||
else
|
||||
depreq = existing.request.requester.request
|
||||
conflict = DependencyConflict.new(depreq, existing, dep)
|
||||
end
|
||||
@conflicts << conflict
|
||||
|
||||
return conflict
|
||||
end
|
||||
|
||||
# Get a list of all specs that satisfy dep
|
||||
possible = @set.find_all(dep)
|
||||
|
||||
case possible.size
|
||||
when 0
|
||||
# If there are none, then our work here is done.
|
||||
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
|
||||
end
|
||||
|
||||
specs
|
||||
end
|
||||
end
|
||||
end
|
|
@ -20,51 +20,51 @@
|
|||
# end
|
||||
# end
|
||||
|
||||
module Gem
|
||||
module Deprecate
|
||||
module Gem::Deprecate
|
||||
|
||||
def self.skip # :nodoc:
|
||||
@skip ||= false
|
||||
end
|
||||
|
||||
def self.skip= v # :nodoc:
|
||||
@skip = v
|
||||
end
|
||||
|
||||
##
|
||||
# Temporarily turn off warnings. Intended for tests only.
|
||||
|
||||
def skip_during
|
||||
Gem::Deprecate.skip, original = true, Gem::Deprecate.skip
|
||||
yield
|
||||
ensure
|
||||
Gem::Deprecate.skip = original
|
||||
end
|
||||
|
||||
##
|
||||
# Simple deprecation method that deprecates +name+ by wrapping it up
|
||||
# in a dummy method. It warns on each call to the dummy method
|
||||
# telling the user of +repl+ (unless +repl+ is :none) and the
|
||||
# year/month that it is planned to go away.
|
||||
|
||||
def deprecate name, repl, year, month
|
||||
class_eval {
|
||||
old = "_deprecated_#{name}"
|
||||
alias_method old, name
|
||||
define_method name do |*args, &block| # TODO: really works on 1.8.7?
|
||||
klass = self.kind_of? Module
|
||||
target = klass ? "#{self}." : "#{self.class}#"
|
||||
msg = [ "NOTE: #{target}#{name} is deprecated",
|
||||
repl == :none ? " with no replacement" : ", use #{repl}",
|
||||
". It will be removed on or after %4d-%02d-01." % [year, month],
|
||||
"\n#{target}#{name} called from #{Gem.location_of_caller.join(":")}",
|
||||
]
|
||||
warn "#{msg.join}." unless Gem::Deprecate.skip
|
||||
send old, *args, &block
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
module_function :deprecate, :skip_during
|
||||
def self.skip # :nodoc:
|
||||
@skip ||= false
|
||||
end
|
||||
|
||||
def self.skip= v # :nodoc:
|
||||
@skip = v
|
||||
end
|
||||
|
||||
##
|
||||
# Temporarily turn off warnings. Intended for tests only.
|
||||
|
||||
def skip_during
|
||||
Gem::Deprecate.skip, original = true, Gem::Deprecate.skip
|
||||
yield
|
||||
ensure
|
||||
Gem::Deprecate.skip = original
|
||||
end
|
||||
|
||||
##
|
||||
# Simple deprecation method that deprecates +name+ by wrapping it up
|
||||
# in a dummy method. It warns on each call to the dummy method
|
||||
# telling the user of +repl+ (unless +repl+ is :none) and the
|
||||
# year/month that it is planned to go away.
|
||||
|
||||
def deprecate name, repl, year, month
|
||||
class_eval {
|
||||
old = "_deprecated_#{name}"
|
||||
alias_method old, name
|
||||
define_method name do |*args, &block| # TODO: really works on 1.8.7?
|
||||
klass = self.kind_of? Module
|
||||
target = klass ? "#{self}." : "#{self.class}#"
|
||||
msg = [ "NOTE: #{target}#{name} is deprecated",
|
||||
repl == :none ? " with no replacement" : "; use #{repl} instead",
|
||||
". It will be removed on or after %4d-%02d-01." % [year, month],
|
||||
"\n#{target}#{name} called from #{Gem.location_of_caller.join(":")}",
|
||||
]
|
||||
warn "#{msg.join}." unless Gem::Deprecate.skip
|
||||
send old, *args, &block
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
module_function :deprecate, :skip_during
|
||||
|
||||
end
|
||||
|
||||
|
|
|
@ -1,243 +0,0 @@
|
|||
#--
|
||||
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
|
||||
# All rights reserved.
|
||||
# See LICENSE.txt for permissions.
|
||||
#++
|
||||
|
||||
require 'rubygems'
|
||||
|
||||
##
|
||||
# The documentation manager generates RDoc and RI for RubyGems.
|
||||
|
||||
class Gem::DocManager
|
||||
|
||||
include Gem::UserInteraction
|
||||
|
||||
@configured_args = []
|
||||
|
||||
def self.configured_args
|
||||
@configured_args ||= []
|
||||
end
|
||||
|
||||
def self.configured_args=(args)
|
||||
case args
|
||||
when Array
|
||||
@configured_args = args
|
||||
when String
|
||||
@configured_args = args.split
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Load RDoc from a gem if it is available, otherwise from Ruby's stdlib
|
||||
|
||||
def self.load_rdoc
|
||||
begin
|
||||
gem 'rdoc'
|
||||
rescue Gem::LoadError
|
||||
# use built-in RDoc
|
||||
end
|
||||
|
||||
begin
|
||||
require 'rdoc/rdoc'
|
||||
|
||||
@rdoc_version = if defined? RDoc::VERSION then
|
||||
Gem::Version.new RDoc::VERSION
|
||||
else
|
||||
Gem::Version.new '1.0.1' # HACK parsing is hard
|
||||
end
|
||||
|
||||
rescue LoadError => e
|
||||
raise Gem::DocumentError,
|
||||
"ERROR: RDoc documentation generator not installed: #{e}"
|
||||
end
|
||||
end
|
||||
|
||||
def self.rdoc_version
|
||||
@rdoc_version
|
||||
end
|
||||
|
||||
##
|
||||
# Updates the RI cache for RDoc 2 if it is installed
|
||||
|
||||
def self.update_ri_cache
|
||||
load_rdoc rescue return
|
||||
|
||||
return unless defined? RDoc::VERSION # RDoc 1 does not have VERSION
|
||||
|
||||
require 'rdoc/ri/driver'
|
||||
|
||||
options = {
|
||||
:use_cache => true,
|
||||
:use_system => true,
|
||||
:use_site => true,
|
||||
:use_home => true,
|
||||
:use_gems => true,
|
||||
:formatter => RDoc::RI::Formatter,
|
||||
}
|
||||
|
||||
RDoc::RI::Driver.new(options).class_cache
|
||||
end
|
||||
|
||||
##
|
||||
# Create a document manager for +spec+. +rdoc_args+ contains arguments for
|
||||
# RDoc (template etc.) as a String.
|
||||
|
||||
def initialize(spec, rdoc_args="")
|
||||
require 'fileutils'
|
||||
@spec = spec
|
||||
@doc_dir = spec.doc_dir
|
||||
@rdoc_args = rdoc_args.nil? ? [] : rdoc_args.split
|
||||
end
|
||||
|
||||
##
|
||||
# Is the RDoc documentation installed?
|
||||
|
||||
def rdoc_installed?
|
||||
File.exist?(File.join(@doc_dir, "rdoc"))
|
||||
end
|
||||
|
||||
##
|
||||
# Is the RI documentation installed?
|
||||
|
||||
def ri_installed?
|
||||
File.exist?(File.join(@doc_dir, "ri"))
|
||||
end
|
||||
|
||||
##
|
||||
# Generate the RI documents for this gem spec.
|
||||
#
|
||||
# Note that if both RI and RDoc documents are generated from the same
|
||||
# process, the RI docs should be done first (a likely bug in RDoc will cause
|
||||
# RI docs generation to fail if run after RDoc).
|
||||
|
||||
def generate_ri
|
||||
setup_rdoc
|
||||
install_ri # RDoc bug, ri goes first
|
||||
|
||||
FileUtils.mkdir_p @doc_dir unless File.exist?(@doc_dir)
|
||||
end
|
||||
|
||||
##
|
||||
# Generate the RDoc documents for this gem spec.
|
||||
#
|
||||
# Note that if both RI and RDoc documents are generated from the same
|
||||
# process, the RI docs should be done first (a likely bug in RDoc will cause
|
||||
# RI docs generation to fail if run after RDoc).
|
||||
|
||||
def generate_rdoc
|
||||
setup_rdoc
|
||||
install_rdoc
|
||||
|
||||
FileUtils.mkdir_p @doc_dir unless File.exist?(@doc_dir)
|
||||
end
|
||||
|
||||
##
|
||||
# Generate and install RDoc into the documentation directory
|
||||
|
||||
def install_rdoc
|
||||
rdoc_dir = File.join @doc_dir, 'rdoc'
|
||||
|
||||
FileUtils.rm_rf rdoc_dir
|
||||
|
||||
say "Installing RDoc documentation for #{@spec.full_name}..."
|
||||
run_rdoc '--op', rdoc_dir
|
||||
end
|
||||
|
||||
##
|
||||
# Generate and install RI into the documentation directory
|
||||
|
||||
def install_ri
|
||||
ri_dir = File.join @doc_dir, 'ri'
|
||||
|
||||
FileUtils.rm_rf ri_dir
|
||||
|
||||
say "Installing ri documentation for #{@spec.full_name}..."
|
||||
run_rdoc '--ri', '--op', ri_dir
|
||||
end
|
||||
|
||||
##
|
||||
# Run RDoc with +args+, which is an ARGV style argument list
|
||||
|
||||
def run_rdoc(*args)
|
||||
args << @spec.rdoc_options
|
||||
args << self.class.configured_args
|
||||
args << @spec.require_paths.clone
|
||||
args << @spec.extra_rdoc_files
|
||||
args << '--title' << "#{@spec.full_name} Documentation"
|
||||
args << '--quiet'
|
||||
args = args.flatten.map do |arg| arg.to_s end
|
||||
|
||||
if self.class.rdoc_version >= Gem::Version.new('2.4.0') then
|
||||
args.delete '--inline-source'
|
||||
args.delete '--promiscuous'
|
||||
args.delete '-p'
|
||||
args.delete '--one-file'
|
||||
# HACK more
|
||||
end
|
||||
|
||||
debug_args = args.dup
|
||||
|
||||
r = RDoc::RDoc.new
|
||||
|
||||
old_pwd = Dir.pwd
|
||||
Dir.chdir @spec.full_gem_path
|
||||
|
||||
say "rdoc #{args.join ' '}" if Gem.configuration.really_verbose
|
||||
|
||||
begin
|
||||
r.document args
|
||||
rescue Errno::EACCES => e
|
||||
dirname = File.dirname e.message.split("-")[1].strip
|
||||
raise Gem::FilePermissionError.new(dirname)
|
||||
rescue Interrupt => e
|
||||
raise e
|
||||
rescue Exception => ex
|
||||
alert_error "While generating documentation for #{@spec.full_name}"
|
||||
ui.errs.puts "... MESSAGE: #{ex}"
|
||||
ui.errs.puts "... RDOC args: #{debug_args.join(' ')}"
|
||||
ui.errs.puts "\t#{ex.backtrace.join "\n\t"}" if
|
||||
Gem.configuration.backtrace
|
||||
terminate_interaction 1
|
||||
ensure
|
||||
Dir.chdir old_pwd
|
||||
end
|
||||
end
|
||||
|
||||
def setup_rdoc
|
||||
if File.exist?(@doc_dir) && !File.writable?(@doc_dir) then
|
||||
raise Gem::FilePermissionError.new(@doc_dir)
|
||||
end
|
||||
|
||||
FileUtils.mkdir_p @doc_dir unless File.exist?(@doc_dir)
|
||||
|
||||
self.class.load_rdoc
|
||||
end
|
||||
|
||||
##
|
||||
# Remove RDoc and RI documentation
|
||||
|
||||
def uninstall_doc
|
||||
base_dir = @spec.base_dir
|
||||
raise Gem::FilePermissionError.new base_dir unless File.writable? base_dir
|
||||
|
||||
# TODO: ok... that's twice... ugh
|
||||
old_name = [
|
||||
@spec.name, @spec.version, @spec.original_platform].join '-'
|
||||
|
||||
doc_dir = @spec.doc_dir
|
||||
unless File.directory? doc_dir then
|
||||
doc_dir = File.join File.dirname(doc_dir), old_name
|
||||
end
|
||||
|
||||
ri_dir = @spec.ri_dir
|
||||
unless File.directory? ri_dir then
|
||||
ri_dir = File.join File.dirname(ri_dir), old_name
|
||||
end
|
||||
|
||||
FileUtils.rm_rf doc_dir
|
||||
FileUtils.rm_rf ri_dir
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -1,35 +1,88 @@
|
|||
class Gem::ErrorReason; end
|
||||
|
||||
# Generated when trying to lookup a gem to indicate that the gem
|
||||
# was found, but that it isn't usable on the current platform.
|
||||
##
|
||||
# This file contains all the various exceptions and other errors that are used
|
||||
# inside of RubyGems.
|
||||
#
|
||||
# fetch and install read these and report them to the user to aid
|
||||
# in figuring out why a gem couldn't be installed.
|
||||
#
|
||||
class Gem::PlatformMismatch < Gem::ErrorReason
|
||||
# DOC: Confirm _all_
|
||||
|
||||
attr_reader :name
|
||||
attr_reader :version
|
||||
attr_reader :platforms
|
||||
module Gem
|
||||
##
|
||||
# Raised when RubyGems is unable to load or activate a gem. Contains the
|
||||
# name and version requirements of the gem that either conflicts with
|
||||
# already activated gems or that RubyGems is otherwise unable to activate.
|
||||
|
||||
def initialize(name, version)
|
||||
@name = name
|
||||
@version = version
|
||||
@platforms = []
|
||||
class LoadError < ::LoadError
|
||||
# Name of gem
|
||||
attr_accessor :name
|
||||
|
||||
# Version requirement of gem
|
||||
attr_accessor :requirement
|
||||
end
|
||||
|
||||
def add_platform(platform)
|
||||
@platforms << platform
|
||||
end
|
||||
# FIX: does this need to exist? The subclass is the only other reference
|
||||
# I can find.
|
||||
class ErrorReason; end
|
||||
|
||||
def wordy
|
||||
prefix = "Found #{@name} (#{@version})"
|
||||
# Generated when trying to lookup a gem to indicate that the gem
|
||||
# was found, but that it isn't usable on the current platform.
|
||||
#
|
||||
# fetch and install read these and report them to the user to aid
|
||||
# in figuring out why a gem couldn't be installed.
|
||||
#
|
||||
class PlatformMismatch < ErrorReason
|
||||
|
||||
if @platforms.size == 1
|
||||
"#{prefix}, but was for platform #{@platforms[0]}"
|
||||
else
|
||||
"#{prefix}, but was for platforms #{@platforms.join(' ,')}"
|
||||
##
|
||||
# the name of the gem
|
||||
attr_reader :name
|
||||
|
||||
##
|
||||
# the version
|
||||
attr_reader :version
|
||||
|
||||
##
|
||||
# The platforms that are mismatched
|
||||
attr_reader :platforms
|
||||
|
||||
def initialize(name, version)
|
||||
@name = name
|
||||
@version = version
|
||||
@platforms = []
|
||||
end
|
||||
|
||||
##
|
||||
# append a platform to the list of mismatched platforms.
|
||||
#
|
||||
# Platforms are added via this instead of injected via the constructor
|
||||
# so that we can loop over a list of mismatches and just add them rather
|
||||
# than perform some kind of calculation mismatch summary before creation.
|
||||
def add_platform(platform)
|
||||
@platforms << platform
|
||||
end
|
||||
|
||||
##
|
||||
# A wordy description of the error.
|
||||
def wordy
|
||||
"Found %s (%), but was for platform%s %s" %
|
||||
[@name,
|
||||
@version,
|
||||
@platforms.size == 1 ? 's' : '',
|
||||
@platforms.join(' ,')]
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# An error that indicates we weren't able to fetch some
|
||||
# data from a source
|
||||
|
||||
class SourceFetchProblem < ErrorReason
|
||||
def initialize(source, error)
|
||||
@source = source
|
||||
@error = error
|
||||
end
|
||||
|
||||
attr_reader :source, :error
|
||||
|
||||
def wordy
|
||||
"Unable to download data from #{@source.uri} - #{@error.message}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,7 +1,14 @@
|
|||
# TODO: the documentation in here is terrible.
|
||||
#
|
||||
# Each exception needs a brief description and the scenarios where it is
|
||||
# likely to be raised
|
||||
|
||||
##
|
||||
# Base exception class for RubyGems. All exception raised by RubyGems are a
|
||||
# subclass of this one.
|
||||
class Gem::Exception < RuntimeError; end
|
||||
class Gem::Exception < RuntimeError
|
||||
attr_accessor :source_exception
|
||||
end
|
||||
|
||||
class Gem::CommandLineError < Gem::Exception; end
|
||||
|
||||
|
@ -24,11 +31,18 @@ class Gem::EndOfYAMLException < Gem::Exception; end
|
|||
|
||||
##
|
||||
# Signals that a file permission error is preventing the user from
|
||||
# installing in the requested directories.
|
||||
# operating on the given directory.
|
||||
|
||||
class Gem::FilePermissionError < Gem::Exception
|
||||
def initialize(path)
|
||||
super("You don't have write permissions into the #{path} directory.")
|
||||
|
||||
attr_reader :directory
|
||||
|
||||
def initialize directory
|
||||
@directory = directory
|
||||
|
||||
super "You don't have write permissions for the #{directory} directory."
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -37,9 +51,12 @@ class Gem::FormatException < Gem::Exception
|
|||
attr_accessor :file_path
|
||||
end
|
||||
|
||||
class Gem::GemNotFoundException < Gem::Exception
|
||||
def initialize(msg, name=nil, version=nil, errors=nil)
|
||||
super msg
|
||||
class Gem::GemNotFoundException < Gem::Exception; end
|
||||
|
||||
class Gem::SpecificGemNotFoundException < Gem::GemNotFoundException
|
||||
def initialize(name, version, errors=nil)
|
||||
super "Could not find a valid gem '#{name}' (#{version}) locally or in a repository"
|
||||
|
||||
@name = name
|
||||
@version = version
|
||||
@errors = errors
|
||||
|
@ -89,3 +106,4 @@ class Gem::SystemExitException < SystemExit
|
|||
end
|
||||
|
||||
end
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ class Gem::Ext::Builder
|
|||
raise Gem::InstallError, "Makefile not found:\n\n#{results.join "\n"}"
|
||||
end
|
||||
|
||||
mf = File.read('Makefile')
|
||||
mf = Gem.read_binary 'Makefile'
|
||||
mf = mf.gsub(/^RUBYARCHDIR\s*=\s*\$[^$]*/, "RUBYARCHDIR = #{dest_path}")
|
||||
mf = mf.gsub(/^RUBYLIBDIR\s*=\s*\$[^$]*/, "RUBYLIBDIR = #{dest_path}")
|
||||
|
||||
|
@ -24,18 +24,14 @@ 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']
|
||||
make_program = $1 || ENV['MAKE'] || ENV['make']
|
||||
unless make_program then
|
||||
make_program = (/mswin/ =~ RUBY_PLATFORM) ? 'nmake' : 'make'
|
||||
end
|
||||
|
||||
['', ' install'].each do |target|
|
||||
cmd = "#{make_program}#{target}"
|
||||
results << cmd
|
||||
results << `#{cmd} #{redirector}`
|
||||
|
||||
raise Gem::InstallError, "make#{target} failed:\n\n#{results}" unless
|
||||
$?.success?
|
||||
run(cmd, results, "make#{target}")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -43,12 +39,20 @@ class Gem::Ext::Builder
|
|||
'2>&1'
|
||||
end
|
||||
|
||||
def self.run(command, results)
|
||||
results << command
|
||||
results << `#{command} #{redirector}`
|
||||
def self.run(command, results, command_name = nil)
|
||||
verbose = Gem.configuration.really_verbose
|
||||
|
||||
if verbose
|
||||
puts(command)
|
||||
system(command)
|
||||
else
|
||||
results << command
|
||||
results << `#{command} #{redirector}`
|
||||
end
|
||||
|
||||
unless $?.success? then
|
||||
raise Gem::InstallError, "#{class_name} failed:\n\n#{results.join "\n"}"
|
||||
results << "Building has failed. See above output for more information on the failure." if verbose
|
||||
raise Gem::InstallError, "#{command_name || class_name} failed:\n\n#{results.join "\n"}"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -8,10 +8,10 @@ require 'rubygems/ext/builder'
|
|||
|
||||
class Gem::Ext::ConfigureBuilder < Gem::Ext::Builder
|
||||
|
||||
def self.build(extension, directory, dest_path, results)
|
||||
def self.build(extension, directory, dest_path, results, args=[])
|
||||
unless File.exist?('Makefile') then
|
||||
cmd = "sh ./configure --prefix=#{dest_path}"
|
||||
cmd << " #{Gem::Command.build_args.join ' '}" unless Gem::Command.build_args.empty?
|
||||
cmd << " #{args.join ' '}" unless args.empty?
|
||||
|
||||
run cmd, results
|
||||
end
|
||||
|
|
|
@ -9,9 +9,9 @@ require 'rubygems/command'
|
|||
|
||||
class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
|
||||
|
||||
def self.build(extension, directory, dest_path, results)
|
||||
def self.build(extension, directory, dest_path, results, args=[])
|
||||
cmd = "#{Gem.ruby} #{File.basename extension}"
|
||||
cmd << " #{Gem::Command.build_args.join ' '}" unless Gem::Command.build_args.empty?
|
||||
cmd << " #{args.join ' '}" unless args.empty?
|
||||
|
||||
run cmd, results
|
||||
|
||||
|
|
|
@ -9,10 +9,10 @@ require 'rubygems/command'
|
|||
|
||||
class Gem::Ext::RakeBuilder < Gem::Ext::Builder
|
||||
|
||||
def self.build(extension, directory, dest_path, results)
|
||||
def self.build(extension, directory, dest_path, results, args=[])
|
||||
if File.basename(extension) =~ /mkrf_conf/i then
|
||||
cmd = "#{Gem.ruby} #{File.basename extension}"
|
||||
cmd << " #{Gem::Command.build_args.join " "}" unless Gem::Command.build_args.empty?
|
||||
cmd << " #{args.join " "}" unless args.empty?
|
||||
run cmd, results
|
||||
end
|
||||
|
||||
|
|
|
@ -1,82 +0,0 @@
|
|||
#--
|
||||
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
|
||||
# All rights reserved.
|
||||
# See LICENSE.txt for permissions.
|
||||
#++
|
||||
|
||||
require 'rubygems/package'
|
||||
|
||||
##
|
||||
# Gem::Format knows the guts of the RubyGem .gem file format and provides the
|
||||
# capability to read gem files
|
||||
|
||||
class Gem::Format
|
||||
|
||||
attr_accessor :spec
|
||||
attr_accessor :file_entries
|
||||
attr_accessor :gem_path
|
||||
|
||||
##
|
||||
# Constructs a Format representing the gem's data which came from +gem_path+
|
||||
|
||||
def initialize(gem_path)
|
||||
@gem_path = gem_path
|
||||
end
|
||||
|
||||
##
|
||||
# Reads the gem +file_path+ using +security_policy+ and returns a Format
|
||||
# representing the data in the gem
|
||||
|
||||
def self.from_file_by_path(file_path, security_policy = nil)
|
||||
unless File.file?(file_path)
|
||||
raise Gem::Exception, "Cannot load gem at [#{file_path}] in #{Dir.pwd}"
|
||||
end
|
||||
|
||||
start = File.read file_path, 20
|
||||
|
||||
if start.nil? or start.length < 20 then
|
||||
nil
|
||||
elsif start.include?("MD5SUM =") # old version gems
|
||||
require 'rubygems/old_format'
|
||||
|
||||
Gem::OldFormat.from_file_by_path file_path
|
||||
else
|
||||
begin
|
||||
open file_path, Gem.binary_mode do |io|
|
||||
from_io io, file_path, security_policy
|
||||
end
|
||||
rescue Gem::Package::TarInvalidError => e
|
||||
message = "corrupt gem (#{e.class}: #{e.message})"
|
||||
raise Gem::Package::FormatError.new(message, file_path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Reads a gem from +io+ at +gem_path+ using +security_policy+ and returns a
|
||||
# Format representing the data from the gem
|
||||
|
||||
def self.from_io(io, gem_path="(io)", security_policy = nil)
|
||||
format = new gem_path
|
||||
|
||||
Gem::Package.open io, 'r', security_policy do |pkg|
|
||||
format.spec = pkg.metadata
|
||||
format.file_entries = []
|
||||
|
||||
pkg.each do |entry|
|
||||
size = entry.header.size
|
||||
mode = entry.header.mode
|
||||
|
||||
format.file_entries << [{
|
||||
"size" => size, "mode" => mode, "path" => entry.full_name,
|
||||
},
|
||||
entry.read
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
format
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -1,90 +0,0 @@
|
|||
#--
|
||||
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
|
||||
# All rights reserved.
|
||||
# See LICENSE.txt for permissions.
|
||||
#++
|
||||
|
||||
#--
|
||||
# Some system might not have OpenSSL installed, therefore the core
|
||||
# library file openssl might not be available. We localize testing
|
||||
# for the presence of OpenSSL in this file.
|
||||
#++
|
||||
|
||||
module Gem
|
||||
class << self
|
||||
##
|
||||
# Is SSL (used by the signing commands) available on this
|
||||
# platform?
|
||||
|
||||
def ssl_available?
|
||||
@ssl_available
|
||||
end
|
||||
|
||||
##
|
||||
# Is SSL available?
|
||||
|
||||
attr_writer :ssl_available
|
||||
|
||||
##
|
||||
# Ensure that SSL is available. Throw an exception if it is not.
|
||||
|
||||
def ensure_ssl_available
|
||||
unless ssl_available?
|
||||
raise Gem::Exception, "SSL is not installed on this system"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# :stopdoc:
|
||||
|
||||
begin
|
||||
require 'openssl'
|
||||
|
||||
# Reference a constant defined in the .rb portion of ssl (just to
|
||||
# make sure that part is loaded too).
|
||||
|
||||
Gem.ssl_available = !!OpenSSL::Digest::SHA1
|
||||
|
||||
class OpenSSL::X509::Certificate
|
||||
# Check the validity of this certificate.
|
||||
def check_validity(issuer_cert = nil, time = Time.now)
|
||||
ret = if @not_before && @not_before > time
|
||||
[false, :expired, "not valid before '#@not_before'"]
|
||||
elsif @not_after && @not_after < time
|
||||
[false, :expired, "not valid after '#@not_after'"]
|
||||
elsif issuer_cert && !verify(issuer_cert.public_key)
|
||||
[false, :issuer, "#{issuer_cert.subject} is not issuer"]
|
||||
else
|
||||
[true, :ok, 'Valid certificate']
|
||||
end
|
||||
|
||||
# return hash
|
||||
{ :is_valid => ret[0], :error => ret[1], :desc => ret[2] }
|
||||
end
|
||||
end
|
||||
|
||||
rescue LoadError, StandardError
|
||||
Gem.ssl_available = false
|
||||
end
|
||||
|
||||
module Gem::SSL
|
||||
|
||||
# We make our own versions of the constants here. This allows us
|
||||
# to reference the constants, even though some systems might not
|
||||
# have SSL installed in the Ruby core package.
|
||||
#
|
||||
# These constants are only used during load time. At runtime, any
|
||||
# method that makes a direct reference to SSL software must be
|
||||
# protected with a Gem.ensure_ssl_available call.
|
||||
|
||||
if Gem.ssl_available? then
|
||||
PKEY_RSA = OpenSSL::PKey::RSA
|
||||
DIGEST_SHA1 = OpenSSL::Digest::SHA1
|
||||
else
|
||||
PKEY_RSA = :rsa
|
||||
DIGEST_SHA1 = :sha1
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -1,172 +0,0 @@
|
|||
require "rubygems"
|
||||
require "rubygems/deprecate"
|
||||
|
||||
##
|
||||
# GemPathSearcher has the capability to find loadable files inside
|
||||
# gems. It generates data up front to speed up searches later.
|
||||
|
||||
class Gem::GemPathSearcher
|
||||
|
||||
##
|
||||
# Initialise the data we need to make searches later.
|
||||
|
||||
def initialize
|
||||
# We want a record of all the installed gemspecs, in the order we wish to
|
||||
# examine them.
|
||||
# TODO: remove this stupid method
|
||||
@gemspecs = init_gemspecs
|
||||
|
||||
# Map gem spec to glob of full require_path directories. Preparing this
|
||||
# information may speed up searches later.
|
||||
@lib_dirs = {}
|
||||
|
||||
@gemspecs.each do |spec|
|
||||
@lib_dirs[spec.object_id] = lib_dirs_for spec
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Look in all the installed gems until a matching +glob+ is found.
|
||||
# Return the _gemspec_ of the gem where it was found. If no match
|
||||
# is found, return nil.
|
||||
#
|
||||
# The gems are searched in alphabetical order, and in reverse
|
||||
# version order.
|
||||
#
|
||||
# For example:
|
||||
#
|
||||
# find('log4r') # -> (log4r-1.1 spec)
|
||||
# find('log4r.rb') # -> (log4r-1.1 spec)
|
||||
# find('rake/rdoctask') # -> (rake-0.4.12 spec)
|
||||
# find('foobarbaz') # -> nil
|
||||
#
|
||||
# Matching paths can have various suffixes ('.rb', '.so', and
|
||||
# others), which may or may not already be attached to _file_.
|
||||
# This method doesn't care about the full filename that matches;
|
||||
# only that there is a match.
|
||||
|
||||
def find(glob)
|
||||
# HACK violation of encapsulation
|
||||
@gemspecs.find do |spec|
|
||||
# TODO: inverted responsibility
|
||||
matching_file? spec, glob
|
||||
end
|
||||
end
|
||||
|
||||
# Looks through the available gemspecs and finds the first
|
||||
# one that contains +file+ as a requirable file.
|
||||
|
||||
def find_spec_for_file(file)
|
||||
@gemspecs.find do |spec|
|
||||
return spec if spec.contains_requirable_file?(file)
|
||||
end
|
||||
end
|
||||
|
||||
def find_active(glob)
|
||||
# HACK violation of encapsulation
|
||||
@gemspecs.find do |spec|
|
||||
# TODO: inverted responsibility
|
||||
spec.loaded? and matching_file? spec, glob
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Works like #find, but finds all gemspecs matching +glob+.
|
||||
|
||||
def find_all(glob)
|
||||
# HACK violation of encapsulation
|
||||
@gemspecs.select do |spec|
|
||||
# TODO: inverted responsibility
|
||||
matching_file? spec, glob
|
||||
end || []
|
||||
end
|
||||
|
||||
def find_in_unresolved(glob)
|
||||
# HACK violation
|
||||
specs = Gem.unresolved_deps.values.map { |dep|
|
||||
Gem.source_index.search dep, true
|
||||
}.flatten
|
||||
|
||||
specs.select do |spec|
|
||||
# TODO: inverted responsibility
|
||||
matching_file? spec, glob
|
||||
end || []
|
||||
end
|
||||
|
||||
def find_in_unresolved_tree glob
|
||||
# HACK violation
|
||||
# TODO: inverted responsibility
|
||||
specs = Gem.unresolved_deps.values.map { |dep|
|
||||
Gem.source_index.search dep, true
|
||||
}.flatten
|
||||
|
||||
specs.reverse_each do |spec|
|
||||
trails = matching_paths(spec, glob)
|
||||
next if trails.empty?
|
||||
return trails.map(&:reverse).sort.first.reverse
|
||||
end
|
||||
|
||||
[]
|
||||
end
|
||||
|
||||
##
|
||||
# Attempts to find a matching path using the require_paths of the given
|
||||
# +spec+.
|
||||
|
||||
def matching_file?(spec, path)
|
||||
not matching_files(spec, path).empty?
|
||||
end
|
||||
|
||||
def matching_paths(spec, path)
|
||||
trails = []
|
||||
|
||||
spec.traverse do |from_spec, dep, to_spec, trail|
|
||||
next unless to_spec.conflicts.empty?
|
||||
trails << trail unless matching_files(to_spec, path).empty?
|
||||
end
|
||||
|
||||
trails
|
||||
end
|
||||
|
||||
##
|
||||
# Returns files matching +path+ in +spec+.
|
||||
#--
|
||||
# Some of the intermediate results are cached in @lib_dirs for speed.
|
||||
|
||||
def matching_files(spec, path)
|
||||
return [] unless @lib_dirs[spec.object_id] # case no paths
|
||||
glob = File.join @lib_dirs[spec.object_id], "#{path}#{Gem.suffix_pattern}"
|
||||
Dir[glob].select { |f| File.file? f.untaint }
|
||||
end
|
||||
|
||||
##
|
||||
# Return a list of all installed gemspecs, sorted by alphabetical order and
|
||||
# in reverse version order. (bar-2, bar-1, foo-2)
|
||||
|
||||
def init_gemspecs
|
||||
Gem::Specification.sort { |a, b|
|
||||
names = a.name <=> b.name
|
||||
next names if names.nonzero?
|
||||
b.version <=> a.version
|
||||
}
|
||||
end
|
||||
|
||||
##
|
||||
# Returns library directories glob for a gemspec. For example,
|
||||
# '/usr/local/lib/ruby/gems/1.8/gems/foobar-1.0/{lib,ext}'
|
||||
|
||||
def lib_dirs_for(spec)
|
||||
"#{spec.full_gem_path}/{#{spec.require_paths.join(',')}}" if
|
||||
spec.require_paths
|
||||
end
|
||||
|
||||
extend Gem::Deprecate
|
||||
|
||||
deprecate :initialize, :none, 2011, 10
|
||||
deprecate :find, :none, 2011, 10
|
||||
deprecate :find_active, :none, 2011, 10
|
||||
deprecate :find_all, :none, 2011, 10
|
||||
deprecate :find_in_unresolved, :none, 2011, 10
|
||||
deprecate :find_in_unresolved_tree, :none, 2011, 10
|
||||
deprecate :find_spec_for_file, :none, 2011, 10
|
||||
end
|
|
@ -4,10 +4,9 @@
|
|||
# See LICENSE.txt for permissions.
|
||||
#++
|
||||
|
||||
require "rubygems"
|
||||
require 'rubygems'
|
||||
require 'rubygems/command_manager'
|
||||
require 'rubygems/config_file'
|
||||
require 'rubygems/doc_manager'
|
||||
|
||||
##
|
||||
# Load additional plugins from $LOAD_PATH
|
||||
|
@ -29,25 +28,21 @@ class Gem::GemRunner
|
|||
# TODO: nuke these options
|
||||
@command_manager_class = options[:command_manager] || Gem::CommandManager
|
||||
@config_file_class = options[:config_file] || Gem::ConfigFile
|
||||
@doc_manager_class = options[:doc_manager] || Gem::DocManager
|
||||
end
|
||||
|
||||
##
|
||||
# Run the gem command with the following arguments.
|
||||
|
||||
def run(args)
|
||||
start_time = Time.now
|
||||
|
||||
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
|
||||
|
||||
Gem::Command.build_args = build_args if build_args
|
||||
|
||||
do_configuration args
|
||||
cmd = @command_manager_class.instance
|
||||
|
||||
|
@ -62,14 +57,7 @@ class Gem::GemRunner
|
|||
Gem::Command.add_specific_extra_args command_name, config_args
|
||||
end
|
||||
|
||||
cmd.run Gem.configuration.args
|
||||
end_time = Time.now
|
||||
|
||||
if Gem.configuration.benchmark then
|
||||
printf "\nExecution time: %0.2f seconds.\n", end_time - start_time
|
||||
puts "Press Enter to finish"
|
||||
STDIN.gets
|
||||
end
|
||||
cmd.run Gem.configuration.args, build_args
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -78,7 +66,6 @@ class Gem::GemRunner
|
|||
Gem.configuration = @config_file_class.new(args)
|
||||
Gem.use_paths Gem.configuration[:gemhome], Gem.configuration[:gempath]
|
||||
Gem::Command.extra_args = Gem.configuration[:gem]
|
||||
@doc_manager_class.configured_args = Gem.configuration[:rdoc]
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
require 'rubygems/remote_fetcher'
|
||||
|
||||
module Gem::GemcutterUtilities
|
||||
# TODO: move to Gem::Command
|
||||
OptionParser.accept Symbol do |value|
|
||||
value.to_sym
|
||||
end
|
||||
|
@ -19,6 +20,8 @@ module Gem::GemcutterUtilities
|
|||
def api_key
|
||||
if options[:key] then
|
||||
verify_api_key options[:key]
|
||||
elsif Gem.configuration.api_keys.key?(host)
|
||||
Gem.configuration.api_keys[host]
|
||||
else
|
||||
Gem.configuration.rubygems_api_key
|
||||
end
|
||||
|
@ -44,12 +47,24 @@ module Gem::GemcutterUtilities
|
|||
end
|
||||
end
|
||||
|
||||
def rubygems_api_request(method, path, host = Gem.host, &block)
|
||||
require 'net/http'
|
||||
host = ENV['RUBYGEMS_HOST'] if ENV['RUBYGEMS_HOST']
|
||||
uri = URI.parse "#{host}/#{path}"
|
||||
attr_writer :host
|
||||
def host
|
||||
configured_host = Gem.host unless
|
||||
Gem.configuration.disable_default_gem_server
|
||||
|
||||
say "Pushing gem to #{host}..."
|
||||
@host ||= ENV['RUBYGEMS_HOST'] || configured_host
|
||||
end
|
||||
|
||||
def rubygems_api_request(method, path, host = nil, &block)
|
||||
require 'net/http'
|
||||
|
||||
self.host = host if host
|
||||
unless self.host
|
||||
alert_error "You must specify a gem server"
|
||||
terminate_interaction 1 # TODO: question this
|
||||
end
|
||||
|
||||
uri = URI.parse "#{self.host}/#{path}"
|
||||
|
||||
request_method = Net::HTTP.const_get method.to_s.capitalize
|
||||
|
||||
|
@ -66,7 +81,7 @@ module Gem::GemcutterUtilities
|
|||
end
|
||||
else
|
||||
say resp.body
|
||||
terminate_interaction 1
|
||||
terminate_interaction 1 # TODO: question this
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -74,8 +89,8 @@ module Gem::GemcutterUtilities
|
|||
if Gem.configuration.api_keys.key? key then
|
||||
Gem.configuration.api_keys[key]
|
||||
else
|
||||
alert_error "No such API key. You can add it with gem keys --add #{key}"
|
||||
terminate_interaction 1
|
||||
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
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
require 'rubygems'
|
||||
require 'rubygems/format'
|
||||
require 'rubygems/package'
|
||||
require 'time'
|
||||
|
||||
begin
|
||||
|
@ -15,11 +15,6 @@ class Gem::Indexer
|
|||
|
||||
include Gem::UserInteraction
|
||||
|
||||
##
|
||||
# Build indexes for RubyGems older than 1.2.0 when true
|
||||
|
||||
attr_accessor :build_legacy
|
||||
|
||||
##
|
||||
# Build indexes for RubyGems 1.2.0 and newer when true
|
||||
|
||||
|
@ -63,15 +58,10 @@ class Gem::Indexer
|
|||
"\n\tgem install builder"
|
||||
end
|
||||
|
||||
options = { :build_legacy => true, :build_modern => true }.merge options
|
||||
options = { :build_modern => true }.merge options
|
||||
|
||||
@build_legacy = options[:build_legacy]
|
||||
@build_modern = options[:build_modern]
|
||||
|
||||
@rss_title = options[:rss_title]
|
||||
@rss_host = options[:rss_host]
|
||||
@rss_gems_host = options[:rss_gems_host]
|
||||
|
||||
@dest_directory = directory
|
||||
@directory = File.join(Dir.tmpdir, "gem_generate_index_#{$$}")
|
||||
|
||||
|
@ -99,8 +89,6 @@ class Gem::Indexer
|
|||
@dest_prerelease_specs_index =
|
||||
File.join(@dest_directory, "prerelease_specs.#{Gem.marshal_version}")
|
||||
|
||||
@rss_index = File.join @directory, 'index.rss'
|
||||
|
||||
@files = []
|
||||
end
|
||||
|
||||
|
@ -109,6 +97,8 @@ class Gem::Indexer
|
|||
# searching, downloading and related activities and do not need deployment
|
||||
# specific information (e.g. list of files). So we abbreviate the spec,
|
||||
# making it much smaller for quicker downloads.
|
||||
#--
|
||||
# TODO move to Gem::Specification
|
||||
|
||||
def abbreviate(spec)
|
||||
spec.files = []
|
||||
|
@ -123,37 +113,15 @@ class Gem::Indexer
|
|||
# Build various indicies
|
||||
|
||||
def build_indicies
|
||||
# Marshal gemspecs are used by both modern and legacy RubyGems
|
||||
|
||||
Gem::Specification.dirs = []
|
||||
Gem::Specification.add_specs(*map_gems_to_specs(gem_file_list))
|
||||
|
||||
build_marshal_gemspecs
|
||||
build_legacy_indicies if @build_legacy
|
||||
build_modern_indicies if @build_modern
|
||||
build_rss
|
||||
|
||||
compress_indicies
|
||||
end
|
||||
|
||||
##
|
||||
# Builds indicies for RubyGems older than 1.2.x
|
||||
|
||||
def build_legacy_indicies
|
||||
index = collect_specs
|
||||
|
||||
say "Generating Marshal master index"
|
||||
|
||||
Gem.time 'Generated Marshal master index' do
|
||||
open @marshal_index, 'wb' do |io|
|
||||
io.write index.dump
|
||||
end
|
||||
end
|
||||
|
||||
@files << @marshal_index
|
||||
@files << "#{@marshal_index}.Z"
|
||||
end
|
||||
|
||||
##
|
||||
# Builds Marshal quick index gemspecs.
|
||||
|
||||
|
@ -238,104 +206,6 @@ class Gem::Indexer
|
|||
"#{@prerelease_specs_index}.gz"]
|
||||
end
|
||||
|
||||
##
|
||||
# Builds an RSS feed for past two days gem releases according to the gem's
|
||||
# date.
|
||||
|
||||
def build_rss
|
||||
if @rss_host.nil? or @rss_gems_host.nil? then
|
||||
if Gem.configuration.really_verbose then
|
||||
alert_warning "no --rss-host or --rss-gems-host, RSS generation disabled"
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
require 'cgi'
|
||||
require 'rubygems/text'
|
||||
|
||||
extend Gem::Text
|
||||
|
||||
Gem.time 'Generated rss' do
|
||||
open @rss_index, 'wb' do |io|
|
||||
rss_host = CGI.escapeHTML @rss_host
|
||||
rss_title = CGI.escapeHTML(@rss_title || 'gems')
|
||||
|
||||
io.puts <<-HEADER
|
||||
<?xml version="1.0"?>
|
||||
<rss version="2.0">
|
||||
<channel>
|
||||
<title>#{rss_title}</title>
|
||||
<link>http://#{rss_host}</link>
|
||||
<description>Recently released gems from http://#{rss_host}</description>
|
||||
<generator>RubyGems v#{Gem::VERSION}</generator>
|
||||
<docs>http://cyber.law.harvard.edu/rss/rss.html</docs>
|
||||
HEADER
|
||||
|
||||
today = Gem::Specification::TODAY
|
||||
yesterday = today - 86400
|
||||
|
||||
index = Gem::Specification.select do |spec|
|
||||
spec_date = spec.date
|
||||
# TODO: remove this and make YAML based specs properly normalized
|
||||
spec_date = Time.parse(spec_date.to_s) if Date === spec_date
|
||||
|
||||
spec_date >= yesterday && spec_date <= today
|
||||
end
|
||||
|
||||
index.sort_by { |spec| [-spec.date.to_i, spec] }.each do |spec|
|
||||
file_name = File.basename spec.cache_file
|
||||
gem_path = CGI.escapeHTML "http://#{@rss_gems_host}/gems/#{file_name}"
|
||||
size = File.stat(spec.loaded_from).size # rescue next
|
||||
|
||||
description = spec.description || spec.summary || ''
|
||||
authors = Array spec.authors
|
||||
emails = Array spec.email
|
||||
authors = emails.zip(authors).map do |email, author|
|
||||
email += " (#{author})" if author and not author.empty?
|
||||
end.join ', '
|
||||
|
||||
description = description.split(/\n\n+/).map do |chunk|
|
||||
format_text chunk, 78
|
||||
end
|
||||
|
||||
description = description.join "\n\n"
|
||||
|
||||
item = ''
|
||||
|
||||
item << <<-ITEM
|
||||
<item>
|
||||
<title>#{CGI.escapeHTML spec.full_name}</title>
|
||||
<description>
|
||||
<pre>#{CGI.escapeHTML description.chomp}</pre>
|
||||
</description>
|
||||
<author>#{CGI.escapeHTML authors}</author>
|
||||
<guid>#{CGI.escapeHTML spec.full_name}</guid>
|
||||
<enclosure url=\"#{gem_path}\"
|
||||
length=\"#{size}\" type=\"application/octet-stream\" />
|
||||
<pubDate>#{spec.date.rfc2822}</pubDate>
|
||||
ITEM
|
||||
|
||||
item << <<-ITEM if spec.homepage
|
||||
<link>#{CGI.escapeHTML spec.homepage}</link>
|
||||
ITEM
|
||||
|
||||
item << <<-ITEM
|
||||
</item>
|
||||
ITEM
|
||||
|
||||
io.puts item
|
||||
end
|
||||
|
||||
io.puts <<-FOOTER
|
||||
</channel>
|
||||
</rss>
|
||||
FOOTER
|
||||
end
|
||||
end
|
||||
|
||||
@files << @rss_index
|
||||
end
|
||||
|
||||
def map_gems_to_specs gems
|
||||
gems.map { |gemfile|
|
||||
if File.size(gemfile) == 0 then
|
||||
|
@ -344,7 +214,7 @@ class Gem::Indexer
|
|||
end
|
||||
|
||||
begin
|
||||
spec = Gem::Format.from_file_by_path(gemfile).spec
|
||||
spec = Gem::Package.new(gemfile).spec
|
||||
spec.loaded_from = gemfile
|
||||
|
||||
# HACK: fuck this shit - borks all tests that use pl1
|
||||
|
@ -373,21 +243,6 @@ class Gem::Indexer
|
|||
}.compact
|
||||
end
|
||||
|
||||
##
|
||||
# Collect specifications from .gem files from the gem directory.
|
||||
|
||||
def collect_specs(gems = gem_file_list)
|
||||
Gem::Deprecate.skip_during do
|
||||
index = Gem::SourceIndex.new
|
||||
|
||||
map_gems_to_specs(gems).each do |spec|
|
||||
index.add_spec spec, spec.original_name
|
||||
end
|
||||
|
||||
index
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Compresses indicies on disk
|
||||
#--
|
||||
|
@ -397,11 +252,6 @@ class Gem::Indexer
|
|||
say "Compressing indicies"
|
||||
|
||||
Gem.time 'Compressed indicies' do
|
||||
if @build_legacy then
|
||||
compress @marshal_index, 'Z'
|
||||
paranoid @marshal_index, 'Z'
|
||||
end
|
||||
|
||||
if @build_modern then
|
||||
gzip @specs_index
|
||||
gzip @latest_specs_index
|
||||
|
@ -559,12 +409,9 @@ class Gem::Indexer
|
|||
end
|
||||
|
||||
##
|
||||
# Perform an in-place update of the repository from newly added gems. Only
|
||||
# works for modern indicies, and sets #build_legacy to false when run.
|
||||
# Perform an in-place update of the repository from newly added gems.
|
||||
|
||||
def update_index
|
||||
@build_legacy = false
|
||||
|
||||
make_temp_directories
|
||||
|
||||
specs_mtime = File.stat(@dest_specs_index).mtime
|
||||
|
@ -584,6 +431,9 @@ class Gem::Indexer
|
|||
specs = map_gems_to_specs updated_gems
|
||||
prerelease, released = specs.partition { |s| s.version.prerelease? }
|
||||
|
||||
Gem::Specification.dirs = []
|
||||
Gem::Specification.add_specs(*specs)
|
||||
|
||||
files = build_marshal_gemspecs
|
||||
|
||||
Gem.time 'Updated indexes' do
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
require 'rubygems'
|
||||
require 'rubygems/user_interaction'
|
||||
|
||||
##
|
||||
# A default post-install hook that displays "Successfully installed
|
||||
# some_gem-1.0"
|
||||
|
||||
Gem.post_install do |installer|
|
||||
ui = Gem::DefaultUserInteraction.ui
|
||||
ui.say "Successfully installed #{installer.spec.full_name}"
|
||||
end
|
||||
|
|
@ -22,6 +22,7 @@ module Gem::InstallUpdateOptions
|
|||
# Add the install/update options to the option parser.
|
||||
|
||||
def add_install_update_options
|
||||
# TODO: use @parser.accept
|
||||
OptionParser.accept Gem::Security::Policy do |value|
|
||||
require 'rubygems/security'
|
||||
|
||||
|
@ -39,21 +40,49 @@ module Gem::InstallUpdateOptions
|
|||
end
|
||||
|
||||
add_option(:"Install/Update", '-n', '--bindir DIR',
|
||||
'Directory where binary files are',
|
||||
'located') do |value, options|
|
||||
'Directory where binary files are',
|
||||
'located') do |value, options|
|
||||
options[:bin_dir] = File.expand_path(value)
|
||||
end
|
||||
|
||||
add_option(:"Install/Update", '-d', '--[no-]rdoc',
|
||||
'Generate RDoc documentation for the gem on',
|
||||
'install') do |value, options|
|
||||
options[:generate_rdoc] = value
|
||||
add_option(:"Install/Update", '--[no-]document [TYPES]', Array,
|
||||
'Generate documentation for installed gems',
|
||||
'List the documentation types you wish to',
|
||||
'generate. For example: rdoc,ri') do |value, options|
|
||||
options[:document] = case value
|
||||
when nil then %w[ri]
|
||||
when false then []
|
||||
else value
|
||||
end
|
||||
end
|
||||
|
||||
add_option(:"Install/Update", '--[no-]ri',
|
||||
'Generate RI documentation for the gem on',
|
||||
'install') do |value, options|
|
||||
options[:generate_ri] = value
|
||||
add_option(:"Install/Update", '-N', '--no-document',
|
||||
'Disable documentation generation') do |value, options|
|
||||
options[:document] = []
|
||||
end
|
||||
|
||||
add_option(:Deprecated, '--[no-]rdoc',
|
||||
'Generate RDoc for installed gems',
|
||||
'Use --document instead') do |value, options|
|
||||
if value then
|
||||
options[:document] << 'rdoc'
|
||||
else
|
||||
options[:document].delete 'rdoc'
|
||||
end
|
||||
|
||||
options[:document].uniq!
|
||||
end
|
||||
|
||||
add_option(:Deprecated, '--[no-]ri',
|
||||
'Generate ri data for installed gems.',
|
||||
'Use --document instead') do |value, options|
|
||||
if value then
|
||||
options[:document] << 'ri'
|
||||
else
|
||||
options[:document].delete 'ri'
|
||||
end
|
||||
|
||||
options[:document].uniq!
|
||||
end
|
||||
|
||||
add_option(:"Install/Update", '-E', '--[no-]env-shebang',
|
||||
|
@ -85,12 +114,6 @@ module Gem::InstallUpdateOptions
|
|||
options[:ignore_dependencies] = value
|
||||
end
|
||||
|
||||
add_option(:"Install/Update", '-y', '--include-dependencies',
|
||||
'Unconditionally install the required',
|
||||
'dependent gems') do |value, options|
|
||||
options[:include_dependencies] = value
|
||||
end
|
||||
|
||||
add_option(:"Install/Update", '--[no-]format-executable',
|
||||
'Make installed executable names match ruby.',
|
||||
'If ruby is ruby18, foo_exec will be',
|
||||
|
@ -105,15 +128,30 @@ module Gem::InstallUpdateOptions
|
|||
end
|
||||
|
||||
add_option(:"Install/Update", "--development",
|
||||
"Install any additional development",
|
||||
"Install additional development",
|
||||
"dependencies") do |value, options|
|
||||
options[:development] = true
|
||||
options[:dev_shallow] = true
|
||||
end
|
||||
|
||||
add_option(:"Install/Update", "--development-all",
|
||||
"Install development dependencies for all",
|
||||
"gems (including dev deps themselves)") do |value, options|
|
||||
options[:development] = true
|
||||
options[:dev_shallow] = false
|
||||
end
|
||||
|
||||
add_option(:"Install/Update", "--conservative",
|
||||
"Don't attempt to upgrade gems already",
|
||||
"meeting version requirement") do |value, options|
|
||||
options[:conservative] = true
|
||||
options[:minimal_deps] = true
|
||||
end
|
||||
|
||||
add_option(:"Install/Update", "--minimal-deps",
|
||||
"Don't upgrade any dependencies that already",
|
||||
"meet version requirements") do |value, options|
|
||||
options[:minimal_deps] = true
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -121,7 +159,7 @@ module Gem::InstallUpdateOptions
|
|||
# Default options for the gem install command.
|
||||
|
||||
def install_update_defaults_str
|
||||
'--rdoc --no-force --wrappers'
|
||||
'--document=rdoc,ri --wrappers'
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -4,15 +4,13 @@
|
|||
# See LICENSE.txt for permissions.
|
||||
#++
|
||||
|
||||
require 'rubygems/format'
|
||||
require 'rubygems/exceptions'
|
||||
require 'rubygems/package'
|
||||
require 'rubygems/ext'
|
||||
require 'rubygems/require_paths_builder'
|
||||
require 'rubygems/user_interaction'
|
||||
|
||||
##
|
||||
# The installer class processes RubyGem .gem files and installs the files
|
||||
# contained in the .gem into the Gem.path.
|
||||
# The installer installs the files contained in the .gem into the Gem.home.
|
||||
#
|
||||
# Gem::Installer does the work of putting files in all the right places on the
|
||||
# filesystem including unpacking the gem into its gem dir, installing the
|
||||
|
@ -39,8 +37,7 @@ class Gem::Installer
|
|||
|
||||
include Gem::UserInteraction
|
||||
|
||||
include Gem::RequirePathsBuilder if Gem::QUICKLOADER_SUCKAGE
|
||||
|
||||
# DOC: Missing docs or :nodoc:.
|
||||
attr_reader :gem
|
||||
|
||||
##
|
||||
|
@ -67,6 +64,7 @@ class Gem::Installer
|
|||
|
||||
attr_accessor :path_warning
|
||||
|
||||
# DOC: Missing docs or :nodoc:.
|
||||
attr_writer :exec_format
|
||||
|
||||
# Defaults to use Ruby's program prefix and suffix.
|
||||
|
@ -80,24 +78,36 @@ class Gem::Installer
|
|||
# Constructs an Installer instance that will install the gem located at
|
||||
# +gem+. +options+ is a Hash with the following keys:
|
||||
#
|
||||
# :bin_dir:: Where to put a bin wrapper if needed.
|
||||
# :development:: Whether or not development dependencies should be installed.
|
||||
# :env_shebang:: Use /usr/bin/env in bin wrappers.
|
||||
# :force:: Overrides all version checks and security policy checks, except
|
||||
# for a signed-gems-only policy.
|
||||
# :ignore_dependencies:: Don't raise if a dependency is missing.
|
||||
# :install_dir:: The directory to install the gem into.
|
||||
# :format_executable:: Format the executable the same as the ruby executable.
|
||||
# If your ruby is ruby18, foo_exec will be installed as
|
||||
# foo_exec18.
|
||||
# :ignore_dependencies:: Don't raise if a dependency is missing.
|
||||
# :install_dir:: The directory to install the gem into.
|
||||
# :security_policy:: Use the specified security policy. See Gem::Security
|
||||
# :user_install:: Indicate that the gem should be unpacked into the users
|
||||
# personal gem directory.
|
||||
# :only_install_dir:: Only validate dependencies against what is in the
|
||||
# install_dir
|
||||
# :wrappers:: Install wrappers if true, symlinks if false.
|
||||
# :build_args:: An Array of arguments to pass to the extension builder
|
||||
# process. If not set, then Gem::Command.build_args is used
|
||||
|
||||
def initialize(gem, options={})
|
||||
require 'fileutils'
|
||||
|
||||
@gem = gem
|
||||
@options = options
|
||||
@package = Gem::Package.new @gem
|
||||
|
||||
process_options
|
||||
|
||||
@package.security_policy = @security_policy
|
||||
|
||||
if options[:user_install] and not options[:unpack] then
|
||||
@gem_home = Gem.user_dir
|
||||
check_that_user_bin_dir_is_in_path
|
||||
|
@ -105,28 +115,79 @@ class Gem::Installer
|
|||
end
|
||||
|
||||
##
|
||||
# Lazy accessor for the spec's gem directory.
|
||||
# Checks if +filename+ exists in +@bin_dir+.
|
||||
#
|
||||
# If +@force+ is set +filename+ is overwritten.
|
||||
#
|
||||
# If +filename+ exists and is a RubyGems wrapper for different gem the user
|
||||
# is consulted.
|
||||
#
|
||||
# If +filename+ exists and +@bin_dir+ is Gem.default_bindir (/usr/local) the
|
||||
# user is consulted.
|
||||
#
|
||||
# Otherwise +filename+ is overwritten.
|
||||
|
||||
def gem_dir
|
||||
@gem_dir ||= spec.gem_dir.dup.untaint
|
||||
def check_executable_overwrite filename # :nodoc:
|
||||
return if @force
|
||||
|
||||
generated_bin = File.join @bin_dir, filename
|
||||
|
||||
return unless File.exist? generated_bin
|
||||
|
||||
ruby_executable = false
|
||||
existing = nil
|
||||
|
||||
open generated_bin, 'rb' do |io|
|
||||
next unless io.gets =~ /^#!/ # shebang
|
||||
io.gets # blankline
|
||||
|
||||
# TODO detect a specially formatted comment instead of trying
|
||||
# to run a regexp against ruby code.
|
||||
next unless io.gets =~ /This file was generated by RubyGems/
|
||||
|
||||
ruby_executable = true
|
||||
existing = io.read.slice(/^gem (['"])(.*?)(\1),/, 2)
|
||||
end
|
||||
|
||||
return if spec.name == existing
|
||||
|
||||
# somebody has written to RubyGems' directory, overwrite, too bad
|
||||
return if Gem.default_bindir != @bin_dir and not ruby_executable
|
||||
|
||||
question = "#{spec.name}'s executable \"#{filename}\" conflicts with "
|
||||
|
||||
if ruby_executable then
|
||||
question << existing
|
||||
|
||||
return if ask_yes_no "#{question}\nOverwrite the executable?", false
|
||||
|
||||
conflict = "installed executable from #{existing}"
|
||||
else
|
||||
question << generated_bin
|
||||
|
||||
return if ask_yes_no "#{question}\nOverwrite the executable?", false
|
||||
|
||||
conflict = generated_bin
|
||||
end
|
||||
|
||||
raise Gem::InstallError,
|
||||
"\"#{filename}\" from #{spec.name} conflicts with #{conflict}"
|
||||
end
|
||||
|
||||
##
|
||||
# Lazy accessor for the installer's Gem::Format instance.
|
||||
# Lazy accessor for the spec's gem directory.
|
||||
|
||||
def format
|
||||
begin
|
||||
@format ||= Gem::Format.from_file_by_path gem, @security_policy
|
||||
rescue Gem::Package::FormatError
|
||||
raise Gem::InstallError, "invalid gem format for #{gem}"
|
||||
end
|
||||
def gem_dir
|
||||
@gem_dir ||= File.join(gem_home, "gems", spec.full_name)
|
||||
end
|
||||
|
||||
##
|
||||
# Lazy accessor for the installer's spec.
|
||||
|
||||
def spec
|
||||
@spec ||= format.spec
|
||||
@spec ||= @package.spec
|
||||
rescue Gem::Package::Error => e
|
||||
raise Gem::InstallError, "invalid gem: #{e.message}"
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -141,11 +202,7 @@ class Gem::Installer
|
|||
# specifications/<gem-version>.gemspec #=> the Gem::Specification
|
||||
|
||||
def install
|
||||
current_home = Gem.dir
|
||||
current_path = Gem.paths.path
|
||||
|
||||
verify_gem_home(options[:unpack])
|
||||
Gem.use_paths gem_home, current_path # HACK: shouldn't need Gem.paths.path
|
||||
|
||||
# If we're forcing the install then disable security unless the security
|
||||
# policy says that we only install signed gems.
|
||||
|
@ -158,31 +215,65 @@ class Gem::Installer
|
|||
ensure_dependencies_met unless @ignore_dependencies
|
||||
end
|
||||
|
||||
Gem.pre_install_hooks.each do |hook|
|
||||
result = hook.call self
|
||||
|
||||
if result == false then
|
||||
location = " at #{$1}" if hook.inspect =~ /@(.*:\d+)/
|
||||
|
||||
message = "pre-install hook#{location} failed for #{spec.full_name}"
|
||||
raise Gem::InstallError, message
|
||||
end
|
||||
end
|
||||
run_pre_install_hooks
|
||||
|
||||
Gem.ensure_gem_subdirectories gem_home
|
||||
|
||||
# Completely remove any previous gem files
|
||||
FileUtils.rm_rf(gem_dir) if File.exist? gem_dir
|
||||
FileUtils.rm_rf(gem_dir)
|
||||
|
||||
FileUtils.mkdir_p gem_dir
|
||||
|
||||
extract_files
|
||||
build_extensions
|
||||
|
||||
Gem.post_build_hooks.each do |hook|
|
||||
result = hook.call self
|
||||
run_post_build_hooks
|
||||
|
||||
if result == false then
|
||||
generate_bin
|
||||
write_spec
|
||||
|
||||
unless @build_args.empty?
|
||||
File.open spec.build_info_file, "w" do |f|
|
||||
@build_args.each { |a| f.puts a }
|
||||
end
|
||||
end
|
||||
|
||||
# TODO should be always cache the file? Other classes have options
|
||||
# to controls if caching is done.
|
||||
cache_file = File.join(gem_home, "cache", "#{spec.full_name}.gem")
|
||||
|
||||
FileUtils.cp gem, cache_file unless File.exist? cache_file
|
||||
|
||||
say spec.post_install_message unless spec.post_install_message.nil?
|
||||
|
||||
spec.loaded_from = spec_file
|
||||
|
||||
Gem::Specification.add_spec spec unless Gem::Specification.include? spec
|
||||
|
||||
run_post_install_hooks
|
||||
|
||||
spec
|
||||
|
||||
# TODO This rescue is in the wrong place. What is raising this exception?
|
||||
# move this rescue to arround the code that actually might raise it.
|
||||
rescue Zlib::GzipFile::Error
|
||||
raise Gem::InstallError, "gzip error installing #{gem}"
|
||||
end
|
||||
|
||||
def run_pre_install_hooks # :nodoc:
|
||||
Gem.pre_install_hooks.each do |hook|
|
||||
if hook.call(self) == false then
|
||||
location = " at #{$1}" if hook.inspect =~ /@(.*:\d+)/
|
||||
|
||||
message = "pre-install hook#{location} failed for #{spec.full_name}"
|
||||
raise Gem::InstallError, message
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def run_post_build_hooks # :nodoc:
|
||||
Gem.post_build_hooks.each do |hook|
|
||||
if hook.call(self) == false then
|
||||
FileUtils.rm_rf gem_dir
|
||||
|
||||
location = " at #{$1}" if hook.inspect =~ /@(.*:\d+)/
|
||||
|
@ -191,32 +282,29 @@ class Gem::Installer
|
|||
raise Gem::InstallError, message
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
generate_bin
|
||||
write_spec
|
||||
|
||||
write_require_paths_file_if_needed if Gem::QUICKLOADER_SUCKAGE
|
||||
|
||||
cache_file = spec.cache_file
|
||||
FileUtils.cp gem, cache_file unless File.exist? cache_file
|
||||
|
||||
say spec.post_install_message unless spec.post_install_message.nil?
|
||||
|
||||
spec.loaded_from = spec.spec_file
|
||||
|
||||
Gem::Specification.add_spec spec unless Gem::Specification.include? spec
|
||||
|
||||
def run_post_install_hooks # :nodoc:
|
||||
Gem.post_install_hooks.each do |hook|
|
||||
hook.call self
|
||||
end
|
||||
end
|
||||
|
||||
return spec
|
||||
rescue Zlib::GzipFile::Error
|
||||
raise Gem::InstallError, "gzip error installing #{gem}"
|
||||
ensure
|
||||
# conditional since we might be here because we're erroring out early.
|
||||
if current_path
|
||||
Gem.use_paths current_home, current_path
|
||||
##
|
||||
#
|
||||
# Return an Array of Specifications contained within the gem_home
|
||||
# we'll be installing into.
|
||||
|
||||
def installed_specs
|
||||
@specs ||= begin
|
||||
specs = []
|
||||
|
||||
Dir[File.join(gem_home, "specifications", "*.gemspec")].each do |path|
|
||||
spec = Gem::Specification.load path.untaint
|
||||
specs << spec if spec
|
||||
end
|
||||
|
||||
specs
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -235,9 +323,11 @@ class Gem::Installer
|
|||
end
|
||||
|
||||
##
|
||||
# True if the gems in the source_index satisfy +dependency+.
|
||||
# True if the gems in the system satisfy +dependency+.
|
||||
|
||||
def installation_satisfies_dependency?(dependency)
|
||||
return true if installed_specs.detect { |s| dependency.matches_spec? s }
|
||||
return false if @only_install_dir
|
||||
not dependency.matching_specs.empty?
|
||||
end
|
||||
|
||||
|
@ -246,18 +336,23 @@ class Gem::Installer
|
|||
|
||||
def unpack(directory)
|
||||
@gem_dir = directory
|
||||
@format = Gem::Format.from_file_by_path gem, @security_policy
|
||||
extract_files
|
||||
end
|
||||
|
||||
##
|
||||
# The location of of the spec file that is installed.
|
||||
#
|
||||
|
||||
def spec_file
|
||||
File.join gem_home, "specifications", "#{spec.full_name}.gemspec"
|
||||
end
|
||||
|
||||
##
|
||||
# Writes the .gemspec specification (in Ruby) to the gem home's
|
||||
# specifications directory.
|
||||
|
||||
def write_spec
|
||||
file_name = spec.spec_file.untaint
|
||||
|
||||
File.open(file_name, "w") do |file|
|
||||
File.open(spec_file, "w") do |file|
|
||||
file.puts spec.to_ruby_for_cache
|
||||
end
|
||||
end
|
||||
|
@ -277,34 +372,34 @@ class Gem::Installer
|
|||
end
|
||||
end
|
||||
|
||||
# DOC: Missing docs or :nodoc:.
|
||||
def generate_bin
|
||||
return if spec.executables.nil? or spec.executables.empty?
|
||||
|
||||
# If the user has asked for the gem to be installed in a directory that is
|
||||
# the system gem directory, then use the system bin directory, else create
|
||||
# (or use) a new bin dir under the gem_home.
|
||||
bindir = @bin_dir || Gem.bindir(gem_home)
|
||||
|
||||
Dir.mkdir bindir unless File.exist? bindir
|
||||
raise Gem::FilePermissionError.new(bindir) unless File.writable? bindir
|
||||
Dir.mkdir @bin_dir unless File.exist? @bin_dir
|
||||
raise Gem::FilePermissionError.new(@bin_dir) unless File.writable? @bin_dir
|
||||
|
||||
spec.executables.each do |filename|
|
||||
filename.untaint
|
||||
bin_path = File.expand_path File.join(gem_dir, spec.bindir, filename)
|
||||
bin_path = File.join gem_dir, spec.bindir, filename
|
||||
|
||||
unless File.exist? bin_path
|
||||
warn "Hey?!?! Where did #{bin_path} go??"
|
||||
unless File.exist? bin_path then
|
||||
# TODO change this to a more useful warning
|
||||
warn "#{bin_path} maybe `gem pristine #{spec.name}` will fix it?"
|
||||
next
|
||||
end
|
||||
|
||||
mode = File.stat(bin_path).mode | 0111
|
||||
FileUtils.chmod mode, bin_path
|
||||
|
||||
check_executable_overwrite filename
|
||||
|
||||
if @wrappers then
|
||||
generate_bin_script filename, bindir
|
||||
generate_bin_script filename, @bin_dir
|
||||
else
|
||||
generate_bin_symlink filename, bindir
|
||||
generate_bin_symlink filename, @bin_dir
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -358,10 +453,21 @@ class Gem::Installer
|
|||
##
|
||||
# Generates a #! line for +bin_file_name+'s wrapper copying arguments if
|
||||
# necessary.
|
||||
#
|
||||
# If the :custom_shebang config is set, then it is used as a template
|
||||
# for how to create the shebang used for to run a gem's executables.
|
||||
#
|
||||
# The template supports 4 expansions:
|
||||
#
|
||||
# $env the path to the unix env utility
|
||||
# $ruby the path to the currently running ruby interpreter
|
||||
# $exec the path to the gem's executable
|
||||
# $name the name of the gem the executable is for
|
||||
#
|
||||
|
||||
def shebang(bin_file_name)
|
||||
ruby_name = Gem::ConfigMap[:ruby_install_name] if @env_shebang
|
||||
path = spec.bin_file bin_file_name
|
||||
path = File.join gem_dir, spec.bindir, bin_file_name
|
||||
first_line = File.open(path, "rb") {|file| file.gets}
|
||||
|
||||
if /\A#!/ =~ first_line then
|
||||
|
@ -371,7 +477,25 @@ class Gem::Installer
|
|||
shebang.strip! # Avoid nasty ^M issues.
|
||||
end
|
||||
|
||||
if not ruby_name then
|
||||
if which = Gem.configuration[:custom_shebang]
|
||||
# replace bin_file_name with "ruby" to avoid endless loops
|
||||
which = which.gsub(/ #{bin_file_name}$/," #{Gem::ConfigMap[:ruby_install_name]}")
|
||||
|
||||
which = which.gsub(/\$(\w+)/) do
|
||||
case $1
|
||||
when "env"
|
||||
@env_path ||= ENV_PATHS.find {|env_path| File.executable? env_path }
|
||||
when "ruby"
|
||||
"#{Gem.ruby}#{opts}"
|
||||
when "exec"
|
||||
bin_file_name
|
||||
when "name"
|
||||
spec.name
|
||||
end
|
||||
end
|
||||
|
||||
"#!#{which}"
|
||||
elsif not ruby_name then
|
||||
"#!#{Gem.ruby}#{opts}"
|
||||
elsif opts then
|
||||
"#!/bin/sh\n'exec' #{ruby_name.dump} '-x' \"$0\" \"$@\"\n#{shebang}"
|
||||
|
@ -382,6 +506,7 @@ class Gem::Installer
|
|||
end
|
||||
end
|
||||
|
||||
# DOC: Missing docs or :nodoc:.
|
||||
def ensure_required_ruby_version_met
|
||||
if rrv = spec.required_ruby_version then
|
||||
unless rrv.satisfied_by? Gem.ruby_version then
|
||||
|
@ -390,9 +515,10 @@ class Gem::Installer
|
|||
end
|
||||
end
|
||||
|
||||
# DOC: Missing docs or :nodoc:.
|
||||
def ensure_required_rubygems_version_met
|
||||
if rrgv = spec.required_rubygems_version then
|
||||
unless rrgv.satisfied_by? Gem::Version.new(Gem::VERSION) then
|
||||
unless rrgv.satisfied_by? Gem.rubygems_version then
|
||||
raise Gem::InstallError,
|
||||
"#{spec.name} requires RubyGems version #{rrgv}. " +
|
||||
"Try 'gem update --system' to update RubyGems itself."
|
||||
|
@ -400,6 +526,7 @@ class Gem::Installer
|
|||
end
|
||||
end
|
||||
|
||||
# DOC: Missing docs or :nodoc:.
|
||||
def ensure_dependencies_met
|
||||
deps = spec.runtime_dependencies
|
||||
deps |= spec.development_dependencies if @development
|
||||
|
@ -409,13 +536,14 @@ class Gem::Installer
|
|||
end
|
||||
end
|
||||
|
||||
# DOC: Missing docs or :nodoc:.
|
||||
def process_options
|
||||
@options = {
|
||||
:bin_dir => nil,
|
||||
:env_shebang => false,
|
||||
:exec_format => false,
|
||||
:force => false,
|
||||
:install_dir => Gem.dir,
|
||||
:only_install_dir => false
|
||||
}.merge options
|
||||
|
||||
@env_shebang = options[:env_shebang]
|
||||
|
@ -425,13 +553,18 @@ class Gem::Installer
|
|||
@format_executable = options[:format_executable]
|
||||
@security_policy = options[:security_policy]
|
||||
@wrappers = options[:wrappers]
|
||||
@bin_dir = options[:bin_dir]
|
||||
@only_install_dir = options[:only_install_dir]
|
||||
|
||||
# If the user has asked for the gem to be installed in a directory that is
|
||||
# the system gem directory, then use the system bin directory, else create
|
||||
# (or use) a new bin dir under the gem_home.
|
||||
@bin_dir = options[:bin_dir] || Gem.bindir(gem_home)
|
||||
@development = options[:development]
|
||||
|
||||
raise "NOTE: Installer option :source_index is dead" if
|
||||
options[:source_index]
|
||||
@build_args = options[:build_args] || Gem::Command.build_args
|
||||
end
|
||||
|
||||
# DOC: Missing docs or :nodoc:.
|
||||
def check_that_user_bin_dir_is_in_path
|
||||
user_bin_dir = @bin_dir || Gem.bindir(gem_home)
|
||||
user_bin_dir.gsub!(File::SEPARATOR, File::ALT_SEPARATOR) if File::ALT_SEPARATOR
|
||||
|
@ -449,6 +582,7 @@ class Gem::Installer
|
|||
end
|
||||
end
|
||||
|
||||
# DOC: Missing docs or :nodoc:.
|
||||
def verify_gem_home(unpack = false)
|
||||
FileUtils.mkdir_p gem_home
|
||||
raise Gem::FilePermissionError, gem_home unless
|
||||
|
@ -499,7 +633,6 @@ GOTO :EOF
|
|||
:WinNT
|
||||
@"#{ruby}" "%~dpn0" %*
|
||||
TEXT
|
||||
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -508,7 +641,14 @@ TEXT
|
|||
|
||||
def build_extensions
|
||||
return if spec.extensions.empty?
|
||||
say "Building native extensions. This could take a while..."
|
||||
|
||||
if @build_args.empty?
|
||||
say "Building native extensions. This could take a while..."
|
||||
else
|
||||
say "Building native extensions with: '#{@build_args.join(' ')}'"
|
||||
say "This could take a while..."
|
||||
end
|
||||
|
||||
dest_path = File.join gem_dir, spec.require_paths.first
|
||||
ran_rake = false # only run rake once
|
||||
|
||||
|
@ -516,6 +656,9 @@ TEXT
|
|||
break if ran_rake
|
||||
results = []
|
||||
|
||||
extension ||= ""
|
||||
extension_dir = File.join gem_dir, File.dirname(extension)
|
||||
|
||||
builder = case extension
|
||||
when /extconf/ then
|
||||
Gem::Ext::ExtConfBuilder
|
||||
|
@ -525,43 +668,41 @@ TEXT
|
|||
ran_rake = true
|
||||
Gem::Ext::RakeBuilder
|
||||
else
|
||||
results = ["No builder for extension '#{extension}'"]
|
||||
nil
|
||||
message = "No builder for extension '#{extension}'"
|
||||
extension_build_error extension_dir, message
|
||||
end
|
||||
|
||||
|
||||
extension_dir = begin
|
||||
File.join gem_dir, File.dirname(extension)
|
||||
rescue TypeError # extension == nil
|
||||
gem_dir
|
||||
end
|
||||
|
||||
|
||||
begin
|
||||
Dir.chdir extension_dir do
|
||||
results = builder.build(extension, gem_dir, dest_path, results)
|
||||
results = builder.build(extension, gem_dir, dest_path,
|
||||
results, @build_args)
|
||||
|
||||
say results.join("\n") if Gem.configuration.really_verbose
|
||||
end
|
||||
rescue
|
||||
results = results.join "\n"
|
||||
extension_build_error(extension_dir, results.join("\n"))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
gem_make_out = File.join extension_dir, 'gem_make.out'
|
||||
##
|
||||
# Logs the build +output+ in +build_dir+, then raises ExtensionBuildError.
|
||||
|
||||
open gem_make_out, 'wb' do |io| io.puts results end
|
||||
def extension_build_error(build_dir, output)
|
||||
gem_make_out = File.join build_dir, 'gem_make.out'
|
||||
|
||||
message = <<-EOF
|
||||
open gem_make_out, 'wb' do |io| io.puts output end
|
||||
|
||||
message = <<-EOF
|
||||
ERROR: Failed to build gem native extension.
|
||||
|
||||
#{results}
|
||||
#{output}
|
||||
|
||||
Gem files will remain installed in #{gem_dir} for inspection.
|
||||
Results logged to #{gem_make_out}
|
||||
EOF
|
||||
|
||||
raise ExtensionBuildError, message
|
||||
end
|
||||
end
|
||||
raise ExtensionBuildError, message
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -570,36 +711,7 @@ EOF
|
|||
# Ensures that files can't be installed outside the gem directory.
|
||||
|
||||
def extract_files
|
||||
raise ArgumentError, "format required to extract from" if @format.nil?
|
||||
|
||||
@format.file_entries.each do |entry, file_data|
|
||||
path = entry['path'].untaint
|
||||
|
||||
if path.start_with? "/" then # for extra sanity
|
||||
raise Gem::InstallError, "attempt to install file into #{entry['path']}"
|
||||
end
|
||||
|
||||
path = File.expand_path File.join(gem_dir, path)
|
||||
|
||||
unless path.start_with? gem_dir then
|
||||
msg = "attempt to install file into %p under %s" %
|
||||
[entry['path'], gem_dir]
|
||||
raise Gem::InstallError, msg
|
||||
end
|
||||
|
||||
FileUtils.rm_rf(path) if File.exist? path
|
||||
|
||||
dir = File.dirname path
|
||||
FileUtils.mkdir_p dir unless File.exist? dir
|
||||
|
||||
File.open(path, "wb") do |out|
|
||||
out.write file_data
|
||||
end
|
||||
|
||||
FileUtils.chmod entry['mode'], path
|
||||
|
||||
say path if Gem.configuration.really_verbose
|
||||
end
|
||||
@package.extract_files gem_dir
|
||||
end
|
||||
|
||||
##
|
||||
|
|
|
@ -6,11 +6,26 @@ class Gem::Installer
|
|||
##
|
||||
# Available through requiring rubygems/installer_test_case
|
||||
|
||||
attr_writer :bin_dir
|
||||
|
||||
##
|
||||
# Available through requiring rubygems/installer_test_case
|
||||
|
||||
attr_writer :build_args
|
||||
|
||||
##
|
||||
# Available through requiring rubygems/installer_test_case
|
||||
|
||||
attr_writer :gem_dir
|
||||
|
||||
##
|
||||
# Available through requiring rubygems/installer_test_case
|
||||
|
||||
attr_writer :force
|
||||
|
||||
##
|
||||
# Available through requiring rubygems/installer_test_case
|
||||
|
||||
attr_writer :format
|
||||
|
||||
##
|
||||
|
@ -54,47 +69,68 @@ end
|
|||
|
||||
class Gem::InstallerTestCase < Gem::TestCase
|
||||
|
||||
##
|
||||
# Creates the following instance variables:
|
||||
#
|
||||
# @spec::
|
||||
# a spec named 'a', intended for regular installs
|
||||
# @user_spec::
|
||||
# a spec named 'b', intended for user installs
|
||||
|
||||
# @gem::
|
||||
# the path to a built gem from @spec
|
||||
# @user_spec::
|
||||
# the path to a built gem from @user_spec
|
||||
#
|
||||
# @installer::
|
||||
# a Gem::Installer for the @spec that installs into @gemhome
|
||||
# @user_installer::
|
||||
# a Gem::Installer for the @user_spec that installs into Gem.user_dir
|
||||
|
||||
def setup
|
||||
super
|
||||
|
||||
@installer_tmp = File.join @tempdir, 'installer'
|
||||
FileUtils.mkdir_p @installer_tmp
|
||||
@spec = quick_gem 'a' do |spec|
|
||||
util_make_exec spec
|
||||
end
|
||||
|
||||
Gem.use_paths @installer_tmp
|
||||
Gem.ensure_gem_subdirectories @installer_tmp
|
||||
@user_spec = quick_gem 'b' do |spec|
|
||||
util_make_exec spec
|
||||
end
|
||||
|
||||
@spec = quick_gem 'a'
|
||||
util_make_exec @spec
|
||||
util_build_gem @spec
|
||||
@gem = @spec.cache_file
|
||||
|
||||
@user_spec = quick_gem 'b'
|
||||
util_make_exec @user_spec
|
||||
util_build_gem @user_spec
|
||||
@user_gem = @user_spec.cache_file
|
||||
|
||||
Gem.use_paths @gemhome
|
||||
@gem = @spec.cache_file
|
||||
@user_gem = @user_spec.cache_file
|
||||
|
||||
@installer = util_installer @spec, @gemhome
|
||||
@user_installer = util_installer @user_spec, Gem.user_dir, :user
|
||||
|
||||
Gem.use_paths @gemhome
|
||||
end
|
||||
|
||||
def util_gem_bindir spec = @spec
|
||||
def util_gem_bindir spec = @spec # :nodoc:
|
||||
# TODO: deprecate
|
||||
spec.bin_dir
|
||||
end
|
||||
|
||||
def util_gem_dir spec = @spec
|
||||
def util_gem_dir spec = @spec # :nodoc:
|
||||
# TODO: deprecate
|
||||
spec.gem_dir
|
||||
end
|
||||
|
||||
##
|
||||
# The path where installed executables live
|
||||
|
||||
def util_inst_bindir
|
||||
File.join @gemhome, "bin"
|
||||
end
|
||||
|
||||
##
|
||||
# Adds an executable named "executable" to +spec+ with the given +shebang+.
|
||||
#
|
||||
# The executable is also written to the bin dir in @tmpdir and the installed
|
||||
# gem directory for +spec+.
|
||||
|
||||
def util_make_exec(spec = @spec, shebang = "#!/usr/bin/ruby")
|
||||
spec.executables = %w[executable]
|
||||
spec.files << 'bin/executable'
|
||||
|
@ -110,6 +146,14 @@ class Gem::InstallerTestCase < Gem::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Builds the @spec gem and returns an installer for it. The built gem
|
||||
# includes:
|
||||
#
|
||||
# bin/executable
|
||||
# lib/code.rb
|
||||
# ext/a/mkrf_conf.rb
|
||||
|
||||
def util_setup_gem(ui = @ui) # HACK fix use_ui to make this automatic
|
||||
@spec.files << File.join('lib', 'code.rb')
|
||||
@spec.extensions << File.join('ext', 'a', 'mkrf_conf.rb')
|
||||
|
@ -129,16 +173,24 @@ class Gem::InstallerTestCase < Gem::TestCase
|
|||
end
|
||||
|
||||
use_ui ui do
|
||||
FileUtils.rm @gem
|
||||
FileUtils.rm_f @gem
|
||||
|
||||
@gem = Gem::Builder.new(@spec).build
|
||||
@gem = Gem::Package.build @spec
|
||||
end
|
||||
end
|
||||
|
||||
@installer = Gem::Installer.new @gem
|
||||
end
|
||||
|
||||
##
|
||||
# Creates an installer for +spec+ that will install into +gem_home+. If
|
||||
# +user+ is true a user-install will be performed.
|
||||
|
||||
def util_installer(spec, gem_home, user=false)
|
||||
Gem::Installer.new spec.cache_file, :user_install => user
|
||||
Gem::Installer.new(spec.cache_file,
|
||||
:install_dir => gem_home,
|
||||
:user_install => user)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
|
|
@ -6,6 +6,17 @@ require 'rubygems/user_interaction'
|
|||
# retrieval during tests.
|
||||
|
||||
class Gem::MockGemUi < Gem::StreamUI
|
||||
##
|
||||
# Raised when you haven't provided enough input to your MockGemUi
|
||||
|
||||
class InputEOFError < RuntimeError
|
||||
|
||||
def initialize question
|
||||
super "Out of input for MockGemUi on #{question.inspect}"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class TermError < RuntimeError
|
||||
attr_reader :exit_code
|
||||
|
||||
|
@ -44,6 +55,12 @@ class Gem::MockGemUi < Gem::StreamUI
|
|||
@terminated = false
|
||||
end
|
||||
|
||||
def ask question
|
||||
raise InputEOFError, question if @ins.eof?
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
def input
|
||||
@ins.string
|
||||
end
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
##
|
||||
#
|
||||
# Represents a gem of name +name+ at +version+ of +platform+. These
|
||||
# wrap the data returned from the indexes.
|
||||
|
||||
require 'rubygems/platform'
|
||||
|
||||
class Gem::NameTuple
|
||||
def initialize(name, version, platform="ruby")
|
||||
@name = name
|
||||
@version = version
|
||||
|
||||
unless platform.kind_of? Gem::Platform
|
||||
platform = "ruby" if !platform or platform.empty?
|
||||
end
|
||||
|
||||
@platform = platform
|
||||
end
|
||||
|
||||
attr_reader :name, :version, :platform
|
||||
|
||||
##
|
||||
# Turn an array of [name, version, platform] into an array of
|
||||
# NameTuple objects.
|
||||
|
||||
def self.from_list list
|
||||
list.map { |t| new(*t) }
|
||||
end
|
||||
|
||||
##
|
||||
# Turn an array of NameTuple objects back into an array of
|
||||
# [name, version, platform] tuples.
|
||||
|
||||
def self.to_basic list
|
||||
list.map { |t| t.to_a }
|
||||
end
|
||||
|
||||
##
|
||||
# A null NameTuple, ie name=nil, version=0
|
||||
|
||||
def self.null
|
||||
new nil, Gem::Version.new(0), nil
|
||||
end
|
||||
|
||||
##
|
||||
# Indicate if this NameTuple matches the current platform.
|
||||
|
||||
def match_platform?
|
||||
Gem::Platform.match @platform
|
||||
end
|
||||
|
||||
##
|
||||
# Indicate if this NameTuple is for a prerelease version.
|
||||
def prerelease?
|
||||
@version.prerelease?
|
||||
end
|
||||
|
||||
##
|
||||
# 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
|
||||
end
|
||||
|
||||
##
|
||||
# Convert back to the [name, version, platform] tuple
|
||||
|
||||
def to_a
|
||||
[@name, @version, @platform]
|
||||
end
|
||||
|
||||
def to_s
|
||||
"#<Gem::NameTuple #{@name}, #{@version}, #{@platform}>"
|
||||
end
|
||||
|
||||
def <=> other
|
||||
to_a <=> other.to_a
|
||||
end
|
||||
|
||||
include Comparable
|
||||
|
||||
##
|
||||
# Compare with +other+. Supports another NameTuple or an Array
|
||||
# in the [name, version, platform] format.
|
||||
|
||||
def == other
|
||||
case other
|
||||
when self.class
|
||||
@name == other.name and
|
||||
@version == other.version and
|
||||
@platform == other.platform
|
||||
when Array
|
||||
to_a == other
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
alias_method :eql?, :==
|
||||
|
||||
def hash
|
||||
to_a.hash
|
||||
end
|
||||
|
||||
end
|
|
@ -1,153 +0,0 @@
|
|||
#--
|
||||
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
|
||||
# All rights reserved.
|
||||
# See LICENSE.txt for permissions.
|
||||
#++
|
||||
|
||||
require 'rubygems'
|
||||
|
||||
##
|
||||
# The format class knows the guts of the RubyGem .gem file format and provides
|
||||
# the capability to read gem files
|
||||
|
||||
class Gem::OldFormat
|
||||
|
||||
attr_accessor :spec, :file_entries, :gem_path
|
||||
|
||||
##
|
||||
# Constructs an instance of a Format object, representing the gem's data
|
||||
# structure.
|
||||
#
|
||||
# gem:: [String] The file name of the gem
|
||||
|
||||
def initialize(gem_path)
|
||||
require 'fileutils'
|
||||
require 'zlib'
|
||||
Gem.load_yaml
|
||||
|
||||
@gem_path = gem_path
|
||||
end
|
||||
|
||||
##
|
||||
# Reads the named gem file and returns a Format object, representing the
|
||||
# data from the gem file
|
||||
#
|
||||
# file_path:: [String] Path to the gem file
|
||||
|
||||
def self.from_file_by_path(file_path)
|
||||
unless File.exist?(file_path)
|
||||
raise Gem::Exception, "Cannot load gem file [#{file_path}]"
|
||||
end
|
||||
|
||||
File.open(file_path, 'rb') do |file|
|
||||
from_io(file, file_path)
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Reads a gem from an io stream and returns a Format object, representing
|
||||
# the data from the gem file
|
||||
#
|
||||
# io:: [IO] Stream from which to read the gem
|
||||
|
||||
def self.from_io(io, gem_path="(io)")
|
||||
format = self.new(gem_path)
|
||||
skip_ruby(io)
|
||||
format.spec = read_spec(io)
|
||||
format.file_entries = []
|
||||
read_files_from_gem(io) do |entry, file_data|
|
||||
format.file_entries << [entry, file_data]
|
||||
end
|
||||
format
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
##
|
||||
# Skips the Ruby self-install header. After calling this method, the
|
||||
# IO index will be set after the Ruby code.
|
||||
#
|
||||
# file:: [IO] The IO to process (skip the Ruby code)
|
||||
|
||||
def self.skip_ruby(file)
|
||||
end_seen = false
|
||||
loop {
|
||||
line = file.gets
|
||||
if(line == nil || line.chomp == "__END__") then
|
||||
end_seen = true
|
||||
break
|
||||
end
|
||||
}
|
||||
|
||||
if end_seen == false then
|
||||
raise Gem::Exception.new("Failed to find end of ruby script while reading gem")
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Reads the specification YAML from the supplied IO and constructs
|
||||
# a Gem::Specification from it. After calling this method, the
|
||||
# IO index will be set after the specification header.
|
||||
#
|
||||
# file:: [IO] The IO to process
|
||||
|
||||
def self.read_spec(file)
|
||||
yaml = ''
|
||||
|
||||
read_until_dashes file do |line|
|
||||
yaml << line
|
||||
end
|
||||
|
||||
Gem::Specification.from_yaml yaml
|
||||
rescue YAML::Error => e
|
||||
raise Gem::Exception, "Failed to parse gem specification out of gem file"
|
||||
rescue ArgumentError => e
|
||||
raise Gem::Exception, "Failed to parse gem specification out of gem file"
|
||||
end
|
||||
|
||||
##
|
||||
# Reads lines from the supplied IO until a end-of-yaml (---) is
|
||||
# reached
|
||||
#
|
||||
# file:: [IO] The IO to process
|
||||
# block:: [String] The read line
|
||||
|
||||
def self.read_until_dashes(file)
|
||||
while((line = file.gets) && line.chomp.strip != "---") do
|
||||
yield line
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Reads the embedded file data from a gem file, yielding an entry
|
||||
# containing metadata about the file and the file contents themselves
|
||||
# for each file that's archived in the gem.
|
||||
# NOTE: Many of these methods should be extracted into some kind of
|
||||
# Gem file read/writer
|
||||
#
|
||||
# gem_file:: [IO] The IO to process
|
||||
|
||||
def self.read_files_from_gem(gem_file)
|
||||
errstr = "Error reading files from gem"
|
||||
header_yaml = ''
|
||||
begin
|
||||
self.read_until_dashes(gem_file) do |line|
|
||||
header_yaml << line
|
||||
end
|
||||
header = YAML.load(header_yaml)
|
||||
raise Gem::Exception, errstr unless header
|
||||
|
||||
header.each do |entry|
|
||||
file_data = ''
|
||||
self.read_until_dashes(gem_file) do |line|
|
||||
file_data << line
|
||||
end
|
||||
yield [entry, Zlib::Inflate.inflate(file_data.strip.unpack("m")[0])]
|
||||
end
|
||||
rescue Zlib::DataError
|
||||
raise Gem::Exception, errstr
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -3,16 +3,54 @@
|
|||
# Copyright (C) 2004 Mauricio Julio Fernández Pradier
|
||||
# See LICENSE.txt for additional licensing information.
|
||||
#++
|
||||
#
|
||||
# Example using a Gem::Package
|
||||
#
|
||||
# Builds a .gem file given a Gem::Specification. A .gem file is a tarball
|
||||
# which contains a data.tar.gz and metadata.gz, and possibly signatures.
|
||||
#
|
||||
# require 'rubygems'
|
||||
# require 'rubygems/package'
|
||||
#
|
||||
# spec = Gem::Specification.new do |s|
|
||||
# s.summary = "Ruby based make-like utility."
|
||||
# s.name = 'rake'
|
||||
# s.version = PKG_VERSION
|
||||
# s.requirements << 'none'
|
||||
# s.files = PKG_FILES
|
||||
# s.description = <<-EOF
|
||||
# Rake is a Make-like program implemented in Ruby. Tasks
|
||||
# and dependencies are specified in standard Ruby syntax.
|
||||
# EOF
|
||||
# end
|
||||
#
|
||||
# Gem::Package.build spec
|
||||
#
|
||||
# Reads a .gem file.
|
||||
#
|
||||
# require 'rubygems'
|
||||
# require 'rubygems/package'
|
||||
#
|
||||
# the_gem = Gem::Package.new(path_to_dot_gem)
|
||||
# the_gem.contents # get the files in the gem
|
||||
# the_gem.extract_files destination_directory # extract the gem into a directory
|
||||
# the_gem.spec # get the spec out of the gem
|
||||
# the_gem.verify # check the gem is OK (contains valid gem specification, contains a not corrupt contents archive)
|
||||
#
|
||||
# #files are the files in the .gem tar file, not the ruby files in the gem
|
||||
# #extract_files and #contents automatically call #verify
|
||||
|
||||
require 'rubygems/security'
|
||||
require 'rubygems/specification'
|
||||
require 'rubygems/user_interaction'
|
||||
require 'zlib'
|
||||
|
||||
module Gem::Package
|
||||
class Gem::Package
|
||||
|
||||
include Gem::UserInteraction
|
||||
|
||||
class Error < Gem::Exception; end
|
||||
|
||||
class Error < StandardError; end
|
||||
class NonSeekableIO < Error; end
|
||||
class ClosedIO < Error; end
|
||||
class BadCheckSum < Error; end
|
||||
class TooLongFileName < Error; end
|
||||
class FormatError < Error
|
||||
attr_reader :path
|
||||
|
||||
|
@ -26,57 +64,489 @@ module Gem::Package
|
|||
|
||||
end
|
||||
|
||||
class PathError < Error
|
||||
def initialize destination, destination_dir
|
||||
super "installing into parent path %s of %s is not allowed" %
|
||||
[destination, destination_dir]
|
||||
end
|
||||
end
|
||||
|
||||
class NonSeekableIO < Error; end
|
||||
|
||||
class TooLongFileName < Error; end
|
||||
|
||||
##
|
||||
# Raised when a tar file is corrupt
|
||||
|
||||
class TarInvalidError < Error; end
|
||||
|
||||
# FIX: zenspider said: does it really take an IO?
|
||||
# passed to a method called open?!? that seems stupid.
|
||||
def self.open(io, mode = "r", signer = nil, &block)
|
||||
tar_type = case mode
|
||||
when 'r' then TarInput
|
||||
when 'w' then TarOutput
|
||||
else
|
||||
raise "Unknown Package open mode"
|
||||
end
|
||||
attr_accessor :build_time # :nodoc:
|
||||
|
||||
tar_type.open(io, signer, &block)
|
||||
##
|
||||
# Checksums for the contents of the package
|
||||
|
||||
attr_reader :checksums
|
||||
|
||||
##
|
||||
# The files in this package. This is not the contents of the gem, just the
|
||||
# files in the top-level container.
|
||||
|
||||
attr_reader :files
|
||||
|
||||
##
|
||||
# The security policy used for verifying the contents of this package.
|
||||
|
||||
attr_accessor :security_policy
|
||||
|
||||
##
|
||||
# Sets the Gem::Specification to use to build this package.
|
||||
|
||||
attr_writer :spec
|
||||
|
||||
def self.build spec, skip_validation=false
|
||||
gem_file = spec.file_name
|
||||
|
||||
package = new gem_file
|
||||
package.spec = spec
|
||||
package.build skip_validation
|
||||
|
||||
gem_file
|
||||
end
|
||||
|
||||
def self.pack(src, destname, signer = nil)
|
||||
TarOutput.open(destname, signer) do |outp|
|
||||
dir_class.chdir(src) do
|
||||
outp.metadata = (file_class.read("RPA/metadata") rescue nil)
|
||||
find_class.find('.') do |entry|
|
||||
case
|
||||
when file_class.file?(entry)
|
||||
entry.sub!(%r{\./}, "")
|
||||
next if entry =~ /\ARPA\//
|
||||
stat = File.stat(entry)
|
||||
outp.add_file_simple(entry, stat.mode, stat.size) do |os|
|
||||
file_class.open(entry, "rb") do |f|
|
||||
os.write(f.read(4096)) until f.eof?
|
||||
end
|
||||
end
|
||||
when file_class.dir?(entry)
|
||||
entry.sub!(%r{\./}, "")
|
||||
next if entry == "RPA"
|
||||
outp.mkdir(entry, file_class.stat(entry).mode)
|
||||
else
|
||||
raise "Don't know how to pack this yet!"
|
||||
end
|
||||
##
|
||||
# Creates a new Gem::Package for the file at +gem+.
|
||||
#
|
||||
# If +gem+ is an existing file in the old format a Gem::Package::Old will be
|
||||
# returned.
|
||||
|
||||
def self.new gem
|
||||
return super unless Gem::Package == self
|
||||
return super unless File.exist? gem
|
||||
|
||||
start = File.read gem, 20
|
||||
|
||||
return super unless start
|
||||
return super unless start.include? 'MD5SUM ='
|
||||
|
||||
Gem::Package::Old.new gem
|
||||
end
|
||||
|
||||
##
|
||||
# Creates a new package that will read or write to the file +gem+.
|
||||
|
||||
def initialize gem # :notnew:
|
||||
@gem = gem
|
||||
|
||||
@build_time = Time.now
|
||||
@checksums = {}
|
||||
@contents = nil
|
||||
@digests = Hash.new { |h, algorithm| h[algorithm] = {} }
|
||||
@files = nil
|
||||
@security_policy = nil
|
||||
@signatures = {}
|
||||
@signer = nil
|
||||
@spec = nil
|
||||
end
|
||||
|
||||
##
|
||||
# Adds a checksum for each entry in the gem to checksums.yaml.gz.
|
||||
|
||||
def add_checksums tar
|
||||
Gem.load_yaml
|
||||
|
||||
checksums_by_algorithm = Hash.new { |h, algorithm| h[algorithm] = {} }
|
||||
|
||||
@checksums.each do |name, digests|
|
||||
digests.each do |algorithm, digest|
|
||||
checksums_by_algorithm[algorithm][name] = digest.hexdigest
|
||||
end
|
||||
end
|
||||
|
||||
tar.add_file_signed 'checksums.yaml.gz', 0444, @signer do |io|
|
||||
gzip_to io do |gz_io|
|
||||
YAML.dump checksums_by_algorithm, gz_io
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Adds the files listed in the packages's Gem::Specification to data.tar.gz
|
||||
# and adds this file to the +tar+.
|
||||
|
||||
def add_contents tar # :nodoc:
|
||||
digests = tar.add_file_signed 'data.tar.gz', 0444, @signer do |io|
|
||||
gzip_to io do |gz_io|
|
||||
Gem::Package::TarWriter.new gz_io do |data_tar|
|
||||
add_files data_tar
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@checksums['data.tar.gz'] = digests
|
||||
end
|
||||
|
||||
##
|
||||
# Adds files included the package's Gem::Specification to the +tar+ file
|
||||
|
||||
def add_files tar # :nodoc:
|
||||
@spec.files.each do |file|
|
||||
stat = File.stat file
|
||||
|
||||
tar.add_file_simple file, stat.mode, stat.size do |dst_io|
|
||||
open file, 'rb' do |src_io|
|
||||
dst_io.write src_io.read 16384 until src_io.eof?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Adds the package's Gem::Specification to the +tar+ file
|
||||
|
||||
def add_metadata tar # :nodoc:
|
||||
digests = tar.add_file_signed 'metadata.gz', 0444, @signer do |io|
|
||||
gzip_to io do |gz_io|
|
||||
gz_io.write @spec.to_yaml
|
||||
end
|
||||
end
|
||||
|
||||
@checksums['metadata.gz'] = digests
|
||||
end
|
||||
|
||||
##
|
||||
# Builds this package based on the specification set by #spec=
|
||||
|
||||
def build skip_validation = false
|
||||
require 'rubygems/security'
|
||||
|
||||
@spec.validate unless skip_validation
|
||||
@spec.mark_version
|
||||
|
||||
setup_signer
|
||||
|
||||
open @gem, 'wb' do |gem_io|
|
||||
Gem::Package::TarWriter.new gem_io do |gem|
|
||||
add_metadata gem
|
||||
add_contents gem
|
||||
add_checksums gem
|
||||
end
|
||||
end
|
||||
|
||||
say <<-EOM
|
||||
Successfully built RubyGem
|
||||
Name: #{@spec.name}
|
||||
Version: #{@spec.version}
|
||||
File: #{File.basename @spec.cache_file}
|
||||
EOM
|
||||
ensure
|
||||
@signer = nil
|
||||
end
|
||||
|
||||
##
|
||||
# A list of file names contained in this gem
|
||||
|
||||
def contents
|
||||
return @contents if @contents
|
||||
|
||||
verify unless @spec
|
||||
|
||||
@contents = []
|
||||
|
||||
open @gem, 'rb' do |io|
|
||||
gem_tar = Gem::Package::TarReader.new io
|
||||
|
||||
gem_tar.each do |entry|
|
||||
next unless entry.full_name == 'data.tar.gz'
|
||||
|
||||
open_tar_gz entry do |pkg_tar|
|
||||
pkg_tar.each do |contents_entry|
|
||||
@contents << contents_entry.full_name
|
||||
end
|
||||
end
|
||||
|
||||
return @contents
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Creates a digest of the TarEntry +entry+ from the digest algorithm set by
|
||||
# the security policy.
|
||||
|
||||
def digest entry # :nodoc:
|
||||
return unless @checksums
|
||||
|
||||
@checksums.each_key do |algorithm|
|
||||
digester = OpenSSL::Digest.new algorithm
|
||||
|
||||
digester << entry.read(16384) until entry.eof?
|
||||
|
||||
entry.rewind
|
||||
|
||||
@digests[algorithm][entry.full_name] = digester
|
||||
end
|
||||
|
||||
@digests
|
||||
end
|
||||
|
||||
##
|
||||
# Extracts the files in this package into +destination_dir+
|
||||
|
||||
def extract_files destination_dir
|
||||
verify unless @spec
|
||||
|
||||
FileUtils.mkdir_p destination_dir
|
||||
|
||||
open @gem, 'rb' do |io|
|
||||
reader = Gem::Package::TarReader.new io
|
||||
|
||||
reader.each do |entry|
|
||||
next unless entry.full_name == 'data.tar.gz'
|
||||
|
||||
extract_tar_gz entry, destination_dir
|
||||
|
||||
return # ignore further entries
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Extracts all the files in the gzipped tar archive +io+ into
|
||||
# +destination_dir+.
|
||||
#
|
||||
# If an entry in the archive contains a relative path above
|
||||
# +destination_dir+ or an absolute path is encountered an exception is
|
||||
# raised.
|
||||
|
||||
def extract_tar_gz io, destination_dir # :nodoc:
|
||||
open_tar_gz io do |tar|
|
||||
tar.each do |entry|
|
||||
destination = install_location entry.full_name, destination_dir
|
||||
|
||||
FileUtils.rm_rf destination
|
||||
|
||||
FileUtils.mkdir_p File.dirname destination
|
||||
|
||||
open destination, 'wb', entry.header.mode do |out|
|
||||
out.write entry.read
|
||||
out.fsync rescue nil # for filesystems without fsync(2)
|
||||
end
|
||||
|
||||
say destination if Gem.configuration.really_verbose
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Gzips content written to +gz_io+ to +io+.
|
||||
#--
|
||||
# Also sets the gzip modification time to the package build time to ease
|
||||
# testing.
|
||||
|
||||
def gzip_to io # :yields: gz_io
|
||||
gz_io = Zlib::GzipWriter.new io, Zlib::BEST_COMPRESSION
|
||||
gz_io.mtime = @build_time
|
||||
|
||||
yield gz_io
|
||||
ensure
|
||||
gz_io.close
|
||||
end
|
||||
|
||||
##
|
||||
# Returns the full path for installing +filename+.
|
||||
#
|
||||
# If +filename+ is not inside +destination_dir+ an exception is raised.
|
||||
|
||||
def install_location filename, destination_dir # :nodoc:
|
||||
raise Gem::Package::PathError.new(filename, destination_dir) if
|
||||
filename.start_with? '/'
|
||||
|
||||
destination = File.join destination_dir, filename
|
||||
destination = File.expand_path destination
|
||||
|
||||
raise Gem::Package::PathError.new(destination, destination_dir) unless
|
||||
destination.start_with? destination_dir
|
||||
|
||||
destination.untaint
|
||||
destination
|
||||
end
|
||||
|
||||
##
|
||||
# Loads a Gem::Specification from the TarEntry +entry+
|
||||
|
||||
def load_spec entry # :nodoc:
|
||||
case entry.full_name
|
||||
when 'metadata' then
|
||||
@spec = Gem::Specification.from_yaml entry.read
|
||||
when 'metadata.gz' then
|
||||
args = [entry]
|
||||
args << { :external_encoding => Encoding::UTF_8 } if
|
||||
Object.const_defined? :Encoding
|
||||
|
||||
Zlib::GzipReader.wrap(*args) do |gzio|
|
||||
@spec = Gem::Specification.from_yaml gzio.read
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Opens +io+ as a gzipped tar archive
|
||||
|
||||
def open_tar_gz io # :nodoc:
|
||||
Zlib::GzipReader.wrap io do |gzio|
|
||||
tar = Gem::Package::TarReader.new gzio
|
||||
|
||||
yield tar
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Reads and loads checksums.yaml.gz from the tar file +gem+
|
||||
|
||||
def read_checksums gem
|
||||
Gem.load_yaml
|
||||
|
||||
@checksums = gem.seek 'checksums.yaml.gz' do |entry|
|
||||
Zlib::GzipReader.wrap entry do |gz_io|
|
||||
YAML.load gz_io.read
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Prepares the gem for signing and checksum generation. If a signing
|
||||
# certificate and key are not present only checksum generation is set up.
|
||||
|
||||
def setup_signer
|
||||
if @spec.signing_key then
|
||||
@signer = Gem::Security::Signer.new @spec.signing_key, @spec.cert_chain
|
||||
@spec.signing_key = nil
|
||||
@spec.cert_chain = @signer.cert_chain.map { |cert| cert.to_s }
|
||||
else
|
||||
@signer = Gem::Security::Signer.new nil, nil
|
||||
@spec.cert_chain = @signer.cert_chain.map { |cert| cert.to_pem } if
|
||||
@signer.cert_chain
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# The spec for this gem.
|
||||
#
|
||||
# If this is a package for a built gem the spec is loaded from the
|
||||
# gem and returned. If this is a package for a gem being built the provided
|
||||
# spec is returned.
|
||||
|
||||
def spec
|
||||
verify unless @spec
|
||||
|
||||
@spec
|
||||
end
|
||||
|
||||
##
|
||||
# Verifies that this gem:
|
||||
#
|
||||
# * Contains a valid gem specification
|
||||
# * Contains a contents archive
|
||||
# * The contents archive is not corrupt
|
||||
#
|
||||
# After verification the gem specification from the gem is available from
|
||||
# #spec
|
||||
|
||||
def verify
|
||||
@files = []
|
||||
@spec = nil
|
||||
|
||||
open @gem, 'rb' do |io|
|
||||
Gem::Package::TarReader.new io do |reader|
|
||||
read_checksums reader
|
||||
|
||||
verify_files reader
|
||||
end
|
||||
end
|
||||
|
||||
verify_checksums @digests, @checksums
|
||||
|
||||
@security_policy.verify_signatures @spec, @digests, @signatures if
|
||||
@security_policy
|
||||
|
||||
true
|
||||
rescue Errno::ENOENT => e
|
||||
raise Gem::Package::FormatError.new e.message
|
||||
rescue Gem::Package::TarInvalidError => e
|
||||
raise Gem::Package::FormatError.new e.message, @gem
|
||||
end
|
||||
|
||||
##
|
||||
# Verifies the +checksums+ against the +digests+. This check is not
|
||||
# cryptographically secure. Missing checksums are ignored.
|
||||
|
||||
def verify_checksums digests, checksums # :nodoc:
|
||||
return unless checksums
|
||||
|
||||
checksums.sort.each do |algorithm, gem_digests|
|
||||
gem_digests.sort.each do |file_name, gem_hexdigest|
|
||||
computed_digest = digests[algorithm][file_name]
|
||||
|
||||
unless computed_digest.hexdigest == gem_hexdigest then
|
||||
raise Gem::Package::FormatError.new \
|
||||
"#{algorithm} checksum mismatch for #{file_name}", @gem
|
||||
end
|
||||
end
|
||||
end
|
||||
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
|
||||
when 'checksums.yaml.gz' then
|
||||
next # already handled
|
||||
else
|
||||
digest entry
|
||||
end
|
||||
|
||||
case file_name
|
||||
when /^metadata(.gz)?$/ then
|
||||
load_spec entry
|
||||
when 'data.tar.gz' then
|
||||
verify_gz entry
|
||||
end
|
||||
end
|
||||
|
||||
unless @spec then
|
||||
raise Gem::Package::FormatError.new 'package metadata is missing', @gem
|
||||
end
|
||||
|
||||
unless @files.include? 'data.tar.gz' then
|
||||
raise Gem::Package::FormatError.new \
|
||||
'package content (data.tar.gz) is missing', @gem
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Verifies that +entry+ is a valid gzipped file.
|
||||
|
||||
def verify_gz entry # :nodoc:
|
||||
Zlib::GzipReader.wrap entry do |gzio|
|
||||
gzio.read 16384 until gzio.eof? # gzip checksum verification
|
||||
end
|
||||
rescue Zlib::GzipFile::Error => e
|
||||
raise Gem::Package::FormatError.new(e.message, entry.full_name)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
require 'rubygems/package/f_sync_dir'
|
||||
require 'rubygems/package/digest_io'
|
||||
require 'rubygems/package/old'
|
||||
require 'rubygems/package/tar_header'
|
||||
require 'rubygems/package/tar_input'
|
||||
require 'rubygems/package/tar_output'
|
||||
require 'rubygems/package/tar_reader'
|
||||
require 'rubygems/package/tar_reader/entry'
|
||||
require 'rubygems/package/tar_writer'
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
##
|
||||
# IO wrapper that creates digests of contents written to the IO it wraps.
|
||||
|
||||
class Gem::Package::DigestIO
|
||||
|
||||
##
|
||||
# Collected digests for wrapped writes.
|
||||
#
|
||||
# {
|
||||
# 'SHA1' => #<OpenSSL::Digest: [...]>,
|
||||
# 'SHA512' => #<OpenSSL::Digest: [...]>,
|
||||
# }
|
||||
|
||||
attr_reader :digests
|
||||
|
||||
##
|
||||
# Wraps +io+ and updates digest for each of the digest algorithms in
|
||||
# the +digests+ Hash. Returns the digests hash. Example:
|
||||
#
|
||||
# io = StringIO.new
|
||||
# digests = {
|
||||
# 'SHA1' => OpenSSL::Digest.new('SHA1'),
|
||||
# 'SHA512' => OpenSSL::Digest.new('SHA512'),
|
||||
# }
|
||||
#
|
||||
# Gem::Package::DigestIO.wrap io, digests do |digest_io|
|
||||
# digest_io.write "hello"
|
||||
# end
|
||||
#
|
||||
# digests['SHA1'].hexdigest #=> "aaf4c61d[...]"
|
||||
# digests['SHA512'].hexdigest #=> "9b71d224[...]"
|
||||
|
||||
def self.wrap io, digests
|
||||
digest_io = new io, digests
|
||||
|
||||
yield digest_io
|
||||
|
||||
return digests
|
||||
end
|
||||
|
||||
##
|
||||
# Creates a new DigestIO instance. Using ::wrap is recommended, see the
|
||||
# ::wrap documentation for documentation of +io+ and +digests+.
|
||||
|
||||
def initialize io, digests
|
||||
@io = io
|
||||
@digests = digests
|
||||
end
|
||||
|
||||
##
|
||||
# Writes +data+ to the underlying IO and updates the digests
|
||||
|
||||
def write data
|
||||
result = @io.write data
|
||||
|
||||
@digests.each do |_, digest|
|
||||
digest << data
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#--
|
||||
# Copyright (C) 2004 Mauricio Julio Fernández Pradier
|
||||
# See LICENSE.txt for additional licensing information.
|
||||
#++
|
||||
|
||||
module Gem::Package::FSyncDir
|
||||
|
||||
private
|
||||
|
||||
##
|
||||
# make sure this hits the disc
|
||||
|
||||
def fsync_dir(dirname)
|
||||
dir = open dirname, 'r'
|
||||
dir.fsync
|
||||
rescue # ignore IOError if it's an unpatched (old) Ruby
|
||||
ensure
|
||||
dir.close if dir rescue nil
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
#--
|
||||
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
|
||||
# All rights reserved.
|
||||
# See LICENSE.txt for permissions.
|
||||
#++
|
||||
|
||||
##
|
||||
# The format class knows the guts of the ancient .gem file format and provides
|
||||
# the capability to read such ancient gems.
|
||||
#
|
||||
# Please pretend this doesn't exist.
|
||||
|
||||
class Gem::Package::Old < Gem::Package
|
||||
|
||||
undef_method :spec=
|
||||
|
||||
##
|
||||
# Creates a new old-format package reader for +gem+. Old-format packages
|
||||
# cannot be written.
|
||||
|
||||
def initialize gem
|
||||
require 'fileutils'
|
||||
require 'zlib'
|
||||
Gem.load_yaml
|
||||
|
||||
@gem = gem
|
||||
@contents = nil
|
||||
@spec = nil
|
||||
end
|
||||
|
||||
##
|
||||
# A list of file names contained in this gem
|
||||
|
||||
def contents
|
||||
return @contents if @contents
|
||||
|
||||
open @gem, 'rb' do |io|
|
||||
read_until_dashes io # spec
|
||||
header = file_list io
|
||||
|
||||
@contents = header.map { |file| file['path'] }
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Extracts the files in this package into +destination_dir+
|
||||
|
||||
def extract_files destination_dir
|
||||
errstr = "Error reading files from gem"
|
||||
|
||||
open @gem, 'rb' do |io|
|
||||
read_until_dashes io # spec
|
||||
header = file_list io
|
||||
raise Gem::Exception, errstr unless header
|
||||
|
||||
header.each do |entry|
|
||||
full_name = entry['path']
|
||||
|
||||
destination = install_location full_name, destination_dir
|
||||
|
||||
file_data = ''
|
||||
|
||||
read_until_dashes io do |line|
|
||||
file_data << line
|
||||
end
|
||||
|
||||
file_data = file_data.strip.unpack("m")[0]
|
||||
file_data = Zlib::Inflate.inflate file_data
|
||||
|
||||
raise Gem::Package::FormatError, "#{full_name} in #{@gem} is corrupt" if
|
||||
file_data.length != entry['size'].to_i
|
||||
|
||||
FileUtils.rm_rf destination
|
||||
|
||||
FileUtils.mkdir_p File.dirname destination
|
||||
|
||||
open destination, 'wb', entry['mode'] do |out|
|
||||
out.write file_data
|
||||
end
|
||||
|
||||
say destination if Gem.configuration.really_verbose
|
||||
end
|
||||
end
|
||||
rescue Zlib::DataError
|
||||
raise Gem::Exception, errstr
|
||||
end
|
||||
|
||||
##
|
||||
# Reads the file list section from the old-format gem +io+
|
||||
|
||||
def file_list io # :nodoc:
|
||||
header = ''
|
||||
|
||||
read_until_dashes io do |line|
|
||||
header << line
|
||||
end
|
||||
|
||||
YAML.load header
|
||||
end
|
||||
|
||||
##
|
||||
# Reads lines until a "---" separator is found
|
||||
|
||||
def read_until_dashes io # :nodoc:
|
||||
while (line = io.gets) && line.chomp.strip != "---" do
|
||||
yield line if block_given?
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Skips the Ruby self-install header in +io+.
|
||||
|
||||
def skip_ruby io # :nodoc:
|
||||
loop do
|
||||
line = io.gets
|
||||
|
||||
return if line.chomp == '__END__'
|
||||
break unless line
|
||||
end
|
||||
|
||||
raise Gem::Exception, "Failed to find end of ruby script while reading gem"
|
||||
end
|
||||
|
||||
##
|
||||
# The specification for this gem
|
||||
|
||||
def spec
|
||||
return @spec if @spec
|
||||
|
||||
yaml = ''
|
||||
|
||||
open @gem, 'rb' do |io|
|
||||
skip_ruby io
|
||||
read_until_dashes io do |line|
|
||||
yaml << line
|
||||
end
|
||||
end
|
||||
|
||||
@spec = Gem::Specification.from_yaml yaml
|
||||
rescue YAML::SyntaxError => e
|
||||
raise Gem::Exception, "Failed to parse gem specification out of gem file"
|
||||
rescue ArgumentError => e
|
||||
raise Gem::Exception, "Failed to parse gem specification out of gem file"
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -102,61 +102,24 @@ class Gem::Package::TarHeader
|
|||
|
||||
fields = header.unpack UNPACK_FORMAT
|
||||
|
||||
name = fields.shift
|
||||
mode = fields.shift.oct
|
||||
uid = fields.shift.oct
|
||||
gid = fields.shift.oct
|
||||
size = fields.shift.oct
|
||||
mtime = fields.shift.oct
|
||||
checksum = fields.shift.oct
|
||||
typeflag = fields.shift
|
||||
linkname = fields.shift
|
||||
magic = fields.shift
|
||||
version = fields.shift.oct
|
||||
uname = fields.shift
|
||||
gname = fields.shift
|
||||
devmajor = fields.shift.oct
|
||||
devminor = fields.shift.oct
|
||||
prefix = fields.shift
|
||||
new :name => fields.shift,
|
||||
:mode => fields.shift.oct,
|
||||
:uid => fields.shift.oct,
|
||||
:gid => fields.shift.oct,
|
||||
:size => fields.shift.oct,
|
||||
:mtime => fields.shift.oct,
|
||||
:checksum => fields.shift.oct,
|
||||
:typeflag => fields.shift,
|
||||
:linkname => fields.shift,
|
||||
:magic => fields.shift,
|
||||
:version => fields.shift.oct,
|
||||
:uname => fields.shift,
|
||||
:gname => fields.shift,
|
||||
:devmajor => fields.shift.oct,
|
||||
:devminor => fields.shift.oct,
|
||||
:prefix => fields.shift,
|
||||
|
||||
new :name => name,
|
||||
:mode => mode,
|
||||
:uid => uid,
|
||||
:gid => gid,
|
||||
:size => size,
|
||||
:mtime => mtime,
|
||||
:checksum => checksum,
|
||||
:typeflag => typeflag,
|
||||
:linkname => linkname,
|
||||
:magic => magic,
|
||||
:version => version,
|
||||
:uname => uname,
|
||||
:gname => gname,
|
||||
:devmajor => devmajor,
|
||||
:devminor => devminor,
|
||||
:prefix => prefix,
|
||||
|
||||
:empty => empty
|
||||
|
||||
# HACK unfactor for Rubinius
|
||||
#new :name => fields.shift,
|
||||
# :mode => fields.shift.oct,
|
||||
# :uid => fields.shift.oct,
|
||||
# :gid => fields.shift.oct,
|
||||
# :size => fields.shift.oct,
|
||||
# :mtime => fields.shift.oct,
|
||||
# :checksum => fields.shift.oct,
|
||||
# :typeflag => fields.shift,
|
||||
# :linkname => fields.shift,
|
||||
# :magic => fields.shift,
|
||||
# :version => fields.shift.oct,
|
||||
# :uname => fields.shift,
|
||||
# :gname => fields.shift,
|
||||
# :devmajor => fields.shift.oct,
|
||||
# :devminor => fields.shift.oct,
|
||||
# :prefix => fields.shift,
|
||||
|
||||
# :empty => empty
|
||||
:empty => empty
|
||||
end
|
||||
|
||||
##
|
||||
|
|
|
@ -1,235 +0,0 @@
|
|||
# -*- coding: iso-8859-1 -*-
|
||||
#++
|
||||
# Copyright (C) 2004 Mauricio Julio Fernández Pradier
|
||||
# See LICENSE.txt for additional licensing information.
|
||||
#--
|
||||
|
||||
require 'zlib'
|
||||
Gem.load_yaml
|
||||
|
||||
class Gem::Package::TarInput
|
||||
|
||||
include Gem::Package::FSyncDir
|
||||
include Enumerable
|
||||
|
||||
attr_reader :metadata
|
||||
|
||||
private_class_method :new
|
||||
|
||||
def self.open(io, security_policy = nil, &block)
|
||||
is = new io, security_policy
|
||||
|
||||
yield is
|
||||
ensure
|
||||
is.close if is
|
||||
end
|
||||
|
||||
def initialize(io, security_policy = nil)
|
||||
@io = io
|
||||
@tarreader = Gem::Package::TarReader.new @io
|
||||
has_meta = false
|
||||
|
||||
data_sig, meta_sig, data_dgst, meta_dgst = nil, nil, nil, nil
|
||||
dgst_algo = security_policy ? Gem::Security::OPT[:dgst_algo] : nil
|
||||
|
||||
@tarreader.each do |entry|
|
||||
case entry.full_name
|
||||
when "metadata"
|
||||
@metadata = load_gemspec entry.read
|
||||
has_meta = true
|
||||
when "metadata.gz"
|
||||
begin
|
||||
# if we have a security_policy, then pre-read the metadata file
|
||||
# and calculate it's digest
|
||||
sio = nil
|
||||
if security_policy
|
||||
Gem.ensure_ssl_available
|
||||
sio = StringIO.new(entry.read)
|
||||
meta_dgst = dgst_algo.digest(sio.string)
|
||||
sio.rewind
|
||||
end
|
||||
|
||||
# Ruby 1.8 doesn't have encoding and YAML is UTF-8
|
||||
args = [sio || entry]
|
||||
args << { :external_encoding => Encoding::UTF_8 } if
|
||||
Object.const_defined?(:Encoding)
|
||||
|
||||
gzis = Zlib::GzipReader.new(*args)
|
||||
|
||||
# YAML wants an instance of IO
|
||||
@metadata = load_gemspec(gzis)
|
||||
has_meta = true
|
||||
ensure
|
||||
gzis.close unless gzis.nil?
|
||||
end
|
||||
when 'metadata.gz.sig'
|
||||
meta_sig = entry.read
|
||||
when 'data.tar.gz.sig'
|
||||
data_sig = entry.read
|
||||
when 'data.tar.gz'
|
||||
if security_policy
|
||||
Gem.ensure_ssl_available
|
||||
data_dgst = dgst_algo.digest(entry.read)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if security_policy then
|
||||
Gem.ensure_ssl_available
|
||||
|
||||
# map trust policy from string to actual class (or a serialized YAML
|
||||
# file, if that exists)
|
||||
if String === security_policy then
|
||||
if Gem::Security::Policies.key? security_policy then
|
||||
# load one of the pre-defined security policies
|
||||
security_policy = Gem::Security::Policies[security_policy]
|
||||
elsif File.exist? security_policy then
|
||||
# FIXME: this doesn't work yet
|
||||
security_policy = YAML.load File.read(security_policy)
|
||||
else
|
||||
raise Gem::Exception, "Unknown trust policy '#{security_policy}'"
|
||||
end
|
||||
end
|
||||
|
||||
if data_sig && data_dgst && meta_sig && meta_dgst then
|
||||
# the user has a trust policy, and we have a signed gem
|
||||
# file, so use the trust policy to verify the gem signature
|
||||
|
||||
begin
|
||||
security_policy.verify_gem(data_sig, data_dgst, @metadata.cert_chain)
|
||||
rescue Exception => e
|
||||
raise "Couldn't verify data signature: #{e}"
|
||||
end
|
||||
|
||||
begin
|
||||
security_policy.verify_gem(meta_sig, meta_dgst, @metadata.cert_chain)
|
||||
rescue Exception => e
|
||||
raise "Couldn't verify metadata signature: #{e}"
|
||||
end
|
||||
elsif security_policy.only_signed
|
||||
raise Gem::Exception, "Unsigned gem"
|
||||
else
|
||||
# FIXME: should display warning here (trust policy, but
|
||||
# either unsigned or badly signed gem file)
|
||||
end
|
||||
end
|
||||
|
||||
@tarreader.rewind
|
||||
|
||||
unless has_meta then
|
||||
path = io.path if io.respond_to? :path
|
||||
error = Gem::Package::FormatError.new 'no metadata found', path
|
||||
raise error
|
||||
end
|
||||
end
|
||||
|
||||
def close
|
||||
@io.close
|
||||
@tarreader.close
|
||||
end
|
||||
|
||||
def each(&block)
|
||||
@tarreader.each do |entry|
|
||||
next unless entry.full_name == "data.tar.gz"
|
||||
is = zipped_stream entry
|
||||
|
||||
begin
|
||||
Gem::Package::TarReader.new is do |inner|
|
||||
inner.each(&block)
|
||||
end
|
||||
ensure
|
||||
is.close if is
|
||||
end
|
||||
end
|
||||
|
||||
@tarreader.rewind
|
||||
end
|
||||
|
||||
def extract_entry(destdir, entry, expected_md5sum = nil)
|
||||
if entry.directory? then
|
||||
dest = File.join destdir, entry.full_name
|
||||
|
||||
if File.directory? dest then
|
||||
FileUtils.chmod entry.header.mode, dest, :verbose => false
|
||||
else
|
||||
FileUtils.mkdir_p dest, :mode => entry.header.mode, :verbose => false
|
||||
end
|
||||
|
||||
fsync_dir dest
|
||||
fsync_dir File.join(dest, "..")
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
# it's a file
|
||||
md5 = Digest::MD5.new if expected_md5sum
|
||||
destdir = File.join destdir, File.dirname(entry.full_name)
|
||||
FileUtils.mkdir_p destdir, :mode => 0755, :verbose => false
|
||||
destfile = File.join destdir, File.basename(entry.full_name)
|
||||
FileUtils.chmod 0600, destfile, :verbose => false rescue nil # Errno::ENOENT
|
||||
|
||||
open destfile, "wb", entry.header.mode do |os|
|
||||
loop do
|
||||
data = entry.read 4096
|
||||
break unless data
|
||||
# HACK shouldn't we check the MD5 before writing to disk?
|
||||
md5 << data if expected_md5sum
|
||||
os.write(data)
|
||||
end
|
||||
|
||||
os.fsync
|
||||
end
|
||||
|
||||
FileUtils.chmod entry.header.mode, destfile, :verbose => false
|
||||
fsync_dir File.dirname(destfile)
|
||||
fsync_dir File.join(File.dirname(destfile), "..")
|
||||
|
||||
if expected_md5sum && expected_md5sum != md5.hexdigest then
|
||||
raise Gem::Package::BadCheckSum
|
||||
end
|
||||
end
|
||||
|
||||
# Attempt to YAML-load a gemspec from the given _io_ parameter. Return
|
||||
# nil if it fails.
|
||||
def load_gemspec(io)
|
||||
Gem::Specification.from_yaml io
|
||||
rescue Gem::Exception
|
||||
nil
|
||||
end
|
||||
|
||||
##
|
||||
# Return an IO stream for the zipped entry.
|
||||
#
|
||||
# NOTE: Originally this method used two approaches, Return a GZipReader
|
||||
# directly, or read the GZipReader into a string and return a StringIO on
|
||||
# the string. The string IO approach was used for versions of ZLib before
|
||||
# 1.2.1 to avoid buffer errors on windows machines. Then we found that
|
||||
# errors happened with 1.2.1 as well, so we changed the condition. Then
|
||||
# we discovered errors occurred with versions as late as 1.2.3. At this
|
||||
# point (after some benchmarking to show we weren't seriously crippling
|
||||
# the unpacking speed) we threw our hands in the air and declared that
|
||||
# this method would use the String IO approach on all platforms at all
|
||||
# times. And that's the way it is.
|
||||
#
|
||||
# Revisited. Here's the beginning of the long story.
|
||||
# http://osdir.com/ml/lang.ruby.gems.devel/2007-06/msg00045.html
|
||||
#
|
||||
# StringIO wraping has never worked as a workaround by definition. Skipping
|
||||
# initial 10 bytes and passing -MAX_WBITS to Zlib::Inflate luckily works as
|
||||
# gzip reader, but it only works if the GZip header is 10 bytes long (see
|
||||
# below) and it does not check inflated stream consistency (CRC value in the
|
||||
# Gzip trailer.)
|
||||
#
|
||||
# RubyGems generated Gzip Header: 10 bytes
|
||||
# magic(2) + method(1) + flag(1) + mtime(4) + exflag(1) + os(1) +
|
||||
# orig_name(0) + comment(0)
|
||||
#
|
||||
# Ideally, it must return a GZipReader without meaningless buffering. We
|
||||
# have lots of CRuby committers around so let's fix windows build when we
|
||||
# received an error.
|
||||
def zipped_stream(entry)
|
||||
Zlib::GzipReader.new entry
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -1,146 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#--
|
||||
# Copyright (C) 2004 Mauricio Julio Fernández Pradier
|
||||
# See LICENSE.txt for additional licensing information.
|
||||
#++
|
||||
|
||||
##
|
||||
# TarOutput is a wrapper to TarWriter that builds gem-format tar file.
|
||||
#
|
||||
# Gem-format tar files contain the following files:
|
||||
# [data.tar.gz] A gzipped tar file containing the files that compose the gem
|
||||
# which will be extracted into the gem/ dir on installation.
|
||||
# [metadata.gz] A YAML format Gem::Specification.
|
||||
# [data.tar.gz.sig] A signature for the gem's data.tar.gz.
|
||||
# [metadata.gz.sig] A signature for the gem's metadata.gz.
|
||||
#
|
||||
# See TarOutput::open for usage details.
|
||||
|
||||
class Gem::Package::TarOutput
|
||||
|
||||
##
|
||||
# Creates a new TarOutput which will yield a TarWriter object for the
|
||||
# data.tar.gz portion of a gem-format tar file.
|
||||
#
|
||||
# See #initialize for details on +io+ and +signer+.
|
||||
#
|
||||
# See #add_gem_contents for details on adding metadata to the tar file.
|
||||
|
||||
def self.open(io, signer = nil, &block) # :yield: data_tar_writer
|
||||
tar_outputter = new io, signer
|
||||
tar_outputter.add_gem_contents(&block)
|
||||
tar_outputter.add_metadata
|
||||
tar_outputter.add_signatures
|
||||
|
||||
ensure
|
||||
tar_outputter.close
|
||||
end
|
||||
|
||||
##
|
||||
# Creates a new TarOutput that will write a gem-format tar file to +io+. If
|
||||
# +signer+ is given, the data.tar.gz and metadata.gz will be signed and
|
||||
# the signatures will be added to the tar file.
|
||||
|
||||
def initialize(io, signer)
|
||||
@io = io
|
||||
@signer = signer
|
||||
|
||||
@tar_writer = Gem::Package::TarWriter.new @io
|
||||
|
||||
@metadata = nil
|
||||
|
||||
@data_signature = nil
|
||||
@meta_signature = nil
|
||||
end
|
||||
|
||||
##
|
||||
# Yields a TarWriter for the data.tar.gz inside a gem-format tar file.
|
||||
# The yielded TarWriter has been extended with a #metadata= method for
|
||||
# attaching a YAML format Gem::Specification which will be written by
|
||||
# add_metadata.
|
||||
|
||||
def add_gem_contents
|
||||
@tar_writer.add_file "data.tar.gz", 0644 do |inner|
|
||||
sio = @signer ? StringIO.new : nil
|
||||
Zlib::GzipWriter.wrap(sio || inner) do |os|
|
||||
|
||||
Gem::Package::TarWriter.new os do |data_tar_writer|
|
||||
# :stopdoc:
|
||||
def data_tar_writer.metadata() @metadata end
|
||||
def data_tar_writer.metadata=(metadata) @metadata = metadata end
|
||||
# :startdoc:
|
||||
|
||||
yield data_tar_writer
|
||||
|
||||
@metadata = data_tar_writer.metadata
|
||||
end
|
||||
end
|
||||
|
||||
# if we have a signing key, then sign the data
|
||||
# digest and return the signature
|
||||
if @signer then
|
||||
require 'rubygems/security'
|
||||
digest = Gem::Security::OPT[:dgst_algo].digest sio.string
|
||||
@data_signature = @signer.sign digest
|
||||
inner.write sio.string
|
||||
end
|
||||
end
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
##
|
||||
# Adds metadata.gz to the gem-format tar file which was saved from a
|
||||
# previous #add_gem_contents call.
|
||||
|
||||
def add_metadata
|
||||
return if @metadata.nil?
|
||||
|
||||
@tar_writer.add_file "metadata.gz", 0644 do |io|
|
||||
begin
|
||||
sio = @signer ? StringIO.new : nil
|
||||
gzos = Zlib::GzipWriter.new(sio || io)
|
||||
gzos.write @metadata
|
||||
ensure
|
||||
gzos.flush
|
||||
gzos.finish
|
||||
|
||||
# if we have a signing key, then sign the metadata digest and return
|
||||
# the signature
|
||||
if @signer then
|
||||
require 'rubygems/security'
|
||||
digest = Gem::Security::OPT[:dgst_algo].digest sio.string
|
||||
@meta_signature = @signer.sign digest
|
||||
io.write sio.string
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Adds data.tar.gz.sig and metadata.gz.sig to the gem-format tar files if
|
||||
# a Gem::Security::Signer was sent to initialize.
|
||||
|
||||
def add_signatures
|
||||
if @data_signature then
|
||||
@tar_writer.add_file 'data.tar.gz.sig', 0644 do |io|
|
||||
io.write @data_signature
|
||||
end
|
||||
end
|
||||
|
||||
if @meta_signature then
|
||||
@tar_writer.add_file 'metadata.gz.sig', 0644 do |io|
|
||||
io.write @meta_signature
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Closes the TarOutput.
|
||||
|
||||
def close
|
||||
@tar_writer.close
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
class Gem::Package::TarReader
|
||||
|
||||
include Gem::Package
|
||||
include Enumerable
|
||||
|
||||
##
|
||||
# Raised if the tar IO is not seekable
|
||||
|
@ -52,9 +52,9 @@ class Gem::Package::TarReader
|
|||
# Iterates over files in the tarball yielding each entry
|
||||
|
||||
def each
|
||||
loop do
|
||||
return if @io.eof?
|
||||
return enum_for __method__ unless block_given?
|
||||
|
||||
until @io.eof? do
|
||||
header = Gem::Package::TarHeader.from @io
|
||||
return if header.empty?
|
||||
|
||||
|
@ -100,6 +100,23 @@ class Gem::Package::TarReader
|
|||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Seeks through the tar file until it finds the +entry+ with +name+ and
|
||||
# yields it. Rewinds the tar file to the beginning when the block
|
||||
# terminates.
|
||||
|
||||
def seek name # :yields: entry
|
||||
found = find do |entry|
|
||||
entry.full_name == name
|
||||
end
|
||||
|
||||
return unless found
|
||||
|
||||
return yield found
|
||||
ensure
|
||||
rewind
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
require 'rubygems/package/tar_reader/entry'
|
||||
|
|
|
@ -40,12 +40,12 @@ class Gem::Package::TarWriter
|
|||
# number of bytes will be more than #limit
|
||||
|
||||
def write(data)
|
||||
if data.size + @written > @limit
|
||||
if data.bytesize + @written > @limit
|
||||
raise FileOverflow, "You tried to feed more data than fits in the file."
|
||||
end
|
||||
@io.write data
|
||||
@written += data.size
|
||||
data.size
|
||||
@written += data.bytesize
|
||||
data.bytesize
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -129,6 +129,62 @@ class Gem::Package::TarWriter
|
|||
self
|
||||
end
|
||||
|
||||
##
|
||||
# Adds +name+ with permissions +mode+ to the tar, yielding +io+ for writing
|
||||
# the file. The +digest_algorithm+ is written to a read-only +name+.sum
|
||||
# file following the given file contents containing the digest name and
|
||||
# hexdigest separated by a tab.
|
||||
#
|
||||
# The created digest object is returned.
|
||||
|
||||
def add_file_digest name, mode, digest_algorithms # :yields: io
|
||||
digests = digest_algorithms.map do |digest_algorithm|
|
||||
digest = digest_algorithm.new
|
||||
[digest.name, digest]
|
||||
end
|
||||
|
||||
digests = Hash[*digests.flatten]
|
||||
|
||||
add_file name, mode do |io|
|
||||
Gem::Package::DigestIO.wrap io, digests do |digest_io|
|
||||
yield digest_io
|
||||
end
|
||||
end
|
||||
|
||||
digests
|
||||
end
|
||||
|
||||
##
|
||||
# Adds +name+ with permissions +mode+ to the tar, yielding +io+ for writing
|
||||
# the file. The +signer+ is used to add a digest file using its
|
||||
# digest_algorithm per add_file_digest and a cryptographic signature in
|
||||
# +name+.sig. If the signer has no key only the checksum file is added.
|
||||
#
|
||||
# Returns the digest.
|
||||
|
||||
def add_file_signed name, mode, signer
|
||||
digest_algorithms = [
|
||||
signer.digest_algorithm,
|
||||
OpenSSL::Digest::SHA512,
|
||||
].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
|
||||
end
|
||||
|
||||
signature = signer.sign signature_digest.digest
|
||||
|
||||
add_file_simple "#{name}.sig", 0444, signature.length do |io|
|
||||
io.write signature
|
||||
end if signature
|
||||
|
||||
digests
|
||||
end
|
||||
|
||||
##
|
||||
# Add file +name+ with permissions +mode+ +size+ bytes long. Yields an IO
|
||||
# to write the file to.
|
||||
|
@ -211,9 +267,9 @@ class Gem::Package::TarWriter
|
|||
# Splits +name+ into a name and prefix that can fit in the TarHeader
|
||||
|
||||
def split_name(name) # :nodoc:
|
||||
raise Gem::Package::TooLongFileName if name.size > 256
|
||||
raise Gem::Package::TooLongFileName if name.bytesize > 256
|
||||
|
||||
if name.size <= 100 then
|
||||
if name.bytesize <= 100 then
|
||||
prefix = ""
|
||||
else
|
||||
parts = name.split(/\//)
|
||||
|
@ -222,14 +278,14 @@ class Gem::Package::TarWriter
|
|||
|
||||
loop do
|
||||
nxt = parts.pop
|
||||
break if newname.size + 1 + nxt.size > 100
|
||||
break if newname.bytesize + 1 + nxt.bytesize > 100
|
||||
newname = nxt + "/" + newname
|
||||
end
|
||||
|
||||
prefix = (parts + [nxt]).join "/"
|
||||
name = newname
|
||||
|
||||
if name.size > 100 or prefix.size > 155 then
|
||||
if name.bytesize > 100 or prefix.bytesize > 155 then
|
||||
raise Gem::Package::TooLongFileName
|
||||
end
|
||||
end
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
require 'rubygems'
|
||||
require 'rubygems/package'
|
||||
begin
|
||||
gem 'rake'
|
||||
rescue Gem::LoadError
|
||||
|
@ -43,13 +44,10 @@ require 'rake/packagetask'
|
|||
# require 'rubygems/package_task'
|
||||
#
|
||||
# spec = Gem::Specification.new do |s|
|
||||
# s.platform = Gem::Platform::RUBY
|
||||
# s.summary = "Ruby based make-like utility."
|
||||
# s.name = 'rake'
|
||||
# s.version = PKG_VERSION
|
||||
# s.requirements << 'none'
|
||||
# s.require_path = 'lib'
|
||||
# s.autorequire = 'rake'
|
||||
# s.files = PKG_FILES
|
||||
# s.description = <<-EOF
|
||||
# Rake is a Make-like program implemented in Ruby. Tasks
|
||||
|
@ -113,7 +111,8 @@ class Gem::PackageTask < Rake::PackageTask
|
|||
file gem_path => [package_dir, gem_dir] + @gem_spec.files do
|
||||
chdir(gem_dir) do
|
||||
when_writing "Creating #{gem_spec.file_name}" do
|
||||
Gem::Builder.new(gem_spec).build
|
||||
Gem::Package.build gem_spec
|
||||
|
||||
verbose trace do
|
||||
mv gem_file, '..'
|
||||
end
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
##
|
||||
#
|
||||
# Gem::PathSupport facilitates the GEM_HOME and GEM_PATH environment settings
|
||||
# to the rest of RubyGems.
|
||||
#
|
||||
|
@ -42,16 +43,18 @@ class Gem::PathSupport
|
|||
# Set the Gem search path (as reported by Gem.path).
|
||||
|
||||
def path=(gpaths)
|
||||
gem_path = [@home]
|
||||
# FIX: it should be [home, *path], not [*path, home]
|
||||
|
||||
gem_path = []
|
||||
|
||||
# FIX: I can't tell wtf this is doing.
|
||||
gpaths ||= (ENV['GEM_PATH'] || "").empty? ? nil : ENV["GEM_PATH"]
|
||||
|
||||
if gpaths then
|
||||
if gpaths.kind_of?(Array) then
|
||||
gem_path.push(*gpaths)
|
||||
if gpaths
|
||||
if gpaths.kind_of?(Array)
|
||||
gem_path = gpaths.dup
|
||||
else
|
||||
gem_path.push(*gpaths.split(File::PATH_SEPARATOR))
|
||||
gem_path = gpaths.split(File::PATH_SEPARATOR)
|
||||
end
|
||||
|
||||
if File::ALT_SEPARATOR then
|
||||
|
@ -59,10 +62,14 @@ class Gem::PathSupport
|
|||
this_path.gsub File::ALT_SEPARATOR, File::SEPARATOR
|
||||
end
|
||||
end
|
||||
else
|
||||
gem_path.push(*Gem.default_path)
|
||||
|
||||
gem_path << APPLE_GEM_HOME if defined?(APPLE_GEM_HOME)
|
||||
gem_path << @home
|
||||
else
|
||||
gem_path = Gem.default_path + [@home]
|
||||
|
||||
if defined?(APPLE_GEM_HOME)
|
||||
gem_path << APPLE_GEM_HOME
|
||||
end
|
||||
end
|
||||
|
||||
@path = gem_path.uniq
|
||||
|
|
|
@ -21,7 +21,8 @@ class Gem::Platform
|
|||
|
||||
def self.match(platform)
|
||||
Gem.platforms.any? do |local_platform|
|
||||
platform.nil? or local_platform == platform or
|
||||
platform.nil? or
|
||||
local_platform == platform or
|
||||
(local_platform != Gem::Platform::RUBY and local_platform =~ platform)
|
||||
end
|
||||
end
|
||||
|
@ -65,28 +66,28 @@ class Gem::Platform
|
|||
@cpu, os = nil, cpu if os.nil? # legacy jruby
|
||||
|
||||
@os, @version = case os
|
||||
when /aix(\d+)/ then [ 'aix', $1 ]
|
||||
when /cygwin/ then [ 'cygwin', nil ]
|
||||
when /darwin(\d+)?/ then [ 'darwin', $1 ]
|
||||
when /^macruby$/ then [ 'macruby', nil ]
|
||||
when /freebsd(\d+)/ then [ 'freebsd', $1 ]
|
||||
when /hpux(\d+)/ then [ 'hpux', $1 ]
|
||||
when /^java$/, /^jruby$/ then [ 'java', nil ]
|
||||
when /^java([\d.]*)/ then [ 'java', $1 ]
|
||||
when /^dotnet$/ then [ 'dotnet', nil ]
|
||||
when /^dotnet([\d.]*)/ then [ 'dotnet', $1 ]
|
||||
when /linux/ then [ 'linux', $1 ]
|
||||
when /mingw32/ then [ 'mingw32', nil ]
|
||||
when /aix(\d+)?/ then [ 'aix', $1 ]
|
||||
when /cygwin/ then [ 'cygwin', nil ]
|
||||
when /darwin(\d+)?/ then [ 'darwin', $1 ]
|
||||
when /^macruby$/ then [ 'macruby', nil ]
|
||||
when /freebsd(\d+)?/ then [ 'freebsd', $1 ]
|
||||
when /hpux(\d+)?/ then [ 'hpux', $1 ]
|
||||
when /^java$/, /^jruby$/ then [ 'java', nil ]
|
||||
when /^java([\d.]*)/ then [ 'java', $1 ]
|
||||
when /^dotnet$/ then [ 'dotnet', nil ]
|
||||
when /^dotnet([\d.]*)/ then [ 'dotnet', $1 ]
|
||||
when /linux/ then [ 'linux', $1 ]
|
||||
when /mingw32/ then [ 'mingw32', nil ]
|
||||
when /(mswin\d+)(\_(\d+))?/ then
|
||||
os, version = $1, $3
|
||||
@cpu = 'x86' if @cpu.nil? and os =~ /32$/
|
||||
[os, version]
|
||||
when /(netbsd[a-z]*)(\d+)/ then [ $1, $2 ]
|
||||
when /openbsd(\d+\.\d+)/ then [ 'openbsd', $1 ]
|
||||
when /solaris(\d+\.\d+)/ then [ 'solaris', $1 ]
|
||||
when /netbsdelf/ then [ 'netbsdelf', nil ]
|
||||
when /openbsd(\d+\.\d+)?/ then [ 'openbsd', $1 ]
|
||||
when /solaris(\d+\.\d+)?/ then [ 'solaris', $1 ]
|
||||
# test
|
||||
when /^(\w+_platform)(\d+)/ then [ $1, $2 ]
|
||||
else [ 'unknown', nil ]
|
||||
when /^(\w+_platform)(\d+)?/ then [ $1, $2 ]
|
||||
else [ 'unknown', nil ]
|
||||
end
|
||||
when Gem::Platform then
|
||||
@cpu = arch.cpu
|
||||
|
@ -109,10 +110,6 @@ class Gem::Platform
|
|||
to_a.compact.join '-'
|
||||
end
|
||||
|
||||
def empty?
|
||||
to_s.empty?
|
||||
end
|
||||
|
||||
##
|
||||
# Is +other+ equal to this platform? Two platforms are equal if they have
|
||||
# the same CPU, OS and version.
|
||||
|
@ -186,9 +183,5 @@ class Gem::Platform
|
|||
# This will be replaced with Gem::Platform::local.
|
||||
|
||||
CURRENT = 'current'
|
||||
|
||||
extend Gem::Deprecate
|
||||
|
||||
deprecate :empty?, :none, 2011, 11
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,316 @@
|
|||
require 'rubygems'
|
||||
require 'rubygems/user_interaction'
|
||||
require 'fileutils'
|
||||
|
||||
begin
|
||||
gem 'rdoc'
|
||||
rescue Gem::LoadError
|
||||
# swallow
|
||||
else
|
||||
# This will force any deps that 'rdoc' might have
|
||||
# (such as json) that are ambigious to be activated, which
|
||||
# is important because we end up using Specification.reset
|
||||
# and we don't want the warning it pops out.
|
||||
Gem.finish_resolve
|
||||
end
|
||||
|
||||
loaded_hook = false
|
||||
|
||||
begin
|
||||
require 'rdoc/rubygems_hook'
|
||||
loaded_hook = true
|
||||
module Gem
|
||||
RDoc = RDoc::RubygemsHook
|
||||
end
|
||||
rescue LoadError
|
||||
end
|
||||
|
||||
##
|
||||
# Gem::RDoc provides methods to generate RDoc and ri data for installed gems.
|
||||
# It works for RDoc 1.0.1 (in Ruby 1.8) up to RDoc 3.6.
|
||||
#
|
||||
# This implementation is considered obsolete. The RDoc project is the
|
||||
# appropriate location to find this functionality. This file provides the
|
||||
# hooks to load RDoc generation code from the "rdoc" gem and a fallback in
|
||||
# case the installed version of RDoc does not have them.
|
||||
|
||||
class Gem::RDoc # :nodoc: all
|
||||
|
||||
include Gem::UserInteraction
|
||||
|
||||
@rdoc_version = nil
|
||||
@specs = []
|
||||
|
||||
##
|
||||
# Force installation of documentation?
|
||||
|
||||
attr_accessor :force
|
||||
|
||||
##
|
||||
# Generate rdoc?
|
||||
|
||||
attr_accessor :generate_rdoc
|
||||
|
||||
##
|
||||
# Generate ri data?
|
||||
|
||||
attr_accessor :generate_ri
|
||||
|
||||
class << self
|
||||
|
||||
##
|
||||
# Loaded version of RDoc. Set by ::load_rdoc
|
||||
|
||||
attr_reader :rdoc_version
|
||||
|
||||
end
|
||||
|
||||
##
|
||||
# Post installs hook that generates documentation for each specification in
|
||||
# +specs+
|
||||
|
||||
def self.generation_hook installer, specs
|
||||
types = installer.document
|
||||
|
||||
generate_rdoc = types.include? 'rdoc'
|
||||
generate_ri = types.include? 'ri'
|
||||
|
||||
specs.each do |spec|
|
||||
new(spec, generate_rdoc, generate_ri).generate
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Loads the RDoc generator
|
||||
|
||||
def self.load_rdoc
|
||||
return if @rdoc_version
|
||||
|
||||
begin
|
||||
require 'rdoc/rdoc'
|
||||
|
||||
@rdoc_version = if ::RDoc.const_defined? :VERSION then
|
||||
Gem::Version.new ::RDoc::VERSION
|
||||
else
|
||||
Gem::Version.new '1.0.1'
|
||||
end
|
||||
|
||||
rescue LoadError => e
|
||||
raise Gem::DocumentError, "RDoc is not installed: #{e}"
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Creates a new documentation generator for +spec+. RDoc and ri data
|
||||
# generation can be enabled or disabled through +generate_rdoc+ and
|
||||
# +generate_ri+ respectively.
|
||||
#
|
||||
# Only +generate_ri+ is enabled by default.
|
||||
|
||||
def initialize spec, generate_rdoc = false, generate_ri = true
|
||||
@doc_dir = spec.doc_dir
|
||||
@file_info = nil
|
||||
@force = false
|
||||
@rdoc = nil
|
||||
@spec = spec
|
||||
|
||||
@generate_rdoc = generate_rdoc
|
||||
@generate_ri = generate_ri
|
||||
|
||||
@rdoc_dir = spec.doc_dir 'rdoc'
|
||||
@ri_dir = spec.doc_dir 'ri'
|
||||
end
|
||||
|
||||
##
|
||||
# Removes legacy rdoc arguments from +args+
|
||||
|
||||
def delete_legacy_args args
|
||||
args.delete '--inline-source'
|
||||
args.delete '--promiscuous'
|
||||
args.delete '-p'
|
||||
args.delete '--one-file'
|
||||
end
|
||||
|
||||
##
|
||||
# Generates documentation using the named +generator+ ("darkfish" or "ri")
|
||||
# and following the given +options+.
|
||||
#
|
||||
# Documentation will be generated into +destination+
|
||||
|
||||
def document generator, options, destination
|
||||
options = options.dup
|
||||
options.exclude ||= [] # TODO maybe move to RDoc::Options#finish
|
||||
options.setup_generator generator
|
||||
options.op_dir = destination
|
||||
options.finish
|
||||
|
||||
@rdoc.options = options
|
||||
@rdoc.generator = options.generator.new options
|
||||
|
||||
say "Installing #{generator} documentation for #{@spec.full_name}"
|
||||
|
||||
FileUtils.mkdir_p options.op_dir
|
||||
|
||||
Dir.chdir options.op_dir do
|
||||
begin
|
||||
@rdoc.class.current = @rdoc
|
||||
@rdoc.generator.generate @file_info
|
||||
ensure
|
||||
@rdoc.class.current = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Generates RDoc and ri data
|
||||
|
||||
def generate
|
||||
return unless @generate_ri or @generate_rdoc
|
||||
|
||||
setup
|
||||
|
||||
if Gem::Requirement.new('< 3').satisfied_by? self.class.rdoc_version then
|
||||
generate_legacy
|
||||
else
|
||||
::RDoc::TopLevel.reset # TODO ::RDoc::RDoc.reset
|
||||
::RDoc::Parser::C.reset
|
||||
|
||||
options = ::RDoc::Options.new
|
||||
options.default_title = "#{@spec.full_name} Documentation"
|
||||
options.files = []
|
||||
options.files.push(*@spec.require_paths)
|
||||
options.files.push(*@spec.extra_rdoc_files)
|
||||
|
||||
args = @spec.rdoc_options
|
||||
|
||||
case config_args = Gem.configuration[:rdoc]
|
||||
when String then
|
||||
args = args.concat config_args.split
|
||||
when Array then
|
||||
args = args.concat config_args
|
||||
end
|
||||
|
||||
delete_legacy_args args
|
||||
options.parse args
|
||||
options.quiet = !Gem.configuration.really_verbose
|
||||
|
||||
@rdoc = new_rdoc
|
||||
@rdoc.options = options
|
||||
|
||||
Dir.chdir @spec.full_gem_path do
|
||||
@file_info = @rdoc.parse_files options.files
|
||||
end
|
||||
|
||||
document 'ri', options, @ri_dir if
|
||||
@generate_ri and (@force or not File.exist? @ri_dir)
|
||||
|
||||
document 'darkfish', options, @rdoc_dir if
|
||||
@generate_rdoc and (@force or not File.exist? @rdoc_dir)
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Generates RDoc and ri data for legacy RDoc versions. This method will not
|
||||
# exist in future versions.
|
||||
|
||||
def generate_legacy
|
||||
if @generate_rdoc then
|
||||
FileUtils.rm_rf @rdoc_dir
|
||||
say "Installing RDoc documentation for #{@spec.full_name}"
|
||||
legacy_rdoc '--op', @rdoc_dir
|
||||
end
|
||||
|
||||
if @generate_ri then
|
||||
FileUtils.rm_rf @ri_dir
|
||||
say "Installing ri documentation for #{@spec.full_name}"
|
||||
legacy_rdoc '--ri', '--op', @ri_dir
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Generates RDoc using a legacy version of RDoc from the ARGV-like +args+.
|
||||
# This method will not exist in future versions.
|
||||
|
||||
def legacy_rdoc *args
|
||||
args << @spec.rdoc_options
|
||||
args << '--quiet'
|
||||
args << @spec.require_paths.clone
|
||||
args << @spec.extra_rdoc_files
|
||||
args << '--title' << "#{@spec.full_name} Documentation"
|
||||
args = args.flatten.map do |arg| arg.to_s end
|
||||
|
||||
delete_legacy_args args if
|
||||
Gem::Requirement.new('>= 2.4.0') =~ self.class.rdoc_version
|
||||
|
||||
r = new_rdoc
|
||||
say "rdoc #{args.join ' '}" if Gem.configuration.really_verbose
|
||||
|
||||
Dir.chdir @spec.full_gem_path do
|
||||
begin
|
||||
r.document args
|
||||
rescue Errno::EACCES => e
|
||||
dirname = File.dirname e.message.split("-")[1].strip
|
||||
raise Gem::FilePermissionError, dirname
|
||||
rescue Interrupt => e
|
||||
raise e
|
||||
rescue Exception => ex
|
||||
alert_error "While generating documentation for #{@spec.full_name}"
|
||||
ui.errs.puts "... MESSAGE: #{ex}"
|
||||
ui.errs.puts "... RDOC args: #{args.join(' ')}"
|
||||
ui.backtrace ex
|
||||
ui.errs.puts "(continuing with the rest of the installation)"
|
||||
ensure
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# #new_rdoc creates a new RDoc instance. This method is provided only to
|
||||
# make testing easier.
|
||||
|
||||
def new_rdoc
|
||||
::RDoc::RDoc.new
|
||||
end
|
||||
|
||||
##
|
||||
# Is rdoc documentation installed?
|
||||
|
||||
def rdoc_installed?
|
||||
File.exist? @rdoc_dir
|
||||
end
|
||||
|
||||
##
|
||||
# Removes generated RDoc and ri data
|
||||
|
||||
def remove
|
||||
base_dir = @spec.base_dir
|
||||
|
||||
raise Gem::FilePermissionError, base_dir unless File.writable? base_dir
|
||||
|
||||
FileUtils.rm_rf @rdoc_dir
|
||||
FileUtils.rm_rf @ri_dir
|
||||
end
|
||||
|
||||
##
|
||||
# Is ri data installed?
|
||||
|
||||
def ri_installed?
|
||||
File.exist? @ri_dir
|
||||
end
|
||||
|
||||
##
|
||||
# Prepares the spec for documentation generation
|
||||
|
||||
def setup
|
||||
self.class.load_rdoc
|
||||
|
||||
raise Gem::FilePermissionError, @doc_dir if
|
||||
File.exist?(@doc_dir) and not File.writable?(@doc_dir)
|
||||
|
||||
FileUtils.mkdir_p @doc_dir unless File.exist? @doc_dir
|
||||
end
|
||||
|
||||
end unless loaded_hook
|
||||
|
||||
Gem.done_installing(&Gem::RDoc.method(:generation_hook))
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
require 'rubygems'
|
||||
require 'rubygems/user_interaction'
|
||||
require 'uri'
|
||||
require 'resolv'
|
||||
|
||||
##
|
||||
# RemoteFetcher handles the details of fetching gems and gem information from
|
||||
|
@ -8,8 +9,6 @@ require 'uri'
|
|||
|
||||
class Gem::RemoteFetcher
|
||||
|
||||
BuiltinSSLCerts = File.expand_path("./ssl_certs/*.pem", File.dirname(__FILE__))
|
||||
|
||||
include Gem::UserInteraction
|
||||
|
||||
##
|
||||
|
@ -34,6 +33,13 @@ class Gem::RemoteFetcher
|
|||
|
||||
end
|
||||
|
||||
##
|
||||
# A FetchError that indicates that the reason for not being
|
||||
# able to fetch data was that the host could not be contacted
|
||||
|
||||
class UnknownHostError < FetchError
|
||||
end
|
||||
|
||||
@fetcher = nil
|
||||
|
||||
##
|
||||
|
@ -53,8 +59,11 @@ class Gem::RemoteFetcher
|
|||
# * nil: respect environment variables (HTTP_PROXY, HTTP_PROXY_USER,
|
||||
# HTTP_PROXY_PASS)
|
||||
# * <tt>:no_proxy</tt>: ignore environment variables and _don't_ use a proxy
|
||||
#
|
||||
# +dns+: An object to use for DNS resolution of the API endpoint.
|
||||
# By default, use Resolv::DNS.
|
||||
|
||||
def initialize(proxy = nil)
|
||||
def initialize(proxy=nil, dns=Resolv::DNS.new)
|
||||
require 'net/http'
|
||||
require 'stringio'
|
||||
require 'time'
|
||||
|
@ -72,6 +81,27 @@ class Gem::RemoteFetcher
|
|||
else URI.parse(proxy)
|
||||
end
|
||||
@user_agent = user_agent
|
||||
@env_no_proxy = get_no_proxy_from_env
|
||||
|
||||
@dns = dns
|
||||
end
|
||||
|
||||
##
|
||||
#
|
||||
# Given a source at +uri+, calculate what hostname to actually
|
||||
# connect to query the data for it.
|
||||
|
||||
def api_endpoint(uri)
|
||||
host = uri.host
|
||||
|
||||
begin
|
||||
res = @dns.getresource "_rubygems._tcp.#{host}",
|
||||
Resolv::DNS::Resource::IN::SRV
|
||||
rescue Resolv::ResolvError
|
||||
uri
|
||||
else
|
||||
URI.parse "#{res.target}#{uri.path}"
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -82,14 +112,13 @@ class Gem::RemoteFetcher
|
|||
# larger, more emcompassing effort. -erikh
|
||||
|
||||
def download_to_cache dependency
|
||||
found = Gem::SpecFetcher.fetcher.fetch dependency, true, true,
|
||||
dependency.prerelease?
|
||||
found, _ = Gem::SpecFetcher.fetcher.spec_for_dependency dependency
|
||||
|
||||
return if found.empty?
|
||||
|
||||
spec, source_uri = found.sort_by { |(s,_)| s.version }.last
|
||||
spec, source = found.sort_by { |(s,_)| s.version }.last
|
||||
|
||||
download spec, source_uri
|
||||
download spec, source.uri.to_s
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -100,11 +129,14 @@ class Gem::RemoteFetcher
|
|||
def download(spec, source_uri, install_dir = Gem.dir)
|
||||
Gem.ensure_gem_subdirectories(install_dir) rescue nil
|
||||
|
||||
if File.writable?(install_dir)
|
||||
cache_dir = File.join install_dir, "cache"
|
||||
else
|
||||
cache_dir = File.join Gem.user_dir, "cache"
|
||||
end
|
||||
cache_dir =
|
||||
if Dir.pwd == install_dir then # see fetch_command
|
||||
install_dir
|
||||
elsif File.writable? install_dir then
|
||||
File.join install_dir, "cache"
|
||||
else
|
||||
File.join Gem.user_dir, "cache"
|
||||
end
|
||||
|
||||
gem_file_name = File.basename spec.cache_file
|
||||
local_gem_path = File.join cache_dir, gem_file_name
|
||||
|
@ -123,6 +155,8 @@ class Gem::RemoteFetcher
|
|||
# URI.parse gets confused by MS Windows paths with forward slashes.
|
||||
scheme = nil if scheme =~ /^[a-z]$/i
|
||||
|
||||
# REFACTOR: split this up and dispatch on scheme (eg download_http)
|
||||
# REFACTOR: be sure to clean up fake fetcher when you do this... cleaner
|
||||
case scheme
|
||||
when 'http', 'https' then
|
||||
unless File.exist? local_gem_path then
|
||||
|
@ -132,7 +166,7 @@ class Gem::RemoteFetcher
|
|||
|
||||
remote_gem_path = source_uri + "gems/#{gem_file_name}"
|
||||
|
||||
gem = self.fetch_path remote_gem_path
|
||||
self.cache_update_path remote_gem_path, local_gem_path
|
||||
rescue Gem::RemoteFetcher::FetchError
|
||||
raise if spec.original_platform == spec.platform
|
||||
|
||||
|
@ -143,11 +177,7 @@ class Gem::RemoteFetcher
|
|||
|
||||
remote_gem_path = source_uri + "gems/#{alternate_name}"
|
||||
|
||||
gem = self.fetch_path remote_gem_path
|
||||
end
|
||||
|
||||
File.open local_gem_path, 'wb' do |fp|
|
||||
fp.write gem
|
||||
self.cache_update_path remote_gem_path, local_gem_path
|
||||
end
|
||||
end
|
||||
when 'file' then
|
||||
|
@ -184,7 +214,7 @@ class Gem::RemoteFetcher
|
|||
say "Using local gem #{local_gem_path}" if
|
||||
Gem.configuration.really_verbose
|
||||
else
|
||||
raise Gem::InstallError, "unsupported URI scheme #{source_uri.scheme}"
|
||||
raise ArgumentError, "unsupported URI scheme #{source_uri.scheme}"
|
||||
end
|
||||
|
||||
local_gem_path
|
||||
|
@ -232,18 +262,54 @@ class Gem::RemoteFetcher
|
|||
uri = URI.parse uri unless URI::Generic === uri
|
||||
|
||||
raise ArgumentError, "bad uri: #{uri}" unless uri
|
||||
raise ArgumentError, "uri scheme is invalid: #{uri.scheme.inspect}" unless
|
||||
uri.scheme
|
||||
|
||||
unless uri.scheme
|
||||
raise ArgumentError, "uri scheme is invalid: #{uri.scheme.inspect}"
|
||||
end
|
||||
|
||||
data = send "fetch_#{uri.scheme}", uri, mtime, head
|
||||
data = Gem.gunzip data if data and not head and uri.to_s =~ /gz$/
|
||||
|
||||
if data and !head and uri.to_s =~ /gz$/
|
||||
begin
|
||||
data = Gem.gunzip data
|
||||
rescue Zlib::GzipFile::Error
|
||||
raise FetchError.new("server did not return a valid file", uri.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
data
|
||||
rescue FetchError
|
||||
raise
|
||||
rescue Timeout::Error
|
||||
raise FetchError.new('timed out', uri.to_s)
|
||||
raise UnknownHostError.new('timed out', uri.to_s)
|
||||
rescue IOError, SocketError, SystemCallError => e
|
||||
raise FetchError.new("#{e.class}: #{e}", uri.to_s)
|
||||
if e.message =~ /getaddrinfo/
|
||||
raise UnknownHostError.new('no such name', uri.to_s)
|
||||
else
|
||||
raise FetchError.new("#{e.class}: #{e}", uri.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Downloads +uri+ to +path+ if necessary. If no path is given, it just
|
||||
# passes the data.
|
||||
|
||||
def cache_update_path(uri, path = nil)
|
||||
mtime = path && File.stat(path).mtime rescue nil
|
||||
|
||||
if mtime && Net::HTTPNotModified === fetch_path(uri, mtime, true)
|
||||
Gem.read_binary(path)
|
||||
else
|
||||
data = fetch_path(uri)
|
||||
|
||||
if path
|
||||
open(path, 'wb') do |io|
|
||||
io.write data
|
||||
end
|
||||
end
|
||||
|
||||
data
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -273,6 +339,17 @@ class Gem::RemoteFetcher
|
|||
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.
|
||||
|
||||
|
@ -296,7 +373,7 @@ class Gem::RemoteFetcher
|
|||
# Normalize the URI by adding "http://" if it is missing.
|
||||
|
||||
def normalize_uri(uri)
|
||||
(uri =~ /^(https?|ftp|file):/) ? uri : "http://#{uri}"
|
||||
(uri =~ /^(https?|ftp|file):/i) ? uri : "http://#{uri}"
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -306,7 +383,7 @@ class Gem::RemoteFetcher
|
|||
def connection_for(uri)
|
||||
net_http_args = [uri.host, uri.port]
|
||||
|
||||
if @proxy_uri then
|
||||
if @proxy_uri and not no_proxy?(uri.host) then
|
||||
net_http_args += [
|
||||
@proxy_uri.host,
|
||||
@proxy_uri.port,
|
||||
|
@ -319,37 +396,23 @@ class Gem::RemoteFetcher
|
|||
@connections[connection_id] ||= Net::HTTP.new(*net_http_args)
|
||||
connection = @connections[connection_id]
|
||||
|
||||
if https?(uri) and !connection.started? then
|
||||
if https?(uri) and not connection.started? then
|
||||
configure_connection_for_https(connection)
|
||||
|
||||
# Don't refactor this with the else branch. We don't want the
|
||||
# http-only code path to not depend on anything in OpenSSL.
|
||||
#
|
||||
begin
|
||||
connection.start
|
||||
rescue OpenSSL::SSL::SSLError, Errno::EHOSTDOWN => e
|
||||
raise FetchError.new(e.message, uri)
|
||||
end
|
||||
else
|
||||
begin
|
||||
connection.start unless connection.started?
|
||||
rescue Errno::EHOSTDOWN => e
|
||||
raise FetchError.new(e.message, uri)
|
||||
end
|
||||
end
|
||||
|
||||
connection.start unless connection.started?
|
||||
|
||||
connection
|
||||
rescue OpenSSL::SSL::SSLError, 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
|
||||
|
@ -360,12 +423,12 @@ class Gem::RemoteFetcher
|
|||
store.set_default_paths
|
||||
add_rubygems_trusted_certs(store)
|
||||
end
|
||||
|
||||
connection.cert_store = store
|
||||
end
|
||||
|
||||
def add_rubygems_trusted_certs(store)
|
||||
Dir.glob(BuiltinSSLCerts).each do |ssl_cert_file|
|
||||
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
|
||||
|
@ -378,13 +441,13 @@ class Gem::RemoteFetcher
|
|||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Read the data from the (source based) URI, but if it is a file:// URI,
|
||||
# read from the filesystem instead.
|
||||
|
||||
def open_uri_or_path(uri, last_modified = nil, head = false, depth = 0)
|
||||
raise "NO: Use fetch_path instead"
|
||||
# TODO: deprecate for fetch_path
|
||||
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
|
||||
|
||||
##
|
||||
|
|
|
@ -0,0 +1,182 @@
|
|||
require 'rubygems'
|
||||
require 'rubygems/dependency'
|
||||
require 'rubygems/dependency_resolver'
|
||||
require 'rubygems/dependency_list'
|
||||
require 'rubygems/installer'
|
||||
require 'tsort'
|
||||
|
||||
module Gem
|
||||
class RequestSet
|
||||
|
||||
include TSort
|
||||
|
||||
def initialize(*deps)
|
||||
@dependencies = deps
|
||||
|
||||
yield self if block_given?
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
# Add +deps+ Gem::Depedency objects to the set.
|
||||
#
|
||||
def import(deps)
|
||||
@dependencies += deps
|
||||
end
|
||||
|
||||
# Resolve the requested dependencies and return an Array of
|
||||
# Specification objects to be activated.
|
||||
#
|
||||
def resolve(set=nil)
|
||||
r = Gem::DependencyResolver.new(@dependencies, set)
|
||||
@requests = r.resolve
|
||||
end
|
||||
|
||||
# Resolve the requested dependencies against the gems
|
||||
# available via Gem.path and return an Array of Specification
|
||||
# objects to be activated.
|
||||
#
|
||||
def resolve_current
|
||||
resolve DependencyResolver::CurrentSet.new
|
||||
end
|
||||
|
||||
# Load a dependency management file.
|
||||
#
|
||||
def load_gemdeps(path)
|
||||
gf = GemDepedencyAPI.new(self, path)
|
||||
gf.load
|
||||
end
|
||||
|
||||
def specs
|
||||
@specs ||= @requests.map { |r| r.full_spec }
|
||||
end
|
||||
|
||||
def tsort_each_node(&block)
|
||||
@requests.each(&block)
|
||||
end
|
||||
|
||||
def tsort_each_child(node)
|
||||
node.spec.dependencies.each do |dep|
|
||||
next if dep.type == :development
|
||||
|
||||
match = @requests.find { |r| dep.match? r.spec.name, r.spec.version }
|
||||
if match
|
||||
begin
|
||||
yield match
|
||||
rescue TSort::Cyclic
|
||||
end
|
||||
else
|
||||
raise Gem::DependencyError, "Unresolved depedency found during sorting - #{dep}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def sorted_requests
|
||||
@sorted ||= strongly_connected_components.flatten
|
||||
end
|
||||
|
||||
def specs_in(dir)
|
||||
Dir["#{dir}/specifications/*.gemspec"].map do |g|
|
||||
Gem::Specification.load g
|
||||
end
|
||||
end
|
||||
|
||||
def install_into(dir, force=true, &b)
|
||||
existing = force ? [] : specs_in(dir)
|
||||
|
||||
dir = File.expand_path dir
|
||||
|
||||
installed = []
|
||||
|
||||
sorted_requests.each do |req|
|
||||
if existing.find { |s| s.full_name == req.spec.full_name }
|
||||
b.call req, nil if b
|
||||
next
|
||||
end
|
||||
|
||||
path = req.download(dir)
|
||||
|
||||
inst = Gem::Installer.new path, :install_dir => dir,
|
||||
:only_install_dir => true
|
||||
|
||||
b.call req, inst if b
|
||||
|
||||
inst.install
|
||||
|
||||
installed << req
|
||||
end
|
||||
|
||||
installed
|
||||
end
|
||||
|
||||
def install(options, &b)
|
||||
if dir = options[:install_dir]
|
||||
return install_into(dir, false, &b)
|
||||
end
|
||||
|
||||
cache_dir = options[:cache_dir] || Gem.dir
|
||||
|
||||
specs = []
|
||||
|
||||
sorted_requests.each do |req|
|
||||
if req.installed?
|
||||
b.call req, nil if b
|
||||
next
|
||||
end
|
||||
|
||||
path = req.download cache_dir
|
||||
|
||||
inst = Gem::Installer.new path, options
|
||||
|
||||
b.call req, inst if b
|
||||
|
||||
specs << inst.install
|
||||
end
|
||||
|
||||
specs
|
||||
end
|
||||
|
||||
# A semi-compatible DSL for Bundler's Gemfile format
|
||||
#
|
||||
class GemDepedencyAPI
|
||||
def initialize(set, path)
|
||||
@set = set
|
||||
@path = path
|
||||
end
|
||||
|
||||
def load
|
||||
instance_eval File.read(@path).untaint, @path, 1
|
||||
end
|
||||
|
||||
# DSL
|
||||
|
||||
def source(url)
|
||||
end
|
||||
|
||||
def gem(name, *reqs)
|
||||
# Ignore the opts for now.
|
||||
reqs.pop if reqs.last.kind_of?(Hash)
|
||||
|
||||
@set.gem name, *reqs
|
||||
end
|
||||
|
||||
def platform(what)
|
||||
if what == :ruby
|
||||
yield
|
||||
end
|
||||
end
|
||||
|
||||
alias_method :platforms, :platform
|
||||
|
||||
def group(*what)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,18 +0,0 @@
|
|||
require 'rubygems'
|
||||
|
||||
# TODO: remove after 1.9.1 dropped
|
||||
module Gem::RequirePathsBuilder
|
||||
def write_require_paths_file_if_needed(spec = @spec, gem_home = @gem_home)
|
||||
return if spec.require_paths == ["lib"] &&
|
||||
(spec.bindir.nil? || spec.bindir == "bin")
|
||||
file_name = File.join(gem_home, 'gems', "#{@spec.full_name}", ".require_paths")
|
||||
file_name.untaint
|
||||
File.open(file_name, "w") do |file|
|
||||
spec.require_paths.each do |path|
|
||||
file.puts path
|
||||
end
|
||||
file.puts spec.bindir if spec.bindir
|
||||
end
|
||||
end
|
||||
end if Gem::QUICKLOADER_SUCKAGE
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
require "rubygems/version"
|
||||
|
||||
##
|
||||
# A Requirement is a set of one or more version restrictions. It supports a
|
||||
# few (<tt>=, !=, >, <, >=, <=, ~></tt>) different restriction operators.
|
||||
|
@ -13,14 +11,16 @@ require "rubygems/version"
|
|||
require "rubygems/version"
|
||||
require "rubygems/deprecate"
|
||||
|
||||
class Gem::Requirement
|
||||
include Comparable
|
||||
# If we're being loaded after yaml was already required, then
|
||||
# load our yaml + workarounds now.
|
||||
Gem.load_yaml if defined? ::YAML
|
||||
|
||||
class Gem::Requirement
|
||||
OPS = { #:nodoc:
|
||||
"=" => lambda { |v, r| v == r },
|
||||
"!=" => lambda { |v, r| v != r },
|
||||
">" => lambda { |v, r| v > r },
|
||||
"<" => lambda { |v, r| v < r },
|
||||
">" => lambda { |v, r| v > r },
|
||||
"<" => lambda { |v, r| v < r },
|
||||
">=" => lambda { |v, r| v >= r },
|
||||
"<=" => lambda { |v, r| v <= r },
|
||||
"~>" => lambda { |v, r| v >= r && v.release < r.bump }
|
||||
|
@ -29,6 +29,10 @@ class Gem::Requirement
|
|||
quoted = OPS.keys.map { |k| Regexp.quote k }.join "|"
|
||||
PATTERN = /\A\s*(#{quoted})?\s*(#{Gem::Version::VERSION_PATTERN})\s*\z/
|
||||
|
||||
DefaultRequirement = [">=", Gem::Version.new(0)]
|
||||
|
||||
class BadRequirementError < ArgumentError; end
|
||||
|
||||
##
|
||||
# Factory method to create a Gem::Requirement object. Input may be
|
||||
# a Version, a String, or nil. Intended to simplify client code.
|
||||
|
@ -36,6 +40,9 @@ class Gem::Requirement
|
|||
# If the input is "weird", the default version requirement is
|
||||
# returned.
|
||||
|
||||
# REFACTOR: There's no reason that this can't be unified with .new.
|
||||
# .new is the standard Ruby factory method.
|
||||
|
||||
def self.create input
|
||||
case input
|
||||
when Gem::Requirement then
|
||||
|
@ -53,10 +60,6 @@ class Gem::Requirement
|
|||
|
||||
##
|
||||
# A default "version requirement" can surely _only_ be '>= 0'.
|
||||
#--
|
||||
# This comment once said:
|
||||
#
|
||||
# "A default "version requirement" can surely _only_ be '> 0'."
|
||||
|
||||
def self.default
|
||||
new '>= 0'
|
||||
|
@ -74,14 +77,23 @@ class Gem::Requirement
|
|||
# parse("1.0") # => ["=", "1.0"]
|
||||
# parse(Gem::Version.new("1.0")) # => ["=, "1.0"]
|
||||
|
||||
# REFACTOR: Little two element arrays like this have no real semantic
|
||||
# value. I'd love to see something like this:
|
||||
# Constraint = Struct.new(:operator, :version); (or similar)
|
||||
# and have a Requirement be a list of Constraints.
|
||||
|
||||
def self.parse obj
|
||||
return ["=", obj] if Gem::Version === obj
|
||||
|
||||
unless PATTERN =~ obj.to_s
|
||||
raise ArgumentError, "Illformed requirement [#{obj.inspect}]"
|
||||
raise BadRequirementError, "Illformed requirement [#{obj.inspect}]"
|
||||
end
|
||||
|
||||
[$1 || "=", Gem::Version.new($2)]
|
||||
if $1 == ">=" && $2 == "0"
|
||||
DefaultRequirement
|
||||
else
|
||||
[$1 || "=", Gem::Version.new($2)]
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -101,13 +113,23 @@ class Gem::Requirement
|
|||
requirements.compact!
|
||||
requirements.uniq!
|
||||
|
||||
requirements << ">= 0" if requirements.empty?
|
||||
@none = (requirements == ">= 0")
|
||||
@requirements = requirements.map! { |r| self.class.parse r }
|
||||
if requirements.empty?
|
||||
@requirements = [DefaultRequirement]
|
||||
else
|
||||
@requirements = requirements.map! { |r| self.class.parse r }
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# true if this gem has no requirements.
|
||||
|
||||
# FIX: maybe this should be using #default ?
|
||||
def none?
|
||||
@none ||= (to_s == ">= 0")
|
||||
if @requirements.size == 1
|
||||
@requirements[0] == DefaultRequirement
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def as_list # :nodoc:
|
||||
|
@ -135,6 +157,7 @@ class Gem::Requirement
|
|||
instance_variable_set "@#{ivar}", val
|
||||
end
|
||||
|
||||
Gem.load_yaml
|
||||
fix_syck_default_key_in_requirements
|
||||
end
|
||||
|
||||
|
@ -142,6 +165,18 @@ class Gem::Requirement
|
|||
yaml_initialize coder.tag, coder.map
|
||||
end
|
||||
|
||||
def to_yaml_properties
|
||||
["@requirements"]
|
||||
end
|
||||
|
||||
def encode_with(coder)
|
||||
coder.add 'requirements', @requirements
|
||||
end
|
||||
|
||||
##
|
||||
# A requirement is a prerelease if any of the versions inside of it
|
||||
# are prereleases
|
||||
|
||||
def prerelease?
|
||||
requirements.any? { |r| r.last.prerelease? }
|
||||
end
|
||||
|
@ -156,6 +191,8 @@ class Gem::Requirement
|
|||
# True if +version+ satisfies this Requirement.
|
||||
|
||||
def satisfied_by? version
|
||||
raise ArgumentError, "Need a Gem::Version: #{version.inspect}" unless
|
||||
Gem::Version === version
|
||||
# #28965: syck has a bug with unquoted '=' YAML.loading as YAML::DefaultKey
|
||||
requirements.all? { |op, rv| (OPS[op] || OPS["="]).call version, rv }
|
||||
end
|
||||
|
@ -176,12 +213,14 @@ class Gem::Requirement
|
|||
as_list.join ", "
|
||||
end
|
||||
|
||||
def <=> other # :nodoc:
|
||||
to_s <=> other.to_s
|
||||
# DOC: this should probably be :nodoc'd
|
||||
def == other
|
||||
Gem::Requirement === other and to_s == other.to_s
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# DOC: this should probably be :nodoc'd
|
||||
def fix_syck_default_key_in_requirements
|
||||
Gem.load_yaml
|
||||
|
||||
|
@ -194,11 +233,9 @@ class Gem::Requirement
|
|||
end
|
||||
end
|
||||
|
||||
# :stopdoc:
|
||||
# Gem::Version::Requirement is used in a lot of old YAML specs. It's aliased
|
||||
# here for backwards compatibility. I'd like to remove this, maybe in RubyGems
|
||||
# 2.0.
|
||||
|
||||
::Gem::Version::Requirement = ::Gem::Requirement
|
||||
# :startdoc:
|
||||
# This is needed for compatibility with older yaml
|
||||
# gemspecs.
|
||||
|
||||
class Gem::Version
|
||||
Requirement = Gem::Requirement
|
||||
end
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,115 @@
|
|||
module Gem::Security
|
||||
|
||||
##
|
||||
# No security policy: all package signature checks are disabled.
|
||||
|
||||
NoSecurity = Policy.new(
|
||||
'No Security',
|
||||
:verify_data => false,
|
||||
:verify_signer => false,
|
||||
:verify_chain => false,
|
||||
:verify_root => false,
|
||||
:only_trusted => false,
|
||||
:only_signed => false
|
||||
)
|
||||
|
||||
##
|
||||
# AlmostNo security policy: only verify that the signing certificate is the
|
||||
# one that actually signed the data. Make no attempt to verify the signing
|
||||
# certificate chain.
|
||||
#
|
||||
# This policy is basically useless. better than nothing, but can still be
|
||||
# easily spoofed, and is not recommended.
|
||||
|
||||
AlmostNoSecurity = Policy.new(
|
||||
'Almost No Security',
|
||||
:verify_data => true,
|
||||
:verify_signer => false,
|
||||
:verify_chain => false,
|
||||
:verify_root => false,
|
||||
:only_trusted => false,
|
||||
:only_signed => false
|
||||
)
|
||||
|
||||
##
|
||||
# Low security policy: only verify that the signing certificate is actually
|
||||
# the gem signer, and that the signing certificate is valid.
|
||||
#
|
||||
# This policy is better than nothing, but can still be easily spoofed, and
|
||||
# is not recommended.
|
||||
|
||||
LowSecurity = Policy.new(
|
||||
'Low Security',
|
||||
:verify_data => true,
|
||||
:verify_signer => true,
|
||||
:verify_chain => false,
|
||||
:verify_root => false,
|
||||
:only_trusted => false,
|
||||
:only_signed => false
|
||||
)
|
||||
|
||||
##
|
||||
# Medium security policy: verify the signing certificate, verify the signing
|
||||
# certificate chain all the way to the root certificate, and only trust root
|
||||
# certificates that we have explicitly allowed trust for.
|
||||
#
|
||||
# This security policy is reasonable, but it allows unsigned packages, so a
|
||||
# malicious person could simply delete the package signature and pass the
|
||||
# gem off as unsigned.
|
||||
|
||||
MediumSecurity = Policy.new(
|
||||
'Medium Security',
|
||||
:verify_data => true,
|
||||
:verify_signer => true,
|
||||
:verify_chain => true,
|
||||
:verify_root => true,
|
||||
:only_trusted => true,
|
||||
:only_signed => false
|
||||
)
|
||||
|
||||
##
|
||||
# High security policy: only allow signed gems to be installed, verify the
|
||||
# signing certificate, verify the signing certificate chain all the way to
|
||||
# the root certificate, and only trust root certificates that we have
|
||||
# explicitly allowed trust for.
|
||||
#
|
||||
# This security policy is significantly more difficult to bypass, and offers
|
||||
# a reasonable guarantee that the contents of the gem have not been altered.
|
||||
|
||||
HighSecurity = Policy.new(
|
||||
'High Security',
|
||||
:verify_data => true,
|
||||
:verify_signer => true,
|
||||
:verify_chain => true,
|
||||
:verify_root => true,
|
||||
:only_trusted => true,
|
||||
:only_signed => true
|
||||
)
|
||||
|
||||
##
|
||||
# Policy used to verify a certificate and key when signing a gem
|
||||
|
||||
SigningPolicy = Policy.new(
|
||||
'Signing Policy',
|
||||
:verify_data => false,
|
||||
:verify_signer => true,
|
||||
:verify_chain => true,
|
||||
:verify_root => true,
|
||||
:only_trusted => false,
|
||||
:only_signed => false
|
||||
)
|
||||
|
||||
##
|
||||
# Hash of configured security policies
|
||||
|
||||
Policies = {
|
||||
'NoSecurity' => NoSecurity,
|
||||
'AlmostNoSecurity' => AlmostNoSecurity,
|
||||
'LowSecurity' => LowSecurity,
|
||||
'MediumSecurity' => MediumSecurity,
|
||||
'HighSecurity' => HighSecurity,
|
||||
# SigningPolicy is not intended for use by `gem -P` so do not list it
|
||||
}
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,227 @@
|
|||
##
|
||||
# A Gem::Security::Policy object encapsulates the settings for verifying
|
||||
# signed gem files. This is the base class. You can either declare an
|
||||
# instance of this or use one of the preset security policies in
|
||||
# Gem::Security::Policies.
|
||||
|
||||
class Gem::Security::Policy
|
||||
|
||||
attr_reader :name
|
||||
|
||||
attr_accessor :only_signed
|
||||
attr_accessor :only_trusted
|
||||
attr_accessor :verify_chain
|
||||
attr_accessor :verify_data
|
||||
attr_accessor :verify_root
|
||||
attr_accessor :verify_signer
|
||||
|
||||
##
|
||||
# Create a new Gem::Security::Policy object with the given mode and
|
||||
# options.
|
||||
|
||||
def initialize name, policy = {}, opt = {}
|
||||
@name = name
|
||||
|
||||
@opt = opt
|
||||
|
||||
# Default to security
|
||||
@only_signed = true
|
||||
@only_trusted = true
|
||||
@verify_chain = true
|
||||
@verify_data = true
|
||||
@verify_root = true
|
||||
@verify_signer = true
|
||||
|
||||
policy.each_pair do |key, val|
|
||||
case key
|
||||
when :verify_data then @verify_data = val
|
||||
when :verify_signer then @verify_signer = val
|
||||
when :verify_chain then @verify_chain = val
|
||||
when :verify_root then @verify_root = val
|
||||
when :only_trusted then @only_trusted = val
|
||||
when :only_signed then @only_signed = val
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Verifies each certificate in +chain+ has signed the following certificate
|
||||
# and is valid for the given +time+.
|
||||
|
||||
def check_chain chain, time
|
||||
chain.each_cons 2 do |issuer, cert|
|
||||
check_cert cert, issuer, time
|
||||
end
|
||||
|
||||
true
|
||||
rescue Gem::Security::Exception => e
|
||||
raise Gem::Security::Exception, "invalid signing chain: #{e.message}"
|
||||
end
|
||||
|
||||
##
|
||||
# Verifies that +data+ matches the +signature+ created by +public_key+ and
|
||||
# the +digest+ algorithm.
|
||||
|
||||
def check_data public_key, digest, signature, data
|
||||
raise Gem::Security::Exception, "invalid signature" unless
|
||||
public_key.verify digest.new, signature, data.digest
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
##
|
||||
# Ensures that +signer+ is valid for +time+ and was signed by the +issuer+.
|
||||
# If the +issuer+ is +nil+ no verification is performed.
|
||||
|
||||
def check_cert signer, issuer, time
|
||||
message = "certificate #{signer.subject}"
|
||||
|
||||
if not_before = signer.not_before and not_before > time then
|
||||
raise Gem::Security::Exception,
|
||||
"#{message} not valid before #{not_before}"
|
||||
end
|
||||
|
||||
if not_after = signer.not_after and not_after < time then
|
||||
raise Gem::Security::Exception, "#{message} not valid after #{not_after}"
|
||||
end
|
||||
|
||||
if issuer and not signer.verify issuer.public_key then
|
||||
raise Gem::Security::Exception,
|
||||
"#{message} was not issued by #{issuer.subject}"
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
##
|
||||
# Ensures the public key of +key+ matches the public key in +signer+
|
||||
|
||||
def check_key signer, key
|
||||
raise Gem::Security::Exception,
|
||||
"certificate #{signer.subject} does not match the signing key" unless
|
||||
signer.public_key.to_pem == key.public_key.to_pem
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
##
|
||||
# Ensures the root certificate in +chain+ is self-signed and valid for
|
||||
# +time+.
|
||||
|
||||
def check_root chain, time
|
||||
root = chain.first
|
||||
|
||||
raise Gem::Security::Exception,
|
||||
"root certificate #{root.subject} is not self-signed " \
|
||||
"(issuer #{root.issuer})" if
|
||||
root.issuer.to_s != root.subject.to_s # HACK to_s is for ruby 1.8
|
||||
|
||||
check_cert root, root, time
|
||||
end
|
||||
|
||||
##
|
||||
# Ensures the root of +chain+ has a trusted certificate in +trust_dir+ and
|
||||
# the digests of the two certificates match according to +digester+
|
||||
|
||||
def check_trust chain, digester, trust_dir
|
||||
root = chain.first
|
||||
|
||||
path = Gem::Security.trust_dir.cert_path root
|
||||
|
||||
unless File.exist? path then
|
||||
message = "root cert #{root.subject} is not trusted"
|
||||
|
||||
message << " (root of signing cert #{chain.last.subject})" if
|
||||
chain.length > 1
|
||||
|
||||
raise Gem::Security::Exception, message
|
||||
end
|
||||
|
||||
save_cert = OpenSSL::X509::Certificate.new File.read path
|
||||
save_dgst = digester.digest save_cert.public_key.to_s
|
||||
|
||||
pkey_str = root.public_key.to_s
|
||||
cert_dgst = digester.digest pkey_str
|
||||
|
||||
raise Gem::Security::Exception,
|
||||
"trusted root certificate #{root.subject} checksum " \
|
||||
"does not match signing root certificate checksum" unless
|
||||
save_dgst == cert_dgst
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def inspect # :nodoc:
|
||||
"[Policy: %s - data: %p signer: %p chain: %p root: %p " \
|
||||
"signed-only: %p trusted-only: %p]" % [
|
||||
@name, @verify_chain, @verify_data, @verify_root, @verify_signer,
|
||||
@only_signed, @only_trusted,
|
||||
]
|
||||
end
|
||||
|
||||
##
|
||||
# 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"
|
||||
end
|
||||
|
||||
opt = @opt
|
||||
digester = Gem::Security::DIGEST_ALGORITHM
|
||||
trust_dir = opt[:trust_dir]
|
||||
time = Time.now
|
||||
|
||||
signer_digests = digests.find do |algorithm, file_digests|
|
||||
file_digests.values.first.name == Gem::Security::DIGEST_NAME
|
||||
end
|
||||
|
||||
signer_digests = digests.values.first || {}
|
||||
|
||||
signer = chain.last
|
||||
|
||||
check_key signer, key if key
|
||||
|
||||
check_cert signer, nil, time if @verify_signer
|
||||
|
||||
check_chain chain, time if @verify_chain
|
||||
|
||||
check_root chain, time if @verify_root
|
||||
|
||||
check_trust chain, digester, trust_dir if @only_trusted
|
||||
|
||||
signer_digests.each do |file, digest|
|
||||
signature = signatures[file]
|
||||
|
||||
raise Gem::Security::Exception, "missing signature for #{file}" unless
|
||||
signature
|
||||
|
||||
check_data signer.public_key, digester, signature, digest if @verify_data
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
##
|
||||
# Extracts the certificate chain from the +spec+ and calls #verify to ensure
|
||||
# the signatures and certificate chain is valid according to the policy..
|
||||
|
||||
def verify_signatures spec, digests, signatures
|
||||
chain = spec.cert_chain.map do |cert_pem|
|
||||
OpenSSL::X509::Certificate.new cert_pem
|
||||
end
|
||||
|
||||
verify chain, nil, digests, signatures
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
alias to_s name # :nodoc:
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
##
|
||||
# Basic OpenSSL-based package signing class.
|
||||
|
||||
class Gem::Security::Signer
|
||||
|
||||
##
|
||||
# The chain of certificates for signing including the signing certificate
|
||||
|
||||
attr_accessor :cert_chain
|
||||
|
||||
##
|
||||
# The private key for the signing certificate
|
||||
|
||||
attr_accessor :key
|
||||
|
||||
##
|
||||
# The digest algorithm used to create the signature
|
||||
|
||||
attr_reader :digest_algorithm
|
||||
|
||||
##
|
||||
# The name of the digest algorithm, used to pull digests out of the hash by
|
||||
# name.
|
||||
|
||||
attr_reader :digest_name # :nodoc:
|
||||
|
||||
##
|
||||
# Creates a new signer with an RSA +key+ or path to a key, and a certificate
|
||||
# +chain+ containing X509 certificates, encoding certificates or paths to
|
||||
# certificates.
|
||||
|
||||
def initialize key, cert_chain
|
||||
@cert_chain = cert_chain
|
||||
@key = key
|
||||
|
||||
unless @key then
|
||||
default_key = File.join Gem.user_home, 'gem-private_key.pem'
|
||||
@key = default_key if File.exist? default_key
|
||||
end
|
||||
|
||||
unless @cert_chain then
|
||||
default_cert = File.join Gem.user_home, 'gem-public_cert.pem'
|
||||
@cert_chain = [default_cert] if File.exist? default_cert
|
||||
end
|
||||
|
||||
@digest_algorithm = Gem::Security::DIGEST_ALGORITHM
|
||||
@digest_name = Gem::Security::DIGEST_NAME
|
||||
|
||||
@key = OpenSSL::PKey::RSA.new File.read @key if
|
||||
@key and not OpenSSL::PKey::RSA === @key
|
||||
|
||||
if @cert_chain then
|
||||
@cert_chain = @cert_chain.compact.map do |cert|
|
||||
next cert if OpenSSL::X509::Certificate === cert
|
||||
|
||||
cert = File.read cert if File.exist? cert
|
||||
|
||||
OpenSSL::X509::Certificate.new cert
|
||||
end
|
||||
|
||||
load_cert_chain
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Loads any missing issuers in the cert chain from the trusted certificates.
|
||||
#
|
||||
# If the issuer does not exist it is ignored as it will be checked later.
|
||||
|
||||
def load_cert_chain # :nodoc:
|
||||
return if @cert_chain.empty?
|
||||
|
||||
while @cert_chain.first.issuer.to_s != @cert_chain.first.subject.to_s do
|
||||
issuer = Gem::Security.trust_dir.issuer_of @cert_chain.first
|
||||
|
||||
break unless issuer # cert chain is verified later
|
||||
|
||||
@cert_chain.unshift issuer
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Sign data with given digest algorithm
|
||||
|
||||
def sign data
|
||||
return unless @key
|
||||
|
||||
if @cert_chain.length == 1 and @cert_chain.last.not_after < Time.now then
|
||||
re_sign_key
|
||||
end
|
||||
|
||||
Gem::Security::SigningPolicy.verify @cert_chain, @key
|
||||
|
||||
@key.sign @digest_algorithm.new, data
|
||||
end
|
||||
|
||||
##
|
||||
# Attempts to re-sign the private key if the signing certificate is expired.
|
||||
#
|
||||
# The key will be re-signed if:
|
||||
# * The expired certificate is self-signed
|
||||
# * The expired certificate is saved at ~/.gem/gem-public_cert.pem
|
||||
# * There is no file matching the expiry date at
|
||||
# ~/.gem/gem-public_cert.pem.expired.%Y%m%d%H%M%S
|
||||
#
|
||||
# If the signing certificate can be re-signed the expired certificate will
|
||||
# be saved as ~/.gem/gem-pubilc_cert.pem.expired.%Y%m%d%H%M%S where the
|
||||
# expiry time (not after) is used for the timestamp.
|
||||
|
||||
def re_sign_key # :nodoc:
|
||||
old_cert = @cert_chain.last
|
||||
|
||||
disk_cert_path = File.join Gem.user_home, 'gem-public_cert.pem'
|
||||
disk_cert = File.read disk_cert_path rescue nil
|
||||
disk_key =
|
||||
File.read File.join(Gem.user_home, 'gem-private_key.pem') rescue nil
|
||||
|
||||
if disk_key == @key.to_pem and disk_cert == old_cert.to_pem then
|
||||
expiry = old_cert.not_after.strftime '%Y%m%d%H%M%S'
|
||||
old_cert_file = "gem-public_cert.pem.expired.#{expiry}"
|
||||
old_cert_path = File.join Gem.user_home, old_cert_file
|
||||
|
||||
unless File.exist? old_cert_path then
|
||||
Gem::Security.write old_cert, old_cert_path
|
||||
|
||||
cert = Gem::Security.re_sign old_cert, @key
|
||||
|
||||
Gem::Security.write cert, disk_cert_path
|
||||
|
||||
@cert_chain = [cert]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
class Gem::Security::TrustDir
|
||||
|
||||
DEFAULT_PERMISSIONS = {
|
||||
:trust_dir => 0700,
|
||||
:trusted_cert => 0600,
|
||||
}
|
||||
|
||||
def initialize dir, permissions = DEFAULT_PERMISSIONS
|
||||
@dir = dir
|
||||
@permissions = permissions
|
||||
|
||||
@digester = Gem::Security::DIGEST_ALGORITHM
|
||||
end
|
||||
|
||||
attr_reader :dir
|
||||
|
||||
##
|
||||
# Returns the path to the trusted +certificate+
|
||||
|
||||
def cert_path certificate
|
||||
name_path certificate.subject
|
||||
end
|
||||
|
||||
##
|
||||
# Enumerates trusted certificates.
|
||||
|
||||
def each_certificate
|
||||
return enum_for __method__ unless block_given?
|
||||
|
||||
glob = File.join @dir, '*.pem'
|
||||
|
||||
Dir[glob].each do |certificate_file|
|
||||
begin
|
||||
certificate = load_certificate certificate_file
|
||||
|
||||
yield certificate, certificate_file
|
||||
rescue OpenSSL::X509::CertificateError
|
||||
next # HACK warn
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Returns the issuer certificate of the given +certificate+ if it exists in
|
||||
# the trust directory.
|
||||
|
||||
def issuer_of certificate
|
||||
path = name_path certificate.issuer
|
||||
|
||||
return unless File.exist? path
|
||||
|
||||
load_certificate path
|
||||
end
|
||||
|
||||
##
|
||||
# Returns the path to the trusted certificate with the given ASN.1 +name+
|
||||
|
||||
def name_path name
|
||||
digest = @digester.hexdigest name.to_s
|
||||
|
||||
File.join @dir, "cert-#{digest}.pem"
|
||||
end
|
||||
|
||||
##
|
||||
# Loads the given +certificate_file+
|
||||
|
||||
def load_certificate certificate_file
|
||||
pem = File.read certificate_file
|
||||
|
||||
OpenSSL::X509::Certificate.new pem
|
||||
end
|
||||
|
||||
##
|
||||
# Add a certificate to trusted certificate list.
|
||||
|
||||
def trust_cert certificate
|
||||
verify
|
||||
|
||||
destination = cert_path certificate
|
||||
|
||||
open destination, 'wb', @permissions[:trusted_cert] do |io|
|
||||
io.write certificate.to_pem
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Make sure the trust directory exists. If it does exist, make sure it's
|
||||
# actually a directory. If not, then create it with the appropriate
|
||||
# permissions.
|
||||
|
||||
def verify
|
||||
if File.exist? @dir then
|
||||
raise Gem::Security::Exception,
|
||||
"trust directory #{@dir} is not a directory" unless
|
||||
File.directory? @dir
|
||||
|
||||
FileUtils.chmod 0700, @dir
|
||||
else
|
||||
FileUtils.mkdir_p @dir, :mode => @permissions[:trust_dir]
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -3,7 +3,7 @@ require 'zlib'
|
|||
require 'erb'
|
||||
|
||||
require 'rubygems'
|
||||
require 'rubygems/doc_manager'
|
||||
require 'rubygems/rdoc'
|
||||
|
||||
##
|
||||
# Gem::Server and allows users to serve gems for consumption by
|
||||
|
@ -17,9 +17,6 @@ require 'rubygems/doc_manager'
|
|||
# * "/quick/" - Individual gemspecs
|
||||
# * "/gems" - Direct access to download the installable gems
|
||||
# * "/rdoc?q=" - Search for installed rdoc documentation
|
||||
# * legacy indexes:
|
||||
# * "/Marshal.#{Gem.marshal_version}" - Full SourceIndex dump of metadata
|
||||
# for installed gems
|
||||
#
|
||||
# == Usage
|
||||
#
|
||||
|
@ -430,53 +427,25 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
|
|||
options[:launch], options[:addresses]).run
|
||||
end
|
||||
|
||||
##
|
||||
# Only the first directory in gem_dirs is used for serving gems
|
||||
|
||||
def initialize(gem_dirs, port, daemon, launch = nil, addresses = nil)
|
||||
Gem::RDoc.load_rdoc
|
||||
Socket.do_not_reverse_lookup = true
|
||||
|
||||
@gem_dirs = Array gem_dirs
|
||||
@port = port
|
||||
@daemon = daemon
|
||||
@launch = launch
|
||||
@gem_dirs = Array gem_dirs
|
||||
@port = port
|
||||
@daemon = daemon
|
||||
@launch = launch
|
||||
@addresses = addresses
|
||||
logger = WEBrick::Log.new nil, WEBrick::BasicLog::FATAL
|
||||
|
||||
logger = WEBrick::Log.new nil, WEBrick::BasicLog::FATAL
|
||||
@server = WEBrick::HTTPServer.new :DoNotListen => true, :Logger => logger
|
||||
|
||||
@spec_dirs = @gem_dirs.map do |gem_dir|
|
||||
spec_dir = File.join gem_dir, 'specifications'
|
||||
|
||||
unless File.directory? spec_dir then
|
||||
raise ArgumentError, "#{gem_dir} does not appear to be a gem repository"
|
||||
end
|
||||
|
||||
spec_dir
|
||||
end
|
||||
@spec_dirs = @gem_dirs.map { |gem_dir| File.join gem_dir, 'specifications' }
|
||||
@spec_dirs.reject! { |spec_dir| !File.directory? spec_dir }
|
||||
|
||||
Gem::Specification.dirs = @gem_dirs
|
||||
end
|
||||
|
||||
def Marshal(req, res)
|
||||
Gem::Specification.reset
|
||||
|
||||
add_date res
|
||||
|
||||
index = Gem::Deprecate.skip_during { Marshal.dump Gem.source_index }
|
||||
|
||||
if req.request_method == 'HEAD' then
|
||||
res['content-length'] = index.length
|
||||
return
|
||||
end
|
||||
|
||||
if req.path =~ /Z$/ then
|
||||
res['content-type'] = 'application/x-deflate'
|
||||
index = Gem.deflate index
|
||||
else
|
||||
res['content-type'] = 'application/octet-stream'
|
||||
end
|
||||
|
||||
res.body << index
|
||||
@have_rdoc_4_plus = nil
|
||||
end
|
||||
|
||||
def add_date res
|
||||
|
@ -485,6 +454,19 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
|
|||
end.max
|
||||
end
|
||||
|
||||
def doc_root gem_name
|
||||
if have_rdoc_4_plus? then
|
||||
"/doc_root/#{gem_name}/"
|
||||
else
|
||||
"/doc_root/#{gem_name}/rdoc/index.html"
|
||||
end
|
||||
end
|
||||
|
||||
def have_rdoc_4_plus?
|
||||
@have_rdoc_4_plus ||=
|
||||
Gem::Requirement.new('>= 4').satisfied_by? Gem::RDoc.rdoc_version
|
||||
end
|
||||
|
||||
def latest_specs(req, res)
|
||||
Gem::Specification.reset
|
||||
|
||||
|
@ -614,14 +596,14 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
|
|||
"authors" => spec.authors.sort.join(", "),
|
||||
"date" => spec.date.to_s,
|
||||
"dependencies" => deps,
|
||||
"doc_path" => "/doc_root/#{spec.full_name}/rdoc/index.html",
|
||||
"doc_path" => doc_root(spec.full_name),
|
||||
"executables" => executables,
|
||||
"only_one_executable" => (executables && executables.size == 1),
|
||||
"full_name" => spec.full_name,
|
||||
"has_deps" => !deps.empty?,
|
||||
"homepage" => spec.homepage,
|
||||
"name" => spec.name,
|
||||
"rdoc_installed" => Gem::DocManager.new(spec).rdoc_installed?,
|
||||
"rdoc_installed" => Gem::RDoc.new(spec).rdoc_installed?,
|
||||
"summary" => spec.summary,
|
||||
"version" => spec.version.to_s,
|
||||
}
|
||||
|
@ -630,7 +612,7 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
|
|||
specs << {
|
||||
"authors" => "Chad Fowler, Rich Kilmer, Jim Weirich, Eric Hodel and others",
|
||||
"dependencies" => [],
|
||||
"doc_path" => "/doc_root/rubygems-#{Gem::VERSION}/rdoc/index.html",
|
||||
"doc_path" => doc_root("rubygems-#{Gem::VERSION}"),
|
||||
"executables" => [{"executable" => 'gem', "is_last" => true}],
|
||||
"only_one_executable" => true,
|
||||
"full_name" => "rubygems-#{Gem::VERSION}",
|
||||
|
@ -730,15 +712,15 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
|
|||
when 1
|
||||
new_path = File.basename(found_gems[0])
|
||||
res.status = 302
|
||||
res['Location'] = "/doc_root/#{new_path}/rdoc/index.html"
|
||||
res['Location'] = doc_root new_path
|
||||
return true
|
||||
else
|
||||
doc_items = []
|
||||
found_gems.each do |file_name|
|
||||
base_name = File.basename(file_name)
|
||||
doc_items << {
|
||||
:name => base_name,
|
||||
:url => "/doc_root/#{base_name}/rdoc/index.html",
|
||||
:name => base_name,
|
||||
:url => doc_root(new_path),
|
||||
:summary => ''
|
||||
}
|
||||
end
|
||||
|
@ -756,9 +738,6 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
|
|||
|
||||
WEBrick::Daemon.start if @daemon
|
||||
|
||||
@server.mount_proc "/Marshal.#{Gem.marshal_version}", method(:Marshal)
|
||||
@server.mount_proc "/Marshal.#{Gem.marshal_version}.Z", method(:Marshal)
|
||||
|
||||
@server.mount_proc "/specs.#{Gem.marshal_version}", method(:specs)
|
||||
@server.mount_proc "/specs.#{Gem.marshal_version}.gz", method(:specs)
|
||||
|
||||
|
@ -779,10 +758,21 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
|
|||
|
||||
@server.mount_proc "/rdoc", method(:rdoc)
|
||||
|
||||
paths = { "/gems" => "/cache/", "/doc_root" => "/doc/" }
|
||||
paths.each do |mount_point, mount_dir|
|
||||
@server.mount(mount_point, WEBrick::HTTPServlet::FileHandler,
|
||||
File.join(@gem_dirs.first, mount_dir), true)
|
||||
file_handlers = {
|
||||
'/gems' => '/cache/',
|
||||
}
|
||||
|
||||
if have_rdoc_4_plus? then
|
||||
@server.mount '/doc_root', RDoc::Servlet, '/doc_root'
|
||||
else
|
||||
file_handlers['/doc_root'] = '/doc/'
|
||||
end
|
||||
|
||||
@gem_dirs.each do |gem_dir|
|
||||
file_handlers.each do |mount_point, mount_dir|
|
||||
@server.mount(mount_point, WEBrick::HTTPServlet::FileHandler,
|
||||
File.join(gem_dir, mount_dir), true)
|
||||
end
|
||||
end
|
||||
|
||||
trap("INT") { @server.shutdown; exit! }
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
require 'uri'
|
||||
require 'fileutils'
|
||||
|
||||
class Gem::Source
|
||||
FILES = {
|
||||
:released => 'specs',
|
||||
:latest => 'latest_specs',
|
||||
:prerelease => 'prerelease_specs',
|
||||
}
|
||||
|
||||
def initialize(uri)
|
||||
unless uri.kind_of? URI
|
||||
uri = URI.parse(uri.to_s)
|
||||
end
|
||||
|
||||
@uri = uri
|
||||
@api_uri = nil
|
||||
end
|
||||
|
||||
attr_reader :uri
|
||||
|
||||
def api_uri
|
||||
require 'rubygems/remote_fetcher'
|
||||
@api_uri ||= Gem::RemoteFetcher.fetcher.api_endpoint uri
|
||||
end
|
||||
|
||||
def <=>(other)
|
||||
if !@uri
|
||||
return 0 unless other.uri
|
||||
return -1
|
||||
end
|
||||
|
||||
return 1 if !other.uri
|
||||
|
||||
@uri.to_s <=> other.uri.to_s
|
||||
end
|
||||
|
||||
include Comparable
|
||||
|
||||
def ==(other)
|
||||
case other
|
||||
when self.class
|
||||
@uri == other.uri
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
alias_method :eql?, :==
|
||||
|
||||
def hash
|
||||
@uri.hash
|
||||
end
|
||||
|
||||
##
|
||||
# Returns the local directory to write +uri+ to.
|
||||
|
||||
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)
|
||||
end
|
||||
|
||||
def update_cache?
|
||||
@update_cache ||= File.stat(Gem.user_home).uid == Process.uid
|
||||
end
|
||||
|
||||
def fetch_spec(name)
|
||||
fetcher = Gem::RemoteFetcher.fetcher
|
||||
|
||||
spec_file_name = name.spec_name
|
||||
|
||||
uri = @uri + "#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}"
|
||||
|
||||
cache_dir = cache_dir uri
|
||||
|
||||
local_spec = File.join cache_dir, spec_file_name
|
||||
|
||||
if File.exist? local_spec then
|
||||
spec = Gem.read_binary local_spec
|
||||
spec = Marshal.load(spec) rescue nil
|
||||
return spec if spec
|
||||
end
|
||||
|
||||
uri.path << '.rz'
|
||||
|
||||
spec = fetcher.fetch_path uri
|
||||
spec = Gem.inflate spec
|
||||
|
||||
if update_cache? then
|
||||
FileUtils.mkdir_p cache_dir
|
||||
|
||||
open local_spec, 'wb' do |io|
|
||||
io.write spec
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: Investigate setting Gem::Specification#loaded_from to a URI
|
||||
Marshal.load spec
|
||||
end
|
||||
|
||||
##
|
||||
# Loads +type+ kind of specs fetching from +@uri+ if the on-disk cache is
|
||||
# out of date.
|
||||
#
|
||||
# +type+ is one of the following:
|
||||
#
|
||||
# :released => Return the list of all released specs
|
||||
# :latest => Return the list of only the highest version of each gem
|
||||
# :prerelease => Return the list of all prerelease only specs
|
||||
#
|
||||
|
||||
def load_specs(type)
|
||||
file = FILES[type]
|
||||
fetcher = Gem::RemoteFetcher.fetcher
|
||||
file_name = "#{file}.#{Gem.marshal_version}"
|
||||
spec_path = @uri + "#{file_name}.gz"
|
||||
cache_dir = cache_dir spec_path
|
||||
local_file = File.join(cache_dir, file_name)
|
||||
retried = false
|
||||
|
||||
FileUtils.mkdir_p cache_dir if update_cache?
|
||||
|
||||
spec_dump = fetcher.cache_update_path(spec_path, local_file)
|
||||
|
||||
begin
|
||||
Gem::NameTuple.from_list Marshal.load(spec_dump)
|
||||
rescue ArgumentError
|
||||
if update_cache? && !retried
|
||||
FileUtils.rm local_file
|
||||
retried = true
|
||||
retry
|
||||
else
|
||||
raise Gem::Exception.new("Invalid spec cache file in #{local_file}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def download(spec, dir=Dir.pwd)
|
||||
fetcher = Gem::RemoteFetcher.fetcher
|
||||
fetcher.download spec, @uri.to_s, dir
|
||||
end
|
||||
end
|
|
@ -1,406 +0,0 @@
|
|||
#--
|
||||
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
|
||||
# All rights reserved.
|
||||
# See LICENSE.txt for permissions.
|
||||
#++
|
||||
|
||||
require 'rubygems/specification'
|
||||
require 'rubygems/deprecate'
|
||||
|
||||
##
|
||||
# The SourceIndex object indexes all the gems available from a
|
||||
# particular source (e.g. a list of gem directories, or a remote
|
||||
# source). A SourceIndex maps a gem full name to a gem
|
||||
# specification.
|
||||
#
|
||||
# NOTE:: The class used to be named Cache, but that became
|
||||
# confusing when cached source fetchers where introduced. The
|
||||
# constant Gem::Cache is an alias for this class to allow old
|
||||
# YAMLized source index objects to load properly.
|
||||
|
||||
class Gem::SourceIndex
|
||||
|
||||
include Enumerable
|
||||
|
||||
attr_reader :gems # :nodoc:
|
||||
|
||||
##
|
||||
# Directories to use to refresh this SourceIndex when calling refresh!
|
||||
|
||||
attr_accessor :spec_dirs
|
||||
|
||||
##
|
||||
# Factory method to construct a source index instance for a given
|
||||
# path.
|
||||
#
|
||||
# deprecated::
|
||||
# If supplied, from_installed_gems will act just like
|
||||
# +from_gems_in+. This argument is deprecated and is provided
|
||||
# just for backwards compatibility, and should not generally
|
||||
# be used.
|
||||
#
|
||||
# return::
|
||||
# SourceIndex instance
|
||||
|
||||
def self.from_installed_gems(*deprecated)
|
||||
if deprecated.empty?
|
||||
from_gems_in(*installed_spec_directories)
|
||||
else
|
||||
warn "NOTE: from_installed_gems(arg) is deprecated. From #{caller.first}"
|
||||
from_gems_in(*deprecated) # HACK warn
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Returns a list of directories from Gem.path that contain specifications.
|
||||
|
||||
def self.installed_spec_directories
|
||||
# TODO: move to Gem::Utils
|
||||
Gem.path.collect { |dir| File.join(dir, "specifications") }
|
||||
end
|
||||
|
||||
##
|
||||
# Creates a new SourceIndex from the ruby format gem specifications in
|
||||
# +spec_dirs+.
|
||||
|
||||
def self.from_gems_in(*spec_dirs)
|
||||
new spec_dirs
|
||||
end
|
||||
|
||||
##
|
||||
# Loads a ruby-format specification from +file_name+ and returns the
|
||||
# loaded spec.
|
||||
|
||||
def self.load_specification(file_name)
|
||||
Gem::Deprecate.skip_during do
|
||||
Gem::Specification.load Gem::Path.new(file_name)
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Constructs a source index instance from the provided specifications, which
|
||||
# is a Hash of gem full names and Gem::Specifications.
|
||||
|
||||
def initialize specs_or_dirs = []
|
||||
@gems = {}
|
||||
@spec_dirs = nil
|
||||
|
||||
case specs_or_dirs
|
||||
when Hash then
|
||||
specs_or_dirs.each do |full_name, spec|
|
||||
add_spec spec
|
||||
end
|
||||
when Array, String then
|
||||
self.spec_dirs = Array(specs_or_dirs)
|
||||
refresh!
|
||||
else
|
||||
arg = specs_or_dirs.inspect
|
||||
warn "NOTE: SourceIndex.new(#{arg}) is deprecated; From #{caller.first}."
|
||||
end
|
||||
end
|
||||
|
||||
def all_gems
|
||||
gems
|
||||
end
|
||||
|
||||
def prerelease_gems
|
||||
@gems.reject { |name, gem| !gem.version.prerelease? }
|
||||
end
|
||||
|
||||
def released_gems
|
||||
@gems.reject { |name, gem| gem.version.prerelease? }
|
||||
end
|
||||
|
||||
##
|
||||
# Reconstruct the source index from the specifications in +spec_dirs+.
|
||||
|
||||
def load_gems_in(*spec_dirs)
|
||||
@gems.clear
|
||||
|
||||
spec_dirs.reverse_each do |spec_dir|
|
||||
spec_files = Dir[File.join(spec_dir, "*.gemspec")]
|
||||
|
||||
spec_files.each do |spec_file|
|
||||
gemspec = Gem::Deprecate.skip_during do
|
||||
Gem::Specification.load spec_file
|
||||
end
|
||||
add_spec gemspec if gemspec
|
||||
end
|
||||
end
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
##
|
||||
# Returns an Array specifications for the latest released versions
|
||||
# of each gem in this index.
|
||||
|
||||
def latest_specs(include_prerelease=false)
|
||||
result = Hash.new { |h,k| h[k] = [] }
|
||||
latest = {}
|
||||
|
||||
sort.each do |_, spec|
|
||||
name = spec.name
|
||||
curr_ver = spec.version
|
||||
prev_ver = latest.key?(name) ? latest[name].version : nil
|
||||
|
||||
next if !include_prerelease && curr_ver.prerelease?
|
||||
next unless prev_ver.nil? or curr_ver >= prev_ver or
|
||||
latest[name].platform != Gem::Platform::RUBY
|
||||
|
||||
if prev_ver.nil? or
|
||||
(curr_ver > prev_ver and spec.platform == Gem::Platform::RUBY) then
|
||||
result[name].clear
|
||||
latest[name] = spec
|
||||
end
|
||||
|
||||
if spec.platform != Gem::Platform::RUBY then
|
||||
result[name].delete_if do |result_spec|
|
||||
result_spec.platform == spec.platform
|
||||
end
|
||||
end
|
||||
|
||||
result[name] << spec
|
||||
end
|
||||
|
||||
result.values.flatten
|
||||
end
|
||||
|
||||
##
|
||||
# An array including only the prerelease gemspecs
|
||||
|
||||
def prerelease_specs
|
||||
prerelease_gems.values
|
||||
end
|
||||
|
||||
##
|
||||
# An array including only the released gemspecs
|
||||
|
||||
def released_specs
|
||||
released_gems.values
|
||||
end
|
||||
|
||||
##
|
||||
# Add a gem specification to the source index.
|
||||
|
||||
def add_spec(gem_spec, name = gem_spec.full_name)
|
||||
# No idea why, but the Indexer wants to insert them using original_name
|
||||
# instead of full_name. So we make it an optional arg.
|
||||
@gems[name] = gem_spec
|
||||
end
|
||||
|
||||
##
|
||||
# Add gem specifications to the source index.
|
||||
|
||||
def add_specs(*gem_specs)
|
||||
Gem::Deprecate.skip_during do
|
||||
gem_specs.each do |spec|
|
||||
add_spec spec
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Remove a gem specification named +full_name+.
|
||||
|
||||
def remove_spec(full_name)
|
||||
@gems.delete full_name
|
||||
end
|
||||
|
||||
##
|
||||
# Iterate over the specifications in the source index.
|
||||
|
||||
def each(&block) # :yields: gem.full_name, gem
|
||||
@gems.each(&block)
|
||||
end
|
||||
|
||||
##
|
||||
# The gem specification given a full gem spec name.
|
||||
|
||||
def specification(full_name)
|
||||
@gems[full_name]
|
||||
end
|
||||
|
||||
##
|
||||
# The signature for the source index. Changes in the signature indicate a
|
||||
# change in the index.
|
||||
|
||||
def index_signature
|
||||
require 'digest'
|
||||
|
||||
Digest::SHA256.new.hexdigest(@gems.keys.sort.join(',')).to_s
|
||||
end
|
||||
|
||||
##
|
||||
# The signature for the given gem specification.
|
||||
|
||||
def gem_signature(gem_full_name)
|
||||
require 'digest'
|
||||
|
||||
Digest::SHA256.new.hexdigest(@gems[gem_full_name].to_yaml).to_s
|
||||
end
|
||||
|
||||
def size
|
||||
@gems.size
|
||||
end
|
||||
alias length size
|
||||
|
||||
##
|
||||
# Find a gem by an exact match on the short name.
|
||||
|
||||
def find_name(gem_name, requirement = Gem::Requirement.default)
|
||||
dep = Gem::Dependency.new gem_name, requirement
|
||||
|
||||
Gem::Deprecate.skip_during do
|
||||
search dep
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Search for a gem by Gem::Dependency +gem_pattern+. If +only_platform+
|
||||
# is true, only gems matching Gem::Platform.local will be returned. An
|
||||
# Array of matching Gem::Specification objects is returned.
|
||||
#
|
||||
# For backwards compatibility, a String or Regexp pattern may be passed as
|
||||
# +gem_pattern+, and a Gem::Requirement for +platform_only+. This
|
||||
# behavior is deprecated and will be removed.
|
||||
|
||||
def search(gem_pattern, platform_or_requirement = false)
|
||||
requirement = nil
|
||||
only_platform = false # FIX: WTF is this?!?
|
||||
|
||||
# TODO - Remove support and warning for legacy arguments after 2008/11
|
||||
unless Gem::Dependency === gem_pattern
|
||||
warn "#{Gem.location_of_caller.join ':'}:Warning: Gem::SourceIndex#search support for #{gem_pattern.class} patterns is deprecated, use #find_name"
|
||||
end
|
||||
|
||||
case gem_pattern
|
||||
when Regexp then
|
||||
requirement = platform_or_requirement || Gem::Requirement.default
|
||||
when Gem::Dependency then
|
||||
only_platform = platform_or_requirement
|
||||
requirement = gem_pattern.requirement
|
||||
|
||||
gem_pattern = if Regexp === gem_pattern.name then
|
||||
gem_pattern.name
|
||||
elsif gem_pattern.name.empty? then
|
||||
//
|
||||
else
|
||||
/^#{Regexp.escape gem_pattern.name}$/
|
||||
end
|
||||
else
|
||||
requirement = platform_or_requirement || Gem::Requirement.default
|
||||
gem_pattern = /#{gem_pattern}/i
|
||||
end
|
||||
|
||||
unless Gem::Requirement === requirement then
|
||||
requirement = Gem::Requirement.create requirement
|
||||
end
|
||||
|
||||
specs = @gems.values.select do |spec|
|
||||
spec.name =~ gem_pattern and
|
||||
requirement.satisfied_by? spec.version
|
||||
end
|
||||
|
||||
if only_platform then
|
||||
specs = specs.select do |spec|
|
||||
Gem::Platform.match spec.platform
|
||||
end
|
||||
end
|
||||
|
||||
specs.sort_by { |s| s.sort_obj }
|
||||
end
|
||||
|
||||
##
|
||||
# Replaces the gems in the source index from specifications in the
|
||||
# directories this source index was created from. Raises an exception if
|
||||
# this source index wasn't created from a directory (via from_gems_in or
|
||||
# from_installed_gems, or having spec_dirs set).
|
||||
|
||||
def refresh!
|
||||
raise 'source index not created from disk' if @spec_dirs.nil?
|
||||
load_gems_in(*@spec_dirs)
|
||||
end
|
||||
|
||||
##
|
||||
# Returns an Array of Gem::Specifications that are not up to date.
|
||||
|
||||
def outdated
|
||||
outdateds = []
|
||||
|
||||
latest_specs.each do |local|
|
||||
dependency = Gem::Dependency.new local.name, ">= #{local.version}"
|
||||
|
||||
fetcher = Gem::SpecFetcher.fetcher
|
||||
remotes = fetcher.find_matching dependency
|
||||
remotes = remotes.map { |(_, version, _), _| version }
|
||||
|
||||
latest = remotes.sort.last
|
||||
|
||||
outdateds << local.name if latest and local.version < latest
|
||||
end
|
||||
|
||||
outdateds
|
||||
end
|
||||
|
||||
def ==(other) # :nodoc:
|
||||
self.class === other and @gems == other.gems
|
||||
end
|
||||
|
||||
def dump
|
||||
Marshal.dump(self)
|
||||
end
|
||||
end
|
||||
|
||||
# :stopdoc:
|
||||
module Gem
|
||||
|
||||
##
|
||||
# Cache is an alias for SourceIndex to allow older YAMLized source index
|
||||
# objects to load properly.
|
||||
|
||||
Cache = SourceIndex
|
||||
|
||||
end
|
||||
|
||||
class Gem::SourceIndex
|
||||
extend Gem::Deprecate
|
||||
|
||||
deprecate :all_gems, :none, 2011, 10
|
||||
|
||||
deprecate :==, :none, 2011, 11 # noisy
|
||||
deprecate :add_specs, :none, 2011, 11 # noisy
|
||||
deprecate :each, :none, 2011, 11
|
||||
deprecate :gems, :none, 2011, 11
|
||||
deprecate :load_gems_in, :none, 2011, 11
|
||||
deprecate :refresh!, :none, 2011, 11
|
||||
deprecate :spec_dirs=, "Specification.dirs=", 2011, 11 # noisy
|
||||
deprecate :add_spec, "Specification.add_spec", 2011, 11
|
||||
deprecate :find_name, "Specification.find_by_name", 2011, 11
|
||||
deprecate :gem_signature, :none, 2011, 11
|
||||
deprecate :index_signature, :none, 2011, 11
|
||||
deprecate :initialize, :none, 2011, 11
|
||||
deprecate :latest_specs, "Specification.latest_specs", 2011, 11
|
||||
deprecate :length, "Specification.all.length", 2011, 11
|
||||
deprecate :outdated, :none, 2011, 11
|
||||
deprecate :prerelease_gems, :none, 2011, 11
|
||||
deprecate :prerelease_specs, :none, 2011, 11
|
||||
deprecate :released_gems, :none, 2011, 11
|
||||
deprecate :released_specs, :none, 2011, 11
|
||||
deprecate :remove_spec, "Specification.remove_spec", 2011, 11
|
||||
deprecate :search, :none, 2011, 11
|
||||
deprecate :size, "Specification.all.size", 2011, 11
|
||||
deprecate :spec_dirs, "Specification.dirs", 2011, 11
|
||||
deprecate :specification, "Specification.find", 2011, 11
|
||||
|
||||
class << self
|
||||
extend Gem::Deprecate
|
||||
|
||||
deprecate :from_gems_in, :none, 2011, 10
|
||||
deprecate :from_installed_gems, :none, 2011, 10
|
||||
deprecate :installed_spec_directories, "Specification.dirs", 2011, 11
|
||||
deprecate :load_specification, :none, 2011, 10
|
||||
end
|
||||
end
|
||||
|
||||
# :startdoc:
|
|
@ -0,0 +1,87 @@
|
|||
require 'rubygems/source'
|
||||
|
||||
class Gem::SourceList
|
||||
def initialize
|
||||
@sources = []
|
||||
end
|
||||
|
||||
attr_reader :sources
|
||||
|
||||
def self.from(ary)
|
||||
list = new
|
||||
|
||||
if ary
|
||||
ary.each do |x|
|
||||
list << x
|
||||
end
|
||||
end
|
||||
|
||||
return list
|
||||
end
|
||||
|
||||
def initialize_copy(other)
|
||||
@sources = @sources.dup
|
||||
end
|
||||
|
||||
def <<(obj)
|
||||
src = case obj
|
||||
when URI
|
||||
Gem::Source.new(obj)
|
||||
when Gem::Source
|
||||
obj
|
||||
else
|
||||
Gem::Source.new(URI.parse(obj))
|
||||
end
|
||||
|
||||
@sources << src
|
||||
src
|
||||
end
|
||||
|
||||
def replace(other)
|
||||
@sources.clear
|
||||
|
||||
other.each do |x|
|
||||
self << x
|
||||
end
|
||||
|
||||
self
|
||||
end
|
||||
|
||||
def each
|
||||
@sources.each { |s| yield s.uri.to_s }
|
||||
end
|
||||
|
||||
def each_source(&b)
|
||||
@sources.each(&b)
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
to_a == other
|
||||
end
|
||||
|
||||
def to_a
|
||||
@sources.map { |x| x.uri.to_s }
|
||||
end
|
||||
|
||||
alias_method :to_ary, :to_a
|
||||
|
||||
def first
|
||||
@sources.first
|
||||
end
|
||||
|
||||
def include?(other)
|
||||
if other.kind_of? Gem::Source
|
||||
@sources.include? other
|
||||
else
|
||||
@sources.find { |x| x.uri.to_s == other.to_s }
|
||||
end
|
||||
end
|
||||
|
||||
def delete(uri)
|
||||
if uri.kind_of? Gem::Source
|
||||
@sources.delete uri
|
||||
else
|
||||
@sources.delete_if { |x| x.uri.to_s == uri.to_s }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,92 @@
|
|||
require 'rubygems/source'
|
||||
|
||||
class Gem::Source::Local < Gem::Source
|
||||
def initialize
|
||||
@uri = nil
|
||||
end
|
||||
|
||||
def load_specs(type)
|
||||
names = []
|
||||
|
||||
@specs = {}
|
||||
|
||||
Dir["*.gem"].each do |file|
|
||||
begin
|
||||
pkg = Gem::Package.new(file)
|
||||
rescue SystemCallError, Gem::Package::FormatError
|
||||
# ignore
|
||||
else
|
||||
tup = pkg.spec.name_tuple
|
||||
@specs[tup] = [File.expand_path(file), pkg]
|
||||
|
||||
case type
|
||||
when :released
|
||||
unless pkg.spec.version.prerelease?
|
||||
names << pkg.spec.name_tuple
|
||||
end
|
||||
when :prerelease
|
||||
if pkg.spec.version.prerelease?
|
||||
names << pkg.spec.name_tuple
|
||||
end
|
||||
when :latest
|
||||
tup = pkg.spec.name_tuple
|
||||
|
||||
cur = names.find { |x| x.name == tup.name }
|
||||
if !cur
|
||||
names << tup
|
||||
elsif cur.version < tup.version
|
||||
names.delete cur
|
||||
names << tup
|
||||
end
|
||||
else
|
||||
names << pkg.spec.name_tuple
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
names
|
||||
end
|
||||
|
||||
def find_gem(gem_name, version=Gem::Requirement.default,
|
||||
prerelease=false)
|
||||
load_specs :complete
|
||||
|
||||
found = []
|
||||
|
||||
@specs.each do |n, data|
|
||||
if n.name == gem_name
|
||||
s = data[1].spec
|
||||
|
||||
if version.satisfied_by?(s.version)
|
||||
if prerelease
|
||||
found << s
|
||||
elsif !s.version.prerelease?
|
||||
found << s
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
found.sort_by { |s| s.version }.last
|
||||
end
|
||||
|
||||
def fetch_spec(name)
|
||||
load_specs :complete
|
||||
|
||||
if data = @specs[name]
|
||||
data.last.spec
|
||||
else
|
||||
raise Gem::Exception, "Unable to find spec for '#{name}'"
|
||||
end
|
||||
end
|
||||
|
||||
def download(spec, cache_dir=nil)
|
||||
load_specs :complete
|
||||
|
||||
@specs.each do |name, data|
|
||||
return data[0] if data[1].spec == spec
|
||||
end
|
||||
|
||||
raise Gem::Exception, "Unable to find file for '#{spec.full_name}'"
|
||||
end
|
||||
end
|
|
@ -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
|
|
@ -2,6 +2,7 @@ require 'rubygems/remote_fetcher'
|
|||
require 'rubygems/user_interaction'
|
||||
require 'rubygems/errors'
|
||||
require 'rubygems/text'
|
||||
require 'rubygems/name_tuple'
|
||||
|
||||
##
|
||||
# SpecFetcher handles metadata updates from remote gem repositories.
|
||||
|
@ -11,17 +12,6 @@ class Gem::SpecFetcher
|
|||
include Gem::UserInteraction
|
||||
include Gem::Text
|
||||
|
||||
FILES = {
|
||||
:all => 'specs',
|
||||
:latest => 'latest_specs',
|
||||
:prerelease => 'prerelease_specs',
|
||||
}
|
||||
|
||||
##
|
||||
# The SpecFetcher cache dir.
|
||||
|
||||
attr_reader :dir # :nodoc:
|
||||
|
||||
##
|
||||
# Cache of latest specs
|
||||
|
||||
|
@ -48,8 +38,6 @@ class Gem::SpecFetcher
|
|||
end
|
||||
|
||||
def initialize
|
||||
require 'fileutils'
|
||||
|
||||
@dir = File.join Gem.user_home, '.gem', 'specs'
|
||||
@update_cache = File.stat(Gem.user_home).uid == Process.uid
|
||||
|
||||
|
@ -60,99 +48,40 @@ class Gem::SpecFetcher
|
|||
@caches = {
|
||||
:latest => @latest_specs,
|
||||
:prerelease => @prerelease_specs,
|
||||
:all => @specs
|
||||
:released => @specs,
|
||||
}
|
||||
|
||||
@fetcher = Gem::RemoteFetcher.fetcher
|
||||
end
|
||||
|
||||
##
|
||||
# Returns the local directory to write +uri+ to.
|
||||
#
|
||||
# Find and fetch gem name tuples that match +dependency+.
|
||||
#
|
||||
# If +matching_platform+ is false, gems for all platforms are returned.
|
||||
|
||||
def cache_dir(uri)
|
||||
# Correct for windows paths
|
||||
escaped_path = uri.path.sub(/^\/([a-z]):\//i, '/\\1-/')
|
||||
File.join @dir, "#{uri.host}%#{uri.port}", File.dirname(escaped_path)
|
||||
end
|
||||
|
||||
##
|
||||
# Fetch specs matching +dependency+. If +all+ is true, all matching
|
||||
# (released) versions are returned. If +matching_platform+ is
|
||||
# false, all platforms are returned. If +prerelease+ is true,
|
||||
# prerelease versions are included.
|
||||
|
||||
def fetch_with_errors(dependency,
|
||||
all = false,
|
||||
matching_platform = true,
|
||||
prerelease = false)
|
||||
|
||||
specs_and_sources, errors = find_matching_with_errors(dependency,
|
||||
all,
|
||||
matching_platform,
|
||||
prerelease)
|
||||
|
||||
ss = specs_and_sources.map do |spec_tuple, source_uri|
|
||||
[fetch_spec(spec_tuple, URI.parse(source_uri)), source_uri]
|
||||
end
|
||||
|
||||
return [ss, errors]
|
||||
end
|
||||
|
||||
def fetch(*args)
|
||||
fetch_with_errors(*args).first
|
||||
end
|
||||
|
||||
def fetch_spec(spec, source_uri)
|
||||
source_uri = URI.parse source_uri if String === source_uri
|
||||
spec = spec - [nil, 'ruby', '']
|
||||
spec_file_name = "#{spec.join '-'}.gemspec"
|
||||
|
||||
uri = source_uri + "#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}"
|
||||
|
||||
cache_dir = cache_dir uri
|
||||
|
||||
local_spec = File.join cache_dir, spec_file_name
|
||||
|
||||
if File.exist? local_spec then
|
||||
spec = Gem.read_binary local_spec
|
||||
else
|
||||
uri.path << '.rz'
|
||||
|
||||
spec = @fetcher.fetch_path uri
|
||||
spec = Gem.inflate spec
|
||||
|
||||
if @update_cache then
|
||||
FileUtils.mkdir_p cache_dir
|
||||
|
||||
open local_spec, 'wb' do |io|
|
||||
io.write spec
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: Investigate setting Gem::Specification#loaded_from to a URI
|
||||
Marshal.load spec
|
||||
end
|
||||
|
||||
##
|
||||
# Find spec names that match +dependency+. If +all+ is true, all
|
||||
# matching released versions are returned. If +matching_platform+
|
||||
# is false, gems for all platforms are returned.
|
||||
|
||||
def find_matching_with_errors(dependency,
|
||||
all = false,
|
||||
matching_platform = true,
|
||||
prerelease = false)
|
||||
def search_for_dependency(dependency, matching_platform=true)
|
||||
found = {}
|
||||
|
||||
rejected_specs = {}
|
||||
|
||||
list(all, prerelease).each do |source_uri, specs|
|
||||
found[source_uri] = specs.select do |spec_name, version, spec_platform|
|
||||
if dependency.match?(spec_name, version)
|
||||
if matching_platform and !Gem::Platform.match(spec_platform)
|
||||
pm = (rejected_specs[dependency] ||= Gem::PlatformMismatch.new(spec_name, version))
|
||||
pm.add_platform spec_platform
|
||||
if dependency.prerelease?
|
||||
type = :complete
|
||||
elsif dependency.latest_version?
|
||||
type = :latest
|
||||
else
|
||||
type = :released
|
||||
end
|
||||
|
||||
list, errors = available_specs(type)
|
||||
list.each do |source, specs|
|
||||
found[source] = specs.select do |tup|
|
||||
if dependency.match?(tup)
|
||||
if matching_platform and !Gem::Platform.match(tup.platform)
|
||||
pm = (
|
||||
rejected_specs[dependency] ||= \
|
||||
Gem::PlatformMismatch.new(tup.name, tup.version))
|
||||
pm.add_platform tup.platform
|
||||
false
|
||||
else
|
||||
true
|
||||
|
@ -161,43 +90,82 @@ class Gem::SpecFetcher
|
|||
end
|
||||
end
|
||||
|
||||
errors = rejected_specs.values
|
||||
errors += rejected_specs.values
|
||||
|
||||
specs_and_sources = []
|
||||
tuples = []
|
||||
|
||||
found.each do |source_uri, specs|
|
||||
uri_str = source_uri.to_s
|
||||
specs_and_sources.concat(specs.map { |spec| [spec, uri_str] })
|
||||
found.each do |source, specs|
|
||||
specs.each do |s|
|
||||
tuples << [s, source]
|
||||
end
|
||||
end
|
||||
|
||||
[specs_and_sources, errors]
|
||||
tuples = tuples.sort_by { |x| x[0] }
|
||||
|
||||
return [tuples, errors]
|
||||
end
|
||||
|
||||
def find_matching(*args)
|
||||
find_matching_with_errors(*args).first
|
||||
|
||||
##
|
||||
# Return all gem name tuples who's names match +obj+
|
||||
|
||||
def detect(type=:complete)
|
||||
tuples = []
|
||||
|
||||
list, _ = available_specs(type)
|
||||
list.each do |source, specs|
|
||||
specs.each do |tup|
|
||||
if yield(tup)
|
||||
tuples << [tup, source]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
tuples
|
||||
end
|
||||
|
||||
|
||||
##
|
||||
# Find and fetch specs that match +dependency+.
|
||||
#
|
||||
# If +matching_platform+ is false, gems for all platforms are returned.
|
||||
|
||||
def spec_for_dependency(dependency, matching_platform=true)
|
||||
tuples, errors = search_for_dependency(dependency, matching_platform)
|
||||
|
||||
specs = []
|
||||
tuples.each do |tup, source|
|
||||
begin
|
||||
spec = source.fetch_spec(tup)
|
||||
rescue Gem::RemoteFetcher::FetchError => e
|
||||
errors << Gem::SourceFetchProblem.new(source, e)
|
||||
else
|
||||
specs << [spec, source]
|
||||
end
|
||||
end
|
||||
|
||||
return [specs, errors]
|
||||
end
|
||||
|
||||
##
|
||||
# Suggests a gem based on the supplied +gem_name+. Returns a string
|
||||
# of the gem name if an approximate match can be found or nil
|
||||
# otherwise. NOTE: for performance reasons only gems which exactly
|
||||
# match the first character of +gem_name+ are considered.
|
||||
# Suggests gems based on the supplied +gem_name+. Returns an array of
|
||||
# alternative gem names.
|
||||
|
||||
def suggest_gems_from_name gem_name
|
||||
gem_name = gem_name.downcase
|
||||
gem_name = gem_name.downcase.tr('_-', '')
|
||||
max = gem_name.size / 2
|
||||
specs = list.values.flatten 1
|
||||
names = available_specs(:complete).first.values.flatten(1)
|
||||
|
||||
matches = specs.map { |name, version, platform|
|
||||
next unless Gem::Platform.match platform
|
||||
matches = names.map { |n|
|
||||
next unless n.match_platform?
|
||||
|
||||
distance = levenshtein_distance gem_name, name.downcase
|
||||
distance = levenshtein_distance gem_name, n.name.downcase.tr('_-', '')
|
||||
|
||||
next if distance >= max
|
||||
|
||||
return [name] if distance == 0
|
||||
return [n.name] if distance == 0
|
||||
|
||||
[name, distance]
|
||||
[n.name, distance]
|
||||
}.compact
|
||||
|
||||
matches = matches.uniq.sort_by { |name, dist| dist }
|
||||
|
@ -206,92 +174,46 @@ class Gem::SpecFetcher
|
|||
end
|
||||
|
||||
##
|
||||
# Returns a list of gems available for each source in Gem::sources. If
|
||||
# +all+ is true, all released versions are returned instead of only latest
|
||||
# versions. If +prerelease+ is true, include prerelease versions.
|
||||
# Returns a list of gems available for each source in Gem::sources.
|
||||
#
|
||||
# +type+ can be one of 3 values:
|
||||
# :released => Return the list of all released specs
|
||||
# :complete => Return the list of all specs
|
||||
# :latest => Return the list of only the highest version of each gem
|
||||
# :prerelease => Return the list of all prerelease only specs
|
||||
#
|
||||
|
||||
def list(all = false, prerelease = false)
|
||||
# TODO: make type the only argument
|
||||
type = if all
|
||||
:all
|
||||
elsif prerelease
|
||||
:prerelease
|
||||
else
|
||||
:latest
|
||||
end
|
||||
def available_specs(type)
|
||||
errors = []
|
||||
list = {}
|
||||
|
||||
list = {}
|
||||
file = FILES[type]
|
||||
cache = @caches[type]
|
||||
|
||||
Gem.sources.each do |source_uri|
|
||||
source_uri = URI.parse source_uri
|
||||
|
||||
unless cache.include? source_uri
|
||||
cache[source_uri] = load_specs source_uri, file
|
||||
end
|
||||
|
||||
list[source_uri] = cache[source_uri]
|
||||
end
|
||||
|
||||
if type == :all
|
||||
list.values.map do |gems|
|
||||
gems.reject! { |g| !g[1] || g[1].prerelease? }
|
||||
end
|
||||
end
|
||||
|
||||
list
|
||||
end
|
||||
|
||||
##
|
||||
# Loads specs in +file+, fetching from +source_uri+ if the on-disk cache is
|
||||
# out of date.
|
||||
|
||||
def load_specs(source_uri, file)
|
||||
file_name = "#{file}.#{Gem.marshal_version}"
|
||||
spec_path = source_uri + "#{file_name}.gz"
|
||||
cache_dir = cache_dir spec_path
|
||||
local_file = File.join(cache_dir, file_name)
|
||||
loaded = false
|
||||
|
||||
if File.exist? local_file then
|
||||
Gem.sources.each_source do |source|
|
||||
begin
|
||||
spec_dump =
|
||||
@fetcher.fetch_path(spec_path, File.mtime(local_file))
|
||||
names = case type
|
||||
when :latest
|
||||
tuples_for source, :latest
|
||||
when :released
|
||||
tuples_for source, :released
|
||||
when :complete
|
||||
tuples_for(source, :prerelease) + tuples_for(source, :released)
|
||||
when :prerelease
|
||||
tuples_for(source, :prerelease)
|
||||
else
|
||||
raise Gem::Exception, "Unknown type - :#{type}"
|
||||
end
|
||||
rescue Gem::RemoteFetcher::FetchError => e
|
||||
alert_warning "Error fetching data: #{e.message}"
|
||||
end
|
||||
|
||||
loaded = true if spec_dump
|
||||
|
||||
spec_dump ||= Gem.read_binary local_file
|
||||
else
|
||||
spec_dump = @fetcher.fetch_path spec_path
|
||||
loaded = true
|
||||
end
|
||||
|
||||
specs = begin
|
||||
Marshal.load spec_dump
|
||||
rescue ArgumentError
|
||||
spec_dump = @fetcher.fetch_path spec_path
|
||||
loaded = true
|
||||
|
||||
Marshal.load spec_dump
|
||||
end
|
||||
|
||||
if loaded and @update_cache then
|
||||
begin
|
||||
FileUtils.mkdir_p cache_dir
|
||||
|
||||
open local_file, 'wb' do |io|
|
||||
io << spec_dump
|
||||
end
|
||||
rescue
|
||||
errors << Gem::SourceFetchProblem.new(source, e)
|
||||
else
|
||||
list[source] = names
|
||||
end
|
||||
end
|
||||
|
||||
specs
|
||||
[list, errors]
|
||||
end
|
||||
|
||||
def tuples_for(source, type)
|
||||
cache = @caches[type]
|
||||
cache[source.uri] ||= source.load_specs(type)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,90 @@
|
|||
This CA certificate is for verifying HTTPS connection to;
|
||||
- https://rubygems.org/ (obtained by RubyGems team)
|
||||
|
||||
Certificate:
|
||||
Data:
|
||||
Version: 3 (0x2)
|
||||
Serial Number: 1 (0x1)
|
||||
Signature Algorithm: sha1WithRSAEncryption
|
||||
Issuer: C=SE, O=AddTrust AB, OU=AddTrust External TTP Network, CN=AddTrust External CA Root
|
||||
Validity
|
||||
Not Before: May 30 10:48:38 2000 GMT
|
||||
Not After : May 30 10:48:38 2020 GMT
|
||||
Subject: C=SE, O=AddTrust AB, OU=AddTrust External TTP Network, CN=AddTrust External CA Root
|
||||
Subject Public Key Info:
|
||||
Public Key Algorithm: rsaEncryption
|
||||
Public-Key: (2048 bit)
|
||||
Modulus:
|
||||
00:b7:f7:1a:33:e6:f2:00:04:2d:39:e0:4e:5b:ed:
|
||||
1f:bc:6c:0f:cd:b5:fa:23:b6:ce:de:9b:11:33:97:
|
||||
a4:29:4c:7d:93:9f:bd:4a:bc:93:ed:03:1a:e3:8f:
|
||||
cf:e5:6d:50:5a:d6:97:29:94:5a:80:b0:49:7a:db:
|
||||
2e:95:fd:b8:ca:bf:37:38:2d:1e:3e:91:41:ad:70:
|
||||
56:c7:f0:4f:3f:e8:32:9e:74:ca:c8:90:54:e9:c6:
|
||||
5f:0f:78:9d:9a:40:3c:0e:ac:61:aa:5e:14:8f:9e:
|
||||
87:a1:6a:50:dc:d7:9a:4e:af:05:b3:a6:71:94:9c:
|
||||
71:b3:50:60:0a:c7:13:9d:38:07:86:02:a8:e9:a8:
|
||||
69:26:18:90:ab:4c:b0:4f:23:ab:3a:4f:84:d8:df:
|
||||
ce:9f:e1:69:6f:bb:d7:42:d7:6b:44:e4:c7:ad:ee:
|
||||
6d:41:5f:72:5a:71:08:37:b3:79:65:a4:59:a0:94:
|
||||
37:f7:00:2f:0d:c2:92:72:da:d0:38:72:db:14:a8:
|
||||
45:c4:5d:2a:7d:b7:b4:d6:c4:ee:ac:cd:13:44:b7:
|
||||
c9:2b:dd:43:00:25:fa:61:b9:69:6a:58:23:11:b7:
|
||||
a7:33:8f:56:75:59:f5:cd:29:d7:46:b7:0a:2b:65:
|
||||
b6:d3:42:6f:15:b2:b8:7b:fb:ef:e9:5d:53:d5:34:
|
||||
5a:27
|
||||
Exponent: 65537 (0x10001)
|
||||
X509v3 extensions:
|
||||
X509v3 Subject Key Identifier:
|
||||
AD:BD:98:7A:34:B4:26:F7:FA:C4:26:54:EF:03:BD:E0:24:CB:54:1A
|
||||
X509v3 Key Usage:
|
||||
Certificate Sign, CRL Sign
|
||||
X509v3 Basic Constraints: critical
|
||||
CA:TRUE
|
||||
X509v3 Authority Key Identifier:
|
||||
keyid:AD:BD:98:7A:34:B4:26:F7:FA:C4:26:54:EF:03:BD:E0:24:CB:54:1A
|
||||
DirName:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root
|
||||
serial:01
|
||||
|
||||
Signature Algorithm: sha1WithRSAEncryption
|
||||
b0:9b:e0:85:25:c2:d6:23:e2:0f:96:06:92:9d:41:98:9c:d9:
|
||||
84:79:81:d9:1e:5b:14:07:23:36:65:8f:b0:d8:77:bb:ac:41:
|
||||
6c:47:60:83:51:b0:f9:32:3d:e7:fc:f6:26:13:c7:80:16:a5:
|
||||
bf:5a:fc:87:cf:78:79:89:21:9a:e2:4c:07:0a:86:35:bc:f2:
|
||||
de:51:c4:d2:96:b7:dc:7e:4e:ee:70:fd:1c:39:eb:0c:02:51:
|
||||
14:2d:8e:bd:16:e0:c1:df:46:75:e7:24:ad:ec:f4:42:b4:85:
|
||||
93:70:10:67:ba:9d:06:35:4a:18:d3:2b:7a:cc:51:42:a1:7a:
|
||||
63:d1:e6:bb:a1:c5:2b:c2:36:be:13:0d:e6:bd:63:7e:79:7b:
|
||||
a7:09:0d:40:ab:6a:dd:8f:8a:c3:f6:f6:8c:1a:42:05:51:d4:
|
||||
45:f5:9f:a7:62:21:68:15:20:43:3c:99:e7:7c:bd:24:d8:a9:
|
||||
91:17:73:88:3f:56:1b:31:38:18:b4:71:0f:9a:cd:c8:0e:9e:
|
||||
8e:2e:1b:e1:8c:98:83:cb:1f:31:f1:44:4c:c6:04:73:49:76:
|
||||
60:0f:c7:f8:bd:17:80:6b:2e:e9:cc:4c:0e:5a:9a:79:0f:20:
|
||||
0a:2e:d5:9e:63:26:1e:55:92:94:d8:82:17:5a:7b:d0:bc:c7:
|
||||
8f:4e:86:04
|
||||
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU
|
||||
MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs
|
||||
IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290
|
||||
MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux
|
||||
FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h
|
||||
bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v
|
||||
dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt
|
||||
H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9
|
||||
uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX
|
||||
mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX
|
||||
a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN
|
||||
E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0
|
||||
WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD
|
||||
VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0
|
||||
Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU
|
||||
cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx
|
||||
IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN
|
||||
AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH
|
||||
YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5
|
||||
6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC
|
||||
Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX
|
||||
c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a
|
||||
mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ=
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,90 @@
|
|||
This CA certificate is for verifying HTTPS connection to;
|
||||
- https://d2chzxaqi4y7f8.cloudfront.net/ (prepared by AWS)
|
||||
|
||||
Certificate:
|
||||
Data:
|
||||
Version: 3 (0x2)
|
||||
Serial Number: 927650371 (0x374ad243)
|
||||
Signature Algorithm: sha1WithRSAEncryption
|
||||
Issuer: C=US, O=Entrust.net, OU=www.entrust.net/CPS incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.net Secure Server Certification Authority
|
||||
Validity
|
||||
Not Before: May 25 16:09:40 1999 GMT
|
||||
Not After : May 25 16:39:40 2019 GMT
|
||||
Subject: C=US, O=Entrust.net, OU=www.entrust.net/CPS incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.net Secure Server Certification Authority
|
||||
Subject Public Key Info:
|
||||
Public Key Algorithm: rsaEncryption
|
||||
Public-Key: (1024 bit)
|
||||
Modulus:
|
||||
00:cd:28:83:34:54:1b:89:f3:0f:af:37:91:31:ff:
|
||||
af:31:60:c9:a8:e8:b2:10:68:ed:9f:e7:93:36:f1:
|
||||
0a:64:bb:47:f5:04:17:3f:23:47:4d:c5:27:19:81:
|
||||
26:0c:54:72:0d:88:2d:d9:1f:9a:12:9f:bc:b3:71:
|
||||
d3:80:19:3f:47:66:7b:8c:35:28:d2:b9:0a:df:24:
|
||||
da:9c:d6:50:79:81:7a:5a:d3:37:f7:c2:4a:d8:29:
|
||||
92:26:64:d1:e4:98:6c:3a:00:8a:f5:34:9b:65:f8:
|
||||
ed:e3:10:ff:fd:b8:49:58:dc:a0:de:82:39:6b:81:
|
||||
b1:16:19:61:b9:54:b6:e6:43
|
||||
Exponent: 3 (0x3)
|
||||
X509v3 extensions:
|
||||
Netscape Cert Type:
|
||||
SSL CA, S/MIME CA, Object Signing CA
|
||||
X509v3 CRL Distribution Points:
|
||||
|
||||
Full Name:
|
||||
DirName: C = US, O = Entrust.net, OU = www.entrust.net/CPS incorp. by ref. (limits liab.), OU = (c) 1999 Entrust.net Limited, CN = Entrust.net Secure Server Certification Authority, CN = CRL1
|
||||
|
||||
Full Name:
|
||||
URI:http://www.entrust.net/CRL/net1.crl
|
||||
|
||||
X509v3 Private Key Usage Period:
|
||||
Not Before: May 25 16:09:40 1999 GMT, Not After: May 25 16:09:40 2019 GMT
|
||||
X509v3 Key Usage:
|
||||
Certificate Sign, CRL Sign
|
||||
X509v3 Authority Key Identifier:
|
||||
keyid:F0:17:62:13:55:3D:B3:FF:0A:00:6B:FB:50:84:97:F3:ED:62:D0:1A
|
||||
|
||||
X509v3 Subject Key Identifier:
|
||||
F0:17:62:13:55:3D:B3:FF:0A:00:6B:FB:50:84:97:F3:ED:62:D0:1A
|
||||
X509v3 Basic Constraints:
|
||||
CA:TRUE
|
||||
1.2.840.113533.7.65.0:
|
||||
0
|
||||
..V4.0....
|
||||
Signature Algorithm: sha1WithRSAEncryption
|
||||
90:dc:30:02:fa:64:74:c2:a7:0a:a5:7c:21:8d:34:17:a8:fb:
|
||||
47:0e:ff:25:7c:8d:13:0a:fb:e4:98:b5:ef:8c:f8:c5:10:0d:
|
||||
f7:92:be:f1:c3:d5:d5:95:6a:04:bb:2c:ce:26:36:65:c8:31:
|
||||
c6:e7:ee:3f:e3:57:75:84:7a:11:ef:46:4f:18:f4:d3:98:bb:
|
||||
a8:87:32:ba:72:f6:3c:e2:3d:9f:d7:1d:d9:c3:60:43:8c:58:
|
||||
0e:22:96:2f:62:a3:2c:1f:ba:ad:05:ef:ab:32:78:87:a0:54:
|
||||
73:19:b5:5c:05:f9:52:3e:6d:2d:45:0b:f7:0a:93:ea:ed:06:
|
||||
f9:b2
|
||||
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC
|
||||
VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u
|
||||
ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc
|
||||
KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u
|
||||
ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05OTA1
|
||||
MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIGA1UE
|
||||
ChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5j
|
||||
b3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF
|
||||
bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUg
|
||||
U2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUA
|
||||
A4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQaO2f55M28Qpku0f1BBc/
|
||||
I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5gXpa0zf3
|
||||
wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OC
|
||||
AdcwggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHb
|
||||
oIHYpIHVMIHSMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5
|
||||
BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1p
|
||||
dHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1pdGVk
|
||||
MTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRp
|
||||
b24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu
|
||||
dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0
|
||||
MFqBDzIwMTkwNTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8Bdi
|
||||
E1U9s/8KAGv7UISX8+1i0BowHQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAa
|
||||
MAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EABAwwChsEVjQuMAMCBJAwDQYJKoZI
|
||||
hvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyNEwr75Ji174z4xRAN
|
||||
95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9n9cd
|
||||
2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI=
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,57 @@
|
|||
This CA certificate is for verifying HTTPS connection to;
|
||||
- https://s3.amazon.com/ (prepared by AWS)
|
||||
|
||||
Certificate:
|
||||
Data:
|
||||
Version: 1 (0x0)
|
||||
Serial Number:
|
||||
7d:d9:fe:07:cf:a8:1e:b7:10:79:67:fb:a7:89:34:c6
|
||||
Signature Algorithm: sha1WithRSAEncryption
|
||||
Issuer: C=US, O=VeriSign, Inc., OU=Class 3 Public Primary Certification Authority - G2, OU=(c) 1998 VeriSign, Inc. - For authorized use only, OU=VeriSign Trust Network
|
||||
Validity
|
||||
Not Before: May 18 00:00:00 1998 GMT
|
||||
Not After : Aug 1 23:59:59 2028 GMT
|
||||
Subject: C=US, O=VeriSign, Inc., OU=Class 3 Public Primary Certification Authority - G2, OU=(c) 1998 VeriSign, Inc. - For authorized use only, OU=VeriSign Trust Network
|
||||
Subject Public Key Info:
|
||||
Public Key Algorithm: rsaEncryption
|
||||
Public-Key: (1024 bit)
|
||||
Modulus:
|
||||
00:cc:5e:d1:11:5d:5c:69:d0:ab:d3:b9:6a:4c:99:
|
||||
1f:59:98:30:8e:16:85:20:46:6d:47:3f:d4:85:20:
|
||||
84:e1:6d:b3:f8:a4:ed:0c:f1:17:0f:3b:f9:a7:f9:
|
||||
25:d7:c1:cf:84:63:f2:7c:63:cf:a2:47:f2:c6:5b:
|
||||
33:8e:64:40:04:68:c1:80:b9:64:1c:45:77:c7:d8:
|
||||
6e:f5:95:29:3c:50:e8:34:d7:78:1f:a8:ba:6d:43:
|
||||
91:95:8f:45:57:5e:7e:c5:fb:ca:a4:04:eb:ea:97:
|
||||
37:54:30:6f:bb:01:47:32:33:cd:dc:57:9b:64:69:
|
||||
61:f8:9b:1d:1c:89:4f:5c:67
|
||||
Exponent: 65537 (0x10001)
|
||||
Signature Algorithm: sha1WithRSAEncryption
|
||||
51:4d:cd:be:5c:cb:98:19:9c:15:b2:01:39:78:2e:4d:0f:67:
|
||||
70:70:99:c6:10:5a:94:a4:53:4d:54:6d:2b:af:0d:5d:40:8b:
|
||||
64:d3:d7:ee:de:56:61:92:5f:a6:c4:1d:10:61:36:d3:2c:27:
|
||||
3c:e8:29:09:b9:11:64:74:cc:b5:73:9f:1c:48:a9:bc:61:01:
|
||||
ee:e2:17:a6:0c:e3:40:08:3b:0e:e7:eb:44:73:2a:9a:f1:69:
|
||||
92:ef:71:14:c3:39:ac:71:a7:91:09:6f:e4:71:06:b3:ba:59:
|
||||
57:26:79:00:f6:f8:0d:a2:33:30:28:d4:aa:58:a0:9d:9d:69:
|
||||
91:fd
|
||||
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDAjCCAmsCEH3Z/gfPqB63EHln+6eJNMYwDQYJKoZIhvcNAQEFBQAwgcExCzAJ
|
||||
BgNVBAYTAlVTMRcwFQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xh
|
||||
c3MgMyBQdWJsaWMgUHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcy
|
||||
MTowOAYDVQQLEzEoYykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3Jp
|
||||
emVkIHVzZSBvbmx5MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMB4X
|
||||
DTk4MDUxODAwMDAwMFoXDTI4MDgwMTIzNTk1OVowgcExCzAJBgNVBAYTAlVTMRcw
|
||||
FQYDVQQKEw5WZXJpU2lnbiwgSW5jLjE8MDoGA1UECxMzQ2xhc3MgMyBQdWJsaWMg
|
||||
UHJpbWFyeSBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEcyMTowOAYDVQQLEzEo
|
||||
YykgMTk5OCBWZXJpU2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5
|
||||
MR8wHQYDVQQLExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMIGfMA0GCSqGSIb3DQEB
|
||||
AQUAA4GNADCBiQKBgQDMXtERXVxp0KvTuWpMmR9ZmDCOFoUgRm1HP9SFIIThbbP4
|
||||
pO0M8RcPO/mn+SXXwc+EY/J8Y8+iR/LGWzOOZEAEaMGAuWQcRXfH2G71lSk8UOg0
|
||||
13gfqLptQ5GVj0VXXn7F+8qkBOvqlzdUMG+7AUcyM83cV5tkaWH4mx0ciU9cZwID
|
||||
AQABMA0GCSqGSIb3DQEBBQUAA4GBAFFNzb5cy5gZnBWyATl4Lk0PZ3BwmcYQWpSk
|
||||
U01UbSuvDV1Ai2TT1+7eVmGSX6bEHRBhNtMsJzzoKQm5EWR0zLVznxxIqbxhAe7i
|
||||
F6YM40AIOw7n60RzKprxaZLvcRTDOaxxp5EJb+RxBrO6WVcmeQD2+A2iMzAo1KpY
|
||||
oJ2daZH9
|
||||
-----END CERTIFICATE-----
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче