summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorhsbt <hsbt@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-05-30 13:01:35 +0000
committerhsbt <hsbt@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2018-05-30 13:01:35 +0000
commit8da8d4b043c37b53a69803c71ff36b478d4776d0 (patch)
tree7c8cec15645e74f19c88e4eb5b210b96174c7d03 /lib
parentc5cb386eba6d9a2d9a8e6ffa8c30137d0c4660c1 (diff)
Merge RubyGems 3.0.0.beta1.
* It drop to support < Ruby 2.2 * Cleanup deprecated methods and classes. * Mark obsoleted methods to deprecate. * and other enhancements. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@63528 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib')
-rw-r--r--lib/rubygems.rb101
-rw-r--r--lib/rubygems/command.rb16
-rw-r--r--lib/rubygems/command_manager.rb1
-rw-r--r--lib/rubygems/commands/build_command.rb14
-rw-r--r--lib/rubygems/commands/environment_command.rb19
-rw-r--r--lib/rubygems/commands/info_command.rb33
-rw-r--r--lib/rubygems/commands/install_command.rb17
-rw-r--r--lib/rubygems/commands/pristine_command.rb11
-rw-r--r--lib/rubygems/commands/setup_command.rb68
-rw-r--r--lib/rubygems/commands/uninstall_command.rb33
-rw-r--r--lib/rubygems/commands/unpack_command.rb12
-rw-r--r--lib/rubygems/commands/update_command.rb15
-rw-r--r--lib/rubygems/compatibility.rb20
-rw-r--r--lib/rubygems/config_file.rb23
-rwxr-xr-xlib/rubygems/core_ext/kernel_require.rb9
-rw-r--r--lib/rubygems/defaults.rb21
-rw-r--r--lib/rubygems/dependency_installer.rb18
-rw-r--r--lib/rubygems/exceptions.rb7
-rw-r--r--lib/rubygems/ext/builder.rb15
-rw-r--r--lib/rubygems/ext/cmake_builder.rb2
-rw-r--r--lib/rubygems/ext/configure_builder.rb3
-rw-r--r--lib/rubygems/ext/ext_conf_builder.rb6
-rw-r--r--lib/rubygems/ext/rake_builder.rb3
-rw-r--r--lib/rubygems/installer.rb30
-rw-r--r--lib/rubygems/installer_test_case.rb13
-rw-r--r--lib/rubygems/local_remote_options.rb6
-rw-r--r--lib/rubygems/package.rb55
-rw-r--r--lib/rubygems/package/old.rb14
-rw-r--r--lib/rubygems/package/tar_header.rb4
-rw-r--r--lib/rubygems/package/tar_reader.rb2
-rw-r--r--lib/rubygems/package/tar_reader/entry.rb2
-rw-r--r--lib/rubygems/package/tar_test_case.rb8
-rw-r--r--lib/rubygems/package/tar_writer.rb12
-rw-r--r--lib/rubygems/rdoc.rb311
-rw-r--r--lib/rubygems/remote_fetcher.rb4
-rw-r--r--lib/rubygems/request/connection_pools.rb19
-rw-r--r--lib/rubygems/request_set.rb41
-rw-r--r--lib/rubygems/request_set/gem_dependency_api.rb4
-rw-r--r--lib/rubygems/request_set/lockfile/parser.rb25
-rw-r--r--lib/rubygems/requirement.rb17
-rw-r--r--lib/rubygems/resolver.rb7
-rw-r--r--lib/rubygems/resolver/api_specification.rb5
-rw-r--r--lib/rubygems/security/trust_dir.rb3
-rw-r--r--lib/rubygems/specification.rb433
-rw-r--r--lib/rubygems/specification_policy.rb410
-rw-r--r--lib/rubygems/stub_specification.rb8
-rw-r--r--lib/rubygems/test_case.rb36
-rw-r--r--lib/rubygems/uninstaller.rb35
-rw-r--r--lib/rubygems/user_interaction.rb34
-rw-r--r--lib/rubygems/util.rb14
-rw-r--r--lib/rubygems/util/licenses.rb67
51 files changed, 1009 insertions, 1077 deletions
diff --git a/lib/rubygems.rb b/lib/rubygems.rb
index 830c16b7d7..d263f29dd2 100644
--- a/lib/rubygems.rb
+++ b/lib/rubygems.rb
@@ -10,7 +10,7 @@ require 'rbconfig'
require 'thread'
module Gem
- VERSION = "2.7.7"
+ VERSION = "3.0.0.beta1"
end
# Must be first since it unloads the prelude from 1.9.2
@@ -247,7 +247,7 @@ module Gem
##
# Find the full path to the executable for gem +name+. If the +exec_name+
- # is not given, the gem's default_executable is chosen, otherwise the
+ # is not given, an exception will be raised, otherwise the
# specified executable's path is returned. +requirements+ allows
# you to specify specific gem versions.
@@ -295,7 +295,7 @@ module Gem
##
# Find the full path to the executable for gem +name+. If the +exec_name+
- # is not given, the gem's default_executable is chosen, otherwise the
+ # is not given, an exception will be raised, otherwise the
# specified executable's path is returned. +requirements+ allows
# you to specify specific gem versions.
#
@@ -373,11 +373,6 @@ module Gem
spec.datadir
end
- class << self
- extend Gem::Deprecate
- deprecate :datadir, "spec.datadir", 2016, 10
- end
-
##
# A Zlib::Deflate.deflate wrapper
@@ -582,20 +577,9 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
#++
def self.find_home
- windows = File::ALT_SEPARATOR
- if not windows or RUBY_VERSION >= '1.9' then
- File.expand_path "~"
- else
- ['HOME', 'USERPROFILE'].each do |key|
- return File.expand_path ENV[key] if ENV[key]
- end
-
- if ENV['HOMEDRIVE'] && ENV['HOMEPATH'] then
- File.expand_path "#{ENV['HOMEDRIVE']}#{ENV['HOMEPATH']}"
- end
- end
+ Dir.home
rescue
- if windows then
+ if Gem.win_platform? then
File.expand_path File.join(ENV['HOMEDRIVE'] || ENV['SystemDrive'], '/')
else
File.expand_path "/"
@@ -696,45 +680,32 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
return if @yaml_loaded
return unless defined?(gem)
- test_syck = ENV['TEST_SYCK']
-
- # Only Ruby 1.8 and 1.9 have syck
- test_syck = false unless /^1\./ =~ RUBY_VERSION
+ begin
+ gem 'psych', '>= 2.0.0'
+ rescue Gem::LoadError
+ # It's OK if the user does not have the psych gem installed. We will
+ # attempt to require the stdlib version
+ end
- unless test_syck
- begin
- gem 'psych', '>= 2.0.0'
- rescue Gem::LoadError
- # It's OK if the user does not have the psych gem installed. We will
- # attempt to require the stdlib version
+ begin
+ # Try requiring the gem version *or* stdlib version of psych.
+ require 'psych'
+ rescue ::LoadError
+ # If we can't load psych, thats fine, go on.
+ else
+ # If 'yaml' has already been required, then we have to
+ # be sure to switch it over to the newly loaded psych.
+ if defined?(YAML::ENGINE) && YAML::ENGINE.yamler != "psych"
+ YAML::ENGINE.yamler = "psych"
end
- begin
- # Try requiring the gem version *or* stdlib version of psych.
- require 'psych'
- rescue ::LoadError
- # If we can't load psych, thats fine, go on.
- else
- # If 'yaml' has already been required, then we have to
- # be sure to switch it over to the newly loaded psych.
- if defined?(YAML::ENGINE) && YAML::ENGINE.yamler != "psych"
- YAML::ENGINE.yamler = "psych"
- end
-
- require 'rubygems/psych_additions'
- require 'rubygems/psych_tree'
- end
+ require 'rubygems/psych_additions'
+ require 'rubygems/psych_tree'
end
require 'yaml'
require 'rubygems/safe_yaml'
- # If we're supposed to be using syck, then we may have to force
- # activate it via the YAML::ENGINE API.
- if test_syck and defined?(YAML::ENGINE)
- YAML::ENGINE.yamler = "syck" unless YAML::ENGINE.syck?
- end
-
# Now that we're sure some kind of yaml library is loaded, pull
# in our hack to deal with Syck's DefaultKey ugliness.
require 'rubygems/syck_hack'
@@ -1376,7 +1347,6 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
autoload :ConfigFile, 'rubygems/config_file'
autoload :Dependency, 'rubygems/dependency'
autoload :DependencyList, 'rubygems/dependency_list'
- autoload :DependencyResolver, 'rubygems/resolver'
autoload :Installer, 'rubygems/installer'
autoload :Licenses, 'rubygems/util/licenses'
autoload :PathSupport, 'rubygems/path_support'
@@ -1397,25 +1367,22 @@ 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
+ ##
+ # Defaults the operating system (or packager) wants to provide for RubyGems.
+
+ require 'rubygems/defaults/operating_system'
+rescue LoadError
+end
+
+if defined?(RUBY_ENGINE) then
begin
##
- # Defaults the operating system (or packager) wants to provide for RubyGems.
+ # Defaults the Ruby implementation wants to provide for RubyGems
- require 'rubygems/defaults/operating_system'
+ require "rubygems/defaults/#{RUBY_ENGINE}"
rescue LoadError
end
-
- if defined?(RUBY_ENGINE) then
- begin
- ##
- # Defaults the Ruby implementation wants to provide for RubyGems
-
- require "rubygems/defaults/#{RUBY_ENGINE}"
- rescue LoadError
- end
- end
end
##
diff --git a/lib/rubygems/command.rb b/lib/rubygems/command.rb
index a7ec212e51..71199c59b4 100644
--- a/lib/rubygems/command.rb
+++ b/lib/rubygems/command.rb
@@ -152,15 +152,23 @@ class Gem::Command
#--
# TODO: replace +domain+ with a parameter to suppress suggestions
- def show_lookup_failure(gem_name, version, errors, domain)
+ def show_lookup_failure(gem_name, version, errors, domain, required_by = nil)
+ gem = "'#{gem_name}' (#{version})"
+ msg = String.new "Could not find a valid gem #{gem}"
+
if errors and !errors.empty?
- msg = "Could not find a valid gem '#{gem_name}' (#{version}), here is why:\n".dup
+ msg << ", 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"
+ if required_by and gem != required_by then
+ msg << " (required by #{required_by}) in any repository"
+ else
+ msg << " in any repository"
+ end
end
+ alert_error msg
+
unless domain == :local then # HACK
suggestions = Gem::SpecFetcher.fetcher.suggest_gems_from_name gem_name
diff --git a/lib/rubygems/command_manager.rb b/lib/rubygems/command_manager.rb
index 887272378e..e22dc5deb3 100644
--- a/lib/rubygems/command_manager.rb
+++ b/lib/rubygems/command_manager.rb
@@ -45,6 +45,7 @@ class Gem::CommandManager
:fetch,
:generate_index,
:help,
+ :info,
:install,
:list,
:lock,
diff --git a/lib/rubygems/commands/build_command.rb b/lib/rubygems/commands/build_command.rb
index 38c45e46f0..0ba24e5ea3 100644
--- a/lib/rubygems/commands/build_command.rb
+++ b/lib/rubygems/commands/build_command.rb
@@ -47,13 +47,15 @@ with gem spec:
end
if File.exist? gemspec then
- spec = Gem::Specification.load gemspec
+ Dir.chdir(File.dirname(gemspec)) do
+ 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
+ if spec then
+ Gem::Package.build spec, options[:force]
+ else
+ alert_error "Error loading gemspec. Aborting."
+ terminate_interaction 1
+ end
end
else
alert_error "Gemspec file not found: #{gemspec}"
diff --git a/lib/rubygems/commands/environment_command.rb b/lib/rubygems/commands/environment_command.rb
index e825c761ad..29c9d4d38f 100644
--- a/lib/rubygems/commands/environment_command.rb
+++ b/lib/rubygems/commands/environment_command.rb
@@ -120,6 +120,8 @@ lib/rubygems/defaults/operating_system.rb
out << " - RUBY EXECUTABLE: #{Gem.ruby}\n"
+ out << " - GIT EXECUTABLE: #{git_path}\n"
+
out << " - EXECUTABLE DIRECTORY: #{Gem.bindir}\n"
out << " - SPEC CACHE DIRECTORY: #{Gem.spec_cache_dir}\n"
@@ -157,4 +159,21 @@ lib/rubygems/defaults/operating_system.rb
out
end
+ private
+
+ ##
+ # Git binary path
+
+ def git_path
+ exts = ENV["PATHEXT"] ? ENV["PATHEXT"].split(";") : [""]
+ ENV["PATH"].split(File::PATH_SEPARATOR).each do |path|
+ exts.each do |ext|
+ exe = File.join(path, "git#{ext}")
+ return exe if File.executable?(exe) && !File.directory?(exe)
+ end
+ end
+
+ return nil
+ end
+
end
diff --git a/lib/rubygems/commands/info_command.rb b/lib/rubygems/commands/info_command.rb
new file mode 100644
index 0000000000..8d9611a957
--- /dev/null
+++ b/lib/rubygems/commands/info_command.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require 'rubygems/command'
+require 'rubygems/commands/query_command'
+
+class Gem::Commands::InfoCommand < Gem::Commands::QueryCommand
+ def initialize
+ super "info", "Show information for the given gem"
+
+ remove_option('--name-matches')
+ remove_option('-d')
+
+ defaults[:details] = true
+ defaults[:exact] = true
+ end
+
+ def description # :nodoc:
+ "Info prints information about the gem such as name,"\
+ " description, website, license and installed paths"
+ end
+
+ def usage # :nodoc:
+ "#{program_name} GEMNAME"
+ end
+
+ def arguments # :nodoc:
+ "GEMNAME name of the gem to print information about"
+ end
+
+ def defaults_str
+ "--local"
+ end
+end
diff --git a/lib/rubygems/commands/install_command.rb b/lib/rubygems/commands/install_command.rb
index 3a7d50517f..c6abf9cd7c 100644
--- a/lib/rubygems/commands/install_command.rb
+++ b/lib/rubygems/commands/install_command.rb
@@ -134,7 +134,8 @@ to write the specification by hand. For example:
def check_version # :nodoc:
if options[:version] != Gem::Requirement.default and
get_all_gem_names.size > 1 then
- alert_error "Can't use --version w/ multiple gems. Use name:ver instead."
+ alert_error "Can't use --version with multiple gems. You can specify multiple gems with" \
+ " version requirments using `gem install 'my_gem:1.0.0' 'my_other_gem:~>2.0.0'`"
terminate_interaction 1
end
end
@@ -148,7 +149,7 @@ to write the specification by hand. For example:
@installed_specs = []
- ENV.delete 'GEM_PATH' if options[:install_dir].nil? and RUBY_VERSION > '1.9'
+ ENV.delete 'GEM_PATH' if options[:install_dir].nil?
check_install_dir
check_version
@@ -250,18 +251,23 @@ to write the specification by hand. For example:
get_all_gem_names_and_versions.each do |gem_name, gem_version|
gem_version ||= options[:version]
+ domain = options[:domain]
+ domain = :local unless options[:suggest_alternate]
begin
install_gem gem_name, gem_version
rescue Gem::InstallError => e
alert_error "Error installing #{gem_name}:\n\t#{e.message}"
exit_code |= 1
- rescue Gem::GemNotFoundException, Gem::UnsatisfiableDependencyError => e
- domain = options[:domain]
- domain = :local unless options[:suggest_alternate]
+ rescue Gem::GemNotFoundException => e
show_lookup_failure e.name, e.version, e.errors, domain
exit_code |= 2
+ rescue Gem::UnsatisfiableDependencyError => e
+ show_lookup_failure e.name, e.version, e.errors, domain,
+ "'#{gem_name}' (#{gem_version})"
+
+ exit_code |= 2
end
end
@@ -300,4 +306,3 @@ to write the specification by hand. For example:
end
end
-
diff --git a/lib/rubygems/commands/pristine_command.rb b/lib/rubygems/commands/pristine_command.rb
index fafe35bec1..817e752266 100644
--- a/lib/rubygems/commands/pristine_command.rb
+++ b/lib/rubygems/commands/pristine_command.rb
@@ -24,7 +24,8 @@ class Gem::Commands::PristineCommand < Gem::Command
add_option('--skip=gem_name',
'used on --all, skip if name == gem_name') do |value, options|
- options[:skip] = value
+ options[:skip] ||= []
+ options[:skip] << value
end
add_option('--[no-]extensions',
@@ -115,9 +116,11 @@ extensions will be restored.
next
end
- if spec.name == options[:skip]
- say "Skipped #{spec.full_name}, it was given through options"
- next
+ if options.has_key? :skip
+ if options[:skip].include? spec.name
+ say "Skipped #{spec.full_name}, it was given through options"
+ next
+ end
end
if spec.bundled_gem_in_old_ruby?
diff --git a/lib/rubygems/commands/setup_command.rb b/lib/rubygems/commands/setup_command.rb
index b6690c9d54..fc87063fe3 100644
--- a/lib/rubygems/commands/setup_command.rb
+++ b/lib/rubygems/commands/setup_command.rb
@@ -6,8 +6,10 @@ 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*$/
+ HISTORY_HEADER = /^===\s*[\d.a-zA-Z]+\s*\/\s*\d{4}-\d{2}-\d{2}\s*$/
+ VERSION_MATCHER = /^===\s*([\d.a-zA-Z]+)\s*\/\s*\d{4}-\d{2}-\d{2}\s*$/
+
+ ENV_PATHS = %w[/usr/bin/env /bin/env]
def initialize
require 'tmpdir'
@@ -85,6 +87,12 @@ class Gem::Commands::SetupCommand < Gem::Command
options[:regenerate_binstubs] = value
end
+ add_option('-E', '--[no-]env-shebang',
+ 'Rewrite executables with a shebang',
+ 'of /usr/bin/env') do |value, options|
+ options[:env_shebang] = value
+ end
+
@verbose = nil
end
@@ -119,6 +127,13 @@ By default, this RubyGems will install gem as:
EOF
end
+ module MakeDirs
+ def mkdir_p(path, *opts)
+ super
+ (@mkdirs ||= []) << path
+ end
+ end
+
def execute
@verbose = Gem.configuration.really_verbose
@@ -137,6 +152,7 @@ By default, this RubyGems will install gem as:
else
extend FileUtils
end
+ extend MakeDirs
lib_dir, bin_dir = make_destination_dirs install_destdir
@@ -150,6 +166,11 @@ By default, this RubyGems will install gem as:
install_default_bundler_gem
+ if mode = options[:dir_mode]
+ @mkdirs.uniq!
+ File.chmod(mode, @mkdirs)
+ end
+
say "RubyGems #{Gem::VERSION} installed"
regenerate_binstubs if options[:regenerate_binstubs]
@@ -216,6 +237,8 @@ By default, this RubyGems will install gem as:
def install_executables(bin_dir)
@bin_file_names = []
+ prog_mode = options[:prog_mode] || 0755
+
executables = { 'gem' => 'bin' }
executables['bundler'] = 'bundler/exe' if Gem::USE_BUNDLER_FOR_GEMDEPS
executables.each do |tool, path|
@@ -238,13 +261,13 @@ By default, this RubyGems will install gem as:
begin
bin = File.readlines bin_file
- bin[0] = "#!#{Gem.ruby}\n"
+ bin[0] = shebang
File.open bin_tmp_file, 'w' do |fp|
fp.puts bin.join
end
- install bin_tmp_file, dest_file, :mode => 0755
+ install bin_tmp_file, dest_file, :mode => prog_mode
@bin_file_names << dest_file
ensure
rm bin_tmp_file
@@ -266,7 +289,7 @@ By default, this RubyGems will install gem as:
TEXT
end
- install bin_cmd_file, "#{dest_file}.bat", :mode => 0755
+ install bin_cmd_file, "#{dest_file}.bat", :mode => prog_mode
ensure
rm bin_cmd_file
end
@@ -275,12 +298,24 @@ By default, this RubyGems will install gem as:
end
end
+ def shebang
+ if options[:env_shebang]
+ ruby_name = RbConfig::CONFIG['ruby_install_name']
+ @env_path ||= ENV_PATHS.find {|env_path| File.executable? env_path }
+ "#!#{@env_path} #{ruby_name}\n"
+ else
+ "#!#{Gem.ruby}\n"
+ end
+ end
+
def install_file file, dest_dir
dest_file = File.join dest_dir, file
dest_dir = File.dirname dest_file
- mkdir_p dest_dir unless File.directory? dest_dir
+ unless File.directory? dest_dir
+ mkdir_p dest_dir, :mode => 0700
+ end
- install file, dest_file, :mode => 0644
+ install file, dest_file, :mode => options[:data_mode] || 0644
end
def install_lib(lib_dir)
@@ -352,7 +387,7 @@ By default, this RubyGems will install gem as:
specs_dir = Gem::Specification.default_specifications_dir
specs_dir = File.join(options[:destdir], specs_dir) unless Gem.win_platform?
- mkdir_p specs_dir
+ mkdir_p specs_dir, :mode => 0700
# Workaround for non-git environment.
gemspec = File.open('bundler/bundler.gemspec', 'rb'){|f| f.read.gsub(/`git ls-files -z`/, "''") }
@@ -387,7 +422,7 @@ By default, this RubyGems will install gem as:
bundler_bin_dir = bundler_spec.bin_dir
bundler_bin_dir = File.join(options[:destdir], bundler_bin_dir) unless Gem.win_platform?
- mkdir_p bundler_bin_dir
+ mkdir_p bundler_bin_dir, :mode => 0700
bundler_spec.executables.each do |e|
cp File.join("bundler", bundler_spec.bindir, e), File.join(bundler_bin_dir, e)
end
@@ -411,8 +446,8 @@ By default, this RubyGems will install gem as:
lib_dir, bin_dir = generate_default_dirs(install_destdir)
end
- mkdir_p lib_dir
- mkdir_p bin_dir
+ mkdir_p lib_dir, :mode => 0700
+ mkdir_p bin_dir, :mode => 0700
return lib_dir, bin_dir
end
@@ -543,8 +578,7 @@ abort "#{deprecation_message}"
if File.exist? release_notes then
history = File.read release_notes
- history.force_encoding Encoding::UTF_8 if
- Object.const_defined? :Encoding
+ history.force_encoding Encoding::UTF_8
history = history.sub(/^# coding:.*?(?=^=)/m, '')
@@ -582,8 +616,14 @@ abort "#{deprecation_message}"
def regenerate_binstubs
require "rubygems/commands/pristine_command"
say "Regenerating binstubs"
+
+ args = %w[--all --only-executables --silent]
+ if options[:env_shebang]
+ args << "--env-shebang"
+ end
+
command = Gem::Commands::PristineCommand.new
- command.invoke(*%w[--all --only-executables --silent])
+ command.invoke(*args)
end
end
diff --git a/lib/rubygems/commands/uninstall_command.rb b/lib/rubygems/commands/uninstall_command.rb
index 20b3a7a1e4..55a052284a 100644
--- a/lib/rubygems/commands/uninstall_command.rb
+++ b/lib/rubygems/commands/uninstall_command.rb
@@ -129,11 +129,7 @@ that is a dependency of an existing gem. You can use the
specs.each do |spec|
options[:version] = spec.version
-
- begin
- Gem::Uninstaller.new(spec.name, options).uninstall
- rescue Gem::InstallError
- end
+ uninstall_gem spec.name
end
alert "Uninstalled all gems in #{options[:install_dir]}"
@@ -153,14 +149,27 @@ that is a dependency of an existing gem. You can use the
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::GemNotInHomeException => e
- spec = e.spec
- alert("In order to remove #{spec.name}, please execute:\n" +
- "\tgem uninstall #{spec.name} --install-dir=#{spec.installation_path}")
- end
+ uninstall_gem(gem_name)
end
end
+ def uninstall_gem(gem_name)
+ uninstall(gem_name)
+ rescue Gem::InstallError
+ nil
+ 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}")
+ rescue Gem::UninstallError => e
+ spec = e.spec
+ alert_error("Error: unable to successfully uninstall '#{spec.name}' which is " +
+ "located at '#{spec.full_gem_path}'. This is most likely because" +
+ "the current user does not have the appropriate permissions")
+ terminate_interaction 1
+ end
+
+ def uninstall(gem_name)
+ Gem::Uninstaller.new(gem_name, options).uninstall
+ end
end
diff --git a/lib/rubygems/commands/unpack_command.rb b/lib/rubygems/commands/unpack_command.rb
index bdcfd524f1..b2edf7d349 100644
--- a/lib/rubygems/commands/unpack_command.rb
+++ b/lib/rubygems/commands/unpack_command.rb
@@ -94,7 +94,17 @@ command help for an example.
spec_file = File.basename spec.spec_file
- File.open spec_file, 'w' do |io|
+ FileUtils.mkdir_p @options[:target] if @options[:target]
+
+ destination = begin
+ if @options[:target]
+ File.join @options[:target], spec_file
+ else
+ spec_file
+ end
+ end
+
+ File.open destination, 'w' do |io|
io.write metadata
end
else
diff --git a/lib/rubygems/commands/update_command.rb b/lib/rubygems/commands/update_command.rb
index 93ee60e1ab..1c86ba6753 100644
--- a/lib/rubygems/commands/update_command.rb
+++ b/lib/rubygems/commands/update_command.rb
@@ -85,18 +85,27 @@ command to remove old versions.
end
def execute
-
if options[:system] then
update_rubygems
return
end
- say "Updating installed gems"
-
hig = highest_installed_gems
gems_to_update = which_to_update hig, options[:args].uniq
+ if options[:explain]
+ say "Gems to update:"
+
+ gems_to_update.each do |(name, version)|
+ say " #{name}-#{version}"
+ end
+
+ return
+ end
+
+ say "Updating installed gems"
+
updated = update_gems gems_to_update
updated_names = updated.map { |spec| spec.name }
diff --git a/lib/rubygems/compatibility.rb b/lib/rubygems/compatibility.rb
index 2056b5b53a..24c741e99b 100644
--- a/lib/rubygems/compatibility.rb
+++ b/lib/rubygems/compatibility.rb
@@ -9,26 +9,6 @@
# 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 path = $LOADED_FEATURES.find {|n| n.end_with? '/rubygems.rb'} then
- 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
diff --git a/lib/rubygems/config_file.rb b/lib/rubygems/config_file.rb
index c0d19dbfc2..c0fe3d8964 100644
--- a/lib/rubygems/config_file.rb
+++ b/lib/rubygems/config_file.rb
@@ -48,7 +48,7 @@ class Gem::ConfigFile
# For Ruby packagers to set configuration defaults. Set in
# rubygems/defaults/operating_system.rb
- OPERATING_SYSTEM_DEFAULTS = {}
+ OPERATING_SYSTEM_DEFAULTS = Gem.operating_system_defaults
##
# For Ruby implementers to set configuration defaults. Set in
@@ -63,26 +63,7 @@ class Gem::ConfigFile
require "etc"
Etc.sysconfdir
rescue LoadError, NoMethodError
- begin
- # TODO: remove after we drop 1.8.7 and 1.9.1
- require 'Win32API'
-
- CSIDL_COMMON_APPDATA = 0x0023
- path = 0.chr * 260
- if RUBY_VERSION > '1.9' then
- SHGetFolderPath = Win32API.new 'shell32', 'SHGetFolderPath', 'PLPLP',
- 'L', :stdcall
- SHGetFolderPath.call nil, CSIDL_COMMON_APPDATA, nil, 1, path
- else
- SHGetFolderPath = Win32API.new 'shell32', 'SHGetFolderPath', 'LLLLP',
- 'L'
- SHGetFolderPath.call 0, CSIDL_COMMON_APPDATA, 0, 1, path
- end
-
- path.strip
- rescue LoadError
- RbConfig::CONFIG["sysconfdir"] || "/etc"
- end
+ RbConfig::CONFIG["sysconfdir"] || "/etc"
end
# :startdoc:
diff --git a/lib/rubygems/core_ext/kernel_require.rb b/lib/rubygems/core_ext/kernel_require.rb
index d3df9d85f9..e93cd7c728 100755
--- a/lib/rubygems/core_ext/kernel_require.rb
+++ b/lib/rubygems/core_ext/kernel_require.rb
@@ -11,13 +11,8 @@ module Kernel
RUBYGEMS_ACTIVATION_MONITOR = Monitor.new # :nodoc:
- 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.
-
+ # Make sure we have a reference to Ruby's original Kernel#require
+ unless defined?(gem_original_require)
alias gem_original_require require
private :gem_original_require
end
diff --git a/lib/rubygems/defaults.rb b/lib/rubygems/defaults.rb
index 43d57fc808..b8222877ae 100644
--- a/lib/rubygems/defaults.rb
+++ b/lib/rubygems/defaults.rb
@@ -176,7 +176,26 @@ module Gem
end
##
- # Default options for gem commands.
+ # Default options for gem commands for Ruby packagers.
+ #
+ # The options here should be structured as an array of string "gem"
+ # command names as keys and a string of the default options as values.
+ #
+ # Example:
+ #
+ # def self.operating_system_defaults
+ # {
+ # 'install' => '--no-rdoc --no-ri --env-shebang',
+ # 'update' => '--no-rdoc --no-ri --env-shebang'
+ # }
+ # end
+
+ def self.operating_system_defaults
+ {}
+ end
+
+ ##
+ # Default options for gem commands for Ruby implementers.
#
# The options here should be structured as an array of string "gem"
# command names as keys and a string of the default options as values.
diff --git a/lib/rubygems/dependency_installer.rb b/lib/rubygems/dependency_installer.rb
index 879e085359..4b474e1d19 100644
--- a/lib/rubygems/dependency_installer.rb
+++ b/lib/rubygems/dependency_installer.rb
@@ -15,6 +15,7 @@ require 'rubygems/deprecate'
class Gem::DependencyInstaller
include Gem::UserInteraction
+ extend Gem::Deprecate
DEFAULT_OPTIONS = { # :nodoc:
:env_shebang => false,
@@ -42,15 +43,6 @@ class Gem::DependencyInstaller
attr_reader :errors
##
- #--
- # TODO remove, no longer used
-
- attr_reader :gems_to_install # :nodoc:
-
- extend Gem::Deprecate
- deprecate :gems_to_install, :none, 2016, 10
-
- ##
# List of gems installed by #install in alphabetic order
attr_reader :installed_gems
@@ -97,6 +89,9 @@ class Gem::DependencyInstaller
@build_args = options[:build_args]
@build_docs_in_background = options[:build_docs_in_background]
@install_as_default = options[:install_as_default]
+ @dir_mode = options[:dir_mode]
+ @data_mode = options[:data_mode]
+ @prog_mode = options[:prog_mode]
# Indicates that we should not try to update any deps unless
# we absolutely must.
@@ -404,7 +399,10 @@ class Gem::DependencyInstaller
:user_install => @user_install,
:wrappers => @wrappers,
:build_root => @build_root,
- :install_as_default => @install_as_default
+ :install_as_default => @install_as_default,
+ :dir_mode => @dir_mode,
+ :data_mode => @data_mode,
+ :prog_mode => @prog_mode,
}
options[:install_dir] = @install_dir if @only_install_dir
diff --git a/lib/rubygems/exceptions.rb b/lib/rubygems/exceptions.rb
index 328bd5f476..5999bc2a0d 100644
--- a/lib/rubygems/exceptions.rb
+++ b/lib/rubygems/exceptions.rb
@@ -56,6 +56,13 @@ class Gem::GemNotInHomeException < Gem::Exception
attr_accessor :spec
end
+###
+# Raised when removing a gem with the uninstall command fails
+
+class Gem::UninstallError < Gem::Exception
+ attr_accessor :spec
+end
+
class Gem::DocumentError < Gem::Exception; end
##
diff --git a/lib/rubygems/ext/builder.rb b/lib/rubygems/ext/builder.rb
index eb9db199d5..805ef02422 100644
--- a/lib/rubygems/ext/builder.rb
+++ b/lib/rubygems/ext/builder.rb
@@ -39,7 +39,7 @@ class Gem::Ext::Builder
make_program = (/mswin/ =~ RUBY_PLATFORM) ? 'nmake' : 'make'
end
- destdir = '"DESTDIR=%s"' % ENV['DESTDIR'] if RUBY_VERSION > '2.0'
+ destdir = '"DESTDIR=%s"' % ENV['DESTDIR']
['clean', '', 'install'].each do |target|
# Pass DESTDIR via command line to override what's in MAKEFLAGS
@@ -160,11 +160,19 @@ EOF
FileUtils.mkdir_p dest_path
CHDIR_MUTEX.synchronize do
- Dir.chdir extension_dir do
- results = builder.build(extension, @gem_dir, dest_path,
+ pwd = Dir.getwd
+ Dir.chdir extension_dir
+ begin
+ results = builder.build(extension, dest_path,
results, @build_args, lib_dir)
verbose { results.join("\n") }
+ ensure
+ begin
+ Dir.chdir pwd
+ rescue SystemCallError
+ Dir.chdir dest_path
+ end
end
end
@@ -218,4 +226,3 @@ EOF
end
end
-
diff --git a/lib/rubygems/ext/cmake_builder.rb b/lib/rubygems/ext/cmake_builder.rb
index efa3bd1d88..cfcc83c9a7 100644
--- a/lib/rubygems/ext/cmake_builder.rb
+++ b/lib/rubygems/ext/cmake_builder.rb
@@ -2,7 +2,7 @@
require 'rubygems/command'
class Gem::Ext::CmakeBuilder < Gem::Ext::Builder
- def self.build(extension, directory, dest_path, results, args=[], lib_dir=nil)
+ def self.build(extension, dest_path, results, args=[], lib_dir=nil)
unless File.exist?('Makefile') then
cmd = "cmake . -DCMAKE_INSTALL_PREFIX=#{dest_path}"
cmd << " #{Gem::Command.build_args.join ' '}" unless Gem::Command.build_args.empty?
diff --git a/lib/rubygems/ext/configure_builder.rb b/lib/rubygems/ext/configure_builder.rb
index 8b42bf7ee9..1b9e374075 100644
--- a/lib/rubygems/ext/configure_builder.rb
+++ b/lib/rubygems/ext/configure_builder.rb
@@ -7,7 +7,7 @@
class Gem::Ext::ConfigureBuilder < Gem::Ext::Builder
- def self.build(extension, directory, dest_path, results, args=[], lib_dir=nil)
+ def self.build(extension, dest_path, results, args=[], lib_dir=nil)
unless File.exist?('Makefile') then
cmd = "sh ./configure --prefix=#{dest_path}"
cmd << " #{args.join ' '}" unless args.empty?
@@ -21,4 +21,3 @@ class Gem::Ext::ConfigureBuilder < Gem::Ext::Builder
end
end
-
diff --git a/lib/rubygems/ext/ext_conf_builder.rb b/lib/rubygems/ext/ext_conf_builder.rb
index 965b728278..a17881a890 100644
--- a/lib/rubygems/ext/ext_conf_builder.rb
+++ b/lib/rubygems/ext/ext_conf_builder.rb
@@ -11,7 +11,7 @@ require 'tempfile'
class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
FileEntry = FileUtils::Entry_ # :nodoc:
- def self.build(extension, directory, dest_path, results, args=[], lib_dir=nil)
+ def self.build(extension, dest_path, results, args=[], lib_dir=nil)
tmp_dest = Dir.mktmpdir(".gem.", ".")
# Some versions of `mktmpdir` return absolute paths, which will break make
@@ -23,9 +23,7 @@ class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
# spaces do not work.
#
# Details: https://github.com/rubygems/rubygems/issues/977#issuecomment-171544940
- #
- # TODO: Make this unconditional when rubygems no longer supports Ruby 1.9.x.
- tmp_dest = get_relative_path(tmp_dest) unless Gem.win_platform? && RUBY_VERSION <= '2.0'
+ tmp_dest = get_relative_path(tmp_dest)
Tempfile.open %w"siteconf .rb", "." do |siteconf|
siteconf.puts "require 'rbconfig'"
diff --git a/lib/rubygems/ext/rake_builder.rb b/lib/rubygems/ext/rake_builder.rb
index 4b3534bf35..7a5a48c6cc 100644
--- a/lib/rubygems/ext/rake_builder.rb
+++ b/lib/rubygems/ext/rake_builder.rb
@@ -7,7 +7,7 @@
class Gem::Ext::RakeBuilder < Gem::Ext::Builder
- def self.build(extension, directory, dest_path, results, args=[], lib_dir=nil)
+ def self.build(extension, dest_path, results, args=[], lib_dir=nil)
if File.basename(extension) =~ /mkrf_conf/i then
cmd = "#{Gem.ruby} #{File.basename extension}".dup
cmd << " #{args.join " "}" unless args.empty?
@@ -34,4 +34,3 @@ class Gem::Ext::RakeBuilder < Gem::Ext::Builder
end
end
-
diff --git a/lib/rubygems/installer.rb b/lib/rubygems/installer.rb
index 0c58c29ac9..b142454c09 100644
--- a/lib/rubygems/installer.rb
+++ b/lib/rubygems/installer.rb
@@ -110,6 +110,10 @@ class Gem::Installer
class FakePackage
attr_accessor :spec
+ attr_accessor :dir_mode
+ attr_accessor :prog_mode
+ attr_accessor :data_mode
+
def initialize(spec)
@spec = spec
end
@@ -179,6 +183,10 @@ class Gem::Installer
process_options
+ @package.dir_mode = options[:dir_mode]
+ @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]
@@ -298,7 +306,8 @@ class Gem::Installer
FileUtils.rm_rf gem_dir
FileUtils.rm_rf spec.extension_dir
- FileUtils.mkdir_p gem_dir
+ dir_mode = options[:dir_mode]
+ FileUtils.mkdir_p gem_dir, :mode => dir_mode && 0700
if @options[:install_as_default] then
extract_bin
@@ -315,6 +324,8 @@ class Gem::Installer
write_cache_file
end
+ File.chmod(dir_mode, gem_dir) if dir_mode
+
say spec.post_install_message if options[:post_install_message] && !spec.post_install_message.nil?
Gem::Installer.install_lock.synchronize { Gem::Specification.reset }
@@ -468,7 +479,7 @@ class Gem::Installer
return if spec.executables.nil? or spec.executables.empty?
begin
- Dir.mkdir @bin_dir
+ Dir.mkdir @bin_dir, *[options[:dir_mode] && 0700].compact
rescue SystemCallError
raise unless File.directory? @bin_dir
end
@@ -486,7 +497,8 @@ class Gem::Installer
end
mode = File.stat(bin_path).mode
- FileUtils.chmod mode | 0111, bin_path unless (mode | 0111) == mode
+ dir_mode = options[:prog_mode] || (mode | 0111)
+ FileUtils.chmod dir_mode, bin_path unless dir_mode == mode
check_executable_overwrite filename
@@ -511,8 +523,9 @@ class Gem::Installer
FileUtils.rm_f bin_script_path # prior install may have been --no-wrappers
- File.open bin_script_path, 'wb', 0755 do |file|
+ File.open bin_script_path, 'wb', 0700 do |file|
file.print app_script_text(filename)
+ file.chmod(options[:prog_mode] || 0755)
end
verbose bin_script_path
@@ -705,7 +718,7 @@ class Gem::Installer
end
def verify_gem_home(unpack = false) # :nodoc:
- FileUtils.mkdir_p gem_home
+ FileUtils.mkdir_p gem_home, :mode => options[:dir_mode] && 0700
raise Gem::FilePermissionError, gem_home unless
unpack or File.writable?(gem_home)
end
@@ -736,7 +749,7 @@ version = "#{Gem::Requirement.default}.a"
if ARGV.first
str = ARGV.first
- str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding
+ str = str.dup.force_encoding("BINARY")
if str =~ /\\A_(.*)_\\z/ and Gem::Version.correct?($1) then
version = $1
ARGV.shift
@@ -868,7 +881,8 @@ TEXT
build_info_dir = File.join gem_home, 'build_info'
- FileUtils.mkdir_p build_info_dir
+ dir_mode = options[:dir_mode]
+ FileUtils.mkdir_p build_info_dir, :mode => dir_mode && 0700
build_info_file = File.join build_info_dir, "#{spec.full_name}.info"
@@ -877,6 +891,8 @@ TEXT
io.puts arg
end
end
+
+ File.chmod(dir_mode, build_info_dir) if dir_mode
end
##
diff --git a/lib/rubygems/installer_test_case.rb b/lib/rubygems/installer_test_case.rb
index 4cec5da3f4..943c6c1256 100644
--- a/lib/rubygems/installer_test_case.rb
+++ b/lib/rubygems/installer_test_case.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'rubygems/test_case'
require 'rubygems/installer'
-require 'rubygems/deprecate'
class Gem::Installer
@@ -107,18 +106,6 @@ class Gem::InstallerTestCase < Gem::TestCase
Gem::Installer.path_warning = false
end
- def util_gem_bindir spec = @spec # :nodoc:
- spec.bin_dir
- end
-
- def util_gem_dir spec = @spec # :nodoc:
- spec.gem_dir
- end
-
- extend Gem::Deprecate
- deprecate :util_gem_bindir, "@spec.bin_dir", 2016, 10
- deprecate :util_gem_dir, "@spec.gem_dir", 2016, 10
-
##
# The path where installed executables live
diff --git a/lib/rubygems/local_remote_options.rb b/lib/rubygems/local_remote_options.rb
index 597b87ea03..fe09e34e54 100644
--- a/lib/rubygems/local_remote_options.rb
+++ b/lib/rubygems/local_remote_options.rb
@@ -24,8 +24,10 @@ module Gem::LocalRemoteOptions
raise OptionParser::InvalidArgument, value
end
- unless ['http', 'https', 'file', 's3'].include?(uri.scheme)
- raise OptionParser::InvalidArgument, value
+ valid_uri_schemes = ["http", "https", "file", "s3"]
+ unless valid_uri_schemes.include?(uri.scheme)
+ msg = "Invalid uri scheme for #{value}\nPreface URLs with one of #{valid_uri_schemes.map{|s| "#{s}://"}}"
+ raise ArgumentError, msg
end
value
diff --git a/lib/rubygems/package.rb b/lib/rubygems/package.rb
index 729bbd6f79..b20334c8ca 100644
--- a/lib/rubygems/package.rb
+++ b/lib/rubygems/package.rb
@@ -107,6 +107,18 @@ class Gem::Package
attr_writer :spec
+ ##
+ # Permission for directories
+ attr_accessor :dir_mode
+
+ ##
+ # Permission for program files
+ attr_accessor :prog_mode
+
+ ##
+ # Permission for other files
+ attr_accessor :data_mode
+
def self.build spec, skip_validation=false
gem_file = spec.file_name
@@ -148,7 +160,7 @@ class Gem::Package
def initialize gem, security_policy # :notnew:
@gem = gem
- @build_time = Time.now
+ @build_time = ENV["SOURCE_DATE_EPOCH"] ? Time.at(ENV["SOURCE_DATE_EPOCH"].to_i).utc : Time.now
@checksums = {}
@contents = nil
@digests = Hash.new { |h, algorithm| h[algorithm] = {} }
@@ -334,7 +346,7 @@ EOM
def extract_files destination_dir, pattern = "*"
verify unless @spec
- FileUtils.mkdir_p destination_dir
+ FileUtils.mkdir_p destination_dir, :mode => dir_mode && 0700
@gem.with_read_io do |io|
reader = Gem::Package::TarReader.new io
@@ -361,6 +373,7 @@ EOM
# extracted.
def extract_tar_gz io, destination_dir, pattern = "*" # :nodoc:
+ directories = [] if dir_mode
open_tar_gz io do |tar|
tar.each do |entry|
next unless File.fnmatch pattern, entry.full_name, File::FNM_DOTMATCH
@@ -370,19 +383,20 @@ EOM
FileUtils.rm_rf destination
mkdir_options = {}
- mkdir_options[:mode] = entry.header.mode if entry.directory?
+ mkdir_options[:mode] = dir_mode ? 0700 : (entry.header.mode if entry.directory?)
mkdir =
if entry.directory? then
destination
else
File.dirname destination
end
+ directories << mkdir if directories
mkdir_p_safe mkdir, mkdir_options, destination_dir, entry.full_name
File.open destination, 'wb' do |out|
out.write entry.read
- FileUtils.chmod entry.header.mode, destination
+ FileUtils.chmod file_mode(entry.header.mode), destination
end if entry.file?
File.symlink(entry.header.linkname, destination) if entry.symlink?
@@ -390,6 +404,15 @@ EOM
verbose destination
end
end
+
+ if directories
+ directories.uniq!
+ File.chmod(dir_mode, *directories)
+ end
+ end
+
+ def file_mode(mode) # :nodoc:
+ ((mode & 0111).zero? ? data_mode : prog_mode) || mode
end
##
@@ -416,11 +439,8 @@ EOM
raise Gem::Package::PathError.new(filename, destination_dir) if
filename.start_with? '/'
- destination_dir = realpath destination_dir
- destination_dir = File.expand_path destination_dir
-
- destination = File.join destination_dir, filename
- destination = File.expand_path destination
+ destination_dir = File.expand_path(File.realpath(destination_dir))
+ destination = File.expand_path(File.join(destination_dir, filename))
raise Gem::Package::PathError.new(destination, destination_dir) unless
destination.start_with? destination_dir + '/'
@@ -438,10 +458,10 @@ EOM
end
def mkdir_p_safe mkdir, mkdir_options, destination_dir, file_name
- destination_dir = realpath File.expand_path(destination_dir)
+ destination_dir = File.realpath(File.expand_path(destination_dir))
parts = mkdir.split(File::SEPARATOR)
parts.reduce do |path, basename|
- path = realpath path unless path == ""
+ path = File.realpath(path) unless path == ""
path = File.expand_path(path + File::SEPARATOR + basename)
lstat = File.lstat path rescue nil
if !lstat || !lstat.directory?
@@ -463,8 +483,7 @@ EOM
when 'metadata.gz' then
args = [entry]
args << { :external_encoding => Encoding::UTF_8 } if
- Object.const_defined?(:Encoding) &&
- Zlib::GzipReader.method(:wrap).arity != 1
+ Zlib::GzipReader.method(:wrap).arity != 1
Zlib::GzipReader.wrap(*args) do |gzio|
@spec = Gem::Specification.from_yaml gzio.read
@@ -643,16 +662,6 @@ EOM
raise Gem::Package::FormatError.new(e.message, entry.full_name)
end
- if File.respond_to? :realpath
- def realpath file
- File.realpath file
- end
- else
- def realpath file
- file
- end
- end
-
end
require 'rubygems/package/digest_io'
diff --git a/lib/rubygems/package/old.rb b/lib/rubygems/package/old.rb
index 322d682ca8..a8457948ff 100644
--- a/lib/rubygems/package/old.rb
+++ b/lib/rubygems/package/old.rb
@@ -78,9 +78,9 @@ class Gem::Package::Old < Gem::Package
FileUtils.rm_rf destination
- FileUtils.mkdir_p File.dirname destination
+ FileUtils.mkdir_p File.dirname(destination), :mode => dir_mode && 0700
- File.open destination, 'wb', entry['mode'] do |out|
+ File.open destination, 'wb', file_mode(entry['mode']) do |out|
out.write file_data
end
@@ -144,17 +144,9 @@ class Gem::Package::Old < Gem::Package
end
end
- yaml_error = if RUBY_VERSION < '1.9' then
- YAML::ParseError
- elsif YAML.const_defined?(:ENGINE) && YAML::ENGINE.yamler == 'syck' then
- YAML::ParseError
- else
- YAML::SyntaxError
- end
-
begin
@spec = Gem::Specification.from_yaml yaml
- rescue yaml_error
+ rescue YAML::SyntaxError
raise Gem::Exception, "Failed to parse gem specification out of gem file"
end
rescue ArgumentError
diff --git a/lib/rubygems/package/tar_header.rb b/lib/rubygems/package/tar_header.rb
index d557357114..fa1f5b6e46 100644
--- a/lib/rubygems/package/tar_header.rb
+++ b/lib/rubygems/package/tar_header.rb
@@ -94,12 +94,14 @@ class Gem::Package::TarHeader
attr_reader(*FIELDS)
+ EMPTY_HEADER = ("\0" * 512).freeze # :nodoc:
+
##
# Creates a tar header from IO +stream+
def self.from(stream)
header = stream.read 512
- empty = (header == "\0" * 512)
+ empty = (EMPTY_HEADER == header)
fields = header.unpack UNPACK_FORMAT
diff --git a/lib/rubygems/package/tar_reader.rb b/lib/rubygems/package/tar_reader.rb
index 1098336e36..d00f89e7f6 100644
--- a/lib/rubygems/package/tar_reader.rb
+++ b/lib/rubygems/package/tar_reader.rb
@@ -93,10 +93,8 @@ class Gem::Package::TarReader
def rewind
if @init_pos == 0 then
- raise Gem::Package::NonSeekableIO unless @io.respond_to? :rewind
@io.rewind
else
- raise Gem::Package::NonSeekableIO unless @io.respond_to? :pos=
@io.pos = @init_pos
end
end
diff --git a/lib/rubygems/package/tar_reader/entry.rb b/lib/rubygems/package/tar_reader/entry.rb
index 5f958edc2f..b6fb8c3a3a 100644
--- a/lib/rubygems/package/tar_reader/entry.rb
+++ b/lib/rubygems/package/tar_reader/entry.rb
@@ -145,8 +145,6 @@ class Gem::Package::TarReader::Entry
def rewind
check_closed
- raise Gem::Package::NonSeekableIO unless @io.respond_to? :pos=
-
@io.pos = @orig_pos
@read = 0
end
diff --git a/lib/rubygems/package/tar_test_case.rb b/lib/rubygems/package/tar_test_case.rb
index 46ac949587..381a5d0f43 100644
--- a/lib/rubygems/package/tar_test_case.rb
+++ b/lib/rubygems/package/tar_test_case.rb
@@ -94,13 +94,7 @@ class Gem::Package::TarTestCase < Gem::TestCase
ASCIIZ(dname, 155) # char prefix[155]; ASCII + (Z unless filled)
]
- format = "C100C8C8C8C12C12C8CC100C6C2C32C32C8C8C155"
- h = if RUBY_VERSION >= "1.9" then
- arr.join
- else
- arr = arr.join("").split(//).map{|x| x[0]}
- arr.pack format
- end
+ h = arr.join
ret = h + "\0" * (512 - h.size)
assert_equal(512, ret.size)
ret
diff --git a/lib/rubygems/package/tar_writer.rb b/lib/rubygems/package/tar_writer.rb
index 390f7851a3..a2dbab9f2f 100644
--- a/lib/rubygems/package/tar_writer.rb
+++ b/lib/rubygems/package/tar_writer.rb
@@ -106,12 +106,10 @@ class Gem::Package::TarWriter
def add_file(name, mode) # :yields: io
check_closed
- raise Gem::Package::NonSeekableIO unless @io.respond_to? :pos=
-
name, prefix = split_name name
init_pos = @io.pos
- @io.write "\0" * 512 # placeholder for the header
+ @io.write Gem::Package::TarHeader::EMPTY_HEADER # placeholder for the header
yield RestrictedStream.new(@io) if block_given?
@@ -125,7 +123,7 @@ class Gem::Package::TarWriter
header = Gem::Package::TarHeader.new :name => name, :mode => mode,
:size => size, :prefix => prefix,
- :mtime => Time.now
+ :mtime => ENV["SOURCE_DATE_EPOCH"] ? Time.at(ENV["SOURCE_DATE_EPOCH"].to_i).utc : Time.now
@io.write header
@io.pos = final_pos
@@ -220,7 +218,7 @@ class Gem::Package::TarWriter
header = Gem::Package::TarHeader.new(:name => name, :mode => mode,
:size => size, :prefix => prefix,
- :mtime => Time.now).to_s
+ :mtime => ENV["SOURCE_DATE_EPOCH"] ? Time.at(ENV["SOURCE_DATE_EPOCH"].to_i).utc : Time.now).to_s
@io.write header
os = BoundedStream.new @io, size
@@ -248,7 +246,7 @@ class Gem::Package::TarWriter
:size => 0, :typeflag => "2",
:linkname => target,
:prefix => prefix,
- :mtime => Time.now).to_s
+ :mtime => ENV["SOURCE_DATE_EPOCH"] ? Time.at(ENV["SOURCE_DATE_EPOCH"].to_i).utc : Time.now).to_s
@io.write header
@@ -301,7 +299,7 @@ class Gem::Package::TarWriter
header = Gem::Package::TarHeader.new :name => name, :mode => mode,
:typeflag => "5", :size => 0,
:prefix => prefix,
- :mtime => Time.now
+ :mtime => ENV["SOURCE_DATE_EPOCH"] ? Time.at(ENV["SOURCE_DATE_EPOCH"].to_i).utc : Time.now
@io.write header
diff --git a/lib/rubygems/rdoc.rb b/lib/rubygems/rdoc.rb
index 7043bd2a31..dfaf7c55bf 100644
--- a/lib/rubygems/rdoc.rb
+++ b/lib/rubygems/rdoc.rb
@@ -1,7 +1,5 @@
# frozen_string_literal: true
require 'rubygems'
-require 'rubygems/user_interaction'
-require 'fileutils'
begin
gem 'rdoc'
@@ -15,321 +13,12 @@ else
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
- extend 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
- start = Time.now
- 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
-
- return unless generate_rdoc or generate_ri
-
- duration = (Time.now - start).to_i
- names = specs.map(&:name).join ', '
-
- say "Done installing documentation for #{names} after #{duration} seconds"
- end
-
- ##
- # Loads the RDoc generator
-
- def self.load_rdoc
- return if @rdoc_version
-
- 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
-
- ##
- # 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 = true, 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+
- #--
- # TODO move to RDoc::Options
-
- 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
- generator_name = generator
-
- options = options.dup
- options.exclude ||= [] # TODO maybe move to RDoc::Options#finish
- options.setup_generator generator
- options.op_dir = destination
- options.finish
-
- generator = options.generator.new @rdoc.store, options
-
- @rdoc.options = options
- @rdoc.generator = generator
-
- say "Installing #{generator_name} 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
-
- options = nil
-
- if Gem::Requirement.new('< 3').satisfied_by? self.class.rdoc_version then
- generate_legacy
- return
- end
-
- ::RDoc::TopLevel.reset # TODO ::RDoc::RDoc.reset
- ::RDoc::Parser::C.reset
-
- args = @spec.rdoc_options
- args.concat @spec.source_paths
- args.concat @spec.extra_rdoc_files
-
- 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
-
- Dir.chdir @spec.full_gem_path do
- options = ::RDoc::Options.new
- options.default_title = "#{@spec.full_name} Documentation"
- options.parse args
- end
-
- options.quiet = !Gem.configuration.really_verbose
-
- @rdoc = new_rdoc
- @rdoc.options = options
-
- say "Parsing documentation for #{@spec.full_name}"
-
- 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
-
- ##
- # 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
- verbose { "rdoc #{args.join ' '}" }
-
- 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)"
- end
- end
- end
-
- ##
- # #new_rdoc creates a new RDoc instance. This method is provided only to
- # make testing easier.
-
- def new_rdoc # :nodoc:
- ::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))
diff --git a/lib/rubygems/remote_fetcher.rb b/lib/rubygems/remote_fetcher.rb
index 80fb1aaaa5..a829268f39 100644
--- a/lib/rubygems/remote_fetcher.rb
+++ b/lib/rubygems/remote_fetcher.rb
@@ -164,9 +164,7 @@ class Gem::RemoteFetcher
begin
source_uri = URI.parse(source_uri)
rescue
- source_uri = URI.parse(URI.const_defined?(:DEFAULT_PARSER) ?
- URI::DEFAULT_PARSER.escape(source_uri.to_s) :
- URI.escape(source_uri.to_s))
+ source_uri = URI.parse(URI::DEFAULT_PARSER.escape(source_uri.to_s))
end
end
diff --git a/lib/rubygems/request/connection_pools.rb b/lib/rubygems/request/connection_pools.rb
index 31fc609800..d70dea785a 100644
--- a/lib/rubygems/request/connection_pools.rb
+++ b/lib/rubygems/request/connection_pools.rb
@@ -54,10 +54,23 @@ class Gem::Request::ConnectionPools # :nodoc:
host = host.downcase
env_no_proxy.any? do |pattern|
- pattern = pattern.downcase
+ env_no_proxy_pattern = pattern.downcase.dup
- host[-pattern.length, pattern.length] == pattern or
- (pattern.start_with? '.' and pattern[1..-1] == host)
+ # Remove dot in front of pattern for wildcard matching
+ env_no_proxy_pattern[0] = "" if env_no_proxy_pattern[0] == "."
+
+ host_tokens = host.split(".")
+ pattern_tokens = env_no_proxy_pattern.split(".")
+
+ intersection = (host_tokens - pattern_tokens) | (pattern_tokens - host_tokens)
+
+ # When we do the split into tokens we miss a dot character, so add it back if we need it
+ missing_dot = intersection.length > 0 ? 1 : 0
+ start = intersection.join(".").size + missing_dot
+
+ no_proxy_host = host[start..-1]
+
+ env_no_proxy_pattern == no_proxy_host
end
end
diff --git a/lib/rubygems/request_set.rb b/lib/rubygems/request_set.rb
index e54a7b8818..89f47616ec 100644
--- a/lib/rubygems/request_set.rb
+++ b/lib/rubygems/request_set.rb
@@ -191,22 +191,7 @@ class Gem::RequestSet
return requests if options[:gemdeps]
- specs = requests.map do |request|
- case request
- when Gem::Resolver::ActivationRequest then
- request.spec.spec
- else
- request
- end
- end
-
- require 'rubygems/dependency_installer'
- inst = Gem::DependencyInstaller.new options
- inst.installed_gems.replace specs
-
- Gem.done_installing_hooks.each do |hook|
- hook.call inst, specs
- end unless Gem.done_installing_hooks.empty?
+ install_hooks requests, options
requests
end
@@ -283,12 +268,36 @@ class Gem::RequestSet
installed << request
end
+ install_hooks installed, options
+
installed
ensure
ENV['GEM_HOME'] = gem_home
end
##
+ # Call hooks on installed gems
+
+ def install_hooks requests, options
+ specs = requests.map do |request|
+ case request
+ when Gem::Resolver::ActivationRequest then
+ request.spec.spec
+ else
+ request
+ end
+ end
+
+ require "rubygems/dependency_installer"
+ inst = Gem::DependencyInstaller.new options
+ inst.installed_gems.replace specs
+
+ Gem.done_installing_hooks.each do |hook|
+ hook.call inst, specs
+ end unless Gem.done_installing_hooks.empty?
+ end
+
+ ##
# Load a dependency management file.
def load_gemdeps path, without_groups = [], installing = false
diff --git a/lib/rubygems/request_set/gem_dependency_api.rb b/lib/rubygems/request_set/gem_dependency_api.rb
index 867086cc0e..a26e8b90a3 100644
--- a/lib/rubygems/request_set/gem_dependency_api.rb
+++ b/lib/rubygems/request_set/gem_dependency_api.rb
@@ -842,8 +842,4 @@ Gem dependencies file #{@path} includes git reference for both ref/branch and ta
Gem.sources << url
end
- # TODO: remove this typo name at RubyGems 3.0
-
- Gem::RequestSet::GemDepedencyAPI = self # :nodoc:
-
end
diff --git a/lib/rubygems/request_set/lockfile/parser.rb b/lib/rubygems/request_set/lockfile/parser.rb
index ebea940188..368b56e6e6 100644
--- a/lib/rubygems/request_set/lockfile/parser.rb
+++ b/lib/rubygems/request_set/lockfile/parser.rb
@@ -325,24 +325,13 @@ class Gem::RequestSet::Lockfile::Parser
@tokens.peek
end
- if [].respond_to? :flat_map
- def pinned_requirement name # :nodoc:
- requirement = Gem::Dependency.new name
- specification = @set.sets.flat_map { |set|
- set.find_all(requirement)
- }.compact.first
-
- specification && specification.version
- end
- else # FIXME: remove when 1.8 is dropped
- def pinned_requirement name # :nodoc:
- requirement = Gem::Dependency.new name
- specification = @set.sets.map { |set|
- set.find_all(requirement)
- }.flatten(1).compact.first
-
- specification && specification.version
- end
+ def pinned_requirement name # :nodoc:
+ requirement = Gem::Dependency.new name
+ specification = @set.sets.flat_map { |set|
+ set.find_all(requirement)
+ }.compact.first
+
+ specification && specification.version
end
##
diff --git a/lib/rubygems/requirement.rb b/lib/rubygems/requirement.rb
index 2a60c86e69..430f351280 100644
--- a/lib/rubygems/requirement.rb
+++ b/lib/rubygems/requirement.rb
@@ -133,6 +133,7 @@ class Gem::Requirement
@requirements = [DefaultRequirement]
else
@requirements = requirements.map! { |r| self.class.parse r }
+ sort_requirements!
end
end
@@ -146,6 +147,7 @@ class Gem::Requirement
new = new.map { |r| self.class.parse r }
@requirements.concat new
+ sort_requirements!
end
##
@@ -183,11 +185,11 @@ class Gem::Requirement
end
def as_list # :nodoc:
- requirements.map { |op, version| "#{op} #{version}" }.sort
+ requirements.map { |op, version| "#{op} #{version}" }
end
def hash # :nodoc:
- requirements.sort.hash
+ requirements.hash
end
def marshal_dump # :nodoc:
@@ -264,7 +266,8 @@ class Gem::Requirement
end
def == other # :nodoc:
- Gem::Requirement === other and to_s == other.to_s
+ return unless Gem::Requirement === other
+ requirements == other.requirements
end
private
@@ -279,6 +282,14 @@ class Gem::Requirement
end
end
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)
+ end
+ end
end
class Gem::Version
diff --git a/lib/rubygems/resolver.rb b/lib/rubygems/resolver.rb
index 13ee035e4c..866998dc59 100644
--- a/lib/rubygems/resolver.rb
+++ b/lib/rubygems/resolver.rb
@@ -171,7 +171,7 @@ class Gem::Resolver
include Molinillo::UI
def output
- @output ||= debug? ? $stdout : File.open(Gem::Util::NULL_DEVICE, 'w')
+ @output ||= debug? ? $stdout : File.open(IO::NULL, 'w')
end
def debug?
@@ -314,11 +314,6 @@ class Gem::Resolver
end
-##
-# TODO remove in RubyGems 3
-
-Gem::DependencyResolver = Gem::Resolver # :nodoc:
-
require 'rubygems/resolver/activation_request'
require 'rubygems/resolver/conflict'
require 'rubygems/resolver/dependency_request'
diff --git a/lib/rubygems/resolver/api_specification.rb b/lib/rubygems/resolver/api_specification.rb
index 1e22dd0b6f..c4e8a7cb54 100644
--- a/lib/rubygems/resolver/api_specification.rb
+++ b/lib/rubygems/resolver/api_specification.rb
@@ -21,6 +21,7 @@ class Gem::Resolver::APISpecification < Gem::Resolver::Specification
@name = api_data[:name]
@version = Gem::Version.new api_data[:number]
@platform = Gem::Platform.new api_data[:platform]
+ @original_platform = api_data[:platform]
@dependencies = api_data[:dependencies].map do |name, ver|
Gem::Dependency.new name, ver.split(/\s*,\s*/)
end
@@ -73,7 +74,11 @@ class Gem::Resolver::APISpecification < Gem::Resolver::Specification
@spec ||=
begin
tuple = Gem::NameTuple.new @name, @version, @platform
+ source.fetch_spec tuple
+ rescue Gem::RemoteFetcher::FetchError
+ raise if @original_platform == @platform
+ tuple = Gem::NameTuple.new @name, @version, @original_platform
source.fetch_spec tuple
end
end
diff --git a/lib/rubygems/security/trust_dir.rb b/lib/rubygems/security/trust_dir.rb
index 849cf3cd3e..62dbe29e4e 100644
--- a/lib/rubygems/security/trust_dir.rb
+++ b/lib/rubygems/security/trust_dir.rb
@@ -93,8 +93,9 @@ class Gem::Security::TrustDir
destination = cert_path certificate
- File.open destination, 'wb', @permissions[:trusted_cert] do |io|
+ File.open destination, 'wb', 0600 do |io|
io.write certificate.to_pem
+ io.chmod(@permissions[:trusted_cert])
end
end
diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb
index 49cd50794f..2fe315f4f6 100644
--- a/lib/rubygems/specification.rb
+++ b/lib/rubygems/specification.rb
@@ -13,9 +13,9 @@ require 'rubygems/platform'
require 'rubygems/deprecate'
require 'rubygems/basic_specification'
require 'rubygems/stub_specification'
+require 'rubygems/specification_policy'
require 'rubygems/util/list'
require 'stringio'
-require 'uri'
##
# The Specification class contains the information for a Gem. Typically
@@ -135,7 +135,7 @@ class Gem::Specification < Gem::BasicSpecification
:autorequire => nil,
:bindir => 'bin',
:cert_chain => [],
- :date => TODAY,
+ :date => nil,
:dependencies => [],
:description => nil,
:email => nil,
@@ -788,52 +788,22 @@ class Gem::Specification < Gem::BasicSpecification
end
private_class_method :installed_stubs
- if [].respond_to? :flat_map
- def self.map_stubs(dirs, pattern) # :nodoc:
- dirs.flat_map { |dir|
- base_dir = File.dirname dir
- gems_dir = File.join base_dir, "gems"
- gemspec_stubs_in(dir, pattern) { |path| yield path, base_dir, gems_dir }
- }
- end
- else # FIXME: remove when 1.8 is dropped
- def self.map_stubs(dirs, pattern) # :nodoc:
- dirs.map { |dir|
- base_dir = File.dirname dir
- gems_dir = File.join base_dir, "gems"
- gemspec_stubs_in(dir, pattern) { |path| yield path, base_dir, gems_dir }
- }.flatten 1
- end
+ def self.map_stubs(dirs, pattern) # :nodoc:
+ dirs.flat_map { |dir|
+ base_dir = File.dirname dir
+ gems_dir = File.join base_dir, "gems"
+ gemspec_stubs_in(dir, pattern) { |path| yield path, base_dir, gems_dir }
+ }
end
private_class_method :map_stubs
- uniq_takes_a_block = false
- [1,2].uniq { uniq_takes_a_block = true }
-
- if uniq_takes_a_block
- def self.uniq_by(list, &block) # :nodoc:
- list.uniq(&block)
- end
- else # FIXME: remove when 1.8 is dropped
- def self.uniq_by(list) # :nodoc:
- values = {}
- list.each { |item|
- value = yield item
- values[value] ||= item
- }
- values.values
- end
+ def self.uniq_by(list, &block) # :nodoc:
+ list.uniq(&block)
end
private_class_method :uniq_by
- if [].respond_to? :sort_by!
- def self.sort_by! list, &block
- list.sort_by!(&block)
- end
- else # FIXME: remove when 1.8 is dropped
- def self.sort_by! list, &block
- list.replace list.sort_by(&block)
- end
+ def self.sort_by! list, &block
+ list.sort_by!(&block)
end
private_class_method :sort_by!
@@ -1185,11 +1155,7 @@ class Gem::Specification < Gem::BasicSpecification
file = file.dup.untaint
return unless File.file?(file)
- code = if defined? Encoding
- File.read file, :mode => 'r:UTF-8:-'
- else
- File.read file
- end
+ code = File.read file, :mode => 'r:UTF-8:-'
code.untaint
@@ -1747,13 +1713,16 @@ class Gem::Specification < Gem::BasicSpecification
}
end
- ##
- # The date this gem was created. Lazily defaults to the current UTC date.
+ # The date this gem was created.
#
- # There is no need to set this in your gem specification.
+ # If SOURCE_DATE_EPOCH is set as an environment variable, use that to support
+ # reproducible builds; otherwise, default to the current UTC date.
+ #
+ # Details on SOURCE_DATE_EPOCH:
+ # https://reproducible-builds.org/specs/source-date-epoch/
def date
- @date ||= TODAY
+ @date ||= ENV["SOURCE_DATE_EPOCH"] ? Time.utc(*Time.at(ENV["SOURCE_DATE_EPOCH"].to_i).utc.to_a[3..5].reverse) : TODAY
end
DateLike = Object.new # :nodoc:
@@ -2620,32 +2589,23 @@ class Gem::Specification < Gem::BasicSpecification
end
def to_yaml(opts = {}) # :nodoc:
- if (YAML.const_defined?(:ENGINE) && !YAML::ENGINE.syck?) ||
- (defined?(Psych) && YAML == Psych) then
- # Because the user can switch the YAML engine behind our
- # back, we have to check again here to make sure that our
- # psych code was properly loaded, and load it if not.
- unless Gem.const_defined?(:NoAliasYAMLTree)
- require 'rubygems/psych_tree'
- end
+ # Because the user can switch the YAML engine behind our
+ # back, we have to check again here to make sure that our
+ # psych code was properly loaded, and load it if not.
+ unless Gem.const_defined?(:NoAliasYAMLTree)
+ require 'rubygems/psych_tree'
+ end
- builder = Gem::NoAliasYAMLTree.create
- builder << self
- ast = builder.tree
+ builder = Gem::NoAliasYAMLTree.create
+ builder << self
+ ast = builder.tree
- io = StringIO.new
- io.set_encoding Encoding::UTF_8 if Object.const_defined? :Encoding
+ io = StringIO.new
+ io.set_encoding Encoding::UTF_8
- Psych::Visitors::Emitter.new(io).accept(ast)
+ Psych::Visitors::Emitter.new(io).accept(ast)
- io.string.gsub(/ !!null \n/, " \n")
- else
- YAML.quick_emit object_id, opts do |out|
- out.map taguri, to_yaml_style do |map|
- encode_with map
- end
- end
- end
+ io.string.gsub(/ !!null \n/, " \n")
end
##
@@ -2691,330 +2651,39 @@ class Gem::Specification < Gem::BasicSpecification
extend Gem::UserInteraction
normalize
- nil_attributes = self.class.non_nil_attributes.find_all do |attrname|
- instance_variable_get("@#{attrname}").nil?
- end
-
- unless nil_attributes.empty? then
- raise Gem::InvalidSpecificationException,
- "#{nil_attributes.join ', '} must not be nil"
- end
-
- if packaging and rubygems_version != Gem::VERSION then
- raise Gem::InvalidSpecificationException,
- "expected RubyGems version #{Gem::VERSION}, was #{rubygems_version}"
- end
-
- @@required_attributes.each do |symbol|
- unless self.send symbol then
- raise Gem::InvalidSpecificationException,
- "missing value for attribute #{symbol}"
- end
- end
-
- 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
-
- if raw_require_paths.empty? then
- raise Gem::InvalidSpecificationException,
- 'specification must have at least one require_path'
- end
-
- @files.delete_if { |x| File.directory?(x) && !File.symlink?(x) }
- @test_files.delete_if { |x| File.directory?(x) && !File.symlink?(x) }
- @executables.delete_if { |x| File.directory?(File.join(@bindir, x)) }
- @extra_rdoc_files.delete_if { |x| File.directory?(x) && !File.symlink?(x) }
- @extensions.delete_if { |x| File.directory?(x) && !File.symlink?(x) }
-
- non_files = files.reject { |x| File.file?(x) || File.symlink?(x) }
-
- unless not packaging or non_files.empty? then
- raise Gem::InvalidSpecificationException,
- "[\"#{non_files.join "\", \""}\"] are not files"
- end
-
- if files.include? file_name then
- raise Gem::InvalidSpecificationException,
- "#{full_name} contains itself (#{file_name}), check your files list"
- end
-
- unless specification_version.is_a?(Integer)
- raise Gem::InvalidSpecificationException,
- 'specification_version must be an Integer (did you mean version?)'
- end
-
- case platform
- when Gem::Platform, Gem::Platform::RUBY then # ok
- else
- raise Gem::InvalidSpecificationException,
- "invalid platform #{platform.inspect}, see Gem::Platform"
- end
-
- self.class.array_attributes.each do |field|
- val = self.send field
- klass = case field
- when :dependencies
- Gem::Dependency
- else
- String
- end
-
- unless Array === val and val.all? { |x| x.kind_of?(klass) } then
- raise(Gem::InvalidSpecificationException,
- "#{field} must be an Array of #{klass}")
- end
- end
-
- [:authors].each do |field|
- val = self.send field
- raise Gem::InvalidSpecificationException, "#{field} may not be empty" if
- val.empty?
- end
-
- unless Hash === metadata
- raise Gem::InvalidSpecificationException,
- 'metadata must be a hash'
- end
-
- validate_metadata
-
- licenses.each { |license|
- if license.length > 64
- raise Gem::InvalidSpecificationException,
- "each license must be 64 characters or less"
- end
-
- if !Gem::Licenses.match?(license)
- suggestions = Gem::Licenses.suggestions(license)
- message = <<-warning
-license value '#{license}' is invalid. Use a license identifier from
-http://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard license.
- warning
- message += "Did you mean #{suggestions.map { |s| "'#{s}'"}.join(', ')}?\n" unless suggestions.nil?
- warning(message)
- end
- }
-
- warning <<-warning if licenses.empty?
-licenses is empty, but is recommended. Use a license identifier from
-http://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard license.
- warning
-
- validate_permissions
-
- # reject lazy developers:
-
- lazy = '"FIxxxXME" or "TOxxxDO"'.gsub(/xxx/, '')
-
- unless authors.grep(/FI XME|TO DO/x).empty? then
- raise Gem::InvalidSpecificationException, "#{lazy} is not an author"
- end
-
- unless Array(email).grep(/FI XME|TO DO/x).empty? then
- raise Gem::InvalidSpecificationException, "#{lazy} is not an email"
- end
-
- if description =~ /FI XME|TO DO/x then
- raise Gem::InvalidSpecificationException, "#{lazy} is not a description"
- end
-
- if summary =~ /FI XME|TO DO/x then
- raise Gem::InvalidSpecificationException, "#{lazy} is not a summary"
- end
-
- # Make sure a homepage is valid HTTP/HTTPS URI
- if homepage and not homepage.empty?
- 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
-
- # Warnings
-
- %w[author homepage summary files].each do |attribute|
- value = self.send attribute
- warning "no #{attribute} specified" if value.nil? or value.empty?
- end
-
- if description == summary then
- warning 'description and summary are identical'
- end
-
- # TODO: raise at some given date
- warning "deprecated autorequire specified" if autorequire
-
- executables.each do |executable|
- executable_path = File.join(bindir, executable)
- shebang = File.read(executable_path, 2) == '#!'
-
- warning "#{executable_path} is missing #! line" unless shebang
- end
-
- files.each do |file|
- next unless File.symlink?(file)
- warning "#{file} is a symlink, which is not supported on all platforms"
- end
-
- validate_dependencies
-
- true
+ 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 validate_metadata
- url_validation_regex = %r{\Ahttps?:\/\/([^\s:@]+:[^\s:@]*@)?[A-Za-z\d\-]+(\.[A-Za-z\d\-]+)+\.?(:\d{1,5})?([\/?]\S*)?\z}
- link_keys = %w(
- bug_tracker_uri
- changelog_uri
- documentation_uri
- homepage_uri
- mailing_list_uri
- source_code_uri
- wiki_uri
- )
-
- metadata.each do|key, value|
- if !key.kind_of?(String)
- raise Gem::InvalidSpecificationException,
- "metadata keys must be a String"
- end
-
- if key.size > 128
- raise Gem::InvalidSpecificationException,
- "metadata key too large (#{key.size} > 128)"
- end
-
- if !value.kind_of?(String)
- raise Gem::InvalidSpecificationException,
- "metadata values must be a String"
- end
-
- if value.size > 1024
- raise Gem::InvalidSpecificationException,
- "metadata value too large (#{value.size} > 1024)"
- end
+ def keep_only_files_and_directories
+ @executables.delete_if { |x| File.directory?(File.join(@bindir, x)) }
+ @extensions.delete_if { |x| File.directory?(x) && !File.symlink?(x) }
+ @extra_rdoc_files.delete_if { |x| File.directory?(x) && !File.symlink?(x) }
+ @files.delete_if { |x| File.directory?(x) && !File.symlink?(x) }
+ @test_files.delete_if { |x| File.directory?(x) && !File.symlink?(x) }
+ end
- if link_keys.include? key
- if value !~ url_validation_regex
- raise Gem::InvalidSpecificationException,
- "metadata['#{key}'] has invalid link: #{value.inspect}"
- end
- end
- end
+ def validate_metadata
+ Gem::SpecificationPolicy.new(self).validate_metadata
end
##
# Checks that dependencies use requirements as we recommend. Warnings are
# issued when dependencies are open-ended or overly strict for semantic
# versioning.
-
- def validate_dependencies # :nodoc:
- # NOTE: see REFACTOR note in Gem::Dependency about types - this might be brittle
- seen = Gem::Dependency::TYPES.inject({}) { |types, type| types.merge({ type => {}}) }
-
- error_messages = []
- warning_messages = []
- dependencies.each do |dep|
- if prev = seen[dep.type][dep.name] then
- error_messages << <<-MESSAGE
-duplicate dependency on #{dep}, (#{prev.requirement}) use:
- add_#{dep.type}_dependency '#{dep.name}', '#{dep.requirement}', '#{prev.requirement}'
- MESSAGE
- end
-
- seen[dep.type][dep.name] = dep
-
- prerelease_dep = dep.requirements_list.any? do |req|
- Gem::Requirement.new(req).prerelease?
- end
-
- 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
-
- 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}'
- WARNING
- end
-
- open_ended = dep.requirement.requirements.all? do |op, version|
- not version.prerelease? and (op == '>' or op == '>=')
- end
-
- if open_ended then
- op, dep_version = dep.requirement.requirements.first
-
- base = dep_version.segments.first 2
-
- bugfix = if op == '>' then
- ", '> #{dep_version}'"
- elsif op == '>=' and base != dep_version.segments then
- ", '>= #{dep_version}'"
- end
-
- warning_messages << <<-WARNING
-open-ended dependency on #{dep} is not recommended
- if #{dep.name} is semantically versioned, use:
- add_#{dep.type}_dependency '#{dep.name}', '~> #{base.join '.'}'#{bugfix}
- WARNING
- end
- end
- if error_messages.any?
- raise Gem::InvalidSpecificationException, error_messages.join
- end
- if warning_messages.any?
- warning_messages.each { |warning_message| warning warning_message }
- end
+ def validate_dependencies
+ Gem::SpecificationPolicy.new(self).validate_dependencies
end
##
# Checks to see if the files to be packaged are world-readable.
-
def validate_permissions
- return if Gem.win_platform?
-
- 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
- next unless File.file?(exec)
- next if File.stat(exec).executable?
- warning "#{exec} is not executable"
- end
+ Gem::SpecificationPolicy.new(self).validate_permissions
end
##
@@ -3024,7 +2693,11 @@ open-ended dependency on #{dep} is not recommended
def version= version
@version = Gem::Version.create(version)
- self.required_rubygems_version = '> 1.3.1' if @version.prerelease?
+ # skip to set required_ruby_version when pre-released rubygems.
+ # It caused to raise CircularDependencyError
+ if @version.prerelease? && @name.strip != "rubygems"
+ self.required_rubygems_version = '> 1.3.1'
+ end
invalidate_memoized_attributes
return @version
diff --git a/lib/rubygems/specification_policy.rb b/lib/rubygems/specification_policy.rb
new file mode 100644
index 0000000000..a05edcd5c9
--- /dev/null
+++ b/lib/rubygems/specification_policy.rb
@@ -0,0 +1,410 @@
+require 'delegate'
+require 'uri'
+
+class Gem::SpecificationPolicy < SimpleDelegator
+ VALID_NAME_PATTERN = /\A[a-zA-Z0-9\.\-\_]+\z/ # :nodoc:
+
+ VALID_URI_PATTERN = %r{\Ahttps?:\/\/([^\s:@]+:[^\s:@]*@)?[A-Za-z\d\-]+(\.[A-Za-z\d\-]+)+\.?(:\d{1,5})?([\/?]\S*)?\z} # :nodoc:
+
+ METADATA_LINK_KEYS = %w[
+ bug_tracker_uri
+ changelog_uri
+ documentation_uri
+ homepage_uri
+ mailing_list_uri
+ source_code_uri
+ wiki_uri
+ ] # :nodoc:
+
+ ##
+ # If set to true, run packaging-specific checks, as well.
+
+ attr_accessor :packaging
+
+ ##
+ # Checks that the specification contains all required fields, and does a
+ # very basic sanity check.
+ #
+ # Raises InvalidSpecificationException if the spec does not pass the
+ # checks.
+
+ def validate
+ validate_nil_attributes
+
+ validate_rubygems_version
+
+ validate_required_attributes
+
+ validate_name
+
+ validate_require_paths
+
+ keep_only_files_and_directories
+
+ validate_non_files
+
+ validate_self_inclusion_in_files_list
+
+ validate_specification_version
+
+ validate_platform
+
+ validate_array_attributes
+
+ validate_authors_field
+
+ validate_metadata
+
+ validate_licenses
+
+ validate_permissions
+
+ validate_lazy_metadata
+
+ validate_values
+
+ validate_dependencies
+ true
+ end
+
+ ##
+ # Implementation for Specification#validate_metadata
+
+ 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
+ end
+
+ ##
+ # Implementation for Specification#validate_dependencies
+
+ def validate_dependencies # :nodoc:
+ # NOTE: see REFACTOR note in Gem::Dependency about types - this might be brittle
+ seen = Gem::Dependency::TYPES.inject({}) { |types, type| types.merge({ type => {}}) }
+
+ error_messages = []
+ warning_messages = []
+ dependencies.each do |dep|
+ if prev = seen[dep.type][dep.name] then
+ error_messages << <<-MESSAGE
+duplicate dependency on #{dep}, (#{prev.requirement}) use:
+ add_#{dep.type}_dependency '#{dep.name}', '#{dep.requirement}', '#{prev.requirement}'
+ MESSAGE
+ end
+
+ seen[dep.type][dep.name] = dep
+
+ prerelease_dep = dep.requirements_list.any? do |req|
+ Gem::Requirement.new(req).prerelease?
+ end
+
+ 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
+
+ if open_ended then
+ op, dep_version = dep.requirement.requirements.first
+
+ base = dep_version.segments.first 2
+
+ bugfix = if op == '>' then
+ ", '> #{dep_version}'"
+ elsif op == '>=' and base != dep_version.segments then
+ ", '>= #{dep_version}'"
+ end
+
+ warning_messages << <<-WARNING
+open-ended dependency on #{dep} is not recommended
+ if #{dep.name} is semantically versioned, use:
+ add_#{dep.type}_dependency '#{dep.name}', '~> #{base.join '.'}'#{bugfix}
+ WARNING
+ 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 }
+ end
+ end
+
+ ##
+ # Issues a warning for each file to be packaged which is world-readable.
+ #
+ # Implementation for Specification#validate_permissions
+
+ def validate_permissions
+ return if Gem.win_platform?
+
+ 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
+ next unless File.file?(exec)
+ next if File.stat(exec).executable?
+ warning "#{exec} is not executable"
+ end
+ end
+
+ private
+
+ def validate_nil_attributes
+ nil_attributes = Gem::Specification.non_nil_attributes.select do |attrname|
+ __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
+ return unless packaging
+ 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
+
+ def validate_array_attributes
+ Gem::Specification.array_attributes.each do |field|
+ validate_array_attribute(field)
+ end
+ end
+
+ def validate_array_attribute(field)
+ val = self.send(field)
+ klass = case field
+ when :dependencies then
+ Gem::Dependency
+ else
+ String
+ end
+
+ unless Array === val and val.all? {|x| x.kind_of?(klass)} then
+ raise(Gem::InvalidSpecificationException,
+ "#{field} must be an Array of #{klass}")
+ end
+ end
+
+ 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
+ suggestions = Gem::Licenses.suggestions(license)
+ message = <<-warning
+license value '#{license}' is invalid. Use a license identifier from
+http://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard license.
+ warning
+ message += "Did you mean #{suggestions.map { |s| "'#{s}'"}.join(', ')}?\n" unless suggestions.nil?
+ warning(message)
+ end
+ }
+
+ warning <<-warning if licenses.empty?
+licenses is empty, but is recommended. Use a license identifier from
+http://spdx.org/licenses or '#{Gem::Licenses::NONSTANDARD}' for a nonstandard license.
+ warning
+ end
+
+ LAZY = '"FIxxxXME" or "TOxxxDO"'.gsub(/xxx/, '')
+ LAZY_PATTERN = /FI XME|TO DO/x
+ HOMEPAGE_URI_PATTERN = /\A[a-z][a-z\d+.-]*:/i
+
+ 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
+ if homepage and not homepage.empty?
+ 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
+
+ def validate_values
+ %w[author homepage summary files].each do |attribute|
+ validate_attribute_present(attribute)
+ end
+
+ if description == summary then
+ warning "description and summary are identical"
+ end
+
+ # TODO: raise at some given date
+ warning "deprecated autorequire specified" if autorequire
+
+ executables.each do |executable|
+ validate_shebang_line_in(executable)
+ end
+
+ 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
+ warning("no #{attribute} specified") if value.nil? || value.empty?
+ end
+
+ def validate_shebang_line_in(executable)
+ executable_path = File.join(bindir, executable)
+ return if File.read(executable_path, 2) == '#!'
+
+ warning "#{executable_path} is missing #! line"
+ end
+end
diff --git a/lib/rubygems/stub_specification.rb b/lib/rubygems/stub_specification.rb
index ae2effbc84..62689c21f9 100644
--- a/lib/rubygems/stub_specification.rb
+++ b/lib/rubygems/stub_specification.rb
@@ -8,12 +8,8 @@ class Gem::StubSpecification < Gem::BasicSpecification
# :nodoc:
PREFIX = "# stub: "
- OPEN_MODE = # :nodoc:
- if Object.const_defined? :Encoding then
- 'r:UTF-8:-'
- else
- 'r'
- end
+ # :nodoc:
+ OPEN_MODE = 'r:UTF-8:-'
class StubLine # :nodoc: all
attr_reader :name, :version, :platform, :require_paths, :extensions,
diff --git a/lib/rubygems/test_case.rb b/lib/rubygems/test_case.rb
index f03515cc8f..f1cd3d274c 100644
--- a/lib/rubygems/test_case.rb
+++ b/lib/rubygems/test_case.rb
@@ -2,7 +2,7 @@
# TODO: $SAFE = 1
begin
- gem 'minitest', '~> 4.0'
+ gem 'minitest', '~> 5.0'
rescue NoMethodError, Gem::LoadError
# for ruby tests
end
@@ -18,6 +18,16 @@ begin
rescue Gem::LoadError
end
+begin
+ require 'simplecov'
+ SimpleCov.start do
+ add_filter "/test/"
+ add_filter "/bundler/"
+ add_filter "/lib/rubygems/resolver/molinillo"
+ end
+rescue LoadError
+end
+
# We have to load these up front because otherwise we'll try to load
# them while we're testing rubygems, and thus we can't actually load them.
unless Gem::Dependency.new('rdoc', '>= 3.10').matching_specs.empty?
@@ -86,7 +96,7 @@ end
#
# Tests are always run at a safe level of 1.
-class Gem::TestCase < MiniTest::Unit::TestCase
+class Gem::TestCase < (defined?(Minitest::Test) ? Minitest::Test : MiniTest::Unit::TestCase)
extend Gem::Deprecate
@@ -595,7 +605,7 @@ class Gem::TestCase < MiniTest::Unit::TestCase
def mu_pp(obj)
s = String.new
s = PP.pp obj, s
- s = s.force_encoding(Encoding.default_external) if defined? Encoding
+ s = s.force_encoding(Encoding.default_external)
s.chomp
end
@@ -819,8 +829,7 @@ class Gem::TestCase < MiniTest::Unit::TestCase
spec
end
- # TODO: mark deprecate after replacing util_spec from new_spec
- # deprecate :new_spec, :none, 2018, 12
+ deprecate :new_spec, :none, 2018, 12
def new_default_spec(name, version, deps = nil, *files)
spec = util_spec name, version, deps
@@ -845,7 +854,7 @@ class Gem::TestCase < MiniTest::Unit::TestCase
# Creates a spec with +name+, +version+. +deps+ can specify the dependency
# or a +block+ can be given for full customization of the specification.
- def util_spec name, version = 2, deps = nil # :yields: specification
+ def util_spec name, version = 2, deps = nil, *files # :yields: specification
raise "deps or block, not both" if deps and block_given?
spec = Gem::Specification.new do |s|
@@ -858,6 +867,8 @@ class Gem::TestCase < MiniTest::Unit::TestCase
s.summary = "this is a summary"
s.description = "This is a test description"
+ s.files.push(*files) unless files.empty?
+
yield s if block_given?
end
@@ -869,6 +880,19 @@ class Gem::TestCase < MiniTest::Unit::TestCase
end
end
+ unless files.empty? then
+ write_file spec.spec_file do |io|
+ io.write spec.to_ruby_for_cache
+ end
+
+ util_build_gem spec
+
+ cache_file = File.join @tempdir, 'gems', "#{spec.full_name}.gem"
+ FileUtils.mkdir_p File.dirname cache_file
+ FileUtils.mv spec.cache_file, cache_file
+ FileUtils.rm spec.spec_file
+ end
+
Gem::Specification.reset
return spec
diff --git a/lib/rubygems/uninstaller.rb b/lib/rubygems/uninstaller.rb
index 89f47a45fe..3059099373 100644
--- a/lib/rubygems/uninstaller.rb
+++ b/lib/rubygems/uninstaller.rb
@@ -213,8 +213,8 @@ class Gem::Uninstaller
exe_file = File.join bin_dir, exe_name
- FileUtils.rm_f exe_file
- FileUtils.rm_f "#{exe_file}.bat"
+ safe_delete { FileUtils.rm exe_file }
+ safe_delete { FileUtils.rm "#{exe_file}.bat" }
end
else
say "Executables and scripts will remain installed."
@@ -250,26 +250,26 @@ class Gem::Uninstaller
raise Gem::FilePermissionError, spec.base_dir unless
File.writable?(spec.base_dir)
- FileUtils.rm_rf spec.full_gem_path
- FileUtils.rm_rf spec.extension_dir
+ safe_delete { FileUtils.rm_r spec.full_gem_path }
+ safe_delete { FileUtils.rm_r spec.extension_dir }
old_platform_name = spec.original_name
- gemspec = spec.spec_file
-
- unless File.exist? gemspec then
- gemspec = File.join(File.dirname(gemspec), "#{old_platform_name}.gemspec")
- end
-
- FileUtils.rm_rf gemspec
gem = spec.cache_file
gem = File.join(spec.cache_dir, "#{old_platform_name}.gem") unless
File.exist? gem
- FileUtils.rm_rf gem
+ safe_delete { FileUtils.rm_r gem }
Gem::RDoc.new(spec).remove
+ gemspec = spec.spec_file
+
+ unless File.exist? gemspec then
+ gemspec = File.join(File.dirname(gemspec), "#{old_platform_name}.gemspec")
+ end
+
+ safe_delete { FileUtils.rm_r gemspec }
say "Successfully uninstalled #{spec.full_name}"
Gem::Specification.reset
@@ -343,4 +343,15 @@ class Gem::Uninstaller
filename
end
end
+
+ def safe_delete(&block)
+ block.call
+ rescue Errno::ENOENT
+ nil
+ rescue Errno::EPERM
+ e = Gem::UninstallError.new
+ e.spec = @spec
+
+ raise e
+ end
end
diff --git a/lib/rubygems/user_interaction.rb b/lib/rubygems/user_interaction.rb
index 91236eec15..6a5aecf1b7 100644
--- a/lib/rubygems/user_interaction.rb
+++ b/lib/rubygems/user_interaction.rb
@@ -205,11 +205,7 @@ class Gem::StreamUI
# Returns true if TTY methods should be used on this StreamUI.
def tty?
- if RUBY_VERSION < '1.9.3' and RUBY_PLATFORM =~ /mingw|mswin/ then
- @usetty
- else
- @usetty && @ins.tty?
- end
+ @usetty && @ins.tty?
end
##
@@ -324,29 +320,7 @@ class Gem::StreamUI
def _gets_noecho
require_io_console
- if IO.method_defined?(:noecho) then
- @ins.noecho {@ins.gets}
- elsif Gem.win_platform?
- require "Win32API"
- password = ''
-
- while char = Win32API.new("crtdll", "_getch", [ ], "L").Call do
- break if char == 10 || char == 13 # received carriage return or newline
- if char == 127 || char == 8 # backspace and delete
- password.slice!(-1, 1)
- else
- password << char.chr
- end
- end
- password
- else
- system "stty -echo"
- begin
- @ins.gets
- ensure
- system "stty echo"
- end
- end
+ @ins.noecho {@ins.gets}
end
##
@@ -684,8 +658,8 @@ class Gem::SilentUI < Gem::StreamUI
def initialize
reader, writer = nil, nil
- reader = File.open(Gem::Util::NULL_DEVICE, 'r')
- writer = File.open(Gem::Util::NULL_DEVICE, 'w')
+ reader = File.open(IO::NULL, 'r')
+ writer = File.open(IO::NULL, 'w')
super reader, writer, writer, false
end
diff --git a/lib/rubygems/util.rb b/lib/rubygems/util.rb
index 6c75910004..9f5b9a2239 100644
--- a/lib/rubygems/util.rb
+++ b/lib/rubygems/util.rb
@@ -15,7 +15,7 @@ module Gem::Util
data = StringIO.new(data, 'r')
unzipped = Zlib::GzipReader.new(data).read
- unzipped.force_encoding Encoding::BINARY if Object.const_defined? :Encoding
+ unzipped.force_encoding Encoding::BINARY
unzipped
end
@@ -26,7 +26,7 @@ module Gem::Util
require 'zlib'
require 'stringio'
zipped = StringIO.new(String.new, 'w')
- zipped.set_encoding Encoding::BINARY if Object.const_defined? :Encoding
+ zipped.set_encoding Encoding::BINARY
Zlib::GzipWriter.wrap zipped do |io| io.write data end
@@ -67,13 +67,11 @@ module Gem::Util
end
end
- NULL_DEVICE = defined?(IO::NULL) ? IO::NULL : Gem.win_platform? ? 'NUL' : '/dev/null'
-
##
# Invokes system, but silences all output.
def self.silent_system *command
- opt = {:out => NULL_DEVICE, :err => [:child, :out]}
+ opt = {:out => IO::NULL, :err => [:child, :out]}
if Hash === command.last
opt.update(command.last)
cmds = command[0...-1]
@@ -86,15 +84,13 @@ module Gem::Util
@silent_mutex ||= Mutex.new
- null_device = NULL_DEVICE
-
@silent_mutex.synchronize do
begin
stdout = STDOUT.dup
stderr = STDERR.dup
- STDOUT.reopen null_device, 'w'
- STDERR.reopen null_device, 'w'
+ STDOUT.reopen IO::NULL, 'w'
+ STDERR.reopen IO::NULL, 'w'
return system(*command)
ensure
diff --git a/lib/rubygems/util/licenses.rb b/lib/rubygems/util/licenses.rb
index 96fed282f1..2a536575c0 100644
--- a/lib/rubygems/util/licenses.rb
+++ b/lib/rubygems/util/licenses.rb
@@ -8,7 +8,7 @@ class Gem::Licenses
# Software Package Data Exchange (SPDX) standard open-source software
# license identifiers
- IDENTIFIERS = %w(
+ LICENSE_IDENTIFIERS = %w(
0BSD
AAL
ADSL
@@ -19,6 +19,8 @@ class Gem::Licenses
AFL-3.0
AGPL-1.0
AGPL-3.0
+ AGPL-3.0-only
+ AGPL-3.0-or-later
AMDPLPA
AML
AMPAS
@@ -41,9 +43,11 @@ class Gem::Licenses
Artistic-1.0-Perl
Artistic-1.0-cl8
Artistic-2.0
+ BSD-1-Clause
BSD-2-Clause
BSD-2-Clause-FreeBSD
BSD-2-Clause-NetBSD
+ BSD-2-Clause-Patent
BSD-3-Clause
BSD-3-Clause-Attribution
BSD-3-Clause-Clear
@@ -96,6 +100,8 @@ class Gem::Licenses
CC0-1.0
CDDL-1.0
CDDL-1.1
+ CDLA-Permissive-1.0
+ CDLA-Sharing-1.0
CECILL-1.0
CECILL-1.1
CECILL-2.0
@@ -124,9 +130,11 @@ class Gem::Licenses
EFL-1.0
EFL-2.0
EPL-1.0
+ EPL-2.0
EUDatagrid
EUPL-1.0
EUPL-1.1
+ EUPL-1.2
Entessa
ErlPL-1.1
Eurosym
@@ -138,13 +146,23 @@ class Gem::Licenses
Frameworx-1.0
FreeImage
GFDL-1.1
+ GFDL-1.1-only
+ GFDL-1.1-or-later
GFDL-1.2
+ GFDL-1.2-only
+ GFDL-1.2-or-later
GFDL-1.3
+ GFDL-1.3-only
+ GFDL-1.3-or-later
GL2PS
GPL-1.0
GPL-1.0+
+ GPL-1.0-only
+ GPL-1.0-or-later
GPL-2.0
GPL-2.0+
+ GPL-2.0-only
+ GPL-2.0-or-later
GPL-2.0-with-GCC-exception
GPL-2.0-with-autoconf-exception
GPL-2.0-with-bison-exception
@@ -152,6 +170,8 @@ class Gem::Licenses
GPL-2.0-with-font-exception
GPL-3.0
GPL-3.0+
+ GPL-3.0-only
+ GPL-3.0-or-later
GPL-3.0-with-GCC-exception
GPL-3.0-with-autoconf-exception
Giftware
@@ -177,10 +197,16 @@ class Gem::Licenses
LAL-1.3
LGPL-2.0
LGPL-2.0+
+ LGPL-2.0-only
+ LGPL-2.0-or-later
LGPL-2.1
LGPL-2.1+
+ LGPL-2.1-only
+ LGPL-2.1-or-later
LGPL-3.0
LGPL-3.0+
+ LGPL-3.0-only
+ LGPL-3.0-or-later
LGPLLR
LPL-1.0
LPL-1.02
@@ -317,7 +343,6 @@ class Gem::Licenses
W3C-19980720
W3C-20150513
WTFPL
- WXwindows
Watcom-1.0
Wsuipa
X11
@@ -349,17 +374,49 @@ class Gem::Licenses
mpich2
psfrag
psutils
+ wxWindows
xinetd
xpp
zlib-acknowledgement
).freeze
+ # exception identifiers
+ EXCEPTION_IDENTIFIERS = %w(
+ 389-exception
+ Autoconf-exception-2.0
+ Autoconf-exception-3.0
+ Bison-exception-2.2
+ Bootloader-exception
+ CLISP-exception-2.0
+ Classpath-exception-2.0
+ DigiRule-FOSS-exception
+ FLTK-exception
+ Fawkes-Runtime-exception
+ Font-exception-2.0
+ GCC-exception-2.0
+ GCC-exception-3.1
+ LZMA-exception
+ Libtool-exception
+ Linux-syscall-note
+ Nokia-Qt-exception-1.1
+ OCCT-exception-1.0
+ Qwt-exception-1.0
+ WxWindows-exception-3.1
+ eCos-exception-2.0
+ freertos-exception-2.0
+ gnu-javamail-exception
+ i2p-gpl-java-exception
+ mif-exception
+ openvpn-openssl-exception
+ u-boot-exception-2.0
+ ).freeze
+
REGEXP = %r{
\A
(
- #{Regexp.union(IDENTIFIERS)}
+ #{Regexp.union(LICENSE_IDENTIFIERS)}
\+?
- (\s WITH \s .+)?
+ (\s WITH \s #{Regexp.union(EXCEPTION_IDENTIFIERS)})?
| #{NONSTANDARD}
)
\Z
@@ -370,7 +427,7 @@ class Gem::Licenses
end
def self.suggestions(license)
- by_distance = IDENTIFIERS.group_by do |identifier|
+ by_distance = LICENSE_IDENTIFIERS.group_by do |identifier|
levenshtein_distance(identifier, license)
end
lowest = by_distance.keys.min