diff options
33 files changed, 329 insertions, 158 deletions
@@ -7,7 +7,6 @@ #++ require 'rbconfig' -require 'thread' module Gem VERSION = "3.0.0.beta1" @@ -526,8 +525,9 @@ An Array (#{env.inspect}) was passed in from #{caller[3]} end def self.find_files_from_load_path glob # :nodoc: $LOAD_PATH.map { |load_path| - Dir["#{File.expand_path glob, load_path}#{Gem.suffix_pattern}"] }.flatten.select { |file| File.file? file.untaint } end @@ -1119,8 +1119,9 @@ An Array (#{env.inspect}) was passed in from #{caller[3]} path = "rubygems_plugin" files = [] $LOAD_PATH.each do |load_path| - globbed = Dir["#{File.expand_path path, load_path}#{Gem.suffix_pattern}"] globbed.each do |load_path_file| files << load_path_file if File.file?(load_path_file.untaint) @@ -152,7 +152,7 @@ class Gem::BasicSpecification # The path to the data directory for this gem. def datadir -# TODO: drop the extra ", gem_name" which is uselessly redundant File.expand_path(File.join(gems_dir, full_name, "data", name)).untaint end @@ -282,7 +282,7 @@ class Gem::BasicSpecification self.raw_require_paths.first end else - "lib" # default value for require_paths for bundler/inline end "#{self.full_gem_path}/#{dirs}".dup.untaint @@ -71,6 +71,10 @@ class Gem::CommandManager :yank, ] ## # Return the authoritative instance of the command manager. @@ -174,6 +178,8 @@ class Gem::CommandManager end def find_command(cmd_name) possibilities = find_command_possibilities cmd_name if possibilities.size > 1 then @@ -186,6 +192,11 @@ class Gem::CommandManager self[possibilities.first] end def find_command_possibilities(cmd_name) len = cmd_name.length @@ -10,6 +10,10 @@ class Gem::Commands::BuildCommand < Gem::Command add_option '--force', 'skip validation of the spec' do |value, options| options[:force] = true end end def arguments # :nodoc: @@ -51,7 +55,7 @@ with gem spec: spec = Gem::Specification.load File.basename(gemspec) if spec then - Gem::Package.build spec, options[:force] else alert_error "Error loading gemspec. Aborting." terminate_interaction 1 @@ -87,7 +87,7 @@ class Gem::Commands::CertCommand < Gem::Command add_option('-d', '--days NUMBER_OF_DAYS', 'Days before the certificate expires') do |days, options| - options[:expiration_length_days] = days.to_i end end @@ -22,6 +22,12 @@ class Gem::Commands::CleanupCommand < Gem::Command options[:check_dev] = value end @candidate_gems = nil @default_gems = [] @full = nil @@ -124,8 +130,10 @@ If no gems are named all gems in GEM_HOME are cleaned. spec.default_gem? } gems_to_cleanup = gems_to_cleanup.select { |spec| - spec.base_dir == @original_home } @default_gems += default_gems @@ -117,6 +117,13 @@ to write the specification by hand. For example: some_extension_gem (1.0) $ EOF end @@ -60,8 +60,14 @@ class Gem::Commands::OpenCommand < Gem::Command def open_gem name spec = spec_for name return false unless spec open_editor(spec.full_gem_path) end @@ -46,6 +46,12 @@ class Gem::Commands::PristineCommand < Gem::Command options[:env_shebang] = value end add_version_option('restore to', 'pristine condition') end @@ -160,12 +166,15 @@ extensions will be restored. install_defaults.to_s['--env-shebang'] end installer_options = { :wrappers => true, :force => true, :install_dir => spec.base_dir, :env_shebang => env_shebang, :build_args => spec.build_args, } if options[:only_executables] then @@ -29,6 +29,8 @@ command. For further discussion see the help for the yank command. def initialize super 'push', 'Push a gem up to the gem server', :host => self.host add_proxy_option add_key_option @@ -36,20 +38,41 @@ command. For further discussion see the help for the yank command. 'Push to another gemcutter-compatible host', ' (e.g. https://rubygems.org)') do |value, options| options[:host] = value end @host = nil end def execute - @host = options[:host] sign_in @host - send_gem get_one_gem_name end - def send_gem name args = [:post, "api/v1/gems"] latest_rubygems_version = Gem.latest_rubygems_version @@ -100,5 +123,15 @@ You can upgrade or downgrade to the latest release version with: with_response response end end @@ -84,7 +84,7 @@ class Gem::Commands::SetupCommand < Gem::Command add_option '--[no-]regenerate-binstubs', 'Regenerate gem binstubs' do |value, options| - options[:regenerate_binstubs] = value end add_option('-E', '--[no-]env-shebang', @@ -468,8 +468,8 @@ By default, this RubyGems will install gem as: (prefix == RbConfig::CONFIG['libdir'] or # this one is important prefix == File.join(RbConfig::CONFIG['libdir'], 'ruby')) then - lib_dir = RbConfig::CONFIG[site_or_vendor] - bin_dir = RbConfig::CONFIG['bindir'] else lib_dir = File.join prefix, 'lib' bin_dir = File.join prefix, 'bin' @@ -10,7 +10,7 @@ class Gem::Commands::SigninCommand < Gem::Command 'It defaults to https://rubygems.org' add_option('--host HOST', 'Push to another gemcutter-compatible host') do |value, options| - options[:host] = value end end @@ -48,7 +48,7 @@ class Gem::Commands::UninstallCommand < Gem::Command end add_option('-n', '--bindir DIR', - 'Directory to remove binaries from') do |value, options| options[:bin_dir] = File.expand_path(value) end @@ -458,7 +458,7 @@ class Gem::DependencyInstaller rescue Gem::Package::FormatError end end - # else This is a dependency. InstallerSet handles this case end end @@ -6,7 +6,6 @@ #++ require 'rubygems/user_interaction' -require 'thread' class Gem::Ext::Builder @@ -271,7 +271,7 @@ class Gem::Indexer # List of gem file names to index. def gem_file_list - Dir[File.join(@dest_directory, "gems", '*.gem')] end ## @@ -25,7 +25,7 @@ module Gem::InstallUpdateOptions end add_option(:"Install/Update", '-n', '--bindir DIR', - 'Directory where binary files are', 'located') do |value, options| options[:bin_dir] = File.expand_path(value) end @@ -187,6 +187,8 @@ class Gem::Installer @package.prog_mode = options[:prog_mode] @package.data_mode = options[:data_mode] if options[:user_install] and not options[:unpack] then @gem_home = Gem.user_dir @bin_dir = Gem.bindir gem_home unless options[:bin_dir] @@ -379,7 +381,7 @@ class Gem::Installer @specs ||= begin specs = [] - Dir[File.join(gem_home, "specifications", "*.gemspec")].each do |path| spec = Gem::Specification.load path.untaint specs << spec if spec end @@ -769,15 +771,30 @@ TEXT # return the stub script text used to launch the true Ruby script def windows_stub_script(bindir, bin_file_name) - ruby = Gem.ruby.gsub(/^\"|\"$/, "").tr(File::SEPARATOR, "\\") - return <<-TEXT @ECHO OFF -IF NOT "%~f0" == "~f0" GOTO :WinNT -@"#{ruby}" "#{File.join(bindir, bin_file_name)}" %1 %2 %3 %4 %5 %6 %7 %8 %9 -GOTO :EOF -:WinNT -@"#{ruby}" "%~dpn0" %* -TEXT end ## @@ -119,12 +119,12 @@ class Gem::Package # Permission for other files attr_accessor :data_mode - 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 @@ -254,12 +254,14 @@ class Gem::Package ## # Builds this package based on the specification set by #spec= - def build skip_validation = false Gem.load_yaml require 'rubygems/security' @spec.mark_version - @spec.validate unless skip_validation setup_signer @@ -119,6 +119,12 @@ class Gem::Package::TarReader::Entry bytes_read end ## # Reads +len+ bytes from the tar file entry, or the rest of the entry if # nil @@ -137,7 +143,19 @@ class Gem::Package::TarReader::Entry ret end - alias readpartial read # :nodoc: ## # Rewinds to the beginning of the tar file entry @@ -23,12 +23,14 @@ class Gem::PathSupport # hashtable, or defaults to ENV, the system environment. # def initialize(env) - @home = env["GEM_HOME"] || Gem.default_dir if File::ALT_SEPARATOR then - @home = @home.gsub(File::ALT_SEPARATOR, File::SEPARATOR) end @path = split_gem_path env["GEM_PATH"], @home @spec_cache_dir = env["GEM_SPEC_CACHE"] || Gem.default_spec_cache_dir @@ -65,7 +67,7 @@ class Gem::PathSupport gem_path = default_path end - gem_path.uniq end # Return the default Gem path @@ -77,4 +79,12 @@ class Gem::PathSupport end gem_path end end @@ -384,17 +384,15 @@ class Gem::RemoteFetcher require 'base64' require 'openssl' - unless uri.user && uri.password - raise FetchError.new("credentials needed in s3 source, like s3://key:secret@bucket-name/", uri.to_s) - end expiration ||= s3_expiration canonical_path = "/#{uri.host}#{uri.path}" payload = "GET\n\n\n#{expiration}\n#{canonical_path}" - digest = OpenSSL::HMAC.digest('sha1', uri.password, payload) # URI.escape is deprecated, and there isn't yet a replacement that does quite what we want signature = Base64.encode64(digest).gsub("\n", '').gsub(/[\+\/=]/) { |c| BASE64_URI_TRANSLATE[c] } - URI.parse("https://#{uri.host}.s3.amazonaws.com#{uri.path}?AWSAccessKeyId=#{uri.user}&Expires=#{expiration}&Signature=#{signature}") end def s3_expiration @@ -414,4 +412,21 @@ class Gem::RemoteFetcher @pools[proxy] ||= Gem::Request::ConnectionPools.new proxy, @cert_files end end end @@ -1,6 +1,5 @@ # frozen_string_literal: true require 'net/http' -require 'thread' require 'time' require 'rubygems/user_interaction' @@ -1,5 +1,4 @@ # frozen_string_literal: true -require 'thread' class Gem::Request::ConnectionPools # :nodoc: @@ -417,7 +417,7 @@ class Gem::RequestSet end def specs_in dir - Dir["#{dir}/specifications/*.gemspec"].map do |g| Gem::Specification.load g end end @@ -284,7 +284,7 @@ class Gem::Requirement end def sort_requirements! # :nodoc: - @requirements.sort! do |l, r| comp = l.last <=> r.last # first, sort by the requirement's version next comp unless comp == 0 l.first <=> r.first # then, sort by the operator (for stability) @@ -2,8 +2,12 @@ ## # Basic OpenSSL-based package signing class. class Gem::Security::Signer ## # The chain of certificates for signing including the signing certificate @@ -33,6 +37,7 @@ class Gem::Security::Signer def initialize key, cert_chain, passphrase = nil @cert_chain = cert_chain @key = key unless @key then default_key = File.join Gem.default_key_path @@ -47,8 +52,10 @@ class Gem::Security::Signer @digest_algorithm = Gem::Security::DIGEST_ALGORITHM @digest_name = Gem::Security::DIGEST_NAME - @key = OpenSSL::PKey::RSA.new File.read(@key), passphrase if - @key and not OpenSSL::PKey::RSA === @key if @cert_chain then @cert_chain = @cert_chain.compact.map do |cert| @@ -121,6 +128,7 @@ class Gem::Security::Signer # The key will be re-signed if: # * The expired certificate is self-signed # * The expired certificate is saved at ~/.gem/gem-public_cert.pem # * There is no file matching the expiry date at # ~/.gem/gem-public_cert.pem.expired.%Y%m%d%H%M%S # @@ -131,22 +139,29 @@ class Gem::Security::Signer def re_sign_key # :nodoc: old_cert = @cert_chain.last - disk_cert_path = File.join Gem.default_cert_path - disk_cert = File.read disk_cert_path rescue nil - disk_key = - File.read File.join(Gem.default_key_path) rescue nil - 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, ".gem", 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 @@ -202,7 +202,7 @@ class Gem::SpecFetcher }.compact matches = if matches.empty? && type != :prerelease - suggest_gems_from_name gem_name, :prerelease else matches.uniq.sort_by { |name, dist| dist } end @@ -172,9 +172,9 @@ class Gem::Specification < Gem::BasicSpecification when String v.dump when Numeric - "default_value(:#{k})" else - "default_value(:#{k}).dup" end end @@ -761,14 +761,14 @@ class Gem::Specification < Gem::BasicSpecification def self.each_gemspec(dirs) # :nodoc: dirs.each do |dir| - Dir[File.join(dir, "*.gemspec")].each do |path| yield path.untaint end end end def self.gemspec_stubs_in dir, pattern - Dir[File.join(dir, pattern)].map { |path| yield path }.select(&:valid?) end private_class_method :gemspec_stubs_in @@ -820,11 +820,11 @@ class Gem::Specification < Gem::BasicSpecification def self.stubs @@stubs ||= begin pattern = "*.gemspec" - stubs = default_stubs(pattern).concat installed_stubs(dirs, pattern) stubs = uniq_by(stubs) { |stub| stub.full_name } _resort!(stubs) - @@stubs_by_name = stubs.group_by(&:name) stubs end end @@ -833,13 +833,15 @@ class Gem::Specification < Gem::BasicSpecification ## # Returns a Gem::StubSpecification for installed gem named +name+ def self.stubs_for name if @@stubs @@stubs_by_name[name] || [] else pattern = "#{name}-*.gemspec" - stubs = default_stubs(pattern) + installed_stubs(dirs, pattern) stubs = uniq_by(stubs) { |stub| stub.full_name }.group_by(&:name) stubs.each_value { |v| _resort!(v) } @@ -1280,11 +1282,17 @@ class Gem::Specification < Gem::BasicSpecification unresolved = unresolved_deps unless unresolved.empty? then w = "W" + "ARN" - warn "#{w}: Unresolved specs during Gem::Specification.reset:" unresolved.values.each do |dep| warn " #{dep}" end - warn "#{w}: Clearing out unresolved specs." warn "Please report a bug if this causes problems." unresolved.clear end @@ -2645,19 +2653,14 @@ class Gem::Specification < Gem::BasicSpecification # Raises InvalidSpecificationException if the spec does not pass the # checks.. - def validate packaging = true - @warnings = 0 require 'rubygems/user_interaction' extend Gem::UserInteraction normalize validation_policy = Gem::SpecificationPolicy.new(self) validation_policy.packaging = packaging - validation_policy.validate - ensure - if $! or @warnings > 0 then - alert_warning "See http://guides.rubygems.org/specification-reference/ for help" - end end def keep_only_files_and_directories @@ -2744,12 +2747,6 @@ class Gem::Specification < Gem::BasicSpecification @installed_by_version ||= nil end - def warning statement # :nodoc: - @warnings += 1 - - alert_warning statement - end - def raw_require_paths # :nodoc: @require_paths end @@ -16,6 +16,12 @@ class Gem::SpecificationPolicy < SimpleDelegator wiki_uri ] # :nodoc: ## # If set to true, run packaging-specific checks, as well. @@ -28,7 +34,7 @@ class Gem::SpecificationPolicy < SimpleDelegator # Raises InvalidSpecificationException if the spec does not pass the # checks. - def validate validate_nil_attributes validate_rubygems_version @@ -64,6 +70,15 @@ class Gem::SpecificationPolicy < SimpleDelegator validate_values validate_dependencies true end @@ -72,35 +87,29 @@ class Gem::SpecificationPolicy < SimpleDelegator def validate_metadata unless Hash === metadata then - raise Gem::InvalidSpecificationException, - 'metadata must be a hash' end metadata.each do |key, value| if !key.kind_of?(String) then - raise Gem::InvalidSpecificationException, - "metadata keys must be a String" end if key.size > 128 then - raise Gem::InvalidSpecificationException, - "metadata key too large (#{key.size} > 128)" end if !value.kind_of?(String) then - raise Gem::InvalidSpecificationException, - "metadata values must be a String" end if value.size > 1024 then - raise Gem::InvalidSpecificationException, - "metadata value too large (#{value.size} > 1024)" end if METADATA_LINK_KEYS.include? key then if value !~ VALID_URI_PATTERN then - raise Gem::InvalidSpecificationException, - "metadata['#{key}'] has invalid link: #{value.inspect}" end end end @@ -132,30 +141,6 @@ duplicate dependency on #{dep}, (#{prev.requirement}) use: warning_messages << "prerelease dependency on #{dep} is not recommended" if prerelease_dep && !version.prerelease? - overly_strict = dep.requirement.requirements.length == 1 && - dep.requirement.requirements.any? do |op, version| - op == '~>' and - not version.prerelease? and - version.segments.length > 2 and - version.segments.first != 0 - end - - if overly_strict then - _, dep_version = dep.requirement.requirements.first - - base = dep_version.segments.first 2 - upper_bound = dep_version.segments.first(dep_version.segments.length - 1) - upper_bound[-1] += 1 - - warning_messages << <<-WARNING -pessimistic dependency on #{dep} may be overly strict - if #{dep.name} is semantically versioned, use: - add_#{dep.type}_dependency '#{dep.name}', '~> #{base.join '.'}', '>= #{dep_version}' - if #{dep.name} is not semantically versioned, you can bypass this warning with: - add_#{dep.type}_dependency '#{dep.name}', '>= #{dep_version}', '< #{upper_bound.join '.'}.a' - WARNING - end - open_ended = dep.requirement.requirements.all? do |op, version| not version.prerelease? and (op == '>' or op == '>=') end @@ -179,7 +164,7 @@ open-ended dependency on #{dep} is not recommended end end if error_messages.any? then - raise Gem::InvalidSpecificationException, error_messages.join end if warning_messages.any? then warning_messages.each { |warning_message| warning warning_message } @@ -215,45 +200,38 @@ open-ended dependency on #{dep} is not recommended __getobj__.instance_variable_get("@#{attrname}").nil? end return if nil_attributes.empty? - raise Gem::InvalidSpecificationException, - "#{nil_attributes.join ', '} must not be nil" end def validate_rubygems_version return unless packaging return if rubygems_version == Gem::VERSION - raise Gem::InvalidSpecificationException, - "expected RubyGems version #{Gem::VERSION}, was #{rubygems_version}" end def validate_required_attributes Gem::Specification.required_attributes.each do |symbol| unless send symbol then - raise Gem::InvalidSpecificationException, - "missing value for attribute #{symbol}" end end end def validate_name if !name.is_a?(String) then - raise Gem::InvalidSpecificationException, - "invalid value for attribute name: \"#{name.inspect}\" must be a string" elsif name !~ /[a-zA-Z]/ then - raise Gem::InvalidSpecificationException, - "invalid value for attribute name: #{name.dump} must include at least one letter" elsif name !~ VALID_NAME_PATTERN then - raise Gem::InvalidSpecificationException, - "invalid value for attribute name: #{name.dump} can only include letters, numbers, dashes, and underscores" end end def validate_require_paths return unless raw_require_paths.empty? - raise Gem::InvalidSpecificationException, - 'specification must have at least one require_path' end def validate_non_files @@ -261,31 +239,27 @@ open-ended dependency on #{dep} is not recommended non_files = files.reject {|x| File.file?(x) || File.symlink?(x)} unless non_files.empty? then - raise Gem::InvalidSpecificationException, - "[\"#{non_files.join "\", \""}\"] are not files" end end def validate_self_inclusion_in_files_list return unless files.include?(file_name) - - raise Gem::InvalidSpecificationException, - "#{full_name} contains itself (#{file_name}), check your files list" end def validate_specification_version return if specification_version.is_a?(Integer) - - raise Gem::InvalidSpecificationException, - 'specification_version must be an Integer (did you mean version?)' end def validate_platform case platform when Gem::Platform, Gem::Platform::RUBY then # ok else - raise Gem::InvalidSpecificationException, - "invalid platform #{platform.inspect}, see Gem::Platform" end end @@ -313,15 +287,13 @@ open-ended dependency on #{dep} is not recommended def validate_authors_field return unless authors.empty? - raise Gem::InvalidSpecificationException, - "authors may not be empty" end def validate_licenses licenses.each { |license| if license.length > 64 then - raise Gem::InvalidSpecificationException, - "each license must be 64 characters or less" end if !Gem::Licenses.match?(license) then @@ -347,19 +319,19 @@ http://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard li def validate_lazy_metadata unless authors.grep(LAZY_PATTERN).empty? then - raise Gem::InvalidSpecificationException, "#{LAZY} is not an author" end unless Array(email).grep(LAZY_PATTERN).empty? then - raise Gem::InvalidSpecificationException, "#{LAZY} is not an email" end if description =~ LAZY_PATTERN then - raise Gem::InvalidSpecificationException, "#{LAZY} is not a description" end if summary =~ LAZY_PATTERN then - raise Gem::InvalidSpecificationException, "#{LAZY} is not a summary" end # Make sure a homepage is valid HTTP/HTTPS URI @@ -367,10 +339,10 @@ http://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard li begin homepage_uri = URI.parse(homepage) unless [URI::HTTP, URI::HTTPS].member? homepage_uri.class - raise Gem::InvalidSpecificationException, "\"#{homepage}\" is not a valid HTTP URI" end rescue URI::InvalidURIError - raise Gem::InvalidSpecificationException, "\"#{homepage}\" is not a valid HTTP URI" end end end @@ -407,4 +379,20 @@ http://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard li warning "#{executable_path} is missing #! line" end end @@ -13,6 +13,15 @@ else require 'rubygems' end begin gem 'minitest' rescue Gem::LoadError @@ -382,6 +391,11 @@ class Gem::TestCase < (defined?(Minitest::Test) ? Minitest::Test : MiniTest::Uni util_set_arch 'i686-darwin8.10.1' end @marshal_version = "#{Marshal::MAJOR_VERSION}.#{Marshal::MINOR_VERSION}" @orig_LOADED_FEATURES = $LOADED_FEATURES.dup end @@ -449,6 +463,10 @@ class Gem::TestCase < (defined?(Minitest::Test) ? Minitest::Test : MiniTest::Uni Gem::Specification.unresolved_deps.clear Gem::refresh @back_ui.close end @@ -80,8 +80,6 @@ module Gem::Util end return system(*(cmds << opt)) rescue TypeError - require 'thread' - @silent_mutex ||= Mutex.new @silent_mutex.synchronize do @@ -118,4 +116,16 @@ module Gem::Util end end end @@ -170,7 +170,10 @@ class Gem::Version # True if the +version+ string matches RubyGems' requirements. def self.correct? version - return false if version.nil? !!(version.to_s =~ ANCHORED_VERSION_PATTERN) end @@ -325,7 +328,9 @@ class Gem::Version segments.pop while segments.size > 2 segments.push 0 while segments.size < 2 - "~> #{segments.join(".")}" end ## |