mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Prepare to release RubyGems 3.1.0 final version.
This commit is contained in:
parent
26774351dc
commit
82cc2843a9
23 changed files with 393 additions and 142 deletions
|
@ -160,24 +160,14 @@ module Gem
|
|||
].freeze
|
||||
|
||||
##
|
||||
# Exception classes used in a Gem.read_binary +rescue+ statement. Not all of
|
||||
# these are defined in Ruby 1.8.7, hence the need for this convoluted setup.
|
||||
# Exception classes used in a Gem.read_binary +rescue+ statement
|
||||
|
||||
READ_BINARY_ERRORS = begin
|
||||
read_binary_errors = [Errno::EACCES, Errno::EROFS, Errno::ENOSYS]
|
||||
read_binary_errors << Errno::ENOTSUP if Errno.const_defined?(:ENOTSUP)
|
||||
read_binary_errors
|
||||
end.freeze
|
||||
READ_BINARY_ERRORS = [Errno::EACCES, Errno::EROFS, Errno::ENOSYS, Errno::ENOTSUP].freeze
|
||||
|
||||
##
|
||||
# Exception classes used in Gem.write_binary +rescue+ statement. Not all of
|
||||
# these are defined in Ruby 1.8.7.
|
||||
# Exception classes used in Gem.write_binary +rescue+ statement
|
||||
|
||||
WRITE_BINARY_ERRORS = begin
|
||||
write_binary_errors = [Errno::ENOSYS]
|
||||
write_binary_errors << Errno::ENOTSUP if Errno.const_defined?(:ENOTSUP)
|
||||
write_binary_errors
|
||||
end.freeze
|
||||
WRITE_BINARY_ERRORS = [Errno::ENOSYS, Errno::ENOTSUP].freeze
|
||||
|
||||
@@win_platform = nil
|
||||
|
||||
|
|
|
@ -369,22 +369,44 @@ class Gem::Command
|
|||
end
|
||||
end
|
||||
|
||||
def deprecate_option(short_name: nil, long_name: nil, version: nil)
|
||||
@deprecated_options[command].merge!({ short_name => { "rg_version_to_expire" => version } }) if short_name
|
||||
@deprecated_options[command].merge!({ long_name => { "rg_version_to_expire" => version } }) if long_name
|
||||
##
|
||||
# Mark a command-line option as deprecated, and optionally specify a
|
||||
# deprecation horizon.
|
||||
#
|
||||
# Note that with the current implementation, every version of the option needs
|
||||
# to be explicitly deprecated, so to deprecate an option defined as
|
||||
#
|
||||
# add_option('-t', '--[no-]test', 'Set test mode') do |value, options|
|
||||
# # ... stuff ...
|
||||
# end
|
||||
#
|
||||
# you would need to explicitly add a call to `deprecate_option` for every
|
||||
# version of the option you want to deprecate, like
|
||||
#
|
||||
# deprecate_option('-t')
|
||||
# deprecate_option('--test')
|
||||
# deprecate_option('--no-test')
|
||||
|
||||
def deprecate_option(name, version: nil, extra_msg: nil)
|
||||
@deprecated_options[command].merge!({ name => { "rg_version_to_expire" => version, "extra_msg" => extra_msg } })
|
||||
end
|
||||
|
||||
def check_deprecated_options(options)
|
||||
options.each do |option|
|
||||
if option_is_deprecated?(option)
|
||||
version_to_expire = @deprecated_options[command][option]["rg_version_to_expire"]
|
||||
deprecation = @deprecated_options[command][option]
|
||||
version_to_expire = deprecation["rg_version_to_expire"]
|
||||
|
||||
deprecate_option_msg = if version_to_expire
|
||||
"The \"#{option}\" option has been deprecated and will be removed in Rubygems #{version_to_expire}, its use is discouraged."
|
||||
"The \"#{option}\" option has been deprecated and will be removed in Rubygems #{version_to_expire}."
|
||||
else
|
||||
"The \"#{option}\" option has been deprecated and will be removed in future versions of Rubygems, its use is discouraged."
|
||||
"The \"#{option}\" option has been deprecated and will be removed in future versions of Rubygems."
|
||||
end
|
||||
|
||||
extra_msg = deprecation["extra_msg"]
|
||||
|
||||
deprecate_option_msg += " #{extra_msg}" if extra_msg
|
||||
|
||||
alert_warning(deprecate_option_msg)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -25,6 +25,9 @@ class Gem::Commands::GenerateIndexCommand < Gem::Command
|
|||
options[:build_modern] = value
|
||||
end
|
||||
|
||||
deprecate_option('--modern', version: '4.0', extra_msg: 'Modern indexes (specs, latest_specs, and prerelease_specs) are always generated, so this option is not needed.')
|
||||
deprecate_option('--no-modern', version: '4.0', extra_msg: 'The `--no-modern` option is currently ignored. Modern indexes (specs, latest_specs, and prerelease_specs) are always generated.')
|
||||
|
||||
add_option '--update',
|
||||
'Update modern indexes with gems added',
|
||||
'since the last update' do |value, options|
|
||||
|
|
|
@ -98,7 +98,7 @@ class Gem::Commands::SetupCommand < Gem::Command
|
|||
end
|
||||
|
||||
def check_ruby_version
|
||||
required_version = Gem::Requirement.new '>= 1.8.7'
|
||||
required_version = Gem::Requirement.new '>= 2.3.0'
|
||||
|
||||
unless required_version.satisfied_by? Gem.ruby_version
|
||||
alert_error "Expected Ruby version #{required_version}, is #{Gem.ruby_version}"
|
||||
|
|
|
@ -43,6 +43,8 @@ class Gem::Commands::SourcesCommand < Gem::Command
|
|||
|
||||
source = Gem::Source.new source_uri
|
||||
|
||||
check_typo_squatting(source)
|
||||
|
||||
begin
|
||||
if Gem.sources.include? source
|
||||
say "source #{source_uri} already present in the cache"
|
||||
|
@ -62,6 +64,18 @@ class Gem::Commands::SourcesCommand < Gem::Command
|
|||
end
|
||||
end
|
||||
|
||||
def check_typo_squatting(source)
|
||||
if source.typo_squatting?("rubygems.org")
|
||||
question = <<-QUESTION.chomp
|
||||
#{source.uri.to_s} is too similar to https://rubygems.org
|
||||
|
||||
Do you want to add this source?
|
||||
QUESTION
|
||||
|
||||
terminate_interaction 1 unless ask_yes_no question
|
||||
end
|
||||
end
|
||||
|
||||
def check_rubygems_https(source_uri) # :nodoc:
|
||||
uri = URI source_uri
|
||||
|
||||
|
|
|
@ -6,13 +6,17 @@ if RUBY_VERSION >= "2.5"
|
|||
module Kernel
|
||||
path = "#{__dir__}/" # Frames to be skipped start with this path.
|
||||
|
||||
# Suppress "method redefined" warning
|
||||
original_warn = instance_method(:warn)
|
||||
Module.new {define_method(:warn, original_warn)}
|
||||
|
||||
original_warn = method(:warn)
|
||||
|
||||
module_function define_method(:warn) {|*messages, **kw|
|
||||
remove_method :warn
|
||||
|
||||
class << self
|
||||
|
||||
remove_method :warn
|
||||
|
||||
end
|
||||
|
||||
define_method(:warn) do |*messages, **kw|
|
||||
unless uplevel = kw[:uplevel]
|
||||
if Gem.java_platform?
|
||||
return original_warn.call(*messages)
|
||||
|
@ -46,6 +50,6 @@ if RUBY_VERSION >= "2.5"
|
|||
|
||||
kw[:uplevel] = uplevel
|
||||
original_warn.call(*messages, **kw)
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
#++
|
||||
|
||||
require 'rubygems/user_interaction'
|
||||
require "open3"
|
||||
|
||||
class Gem::Ext::Builder
|
||||
|
||||
|
@ -68,6 +67,7 @@ class Gem::Ext::Builder
|
|||
results << "current directory: #{Dir.pwd}"
|
||||
results << (command.respond_to?(:shelljoin) ? command.shelljoin : command)
|
||||
|
||||
require "open3"
|
||||
output, status = Open3.capture2e(*command)
|
||||
if verbose
|
||||
puts output
|
||||
|
|
|
@ -4,6 +4,7 @@ require 'rubygems/request'
|
|||
require 'rubygems/request/connection_pools'
|
||||
require 'rubygems/s3_uri_signer'
|
||||
require 'rubygems/uri_formatter'
|
||||
require 'rubygems/uri_parsing'
|
||||
require 'rubygems/user_interaction'
|
||||
require 'resolv'
|
||||
require 'rubygems/deprecate'
|
||||
|
@ -17,12 +18,16 @@ class Gem::RemoteFetcher
|
|||
include Gem::UserInteraction
|
||||
extend Gem::Deprecate
|
||||
|
||||
include Gem::UriParsing
|
||||
|
||||
##
|
||||
# A FetchError exception wraps up the various possible IO and HTTP failures
|
||||
# that could happen while downloading from the internet.
|
||||
|
||||
class FetchError < Gem::Exception
|
||||
|
||||
include Gem::UriParsing
|
||||
|
||||
##
|
||||
# The URI which was being accessed when the exception happened.
|
||||
|
||||
|
@ -30,13 +35,12 @@ class Gem::RemoteFetcher
|
|||
|
||||
def initialize(message, uri)
|
||||
super message
|
||||
begin
|
||||
uri = URI(uri)
|
||||
uri.password = 'REDACTED' if uri.password
|
||||
@uri = uri.to_s
|
||||
rescue URI::InvalidURIError, ArgumentError
|
||||
@uri = uri
|
||||
end
|
||||
|
||||
uri = parse_uri(uri)
|
||||
|
||||
uri.password = 'REDACTED' if uri.respond_to?(:password) && uri.password
|
||||
|
||||
@uri = uri.to_s
|
||||
end
|
||||
|
||||
def to_s # :nodoc:
|
||||
|
@ -107,7 +111,7 @@ class Gem::RemoteFetcher
|
|||
|
||||
spec, source = found.max_by { |(s,_)| s.version }
|
||||
|
||||
download spec, source.uri.to_s
|
||||
download spec, source.uri
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -130,18 +134,7 @@ class Gem::RemoteFetcher
|
|||
|
||||
FileUtils.mkdir_p cache_dir rescue nil unless File.exist? cache_dir
|
||||
|
||||
# Always escape URI's to deal with potential spaces and such
|
||||
# It should also be considered that source_uri may already be
|
||||
# a valid URI with escaped characters. e.g. "{DESede}" is encoded
|
||||
# as "%7BDESede%7D". If this is escaped again the percentage
|
||||
# symbols will be escaped.
|
||||
unless source_uri.is_a?(URI::Generic)
|
||||
begin
|
||||
source_uri = URI.parse(source_uri)
|
||||
rescue
|
||||
source_uri = URI.parse(URI::DEFAULT_PARSER.escape(source_uri.to_s))
|
||||
end
|
||||
end
|
||||
source_uri = parse_uri(source_uri)
|
||||
|
||||
scheme = source_uri.scheme
|
||||
|
||||
|
@ -159,7 +152,7 @@ class Gem::RemoteFetcher
|
|||
remote_gem_path = source_uri + "gems/#{gem_file_name}"
|
||||
|
||||
self.cache_update_path remote_gem_path, local_gem_path
|
||||
rescue Gem::RemoteFetcher::FetchError
|
||||
rescue FetchError
|
||||
raise if spec.original_platform == spec.platform
|
||||
|
||||
alternate_name = "#{spec.original_name}.gem"
|
||||
|
@ -236,7 +229,7 @@ class Gem::RemoteFetcher
|
|||
unless location = response['Location']
|
||||
raise FetchError.new("redirecting but no redirect location was given", uri)
|
||||
end
|
||||
location = URI.parse response['Location']
|
||||
location = parse_uri location
|
||||
|
||||
if https?(uri) && !https?(location)
|
||||
raise FetchError.new("redirecting to non-https resource: #{location}", uri)
|
||||
|
@ -254,9 +247,7 @@ class Gem::RemoteFetcher
|
|||
# Downloads +uri+ and returns it as a String.
|
||||
|
||||
def fetch_path(uri, mtime = nil, head = false)
|
||||
uri = URI.parse uri unless URI::Generic === uri
|
||||
|
||||
raise ArgumentError, "bad uri: #{uri}" unless uri
|
||||
uri = parse_uri uri
|
||||
|
||||
unless uri.scheme
|
||||
raise ArgumentError, "uri scheme is invalid: #{uri.scheme.inspect}"
|
||||
|
@ -268,21 +259,19 @@ class Gem::RemoteFetcher
|
|||
begin
|
||||
data = Gem::Util.gunzip data
|
||||
rescue Zlib::GzipFile::Error
|
||||
raise FetchError.new("server did not return a valid file", uri.to_s)
|
||||
raise FetchError.new("server did not return a valid file", uri)
|
||||
end
|
||||
end
|
||||
|
||||
data
|
||||
rescue FetchError
|
||||
raise
|
||||
rescue Timeout::Error
|
||||
raise UnknownHostError.new('timed out', uri.to_s)
|
||||
raise UnknownHostError.new('timed out', uri)
|
||||
rescue IOError, SocketError, SystemCallError,
|
||||
*(OpenSSL::SSL::SSLError if defined?(OpenSSL)) => e
|
||||
if e.message =~ /getaddrinfo/
|
||||
raise UnknownHostError.new('no such name', uri.to_s)
|
||||
raise UnknownHostError.new('no such name', uri)
|
||||
else
|
||||
raise FetchError.new("#{e.class}: #{e}", uri.to_s)
|
||||
raise FetchError.new("#{e.class}: #{e}", uri)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ class Gem::Request
|
|||
end
|
||||
|
||||
def self.proxy_uri(proxy) # :nodoc:
|
||||
require "uri"
|
||||
case proxy
|
||||
when :no_proxy then nil
|
||||
when URI::HTTP then proxy
|
||||
|
@ -173,6 +174,7 @@ class Gem::Request
|
|||
:no_proxy : get_proxy_from_env('http')
|
||||
end
|
||||
|
||||
require "uri"
|
||||
uri = URI(Gem::UriFormatter.new(env_proxy).normalize)
|
||||
|
||||
if uri and uri.user.nil? and uri.password.nil?
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
autoload :FileUtils, 'fileutils'
|
||||
autoload :URI, 'uri'
|
||||
|
||||
require "rubygems/text"
|
||||
##
|
||||
# A Source knows how to list and fetch gems from a RubyGems marshal index.
|
||||
#
|
||||
|
@ -11,6 +11,7 @@ autoload :URI, 'uri'
|
|||
class Gem::Source
|
||||
|
||||
include Comparable
|
||||
include Gem::Text
|
||||
|
||||
FILES = { # :nodoc:
|
||||
:released => 'specs',
|
||||
|
@ -219,6 +220,11 @@ class Gem::Source
|
|||
end
|
||||
end
|
||||
|
||||
def typo_squatting?(host, distance_threshold=4)
|
||||
return if @uri.host.nil?
|
||||
levenshtein_distance(@uri.host, host) <= distance_threshold
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
require 'rubygems/source/git'
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
require 'delegate'
|
||||
require 'uri'
|
||||
require 'rubygems/user_interaction'
|
||||
|
||||
class Gem::SpecificationPolicy < SimpleDelegator
|
||||
class Gem::SpecificationPolicy
|
||||
|
||||
include Gem::UserInteraction
|
||||
|
||||
|
@ -25,7 +23,7 @@ class Gem::SpecificationPolicy < SimpleDelegator
|
|||
def initialize(specification)
|
||||
@warnings = 0
|
||||
|
||||
super(specification)
|
||||
@specification = specification
|
||||
end
|
||||
|
||||
##
|
||||
|
@ -51,7 +49,7 @@ class Gem::SpecificationPolicy < SimpleDelegator
|
|||
|
||||
validate_require_paths
|
||||
|
||||
keep_only_files_and_directories
|
||||
@specification.keep_only_files_and_directories
|
||||
|
||||
validate_non_files
|
||||
|
||||
|
@ -92,6 +90,8 @@ class Gem::SpecificationPolicy < SimpleDelegator
|
|||
# Implementation for Specification#validate_metadata
|
||||
|
||||
def validate_metadata
|
||||
metadata = @specification.metadata
|
||||
|
||||
unless Hash === metadata
|
||||
error 'metadata must be a hash'
|
||||
end
|
||||
|
@ -130,7 +130,7 @@ class Gem::SpecificationPolicy < SimpleDelegator
|
|||
|
||||
error_messages = []
|
||||
warning_messages = []
|
||||
dependencies.each do |dep|
|
||||
@specification.dependencies.each do |dep|
|
||||
if prev = seen[dep.type][dep.name]
|
||||
error_messages << <<-MESSAGE
|
||||
duplicate dependency on #{dep}, (#{prev.requirement}) use:
|
||||
|
@ -145,7 +145,7 @@ duplicate dependency on #{dep}, (#{prev.requirement}) use:
|
|||
end
|
||||
|
||||
warning_messages << "prerelease dependency on #{dep} is not recommended" if
|
||||
prerelease_dep && !version.prerelease?
|
||||
prerelease_dep && !@specification.version.prerelease?
|
||||
|
||||
open_ended = dep.requirement.requirements.all? do |op, version|
|
||||
not version.prerelease? and (op == '>' or op == '>=')
|
||||
|
@ -190,14 +190,14 @@ duplicate dependency on #{dep}, (#{prev.requirement}) use:
|
|||
def validate_permissions
|
||||
return if Gem.win_platform?
|
||||
|
||||
files.each do |file|
|
||||
@specification.files.each do |file|
|
||||
next unless File.file?(file)
|
||||
next if File.stat(file).mode & 0444 == 0444
|
||||
warning "#{file} is not world-readable"
|
||||
end
|
||||
|
||||
executables.each do |name|
|
||||
exec = File.join bindir, name
|
||||
@specification.executables.each do |name|
|
||||
exec = File.join @specification.bindir, name
|
||||
next unless File.file?(exec)
|
||||
next if File.stat(exec).executable?
|
||||
warning "#{exec} is not executable"
|
||||
|
@ -208,7 +208,7 @@ duplicate dependency on #{dep}, (#{prev.requirement}) use:
|
|||
|
||||
def validate_nil_attributes
|
||||
nil_attributes = Gem::Specification.non_nil_attributes.select do |attrname|
|
||||
__getobj__.instance_variable_get("@#{attrname}").nil?
|
||||
@specification.instance_variable_get("@#{attrname}").nil?
|
||||
end
|
||||
return if nil_attributes.empty?
|
||||
error "#{nil_attributes.join ', '} must not be nil"
|
||||
|
@ -216,6 +216,9 @@ duplicate dependency on #{dep}, (#{prev.requirement}) use:
|
|||
|
||||
def validate_rubygems_version
|
||||
return unless packaging
|
||||
|
||||
rubygems_version = @specification.rubygems_version
|
||||
|
||||
return if rubygems_version == Gem::VERSION
|
||||
|
||||
error "expected RubyGems version #{Gem::VERSION}, was #{rubygems_version}"
|
||||
|
@ -223,13 +226,15 @@ duplicate dependency on #{dep}, (#{prev.requirement}) use:
|
|||
|
||||
def validate_required_attributes
|
||||
Gem::Specification.required_attributes.each do |symbol|
|
||||
unless send symbol
|
||||
unless @specification.send symbol
|
||||
error "missing value for attribute #{symbol}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def validate_name
|
||||
name = @specification.name
|
||||
|
||||
if !name.is_a?(String)
|
||||
error "invalid value for attribute name: \"#{name.inspect}\" must be a string"
|
||||
elsif name !~ /[a-zA-Z]/
|
||||
|
@ -242,14 +247,15 @@ duplicate dependency on #{dep}, (#{prev.requirement}) use:
|
|||
end
|
||||
|
||||
def validate_require_paths
|
||||
return unless raw_require_paths.empty?
|
||||
return unless @specification.raw_require_paths.empty?
|
||||
|
||||
error 'specification must have at least one require_path'
|
||||
end
|
||||
|
||||
def validate_non_files
|
||||
return unless packaging
|
||||
non_files = files.reject {|x| File.file?(x) || File.symlink?(x)}
|
||||
|
||||
non_files = @specification.files.reject {|x| File.file?(x) || File.symlink?(x)}
|
||||
|
||||
unless non_files.empty?
|
||||
error "[\"#{non_files.join "\", \""}\"] are not files"
|
||||
|
@ -257,18 +263,22 @@ duplicate dependency on #{dep}, (#{prev.requirement}) use:
|
|||
end
|
||||
|
||||
def validate_self_inclusion_in_files_list
|
||||
return unless files.include?(file_name)
|
||||
file_name = @specification.file_name
|
||||
|
||||
error "#{full_name} contains itself (#{file_name}), check your files list"
|
||||
return unless @specification.files.include?(file_name)
|
||||
|
||||
error "#{@specification.full_name} contains itself (#{file_name}), check your files list"
|
||||
end
|
||||
|
||||
def validate_specification_version
|
||||
return if specification_version.is_a?(Integer)
|
||||
return if @specification.specification_version.is_a?(Integer)
|
||||
|
||||
error 'specification_version must be an Integer (did you mean version?)'
|
||||
end
|
||||
|
||||
def validate_platform
|
||||
platform = @specification.platform
|
||||
|
||||
case platform
|
||||
when Gem::Platform, Gem::Platform::RUBY # ok
|
||||
else
|
||||
|
@ -283,7 +293,7 @@ duplicate dependency on #{dep}, (#{prev.requirement}) use:
|
|||
end
|
||||
|
||||
def validate_array_attribute(field)
|
||||
val = self.send(field)
|
||||
val = @specification.send(field)
|
||||
klass = case field
|
||||
when :dependencies then
|
||||
Gem::Dependency
|
||||
|
@ -298,12 +308,14 @@ duplicate dependency on #{dep}, (#{prev.requirement}) use:
|
|||
end
|
||||
|
||||
def validate_authors_field
|
||||
return unless authors.empty?
|
||||
return unless @specification.authors.empty?
|
||||
|
||||
error "authors may not be empty"
|
||||
end
|
||||
|
||||
def validate_licenses
|
||||
licenses = @specification.licenses
|
||||
|
||||
licenses.each do |license|
|
||||
if license.length > 64
|
||||
error "each license must be 64 characters or less"
|
||||
|
@ -331,24 +343,27 @@ http://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard li
|
|||
HOMEPAGE_URI_PATTERN = /\A[a-z][a-z\d+.-]*:/i.freeze
|
||||
|
||||
def validate_lazy_metadata
|
||||
unless authors.grep(LAZY_PATTERN).empty?
|
||||
unless @specification.authors.grep(LAZY_PATTERN).empty?
|
||||
error "#{LAZY} is not an author"
|
||||
end
|
||||
|
||||
unless Array(email).grep(LAZY_PATTERN).empty?
|
||||
unless Array(@specification.email).grep(LAZY_PATTERN).empty?
|
||||
error "#{LAZY} is not an email"
|
||||
end
|
||||
|
||||
if description =~ LAZY_PATTERN
|
||||
if @specification.description =~ LAZY_PATTERN
|
||||
error "#{LAZY} is not a description"
|
||||
end
|
||||
|
||||
if summary =~ LAZY_PATTERN
|
||||
if @specification.summary =~ LAZY_PATTERN
|
||||
error "#{LAZY} is not a summary"
|
||||
end
|
||||
|
||||
homepage = @specification.homepage
|
||||
|
||||
# Make sure a homepage is valid HTTP/HTTPS URI
|
||||
if homepage and not homepage.empty?
|
||||
require 'uri'
|
||||
begin
|
||||
homepage_uri = URI.parse(homepage)
|
||||
unless [URI::HTTP, URI::HTTPS].member? homepage_uri.class
|
||||
|
@ -365,29 +380,29 @@ http://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard li
|
|||
validate_attribute_present(attribute)
|
||||
end
|
||||
|
||||
if description == summary
|
||||
if @specification.description == @specification.summary
|
||||
warning "description and summary are identical"
|
||||
end
|
||||
|
||||
# TODO: raise at some given date
|
||||
warning "deprecated autorequire specified" if autorequire
|
||||
warning "deprecated autorequire specified" if @specification.autorequire
|
||||
|
||||
executables.each do |executable|
|
||||
@specification.executables.each do |executable|
|
||||
validate_shebang_line_in(executable)
|
||||
end
|
||||
|
||||
files.select { |f| File.symlink?(f) }.each do |file|
|
||||
@specification.files.select { |f| File.symlink?(f) }.each do |file|
|
||||
warning "#{file} is a symlink, which is not supported on all platforms"
|
||||
end
|
||||
end
|
||||
|
||||
def validate_attribute_present(attribute)
|
||||
value = self.send attribute
|
||||
value = @specification.send attribute
|
||||
warning("no #{attribute} specified") if value.nil? || value.empty?
|
||||
end
|
||||
|
||||
def validate_shebang_line_in(executable)
|
||||
executable_path = File.join(bindir, executable)
|
||||
executable_path = File.join(@specification.bindir, executable)
|
||||
return if File.read(executable_path, 2) == '#!'
|
||||
|
||||
warning "#{executable_path} is missing #! line"
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
require 'cgi'
|
||||
require 'uri'
|
||||
|
||||
##
|
||||
# The UriFormatter handles URIs from user-input and escaping.
|
||||
|
|
36
lib/rubygems/uri_parser.rb
Normal file
36
lib/rubygems/uri_parser.rb
Normal file
|
@ -0,0 +1,36 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
##
|
||||
# The UriParser handles parsing URIs.
|
||||
#
|
||||
|
||||
class Gem::UriParser
|
||||
|
||||
##
|
||||
# Parses the #uri, raising if it's invalid
|
||||
|
||||
def parse!(uri)
|
||||
raise URI::InvalidURIError unless uri
|
||||
|
||||
# Always escape URI's to deal with potential spaces and such
|
||||
# It should also be considered that source_uri may already be
|
||||
# a valid URI with escaped characters. e.g. "{DESede}" is encoded
|
||||
# as "%7BDESede%7D". If this is escaped again the percentage
|
||||
# symbols will be escaped.
|
||||
begin
|
||||
URI.parse(uri)
|
||||
rescue URI::InvalidURIError
|
||||
URI.parse(URI::DEFAULT_PARSER.escape(uri))
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# Parses the #uri, returning the original uri if it's invalid
|
||||
|
||||
def parse(uri)
|
||||
parse!(uri)
|
||||
rescue URI::InvalidURIError
|
||||
uri
|
||||
end
|
||||
|
||||
end
|
23
lib/rubygems/uri_parsing.rb
Normal file
23
lib/rubygems/uri_parsing.rb
Normal file
|
@ -0,0 +1,23 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "rubygems/uri_parser"
|
||||
|
||||
module Gem::UriParsing
|
||||
|
||||
def parse_uri(source_uri)
|
||||
return source_uri unless source_uri.is_a?(String)
|
||||
|
||||
uri_parser.parse(source_uri)
|
||||
end
|
||||
|
||||
private :parse_uri
|
||||
|
||||
def uri_parser
|
||||
require "uri"
|
||||
|
||||
Gem::UriParser.new
|
||||
end
|
||||
|
||||
private :uri_parser
|
||||
|
||||
end
|
|
@ -197,9 +197,9 @@ class TestGemCommand < Gem::TestCase
|
|||
assert_equal ['-h', 'command'], args
|
||||
end
|
||||
|
||||
def test_deprecate_option_long_name
|
||||
def test_deprecate_option
|
||||
deprecate_msg = <<-EXPECTED
|
||||
WARNING: The \"--test\" option has been deprecated and will be removed in Rubygems 3.1, its use is discouraged.
|
||||
WARNING: The \"--test\" option has been deprecated and will be removed in Rubygems 3.1.
|
||||
EXPECTED
|
||||
|
||||
testCommand = Class.new(Gem::Command) do
|
||||
|
@ -210,7 +210,7 @@ WARNING: The \"--test\" option has been deprecated and will be removed in Rubyg
|
|||
options[:test] = true
|
||||
end
|
||||
|
||||
deprecate_option(long_name: '--test', version: '3.1')
|
||||
deprecate_option('--test', version: '3.1')
|
||||
end
|
||||
|
||||
def execute
|
||||
|
@ -228,7 +228,7 @@ WARNING: The \"--test\" option has been deprecated and will be removed in Rubyg
|
|||
|
||||
def test_deprecate_option_no_version
|
||||
deprecate_msg = <<-EXPECTED
|
||||
WARNING: The \"--test\" option has been deprecated and will be removed in future versions of Rubygems, its use is discouraged.
|
||||
WARNING: The \"--test\" option has been deprecated and will be removed in future versions of Rubygems.
|
||||
EXPECTED
|
||||
|
||||
testCommand = Class.new(Gem::Command) do
|
||||
|
@ -239,7 +239,7 @@ WARNING: The \"--test\" option has been deprecated and will be removed in futur
|
|||
options[:test] = true
|
||||
end
|
||||
|
||||
deprecate_option(long_name: '--test')
|
||||
deprecate_option('--test')
|
||||
end
|
||||
|
||||
def execute
|
||||
|
@ -255,9 +255,9 @@ WARNING: The \"--test\" option has been deprecated and will be removed in futur
|
|||
end
|
||||
end
|
||||
|
||||
def test_deprecate_option_short_name
|
||||
def test_deprecate_option_extra_message
|
||||
deprecate_msg = <<-EXPECTED
|
||||
WARNING: The \"-t\" option has been deprecated and will be removed in Rubygems 3.5, its use is discouraged.
|
||||
WARNING: The \"--test\" option has been deprecated and will be removed in Rubygems 3.1. Whether you set `--test` mode or not, this dummy app always runs in test mode.
|
||||
EXPECTED
|
||||
|
||||
testCommand = Class.new(Gem::Command) do
|
||||
|
@ -268,7 +268,7 @@ WARNING: The \"-t\" option has been deprecated and will be removed in Rubygems
|
|||
options[:test] = true
|
||||
end
|
||||
|
||||
deprecate_option(short_name: '-t', version: '3.5')
|
||||
deprecate_option('--test', version: '3.1', extra_msg: 'Whether you set `--test` mode or not, this dummy app always runs in test mode.')
|
||||
end
|
||||
|
||||
def execute
|
||||
|
@ -279,7 +279,36 @@ WARNING: The \"-t\" option has been deprecated and will be removed in Rubygems
|
|||
cmd = testCommand.new
|
||||
|
||||
use_ui @ui do
|
||||
cmd.invoke("-t")
|
||||
cmd.invoke("--test")
|
||||
assert_equal deprecate_msg, @ui.error
|
||||
end
|
||||
end
|
||||
|
||||
def test_deprecate_option_extra_message_and_no_version
|
||||
deprecate_msg = <<-EXPECTED
|
||||
WARNING: The \"--test\" option has been deprecated and will be removed in future versions of Rubygems. Whether you set `--test` mode or not, this dummy app always runs in test mode.
|
||||
EXPECTED
|
||||
|
||||
testCommand = Class.new(Gem::Command) do
|
||||
def initialize
|
||||
super('test', 'Gem::Command instance for testing')
|
||||
|
||||
add_option('-t', '--test', 'Test command') do |value, options|
|
||||
options[:test] = true
|
||||
end
|
||||
|
||||
deprecate_option('--test', extra_msg: 'Whether you set `--test` mode or not, this dummy app always runs in test mode.')
|
||||
end
|
||||
|
||||
def execute
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
cmd = testCommand.new
|
||||
|
||||
use_ui @ui do
|
||||
cmd.invoke("--test")
|
||||
assert_equal deprecate_msg, @ui.error
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,6 +3,10 @@ require 'rubygems/test_case'
|
|||
require 'rubygems/indexer'
|
||||
require 'rubygems/commands/generate_index_command'
|
||||
|
||||
unless defined?(Builder::XChar)
|
||||
warn "generate_index tests are being skipped. Install builder gem."
|
||||
end
|
||||
|
||||
class TestGemCommandsGenerateIndexCommand < Gem::TestCase
|
||||
|
||||
def setup
|
||||
|
@ -22,6 +26,18 @@ class TestGemCommandsGenerateIndexCommand < Gem::TestCase
|
|||
assert File.exist?(specs), specs
|
||||
end
|
||||
|
||||
def test_execute_no_modern
|
||||
@cmd.options[:modern] = false
|
||||
|
||||
use_ui @ui do
|
||||
@cmd.execute
|
||||
end
|
||||
|
||||
specs = File.join @gemhome, "specs.4.8.gz"
|
||||
|
||||
assert File.exist?(specs), specs
|
||||
end
|
||||
|
||||
def test_handle_options_directory
|
||||
return if win_platform?
|
||||
refute_equal '/nonexistent', @cmd.options[:directory]
|
||||
|
@ -47,4 +63,24 @@ class TestGemCommandsGenerateIndexCommand < Gem::TestCase
|
|||
assert @cmd.options[:update]
|
||||
end
|
||||
|
||||
end if ''.respond_to? :to_xs
|
||||
def test_handle_options_modern
|
||||
use_ui @ui do
|
||||
@cmd.handle_options %w[--modern]
|
||||
end
|
||||
|
||||
assert_equal \
|
||||
"WARNING: The \"--modern\" option has been deprecated and will be removed in Rubygems 4.0. Modern indexes (specs, latest_specs, and prerelease_specs) are always generated, so this option is not needed.\n",
|
||||
@ui.error
|
||||
end
|
||||
|
||||
def test_handle_options_no_modern
|
||||
use_ui @ui do
|
||||
@cmd.handle_options %w[--no-modern]
|
||||
end
|
||||
|
||||
assert_equal \
|
||||
"WARNING: The \"--no-modern\" option has been deprecated and will be removed in Rubygems 4.0. The `--no-modern` option is currently ignored. Modern indexes (specs, latest_specs, and prerelease_specs) are always generated.\n",
|
||||
@ui.error
|
||||
end
|
||||
|
||||
end if defined?(Builder::XChar)
|
||||
|
|
|
@ -4,20 +4,15 @@ require "rubygems/test_case"
|
|||
require "rubygems/commands/help_command"
|
||||
require "rubygems/package"
|
||||
require "rubygems/command_manager"
|
||||
require File.expand_path('../rubygems_plugin', __FILE__)
|
||||
|
||||
class TestGemCommandsHelpCommand < Gem::TestCase
|
||||
|
||||
# previously this was calc'd in setup, but 1.8.7 had
|
||||
# intermittent failures, but no issues with above require
|
||||
PLUGIN = File.expand_path('../rubygems_plugin.rb', __FILE__)
|
||||
|
||||
def setup
|
||||
super
|
||||
|
||||
@cmd = Gem::Commands::HelpCommand.new
|
||||
|
||||
load PLUGIN unless Gem::Commands.const_defined? :InterruptCommand
|
||||
load File.expand_path('../rubygems_plugin.rb', __FILE__) unless Gem::Commands.const_defined? :InterruptCommand
|
||||
end
|
||||
|
||||
def test_gem_help_bad
|
||||
|
|
|
@ -74,6 +74,80 @@ class TestGemCommandsSourcesCommand < Gem::TestCase
|
|||
assert_equal '', @ui.error
|
||||
end
|
||||
|
||||
def test_execute_add_allow_typo_squatting_source
|
||||
rubygems_org = "https://rubyems.org"
|
||||
|
||||
spec_fetcher do |fetcher|
|
||||
fetcher.spec("a", 1)
|
||||
end
|
||||
|
||||
specs = Gem::Specification.map do |spec|
|
||||
[spec.name, spec.version, spec.original_platform]
|
||||
end
|
||||
|
||||
specs_dump_gz = StringIO.new
|
||||
Zlib::GzipWriter.wrap(specs_dump_gz) do |io|
|
||||
Marshal.dump(specs, io)
|
||||
end
|
||||
|
||||
@fetcher.data["#{rubygems_org}/specs.#{@marshal_version}.gz"] = specs_dump_gz.string
|
||||
@cmd.handle_options %W[--add #{rubygems_org}]
|
||||
ui = Gem::MockGemUi.new("y")
|
||||
|
||||
use_ui ui do
|
||||
@cmd.execute
|
||||
end
|
||||
|
||||
expected = "https://rubyems.org is too similar to https://rubygems.org\n\nDo you want to add this source? [yn] https://rubyems.org added to sources\n"
|
||||
|
||||
assert_equal expected, ui.output
|
||||
|
||||
source = Gem::Source.new(rubygems_org)
|
||||
assert Gem.sources.include?(source)
|
||||
|
||||
assert_empty ui.error
|
||||
end
|
||||
|
||||
def test_execute_add_deny_typo_squatting_source
|
||||
rubygems_org = "https://rubyems.org"
|
||||
|
||||
spec_fetcher do |fetcher|
|
||||
fetcher.spec("a", 1)
|
||||
end
|
||||
|
||||
specs = Gem::Specification.map do |spec|
|
||||
[spec.name, spec.version, spec.original_platform]
|
||||
end
|
||||
|
||||
specs_dump_gz = StringIO.new
|
||||
Zlib::GzipWriter.wrap(specs_dump_gz) do |io|
|
||||
Marshal.dump(specs, io)
|
||||
end
|
||||
|
||||
@fetcher.data["#{rubygems_org}/specs.#{@marshal_version}.gz"] =
|
||||
specs_dump_gz.string
|
||||
|
||||
@cmd.handle_options %W[--add #{rubygems_org}]
|
||||
|
||||
ui = Gem::MockGemUi.new("n")
|
||||
|
||||
use_ui ui do
|
||||
|
||||
assert_raises Gem::MockGemUi::TermError do
|
||||
@cmd.execute
|
||||
end
|
||||
end
|
||||
|
||||
expected = "https://rubyems.org is too similar to https://rubygems.org\n\nDo you want to add this source? [yn] "
|
||||
|
||||
assert_equal expected, ui.output
|
||||
|
||||
source = Gem::Source.new(rubygems_org)
|
||||
refute Gem.sources.include?(source)
|
||||
|
||||
assert_empty ui.error
|
||||
end
|
||||
|
||||
def test_execute_add_nonexistent_source
|
||||
spec_fetcher
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
require 'rubygems/test_case'
|
||||
require 'rubygems/gem_runner'
|
||||
|
||||
class TestGemGemRunner < Gem::TestCase
|
||||
|
||||
|
@ -8,6 +7,8 @@ class TestGemGemRunner < Gem::TestCase
|
|||
super
|
||||
|
||||
@orig_args = Gem::Command.build_args
|
||||
|
||||
require 'rubygems/gem_runner'
|
||||
@runner = Gem::GemRunner.new
|
||||
end
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ require 'rubygems/test_case'
|
|||
require 'rubygems/indexer'
|
||||
|
||||
unless defined?(Builder::XChar)
|
||||
warn "Gem::Indexer tests are being skipped. Install builder gem." if $VERBOSE
|
||||
warn "Gem::Indexer tests are being skipped. Install builder gem."
|
||||
end
|
||||
|
||||
class TestGemIndexer < Gem::TestCase
|
||||
|
|
|
@ -235,4 +235,18 @@ class TestGemSource < Gem::TestCase
|
|||
refute @source.update_cache?
|
||||
end
|
||||
|
||||
def test_typo_squatting
|
||||
rubygems_source = Gem::Source.new("https://rubgems.org")
|
||||
assert rubygems_source.typo_squatting?("rubygems.org")
|
||||
assert rubygems_source.typo_squatting?("rubyagems.org")
|
||||
assert rubygems_source.typo_squatting?("rubyasgems.org")
|
||||
refute rubygems_source.typo_squatting?("rubysertgems.org")
|
||||
end
|
||||
|
||||
def test_typo_squatting_custom_distance_threshold
|
||||
rubygems_source = Gem::Source.new("https://rubgems.org")
|
||||
distance_threshold = 5
|
||||
assert rubygems_source.typo_squatting?("rubysertgems.org", distance_threshold)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -5,7 +5,7 @@ class TestRemoteFetchError < Gem::TestCase
|
|||
|
||||
def test_password_redacted
|
||||
error = Gem::RemoteFetcher::FetchError.new('There was an error fetching', 'https://user:secret@gemsource.org')
|
||||
refute_match error.to_s, 'secret'
|
||||
refute_match 'secret', error.to_s
|
||||
end
|
||||
|
||||
def test_invalid_url
|
||||
|
|
|
@ -365,19 +365,16 @@ class TestGemRequire < Gem::TestCase
|
|||
end
|
||||
|
||||
def test_realworld_default_gem
|
||||
begin
|
||||
gem 'json'
|
||||
rescue Gem::MissingSpecError
|
||||
skip "default gems are only available after ruby installation"
|
||||
end
|
||||
testing_ruby_repo = !ENV["GEM_COMMAND"].nil?
|
||||
skip "this test can't work under ruby-core setup" if testing_ruby_repo || java_platform?
|
||||
|
||||
cmd = <<-RUBY
|
||||
$stderr = $stdout
|
||||
require "json"
|
||||
puts Gem.loaded_specs["json"].default_gem?
|
||||
puts Gem.loaded_specs["json"]
|
||||
RUBY
|
||||
output = Gem::Util.popen(Gem.ruby, "-e", cmd).strip
|
||||
assert_equal "true", output
|
||||
refute_empty output
|
||||
end
|
||||
|
||||
def test_default_gem_and_normal_gem
|
||||
|
@ -499,36 +496,38 @@ class TestGemRequire < Gem::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
# uplevel is 2.5+ only and jruby has some issues with it
|
||||
if RUBY_VERSION >= "2.5" && !java_platform?
|
||||
def test_no_kernel_require_in_warn_with_uplevel
|
||||
lib = File.realpath("../../../lib", __FILE__)
|
||||
Dir.mktmpdir("warn_test") do |dir|
|
||||
File.write(dir + "/sub.rb", "warn 'uplevel', 'test', uplevel: 1\n")
|
||||
File.write(dir + "/main.rb", "require 'sub'\n")
|
||||
_, err = capture_subprocess_io do
|
||||
system(@@ruby, "-w", "-rpp", "--disable=gems", "-I", lib, "-C", dir, "-I.", "main.rb")
|
||||
# uplevel is 2.5+ only
|
||||
if RUBY_VERSION >= "2.5"
|
||||
["", "Kernel."].each do |prefix|
|
||||
define_method "test_no_kernel_require_in_#{prefix.tr(".", "_")}warn_with_uplevel" do
|
||||
lib = File.realpath("../../../lib", __FILE__)
|
||||
Dir.mktmpdir("warn_test") do |dir|
|
||||
File.write(dir + "/sub.rb", "#{prefix}warn 'uplevel', 'test', uplevel: 1\n")
|
||||
File.write(dir + "/main.rb", "require 'sub'\n")
|
||||
_, err = capture_subprocess_io do
|
||||
system(@@ruby, "-w", "--disable=gems", "-I", lib, "-C", dir, "-I.", "main.rb")
|
||||
end
|
||||
assert_match(/main\.rb:1: warning: uplevel\ntest\n$/, err)
|
||||
_, err = capture_subprocess_io do
|
||||
system(@@ruby, "-w", "--enable=gems", "-I", lib, "-C", dir, "-I.", "main.rb")
|
||||
end
|
||||
assert_match(/main\.rb:1: warning: uplevel\ntest\n$/, err)
|
||||
end
|
||||
assert_equal "main.rb:1: warning: uplevel\ntest\n", err
|
||||
_, err = capture_subprocess_io do
|
||||
system(@@ruby, "-w", "-rpp", "--enable=gems", "-I", lib, "-C", dir, "-I.", "main.rb")
|
||||
end
|
||||
assert_equal "main.rb:1: warning: uplevel\ntest\n", err
|
||||
end
|
||||
end
|
||||
|
||||
def test_no_other_behavioral_changes_with_kernel_warn
|
||||
lib = File.realpath("../../../lib", __FILE__)
|
||||
Dir.mktmpdir("warn_test") do |dir|
|
||||
File.write(dir + "/main.rb", "warn({x:1}, {y:2}, [])\n")
|
||||
_, err = capture_subprocess_io do
|
||||
system(@@ruby, "-w", "-rpp", "--disable=gems", "-I", lib, "-C", dir, "-I.", "main.rb")
|
||||
define_method "test_no_other_behavioral_changes_with_#{prefix.tr(".", "_")}warn" do
|
||||
lib = File.realpath("../../../lib", __FILE__)
|
||||
Dir.mktmpdir("warn_test") do |dir|
|
||||
File.write(dir + "/main.rb", "#{prefix}warn({x:1}, {y:2}, [])\n")
|
||||
_, err = capture_subprocess_io do
|
||||
system(@@ruby, "-w", "--disable=gems", "-I", lib, "-C", dir, "main.rb")
|
||||
end
|
||||
assert_match(/{:x=>1}\n{:y=>2}\n$/, err)
|
||||
_, err = capture_subprocess_io do
|
||||
system(@@ruby, "-w", "--enable=gems", "-I", lib, "-C", dir, "main.rb")
|
||||
end
|
||||
assert_match(/{:x=>1}\n{:y=>2}\n$/, err)
|
||||
end
|
||||
assert_equal "{:x=>1}\n{:y=>2}\n", err
|
||||
_, err = capture_subprocess_io do
|
||||
system(@@ruby, "-w", "-rpp", "--enable=gems", "-I", lib, "-C", dir, "-I.", "main.rb")
|
||||
end
|
||||
assert_equal "{:x=>1}\n{:y=>2}\n", err
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue