mirror of
				https://github.com/ruby/ruby.git
				synced 2022-11-09 12:17:21 -05:00 
			
		
		
		
	* Fix gem pristine not accounting for user installed gems. Pull request
    #2914 by Luis Sagastume.
  * Refactor keyword argument test for Ruby 2.7. Pull request #2947 by
    SHIBATA Hiroshi.
  * Fix errors at frozen Gem::Version. Pull request #2949 by Nobuyoshi
    Nakada.
  * Remove taint usage on Ruby 2.7+. Pull request #2951 by Jeremy Evans.
  * Check Manifest.txt is up to date. Pull request #2953 by David Rodríguez.
  * Clarify symlink conditionals in tests. Pull request #2962 by David
    Rodríguez.
  * Update command line parsing to work under ps. Pull request #2966 by
    David Rodríguez.
  * Properly test `Gem::Specifications.stub_for`. Pull request #2970 by
    David Rodríguez.
  * Fix Gem::LOADED_SPECS_MUTEX handling for recursive locking. Pull request
    #2985 by MSP-Greg.
		
	
			
		
			
				
	
	
		
			492 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
			
		
		
	
	
			492 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
# frozen_string_literal: true
 | 
						|
#--
 | 
						|
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
 | 
						|
# All rights reserved.
 | 
						|
# See LICENSE.txt for permissions.
 | 
						|
#++
 | 
						|
 | 
						|
require 'rubygems/user_interaction'
 | 
						|
require 'rbconfig'
 | 
						|
 | 
						|
##
 | 
						|
# Gem::ConfigFile RubyGems options and gem command options from gemrc.
 | 
						|
#
 | 
						|
# 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
 | 
						|
# allow you to specify default arguments:
 | 
						|
#
 | 
						|
#   install: --no-rdoc --no-ri
 | 
						|
#   update: --no-rdoc --no-ri
 | 
						|
#
 | 
						|
# You can use <tt>gem:</tt> to set default arguments for all commands.
 | 
						|
#
 | 
						|
# RubyGems options use symbol keys.  Valid options are:
 | 
						|
#
 | 
						|
# +:backtrace+:: See #backtrace
 | 
						|
# +:sources+:: Sets Gem::sources
 | 
						|
# +:verbose+:: See #verbose
 | 
						|
# +:concurrent_downloads+:: See #concurrent_downloads
 | 
						|
#
 | 
						|
# 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
 | 
						|
 | 
						|
  include Gem::UserInteraction
 | 
						|
 | 
						|
  DEFAULT_BACKTRACE = false
 | 
						|
  DEFAULT_BULK_THRESHOLD = 1000
 | 
						|
  DEFAULT_VERBOSITY = true
 | 
						|
  DEFAULT_UPDATE_SOURCES = true
 | 
						|
  DEFAULT_CONCURRENT_DOWNLOADS = 8
 | 
						|
  DEFAULT_CERT_EXPIRATION_LENGTH_DAYS = 365
 | 
						|
 | 
						|
  ##
 | 
						|
  # For Ruby packagers to set configuration defaults.  Set in
 | 
						|
  # rubygems/defaults/operating_system.rb
 | 
						|
 | 
						|
  OPERATING_SYSTEM_DEFAULTS = Gem.operating_system_defaults
 | 
						|
 | 
						|
  ##
 | 
						|
  # For Ruby implementers to set configuration defaults.  Set in
 | 
						|
  # rubygems/defaults/#{RUBY_ENGINE}.rb
 | 
						|
 | 
						|
  PLATFORM_DEFAULTS = Gem.platform_defaults
 | 
						|
 | 
						|
  # :stopdoc:
 | 
						|
 | 
						|
  SYSTEM_CONFIG_PATH =
 | 
						|
    begin
 | 
						|
      require "etc"
 | 
						|
      Etc.sysconfdir
 | 
						|
    rescue LoadError, NoMethodError
 | 
						|
      RbConfig::CONFIG["sysconfdir"] || "/etc"
 | 
						|
    end
 | 
						|
 | 
						|
  # :startdoc:
 | 
						|
 | 
						|
  SYSTEM_WIDE_CONFIG_FILE = File.join SYSTEM_CONFIG_PATH, 'gemrc'
 | 
						|
 | 
						|
  ##
 | 
						|
  # List of arguments supplied to the config file object.
 | 
						|
 | 
						|
  attr_reader :args
 | 
						|
 | 
						|
  ##
 | 
						|
  # Where to look for gems (deprecated)
 | 
						|
 | 
						|
  attr_accessor :path
 | 
						|
 | 
						|
  ##
 | 
						|
  # Where to install gems (deprecated)
 | 
						|
 | 
						|
  attr_accessor :home
 | 
						|
 | 
						|
  ##
 | 
						|
  # True if we print backtraces on errors.
 | 
						|
 | 
						|
  attr_writer :backtrace
 | 
						|
 | 
						|
  ##
 | 
						|
  # Bulk threshold value.  If the number of missing gems are above this
 | 
						|
  # threshold value, then a bulk download technique is used.  (deprecated)
 | 
						|
 | 
						|
  attr_accessor :bulk_threshold
 | 
						|
 | 
						|
  ##
 | 
						|
  # Verbose level of output:
 | 
						|
  # * false -- No output
 | 
						|
  # * true -- Normal output
 | 
						|
  # * :loud -- Extra output
 | 
						|
 | 
						|
  attr_accessor :verbose
 | 
						|
 | 
						|
  ##
 | 
						|
  # Number of gem downloads that should be performed concurrently.
 | 
						|
 | 
						|
  attr_accessor :concurrent_downloads
 | 
						|
 | 
						|
  ##
 | 
						|
  # True if we want to update the SourceInfoCache every time, false otherwise
 | 
						|
 | 
						|
  attr_accessor :update_sources
 | 
						|
 | 
						|
  ##
 | 
						|
  # 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
 | 
						|
 | 
						|
  ##
 | 
						|
  # Path name of directory or file of openssl CA certificate, used for remote
 | 
						|
  # https connection
 | 
						|
 | 
						|
  attr_accessor :ssl_ca_cert
 | 
						|
 | 
						|
  ##
 | 
						|
  # sources to look for gems
 | 
						|
  attr_accessor :sources
 | 
						|
 | 
						|
  ##
 | 
						|
  # Expiration length to sign a certificate
 | 
						|
 | 
						|
  attr_accessor :cert_expiration_length_days
 | 
						|
 | 
						|
  ##
 | 
						|
  # Path name of directory or file of openssl client certificate, used for remote https connection with client authentication
 | 
						|
 | 
						|
  attr_reader :ssl_client_cert
 | 
						|
 | 
						|
  ##
 | 
						|
  # Create the config file object.  +args+ is the list of arguments
 | 
						|
  # from the command line.
 | 
						|
  #
 | 
						|
  # The following command line options are handled early here rather
 | 
						|
  # than later at the time most command options are processed.
 | 
						|
  #
 | 
						|
  # <tt>--config-file</tt>, <tt>--config-file==NAME</tt>::
 | 
						|
  #   Obviously these need to be handled by the ConfigFile object to ensure we
 | 
						|
  #   get the right config file.
 | 
						|
  #
 | 
						|
  # <tt>--backtrace</tt>::
 | 
						|
  #   Backtrace needs to be turned on early so that errors before normal
 | 
						|
  #   option parsing can be properly handled.
 | 
						|
  #
 | 
						|
  # <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(args)
 | 
						|
    set_config_file_name(args)
 | 
						|
 | 
						|
    @backtrace = DEFAULT_BACKTRACE
 | 
						|
    @bulk_threshold = DEFAULT_BULK_THRESHOLD
 | 
						|
    @verbose = DEFAULT_VERBOSITY
 | 
						|
    @update_sources = DEFAULT_UPDATE_SOURCES
 | 
						|
    @concurrent_downloads = DEFAULT_CONCURRENT_DOWNLOADS
 | 
						|
    @cert_expiration_length_days = DEFAULT_CERT_EXPIRATION_LENGTH_DAYS
 | 
						|
 | 
						|
    operating_system_config = Marshal.load Marshal.dump(OPERATING_SYSTEM_DEFAULTS)
 | 
						|
    platform_config = Marshal.load Marshal.dump(PLATFORM_DEFAULTS)
 | 
						|
    system_config = load_file SYSTEM_WIDE_CONFIG_FILE
 | 
						|
    user_config = load_file config_file_name.dup.tap(&Gem::UNTAINT)
 | 
						|
 | 
						|
    environment_config = (ENV['GEMRC'] || '')
 | 
						|
      .split(File::PATH_SEPARATOR).inject({}) do |result, file|
 | 
						|
        result.merge load_file file
 | 
						|
      end
 | 
						|
 | 
						|
    @hash = operating_system_config.merge platform_config
 | 
						|
    unless args.index '--norc'
 | 
						|
      @hash = @hash.merge system_config
 | 
						|
      @hash = @hash.merge user_config
 | 
						|
      @hash = @hash.merge environment_config
 | 
						|
    end
 | 
						|
 | 
						|
    # HACK these override command-line args, which is bad
 | 
						|
    @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
 | 
						|
    @sources                     = @hash[:sources]                     if @hash.key? :sources
 | 
						|
    @cert_expiration_length_days = @hash[:cert_expiration_length_days] if @hash.key? :cert_expiration_length_days
 | 
						|
 | 
						|
    @ssl_verify_mode  = @hash[:ssl_verify_mode]  if @hash.key? :ssl_verify_mode
 | 
						|
    @ssl_ca_cert      = @hash[:ssl_ca_cert]      if @hash.key? :ssl_ca_cert
 | 
						|
    @ssl_client_cert  = @hash[:ssl_client_cert]  if @hash.key? :ssl_client_cert
 | 
						|
 | 
						|
    @api_keys         = nil
 | 
						|
    @rubygems_api_key = nil
 | 
						|
 | 
						|
    handle_arguments args
 | 
						|
  end
 | 
						|
 | 
						|
  ##
 | 
						|
  # Hash of RubyGems.org and alternate API keys
 | 
						|
 | 
						|
  def api_keys
 | 
						|
    load_api_keys unless @api_keys
 | 
						|
 | 
						|
    @api_keys
 | 
						|
  end
 | 
						|
 | 
						|
  ##
 | 
						|
  # Checks the permissions of the credentials file.  If they are not 0600 an
 | 
						|
  # error message is displayed and RubyGems aborts.
 | 
						|
 | 
						|
  def check_credentials_permissions
 | 
						|
    return if Gem.win_platform? # windows doesn't write 0600 as 0600
 | 
						|
    return unless File.exist? credentials_path
 | 
						|
 | 
						|
    existing_permissions = File.stat(credentials_path).mode & 0777
 | 
						|
 | 
						|
    return if existing_permissions == 0600
 | 
						|
 | 
						|
    alert_error <<-ERROR
 | 
						|
Your gem push credentials file located at:
 | 
						|
 | 
						|
\t#{credentials_path}
 | 
						|
 | 
						|
has file permissions of 0#{existing_permissions.to_s 8} but 0600 is required.
 | 
						|
 | 
						|
To fix this error run:
 | 
						|
 | 
						|
\tchmod 0600 #{credentials_path}
 | 
						|
 | 
						|
You should reset your credentials at:
 | 
						|
 | 
						|
\thttps://rubygems.org/profile/edit
 | 
						|
 | 
						|
if you believe they were disclosed to a third party.
 | 
						|
    ERROR
 | 
						|
 | 
						|
    terminate_interaction 1
 | 
						|
  end
 | 
						|
 | 
						|
  ##
 | 
						|
  # Location of RubyGems.org credentials
 | 
						|
 | 
						|
  def credentials_path
 | 
						|
    File.join Gem.user_home, '.gem', 'credentials'
 | 
						|
  end
 | 
						|
 | 
						|
  def load_api_keys
 | 
						|
    check_credentials_permissions
 | 
						|
 | 
						|
    @api_keys = if File.exist? credentials_path
 | 
						|
                  load_file(credentials_path)
 | 
						|
                else
 | 
						|
                  @hash
 | 
						|
                end
 | 
						|
 | 
						|
    if @api_keys.key? :rubygems_api_key
 | 
						|
      @rubygems_api_key    = @api_keys[:rubygems_api_key]
 | 
						|
      @api_keys[:rubygems] = @api_keys.delete :rubygems_api_key unless
 | 
						|
        @api_keys.key? :rubygems
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  ##
 | 
						|
  # Returns the RubyGems.org API key
 | 
						|
 | 
						|
  def rubygems_api_key
 | 
						|
    load_api_keys unless @rubygems_api_key
 | 
						|
 | 
						|
    @rubygems_api_key
 | 
						|
  end
 | 
						|
 | 
						|
  ##
 | 
						|
  # Sets the RubyGems.org API key to +api_key+
 | 
						|
 | 
						|
  def rubygems_api_key=(api_key)
 | 
						|
    set_api_key :rubygems_api_key, api_key
 | 
						|
 | 
						|
    @rubygems_api_key = api_key
 | 
						|
  end
 | 
						|
 | 
						|
  ##
 | 
						|
  # Set a specific host's API key to +api_key+
 | 
						|
 | 
						|
  def set_api_key(host, api_key)
 | 
						|
    check_credentials_permissions
 | 
						|
 | 
						|
    config = load_file(credentials_path).merge(host => api_key)
 | 
						|
 | 
						|
    dirname = File.dirname credentials_path
 | 
						|
    Dir.mkdir(dirname) unless File.exist? dirname
 | 
						|
 | 
						|
    Gem.load_yaml
 | 
						|
 | 
						|
    permissions = 0600 & (~File.umask)
 | 
						|
    File.open(credentials_path, 'w', permissions) do |f|
 | 
						|
      f.write config.to_yaml
 | 
						|
    end
 | 
						|
 | 
						|
    load_api_keys # reload
 | 
						|
  end
 | 
						|
 | 
						|
  ##
 | 
						|
  # Remove the +~/.gem/credentials+ file to clear all the current sessions.
 | 
						|
 | 
						|
  def unset_api_key!
 | 
						|
    return false unless File.exist?(credentials_path)
 | 
						|
 | 
						|
    File.delete(credentials_path)
 | 
						|
  end
 | 
						|
 | 
						|
  def load_file(filename)
 | 
						|
    Gem.load_yaml
 | 
						|
 | 
						|
    yaml_errors = [ArgumentError]
 | 
						|
    yaml_errors << Psych::SyntaxError if defined?(Psych::SyntaxError)
 | 
						|
 | 
						|
    return {} unless filename && !filename.empty? && File.exist?(filename)
 | 
						|
 | 
						|
    begin
 | 
						|
      content = Gem::SafeYAML.load(File.read(filename))
 | 
						|
      unless content.kind_of? Hash
 | 
						|
        warn "Failed to load #{filename} because it doesn't contain valid YAML hash"
 | 
						|
        return {}
 | 
						|
      end
 | 
						|
      return content
 | 
						|
    rescue *yaml_errors => e
 | 
						|
      warn "Failed to load #{filename}, #{e}"
 | 
						|
    rescue Errno::EACCES
 | 
						|
      warn "Failed to load #{filename} due to permissions problem."
 | 
						|
    end
 | 
						|
 | 
						|
    {}
 | 
						|
  end
 | 
						|
 | 
						|
  # True if the backtrace option has been specified, or debug is on.
 | 
						|
  def backtrace
 | 
						|
    @backtrace or $DEBUG
 | 
						|
  end
 | 
						|
 | 
						|
  # The name of the configuration file.
 | 
						|
  def config_file_name
 | 
						|
    @config_file_name || Gem.config_file
 | 
						|
  end
 | 
						|
 | 
						|
  # Delegates to @hash
 | 
						|
  def each(&block)
 | 
						|
    hash = @hash.dup
 | 
						|
    hash.delete :update_sources
 | 
						|
    hash.delete :verbose
 | 
						|
    hash.delete :backtrace
 | 
						|
    hash.delete :bulk_threshold
 | 
						|
 | 
						|
    yield :update_sources, @update_sources
 | 
						|
    yield :verbose, @verbose
 | 
						|
    yield :backtrace, @backtrace
 | 
						|
    yield :bulk_threshold, @bulk_threshold
 | 
						|
 | 
						|
    yield 'config_file_name', @config_file_name if @config_file_name
 | 
						|
 | 
						|
    hash.each(&block)
 | 
						|
  end
 | 
						|
 | 
						|
  # Handle the command arguments.
 | 
						|
  def handle_arguments(arg_list)
 | 
						|
    @args = []
 | 
						|
 | 
						|
    arg_list.each do |arg|
 | 
						|
      case arg
 | 
						|
      when /^--(backtrace|traceback)$/ then
 | 
						|
        @backtrace = true
 | 
						|
      when /^--debug$/ then
 | 
						|
        $DEBUG = true
 | 
						|
 | 
						|
        warn 'NOTE:  Debugging mode prints all exceptions even when rescued'
 | 
						|
      else
 | 
						|
        @args << arg
 | 
						|
      end
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  # Really verbose mode gives you extra output.
 | 
						|
  def really_verbose
 | 
						|
    case verbose
 | 
						|
    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.fetch(:backtrace, DEFAULT_BACKTRACE)
 | 
						|
    yaml_hash[:bulk_threshold] = @hash.fetch(:bulk_threshold, DEFAULT_BULK_THRESHOLD)
 | 
						|
    yaml_hash[:sources] = Gem.sources.to_a
 | 
						|
    yaml_hash[:update_sources] = @hash.fetch(:update_sources, DEFAULT_UPDATE_SOURCES)
 | 
						|
    yaml_hash[:verbose] = @hash.fetch(:verbose, DEFAULT_VERBOSITY)
 | 
						|
 | 
						|
    yaml_hash[:concurrent_downloads] =
 | 
						|
      @hash.fetch(:concurrent_downloads, DEFAULT_CONCURRENT_DOWNLOADS)
 | 
						|
 | 
						|
    yaml_hash[:ssl_verify_mode] =
 | 
						|
      @hash[:ssl_verify_mode] if @hash.key? :ssl_verify_mode
 | 
						|
 | 
						|
    yaml_hash[:ssl_ca_cert] =
 | 
						|
      @hash[:ssl_ca_cert] if @hash.key? :ssl_ca_cert
 | 
						|
 | 
						|
    yaml_hash[:ssl_client_cert] =
 | 
						|
      @hash[:ssl_client_cert] if @hash.key? :ssl_client_cert
 | 
						|
 | 
						|
    keys = yaml_hash.keys.map { |key| key.to_s }
 | 
						|
    keys << 'debug'
 | 
						|
    re = Regexp.union(*keys)
 | 
						|
 | 
						|
    @hash.each do |key, value|
 | 
						|
      key = key.to_s
 | 
						|
      next if key =~ re
 | 
						|
      yaml_hash[key.to_s] = value
 | 
						|
    end
 | 
						|
 | 
						|
    yaml_hash.to_yaml
 | 
						|
  end
 | 
						|
 | 
						|
  # Writes out this config file, replacing its source.
 | 
						|
  def write
 | 
						|
    File.open config_file_name, 'w' do |io|
 | 
						|
      io.write to_yaml
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
  # Return the configuration information for +key+.
 | 
						|
  def [](key)
 | 
						|
    @hash[key.to_s]
 | 
						|
  end
 | 
						|
 | 
						|
  # Set configuration option +key+ to +value+.
 | 
						|
  def []=(key, value)
 | 
						|
    @hash[key.to_s] = value
 | 
						|
  end
 | 
						|
 | 
						|
  def ==(other) # :nodoc:
 | 
						|
    self.class === other and
 | 
						|
      @backtrace == other.backtrace and
 | 
						|
      @bulk_threshold == other.bulk_threshold and
 | 
						|
      @verbose == other.verbose and
 | 
						|
      @update_sources == other.update_sources and
 | 
						|
      @hash == other.hash
 | 
						|
  end
 | 
						|
 | 
						|
  attr_reader :hash
 | 
						|
  protected :hash
 | 
						|
 | 
						|
  private
 | 
						|
 | 
						|
  def set_config_file_name(args)
 | 
						|
    @config_file_name = ENV["GEMRC"]
 | 
						|
    need_config_file_name = false
 | 
						|
 | 
						|
    args.each do |arg|
 | 
						|
      if need_config_file_name
 | 
						|
        @config_file_name = arg
 | 
						|
        need_config_file_name = false
 | 
						|
      elsif arg =~ /^--config-file=(.*)/
 | 
						|
        @config_file_name = $1
 | 
						|
      elsif arg =~ /^--config-file$/
 | 
						|
        need_config_file_name = true
 | 
						|
      end
 | 
						|
    end
 | 
						|
  end
 | 
						|
 | 
						|
end
 |