summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHiroshi SHIBATA <hsbt@ruby-lang.org>2020-12-08 16:33:39 +0900
committerHiroshi SHIBATA <hsbt@ruby-lang.org>2020-12-08 17:30:02 +0900
commit4aca77edde91f826aa243e268bf1ef5214530583 (patch)
treeef0cf1a95fcced00ca5fa40f3412c567bf95d705
parent6a6a24df9b72750d12f9b15192bdb7517e668efb (diff)
Merge prepare version of RubyGems 3.2.0
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/3864
-rw-r--r--lib/rubygems.rb8
-rw-r--r--lib/rubygems/available_set.rb2
-rw-r--r--lib/rubygems/command_manager.rb4
-rw-r--r--lib/rubygems/commands/build_command.rb57
-rw-r--r--lib/rubygems/commands/cert_command.rb2
-rw-r--r--lib/rubygems/commands/help_command.rb2
-rw-r--r--lib/rubygems/commands/owner_command.rb12
-rw-r--r--lib/rubygems/commands/pristine_command.rb2
-rw-r--r--lib/rubygems/commands/push_command.rb10
-rw-r--r--lib/rubygems/commands/query_command.rb17
-rw-r--r--lib/rubygems/commands/server_command.rb4
-rw-r--r--lib/rubygems/commands/setup_command.rb70
-rw-r--r--lib/rubygems/commands/sources_command.rb8
-rw-r--r--lib/rubygems/commands/specification_command.rb6
-rw-r--r--lib/rubygems/commands/yank_command.rb8
-rw-r--r--lib/rubygems/core_ext/kernel_require.rb3
-rw-r--r--lib/rubygems/core_ext/kernel_warn.rb13
-rw-r--r--lib/rubygems/defaults.rb4
-rw-r--r--lib/rubygems/dependency.rb2
-rw-r--r--lib/rubygems/dependency_installer.rb7
-rw-r--r--lib/rubygems/ext/builder.rb45
-rw-r--r--lib/rubygems/ext/cmake_builder.rb8
-rw-r--r--lib/rubygems/ext/configure_builder.rb8
-rw-r--r--lib/rubygems/ext/ext_conf_builder.rb33
-rw-r--r--lib/rubygems/ext/rake_builder.rb6
-rw-r--r--lib/rubygems/gemcutter_utilities.rb104
-rw-r--r--lib/rubygems/indexer.rb1
-rw-r--r--lib/rubygems/install_update_options.rb4
-rw-r--r--lib/rubygems/installer.rb21
-rw-r--r--lib/rubygems/installer_test_case.rb13
-rw-r--r--lib/rubygems/name_tuple.rb2
-rw-r--r--lib/rubygems/openssl.rb8
-rw-r--r--lib/rubygems/package.rb5
-rw-r--r--lib/rubygems/package/tar_header.rb2
-rw-r--r--lib/rubygems/package/tar_test_case.rb2
-rw-r--r--lib/rubygems/platform.rb25
-rw-r--r--lib/rubygems/query_utils.rb9
-rw-r--r--lib/rubygems/remote_fetcher.rb3
-rw-r--r--lib/rubygems/request.rb7
-rw-r--r--lib/rubygems/request_set/gem_dependency_api.rb8
-rw-r--r--lib/rubygems/requirement.rb2
-rw-r--r--lib/rubygems/resolver.rb2
-rw-r--r--lib/rubygems/resolver/activation_request.rb10
-rw-r--r--lib/rubygems/resolver/api_specification.rb6
-rw-r--r--lib/rubygems/resolver/conflict.rb2
-rw-r--r--lib/rubygems/resolver/dependency_request.rb2
-rw-r--r--lib/rubygems/resolver/index_specification.rb11
-rw-r--r--lib/rubygems/resolver/installer_set.rb3
-rw-r--r--lib/rubygems/resolver/lock_set.rb2
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo.rb11
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/delegates/resolution_state.rb7
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/delegates/specification_provider.rb1
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb44
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/action.rb1
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb3
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_vertex.rb3
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/delete_edge.rb3
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb3
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/log.rb13
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/set_payload.rb3
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/tag.rb7
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/vertex.rb53
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/errors.rb82
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb3
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/modules/specification_provider.rb1
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/modules/ui.rb4
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb671
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/resolver.rb5
-rw-r--r--lib/rubygems/resolver/molinillo/lib/molinillo/state.rb12
-rw-r--r--lib/rubygems/resolver/specification.rb2
-rw-r--r--lib/rubygems/s3_uri_signer.rb2
-rw-r--r--lib/rubygems/security.rb3
-rw-r--r--lib/rubygems/security/policy.rb2
-rw-r--r--lib/rubygems/security/signer.rb2
-rw-r--r--lib/rubygems/server.rb2
-rw-r--r--lib/rubygems/source.rb14
-rw-r--r--lib/rubygems/spec_fetcher.rb2
-rw-r--r--lib/rubygems/specification.rb16
-rw-r--r--lib/rubygems/ssl_certs/rubygems.global.ssl.fastly.net/DigiCertHighAssuranceEVRootCA.pem23
-rw-r--r--lib/rubygems/ssl_certs/rubygems.org/GlobalSignRootCA.pem (renamed from lib/rubygems/ssl_certs/index.rubygems.org/GlobalSignRootCA.pem)0
-rw-r--r--lib/rubygems/stub_specification.rb2
-rw-r--r--lib/rubygems/test_case.rb39
-rw-r--r--lib/rubygems/uri_formatter.rb3
-rw-r--r--lib/rubygems/version_option.rb6
-rw-r--r--test/rubygems/test_bundled_ca.rb14
-rw-r--r--test/rubygems/test_gem.rb6
-rw-r--r--test/rubygems/test_gem_commands_build_command.rb202
-rw-r--r--test/rubygems/test_gem_commands_cert_command.rb4
-rw-r--r--test/rubygems/test_gem_commands_cleanup_command.rb2
-rw-r--r--test/rubygems/test_gem_commands_contents_command.rb4
-rw-r--r--test/rubygems/test_gem_commands_help_command.rb17
-rw-r--r--test/rubygems/test_gem_commands_install_command.rb2
-rw-r--r--test/rubygems/test_gem_commands_owner_command.rb50
-rw-r--r--test/rubygems/test_gem_commands_pristine_command.rb4
-rw-r--r--test/rubygems/test_gem_commands_push_command.rb36
-rw-r--r--test/rubygems/test_gem_commands_query_command.rb6
-rw-r--r--test/rubygems/test_gem_commands_setup_command.rb31
-rw-r--r--test/rubygems/test_gem_commands_signin_command.rb30
-rw-r--r--test/rubygems/test_gem_commands_sources_command.rb60
-rw-r--r--test/rubygems/test_gem_commands_specification_command.rb28
-rw-r--r--test/rubygems/test_gem_commands_update_command.rb4
-rw-r--r--test/rubygems/test_gem_commands_yank_command.rb32
-rw-r--r--test/rubygems/test_gem_dependency_installer.rb38
-rw-r--r--test/rubygems/test_gem_dependency_list.rb4
-rw-r--r--test/rubygems/test_gem_ext_builder.rb24
-rw-r--r--test/rubygems/test_gem_ext_cmake_builder.rb12
-rw-r--r--test/rubygems/test_gem_ext_configure_builder.rb12
-rw-r--r--test/rubygems/test_gem_ext_ext_conf_builder.rb35
-rw-r--r--test/rubygems/test_gem_ext_rake_builder.rb18
-rw-r--r--test/rubygems/test_gem_gem_runner.rb9
-rw-r--r--test/rubygems/test_gem_gemcutter_utilities.rb6
-rw-r--r--test/rubygems/test_gem_install_update_options.rb18
-rw-r--r--test/rubygems/test_gem_installer.rb85
-rw-r--r--test/rubygems/test_gem_package.rb16
-rw-r--r--test/rubygems/test_gem_package_old.rb8
-rw-r--r--test/rubygems/test_gem_package_tar_writer.rb2
-rw-r--r--test/rubygems/test_gem_platform.rb67
-rw-r--r--test/rubygems/test_gem_remote_fetcher.rb32
-rw-r--r--test/rubygems/test_gem_request.rb4
-rw-r--r--test/rubygems/test_gem_request_set_lockfile.rb8
-rw-r--r--test/rubygems/test_gem_resolver_api_set.rb16
-rw-r--r--test/rubygems/test_gem_resolver_conflict.rb2
-rw-r--r--test/rubygems/test_gem_resolver_vendor_set.rb2
-rw-r--r--test/rubygems/test_gem_security.rb4
-rw-r--r--test/rubygems/test_gem_security_policy.rb4
-rw-r--r--test/rubygems/test_gem_security_signer.rb4
-rw-r--r--test/rubygems/test_gem_security_trust_dir.rb4
-rw-r--r--test/rubygems/test_gem_source.rb5
-rw-r--r--test/rubygems/test_gem_source_subpath_problem.rb49
-rw-r--r--test/rubygems/test_gem_specification.rb9
-rw-r--r--test/rubygems/test_gem_uninstaller.rb14
-rw-r--r--test/rubygems/test_gem_validator.rb2
-rw-r--r--test/rubygems/test_gem_version_option.rb2
-rw-r--r--test/rubygems/test_require.rb59
134 files changed, 2003 insertions, 746 deletions
diff --git a/lib/rubygems.rb b/lib/rubygems.rb
index eabe1c45dd..e6a3c63c60 100644
--- a/lib/rubygems.rb
+++ b/lib/rubygems.rb
@@ -8,7 +8,7 @@
require 'rbconfig'
module Gem
- VERSION = "3.2.0.rc.2".freeze
+ VERSION = "3.2.0".freeze
end
# Must be first since it unloads the prelude from 1.9.2
@@ -119,6 +119,10 @@ module Gem
# to avoid deprecation warnings in Ruby 2.7.
UNTAINT = RUBY_VERSION < '2.7' ? :untaint.to_sym : proc{}
+ # When https://bugs.ruby-lang.org/issues/17259 is available, there is no need to override Kernel#warn
+ KERNEL_WARN_IGNORES_INTERNAL_ENTRIES = RUBY_ENGINE == "truffleruby" ||
+ (RUBY_ENGINE == "ruby" && RUBY_VERSION >= '3.0')
+
##
# An Array of Regexps that match windows Ruby platforms.
@@ -975,7 +979,7 @@ An Array (#{env.inspect}) was passed in from #{caller[3]}
val = RbConfig::CONFIG[key]
next unless val and not val.empty?
".#{val}"
- end
+ end,
].compact.uniq
end
diff --git a/lib/rubygems/available_set.rb b/lib/rubygems/available_set.rb
index 80ef29df64..499483d9e9 100644
--- a/lib/rubygems/available_set.rb
+++ b/lib/rubygems/available_set.rb
@@ -73,7 +73,7 @@ class Gem::AvailableSet
end
def match_platform!
- @set.reject! {|t| !Gem::Platform.match(t.spec.platform) }
+ @set.reject! {|t| !Gem::Platform.match_spec?(t.spec) }
@sorted = nil
self
end
diff --git a/lib/rubygems/command_manager.rb b/lib/rubygems/command_manager.rb
index 1dcb577f7e..97e52544ca 100644
--- a/lib/rubygems/command_manager.rb
+++ b/lib/rubygems/command_manager.rb
@@ -73,7 +73,7 @@ class Gem::CommandManager
].freeze
ALIAS_COMMANDS = {
- 'i' => 'install'
+ 'i' => 'install',
}.freeze
##
@@ -174,8 +174,8 @@ class Gem::CommandManager
else
cmd_name = args.shift.downcase
cmd = find_command cmd_name
- cmd.invoke_with_build_args args, build_args
cmd.deprecation_warning if cmd.deprecated?
+ cmd.invoke_with_build_args args, build_args
end
end
diff --git a/lib/rubygems/commands/build_command.rb b/lib/rubygems/commands/build_command.rb
index eaf8573d8f..fff5f7c76f 100644
--- a/lib/rubygems/commands/build_command.rb
+++ b/lib/rubygems/commands/build_command.rb
@@ -61,14 +61,18 @@ Gems can be saved to a specified filename with the output option:
end
def execute
- gem_name = get_one_optional_argument || find_gemspec
- build_gem(gem_name)
+ if build_path = options[:build_path]
+ Dir.chdir(build_path) { build_gem }
+ return
+ end
+
+ build_gem
end
private
- def find_gemspec
- gemspecs = Dir.glob("*.gemspec").sort
+ def find_gemspec(glob = "*.gemspec")
+ gemspecs = Dir.glob(glob).sort
if gemspecs.size > 1
alert_error "Multiple gemspecs found: #{gemspecs}, please specify one"
@@ -78,28 +82,19 @@ Gems can be saved to a specified filename with the output option:
gemspecs.first
end
- def build_gem(gem_name)
- gemspec = File.exist?(gem_name) ? gem_name : "#{gem_name}.gemspec"
-
- if File.exist?(gemspec)
- spec = Gem::Specification.load(gemspec)
-
- if options[:build_path]
- Dir.chdir(File.dirname(gemspec)) do
- spec = Gem::Specification.load(File.basename(gemspec))
- build_package(spec)
- end
- else
- build_package(spec)
- end
+ def build_gem
+ gemspec = resolve_gem_name
+ if gemspec
+ build_package(gemspec)
else
- alert_error "Gemspec file not found: #{gemspec}"
+ alert_error error_message
terminate_interaction(1)
end
end
- def build_package(spec)
+ def build_package(gemspec)
+ spec = Gem::Specification.load(gemspec)
if spec
Gem::Package.build(
spec,
@@ -112,4 +107,26 @@ Gems can be saved to a specified filename with the output option:
terminate_interaction 1
end
end
+
+ def resolve_gem_name
+ return find_gemspec unless gem_name
+
+ if File.exist?(gem_name)
+ gem_name
+ else
+ find_gemspec("#{gem_name}.gemspec") || find_gemspec(gem_name)
+ end
+ end
+
+ def error_message
+ if gem_name
+ "Couldn't find a gemspec file matching '#{gem_name}' in #{Dir.pwd}"
+ else
+ "Couldn't find a gemspec file in #{Dir.pwd}"
+ end
+ end
+
+ def gem_name
+ get_one_optional_argument
+ end
end
diff --git a/lib/rubygems/commands/cert_command.rb b/lib/rubygems/commands/cert_command.rb
index e5355d3652..998df0621b 100644
--- a/lib/rubygems/commands/cert_command.rb
+++ b/lib/rubygems/commands/cert_command.rb
@@ -311,4 +311,4 @@ For further reading on signing gems see `ri Gem::Security`.
# It's simple, but is all we need
email =~ /\A.+@.+\z/
end
-end if defined?(OpenSSL::SSL)
+end if Gem::HAVE_OPENSSL
diff --git a/lib/rubygems/commands/help_command.rb b/lib/rubygems/commands/help_command.rb
index 9ba8bf1293..4e8d7600fb 100644
--- a/lib/rubygems/commands/help_command.rb
+++ b/lib/rubygems/commands/help_command.rb
@@ -332,6 +332,8 @@ platform.
@command_manager.command_names.each do |cmd_name|
command = @command_manager[cmd_name]
+ next if command.deprecated?
+
summary =
if command
command.summary
diff --git a/lib/rubygems/commands/owner_command.rb b/lib/rubygems/commands/owner_command.rb
index 0f01823967..46172854cf 100644
--- a/lib/rubygems/commands/owner_command.rb
+++ b/lib/rubygems/commands/owner_command.rb
@@ -53,7 +53,7 @@ permission to.
def execute
@host = options[:host]
- sign_in
+ sign_in(scope: get_owner_scope)
name = get_one_gem_name
add_owners name, options[:add]
@@ -102,10 +102,18 @@ permission to.
private
def send_owner_request(method, name, owner)
- rubygems_api_request method, "api/v1/gems/#{name}/owners" do |request|
+ rubygems_api_request method, "api/v1/gems/#{name}/owners", scope: get_owner_scope(method: method) do |request|
request.set_form_data 'email' => owner
request.add_field "Authorization", api_key
request.add_field "OTP", options[:otp] if options[:otp]
end
end
+
+ def get_owner_scope(method: nil)
+ if method == :post || options.any? && options[:add].any?
+ :add_owner
+ elsif method == :delete || options.any? && options[:remove].any?
+ :remove_owner
+ end
+ end
end
diff --git a/lib/rubygems/commands/pristine_command.rb b/lib/rubygems/commands/pristine_command.rb
index db8136c9a6..143105981e 100644
--- a/lib/rubygems/commands/pristine_command.rb
+++ b/lib/rubygems/commands/pristine_command.rb
@@ -170,7 +170,7 @@ extensions will be restored.
:install_dir => spec.base_dir,
:env_shebang => env_shebang,
:build_args => spec.build_args,
- :bin_dir => bin_dir
+ :bin_dir => bin_dir,
}
if options[:only_executables]
diff --git a/lib/rubygems/commands/push_command.rb b/lib/rubygems/commands/push_command.rb
index 003b2dacc7..8885269ecb 100644
--- a/lib/rubygems/commands/push_command.rb
+++ b/lib/rubygems/commands/push_command.rb
@@ -61,7 +61,7 @@ The push command will use ~/.gem/credentials to authenticate to a server, but yo
options[:host]
end
- sign_in @host
+ sign_in @host, scope: get_push_scope
send_gem(gem_name)
end
@@ -86,7 +86,7 @@ The push command will use ~/.gem/credentials to authenticate to a server, but yo
private
def send_push_request(name, args)
- rubygems_api_request(*args) do |request|
+ rubygems_api_request(*args, scope: get_push_scope) do |request|
request.body = Gem.read_binary name
request.add_field "Content-Length", request.body.size
request.add_field "Content-Type", "application/octet-stream"
@@ -100,7 +100,11 @@ The push command will use ~/.gem/credentials to authenticate to a server, but yo
[
gem_metadata["default_gem_server"],
- gem_metadata["allowed_push_host"]
+ gem_metadata["allowed_push_host"],
]
end
+
+ def get_push_scope
+ :push_rubygem
+ end
end
diff --git a/lib/rubygems/commands/query_command.rb b/lib/rubygems/commands/query_command.rb
index 406a34e549..789afd6509 100644
--- a/lib/rubygems/commands/query_command.rb
+++ b/lib/rubygems/commands/query_command.rb
@@ -9,6 +9,14 @@ class Gem::Commands::QueryCommand < Gem::Command
include Gem::QueryUtils
+ alias warning_without_suggested_alternatives deprecation_warning
+ def deprecation_warning
+ warning_without_suggested_alternatives
+
+ message = "It is recommended that you use `gem search` or `gem list` instead.\n"
+ alert_warning message unless Gem::Deprecate.skip
+ end
+
def initialize(name = 'query',
summary = 'Query gem information in local or remote repositories')
super name, summary,
@@ -23,4 +31,13 @@ class Gem::Commands::QueryCommand < Gem::Command
add_query_options
end
+
+ def description # :nodoc:
+ <<-EOF
+The query command is the basis for the list and search commands.
+
+You should really use the list and search commands instead. This command
+is too hard to use.
+ EOF
+ end
end
diff --git a/lib/rubygems/commands/server_command.rb b/lib/rubygems/commands/server_command.rb
index 91d5e267f8..594cf77f66 100644
--- a/lib/rubygems/commands/server_command.rb
+++ b/lib/rubygems/commands/server_command.rb
@@ -1,8 +1,12 @@
# frozen_string_literal: true
require 'rubygems/command'
require 'rubygems/server'
+require 'rubygems/deprecate'
class Gem::Commands::ServerCommand < Gem::Command
+ extend Gem::Deprecate
+ rubygems_deprecate_command
+
def initialize
super 'server', 'Documentation and gem repository HTTP server',
:port => 8808, :gemdir => [], :daemon => false
diff --git a/lib/rubygems/commands/setup_command.rb b/lib/rubygems/commands/setup_command.rb
index b63920ab8d..22b1371a1f 100644
--- a/lib/rubygems/commands/setup_command.rb
+++ b/lib/rubygems/commands/setup_command.rb
@@ -322,13 +322,10 @@ By default, this RubyGems will install gem as:
libs.each do |tool, path|
say "Installing #{tool}" if @verbose
- lib_files = rb_files_in path
- lib_files.concat(bundler_template_files) if tool == 'Bundler'
-
- pem_files = pem_files_in path
+ lib_files = files_in path
Dir.chdir path do
- install_file_list(lib_files + pem_files, lib_dir)
+ install_file_list(lib_files, lib_dir)
end
end
end
@@ -394,10 +391,6 @@ By default, this RubyGems will install gem as:
specs_dir = File.join(options[:destdir], specs_dir) unless Gem.win_platform?
mkdir_p specs_dir, :mode => 0755
- # Workaround for non-git environment.
- gemspec = File.open('bundler/bundler.gemspec', 'rb'){|f| f.read.gsub(/`git ls-files -z`/, "''") }
- File.open('bundler/bundler.gemspec', 'w'){|f| f.write gemspec }
-
bundler_spec = Gem::Specification.load("bundler/bundler.gemspec")
bundler_spec.files = Dir.chdir("bundler") { Dir["{*.md,{lib,exe,man}/**/*}"] }
bundler_spec.executables -= %w[bundler bundle_ruby]
@@ -518,44 +511,24 @@ By default, this RubyGems will install gem as:
[lib_dir, bin_dir]
end
- def pem_files_in(dir)
- Dir.chdir dir do
- Dir[File.join('**', '*pem')]
- end
- end
-
- def rb_files_in(dir)
+ def files_in(dir)
Dir.chdir dir do
- Dir[File.join('**', '*rb')]
+ Dir.glob(File.join('**', '*'), File::FNM_DOTMATCH).
+ select{|f| !File.directory?(f) }
end
end
# for installation of bundler as default gems
def bundler_man1_files_in(dir)
Dir.chdir dir do
- Dir['bundle*.1{,.txt,.ronn}']
+ Dir['bundle*.1']
end
end
# for installation of bundler as default gems
def bundler_man5_files_in(dir)
Dir.chdir dir do
- Dir['gemfile.5{,.txt,.ronn}']
- end
- end
-
- def bundler_template_files
- Dir.chdir "bundler/lib" do
- Dir.glob(File.join('bundler', 'templates', '**', '*'), File::FNM_DOTMATCH).
- select{|f| !File.directory?(f) }
- end
- end
-
- # for cleanup old bundler files
- def template_files_in(dir)
- Dir.chdir dir do
- Dir.glob(File.join('templates', '**', '*'), File::FNM_DOTMATCH).
- select{|f| !File.directory?(f) }
+ Dir['gemfile.5']
end
end
@@ -595,11 +568,9 @@ abort "#{deprecation_message}"
lib_dirs = { File.join(lib_dir, 'rubygems') => 'lib/rubygems' }
lib_dirs[File.join(lib_dir, 'bundler')] = 'bundler/lib/bundler'
lib_dirs.each do |old_lib_dir, new_lib_dir|
- lib_files = rb_files_in(new_lib_dir)
- lib_files.concat(template_files_in(new_lib_dir)) if new_lib_dir =~ /bundler/
+ lib_files = files_in(new_lib_dir)
- old_lib_files = rb_files_in(old_lib_dir)
- old_lib_files.concat(template_files_in(old_lib_dir)) if old_lib_dir =~ /bundler/
+ old_lib_files = files_in(old_lib_dir)
to_remove = old_lib_files - lib_files
@@ -617,16 +588,25 @@ abort "#{deprecation_message}"
def remove_old_man_files(man_dir)
man_dirs = { man_dir => "bundler/man" }
man_dirs.each do |old_man_dir, new_man_dir|
- ["1", "5"].each do |section|
- man_files = send(:"bundler_man#{section}_files_in", new_man_dir)
+ man1_files = bundler_man1_files_in(new_man_dir)
- old_man_dir_with_section = "#{old_man_dir}/man#{section}"
- old_man_files = send(:"bundler_man#{section}_files_in", old_man_dir_with_section)
+ old_man1_dir = "#{old_man_dir}/man1"
+ old_man1_files = bundler_man1_files_in(old_man1_dir)
+ old_man1_files += Dir.chdir(old_man1_dir) { Dir["bundle*.1.{txt,ronn}"] }
- man_to_remove = old_man_files - man_files
+ man1_to_remove = old_man1_files - man1_files
- remove_file_list(man_to_remove, old_man_dir_with_section)
- end
+ remove_file_list(man1_to_remove, old_man1_dir)
+
+ man5_files = bundler_man5_files_in(new_man_dir)
+
+ old_man5_dir = "#{old_man_dir}/man5"
+ old_man5_files = bundler_man5_files_in(old_man5_dir)
+ old_man5_files += Dir.chdir(old_man5_dir) { Dir["gemfile.5.{txt,ronn}"] }
+
+ man5_to_remove = old_man5_files - man5_files
+
+ remove_file_list(man5_to_remove, old_man5_dir)
end
end
diff --git a/lib/rubygems/commands/sources_command.rb b/lib/rubygems/commands/sources_command.rb
index 3be3a5dc79..f74fb12e42 100644
--- a/lib/rubygems/commands/sources_command.rb
+++ b/lib/rubygems/commands/sources_command.rb
@@ -34,6 +34,10 @@ class Gem::Commands::SourcesCommand < Gem::Command
options[:update] = value
end
+ add_option '-f', '--[no-]force', "Do not show any confirmation prompts and behave as if 'yes' was always answered" do |value, options|
+ options[:force] = value
+ end
+
add_proxy_option
end
@@ -71,7 +75,7 @@ class Gem::Commands::SourcesCommand < Gem::Command
Do you want to add this source?
QUESTION
- terminate_interaction 1 unless ask_yes_no question
+ terminate_interaction 1 unless options[:force] || ask_yes_no(question)
end
end
@@ -86,7 +90,7 @@ https://rubygems.org is recommended for security over #{uri}
Do you want to add this insecure source?
QUESTION
- terminate_interaction 1 unless ask_yes_no question
+ terminate_interaction 1 unless options[:force] || ask_yes_no(question)
end
end
diff --git a/lib/rubygems/commands/specification_command.rb b/lib/rubygems/commands/specification_command.rb
index e4a8afab21..3fddaaaf30 100644
--- a/lib/rubygems/commands/specification_command.rb
+++ b/lib/rubygems/commands/specification_command.rb
@@ -126,6 +126,12 @@ Specific fields in the specification can be extracted in YAML format:
terminate_interaction 1
end
+ platform = get_platform_from_requirements(options)
+
+ if platform
+ specs = specs.select{|s| s.platform.to_s == platform }
+ end
+
unless options[:all]
specs = [specs.max_by {|s| s.version }]
end
diff --git a/lib/rubygems/commands/yank_command.rb b/lib/rubygems/commands/yank_command.rb
index 6ad74de96b..0e08bbfd5d 100644
--- a/lib/rubygems/commands/yank_command.rb
+++ b/lib/rubygems/commands/yank_command.rb
@@ -47,7 +47,7 @@ data you will need to change them immediately and yank your gem.
def execute
@host = options[:host]
- sign_in @host
+ sign_in @host, scope: get_yank_scope
version = get_version_from_requirements(options[:version])
platform = get_platform_from_requirements(options)
@@ -72,7 +72,7 @@ data you will need to change them immediately and yank your gem.
def yank_api_request(method, version, platform, api)
name = get_one_gem_name
- response = rubygems_api_request(method, api, host) do |request|
+ response = rubygems_api_request(method, api, host, scope: get_yank_scope) do |request|
request.add_field("Authorization", api_key)
request.add_field("OTP", options[:otp]) if options[:otp]
@@ -93,7 +93,7 @@ data you will need to change them immediately and yank your gem.
nil
end
- def get_platform_from_requirements(requirements)
- Gem.platforms[1].to_s if requirements.key? :added_platform
+ def get_yank_scope
+ :yank_rubygem
end
end
diff --git a/lib/rubygems/core_ext/kernel_require.rb b/lib/rubygems/core_ext/kernel_require.rb
index edf046651e..4b867c55e9 100644
--- a/lib/rubygems/core_ext/kernel_require.rb
+++ b/lib/rubygems/core_ext/kernel_require.rb
@@ -17,6 +17,8 @@ module Kernel
private :gem_original_require
end
+ file = Gem::KERNEL_WARN_IGNORES_INTERNAL_ENTRIES ? "<internal:#{__FILE__}>" : __FILE__
+ module_eval <<'RUBY', file, __LINE__ + 1
##
# When RubyGems is required, Kernel#require is replaced with our own which
# is capable of loading gems on demand.
@@ -166,6 +168,7 @@ module Kernel
end
end
end
+RUBY
private :require
diff --git a/lib/rubygems/core_ext/kernel_warn.rb b/lib/rubygems/core_ext/kernel_warn.rb
index e030ef815c..3373cfdd3b 100644
--- a/lib/rubygems/core_ext/kernel_warn.rb
+++ b/lib/rubygems/core_ext/kernel_warn.rb
@@ -1,12 +1,12 @@
# frozen_string_literal: true
# `uplevel` keyword argument of Kernel#warn is available since ruby 2.5.
-if RUBY_VERSION >= "2.5"
+if RUBY_VERSION >= "2.5" && !Gem::KERNEL_WARN_IGNORES_INTERNAL_ENTRIES
module Kernel
rubygems_path = "#{__dir__}/" # Frames to be skipped start with this path.
- original_warn = method(:warn)
+ original_warn = instance_method(:warn)
remove_method :warn
@@ -17,9 +17,9 @@ if RUBY_VERSION >= "2.5"
module_function define_method(:warn) {|*messages, **kw|
unless uplevel = kw[:uplevel]
if Gem.java_platform?
- return original_warn.call(*messages)
+ return original_warn.bind(self).call(*messages)
else
- return original_warn.call(*messages, **kw)
+ return original_warn.bind(self).call(*messages, **kw)
end
end
@@ -45,11 +45,10 @@ if RUBY_VERSION >= "2.5"
end
end
end
- uplevel = start
+ kw[:uplevel] = start
end
- kw[:uplevel] = uplevel
- original_warn.call(*messages, **kw)
+ original_warn.bind(self).call(*messages, **kw)
}
end
end
diff --git a/lib/rubygems/defaults.rb b/lib/rubygems/defaults.rb
index 2d0b077fdc..8aae67cd6b 100644
--- a/lib/rubygems/defaults.rb
+++ b/lib/rubygems/defaults.rb
@@ -38,13 +38,13 @@ module Gem
[
File.dirname(RbConfig::CONFIG['sitedir']),
'Gems',
- RbConfig::CONFIG['ruby_version']
+ RbConfig::CONFIG['ruby_version'],
]
else
[
RbConfig::CONFIG['rubylibprefix'],
'gems',
- RbConfig::CONFIG['ruby_version']
+ RbConfig::CONFIG['ruby_version'],
]
end
diff --git a/lib/rubygems/dependency.rb b/lib/rubygems/dependency.rb
index 8634d71a72..68f3e3d991 100644
--- a/lib/rubygems/dependency.rb
+++ b/lib/rubygems/dependency.rb
@@ -281,7 +281,7 @@ class Gem::Dependency
if platform_only
matches.reject! do |spec|
- spec.nil? || !Gem::Platform.match(spec.platform)
+ spec.nil? || !Gem::Platform.match_spec?(spec)
end
end
diff --git a/lib/rubygems/dependency_installer.rb b/lib/rubygems/dependency_installer.rb
index 1afdc4b4c2..fb555a46d4 100644
--- a/lib/rubygems/dependency_installer.rb
+++ b/lib/rubygems/dependency_installer.rb
@@ -27,7 +27,7 @@ class Gem::DependencyInstaller
:wrappers => true,
:build_args => nil,
:build_docs_in_background => false,
- :install_as_default => false
+ :install_as_default => false,
}.freeze
##
@@ -283,10 +283,9 @@ class Gem::DependencyInstaller
request_set.development_shallow = @dev_shallow
request_set.soft_missing = @force
request_set.prerelease = @prerelease
- request_set.remote = false unless consider_remote?
installer_set = Gem::Resolver::InstallerSet.new @domain
- installer_set.ignore_installed = @only_install_dir
+ installer_set.ignore_installed = (@minimal_deps == false) || @only_install_dir
if consider_local?
if dep_or_name =~ /\.gem$/ and File.file? dep_or_name
@@ -307,6 +306,7 @@ class Gem::DependencyInstaller
dependency =
if spec = installer_set.local?(dep_or_name)
+ installer_set.remote = nil if spec.dependencies.none?
Gem::Dependency.new spec.name, version
elsif String === dep_or_name
Gem::Dependency.new dep_or_name, version
@@ -321,6 +321,7 @@ class Gem::DependencyInstaller
installer_set.add_always_install dependency
request_set.always_install = installer_set.always_install
+ request_set.remote = installer_set.consider_remote?
if @ignore_dependencies
installer_set.ignore_dependencies = true
diff --git a/lib/rubygems/ext/builder.rb b/lib/rubygems/ext/builder.rb
index afc8cb0ee4..f6de6a50d7 100644
--- a/lib/rubygems/ext/builder.rb
+++ b/lib/rubygems/ext/builder.rb
@@ -10,14 +10,6 @@ require_relative '../user_interaction'
class Gem::Ext::Builder
include Gem::UserInteraction
- ##
- # The builder shells-out to run various commands after changing the
- # directory. This means multiple installations cannot be allowed to build
- # extensions in parallel as they may change each other's directories leading
- # to broken extensions or failed installations.
-
- CHDIR_MUTEX = Mutex.new # :nodoc:
-
attr_accessor :build_args # :nodoc:
def self.class_name
@@ -25,8 +17,8 @@ class Gem::Ext::Builder
$1.downcase
end
- def self.make(dest_path, results)
- unless File.exist? 'Makefile'
+ def self.make(dest_path, results, make_dir = Dir.pwd)
+ unless File.exist? File.join(make_dir, 'Makefile')
raise Gem::InstallError, 'Makefile not found'
end
@@ -44,32 +36,32 @@ class Gem::Ext::Builder
cmd = [
make_program,
destdir,
- target
+ target,
].join(' ').rstrip
begin
- run(cmd, results, "make #{target}".rstrip)
+ run(cmd, results, "make #{target}".rstrip, make_dir)
rescue Gem::InstallError
raise unless target == 'clean' # ignore clean failure
end
end
end
- def self.run(command, results, command_name = nil)
+ def self.run(command, results, command_name = nil, dir = Dir.pwd)
verbose = Gem.configuration.really_verbose
begin
rubygems_gemdeps, ENV['RUBYGEMS_GEMDEPS'] = ENV['RUBYGEMS_GEMDEPS'], nil
if verbose
- puts("current directory: #{Dir.pwd}")
+ puts("current directory: #{dir}")
p(command)
end
- results << "current directory: #{Dir.pwd}"
+ results << "current directory: #{dir}"
results << (command.respond_to?(:shelljoin) ? command.shelljoin : command)
require "open3"
# Set $SOURCE_DATE_EPOCH for the subprocess.
env = {'SOURCE_DATE_EPOCH' => Gem.source_date_epoch_string}
- output, status = Open3.capture2e(env, *command)
+ output, status = Open3.capture2e(env, *command, :chdir => dir)
if verbose
puts output
else
@@ -161,22 +153,10 @@ EOF
begin
FileUtils.mkdir_p dest_path
- CHDIR_MUTEX.synchronize do
- 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
+ results = builder.build(extension, dest_path,
+ results, @build_args, lib_dir, extension_dir)
+
+ verbose { results.join("\n") }
write_gem_make_out results.join "\n"
rescue => e
@@ -201,6 +181,7 @@ EOF
dest_path = @spec.extension_dir
+ require "fileutils"
FileUtils.rm_f @spec.gem_build_complete_path
@spec.extensions.each do |extension|
diff --git a/lib/rubygems/ext/cmake_builder.rb b/lib/rubygems/ext/cmake_builder.rb
index 519372e742..2efec91f15 100644
--- a/lib/rubygems/ext/cmake_builder.rb
+++ b/lib/rubygems/ext/cmake_builder.rb
@@ -2,15 +2,15 @@
require_relative '../command'
class Gem::Ext::CmakeBuilder < Gem::Ext::Builder
- def self.build(extension, dest_path, results, args=[], lib_dir=nil)
- unless File.exist?('Makefile')
+ def self.build(extension, dest_path, results, args=[], lib_dir=nil, cmake_dir=Dir.pwd)
+ unless File.exist?(File.join(cmake_dir, 'Makefile'))
cmd = "cmake . -DCMAKE_INSTALL_PREFIX=#{dest_path}"
cmd << " #{Gem::Command.build_args.join ' '}" unless Gem::Command.build_args.empty?
- run cmd, results
+ run cmd, results, class_name, cmake_dir
end
- make dest_path, results
+ make dest_path, results, cmake_dir
results
end
diff --git a/lib/rubygems/ext/configure_builder.rb b/lib/rubygems/ext/configure_builder.rb
index 209e75fe8e..36a758989b 100644
--- a/lib/rubygems/ext/configure_builder.rb
+++ b/lib/rubygems/ext/configure_builder.rb
@@ -6,15 +6,15 @@
#++
class Gem::Ext::ConfigureBuilder < Gem::Ext::Builder
- def self.build(extension, dest_path, results, args=[], lib_dir=nil)
- unless File.exist?('Makefile')
+ def self.build(extension, dest_path, results, args=[], lib_dir=nil, configure_dir=Dir.pwd)
+ unless File.exist?(File.join(configure_dir, 'Makefile'))
cmd = "sh ./configure --prefix=#{dest_path}"
cmd << " #{args.join ' '}" unless args.empty?
- run cmd, results
+ run cmd, results, class_name, configure_dir
end
- make dest_path, results
+ make dest_path, results, configure_dir
results
end
diff --git a/lib/rubygems/ext/ext_conf_builder.rb b/lib/rubygems/ext/ext_conf_builder.rb
index 305e1dcfb1..fede270417 100644
--- a/lib/rubygems/ext/ext_conf_builder.rb
+++ b/lib/rubygems/ext/ext_conf_builder.rb
@@ -8,11 +8,11 @@
require 'shellwords'
class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
- def self.build(extension, dest_path, results, args=[], lib_dir=nil)
+ def self.build(extension, dest_path, results, args=[], lib_dir=nil, extension_dir=Dir.pwd)
require 'fileutils'
require 'tempfile'
- tmp_dest = Dir.mktmpdir(".gem.", ".")
+ tmp_dest = Dir.mktmpdir(".gem.", extension_dir)
# Some versions of `mktmpdir` return absolute paths, which will break make
# if the paths contain spaces. However, on Ruby 1.9.x on Windows, relative
@@ -23,9 +23,9 @@ class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
# spaces do not work.
#
# Details: https://github.com/rubygems/rubygems/issues/977#issuecomment-171544940
- tmp_dest = get_relative_path(tmp_dest)
+ tmp_dest = get_relative_path(tmp_dest, extension_dir)
- Tempfile.open %w[siteconf .rb], "." do |siteconf|
+ Tempfile.open %w[siteconf .rb], extension_dir do |siteconf|
siteconf.puts "require 'rbconfig'"
siteconf.puts "dest_path = #{tmp_dest.dump}"
%w[sitearchdir sitelibdir].each do |dir|
@@ -38,19 +38,22 @@ class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
destdir = ENV["DESTDIR"]
begin
+ # workaround for https://github.com/oracle/truffleruby/issues/2115
+ siteconf_path = RUBY_ENGINE == "truffleruby" ? siteconf.path.dup : siteconf.path
cmd = Gem.ruby.shellsplit << "-I" << File.expand_path("../../..", __FILE__) <<
- "-r" << get_relative_path(siteconf.path) << File.basename(extension)
+ "-r" << get_relative_path(siteconf_path, extension_dir) << File.basename(extension)
cmd.push(*args)
begin
- run(cmd, results) do |s, r|
- if File.exist? 'mkmf.log'
+ run(cmd, results, class_name, extension_dir) do |s, r|
+ mkmf_log = File.join(extension_dir, 'mkmf.log')
+ if File.exist? mkmf_log
unless s.success?
r << "To see why this extension failed to compile, please check" \
" the mkmf.log which can be found here:\n"
r << " " + File.join(dest_path, 'mkmf.log') + "\n"
end
- FileUtils.mv 'mkmf.log', dest_path
+ FileUtils.mv mkmf_log, dest_path
end
end
siteconf.unlink
@@ -58,18 +61,20 @@ class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
ENV["DESTDIR"] = nil
- make dest_path, results
+ make dest_path, results, extension_dir
if tmp_dest
+ full_tmp_dest = File.join(extension_dir, tmp_dest)
+
# TODO remove in RubyGems 3
if Gem.install_extension_in_lib and lib_dir
FileUtils.mkdir_p lib_dir
- entries = Dir.entries(tmp_dest) - %w[. ..]
- entries = entries.map {|entry| File.join tmp_dest, entry }
+ entries = Dir.entries(full_tmp_dest) - %w[. ..]
+ entries = entries.map {|entry| File.join full_tmp_dest, entry }
FileUtils.cp_r entries, lib_dir, :remove_destination => true
end
- FileUtils::Entry_.new(tmp_dest).traverse do |ent|
+ FileUtils::Entry_.new(full_tmp_dest).traverse do |ent|
destent = ent.class.new(dest_path, ent.rel)
destent.exist? or FileUtils.mv(ent.path, destent.path)
end
@@ -87,8 +92,8 @@ class Gem::Ext::ExtConfBuilder < Gem::Ext::Builder
private
- def self.get_relative_path(path)
- path[0..Dir.pwd.length - 1] = '.' if path.start_with?(Dir.pwd)
+ def self.get_relative_path(path, base)
+ path[0..base.length - 1] = '.' if path.start_with?(base)
path
end
end
diff --git a/lib/rubygems/ext/rake_builder.rb b/lib/rubygems/ext/rake_builder.rb
index 53507090fe..34c3922f2f 100644
--- a/lib/rubygems/ext/rake_builder.rb
+++ b/lib/rubygems/ext/rake_builder.rb
@@ -8,9 +8,9 @@
require "shellwords"
class Gem::Ext::RakeBuilder < Gem::Ext::Builder
- def self.build(extension, dest_path, results, args=[], lib_dir=nil)
+ def self.build(extension, dest_path, results, args=[], lib_dir=nil, extension_dir=Dir.pwd)
if File.basename(extension) =~ /mkrf_conf/i
- run([Gem.ruby, File.basename(extension), *args], results)
+ run([Gem.ruby, File.basename(extension), *args], results, class_name, extension_dir)
end
rake = ENV['rake']
@@ -26,7 +26,7 @@ class Gem::Ext::RakeBuilder < Gem::Ext::Builder
end
rake_args = ["RUBYARCHDIR=#{dest_path}", "RUBYLIBDIR=#{dest_path}", *args]
- run(rake + rake_args, results)
+ run(rake + rake_args, results, class_name, extension_dir)
results
end
diff --git a/lib/rubygems/gemcutter_utilities.rb b/lib/rubygems/gemcutter_utilities.rb
index bba9280691..d021f47e24 100644
--- a/lib/rubygems/gemcutter_utilities.rb
+++ b/lib/rubygems/gemcutter_utilities.rb
@@ -8,10 +8,12 @@ require 'rubygems/text'
module Gem::GemcutterUtilities
ERROR_CODE = 1
+ API_SCOPES = %i[index_rubygems push_rubygem yank_rubygem add_owner remove_owner access_webhooks show_dashboard].freeze
include Gem::Text
attr_writer :host
+ attr_writer :scope
##
# Add the --key option
@@ -72,7 +74,7 @@ module Gem::GemcutterUtilities
#
# If +allowed_push_host+ metadata is present, then it will only allow that host.
- def rubygems_api_request(method, path, host = nil, allowed_push_host = nil, &block)
+ def rubygems_api_request(method, path, host = nil, allowed_push_host = nil, scope: nil, &block)
require 'net/http'
self.host = host if host
@@ -95,11 +97,19 @@ module Gem::GemcutterUtilities
request_method = Net::HTTP.const_get method.to_s.capitalize
response = Gem::RemoteFetcher.fetcher.request(uri, request_method, &block)
- return response unless mfa_unauthorized?(response)
- Gem::RemoteFetcher.fetcher.request(uri, request_method) do |req|
- req.add_field "OTP", get_otp
- block.call(req)
+ if mfa_unauthorized?(response)
+ response = Gem::RemoteFetcher.fetcher.request(uri, request_method) do |req|
+ req.add_field "OTP", get_otp
+ block.call(req)
+ end
+ end
+
+ if api_key_forbidden?(response)
+ update_scope(scope)
+ Gem::RemoteFetcher.fetcher.request(uri, request_method, &block)
+ else
+ response
end
end
@@ -112,19 +122,37 @@ module Gem::GemcutterUtilities
ask 'Code: '
end
+ def update_scope(scope)
+ sign_in_host = self.host
+ pretty_host = pretty_host(sign_in_host)
+ update_scope_params = { scope => true }
+
+ say "The existing key doesn't have access of #{scope} on #{pretty_host}. Please sign in to update access."
+
+ email = ask " Email: "
+ password = ask_for_password "Password: "
+
+ response = rubygems_api_request(:put, "api/v1/api_key",
+ sign_in_host, scope: scope) do |request|
+ request.basic_auth email, password
+ request.add_field "OTP", options[:otp] if options[:otp]
+ request.body = URI.encode_www_form({:api_key => api_key }.merge(update_scope_params))
+ end
+
+ with_response response do |resp|
+ say "Added #{scope} scope to the existing API key"
+ end
+ end
+
##
# Signs in with the RubyGems API at +sign_in_host+ and sets the rubygems API
# key.
- def sign_in(sign_in_host = nil)
+ def sign_in(sign_in_host = nil, scope: nil)
sign_in_host ||= self.host
return if api_key
- pretty_host = if Gem::DEFAULT_HOST == sign_in_host
- 'RubyGems.org'
- else
- sign_in_host
- end
+ pretty_host = pretty_host(sign_in_host)
say "Enter your #{pretty_host} credentials."
say "Don't have an account yet? " +
@@ -134,14 +162,18 @@ module Gem::GemcutterUtilities
password = ask_for_password "Password: "
say "\n"
- response = rubygems_api_request(:get, "api/v1/api_key",
- sign_in_host) do |request|
+ key_name = get_key_name(scope)
+ scope_params = get_scope_params(scope)
+
+ response = rubygems_api_request(:post, "api/v1/api_key",
+ sign_in_host, scope: scope) do |request|
request.basic_auth email, password
request.add_field "OTP", options[:otp] if options[:otp]
+ request.body = URI.encode_www_form({ name: key_name }.merge(scope_params))
end
with_response response do |resp|
- say "Signed in."
+ say "Signed in with API key: #{key_name}."
set_api_key host, resp.body
end
end
@@ -195,4 +227,48 @@ module Gem::GemcutterUtilities
end
end
+ private
+
+ def pretty_host(host)
+ if Gem::DEFAULT_HOST == host
+ 'RubyGems.org'
+ else
+ host
+ end
+ end
+
+ def get_scope_params(scope)
+ scope_params = {}
+
+ if scope
+ scope_params = { scope => true }
+ else
+ say "Please select scopes you want to enable for the API key (y/n)"
+ API_SCOPES.each do |scope|
+ selected = ask "#{scope} [y/N]: "
+ scope_params[scope] = true if selected =~ /^[yY](es)?$/
+ end
+ say "\n"
+ end
+
+ scope_params
+ end
+
+ def get_key_name(scope)
+ hostname = Socket.gethostname || "unkown-host"
+ user = ENV["USER"] || ENV["USERNAME"] || "unkown-user"
+ ts = Time.now.strftime("%Y%m%d%H%M%S")
+ default_key_name = "#{hostname}-#{user}-#{ts}"
+
+ key_name = ask "API Key name [#{default_key_name}]: " unless scope
+ if key_name.nil? || key_name.empty?
+ default_key_name
+ else
+ key_name
+ end
+ end
+
+ def api_key_forbidden?(response)
+ response.kind_of?(Net::HTTPForbidden) && response.body.start_with?("The API key doesn't have access")
+ end
end
diff --git a/lib/rubygems/indexer.rb b/lib/rubygems/indexer.rb
index 9f4bd12c46..31285ca962 100644
--- a/lib/rubygems/indexer.rb
+++ b/lib/rubygems/indexer.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
require 'rubygems'
require 'rubygems/package'
-require 'time'
require 'tmpdir'
##
diff --git a/lib/rubygems/install_update_options.rb b/lib/rubygems/install_update_options.rb
index b46b53e76b..ef1ad1edcb 100644
--- a/lib/rubygems/install_update_options.rb
+++ b/lib/rubygems/install_update_options.rb
@@ -122,10 +122,10 @@ module Gem::InstallUpdateOptions
options[:minimal_deps] = true
end
- add_option(:"Install/Update", "--minimal-deps",
+ add_option(:"Install/Update", "--[no-]minimal-deps",
"Don't upgrade any dependencies that already",
"meet version requirements") do |value, options|
- options[:minimal_deps] = true
+ options[:minimal_deps] = value
end
add_option(:"Install/Update", "--[no-]post-install-message",
diff --git a/lib/rubygems/installer.rb b/lib/rubygems/installer.rb
index 33171a8eb9..2c583743b9 100644
--- a/lib/rubygems/installer.rb
+++ b/lib/rubygems/installer.rb
@@ -12,7 +12,6 @@ require 'rubygems/deprecate'
require 'rubygems/package'
require 'rubygems/ext'
require 'rubygems/user_interaction'
-require 'fileutils'
##
# The installer installs the files contained in the .gem into the Gem.home.
@@ -433,7 +432,7 @@ class Gem::Installer
#
def default_spec_file
- File.join Gem.default_specifications_dir, "#{spec.full_name}.gemspec"
+ File.join gem_home, "specifications", "default", "#{spec.full_name}.gemspec"
end
##
@@ -492,7 +491,11 @@ class Gem::Installer
mode = File.stat(bin_path).mode
dir_mode = options[:prog_mode] || (mode | 0111)
- FileUtils.chmod dir_mode, bin_path unless dir_mode == mode
+
+ unless dir_mode == mode
+ require 'fileutils'
+ FileUtils.chmod dir_mode, bin_path
+ end
check_executable_overwrite filename
@@ -527,6 +530,7 @@ class Gem::Installer
def generate_bin_script(filename, bindir)
bin_script_path = File.join bindir, formatted_program_filename(filename)
+ require 'fileutils'
FileUtils.rm_f bin_script_path # prior install may have been --no-wrappers
File.open bin_script_path, 'wb', 0755 do |file|
@@ -670,7 +674,7 @@ class Gem::Installer
:env_shebang => false,
:force => false,
:only_install_dir => false,
- :post_install_message => true
+ :post_install_message => true,
}.merge options
@env_shebang = options[:env_shebang]
@@ -693,11 +697,10 @@ class Gem::Installer
@build_args = options[:build_args] || Gem::Command.build_args
unless @build_root.nil?
- require 'pathname'
- @build_root = Pathname.new(@build_root).expand_path
- @bin_dir = File.join(@build_root, options[:bin_dir] || Gem.bindir(@gem_home))
- @gem_home = File.join(@build_root, @gem_home)
- alert_warning "You build with buildroot.\n Build root: #{@build_root}\n Bin dir: #{@bin_dir}\n Gem home: #{@gem_home}"
+ @bin_dir = File.join(@build_root, @bin_dir.gsub(/^[a-zA-Z]:/, ''))
+ @gem_home = File.join(@build_root, @gem_home.gsub(/^[a-zA-Z]:/, ''))
+ @plugins_dir = File.join(@build_root, @plugins_dir.gsub(/^[a-zA-Z]:/, ''))
+ alert_warning "You build with buildroot.\n Build root: #{@build_root}\n Bin dir: #{@bin_dir}\n Gem home: #{@gem_home}\n Plugins dir: #{@plugins_dir}"
end
end
diff --git a/lib/rubygems/installer_test_case.rb b/lib/rubygems/installer_test_case.rb
index d78b6a4712..416dac7be6 100644
--- a/lib/rubygems/installer_test_case.rb
+++ b/lib/rubygems/installer_test_case.rb
@@ -108,9 +108,9 @@ class Gem::InstallerTestCase < Gem::TestCase
#
# And returns a Gem::Installer for the @spec that installs into @gemhome
- def setup_base_installer
+ def setup_base_installer(force = true)
@gem = setup_base_gem
- util_installer @spec, @gemhome
+ util_installer @spec, @gemhome, false, force
end
##
@@ -182,7 +182,7 @@ class Gem::InstallerTestCase < Gem::TestCase
# lib/code.rb
# ext/a/mkrf_conf.rb
- def util_setup_gem(ui = @ui)
+ def util_setup_gem(ui = @ui, force = true)
@spec.files << File.join('lib', 'code.rb')
@spec.extensions << File.join('ext', 'a', 'mkrf_conf.rb')
@@ -214,17 +214,18 @@ class Gem::InstallerTestCase < Gem::TestCase
end
end
- Gem::Installer.at @gem
+ Gem::Installer.at @gem, :force => force
end
##
# Creates an installer for +spec+ that will install into +gem_home+. If
# +user+ is true a user-install will be performed.
- def util_installer(spec, gem_home, user=false)
+ def util_installer(spec, gem_home, user=false, force=true)
Gem::Installer.at(spec.cache_file,
:install_dir => gem_home,
- :user_install => user)
+ :user_install => user,
+ :force => force)
end
@@symlink_supported = nil
diff --git a/lib/rubygems/name_tuple.rb b/lib/rubygems/name_tuple.rb
index cb5604e8dd..3d0afa3094 100644
--- a/lib/rubygems/name_tuple.rb
+++ b/lib/rubygems/name_tuple.rb
@@ -59,7 +59,7 @@ class Gem::NameTuple
# Indicate if this NameTuple matches the current platform.
def match_platform?
- Gem::Platform.match @platform
+ Gem::Platform.match_gem? @platform, @name
end
##
diff --git a/lib/rubygems/openssl.rb b/lib/rubygems/openssl.rb
index 39ef91e888..c44f619c4c 100644
--- a/lib/rubygems/openssl.rb
+++ b/lib/rubygems/openssl.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
-begin
- require "openssl"
-rescue LoadError => e
- raise unless e.path == 'openssl'
+autoload :OpenSSL, "openssl"
+
+module Gem
+ HAVE_OPENSSL = defined? OpenSSL::SSL # :nodoc:
end
diff --git a/lib/rubygems/package.rb b/lib/rubygems/package.rb
index 9b53cd4a7b..0587cd212b 100644
--- a/lib/rubygems/package.rb
+++ b/lib/rubygems/package.rb
@@ -44,7 +44,6 @@
require "rubygems"
require 'rubygems/security'
require 'rubygems/user_interaction'
-require 'zlib'
class Gem::Package
include Gem::UserInteraction
@@ -186,6 +185,8 @@ class Gem::Package
# Creates a new package that will read or write to the file +gem+.
def initialize(gem, security_policy) # :notnew:
+ require 'zlib'
+
@gem = gem
@build_time = Gem.source_date_epoch
@@ -297,7 +298,7 @@ class Gem::Package
setup_signer(
signer_options: {
- expiration_length_days: Gem.configuration.cert_expiration_length_days
+ expiration_length_days: Gem.configuration.cert_expiration_length_days,
}
)
diff --git a/lib/rubygems/package/tar_header.rb b/lib/rubygems/package/tar_header.rb
index f19aea549d..ce9b49e3eb 100644
--- a/lib/rubygems/package/tar_header.rb
+++ b/lib/rubygems/package/tar_header.rb
@@ -229,7 +229,7 @@ class Gem::Package::TarHeader
gname,
oct(devmajor, 7),
oct(devminor, 7),
- prefix
+ prefix,
]
header = header.pack PACK_FORMAT
diff --git a/lib/rubygems/package/tar_test_case.rb b/lib/rubygems/package/tar_test_case.rb
index 5fc34d2e8c..1161d0a5a8 100644
--- a/lib/rubygems/package/tar_test_case.rb
+++ b/lib/rubygems/package/tar_test_case.rb
@@ -90,7 +90,7 @@ class Gem::Package::TarTestCase < Gem::TestCase
ASCIIZ("wheel", 32), # char gname[32]; ASCIIZ
Z(to_oct(0, 7)), # char devmajor[8]; 0 padded, octal, null
Z(to_oct(0, 7)), # char devminor[8]; 0 padded, octal, null
- ASCIIZ(dname, 155) # char prefix[155]; ASCII + (Z unless filled)
+ ASCIIZ(dname, 155), # char prefix[155]; ASCII + (Z unless filled)
]
h = arr.join
diff --git a/lib/rubygems/platform.rb b/lib/rubygems/platform.rb
index 34306fcf83..a500fd24c8 100644
--- a/lib/rubygems/platform.rb
+++ b/lib/rubygems/platform.rb
@@ -9,11 +9,7 @@ require "rubygems/deprecate"
class Gem::Platform
@local = nil
- attr_accessor :cpu
-
- attr_accessor :os
-
- attr_accessor :version
+ attr_accessor :cpu, :os, :version
def self.local
arch = RbConfig::CONFIG['arch']
@@ -22,18 +18,33 @@ class Gem::Platform
end
def self.match(platform)
- Gem.platforms.any? do |local_platform|
+ match_platforms?(platform, Gem.platforms)
+ end
+
+ def self.match_platforms?(platform, platforms)
+ platforms.any? do |local_platform|
platform.nil? or
local_platform == platform or
(local_platform != Gem::Platform::RUBY and local_platform =~ platform)
end
end
+ private_class_method :match_platforms?
+
+ def self.match_spec?(spec)
+ match_gem?(spec.platform, spec.name)
+ end
+
+ def self.match_gem?(platform, gem_name)
+ # Note: this method might be redefined by Ruby implementations to
+ # customize behavior per RUBY_ENGINE, gem_name or other criteria.
+ match_platforms?(platform, Gem.platforms)
+ end
def self.installable?(spec)
if spec.respond_to? :installable_platform?
spec.installable_platform?
else
- match spec.platform
+ match_spec? spec
end
end
diff --git a/lib/rubygems/query_utils.rb b/lib/rubygems/query_utils.rb
index 4e9b6efee6..ea0f260ab4 100644
--- a/lib/rubygems/query_utils.rb
+++ b/lib/rubygems/query_utils.rb
@@ -57,15 +57,6 @@ module Gem::QueryUtils
"--local --name-matches // --no-details --versions --no-installed"
end
- def description # :nodoc:
- <<-EOF
-The query command is the basis for the list and search commands.
-
-You should really use the list and search commands instead. This command
-is too hard to use.
- EOF
- end
-
def execute
gem_names = Array(options[:name])
diff --git a/lib/rubygems/remote_fetcher.rb b/lib/rubygems/remote_fetcher.rb
index 40ac0e95c0..8ebe6acc70 100644
--- a/lib/rubygems/remote_fetcher.rb
+++ b/lib/rubygems/remote_fetcher.rb
@@ -78,7 +78,6 @@ class Gem::RemoteFetcher
def initialize(proxy=nil, dns=nil, headers={})
require 'net/http'
require 'stringio'
- require 'time'
require 'uri'
Socket.do_not_reverse_lookup = true
@@ -263,7 +262,7 @@ class Gem::RemoteFetcher
rescue Timeout::Error
raise UnknownHostError.new('timed out', uri)
rescue IOError, SocketError, SystemCallError,
- *(OpenSSL::SSL::SSLError if defined?(OpenSSL)) => e
+ *(OpenSSL::SSL::SSLError if Gem::HAVE_OPENSSL) => e
if e.message =~ /getaddrinfo/
raise UnknownHostError.new('no such name', uri)
else
diff --git a/lib/rubygems/request.rb b/lib/rubygems/request.rb
index 75f9e9979a..1ed0fbcb99 100644
--- a/lib/rubygems/request.rb
+++ b/lib/rubygems/request.rb
@@ -1,6 +1,5 @@
# frozen_string_literal: true
require 'net/http'
-require 'time'
require 'rubygems/user_interaction'
class Gem::Request
@@ -45,7 +44,8 @@ class Gem::Request
end
def self.configure_connection_for_https(connection, cert_files)
- require 'openssl'
+ raise Gem::Exception.new('OpenSSl is not available. Install OpenSSL and rebuild Ruby (preferred) or use non-HTTPS sources') unless Gem::HAVE_OPENSSL
+
connection.use_ssl = true
connection.verify_mode =
Gem.configuration.ssl_verify_mode || OpenSSL::SSL::VERIFY_PEER
@@ -125,7 +125,7 @@ class Gem::Request
def connection_for(uri)
@connection_pool.checkout
- rescue defined?(OpenSSL::SSL) ? OpenSSL::SSL::SSLError : Errno::EHOSTDOWN,
+ rescue Gem::HAVE_OPENSSL ? OpenSSL::SSL::SSLError : Errno::EHOSTDOWN,
Errno::EHOSTDOWN => e
raise Gem::RemoteFetcher::FetchError.new(e.message, uri)
end
@@ -143,6 +143,7 @@ class Gem::Request
request.add_field 'Keep-Alive', '30'
if @last_modified
+ require 'time'
request.add_field 'If-Modified-Since', @last_modified.httpdate
end
diff --git a/lib/rubygems/request_set/gem_dependency_api.rb b/lib/rubygems/request_set/gem_dependency_api.rb
index 9fbe3a1e44..7188b07346 100644
--- a/lib/rubygems/request_set/gem_dependency_api.rb
+++ b/lib/rubygems/request_set/gem_dependency_api.rb
@@ -88,7 +88,7 @@ class Gem::RequestSet::GemDependencyAPI
:truffleruby => Gem::Platform::RUBY,
:x64_mingw => x64_mingw,
:x64_mingw_20 => x64_mingw,
- :x64_mingw_21 => x64_mingw
+ :x64_mingw_21 => x64_mingw,
}.freeze
gt_eq_0 = Gem::Requirement.new '>= 0'
@@ -379,7 +379,7 @@ class Gem::RequestSet::GemDependencyAPI
Gem::Requirement.create requirements
end
- return unless gem_platforms options
+ return unless gem_platforms name, options
groups = gem_group name, options
@@ -532,7 +532,7 @@ Gem dependencies file #{@path} includes git reference for both ref/branch and ta
# Handles the platforms: option from +options+. Returns true if the
# platform matches the current platform.
- def gem_platforms(options) # :nodoc:
+ def gem_platforms(name, options) # :nodoc:
platform_names = Array(options.delete :platform)
platform_names.concat Array(options.delete :platforms)
platform_names.concat @current_platforms if @current_platforms
@@ -543,7 +543,7 @@ Gem dependencies file #{@path} includes git reference for both ref/branch and ta
raise ArgumentError, "unknown platform #{platform_name.inspect}" unless
platform = PLATFORM_MAP[platform_name]
- next false unless Gem::Platform.match platform
+ next false unless Gem::Platform.match_gem? platform, name
if engines = ENGINE_MAP[platform_name]
next false unless engines.include? Gem.ruby_engine
diff --git a/lib/rubygems/requirement.rb b/lib/rubygems/requirement.rb
index a2a5c7bca1..430060e2ff 100644
--- a/lib/rubygems/requirement.rb
+++ b/lib/rubygems/requirement.rb
@@ -16,7 +16,7 @@ class Gem::Requirement
"<" => lambda {|v, r| v < r },
">=" => lambda {|v, r| v >= r },
"<=" => lambda {|v, r| v <= r },
- "~>" => lambda {|v, r| v >= r && v.release < r.bump }
+ "~>" => lambda {|v, r| v >= r && v.release < r.bump },
}.freeze
SOURCE_SET_REQUIREMENT = Struct.new(:for_lockfile).new "!" # :nodoc:
diff --git a/lib/rubygems/resolver.rb b/lib/rubygems/resolver.rb
index fa5f5e6bb2..e1c0d2dd0a 100644
--- a/lib/rubygems/resolver.rb
+++ b/lib/rubygems/resolver.rb
@@ -281,7 +281,7 @@ class Gem::Resolver
amount_constrained(dependency),
conflicts[name] ? 0 : 1,
activated.vertex_named(name).payload ? 0 : search_for(dependency).count,
- i # for stable sort
+ i, # for stable sort
]
end
end
diff --git a/lib/rubygems/resolver/activation_request.rb b/lib/rubygems/resolver/activation_request.rb
index 293df1efe9..ae35681db9 100644
--- a/lib/rubygems/resolver/activation_request.rb
+++ b/lib/rubygems/resolver/activation_request.rb
@@ -28,12 +28,20 @@ class Gem::Resolver::ActivationRequest
when Gem::Specification
@spec == other
when Gem::Resolver::ActivationRequest
- @spec == other.spec && @request == other.request
+ @spec == other.spec
else
false
end
end
+ def eql?(other)
+ self == other
+ end
+
+ def hash
+ @spec.hash
+ end
+
##
# Is this activation request for a development dependency?
diff --git a/lib/rubygems/resolver/api_specification.rb b/lib/rubygems/resolver/api_specification.rb
index 232c2b041b..589ea1ba61 100644
--- a/lib/rubygems/resolver/api_specification.rb
+++ b/lib/rubygems/resolver/api_specification.rb
@@ -46,6 +46,10 @@ class Gem::Resolver::APISpecification < Gem::Resolver::Specification
@dependencies == other.dependencies
end
+ def hash
+ @set.hash ^ @name.hash ^ @version.hash ^ @platform.hash ^ @dependencies.hash
+ end
+
def fetch_development_dependencies # :nodoc:
spec = source.fetch_spec Gem::NameTuple.new @name, @version, @platform
@@ -53,7 +57,7 @@ class Gem::Resolver::APISpecification < Gem::Resolver::Specification
end
def installable_platform? # :nodoc:
- Gem::Platform.match @platform
+ Gem::Platform.match_gem? @platform, @name
end
def pretty_print(q) # :nodoc:
diff --git a/lib/rubygems/resolver/conflict.rb b/lib/rubygems/resolver/conflict.rb
index 2ce63feef2..4c4588d7e8 100644
--- a/lib/rubygems/resolver/conflict.rb
+++ b/lib/rubygems/resolver/conflict.rb
@@ -85,7 +85,7 @@ class Gem::Resolver::Conflict
activated, requirement,
request_path(@activated).reverse.join(", depends on\n "),
request_path(@failed_dep).reverse.join(", depends on\n "),
- matching,
+ matching
]
end
diff --git a/lib/rubygems/resolver/dependency_request.rb b/lib/rubygems/resolver/dependency_request.rb
index 77539c340f..356aadb3b2 100644
--- a/lib/rubygems/resolver/dependency_request.rb
+++ b/lib/rubygems/resolver/dependency_request.rb
@@ -28,7 +28,7 @@ class Gem::Resolver::DependencyRequest
when Gem::Dependency
@dependency == other
when Gem::Resolver::DependencyRequest
- @dependency == other.dependency && @requester == other.requester
+ @dependency == other.dependency
else
false
end
diff --git a/lib/rubygems/resolver/index_specification.rb b/lib/rubygems/resolver/index_specification.rb
index d80f121189..3b75b719b0 100644
--- a/lib/rubygems/resolver/index_specification.rb
+++ b/lib/rubygems/resolver/index_specification.rb
@@ -33,6 +33,17 @@ class Gem::Resolver::IndexSpecification < Gem::Resolver::Specification
spec.dependencies
end
+ def ==(other)
+ self.class === other &&
+ @name == other.name &&
+ @version == other.version &&
+ @platform == other.platform
+ end
+
+ def hash
+ @name.hash ^ @version.hash ^ @platform.hash
+ end
+
def inspect # :nodoc:
'#<%s %s source %s>' % [self.class, full_name, @source]
end
diff --git a/lib/rubygems/resolver/installer_set.rb b/lib/rubygems/resolver/installer_set.rb
index eaa7e207b2..b0e4ce5a37 100644
--- a/lib/rubygems/resolver/installer_set.rb
+++ b/lib/rubygems/resolver/installer_set.rb
@@ -32,7 +32,6 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
super()
@domain = domain
- @remote = consider_remote?
@f = Gem::SpecFetcher.fetcher
@@ -170,7 +169,7 @@ class Gem::Resolver::InstallerSet < Gem::Resolver::Set
always_install = @always_install.map {|s| s.full_name }
'#<%s domain: %s specs: %p always install: %p>' % [
- self.class, @domain, @specs.keys, always_install,
+ self.class, @domain, @specs.keys, always_install
]
end
diff --git a/lib/rubygems/resolver/lock_set.rb b/lib/rubygems/resolver/lock_set.rb
index 1ab03e753b..eabf217aba 100644
--- a/lib/rubygems/resolver/lock_set.rb
+++ b/lib/rubygems/resolver/lock_set.rb
@@ -28,7 +28,7 @@ class Gem::Resolver::LockSet < Gem::Resolver::Set
def add(name, version, platform) # :nodoc:
version = Gem::Version.new version
specs = [
- Gem::Resolver::LockSpecification.new(self, name, version, @sources, platform)
+ Gem::Resolver::LockSpecification.new(self, name, version, @sources, platform),
]
@specs.concat specs
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo.rb b/lib/rubygems/resolver/molinillo/lib/molinillo.rb
index 0ae4b6a912..f67badbde7 100644
--- a/lib/rubygems/resolver/molinillo/lib/molinillo.rb
+++ b/lib/rubygems/resolver/molinillo/lib/molinillo.rb
@@ -1,9 +1,10 @@
# frozen_string_literal: true
-require 'rubygems/resolver/molinillo/lib/molinillo/gem_metadata'
-require 'rubygems/resolver/molinillo/lib/molinillo/errors'
-require 'rubygems/resolver/molinillo/lib/molinillo/resolver'
-require 'rubygems/resolver/molinillo/lib/molinillo/modules/ui'
-require 'rubygems/resolver/molinillo/lib/molinillo/modules/specification_provider'
+
+require_relative 'molinillo/gem_metadata'
+require_relative 'molinillo/errors'
+require_relative 'molinillo/resolver'
+require_relative 'molinillo/modules/ui'
+require_relative 'molinillo/modules/specification_provider'
# Gem::Resolver::Molinillo is a generic dependency resolution algorithm.
module Gem::Resolver::Molinillo
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/delegates/resolution_state.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/delegates/resolution_state.rb
index 1bbc72c1f6..d540d3baff 100644
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/delegates/resolution_state.rb
+++ b/lib/rubygems/resolver/molinillo/lib/molinillo/delegates/resolution_state.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
module Gem::Resolver::Molinillo
# @!visibility private
module Delegates
@@ -45,6 +46,12 @@ module Gem::Resolver::Molinillo
current_state = state || Gem::Resolver::Molinillo::ResolutionState.empty
current_state.conflicts
end
+
+ # (see Gem::Resolver::Molinillo::ResolutionState#unused_unwind_options)
+ def unused_unwind_options
+ current_state = state || Gem::Resolver::Molinillo::ResolutionState.empty
+ current_state.unused_unwind_options
+ end
end
end
end
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/delegates/specification_provider.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/delegates/specification_provider.rb
index 71903c7e86..2ddb0ac426 100644
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/delegates/specification_provider.rb
+++ b/lib/rubygems/resolver/molinillo/lib/molinillo/delegates/specification_provider.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
module Gem::Resolver::Molinillo
module Delegates
# Delegates all {Gem::Resolver::Molinillo::SpecificationProvider} methods to a
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb
index b413e3ab6a..773bb3417f 100644
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb
+++ b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph.rb
@@ -1,9 +1,10 @@
# frozen_string_literal: true
+
require 'set'
require 'tsort'
-require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/log'
-require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/vertex'
+require_relative 'dependency_graph/log'
+require_relative 'dependency_graph/vertex'
module Gem::Resolver::Molinillo
# A directed acyclic graph that is tuned to hold named dependencies
@@ -123,6 +124,7 @@ module Gem::Resolver::Molinillo
dot.join("\n")
end
+ # @param [DependencyGraph] other
# @return [Boolean] whether the two dependency graphs are equal, determined
# by a recursive traversal of each {#root_vertices} and its
# {Vertex#successors}
@@ -147,8 +149,8 @@ module Gem::Resolver::Molinillo
vertex = add_vertex(name, payload, root)
vertex.explicit_requirements << requirement if root
parent_names.each do |parent_name|
- parent_node = vertex_named(parent_name)
- add_edge(parent_node, vertex, requirement)
+ parent_vertex = vertex_named(parent_name)
+ add_edge(parent_vertex, vertex, requirement)
end
vertex
end
@@ -189,7 +191,7 @@ module Gem::Resolver::Molinillo
# @return [Edge] the added edge
def add_edge(origin, destination, requirement)
if destination.path_to?(origin)
- raise CircularDependencyError.new([origin, destination])
+ raise CircularDependencyError.new(path(destination, origin))
end
add_edge_no_circular(origin, destination, requirement)
end
@@ -218,5 +220,37 @@ module Gem::Resolver::Molinillo
def add_edge_no_circular(origin, destination, requirement)
log.add_edge_no_circular(self, origin.name, destination.name, requirement)
end
+
+ # Returns the path between two vertices
+ # @raise [ArgumentError] if there is no path between the vertices
+ # @param [Vertex] from
+ # @param [Vertex] to
+ # @return [Array<Vertex>] the shortest path from `from` to `to`
+ def path(from, to)
+ distances = Hash.new(vertices.size + 1)
+ distances[from.name] = 0
+ predecessors = {}
+ each do |vertex|
+ vertex.successors.each do |successor|
+ if distances[successor.name] > distances[vertex.name] + 1
+ distances[successor.name] = distances[vertex.name] + 1
+ predecessors[successor] = vertex
+ end
+ end
+ end
+
+ path = [to]
+ while before = predecessors[to]
+ path << before
+ to = before
+ break if to == from
+ end
+
+ unless path.last.equal?(from)
+ raise ArgumentError, "There is no path from #{from.name} to #{to.name}"
+ end
+
+ path.reverse
+ end
end
end
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/action.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/action.rb
index eeedabb069..cc140031b3 100644
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/action.rb
+++ b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/action.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
module Gem::Resolver::Molinillo
class DependencyGraph
# An action that modifies a {DependencyGraph} that is reversible.
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb
index e994e59d05..5570483253 100644
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb
+++ b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: true
-require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/action'
+
+require_relative 'action'
module Gem::Resolver::Molinillo
class DependencyGraph
# @!visibility private
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_vertex.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_vertex.rb
index 6cde933080..f1411d5efa 100644
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_vertex.rb
+++ b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_vertex.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: true
-require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/action'
+
+require_relative 'action'
module Gem::Resolver::Molinillo
class DependencyGraph
# @!visibility private
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/delete_edge.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/delete_edge.rb
index d44aaf1f06..3b48d77a50 100644
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/delete_edge.rb
+++ b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/delete_edge.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: true
-require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/action'
+
+require_relative 'action'
module Gem::Resolver::Molinillo
class DependencyGraph
# @!visibility private
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb
index fa03e2d365..92f60d5be8 100644
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb
+++ b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/detach_vertex_named.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: true
-require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/action'
+
+require_relative 'action'
module Gem::Resolver::Molinillo
class DependencyGraph
# @!visibility private
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/log.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/log.rb
index 5cdd84b5c1..7aeb8847ec 100644
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/log.rb
+++ b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/log.rb
@@ -1,10 +1,11 @@
# frozen_string_literal: true
-require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_edge_no_circular'
-require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/add_vertex'
-require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/delete_edge'
-require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/detach_vertex_named'
-require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/set_payload'
-require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/tag'
+
+require_relative 'add_edge_no_circular'
+require_relative 'add_vertex'
+require_relative 'delete_edge'
+require_relative 'detach_vertex_named'
+require_relative 'set_payload'
+require_relative 'tag'
module Gem::Resolver::Molinillo
class DependencyGraph
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/set_payload.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/set_payload.rb
index 02cfba64a7..726292a2c3 100644
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/set_payload.rb
+++ b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/set_payload.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: true
-require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/action'
+
+require_relative 'action'
module Gem::Resolver::Molinillo
class DependencyGraph
# @!visibility private
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/tag.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/tag.rb
index 0cb08075ca..bfe6fd31f8 100644
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/tag.rb
+++ b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/tag.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: true
-require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph/action'
+
+require_relative 'action'
module Gem::Resolver::Molinillo
class DependencyGraph
# @!visibility private
@@ -13,11 +14,11 @@ module Gem::Resolver::Molinillo
end
# (see Action#up)
- def up(_graph)
+ def up(graph)
end
# (see Action#down)
- def down(_graph)
+ def down(graph)
end
# @!group Tag
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/vertex.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/vertex.rb
index cebd9cafdd..88b6580a52 100644
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/vertex.rb
+++ b/lib/rubygems/resolver/molinillo/lib/molinillo/dependency_graph/vertex.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
module Gem::Resolver::Molinillo
class DependencyGraph
# A vertex in a {DependencyGraph} that encapsulates a {#name} and a
@@ -32,7 +33,7 @@ module Gem::Resolver::Molinillo
# @return [Array<Object>] all of the requirements that required
# this vertex
def requirements
- incoming_edges.map(&:requirement) + explicit_requirements
+ (incoming_edges.map(&:requirement) + explicit_requirements).uniq
end
# @return [Array<Edge>] the edges of {#graph} that have `self` as their
@@ -49,14 +50,25 @@ module Gem::Resolver::Molinillo
incoming_edges.map(&:origin)
end
- # @return [Array<Vertex>] the vertices of {#graph} where `self` is a
+ # @return [Set<Vertex>] the vertices of {#graph} where `self` is a
# {#descendent?}
def recursive_predecessors
- vertices = predecessors
- vertices += vertices.map(&:recursive_predecessors).flatten(1)
- vertices.uniq!
+ _recursive_predecessors
+ end
+
+ # @param [Set<Vertex>] vertices the set to add the predecessors to
+ # @return [Set<Vertex>] the vertices of {#graph} where `self` is a
+ # {#descendent?}
+ def _recursive_predecessors(vertices = Set.new)
+ incoming_edges.each do |edge|
+ vertex = edge.origin
+ next unless vertices.add?(vertex)
+ vertex._recursive_predecessors(vertices)
+ end
+
vertices
end
+ protected :_recursive_predecessors
# @return [Array<Vertex>] the vertices of {#graph} that have an edge with
# `self` as their {Edge#origin}
@@ -64,14 +76,25 @@ module Gem::Resolver::Molinillo
outgoing_edges.map(&:destination)
end
- # @return [Array<Vertex>] the vertices of {#graph} where `self` is an
+ # @return [Set<Vertex>] the vertices of {#graph} where `self` is an
# {#ancestor?}
def recursive_successors
- vertices = successors
- vertices += vertices.map(&:recursive_successors).flatten(1)
- vertices.uniq!
+ _recursive_successors
+ end
+
+ # @param [Set<Vertex>] vertices the set to add the successors to
+ # @return [Set<Vertex>] the vertices of {#graph} where `self` is an
+ # {#ancestor?}
+ def _recursive_successors(vertices = Set.new)
+ outgoing_edges.each do |edge|
+ vertex = edge.destination
+ next unless vertices.add?(vertex)
+ vertex._recursive_successors(vertices)
+ end
+
vertices
end
+ protected :_recursive_successors
# @return [String] a string suitable for debugging
def inspect
@@ -107,11 +130,21 @@ module Gem::Resolver::Molinillo
# dependency graph?
# @return true iff there is a path following edges within this {#graph}
def path_to?(other)
- equal?(other) || successors.any? { |v| v.path_to?(other) }
+ _path_to?(other)
end
alias descendent? path_to?
+ # @param [Vertex] other the vertex to check if there's a path to
+ # @param [Set<Vertex>] visited the vertices of {#graph} that have been visited
+ # @return [Boolean] whether there is a path to `other` from `self`
+ def _path_to?(other, visited = Set.new)
+ return false unless visited.add?(self)
+ return true if equal?(other)
+ successors.any? { |v| v._path_to?(other, visited) }
+ end
+ protected :_path_to?
+
# Is there a path from `other` to `self` following edges in the
# dependency graph?
# @return true iff there is a path following edges within this {#graph}
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/errors.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/errors.rb
index 129246bf4a..2ec6b068ac 100644
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/errors.rb
+++ b/lib/rubygems/resolver/molinillo/lib/molinillo/errors.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
module Gem::Resolver::Molinillo
# An error that occurred during the resolution process
class ResolverError < StandardError; end
@@ -17,7 +18,7 @@ module Gem::Resolver::Molinillo
# @param [Array<Object>] required_by @see {#required_by}
def initialize(dependency, required_by = [])
@dependency = dependency
- @required_by = required_by
+ @required_by = required_by.uniq
super()
end
@@ -41,11 +42,11 @@ module Gem::Resolver::Molinillo
attr_reader :dependencies
# Initializes a new error with the given circular vertices.
- # @param [Array<DependencyGraph::Vertex>] nodes the nodes in the dependency
+ # @param [Array<DependencyGraph::Vertex>] vertices the vertices in the dependency
# that caused the error
- def initialize(nodes)
- super "There is a circular dependency between #{nodes.map(&:name).join(' and ')}"
- @dependencies = nodes.map(&:payload).to_set
+ def initialize(vertices)
+ super "There is a circular dependency between #{vertices.map(&:name).join(' and ')}"
+ @dependencies = vertices.map { |vertex| vertex.payload.possibilities.last }.to_set
end
end
@@ -55,11 +56,16 @@ module Gem::Resolver::Molinillo
# resolution to fail
attr_reader :conflicts
+ # @return [SpecificationProvider] the specification provider used during
+ # resolution
+ attr_reader :specification_provider
+
# Initializes a new error with the given version conflicts.
# @param [{String => Resolution::Conflict}] conflicts see {#conflicts}
- def initialize(conflicts)
+ # @param [SpecificationProvider] specification_provider see {#specification_provider}
+ def initialize(conflicts, specification_provider)
pairs = []
- conflicts.values.flatten.map(&:requirements).flatten.each do |conflicting|
+ conflicts.values.flat_map(&:requirements).each do |conflicting|
conflicting.each do |source, conflict_requirements|
conflict_requirements.each do |c|
pairs << [c, source]
@@ -69,7 +75,69 @@ module Gem::Resolver::Molinillo
super "Unable to satisfy the following requirements:\n\n" \
"#{pairs.map { |r, d| "- `#{r}` required by `#{d}`" }.join("\n")}"
+
@conflicts = conflicts
+ @specification_provider = specification_provider
+ end
+
+ require_relative 'delegates/specification_provider'
+ include Delegates::SpecificationProvider
+
+ # @return [String] An error message that includes requirement trees,
+ # which is much more detailed & customizable than the default message
+ # @param [Hash] opts the options to create a message with.
+ # @option opts [String] :solver_name The user-facing name of the solver
+ # @option opts [String] :possibility_type The generic name of a possibility
+ # @option opts [Proc] :reduce_trees A proc that reduced the list of requirement trees
+ # @option opts [Proc] :printable_requirement A proc that pretty-prints requirements
+ # @option opts [Proc] :additional_message_for_conflict A proc that appends additional
+ # messages for each conflict
+ # @option opts [Proc] :version_for_spec A proc that returns the version number for a
+ # possibility
+ def message_with_trees(opts = {})
+ solver_name = opts.delete(:solver_name) { self.class.name.split('::').first }
+ possibility_type = opts.delete(:possibility_type) { 'possibility named' }
+ reduce_trees = opts.delete(:reduce_trees) { proc { |trees| trees.uniq.sort_by(&:to_s) } }
+ printable_requirement = opts.delete(:printable_requirement) { proc { |req| req.to_s } }
+ additional_message_for_conflict = opts.delete(:additional_message_for_conflict) { proc {} }
+ version_for_spec = opts.delete(:version_for_spec) { proc(&:to_s) }
+ incompatible_version_message_for_conflict = opts.delete(:incompatible_version_message_for_conflict) do
+ proc do |name, _conflict|
+ %(#{solver_name} could not find compatible versions for #{possibility_type} "#{name}":)
+ end
+ end
+
+ conflicts.sort.reduce(''.dup) do |o, (name, conflict)|
+ o << "\n" << incompatible_version_message_for_conflict.call(name, conflict) << "\n"
+ if conflict.locked_requirement
+ o << %( In snapshot (#{name_for_locking_dependency_source}):\n)
+ o << %( #{printable_requirement.call(conflict.locked_requirement)}\n)
+ o << %(\n)
+ end
+ o << %( In #{name_for_explicit_dependency_source}:\n)
+ trees = reduce_trees.call(conflict.requirement_trees)
+
+ o << trees.map do |tree|
+ t = ''.dup
+ depth = 2
+ tree.each do |req|
+ t << ' ' * depth << req.to_s
+ unless tree.last == req
+ if spec = conflict.activated_by_name[name_for(req)]
+ t << %( was resolved to #{version_for_spec.call(spec)}, which)
+ end
+ t << %( depends on)
+ end
+ t << %(\n)
+ depth += 1
+ end
+ t
+ end.join("\n")
+
+ additional_message_for_conflict.call(o, name, conflict)
+
+ o
+ end.strip
end
end
end
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb
index c5b5bd729f..6b5ada7ade 100644
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb
+++ b/lib/rubygems/resolver/molinillo/lib/molinillo/gem_metadata.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: true
+
module Gem::Resolver::Molinillo
# The version of Gem::Resolver::Molinillo.
- VERSION = '0.5.7'.freeze
+ VERSION = '0.7.0'.freeze
end
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/modules/specification_provider.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/modules/specification_provider.rb
index 916345b12a..a44b9c0d5d 100644
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/modules/specification_provider.rb
+++ b/lib/rubygems/resolver/molinillo/lib/molinillo/modules/specification_provider.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
module Gem::Resolver::Molinillo
# Provides information about specifcations and dependencies to the resolver,
# allowing the {Resolver} class to remain generic while still providing power
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/modules/ui.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/modules/ui.rb
index dbc4e000e4..a810fd519c 100644
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/modules/ui.rb
+++ b/lib/rubygems/resolver/molinillo/lib/molinillo/modules/ui.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
module Gem::Resolver::Molinillo
# Conveys information about the resolution process to a user.
module UI
@@ -48,7 +49,8 @@ module Gem::Resolver::Molinillo
if debug?
debug_info = yield
debug_info = debug_info.inspect unless debug_info.is_a?(String)
- output.puts debug_info.split("\n").map { |s| ' ' * depth + s }
+ debug_info = debug_info.split("\n").map { |s| ":#{depth.to_s.rjust 4}: #{s}" }
+ output.puts debug_info
end
end
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb
index 73a4242157..f1c60ec544 100644
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb
+++ b/lib/rubygems/resolver/molinillo/lib/molinillo/resolution.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
module Gem::Resolver::Molinillo
class Resolver
# A specific resolution from a given {Resolver}
@@ -8,22 +9,125 @@ module Gem::Resolver::Molinillo
# @attr [{String,Nil=>[Object]}] requirements the requirements that caused the conflict
# @attr [Object, nil] existing the existing spec that was in conflict with
# the {#possibility}
- # @attr [Object] possibility the spec that was unable to be activated due
- # to a conflict
+ # @attr [Object] possibility_set the set of specs that was unable to be
+ # activated due to a conflict.
# @attr [Object] locked_requirement the relevant locking requirement.
# @attr [Array<Array<Object>>] requirement_trees the different requirement
# trees that led to every requirement for the conflicting name.
# @attr [{String=>Object}] activated_by_name the already-activated specs.
+ # @attr [Object] underlying_error an error that has occurred during resolution, and
+ # will be raised at the end of it if no resolution is found.
Conflict = Struct.new(
:requirement,
:requirements,
:existing,
- :possibility,
+ :possibility_set,
:locked_requirement,
:requirement_trees,
- :activated_by_name
+ :activated_by_name,
+ :underlying_error
)
+ class Conflict
+ # @return [Object] a spec that was unable to be activated due to a conflict
+ def possibility
+ possibility_set && possibility_set.latest_version
+ end
+ end
+
+ # A collection of possibility states that share the same dependencies
+ # @attr [Array] dependencies the dependencies for this set of possibilities
+ # @attr [Array] possibilities the possibilities
+ PossibilitySet = Struct.new(:dependencies, :possibilities)
+
+ class PossibilitySet
+ # String representation of the possibility set, for debugging
+ def to_s
+ "[#{possibilities.join(', ')}]"
+ end
+
+ # @return [Object] most up-to-date dependency in the possibility set
+ def latest_version
+ possibilities.last
+ end
+ end
+
+ # Details of the state to unwind to when a conflict occurs, and the cause of the unwind
+ # @attr [Integer] state_index the index of the state to unwind to
+ # @attr [Object] state_requirement the requirement of the state we're unwinding to
+ # @attr [Array] requirement_tree for the requirement we're relaxing
+ # @attr [Array] conflicting_requirements the requirements that combined to cause the conflict
+ # @attr [Array] requirement_trees for the conflict
+ # @attr [Array] requirements_unwound_to_instead array of unwind requirements that were chosen over this unwind
+ UnwindDetails = Struct.new(
+ :state_index,
+ :state_requirement,
+ :requirement_tree,
+ :conflicting_requirements,
+ :requirement_trees,
+ :requirements_unwound_to_instead
+ )
+
+ class UnwindDetails
+ include Comparable
+
+ # We compare UnwindDetails when choosing which state to unwind to. If
+ # two options have the same state_index we prefer the one most
+ # removed from a requirement that caused the conflict. Both options
+ # would unwind to the same state, but a `grandparent` option will
+ # filter out fewer of its possibilities after doing so - where a state
+ # is both a `parent` and a `grandparent` to requirements that have
+ # caused a conflict this is the correct behaviour.
+ # @param [UnwindDetail] other UnwindDetail to be compared
+ # @return [Integer] integer specifying ordering
+ def <=>(other)
+ if state_index > other.state_index
+ 1
+ elsif state_index == other.state_index
+ reversed_requirement_tree_index <=> other.reversed_requirement_tree_index
+ else
+ -1
+ end
+ end
+
+ # @return [Integer] index of state requirement in reversed requirement tree
+ # (the conflicting requirement itself will be at position 0)
+ def reversed_requirement_tree_index
+ @reversed_requirement_tree_index ||=
+ if state_requirement
+ requirement_tree.reverse.index(state_requirement)
+ else
+ 999_999
+ end
+ end
+
+ # @return [Boolean] where the requirement of the state we're unwinding
+ # to directly caused the conflict. Note: in this case, it is
+ # impossible for the state we're unwinding to to be a parent of
+ # any of the other conflicting requirements (or we would have
+ # circularity)
+ def unwinding_to_primary_requirement?
+ requirement_tree.last == state_requirement
+ end
+
+ # @return [Array] array of sub-dependencies to avoid when choosing a
+ # new possibility for the state we've unwound to. Only relevant for
+ # non-primary unwinds
+ def sub_dependencies_to_avoid
+ @requirements_to_avoid ||=
+ requirement_trees.map do |tree|
+ index = tree.index(state_requirement)
+ tree[index + 1] if index
+ end.compact
+ end
+
+ # @return [Array] array of all the requirements that led to the need for
+ # this unwind
+ def all_requirements
+ @all_requirements ||= requirement_trees.flatten(1)
+ end
+ end
+
# @return [SpecificationProvider] the provider that knows about
# dependencies, requirements, specifications, versions, etc.
attr_reader :specification_provider
@@ -64,7 +168,7 @@ module Gem::Resolver::Molinillo
start_resolution
while state
- break unless state.requirements.any? || state.requirement
+ break if !state.requirement && state.requirements.empty?
indicate_progress
if state.respond_to?(:pop_possibility_state) # DependencyState
debug(depth) { "Creating possibility state for #{requirement} (#{possibilities.count} remaining)" }
@@ -78,7 +182,7 @@ module Gem::Resolver::Molinillo
process_topmost_state
end
- activated.freeze
+ resolve_activated_specs
ensure
end_resolution
end
@@ -103,12 +207,25 @@ module Gem::Resolver::Molinillo
def start_resolution
@started_at = Time.now
- handle_missing_or_push_dependency_state(initial_state)
+ push_initial_state
debug { "Starting resolution (#{@started_at})\nUser-requested dependencies: #{original_requested}" }
resolver_ui.before_resolution
end
+ def resolve_activated_specs
+ activated.vertices.each do |_, vertex|
+ next unless vertex.payload
+
+ latest_version = vertex.payload.possibilities.reverse_each.find do |possibility|
+ vertex.requirements.all? { |req| requirement_satisfied_by?(req, activated, possibility) }
+ end
+
+ activated.set_payload(vertex.name, latest_version)
+ end
+ activated.freeze
+ end
+
# Ends the resolution process
# @return [void]
def end_resolution
@@ -121,11 +238,11 @@ module Gem::Resolver::Molinillo
debug { 'Activated: ' + Hash[activated.vertices.select { |_n, v| v.payload }].keys.join(', ') } if state
end
- require 'rubygems/resolver/molinillo/lib/molinillo/state'
- require 'rubygems/resolver/molinillo/lib/molinillo/modules/specification_provider'
+ require_relative 'state'
+ require_relative 'modules/specification_provider'
- require 'rubygems/resolver/molinillo/lib/molinillo/delegates/resolution_state'
- require 'rubygems/resolver/molinillo/lib/molinillo/delegates/specification_provider'
+ require_relative 'delegates/resolution_state'
+ require_relative 'delegates/specification_provider'
include Gem::Resolver::Molinillo::Delegates::ResolutionState
include Gem::Resolver::Molinillo::Delegates::SpecificationProvider
@@ -136,9 +253,12 @@ module Gem::Resolver::Molinillo
if possibility
attempt_to_activate
else
- create_conflict if state.is_a? PossibilityState
- unwind_for_conflict until possibility && state.is_a?(DependencyState)
+ create_conflict
+ unwind_for_conflict
end
+ rescue CircularDependencyError => underlying_error
+ create_conflict(underlying_error)
+ unwind_for_conflict
end
# @return [Object] the current possibility that the resolution is trying
@@ -153,63 +273,292 @@ module Gem::Resolver::Molinillo
states.last
end
- # Creates the initial state for the resolution, based upon the
+ # Creates and pushes the initial state for the resolution, based upon the
# {#requested} dependencies
- # @return [DependencyState] the initial state for the resolution
- def initial_state
+ # @return [void]
+ def push_initial_state
graph = DependencyGraph.new.tap do |dg|
- original_requested.each { |r| dg.add_vertex(name_for(r), nil, true).tap { |v| v.explicit_requirements << r } }
+ original_requested.each do |requested|
+ vertex = dg.add_vertex(name_for(requested), nil, true)
+ vertex.explicit_requirements << requested
+ end
dg.tag(:initial_state)
end
- requirements = sort_dependencies(original_requested, graph, {})
- initial_requirement = requirements.shift
- DependencyState.new(
- initial_requirement && name_for(initial_requirement),
- requirements,
- graph,
- initial_requirement,
- initial_requirement && search_for(initial_requirement),
- 0,
- {}
- )
+ push_state_for_requirements(original_requested, true, graph)
end
# Unwinds the states stack because a conflict has been encountered
# @return [void]
def unwind_for_conflict
- debug(depth) { "Unwinding for conflict: #{requirement} to #{state_index_for_unwind / 2}" }
+ details_for_unwind = build_details_for_unwind
+ unwind_options = unused_unwind_options
+ debug(depth) { "Unwinding for conflict: #{requirement} to #{details_for_unwind.state_index / 2}" }
conflicts.tap do |c|
- sliced_states = states.slice!((state_index_for_unwind + 1)..-1)
- raise VersionConflict.new(c) unless state
+ sliced_states = states.slice!((details_for_unwind.state_index + 1)..-1)
+ raise_error_unless_state(c)
activated.rewind_to(sliced_states.first || :initial_state) if sliced_states
state.conflicts = c
+ state.unused_unwind_options = unwind_options
+ filter_possibilities_after_unwind(details_for_unwind)
index = states.size - 1
@parents_of.each { |_, a| a.reject! { |i| i >= index } }
+ state.unused_unwind_options.reject! { |uw| uw.state_index >= index }
+ end
+ end
+
+ # Raises a VersionConflict error, or any underlying error, if there is no
+ # current state
+ # @return [void]
+ def raise_error_unless_state(conflicts)
+ return if state
+
+ error = conflicts.values.map(&:underlying_error).compact.first
+ raise error || VersionConflict.new(conflicts, specification_provider)
+ end
+
+ # @return [UnwindDetails] Details of the nearest index to which we could unwind
+ def build_details_for_unwind
+ # Get the possible unwinds for the current conflict
+ current_conflict = conflicts[name]
+ binding_requirements = binding_requirements_for_conflict(current_conflict)
+ unwind_details = unwind_options_for_requirements(binding_requirements)
+
+ last_detail_for_current_unwind = unwind_details.sort.last
+ current_detail = last_detail_for_current_unwind
+
+ # Look for past conflicts that could be unwound to affect the
+ # requirement tree for the current conflict
+ relevant_unused_unwinds = unused_unwind_options.select do |alternative|
+ intersecting_requirements =
+ last_detail_for_current_unwind.all_requirements &
+ alternative.requirements_unwound_to_instead
+ next if intersecting_requirements.empty?
+ # Find the highest index unwind whilst looping through
+ current_detail = alternative if alternative > current_detail
+ alternative
+ end
+
+ # Add the current unwind options to the `unused_unwind_options` array.
+ # The "used" option will be filtered out during `unwind_for_conflict`.
+ state.unused_unwind_options += unwind_details.reject { |detail| detail.state_index == -1 }
+
+ # Update the requirements_unwound_to_instead on any relevant unused unwinds
+ relevant_unused_unwinds.each { |d| d.requirements_unwound_to_instead << current_detail.state_requirement }
+ unwind_details.each { |d| d.requirements_unwound_to_instead << current_detail.state_requirement }
+
+ current_detail
+ end
+
+ # @param [Array<Object>] binding_requirements array of requirements that combine to create a conflict
+ # @return [Array<UnwindDetails>] array of UnwindDetails that have a chance
+ # of resolving the passed requirements
+ def unwind_options_for_requirements(binding_requirements)
+ unwind_details = []
+
+ trees = []
+ binding_requirements.reverse_each do |r|
+ partial_tree = [r]
+ trees << partial_tree
+ unwind_details << UnwindDetails.new(-1, nil, partial_tree, binding_requirements, trees, [])
+
+ # If this requirement has alternative possibilities, check if any would
+ # satisfy the other requirements that created this conflict
+ requirement_state = find_state_for(r)
+ if conflict_fixing_possibilities?(requirement_state, binding_requirements)
+ unwind_details << UnwindDetails.new(
+ states.index(requirement_state),
+ r,
+ partial_tree,
+ binding_requirements,
+ trees,
+ []
+ )
+ end
+
+ # Next, look at the parent of this requirement, and check if the requirement
+ # could have been avoided if an alternative PossibilitySet had been chosen
+ parent_r = parent_of(r)
+ next if parent_r.nil?
+ partial_tree.unshift(parent_r)
+ requirement_state = find_state_for(parent_r)
+ if requirement_state.possibilities.any? { |set| !set.dependencies.include?(r) }
+ unwind_details << UnwindDetails.new(
+ states.index(requirement_state),
+ parent_r,
+ partial_tree,
+ binding_requirements,
+ trees,
+ []
+ )
+ end
+
+ # Finally, look at the grandparent and up of this requirement, looking
+ # for any possibilities that wouldn't create their parent requirement
+ grandparent_r = parent_of(parent_r)
+ until grandparent_r.nil?
+ partial_tree.unshift(grandparent_r)
+ requirement_state = find_state_for(grandparent_r)
+ if requirement_state.possibilities.any? { |set| !set.dependencies.include?(parent_r) }
+ unwind_details << UnwindDetails.new(
+ states.index(requirement_state),
+ grandparent_r,
+ partial_tree,
+ binding_requirements,
+ trees,
+ []
+ )
+ end
+ parent_r = grandparent_r
+ grandparent_r = parent_of(parent_r)
+ end
+ end
+
+ unwind_details
+ end
+
+ # @param [DependencyState] state
+ # @param [Array] binding_requirements array of requirements
+ # @return [Boolean] whether or not the given state has any possibilities
+ # that could satisfy the given requirements
+ def conflict_fixing_possibilities?(state, binding_requirements)
+ return false unless state
+
+ state.possibilities.any? do |possibility_set|
+ possibility_set.possibilities.any? do |poss|
+ possibility_satisfies_requirements?(poss, binding_requirements)
+ end
+ end
+ end
+
+ # Filter's a state's possibilities to remove any that would not fix the
+ # conflict we've just rewound from
+ # @param [UnwindDetails] unwind_details details of the conflict just
+ # unwound from
+ # @return [void]
+ def filter_possibilities_after_unwind(unwind_details)
+ return unless state && !state.possibilities.empty?
+
+ if unwind_details.unwinding_to_primary_requirement?
+ filter_possibilities_for_primary_unwind(unwind_details)
+ else
+ filter_possibilities_for_parent_unwind(unwind_details)
+ end
+ end
+
+ # Filter's a state's possibilities to remove any that would not satisfy
+ # the requirements in the conflict we've just rewound from
+ # @param [UnwindDetails] unwind_details details of the conflict just unwound from
+ # @return [void]
+ def filter_possibilities_for_primary_unwind(unwind_details)
+ unwinds_to_state = unused_unwind_options.select { |uw| uw.state_index == unwind_details.state_index }
+ unwinds_to_state << unwind_details
+ unwind_requirement_sets = unwinds_to_state.map(&:conflicting_requirements)
+
+ state.possibilities.reject! do |possibility_set|
+ possibility_set.possibilities.none? do |poss|
+ unwind_requirement_sets.any? do |requirements|
+ possibility_satisfies_requirements?(poss, requirements)
+ end
+ end
end
end
- # @return [Integer] The index to which the resolution should unwind in the
- # case of conflict.
- def state_index_for_unwind
- current_requirement = requirement
- existing_requirement = requirement_for_existing_name(name)
- index = -1
- [current_requirement, existing_requirement].each do |r|
- until r.nil?
- current_state = find_state_for(r)
- if state_any?(current_state)
- current_index = states.index(current_state)
- index = current_index if current_index > index
- break
+ # @param [Object] possibility a single possibility
+ # @param [Array] requirements an array of requirements
+ # @return [Boolean] whether the possibility satisfies all of the
+ # given requirements
+ def possibility_satisfies_requirements?(possibility, requirements)
+ name = name_for(possibility)
+
+ activated.tag(:swap)
+ activated.set_payload(name, possibility) if activated.vertex_named(name)
+ satisfied = requirements.all? { |r| requirement_satisfied_by?(r, activated, possibility) }
+ activated.rewind_to(:swap)
+
+ satisfied
+ end
+
+ # Filter's a state's possibilities to remove any that would (eventually)
+ # create a requirement in the conflict we've just rewound from
+ # @param [UnwindDetails] unwind_details details of the conflict just unwound from
+ # @return [void]
+ def filter_possibilities_for_parent_unwind(unwind_details)
+ unwinds_to_state = unused_unwind_options.select { |uw| uw.state_index == unwind_details.state_index }
+ unwinds_to_state << unwind_details
+
+ primary_unwinds = unwinds_to_state.select(&:unwinding_to_primary_requirement?).uniq
+ parent_unwinds = unwinds_to_state.uniq - primary_unwinds
+
+ allowed_possibility_sets = primary_unwinds.flat_map do |unwind|
+ states[unwind.state_index].possibilities.select do |possibility_set|
+ possibility_set.possibilities.any? do |poss|
+ possibility_satisfies_requirements?(poss, unwind.conflicting_requirements)
end
- r = parent_of(r)
end
end
- index
+ requirements_to_avoid = parent_unwinds.flat_map(&:sub_dependencies_to_avoid)
+
+ state.possibilities.reject! do |possibility_set|
+ !allowed_possibility_sets.include?(possibility_set) &&
+ (requirements_to_avoid - possibility_set.dependencies).empty?
+ end
end
+ # @param [Conflict] conflict
+ # @return [Array] minimal array of requirements that would cause the passed
+ # conflict to occur.
+ def binding_requirements_for_conflict(conflict)
+ return [conflict.requirement] if conflict.possibility.nil?
+
+ possible_binding_requirements = conflict.requirements.values.flatten(1).uniq
+
+ # When there's a `CircularDependency` error the conflicting requirement
+ # (the one causing the circular) won't be `conflict.requirement`
+ # (which won't be for the right state, because we won't have created it,
+ # because it's circular).
+ # We need to make sure we have that requirement in the conflict's list,
+ # otherwise we won't be able to unwind properly, so we just return all
+ # the requirements for the conflict.
+ return possible_binding_requirements if conflict.underlying_error
+
+ possibilities = search_for(conflict.requirement)
+
+ # If all the requirements together don't filter out all possibilities,
+ # then the only two requirements we need to consider are the initial one
+ # (where the dependency's version was first chosen) and the last
+ if binding_requirement_in_set?(nil, possible_binding_requirements, possibilities)
+ return [conflict.requirement, requirement_for_existing_name(name_for(conflict.requirement))].compact
+ end
+
+ # Loop through the possible binding requirements, removing each one
+ # that doesn't bind. Use a `reverse_each` as we want the earliest set of
+ # binding requirements, and don't use `reject!` as we wish to refine the
+ # array *on each iteration*.
+ binding_requirements = possible_binding_requirements.dup
+ possible_binding_requirements.reverse_each do |req|
+ next if req == conflict.requirement
+ unless binding_requirement_in_set?(req, binding_requirements, possibilities)
+ binding_requirements -= [req]
+ end
+ end
+
+ binding_requirements
+ end
+
+ # @param [Object] requirement we wish to check
+ # @param [Array] possible_binding_requirements array of requirements
+ # @param [Array] possibilities array of possibilities the requirements will be used to filter
+ # @return [Boolean] whether or not the given requirement is required to filter
+ # out all elements of the array of possibilities.
+ def binding_requirement_in_set?(requirement, possible_binding_requirements, possibilities)
+ possibilities.any? do |poss|
+ possibility_satisfies_requirements?(poss, possible_binding_requirements - [requirement])
+ end
+ end
+
+ # @param [Object] requirement
# @return [Object] the requirement that led to `requirement` being added
# to the list of requirements.
def parent_of(requirement)
@@ -219,29 +568,27 @@ module Gem::Resolver::Molinillo
parent_state.requirement
end
+ # @param [String] name
# @return [Object] the requirement that led to a version of a possibility
# with the given name being activated.
def requirement_for_existing_name(name)
- return nil unless activated.vertex_named(name).payload
+ return nil unless vertex = activated.vertex_named(name)
+ return nil unless vertex.payload
states.find { |s| s.name == name }.requirement
end
+ # @param [Object] requirement
# @return [ResolutionState] the state whose `requirement` is the given
# `requirement`.
def find_state_for(requirement)
return nil unless requirement
- states.reverse_each.find { |i| requirement == i.requirement && i.is_a?(DependencyState) }
- end
-
- # @return [Boolean] whether or not the given state has any possibilities
- # left.
- def state_any?(state)
- state && state.possibilities.any?
+ states.find { |i| requirement == i.requirement }
end
+ # @param [Object] underlying_error
# @return [Conflict] a {Conflict} that reflects the failure to activate
# the {#possibility} in conjunction with the current {#state}
- def create_conflict
+ def create_conflict(underlying_error = nil)
vertex = activated.vertex_named(name)
locked_requirement = locked_requirement_named(name)
@@ -250,18 +597,21 @@ module Gem::Resolver::Molinillo
requirements[name_for_explicit_dependency_source] = vertex.explicit_requirements
end
requirements[name_for_locking_dependency_source] = [locked_requirement] if locked_requirement
- vertex.incoming_edges.each { |edge| (requirements[edge.origin.payload] ||= []).unshift(edge.requirement) }
+ vertex.incoming_edges.each do |edge|
+ (requirements[edge.origin.payload.latest_version] ||= []).unshift(edge.requirement)
+ end
activated_by_name = {}
- activated.each { |v| activated_by_name[v.name] = v.payload if v.payload }
+ activated.each { |v| activated_by_name[v.name] = v.payload.latest_version if v.payload }
conflicts[name] = Conflict.new(
requirement,
requirements,
- vertex.payload,
+ vertex.payload && vertex.payload.latest_version,
possibility,
locked_requirement,
requirement_trees,
- activated_by_name
+ activated_by_name,
+ underlying_error
)
end
@@ -272,6 +622,7 @@ module Gem::Resolver::Molinillo
vertex.requirements.map { |r| requirement_tree_for(r) }
end
+ # @param [Object] requirement
# @return [Array<Object>] the list of requirements that led to
# `requirement` being required.
def requirement_tree_for(requirement)
@@ -311,116 +662,47 @@ module Gem::Resolver::Molinillo
# @return [void]
def attempt_to_activate
debug(depth) { 'Attempting to activate ' + possibility.to_s }
- existing_node = activated.vertex_named(name)
- if existing_node.payload
- debug(depth) { "Found existing spec (#{existing_node.payload})" }
- attempt_to_activate_existing_spec(existing_node)
+ existing_vertex = activated.vertex_named(name)
+ if existing_vertex.payload
+ debug(depth) { "Found existing spec (#{existing_vertex.payload})" }
+ attempt_to_filter_existing_spec(existing_vertex)
else
- attempt_to_activate_new_spec
- end
- end
-
- # Attempts to activate the current {#possibility} (given that it has
- # already been activated)
- # @return [void]
- def attempt_to_activate_existing_spec(existing_node)
- existing_spec = existing_node.payload
- if requirement_satisfied_by?(requirement, activated, existing_spec)
- new_requirements = requirements.dup
- push_state_for_requirements(new_requirements, false)
- else
- return if attempt_to_swap_possibility
- create_conflict
- debug(depth) { "Unsatisfied by existing spec (#{existing_node.payload})" }
- unwind_for_conflict
- end
- end
-
- # Attempts to swp the current {#possibility} with the already-activated
- # spec with the given name
- # @return [Boolean] Whether the possibility was swapped into {#activated}
- def attempt_to_swap_possibility
- activated.tag(:swap)
- vertex = activated.vertex_named(name)
- activated.set_payload(name, possibility)
- if !vertex.requirements.
- all? { |r| requirement_satisfied_by?(r, activated, possibility) } ||
- !new_spec_satisfied?
- activated.rewind_to(:swap)
- return
- end
- fixup_swapped_children(vertex)
- activate_spec
- end
-
- # Ensures there are no orphaned successors to the given {vertex}.
- # @param [DependencyGraph::Vertex] vertex the vertex to fix up.
- # @return [void]
- def fixup_swapped_children(vertex) # rubocop:disable Metrics/CyclomaticComplexity
- payload = vertex.payload
- deps = dependencies_for(payload).group_by(&method(:name_for))
- vertex.outgoing_edges.each do |outgoing_edge|
- requirement = outgoing_edge.requirement
- parent_index = @parents_of[requirement].last
- succ = outgoing_edge.destination
- matching_deps = Array(deps[succ.name])
- dep_matched = matching_deps.include?(requirement)
-
- # only push the current index when it was originally required by the
- # same named spec
- if parent_index && states[parent_index].name == name
- @parents_of[requirement].push(states.size - 1)
+ latest = possibility.latest_version
+ possibility.possibilities.select! do |possibility|
+ requirement_satisfied_by?(requirement, activated, possibility)
end
-
- if matching_deps.empty? && !succ.root? && succ.predecessors.to_a == [vertex]
- debug(depth) { "Removing orphaned spec #{succ.name} after swapping #{name}" }
- succ.requirements.each { |r| @parents_of.delete(r) }
-
- removed_names = activated.detach_vertex_named(succ.name).map(&:name)
- requirements.delete_if do |r|
- # the only removed vertices are those with no other requirements,
- # so it's safe to delete only based upon name here
- removed_names.include?(name_for(r))
- end
- elsif !dep_matched
- debug(depth) { "Removing orphaned dependency #{requirement} after swapping #{name}" }
- # also reset if we're removing the edge, but only if its parent has
- # already been fixed up
- @parents_of[requirement].push(states.size - 1) if @parents_of[requirement].empty?
-
- activated.delete_edge(outgoing_edge)
- requirements.delete(requirement)
+ if possibility.latest_version.nil?
+ # ensure there's a possibility for better error messages
+ possibility.possibilities << latest if latest
+ create_conflict
+ unwind_for_conflict
+ else
+ activate_new_spec
end
end
end
- # Attempts to activate the current {#possibility} (given that it hasn't
- # already been activated)
+ # Attempts to update the existing vertex's `PossibilitySet` with a filtered version
# @return [void]
- def attempt_to_activate_new_spec
- if new_spec_satisfied?
- activate_spec
+ def attempt_to_filter_existing_spec(vertex)
+ filtered_set = filtered_possibility_set(vertex)
+ if !filtered_set.possibilities.empty?
+ activated.set_payload(name, filtered_set)
+ new_requirements = requirements.dup
+ push_state_for_requirements(new_requirements, false)
else
create_conflict
+ debug(depth) { "Unsatisfied by existing spec (#{vertex.payload})" }
unwind_for_conflict
end
end
- # @return [Boolean] whether the current spec is satisfied as a new
- # possibility.
- def new_spec_satisfied?
- unless requirement_satisfied_by?(requirement, activated, possibility)
- debug(depth) { 'Unsatisfied by requested spec' }
- return false
- end
-
- locked_requirement = locked_requirement_named(name)
-
- locked_spec_satisfied = !locked_requirement ||
- requirement_satisfied_by?(locked_requirement, activated, possibility)
- debug(depth) { 'Unsatisfied by locked spec' } unless locked_spec_satisfied
-
- locked_spec_satisfied
+ # Generates a filtered version of the existing vertex's `PossibilitySet` using the
+ # current state's `requirement`
+ # @param [Object] vertex existing vertex
+ # @return [PossibilitySet] filtered possibility set
+ def filtered_possibility_set(vertex)
+ PossibilitySet.new(vertex.payload.dependencies, vertex.payload.possibilities & possibility.possibilities)
end
# @param [String] requirement_name the spec name to search for
@@ -434,7 +716,7 @@ module Gem::Resolver::Molinillo
# Add the current {#possibility} to the dependency graph of the current
# {#state}
# @return [void]
- def activate_spec
+ def activate_new_spec
conflicts.delete(name)
debug(depth) { "Activated #{name} at #{possibility}" }
activated.set_payload(name, possibility)
@@ -442,14 +724,14 @@ module Gem::Resolver::Molinillo
end
# Requires the dependencies that the recently activated spec has
- # @param [Object] activated_spec the specification that has just been
+ # @param [Object] possibility_set the PossibilitySet that has just been
# activated
# @return [void]
- def require_nested_dependencies_for(activated_spec)
- nested_dependencies = dependencies_for(activated_spec)
+ def require_nested_dependencies_for(possibility_set)
+ nested_dependencies = dependencies_for(possibility_set.latest_version)
debug(depth) { "Requiring nested dependencies (#{nested_dependencies.join(', ')})" }
nested_dependencies.each do |d|
- activated.add_child_vertex(name_for(d), nil, [name_for(activated_spec)], d)
+ activated.add_child_vertex(name_for(d), nil, [name_for(possibility_set.latest_version)], d)
parent_index = states.size - 1
parents = @parents_of[d]
parents << parent_index if parents.empty?
@@ -461,23 +743,82 @@ module Gem::Resolver::Molinillo
# Pushes a new {DependencyState} that encapsulates both existing and new
# requirements
# @param [Array] new_requirements
+ # @param [Boolean] requires_sort
+ # @param [Object] new_activated
# @return [void]
def push_state_for_requirements(new_requirements, requires_sort = true, new_activated = activated)
new_requirements = sort_dependencies(new_requirements.uniq, new_activated, conflicts) if requires_sort
- new_requirement = new_requirements.shift
+ new_requirement = nil
+ loop do
+ new_requirement = new_requirements.shift
+ break if new_requirement.nil? || states.none? { |s| s.requirement == new_requirement }
+ end
new_name = new_requirement ? name_for(new_requirement) : ''.freeze
- possibilities = new_requirement ? search_for(new_requirement) : []
+ possibilities = possibilities_for_requirement(new_requirement)
handle_missing_or_push_dependency_state DependencyState.new(
new_name, new_requirements, new_activated,
- new_requirement, possibilities, depth, conflicts.dup
+ new_requirement, possibilities, depth, conflicts.dup, unused_unwind_options.dup
)
end
+ # Checks a proposed requirement with any existing locked requirement
+ # before generating an array of possibilities for it.
+ # @param [Object] requirement the proposed requirement
+ # @param [Object] activated
+ # @return [Array] possibilities
+ def possibilities_for_requirement(requirement, activated = self.activated)
+ return [] unless requirement
+ if locked_requirement_named(name_for(requirement))
+ return locked_requirement_possibility_set(requirement, activated)
+ end
+
+ group_possibilities(search_for(requirement))
+ end
+
+ # @param [Object] requirement the proposed requirement
+ # @param [Object] activated
+ # @return [Array] possibility set containing only the locked requirement, if any
+ def locked_requirement_possibility_set(requirement, activated = self.activated)
+ all_possibilities = search_for(requirement)
+ locked_requirement = locked_requirement_named(name_for(requirement))
+
+ # Longwinded way to build a possibilities array with either the locked
+ # requirement or nothing in it. Required, since the API for
+ # locked_requirement isn't guaranteed.
+ locked_possibilities = all_possibilities.select do |possibility|
+ requirement_satisfied_by?(locked_requirement, activated, possibility)
+ end
+
+ group_possibilities(locked_possibilities)
+ end
+
+ # Build an array of PossibilitySets, with each element representing a group of
+ # dependency versions that all have the same sub-dependency version constraints
+ # and are contiguous.
+ # @param [Array] possibilities an array of possibilities
+ # @return [Array<PossibilitySet>] an array of possibility sets
+ def group_possibilities(possibilities)
+ possibility_sets = []
+ current_possibility_set = nil
+
+ possibilities.reverse_each do |possibility|
+ dependencies = dependencies_for(possibility)
+ if current_possibility_set && current_possibility_set.dependencies == dependencies
+ current_possibility_set.possibilities.unshift(possibility)
+ else
+ possibility_sets.unshift(PossibilitySet.new(dependencies, [possibility]))
+ current_possibility_set = possibility_sets.first
+ end
+ end
+
+ possibility_sets
+ end
+
# Pushes a new {DependencyState}.
# If the {#specification_provider} says to
# {SpecificationProvider#allow_missing?} that particular requirement, and
# there are no possibilities for that requirement, then `state` is not
- # pushed, and the node in {#activated} is removed, and we continue
+ # pushed, and the vertex in {#activated} is removed, and we continue
# resolving the remaining requirements.
# @param [DependencyState] state
# @return [void]
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/resolver.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/resolver.rb
index 5c59a45c3d..d43121f8ca 100644
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/resolver.rb
+++ b/lib/rubygems/resolver/molinillo/lib/molinillo/resolver.rb
@@ -1,5 +1,6 @@
# frozen_string_literal: true
-require 'rubygems/resolver/molinillo/lib/molinillo/dependency_graph'
+
+require_relative 'dependency_graph'
module Gem::Resolver::Molinillo
# This class encapsulates a dependency resolver.
@@ -8,7 +9,7 @@ module Gem::Resolver::Molinillo
#
#
class Resolver
- require 'rubygems/resolver/molinillo/lib/molinillo/resolution'
+ require_relative 'resolution'
# @return [SpecificationProvider] the specification provider used
# in the resolution process
diff --git a/lib/rubygems/resolver/molinillo/lib/molinillo/state.rb b/lib/rubygems/resolver/molinillo/lib/molinillo/state.rb
index c20de98854..6e7c715fce 100644
--- a/lib/rubygems/resolver/molinillo/lib/molinillo/state.rb
+++ b/lib/rubygems/resolver/molinillo/lib/molinillo/state.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
+
module Gem::Resolver::Molinillo
# A state that a {Resolution} can be in
# @attr [String] name the name of the current requirement
@@ -7,7 +8,8 @@ module Gem::Resolver::Molinillo
# @attr [Object] requirement the current requirement
# @attr [Object] possibilities the possibilities to satisfy the current requirement
# @attr [Integer] depth the depth of the resolution
- # @attr [Set<Object>] conflicts unresolved conflicts
+ # @attr [Hash] conflicts unresolved conflicts, indexed by dependency name
+ # @attr [Array<UnwindDetails>] unused_unwind_options unwinds for previous conflicts that weren't explored
ResolutionState = Struct.new(
:name,
:requirements,
@@ -15,14 +17,15 @@ module Gem::Resolver::Molinillo
:requirement,
:possibilities,
:depth,
- :conflicts
+ :conflicts,
+ :unused_unwind_options
)
class ResolutionState
# Returns an empty resolution state
# @return [ResolutionState] an empty state
def self.empty
- new(nil, [], DependencyGraph.new, nil, nil, 0, Set.new)
+ new(nil, [], DependencyGraph.new, nil, nil, 0, {}, [])
end
end
@@ -40,7 +43,8 @@ module Gem::Resolver::Molinillo
requirement,
[possibilities.pop],
depth + 1,
- conflicts.dup
+ conflicts.dup,
+ unused_unwind_options.dup
).tap do |state|
state.activated.tag(state)
end
diff --git a/lib/rubygems/resolver/specification.rb b/lib/rubygems/resolver/specification.rb
index 7fe2afd3bd..5ae5f15813 100644
--- a/lib/rubygems/resolver/specification.rb
+++ b/lib/rubygems/resolver/specification.rb
@@ -104,7 +104,7 @@ class Gem::Resolver::Specification
# Returns true if this specification is installable on this platform.
def installable_platform?
- Gem::Platform.match spec.platform
+ Gem::Platform.match_spec? spec
end
def local? # :nodoc:
diff --git a/lib/rubygems/s3_uri_signer.rb b/lib/rubygems/s3_uri_signer.rb
index c0b88842a0..f1f9229ca5 100644
--- a/lib/rubygems/s3_uri_signer.rb
+++ b/lib/rubygems/s3_uri_signer.rb
@@ -88,7 +88,7 @@ class Gem::S3URISigner
"AWS4-HMAC-SHA256",
date_time,
credential_info,
- Digest::SHA256.hexdigest(canonical_request)
+ Digest::SHA256.hexdigest(canonical_request),
].join("\n")
end
diff --git a/lib/rubygems/security.rb b/lib/rubygems/security.rb
index bd6d6ff8b9..c80639af6d 100644
--- a/lib/rubygems/security.rb
+++ b/lib/rubygems/security.rb
@@ -6,7 +6,6 @@
#++
require 'rubygems/exceptions'
-require 'fileutils'
require_relative 'openssl'
##
@@ -592,7 +591,7 @@ module Gem::Security
end
-if defined?(OpenSSL::SSL)
+if Gem::HAVE_OPENSSL
require 'rubygems/security/policy'
require 'rubygems/security/policies'
require 'rubygems/security/trust_dir'
diff --git a/lib/rubygems/security/policy.rb b/lib/rubygems/security/policy.rb
index 43abdb6d91..7629d64796 100644
--- a/lib/rubygems/security/policy.rb
+++ b/lib/rubygems/security/policy.rb
@@ -194,7 +194,7 @@ class Gem::Security::Policy
("[Policy: %s - data: %p signer: %p chain: %p root: %p " +
"signed-only: %p trusted-only: %p]") % [
@name, @verify_chain, @verify_data, @verify_root, @verify_signer,
- @only_signed, @only_trusted,
+ @only_signed, @only_trusted
]
end
diff --git a/lib/rubygems/security/signer.rb b/lib/rubygems/security/signer.rb
index 89200f9e38..6c85ab08d2 100644
--- a/lib/rubygems/security/signer.rb
+++ b/lib/rubygems/security/signer.rb
@@ -34,7 +34,7 @@ class Gem::Security::Signer
attr_reader :options
DEFAULT_OPTIONS = {
- expiration_length_days: 365
+ expiration_length_days: 365,
}.freeze
##
diff --git a/lib/rubygems/server.rb b/lib/rubygems/server.rb
index 70ae44609a..a9529f7923 100644
--- a/lib/rubygems/server.rb
+++ b/lib/rubygems/server.rb
@@ -771,7 +771,7 @@ div.method-source-code pre { color: #ffdead; overflow: hidden; }
doc_items << {
:name => base_name,
:url => doc_root(new_path),
- :summary => ''
+ :summary => '',
}
end
diff --git a/lib/rubygems/source.rb b/lib/rubygems/source.rb
index ef232ff35d..891cc3e644 100644
--- a/lib/rubygems/source.rb
+++ b/lib/rubygems/source.rb
@@ -79,7 +79,7 @@ class Gem::Source
def dependency_resolver_set # :nodoc:
return Gem::Resolver::IndexSet.new self if 'file' == uri.scheme
- bundler_api_uri = uri + './api/v1/dependencies'
+ bundler_api_uri = enforce_trailing_slash(uri) + './api/v1/dependencies'
begin
fetcher = Gem::RemoteFetcher.fetcher
@@ -130,7 +130,7 @@ class Gem::Source
spec_file_name = name_tuple.spec_name
- source_uri = uri + "#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}"
+ source_uri = enforce_trailing_slash(uri) + "#{Gem::MARSHAL_SPEC_DIR}#{spec_file_name}"
cache_dir = cache_dir source_uri
@@ -174,7 +174,7 @@ class Gem::Source
file = FILES[type]
fetcher = Gem::RemoteFetcher.fetcher
file_name = "#{file}.#{Gem.marshal_version}"
- spec_path = uri + "#{file_name}.gz"
+ spec_path = enforce_trailing_slash(uri) + "#{file_name}.gz"
cache_dir = cache_dir spec_path
local_file = File.join(cache_dir, file_name)
retried = false
@@ -223,7 +223,13 @@ class Gem::Source
def typo_squatting?(host, distance_threshold=4)
return if @uri.host.nil?
- levenshtein_distance(@uri.host, host) <= distance_threshold
+ levenshtein_distance(@uri.host, host).between? 1, distance_threshold
+ end
+
+ private
+
+ def enforce_trailing_slash(uri)
+ uri.merge(uri.path.gsub(/\/+$/, '') + '/')
end
end
diff --git a/lib/rubygems/spec_fetcher.rb b/lib/rubygems/spec_fetcher.rb
index f748b090cc..b2bcadc49c 100644
--- a/lib/rubygems/spec_fetcher.rb
+++ b/lib/rubygems/spec_fetcher.rb
@@ -98,7 +98,7 @@ class Gem::SpecFetcher
found[source] = specs.select do |tup|
if dependency.match?(tup)
- if matching_platform and !Gem::Platform.match(tup.platform)
+ if matching_platform and !Gem::Platform.match_gem?(tup.platform, tup.name)
pm = (
rejected_specs[dependency] ||= \
Gem::PlatformMismatch.new(tup.name, tup.version))
diff --git a/lib/rubygems/specification.rb b/lib/rubygems/specification.rb
index cb7ec2b76d..d59f57c49f 100644
--- a/lib/rubygems/specification.rb
+++ b/lib/rubygems/specification.rb
@@ -77,18 +77,18 @@ class Gem::Specification < Gem::BasicSpecification
-1 => ['(RubyGems versions up to and including 0.7 did not have versioned specifications)'],
1 => [
'Deprecated "test_suite_file" in favor of the new, but equivalent, "test_files"',
- '"test_file=x" is a shortcut for "test_files=[x]"'
+ '"test_file=x" is a shortcut for "test_files=[x]"',
],
2 => [
'Added "required_rubygems_version"',
'Now forward-compatible with future versions',
],
3 => [
- 'Added Fixnum validation to the specification_version'
+ 'Added Fixnum validation to the specification_version',
],
4 => [
- 'Added sandboxed freeform metadata to the specification version.'
- ]
+ 'Added sandboxed freeform metadata to the specification version.',
+ ],
}.freeze
MARSHAL_FIELDS = { # :nodoc:
@@ -804,7 +804,7 @@ class Gem::Specification < Gem::BasicSpecification
stubs = stubs.uniq {|stub| stub.full_name }
_resort!(stubs)
- @@stubs_by_name = stubs.select {|s| Gem::Platform.match s.platform }.group_by(&:name)
+ @@stubs_by_name = stubs.select {|s| Gem::Platform.match_spec? s }.group_by(&:name)
stubs
end
end
@@ -831,7 +831,7 @@ class Gem::Specification < Gem::BasicSpecification
@@stubs_by_name[name]
else
pattern = "#{name}-*.gemspec"
- stubs = installed_stubs(dirs, pattern).select {|s| Gem::Platform.match s.platform } + default_stubs(pattern)
+ stubs = installed_stubs(dirs, pattern).select {|s| Gem::Platform.match_spec? s } + default_stubs(pattern)
stubs = stubs.uniq {|stub| stub.full_name }.group_by(&:name)
stubs.each_value {|v| _resort!(v) }
@@ -1344,7 +1344,7 @@ class Gem::Specification < Gem::BasicSpecification
true, # has_rdoc
@new_platform,
@licenses,
- @metadata
+ @metadata,
]
end
@@ -2450,7 +2450,7 @@ class Gem::Specification < Gem::BasicSpecification
:version,
:has_rdoc,
:default_executable,
- :metadata
+ :metadata,
]
@@attributes.each do |attr_name|
diff --git a/lib/rubygems/ssl_certs/rubygems.global.ssl.fastly.net/DigiCertHighAssuranceEVRootCA.pem b/lib/rubygems/ssl_certs/rubygems.global.ssl.fastly.net/DigiCertHighAssuranceEVRootCA.pem
deleted file mode 100644
index 9e6810ab70..0000000000
--- a/lib/rubygems/ssl_certs/rubygems.global.ssl.fastly.net/DigiCertHighAssuranceEVRootCA.pem
+++ /dev/null
@@ -1,23 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs
-MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
-d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
-ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL
-MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
-LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
-RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm
-+9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW
-PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM
-xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB
-Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3
-hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg
-EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF
-MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA
-FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec
-nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z
-eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF
-hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2
-Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
-vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
-+OkuE6N36B9K
------END CERTIFICATE-----
diff --git a/lib/rubygems/ssl_certs/index.rubygems.org/GlobalSignRootCA.pem b/lib/rubygems/ssl_certs/rubygems.org/GlobalSignRootCA.pem
index f4ce4ca43d..f4ce4ca43d 100644
--- a/lib/rubygems/ssl_certs/index.rubygems.org/GlobalSignRootCA.pem
+++ b/lib/rubygems/ssl_certs/rubygems.org/GlobalSignRootCA.pem
diff --git a/lib/rubygems/stub_specification.rb b/lib/rubygems/stub_specification.rb
index 5d4d761953..4246f9de86 100644
--- a/lib/rubygems/stub_specification.rb
+++ b/lib/rubygems/stub_specification.rb
@@ -29,7 +29,7 @@ class Gem::StubSpecification < Gem::BasicSpecification
# in their require paths, so lets take advantage of that by pre-allocating
# a require path list for that case.
REQUIRE_PATH_LIST = { # :nodoc:
- 'lib' => ['lib'].freeze
+ 'lib' => ['lib'].freeze,
}.freeze
def initialize(data, extensions)
diff --git a/lib/rubygems/test_case.rb b/lib/rubygems/test_case.rb
index 09b91b6ac6..a8261ef5c2 100644
--- a/lib/rubygems/test_case.rb
+++ b/lib/rubygems/test_case.rb
@@ -252,14 +252,14 @@ class Gem::TestCase < Minitest::Test
msg = message(msg) do
'Expected output containing make command "%s": %s' % [
('%s %s' % [make_command, target]).rstrip,
- output.inspect
+ output.inspect,
]
end
else
msg = message(msg) do
'Expected make command "%s": %s' % [
('%s %s' % [make_command, target]).rstrip,
- output.inspect
+ output.inspect,
]
end
end
@@ -335,6 +335,7 @@ class Gem::TestCase < Minitest::Test
@git = ENV['GIT'] || (win_platform? ? 'git.exe' : 'git')
Gem.ensure_gem_subdirectories @gemhome
+ Gem.ensure_default_gem_subdirectories @gemhome
@orig_LOAD_PATH = $LOAD_PATH.dup
$LOAD_PATH.map! do |s|
@@ -360,26 +361,23 @@ class Gem::TestCase < Minitest::Test
Gem.send :remove_instance_variable, :@ruby_version if
Gem.instance_variables.include? :@ruby_version
- FileUtils.mkdir_p @gemhome
FileUtils.mkdir_p @userhome
ENV['GEM_PRIVATE_KEY_PASSPHRASE'] = PRIVATE_KEY_PASSPHRASE
- @default_dir = File.join @tempdir, 'default'
- @default_spec_dir = File.join @default_dir, "specifications", "default"
if Gem.java_platform?
@orig_default_gem_home = RbConfig::CONFIG['default_gem_home']
- RbConfig::CONFIG['default_gem_home'] = @default_dir
+ RbConfig::CONFIG['default_gem_home'] = @gemhome
else
- Gem.instance_variable_set(:@default_dir, @default_dir)
+ Gem.instance_variable_set(:@default_dir, @gemhome)
end
- FileUtils.mkdir_p @default_spec_dir
+
+ @orig_bindir = RbConfig::CONFIG["bindir"]
+ RbConfig::CONFIG["bindir"] = File.join @gemhome, "bin"
Gem::Specification.unresolved_deps.clear
Gem.use_paths(@gemhome)
- Gem::Security.reset
-
Gem.loaded_specs.clear
Gem.instance_variable_set(:@activated_gem_paths, 0)
Gem.clear_default_specs
@@ -448,6 +446,8 @@ class Gem::TestCase < Minitest::Test
Gem.ruby = @orig_ruby if @orig_ruby
+ RbConfig::CONFIG['bindir'] = @orig_bindir
+
if Gem.java_platform?
RbConfig::CONFIG['default_gem_home'] = @orig_default_gem_home
else
@@ -741,7 +741,7 @@ class Gem::TestCase < Minitest::Test
def install_specs(*specs)
specs.each do |spec|
- Gem::Installer.for_spec(spec).install
+ Gem::Installer.for_spec(spec, :force => true).install
end
Gem.searcher = nil
@@ -751,19 +751,6 @@ class Gem::TestCase < Minitest::Test
# Installs the provided default specs including writing the spec file
def install_default_gems(*specs)
- install_default_specs(*specs)
-
- specs.each do |spec|
- File.open spec.loaded_from, 'w' do |io|
- io.write spec.to_ruby_for_cache
- end
- end
- end
-
- ##
- # Install the provided default specs
-
- def install_default_specs(*specs)
specs.each do |spec|
installer = Gem::Installer.for_spec(spec, :install_as_default => true)
installer.install
@@ -792,7 +779,7 @@ class Gem::TestCase < Minitest::Test
def new_default_spec(name, version, deps = nil, *files)
spec = util_spec name, version, deps
- spec.loaded_from = File.join(@default_spec_dir, spec.spec_name)
+ spec.loaded_from = File.join(@gemhome, "specifications", "default", spec.spec_name)
spec.files = files
lib_dir = File.join(@tempdir, "default_gems", "lib")
@@ -1517,7 +1504,7 @@ Also, a list:
PRIVATE_KEY = nil
PUBLIC_KEY = nil
PUBLIC_CERT = nil
- end if defined?(OpenSSL::SSL)
+ end if Gem::HAVE_OPENSSL
end
require 'rubygems/test_utilities'
diff --git a/lib/rubygems/uri_formatter.rb b/lib/rubygems/uri_formatter.rb
index ab5cc78e67..3bda896875 100644
--- a/lib/rubygems/uri_formatter.rb
+++ b/lib/rubygems/uri_formatter.rb
@@ -1,5 +1,4 @@
# frozen_string_literal: true
-require 'cgi'
##
# The UriFormatter handles URIs from user-input and escaping.
@@ -18,6 +17,8 @@ class Gem::UriFormatter
# Creates a new URI formatter for +uri+.
def initialize(uri)
+ require 'cgi'
+
@uri = uri
end
diff --git a/lib/rubygems/version_option.rb b/lib/rubygems/version_option.rb
index 458a7a6601..be71ef409b 100644
--- a/lib/rubygems/version_option.rb
+++ b/lib/rubygems/version_option.rb
@@ -73,4 +73,10 @@ module Gem::VersionOption
end
end
+ ##
+ # Extract platform given on the command line
+
+ def get_platform_from_requirements(requirements)
+ Gem.platforms[1].to_s if requirements.key? :added_platform
+ end
end
diff --git a/test/rubygems/test_bundled_ca.rb b/test/rubygems/test_bundled_ca.rb
index 557298d8d5..6973758c4c 100644
--- a/test/rubygems/test_bundled_ca.rb
+++ b/test/rubygems/test_bundled_ca.rb
@@ -3,7 +3,7 @@ require 'rubygems/test_case'
require 'net/http'
require 'rubygems/openssl'
-unless defined?(OpenSSL::SSL)
+unless Gem::HAVE_OPENSSL
warn 'Skipping bundled certificates tests. openssl not found.'
end
@@ -46,11 +46,15 @@ class TestBundledCA < Gem::TestCase
assert_https('rubygems.org')
end
- def test_accessing_fastly
- assert_https('rubygems.global.ssl.fastly.net')
+ def test_accessing_www_rubygems
+ assert_https('www.rubygems.org')
+ end
+
+ def test_accessing_staging
+ assert_https('staging.rubygems.org')
end
def test_accessing_new_index
- assert_https('fastly.rubygems.org')
+ assert_https('index.rubygems.org')
end
-end if defined?(OpenSSL::SSL)
+end if Gem::HAVE_OPENSSL
diff --git a/test/rubygems/test_gem.rb b/test/rubygems/test_gem.rb
index cf5c9720b4..344b03be9b 100644
--- a/test/rubygems/test_gem.rb
+++ b/test/rubygems/test_gem.rb
@@ -164,7 +164,7 @@ class TestGem < Gem::TestCase
:prog_mode => win_platform? ? 0410 : 0510,
:data_mode => 0640,
:wrappers => true,
- :format_executable => format_executable
+ :format_executable => format_executable,
}
Dir.chdir @tempdir do
Dir.mkdir 'bin'
@@ -765,7 +765,7 @@ class TestGem < Gem::TestCase
expected = [
File.expand_path('test/rubygems/sff/discover.rb', PROJECT_DIR),
- File.join(foo1.full_gem_path, discover_path)
+ File.join(foo1.full_gem_path, discover_path),
].sort
assert_equal expected, Gem.find_files('sff/discover').sort
@@ -1532,7 +1532,7 @@ class TestGem < Gem::TestCase
tests = [
[:dir0, [ Gem.dir, Gem.user_dir], m0],
- [:dir1, [ Gem.user_dir, Gem.dir], m1]
+ [:dir1, [ Gem.user_dir, Gem.dir], m1],
]
tests.each do |_name, _paths, expected|
diff --git a/test/rubygems/test_gem_commands_build_command.rb b/test/rubygems/test_gem_commands_build_command.rb
index 24c60473f2..fe537780be 100644
--- a/test/rubygems/test_gem_commands_build_command.rb
+++ b/test/rubygems/test_gem_commands_build_command.rb
@@ -231,7 +231,7 @@ class TestGemCommandsBuildCommand < Gem::TestCase
end
assert_equal '', @ui.output
- assert_equal "ERROR: Gemspec file not found: some_gem.gemspec\n", @ui.error
+ assert_equal "ERROR: Couldn't find a gemspec file matching 'some_gem' in #{@tempdir}\n", @ui.error
end
def test_execute_outside_dir
@@ -272,8 +272,200 @@ class TestGemCommandsBuildCommand < Gem::TestCase
assert_equal "this is a summary", spec.summary
end
+ def test_execute_outside_dir_with_glob_argument
+ gemspec_dir = File.join @tempdir, 'build_command_gem'
+ gemspec_file = File.join gemspec_dir, @gem.spec_name
+ readme_file = File.join gemspec_dir, 'README.md'
+
+ FileUtils.mkdir_p gemspec_dir
+
+ File.open readme_file, 'w' do |f|
+ f.write "My awesome gem"
+ end
+
+ File.open gemspec_file, 'w' do |gs|
+ gs.write @gem.to_ruby
+ end
+
+ @cmd.options[:build_path] = gemspec_dir
+ @cmd.options[:args] = ["*.gemspec"]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ output = @ui.output.split "\n"
+ assert_equal " Successfully built RubyGem", output.shift
+ assert_equal " Name: some_gem", output.shift
+ assert_equal " Version: 2", output.shift
+ assert_equal " File: some_gem-2.gem", output.shift
+ assert_equal [], output
+
+ gem_file = File.join gemspec_dir, File.basename(@gem.cache_file)
+ assert File.exist?(gem_file)
+
+ spec = Gem::Package.new(gem_file).spec
+
+ assert_equal "some_gem", spec.name
+ assert_equal "this is a summary", spec.summary
+ end
+
+ def test_execute_outside_dir_no_gemspec_present
+ gemspec_dir = File.join @tempdir, 'build_command_gem'
+ gemspec_file = File.join @tempdir, @gem.spec_name
+ readme_file = File.join gemspec_dir, 'README.md'
+
+ FileUtils.mkdir_p gemspec_dir
+
+ File.open readme_file, 'w' do |f|
+ f.write "My awesome gem"
+ end
+
+ File.open gemspec_file, 'w' do |gs|
+ gs.write @gem.to_ruby
+ end
+
+ @cmd.options[:build_path] = gemspec_dir
+ @cmd.options[:args] = ["*.gemspec"]
+
+ use_ui @ui do
+ assert_raises Gem::MockGemUi::TermError do
+ @cmd.execute
+ end
+ end
+
+ assert_equal "", @ui.output
+ assert_equal "ERROR: Couldn't find a gemspec file matching '*.gemspec' in #{gemspec_dir}\n", @ui.error
+
+ gem_file = File.join gemspec_dir, File.basename(@gem.cache_file)
+ refute File.exist?(gem_file)
+ end
+
+ def test_execute_outside_dir_without_gem_name
+ gemspec_dir = File.join(@tempdir, 'build_command_gem')
+ gemspec_file = File.join(gemspec_dir, @gem.spec_name)
+
+ readme_file = File.join gemspec_dir, 'README.md'
+
+ FileUtils.mkdir_p(gemspec_dir)
+
+ File.open readme_file, 'w' do |f|
+ f.write "My awesome gem"
+ end
+
+ File.open(gemspec_file, "w") do |gs|
+ gs.write(@gem.to_ruby)
+ end
+
+ @cmd.options[:build_path] = gemspec_dir
+ @cmd.options[:args] = []
+
+ use_ui @ui do
+ Dir.chdir(gemspec_dir) do
+ @cmd.execute
+ end
+ end
+
+ output = @ui.output.split("\n")
+ assert_equal " Successfully built RubyGem", output.shift
+ assert_equal " Name: some_gem", output.shift
+ assert_equal " Version: 2", output.shift
+ assert_equal " File: some_gem-2.gem", output.shift
+ assert_equal [], output
+
+ gem_file = File.join gemspec_dir, File.basename(@gem.cache_file)
+ assert File.exist?(gem_file)
+
+ spec = Gem::Package.new(gem_file).spec
+
+ assert_equal "some_gem", spec.name
+ assert_equal "this is a summary", spec.summary
+ end
+
+ def test_execute_outside_dir_with_external_gemspec
+ gemspec_dir = File.join @tempdir, 'gemspec_dir'
+ gemspec_file = File.join gemspec_dir, @gem.spec_name
+
+ gemcode_dir = File.join @tempdir, 'build_command_gem'
+ readme_file = File.join gemcode_dir, 'README.md'
+
+ FileUtils.mkdir_p gemspec_dir
+ FileUtils.mkdir_p gemcode_dir
+
+ File.open readme_file, 'w' do |f|
+ f.write "My awesome gem in nested directory"
+ end
+
+ File.open gemspec_file, 'w' do |gs|
+ gs.write @gem.to_ruby
+ end
+
+ @cmd.options[:build_path] = gemcode_dir
+ @cmd.options[:args] = [gemspec_file]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ output = @ui.output.split "\n"
+ assert_equal " Successfully built RubyGem", output.shift
+ assert_equal " Name: some_gem", output.shift
+ assert_equal " Version: 2", output.shift
+ assert_equal " File: some_gem-2.gem", output.shift
+ assert_equal [], output
+
+ gem_file = File.join gemcode_dir, File.basename(@gem.cache_file)
+ assert File.exist?(gem_file)
+
+ spec = Gem::Package.new(gem_file).spec
+
+ assert_equal "some_gem", spec.name
+ assert_equal "this is a summary", spec.summary
+ end
+
+ def test_execute_outside_dir_with_external_relative_gemspec
+ gemspec_dir = File.join @tempdir, 'gemspec_dir'
+ gemspec_file = File.join gemspec_dir, @gem.spec_name
+
+ gemcode_dir = File.join @tempdir, 'build_command_gem'
+ readme_file = File.join gemcode_dir, 'README.md'
+
+ FileUtils.mkdir_p gemspec_dir
+ FileUtils.mkdir_p gemcode_dir
+
+ File.open readme_file, 'w' do |f|
+ f.write "My awesome gem in nested directory"
+ end
+
+ File.open gemspec_file, 'w' do |gs|
+ gs.write @gem.to_ruby
+ end
+
+ @cmd.options[:build_path] = gemcode_dir
+ @cmd.options[:args] = [File.join("..", "gemspec_dir", @gem.spec_name)]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ output = @ui.output.split "\n"
+ assert_equal " Successfully built RubyGem", output.shift
+ assert_equal " Name: some_gem", output.shift
+ assert_equal " Version: 2", output.shift
+ assert_equal " File: some_gem-2.gem", output.shift
+ assert_equal [], output
+
+ gem_file = File.join gemcode_dir, File.basename(@gem.cache_file)
+ assert File.exist?(gem_file)
+
+ spec = Gem::Package.new(gem_file).spec
+
+ assert_equal "some_gem", spec.name
+ assert_equal "this is a summary", spec.summary
+ end
+
def test_can_find_gemspecs_without_dot_gemspec
- gemspec_file = File.join(@tempdir, @gem.spec_name)
+ gemspec_file = File.join(@tempdir, @gem.name)
File.open gemspec_file + ".gemspec", 'w' do |gs|
gs.write @gem.to_ruby
@@ -390,7 +582,7 @@ class TestGemCommandsBuildCommand < Gem::TestCase
end
def test_build_signed_gem
- skip 'openssl is missing' unless defined?(OpenSSL::SSL) && !java_platform?
+ skip 'openssl is missing' unless Gem::HAVE_OPENSSL && !java_platform?
trust_dir = Gem::Security.trust_dir
@@ -417,7 +609,7 @@ class TestGemCommandsBuildCommand < Gem::TestCase
end
def test_build_signed_gem_with_cert_expiration_length_days
- skip 'openssl is missing' unless defined?(OpenSSL::SSL) && !java_platform?
+ skip 'openssl is missing' unless Gem::HAVE_OPENSSL && !java_platform?
gem_path = File.join Gem.user_home, ".gem"
Dir.mkdir gem_path
@@ -461,7 +653,7 @@ class TestGemCommandsBuildCommand < Gem::TestCase
end
def test_build_auto_resign_cert
- skip 'openssl is missing' unless defined?(OpenSSL::SSL) && !java_platform?
+ skip 'openssl is missing' unless Gem::HAVE_OPENSSL && !java_platform?
gem_path = File.join Gem.user_home, ".gem"
Dir.mkdir gem_path
diff --git a/test/rubygems/test_gem_commands_cert_command.rb b/test/rubygems/test_gem_commands_cert_command.rb
index c4693b07cf..19867bf37a 100644
--- a/test/rubygems/test_gem_commands_cert_command.rb
+++ b/test/rubygems/test_gem_commands_cert_command.rb
@@ -2,7 +2,7 @@
require 'rubygems/test_case'
require 'rubygems/commands/cert_command'
-unless defined?(OpenSSL::SSL)
+unless Gem::HAVE_OPENSSL
warn 'Skipping `gem cert` tests. openssl not found.'
end
@@ -805,4 +805,4 @@ ERROR: --private-key not specified and ~/.gem/gem-private_key.pem does not exis
assert_equal "invalid argument: --sign #{nonexistent}: does not exist",
e.message
end
-end if defined?(OpenSSL::SSL) && !Gem.java_platform?
+end if Gem::HAVE_OPENSSL && !Gem.java_platform?
diff --git a/test/rubygems/test_gem_commands_cleanup_command.rb b/test/rubygems/test_gem_commands_cleanup_command.rb
index 81f9a24db5..d937a5e549 100644
--- a/test/rubygems/test_gem_commands_cleanup_command.rb
+++ b/test/rubygems/test_gem_commands_cleanup_command.rb
@@ -221,7 +221,7 @@ class TestGemCommandsCleanupCommand < Gem::TestCase
@b_2 = util_spec 'b', 3
install_gem @b_1
- install_default_specs @b_default
+ install_default_gems @b_default
install_gem @b_2
@cmd.options[:args] = []
diff --git a/test/rubygems/test_gem_commands_contents_command.rb b/test/rubygems/test_gem_commands_contents_command.rb
index 07b0e0f340..7c89c67dd4 100644
--- a/test/rubygems/test_gem_commands_contents_command.rb
+++ b/test/rubygems/test_gem_commands_contents_command.rb
@@ -227,7 +227,7 @@ lib/foo.rb
nil, "default/gem.rb")
default_gem_spec.executables = ["default_command"]
default_gem_spec.files += ["default_gem.so"]
- install_default_specs(default_gem_spec)
+ install_default_gems(default_gem_spec)
@cmd.options[:args] = %w[default]
@@ -238,7 +238,7 @@ lib/foo.rb
expected = [
[RbConfig::CONFIG['bindir'], 'default_command'],
[RbConfig::CONFIG['rubylibdir'], 'default/gem.rb'],
- [RbConfig::CONFIG['archdir'], 'default_gem.so']
+ [RbConfig::CONFIG['archdir'], 'default_gem.so'],
].sort.map{|a|File.join a }.join "\n"
assert_equal expected, @ui.output.chomp
diff --git a/test/rubygems/test_gem_commands_help_command.rb b/test/rubygems/test_gem_commands_help_command.rb
index 26e22d79be..8d20563a60 100644
--- a/test/rubygems/test_gem_commands_help_command.rb
+++ b/test/rubygems/test_gem_commands_help_command.rb
@@ -40,10 +40,12 @@ class TestGemCommandsHelpCommand < Gem::TestCase
util_gem 'commands' do |out, err|
mgr.command_names.each do |cmd|
- assert_match(/\s+#{cmd}\s+\S+/, out)
+ unless mgr[cmd].deprecated?
+ assert_match(/\s+#{cmd}\s+\S+/, out)
+ end
end
- if defined?(OpenSSL::SSL)
+ if Gem::HAVE_OPENSSL
assert_empty err
refute_match 'No command found for ', out
@@ -51,6 +53,17 @@ class TestGemCommandsHelpCommand < Gem::TestCase
end
end
+ def test_gem_help_commands_omits_deprecated_commands
+ mgr = Gem::CommandManager.new
+
+ util_gem 'commands' do |out, err|
+ deprecated_commands = mgr.command_names.select {|cmd| mgr[cmd].deprecated? }
+ deprecated_commands.each do |cmd|
+ refute_match(/\A\s+#{cmd}\s+\S+\z/, out)
+ end
+ end
+ end
+
def test_gem_no_args_shows_help
util_gem do |out, err|
assert_match(/Usage:/, out)
diff --git a/test/rubygems/test_gem_commands_install_command.rb b/test/rubygems/test_gem_commands_install_command.rb
index ccaa4ec2dc..08530bfeca 100644
--- a/test/rubygems/test_gem_commands_install_command.rb
+++ b/test/rubygems/test_gem_commands_install_command.rb
@@ -410,7 +410,7 @@ ERROR: Possible alternatives: non_existent_with_hint
expected = [
"ERROR: Could not find a valid gem 'non-existent_with-hint' (>= 0) in any repository",
- "ERROR: Possible alternatives: nonexistent-with_hint"
+ "ERROR: Possible alternatives: nonexistent-with_hint",
]
output = @ui.error.split "\n"
diff --git a/test/rubygems/test_gem_commands_owner_command.rb b/test/rubygems/test_gem_commands_owner_command.rb
index 1602ae6839..4280fedff3 100644
--- a/test/rubygems/test_gem_commands_owner_command.rb
+++ b/test/rubygems/test_gem_commands_owner_command.rb
@@ -247,7 +247,7 @@ EOF
@stub_fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners"] = [
[response_fail, 401, 'Unauthorized'],
- [response_success, 200, 'OK']
+ [response_success, 200, 'OK'],
]
@otp_ui = Gem::MockGemUi.new "111111\n"
@@ -275,4 +275,52 @@ EOF
assert_match 'Code: ', @otp_ui.output
assert_equal '111111', @stub_fetcher.last_request['OTP']
end
+
+ def test_remove_owners_unathorized_api_key
+ response_forbidden = "The API key doesn't have access"
+ response_success = "Owner removed successfully."
+
+ @stub_fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners"] = [
+ [response_forbidden, 403, 'Forbidden'],
+ [response_success, 200, "OK"],
+ ]
+ @stub_fetcher.data["#{Gem.host}/api/v1/api_key"] = ["", 200, "OK"]
+ @cmd.instance_variable_set :@scope, :remove_owner
+
+ @stub_ui = Gem::MockGemUi.new "some@mail.com\npass\n"
+ use_ui @stub_ui do
+ @cmd.remove_owners("freewill", ["some@example"])
+ end
+
+ access_notice = "The existing key doesn't have access of remove_owner on RubyGems.org. Please sign in to update access."
+ assert_match access_notice, @stub_ui.output
+ assert_match "Email:", @stub_ui.output
+ assert_match "Password:", @stub_ui.output
+ assert_match "Added remove_owner scope to the existing API key", @stub_ui.output
+ assert_match response_success, @stub_ui.output
+ end
+
+ def test_add_owners_unathorized_api_key
+ response_forbidden = "The API key doesn't have access"
+ response_success = "Owner added successfully."
+
+ @stub_fetcher.data["#{Gem.host}/api/v1/gems/freewill/owners"] = [
+ [response_forbidden, 403, 'Forbidden'],
+ [response_success, 200, "OK"],
+ ]
+ @stub_fetcher.data["#{Gem.host}/api/v1/api_key"] = ["", 200, "OK"]
+ @cmd.instance_variable_set :@scope, :add_owner
+
+ @stub_ui = Gem::MockGemUi.new "some@mail.com\npass\n"
+ use_ui @stub_ui do
+ @cmd.add_owners("freewill", ["some@example"])
+ end
+
+ access_notice = "The existing key doesn't have access of add_owner on RubyGems.org. Please sign in to update access."
+ assert_match access_notice, @stub_ui.output
+ assert_match "Email:", @stub_ui.output
+ assert_match "Password:", @stub_ui.output
+ assert_match "Added add_owner scope to the existing API key", @stub_ui.output
+ assert_match response_success, @stub_ui.output
+ end
end
diff --git a/test/rubygems/test_gem_commands_pristine_command.rb b/test/rubygems/test_gem_commands_pristine_command.rb
index 75243e5fa2..59f34af249 100644
--- a/test/rubygems/test_gem_commands_pristine_command.rb
+++ b/test/rubygems/test_gem_commands_pristine_command.rb
@@ -568,7 +568,7 @@ class TestGemCommandsPristineCommand < Gem::TestCase
assert_equal([
"Restoring gems to pristine condition...",
"Cached gem for a-2 not found, attempting to fetch...",
- "Skipped a-2, it was not found from cache and remote sources"
+ "Skipped a-2, it was not found from cache and remote sources",
], @ui.output.split("\n"))
assert_empty @ui.error
@@ -577,7 +577,7 @@ class TestGemCommandsPristineCommand < Gem::TestCase
def test_execute_default_gem
default_gem_spec = new_default_spec("default", "2.0.0.0",
nil, "default/gem.rb")
- install_default_specs(default_gem_spec)
+ install_default_gems(default_gem_spec)
@cmd.options[:args] = %w[default]
diff --git a/test/rubygems/test_gem_commands_push_command.rb b/test/rubygems/test_gem_commands_push_command.rb
index c23760a8ca..68681af22f 100644
--- a/test/rubygems/test_gem_commands_push_command.rb
+++ b/test/rubygems/test_gem_commands_push_command.rb
@@ -152,7 +152,7 @@ class TestGemCommandsPushCommand < Gem::TestCase
keys = {
:rubygems_api_key => 'KEY',
- @host => @api_key
+ @host => @api_key,
}
FileUtils.mkdir_p File.dirname Gem.configuration.credentials_path
@@ -187,7 +187,7 @@ class TestGemCommandsPushCommand < Gem::TestCase
keys = {
:rubygems_api_key => 'KEY',
- @host => @api_key
+ @host => @api_key,
}
FileUtils.mkdir_p File.dirname Gem.configuration.credentials_path
@@ -271,7 +271,7 @@ class TestGemCommandsPushCommand < Gem::TestCase
keys = {
:rubygems_api_key => 'KEY',
- @host => @api_key
+ @host => @api_key,
}
FileUtils.mkdir_p File.dirname Gem.configuration.credentials_path
@@ -302,7 +302,7 @@ class TestGemCommandsPushCommand < Gem::TestCase
api_key = "PRIVKEY"
keys = {
- host => api_key
+ host => api_key,
}
FileUtils.mkdir_p File.dirname Gem.configuration.credentials_path
@@ -373,7 +373,7 @@ class TestGemCommandsPushCommand < Gem::TestCase
@fetcher.data["#{Gem.host}/api/v1/gems"] = [
[response_fail, 401, 'Unauthorized'],
- [response_success, 200, 'OK']
+ [response_success, 200, 'OK'],
]
@otp_ui = Gem::MockGemUi.new "111111\n"
@@ -404,6 +404,32 @@ class TestGemCommandsPushCommand < Gem::TestCase
assert_equal '111111', @fetcher.last_request['OTP']
end
+ def test_sending_gem_unathorized_api_key
+ response_forbidden = "The API key doesn't have access"
+ response_success = 'Successfully registered gem: freewill (1.0.0)'
+
+ @fetcher.data["#{@host}/api/v1/gems"] = [
+ [response_forbidden, 403, 'Forbidden'],
+ [response_success, 200, "OK"],
+ ]
+
+ @fetcher.data["#{@host}/api/v1/api_key"] = ["", 200, "OK"]
+ @cmd.instance_variable_set :@host, @host
+ @cmd.instance_variable_set :@scope, :push_rubygem
+
+ @ui = Gem::MockGemUi.new "some@mail.com\npass\n"
+ use_ui @ui do
+ @cmd.send_gem(@path)
+ end
+
+ access_notice = "The existing key doesn't have access of push_rubygem on https://rubygems.example. Please sign in to update access."
+ assert_match access_notice, @ui.output
+ assert_match "Email:", @ui.output
+ assert_match "Password:", @ui.output
+ assert_match "Added push_rubygem scope to the existing API key", @ui.output
+ assert_match response_success, @ui.output
+ end
+
private
def singleton_gem_class
diff --git a/test/rubygems/test_gem_commands_query_command.rb b/test/rubygems/test_gem_commands_query_command.rb
index f2f70a10ff..a21bc690fb 100644
--- a/test/rubygems/test_gem_commands_query_command.rb
+++ b/test/rubygems/test_gem_commands_query_command.rb
@@ -644,7 +644,7 @@ a (2 universal-darwin, 1 ruby x86-linux)
spec_fetcher {|fetcher| fetcher.spec 'a', 2 }
a1 = new_default_spec 'a', 1
- install_default_specs a1
+ install_default_gems a1
use_ui @stub_ui do
@cmd.execute
@@ -663,7 +663,7 @@ EOF
def test_execute_show_default_gems_with_platform
a1 = new_default_spec 'a', 1
a1.platform = 'java'
- install_default_specs a1
+ install_default_gems a1
use_ui @stub_ui do
@cmd.execute
@@ -685,7 +685,7 @@ EOF
end
a1 = new_default_spec 'a', 1
- install_default_specs a1
+ install_default_gems a1
@cmd.handle_options %w[-l -d]
diff --git a/test/rubygems/test_gem_commands_setup_command.rb b/test/rubygems/test_gem_commands_setup_command.rb
index 9b6aa87861..afdc5d0979 100644
--- a/test/rubygems/test_gem_commands_setup_command.rb
+++ b/test/rubygems/test_gem_commands_setup_command.rb
@@ -26,12 +26,12 @@ class TestGemCommandsSetupCommand < Gem::TestCase
bundler/exe/bundle
bundler/lib/bundler.rb
bundler/lib/bundler/b.rb
+ bundler/lib/bundler/man/bundle-b.1.ronn
+ bundler/lib/bundler/man/gemfile.5.ronn
bundler/lib/bundler/templates/.circleci/config.yml
bundler/lib/bundler/templates/.travis.yml
bundler/man/bundle-b.1
- bundler/man/bundle-b.1.ronn
bundler/man/gemfile.5
- bundler/man/gemfile.5.ronn
]
create_dummy_files(filelist)
@@ -155,23 +155,18 @@ class TestGemCommandsSetupCommand < Gem::TestCase
assert_match %r{\A#!\s*#{bin_env}#{ruby_exec}}, File.read(gem_bin_path)
end
- def test_pem_files_in
- assert_equal %w[rubygems/ssl_certs/rubygems.org/foo.pem],
- @cmd.pem_files_in('lib').sort
- end
-
- def test_rb_files_in
- assert_equal %w[rubygems.rb rubygems/test_case.rb],
- @cmd.rb_files_in('lib').sort
+ def test_files_in
+ assert_equal %w[rubygems.rb rubygems/ssl_certs/rubygems.org/foo.pem rubygems/test_case.rb],
+ @cmd.files_in('lib').sort
end
def test_bundler_man1_files_in
- assert_equal %w[bundle-b.1 bundle-b.1.ronn],
+ assert_equal %w[bundle-b.1],
@cmd.bundler_man1_files_in('bundler/man').sort
end
def test_bundler_man5_files_in
- assert_equal %w[gemfile.5 gemfile.5.ronn],
+ assert_equal %w[gemfile.5],
@cmd.bundler_man5_files_in('bundler/man').sort
end
@@ -187,7 +182,7 @@ class TestGemCommandsSetupCommand < Gem::TestCase
assert_path_exists File.join(dir, 'bundler.rb')
assert_path_exists File.join(dir, 'bundler/b.rb')
- assert_path_exists File.join(dir, 'bundler/templates/.circleci/config.yml')
+ assert_path_exists File.join(dir, 'bundler/templates/.circleci/config.yml') unless RUBY_ENGINE == "truffleruby" # https://github.com/oracle/truffleruby/issues/2116
assert_path_exists File.join(dir, 'bundler/templates/.travis.yml')
end
end
@@ -199,9 +194,9 @@ class TestGemCommandsSetupCommand < Gem::TestCase
@cmd.install_man dir
assert_path_exists File.join("#{dir}/man1", 'bundle-b.1')
- assert_path_exists File.join("#{dir}/man1", 'bundle-b.1.ronn')
+ refute_path_exists File.join("#{dir}/man1", 'bundle-b.1.ronn')
assert_path_exists File.join("#{dir}/man5", 'gemfile.5')
- assert_path_exists File.join("#{dir}/man5", 'gemfile.5.ronn')
+ refute_path_exists File.join("#{dir}/man5", 'gemfile.5.ronn')
end
end
@@ -297,7 +292,7 @@ class TestGemCommandsSetupCommand < Gem::TestCase
@cmd.remove_old_lib_files lib
- files_that_go.each {|file| refute_path_exists file }
+ files_that_go.each {|file| refute_path_exists(file) unless file == old_bundler_ci && RUBY_ENGINE == "truffleruby" } # https://github.com/oracle/truffleruby/issues/2116
files_that_stay.each {|file| assert_path_exists file }
end
@@ -313,8 +308,8 @@ class TestGemCommandsSetupCommand < Gem::TestCase
gemfile_5_ronn = File.join man, 'man5', 'gemfile.5.ronn'
gemfile_5_txt = File.join man, 'man5', 'gemfile.5.txt'
- files_that_go = [bundle_b_1_txt, gemfile_5_txt]
- files_that_stay = [ruby_1, bundle_b_1, bundle_b_1_ronn, gemfile_5, gemfile_5_ronn]
+ files_that_go = [bundle_b_1_txt, bundle_b_1_ronn, gemfile_5_txt, gemfile_5_ronn]
+ files_that_stay = [ruby_1, bundle_b_1, gemfile_5]
create_dummy_files(files_that_go + files_that_stay)
diff --git a/test/rubygems/test_gem_commands_signin_command.rb b/test/rubygems/test_gem_commands_signin_command.rb
index 21d19fffc9..f8262466b1 100644
--- a/test/rubygems/test_gem_commands_signin_command.rb
+++ b/test/rubygems/test_gem_commands_signin_command.rb
@@ -73,14 +73,38 @@ class TestGemCommandsSigninCommand < Gem::TestCase
assert_equal api_key, credentials[:rubygems_api_key]
end
+ def test_excute_with_key_name_and_scope
+ email = 'you@example.com'
+ password = 'secret'
+ api_key = '1234'
+ fetcher = Gem::RemoteFetcher.fetcher
+
+ key_name_ui = Gem::MockGemUi.new "#{email}\n#{password}\ntest-key\n\ny\n\n\n\n\n\n"
+ util_capture(key_name_ui, nil, api_key, fetcher) { @cmd.execute }
+
+ user = ENV["USER"] || ENV["USERNAME"]
+
+ assert_match "API Key name [#{Socket.gethostname}-#{user}", key_name_ui.output
+ assert_match "index_rubygems [y/N]", key_name_ui.output
+ assert_match "push_rubygem [y/N]", key_name_ui.output
+ assert_match "yank_rubygem [y/N]", key_name_ui.output
+ assert_match "add_owner [y/N]", key_name_ui.output
+ assert_match "remove_owner [y/N]", key_name_ui.output
+ assert_match "access_webhooks [y/N]", key_name_ui.output
+ assert_match "show_dashboard [y/N]", key_name_ui.output
+ assert_equal "name=test-key&push_rubygem=true", fetcher.last_request.body
+
+ credentials = YAML.load_file Gem.configuration.credentials_path
+ assert_equal api_key, credentials[:rubygems_api_key]
+ end
+
# Utility method to capture IO/UI within the block passed
- def util_capture(ui_stub = nil, host = nil, api_key = nil)
+ def util_capture(ui_stub = nil, host = nil, api_key = nil, fetcher = Gem::FakeFetcher.new)
api_key ||= 'a5fdbb6ba150cbb83aad2bb2fede64cf040453903'
response = [api_key, 200, 'OK']
email = 'you@example.com'
password = 'secret'
- fetcher = Gem::FakeFetcher.new
# Set the expected response for the Web-API supplied
ENV['RUBYGEMS_HOST'] = host || Gem::DEFAULT_HOST
@@ -88,7 +112,7 @@ class TestGemCommandsSigninCommand < Gem::TestCase
fetcher.data[data_key] = response
Gem::RemoteFetcher.fetcher = fetcher
- sign_in_ui = ui_stub || Gem::MockGemUi.new("#{email}\n#{password}\n")
+ sign_in_ui = ui_stub || Gem::MockGemUi.new("#{email}\n#{password}\n\n\n\n\n\n\n\n\n")
use_ui sign_in_ui do
yield
diff --git a/test/rubygems/test_gem_commands_sources_command.rb b/test/rubygems/test_gem_commands_sources_command.rb
index ff00b9aa23..59acfb1ed6 100644
--- a/test/rubygems/test_gem_commands_sources_command.rb
+++ b/test/rubygems/test_gem_commands_sources_command.rb
@@ -107,6 +107,36 @@ class TestGemCommandsSourcesCommand < Gem::TestCase
assert_empty ui.error
end
+ def test_execute_add_allow_typo_squatting_source_forced
+ rubygems_org = "https://rubyems.org"
+
+ spec_fetcher do |fetcher|
+ fetcher.spec("a", 1)
+ end
+
+ specs = Gem::Specification.map do |spec|
+ [spec.name, spec.version, spec.original_platform]
+ end
+
+ specs_dump_gz = StringIO.new
+ Zlib::GzipWriter.wrap(specs_dump_gz) do |io|
+ Marshal.dump(specs, io)
+ end
+
+ @fetcher.data["#{rubygems_org}/specs.#{@marshal_version}.gz"] = specs_dump_gz.string
+ @cmd.handle_options %W[--force --add #{rubygems_org}]
+
+ @cmd.execute
+
+ expected = "https://rubyems.org added to sources\n"
+ assert_equal expected, ui.output
+
+ source = Gem::Source.new(rubygems_org)
+ assert Gem.sources.include?(source)
+
+ assert_empty ui.error
+ end
+
def test_execute_add_deny_typo_squatting_source
rubygems_org = "https://rubyems.org"
@@ -283,6 +313,36 @@ source http://gems.example.com/ already present in the cache
assert_empty @ui.error
end
+ def test_execute_add_http_rubygems_org_forced
+ rubygems_org = "http://rubygems.org"
+
+ spec_fetcher do |fetcher|
+ fetcher.spec("a", 1)
+ end
+
+ specs = Gem::Specification.map do |spec|
+ [spec.name, spec.version, spec.original_platform]
+ end
+
+ specs_dump_gz = StringIO.new
+ Zlib::GzipWriter.wrap(specs_dump_gz) do |io|
+ Marshal.dump(specs, io)
+ end
+
+ @fetcher.data["#{rubygems_org}/specs.#{@marshal_version}.gz"] = specs_dump_gz.string
+ @cmd.handle_options %W[--force --add #{rubygems_org}]
+
+ @cmd.execute
+
+ expected = "http://rubygems.org added to sources\n"
+ assert_equal expected, ui.output
+
+ source = Gem::Source.new(rubygems_org)
+ assert Gem.sources.include?(source)
+
+ assert_empty ui.error
+ end
+
def test_execute_add_https_rubygems_org
https_rubygems_org = 'https://rubygems.org/'
diff --git a/test/rubygems/test_gem_commands_specification_command.rb b/test/rubygems/test_gem_commands_specification_command.rb
index e055246cdc..732278eb6f 100644
--- a/test/rubygems/test_gem_commands_specification_command.rb
+++ b/test/rubygems/test_gem_commands_specification_command.rb
@@ -186,6 +186,34 @@ class TestGemCommandsSpecificationCommand < Gem::TestCase
assert_equal Gem::Version.new("1"), spec.version
end
+ def test_execute_remote_with_version_and_platform
+ original_platforms = Gem.platforms.dup
+
+ spec_fetcher do |fetcher|
+ fetcher.spec 'foo', "1"
+ fetcher.spec 'foo', "1" do |s|
+ s.platform = 'x86_64-linux'
+ end
+ end
+
+ @cmd.options[:args] = %w[foo]
+ @cmd.options[:version] = "1"
+ @cmd.options[:domain] = :remote
+ @cmd.options[:added_platform] = true
+ Gem.platforms = [Gem::Platform::RUBY, Gem::Platform.new("x86_64-linux")]
+
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ spec = Gem::Specification.from_yaml @ui.output
+
+ assert_equal Gem::Version.new("1"), spec.version
+ assert_equal Gem::Platform.new("x86_64-linux"), spec.platform
+ ensure
+ Gem.platforms = original_platforms
+ end
+
def test_execute_remote_without_prerelease
spec_fetcher do |fetcher|
fetcher.spec 'foo', '2.0.0'
diff --git a/test/rubygems/test_gem_commands_update_command.rb b/test/rubygems/test_gem_commands_update_command.rb
index cacde06fd5..749e9bee20 100644
--- a/test/rubygems/test_gem_commands_update_command.rb
+++ b/test/rubygems/test_gem_commands_update_command.rb
@@ -198,13 +198,13 @@ class TestGemCommandsUpdateCommand < Gem::TestCase
def test_execute_system_specific_newer_than_or_equal_to_3_2_leaves_plugins_dir_alone
spec_fetcher do |fetcher|
- fetcher.download 'rubygems-update', 3.2 do |s|
+ fetcher.download 'rubygems-update', "3.2.a" do |s|
s.files = %w[setup.rb]
end
end
@cmd.options[:args] = []
- @cmd.options[:system] = "3.2"
+ @cmd.options[:system] = "3.2.a"
FileUtils.mkdir_p Gem.plugindir
plugin_file = File.join(Gem.plugindir, 'a_plugin.rb')
diff --git a/test/rubygems/test_gem_commands_yank_command.rb b/test/rubygems/test_gem_commands_yank_command.rb
index 8e453dfabf..3046655aa8 100644
--- a/test/rubygems/test_gem_commands_yank_command.rb
+++ b/test/rubygems/test_gem_commands_yank_command.rb
@@ -70,7 +70,7 @@ class TestGemCommandsYankCommand < Gem::TestCase
yank_uri = 'http://example/api/v1/gems/yank'
@fetcher.data[yank_uri] = [
[response_fail, 401, 'Unauthorized'],
- ['Successfully yanked', 200, 'OK']
+ ['Successfully yanked', 200, 'OK'],
]
@cmd.options[:args] = %w[a]
@@ -147,4 +147,34 @@ class TestGemCommandsYankCommand < Gem::TestCase
assert_equal 'key', @fetcher.last_request['Authorization']
assert_equal [yank_uri], @fetcher.paths
end
+
+ def test_yank_gem_unathorized_api_key
+ response_forbidden = "The API key doesn't have access"
+ response_success = 'Successfully yanked'
+ host = 'http://example'
+
+ @fetcher.data["#{host}/api/v1/gems/yank"] = [
+ [response_forbidden, 403, 'Forbidden'],
+ [response_success, 200, "OK"],
+ ]
+
+ @fetcher.data["#{host}/api/v1/api_key"] = ["", 200, "OK"]
+ @cmd.options[:args] = %w[a]
+ @cmd.options[:added_platform] = true
+ @cmd.options[:version] = req('= 1.0')
+ @cmd.instance_variable_set :@host, host
+ @cmd.instance_variable_set :@scope, :yank_rubygem
+
+ @ui = Gem::MockGemUi.new "some@mail.com\npass\n"
+ use_ui @ui do
+ @cmd.execute
+ end
+
+ access_notice = "The existing key doesn't have access of yank_rubygem on http://example. Please sign in to update access."
+ assert_match access_notice, @ui.output
+ assert_match "Email:", @ui.output
+ assert_match "Password:", @ui.output
+ assert_match "Added yank_rubygem scope to the existing API key", @ui.output
+ assert_match response_success, @ui.output
+ end
end
diff --git a/test/rubygems/test_gem_dependency_installer.rb b/test/rubygems/test_gem_dependency_installer.rb
index 803b95e88c..fe8a74c750 100644
--- a/test/rubygems/test_gem_dependency_installer.rb
+++ b/test/rubygems/test_gem_dependency_installer.rb
@@ -528,6 +528,40 @@ class TestGemDependencyInstaller < Gem::TestCase
assert_equal %w[a-1 e-1], inst.installed_gems.map {|s| s.full_name }
end
+ def test_install_no_minimal_deps
+ util_setup_gems
+
+ _, e1_gem = util_gem 'e', '1' do |s|
+ s.add_dependency 'b'
+ end
+
+ _, b2_gem = util_gem 'b', '2' do |s|
+ s.add_dependency 'a'
+ end
+
+ FileUtils.mv @a1_gem, @tempdir
+ FileUtils.mv @b1_gem, @tempdir
+ FileUtils.mv b2_gem, @tempdir
+ FileUtils.mv e1_gem, @tempdir
+
+ inst = nil
+
+ Dir.chdir @tempdir do
+ inst = Gem::DependencyInstaller.new :ignore_dependencies => true
+ inst.install 'b', req('= 1')
+ end
+
+ assert_equal %w[b-1], inst.installed_gems.map {|s| s.full_name },
+ 'sanity check'
+
+ Dir.chdir @tempdir do
+ inst = Gem::DependencyInstaller.new :minimal_deps => false
+ inst.install 'e'
+ end
+
+ assert_equal %w[a-1 b-2 e-1], inst.installed_gems.map {|s| s.full_name }
+ end
+
def test_install_no_document
util_setup_gems
@@ -749,7 +783,7 @@ class TestGemDependencyInstaller < Gem::TestCase
inst = nil
Dir.chdir @tempdir do
- inst = Gem::DependencyInstaller.new
+ inst = Gem::DependencyInstaller.new :force => true
inst.install 'a'
end
@@ -842,7 +876,7 @@ class TestGemDependencyInstaller < Gem::TestCase
require 'rubygems/openssl'
- if defined? OpenSSL
+ if Gem::HAVE_OPENSSL
def test_install_security_policy
util_setup_gems
diff --git a/test/rubygems/test_gem_dependency_list.rb b/test/rubygems/test_gem_dependency_list.rb
index d8ef3d4f0e..097e680596 100644
--- a/test/rubygems/test_gem_dependency_list.rb
+++ b/test/rubygems/test_gem_dependency_list.rb
@@ -139,8 +139,8 @@ class TestGemDependencyList < Gem::TestCase
exp = {
"b" => [
- Gem::Dependency.new("a", ">= 1")
- ]
+ Gem::Dependency.new("a", ">= 1"),
+ ],
}
assert_equal exp, @deplist.why_not_ok?
diff --git a/test/rubygems/test_gem_ext_builder.rb b/test/rubygems/test_gem_ext_builder.rb
index abd33d237a..6e6bf89f9c 100644
--- a/test/rubygems/test_gem_ext_builder.rb
+++ b/test/rubygems/test_gem_ext_builder.rb
@@ -30,9 +30,8 @@ class TestGemExtBuilder < Gem::TestCase
ENV['DESTDIR'] = 'destination'
results = []
- Dir.chdir @ext do
- File.open 'Makefile', 'w' do |io|
- io.puts <<-MAKEFILE
+ File.open File.join(@ext, 'Makefile'), 'w' do |io|
+ io.puts <<-MAKEFILE
all:
\t@#{Gem.ruby} -e "puts %Q{all: \#{ENV['DESTDIR']}}"
@@ -41,12 +40,11 @@ clean:
install:
\t@#{Gem.ruby} -e "puts %Q{install: \#{ENV['DESTDIR']}}"
- MAKEFILE
- end
-
- Gem::Ext::Builder.make @dest_path, results
+ MAKEFILE
end
+ Gem::Ext::Builder.make @dest_path, results, @ext
+
results = results.join("\n").b
assert_match %r{"DESTDIR=#{ENV['DESTDIR']}" clean$}, results
@@ -64,20 +62,18 @@ install:
ENV['DESTDIR'] = 'destination'
results = []
- Dir.chdir @ext do
- File.open 'Makefile', 'w' do |io|
- io.puts <<-MAKEFILE
+ File.open File.join(@ext, 'Makefile'), 'w' do |io|
+ io.puts <<-MAKEFILE
all:
\t@#{Gem.ruby} -e "puts %Q{all: \#{ENV['DESTDIR']}}"
install:
\t@#{Gem.ruby} -e "puts %Q{install: \#{ENV['DESTDIR']}}"
- MAKEFILE
- end
-
- Gem::Ext::Builder.make @dest_path, results
+ MAKEFILE
end
+ Gem::Ext::Builder.make @dest_path, results, @ext
+
results = results.join("\n").b
assert_match %r{"DESTDIR=#{ENV['DESTDIR']}" clean$}, results
diff --git a/test/rubygems/test_gem_ext_cmake_builder.rb b/test/rubygems/test_gem_ext_cmake_builder.rb
index 60a1fa13f6..e347359679 100644
--- a/test/rubygems/test_gem_ext_cmake_builder.rb
+++ b/test/rubygems/test_gem_ext_cmake_builder.rb
@@ -36,9 +36,7 @@ install (FILES test.txt DESTINATION bin)
output = []
- Dir.chdir @ext do
- Gem::Ext::CmakeBuilder.build nil, @dest_path, output
- end
+ Gem::Ext::CmakeBuilder.build nil, @dest_path, output, [], nil, @ext
output = output.join "\n"
@@ -54,9 +52,7 @@ install (FILES test.txt DESTINATION bin)
output = []
error = assert_raises Gem::InstallError do
- Dir.chdir @ext do
- Gem::Ext::CmakeBuilder.build nil, @dest_path, output
- end
+ Gem::Ext::CmakeBuilder.build nil, @dest_path, output, [], nil, @ext
end
output = output.join "\n"
@@ -77,9 +73,7 @@ install (FILES test.txt DESTINATION bin)
output = []
- Dir.chdir @ext do
- Gem::Ext::CmakeBuilder.build nil, @dest_path, output
- end
+ Gem::Ext::CmakeBuilder.build nil, @dest_path, output, [], nil, @ext
output = output.join "\n"
diff --git a/test/rubygems/test_gem_ext_configure_builder.rb b/test/rubygems/test_gem_ext_configure_builder.rb
index 7d7f3759ea..a74f5ce055 100644
--- a/test/rubygems/test_gem_ext_configure_builder.rb
+++ b/test/rubygems/test_gem_ext_configure_builder.rb
@@ -25,9 +25,7 @@ class TestGemExtConfigureBuilder < Gem::TestCase
output = []
- Dir.chdir @ext do
- Gem::Ext::ConfigureBuilder.build nil, @dest_path, output
- end
+ Gem::Ext::ConfigureBuilder.build nil, @dest_path, output, [], nil, @ext
assert_match(/^current directory:/, output.shift)
assert_equal "sh ./configure --prefix=#{@dest_path}", output.shift
@@ -48,9 +46,7 @@ class TestGemExtConfigureBuilder < Gem::TestCase
output = []
error = assert_raises Gem::InstallError do
- Dir.chdir @ext do
- Gem::Ext::ConfigureBuilder.build nil, @dest_path, output
- end
+ Gem::Ext::ConfigureBuilder.build nil, @dest_path, output, [], nil, @ext
end
shell_error_msg = %r{(\./configure: .*)|((?:[Cc]an't|cannot) open '?\./configure'?(?:: No such file or directory)?)}
@@ -74,9 +70,7 @@ class TestGemExtConfigureBuilder < Gem::TestCase
end
output = []
- Dir.chdir @ext do
- Gem::Ext::ConfigureBuilder.build nil, @dest_path, output
- end
+ Gem::Ext::ConfigureBuilder.build nil, @dest_path, output, [], nil, @ext
assert_contains_make_command 'clean', output[1]
assert_contains_make_command '', output[4]
diff --git a/test/rubygems/test_gem_ext_ext_conf_builder.rb b/test/rubygems/test_gem_ext_ext_conf_builder.rb
index 05ac538ec2..21fe27166b 100644
--- a/test/rubygems/test_gem_ext_ext_conf_builder.rb
+++ b/test/rubygems/test_gem_ext_ext_conf_builder.rb
@@ -29,12 +29,9 @@ class TestGemExtExtConfBuilder < Gem::TestCase
output = []
- Dir.chdir @ext do
- result =
- Gem::Ext::ExtConfBuilder.build 'extconf.rb', @dest_path, output
+ result = Gem::Ext::ExtConfBuilder.build 'extconf.rb', @dest_path, output, [], nil, @ext
- assert_same result, output
- end
+ assert_same result, output
assert_match(/^current directory:/, output[0])
assert_match(/^#{Gem.ruby}.* extconf.rb/, output[1])
@@ -59,9 +56,7 @@ class TestGemExtExtConfBuilder < Gem::TestCase
output = []
- Dir.chdir @ext do
- Gem::Ext::ExtConfBuilder.build 'extconf.rb', @dest_path, output
- end
+ Gem::Ext::ExtConfBuilder.build 'extconf.rb', @dest_path, output, [], nil, @ext
assert_equal "creating Makefile\n", output[2]
assert_contains_make_command 'clean', output[4]
@@ -86,9 +81,7 @@ class TestGemExtExtConfBuilder < Gem::TestCase
output = []
assert_raises Gem::InstallError do
- Dir.chdir @ext do
- Gem::Ext::ExtConfBuilder.build 'extconf.rb', @dest_path, output
- end
+ Gem::Ext::ExtConfBuilder.build 'extconf.rb', @dest_path, output, [], nil, @ext
end
assert_equal "creating Makefile\n", output[2]
@@ -112,9 +105,7 @@ class TestGemExtExtConfBuilder < Gem::TestCase
output = []
error = assert_raises Gem::InstallError do
- Dir.chdir @ext do
- Gem::Ext::ExtConfBuilder.build 'extconf.rb', @dest_path, output
- end
+ Gem::Ext::ExtConfBuilder.build 'extconf.rb', @dest_path, output, [], nil, @ext
end
assert_equal 'extconf failed, exit code 1', error.message
@@ -139,9 +130,7 @@ class TestGemExtExtConfBuilder < Gem::TestCase
output = []
- Dir.chdir @ext do
- Gem::Ext::ExtConfBuilder.build 'extconf.rb', @dest_path, output
- end
+ Gem::Ext::ExtConfBuilder.build 'extconf.rb', @dest_path, output, [], nil, @ext
refute_includes(output, "To see why this extension failed to compile, please check the mkmf.log which can be found here:\n")
@@ -181,9 +170,7 @@ end
output = []
- Dir.chdir @ext do
- Gem::Ext::ExtConfBuilder.build 'extconf.rb', @dest_path, output
- end
+ Gem::Ext::ExtConfBuilder.build 'extconf.rb', @dest_path, output, [], nil, @ext
assert_contains_make_command 'clean', output[4]
assert_contains_make_command '', output[7]
@@ -207,9 +194,7 @@ end
makefile.puts "install:"
end
- Dir.chdir @ext do
- Gem::Ext::ExtConfBuilder.make @ext, output
- end
+ Gem::Ext::ExtConfBuilder.make @ext, output, @ext
assert_contains_make_command 'clean', output[1]
assert_contains_make_command '', output[4]
@@ -218,9 +203,7 @@ end
def test_class_make_no_Makefile
error = assert_raises Gem::InstallError do
- Dir.chdir @ext do
- Gem::Ext::ExtConfBuilder.make @ext, ['output']
- end
+ Gem::Ext::ExtConfBuilder.make @ext, ['output'], @ext
end
assert_equal 'Makefile not found', error.message
diff --git a/test/rubygems/test_gem_ext_rake_builder.rb b/test/rubygems/test_gem_ext_rake_builder.rb
index a13b09c98b..8cb96f7dd3 100644
--- a/test/rubygems/test_gem_ext_rake_builder.rb
+++ b/test/rubygems/test_gem_ext_rake_builder.rb
@@ -18,9 +18,7 @@ class TestGemExtRakeBuilder < Gem::TestCase
output = []
build_rake_in do |rake|
- Dir.chdir @ext do
- Gem::Ext::RakeBuilder.build 'mkrf_conf.rb', @dest_path, output
- end
+ Gem::Ext::RakeBuilder.build 'mkrf_conf.rb', @dest_path, output, [], nil, @ext
output = output.join "\n"
@@ -38,10 +36,8 @@ class TestGemExtRakeBuilder < Gem::TestCase
output = []
build_rake_in do |rake|
- Dir.chdir @ext do
- non_empty_args_list = ['']
- Gem::Ext::RakeBuilder.build 'mkrf_conf.rb', @dest_path, output, non_empty_args_list
- end
+ non_empty_args_list = ['']
+ Gem::Ext::RakeBuilder.build 'mkrf_conf.rb', @dest_path, output, non_empty_args_list, nil, @ext
output = output.join "\n"
@@ -55,9 +51,7 @@ class TestGemExtRakeBuilder < Gem::TestCase
output = []
build_rake_in do |rake|
- Dir.chdir @ext do
- Gem::Ext::RakeBuilder.build "ext/Rakefile", @dest_path, output, ["test1", "test2"]
- end
+ Gem::Ext::RakeBuilder.build "ext/Rakefile", @dest_path, output, ["test1", "test2"], nil, @ext
output = output.join "\n"
@@ -72,9 +66,7 @@ class TestGemExtRakeBuilder < Gem::TestCase
build_rake_in(false) do |rake|
error = assert_raises Gem::InstallError do
- Dir.chdir @ext do
- Gem::Ext::RakeBuilder.build "mkrf_conf.rb", @dest_path, output
- end
+ Gem::Ext::RakeBuilder.build "mkrf_conf.rb", @dest_path, output, [], nil, @ext
end
assert_match %r{^rake failed}, error.message
diff --git a/test/rubygems/test_gem_gem_runner.rb b/test/rubygems/test_gem_gem_runner.rb
index 72a5c83431..8df11ecebc 100644
--- a/test/rubygems/test_gem_gem_runner.rb
+++ b/test/rubygems/test_gem_gem_runner.rb
@@ -74,17 +74,18 @@ class TestGemGemRunner < Gem::TestCase
args = %w[query]
use_ui @ui do
- assert_nil @runner.run(args)
+ @runner.run(args)
end
assert_match(/WARNING: query command is deprecated. It will be removed in Rubygems [0-9]+/, @ui.error)
+ assert_match(/WARNING: It is recommended that you use `gem search` or `gem list` instead/, @ui.error)
end
def test_info_succeeds
args = %w[info]
use_ui @ui do
- assert_nil @runner.run(args)
+ @runner.run(args)
end
assert_empty @ui.error
@@ -94,7 +95,7 @@ class TestGemGemRunner < Gem::TestCase
args = %w[list]
use_ui @ui do
- assert_nil @runner.run(args)
+ @runner.run(args)
end
assert_empty @ui.error
@@ -104,7 +105,7 @@ class TestGemGemRunner < Gem::TestCase
args = %w[search]
use_ui @ui do
- assert_nil @runner.run(args)
+ @runner.run(args)
end
assert_empty @ui.error
diff --git a/test/rubygems/test_gem_gemcutter_utilities.rb b/test/rubygems/test_gem_gemcutter_utilities.rb
index 48f6b09715..3290a3a908 100644
--- a/test/rubygems/test_gem_gemcutter_utilities.rb
+++ b/test/rubygems/test_gem_gemcutter_utilities.rb
@@ -32,7 +32,7 @@ class TestGemGemcutterUtilities < Gem::TestCase
def test_alternate_key_alternate_host
keys = {
:rubygems_api_key => 'KEY',
- "http://rubygems.engineyard.com" => "EYKEY"
+ "http://rubygems.engineyard.com" => "EYKEY",
}
FileUtils.mkdir_p File.dirname Gem.configuration.credentials_path
@@ -202,7 +202,7 @@ class TestGemGemcutterUtilities < Gem::TestCase
assert_match 'You have enabled multi-factor authentication. Please enter OTP code.', @sign_in_ui.output
assert_match 'Code: ', @sign_in_ui.output
- assert_match 'Signed in.', @sign_in_ui.output
+ assert_match 'Signed in with API key:', @sign_in_ui.output
assert_equal '111111', @fetcher.last_request['OTP']
end
@@ -233,7 +233,7 @@ class TestGemGemcutterUtilities < Gem::TestCase
@fetcher.data["#{host}/api/v1/api_key"] = response
Gem::RemoteFetcher.fetcher = @fetcher
- @sign_in_ui = Gem::MockGemUi.new("#{email}\n#{password}\n" + extra_input)
+ @sign_in_ui = Gem::MockGemUi.new("#{email}\n#{password}\n\n\n\n\n\n\n\n\n" + extra_input)
use_ui @sign_in_ui do
if args.length > 0
diff --git a/test/rubygems/test_gem_install_update_options.rb b/test/rubygems/test_gem_install_update_options.rb
index d9da22b129..b4528dba17 100644
--- a/test/rubygems/test_gem_install_update_options.rb
+++ b/test/rubygems/test_gem_install_update_options.rb
@@ -30,7 +30,7 @@ class TestGemInstallUpdateOptions < Gem::InstallerTestCase
args.concat %w[--vendor] unless Gem.java_platform?
- args.concat %w[-P HighSecurity] if defined?(OpenSSL::SSL)
+ args.concat %w[-P HighSecurity] if Gem::HAVE_OPENSSL
assert @cmd.handles?(args)
end
@@ -92,7 +92,7 @@ class TestGemInstallUpdateOptions < Gem::InstallerTestCase
end
def test_security_policy
- skip 'openssl is missing' unless defined?(OpenSSL::SSL)
+ skip 'openssl is missing' unless Gem::HAVE_OPENSSL
@cmd.handle_options %w[-P HighSecurity]
@@ -100,7 +100,7 @@ class TestGemInstallUpdateOptions < Gem::InstallerTestCase
end
def test_security_policy_unknown
- skip 'openssl is missing' unless defined?(OpenSSL::SSL)
+ skip 'openssl is missing' unless Gem::HAVE_OPENSSL
@cmd.add_install_update_options
@@ -192,4 +192,16 @@ class TestGemInstallUpdateOptions < Gem::InstallerTestCase
assert_equal true, @cmd.options[:post_install_message]
end
+
+ def test_minimal_deps_no
+ @cmd.handle_options %w[--no-minimal-deps]
+
+ assert_equal false, @cmd.options[:minimal_deps]
+ end
+
+ def test_minimal_deps
+ @cmd.handle_options %w[--minimal-deps]
+
+ assert_equal true, @cmd.options[:minimal_deps]
+ end
end
diff --git a/test/rubygems/test_gem_installer.rb b/test/rubygems/test_gem_installer.rb
index e984c7079c..0a9a2e38dd 100644
--- a/test/rubygems/test_gem_installer.rb
+++ b/test/rubygems/test_gem_installer.rb
@@ -83,7 +83,7 @@ end
end
def test_check_executable_overwrite_default_bin_dir
- installer = setup_base_installer
+ installer = setup_base_installer(false)
bindir(Gem.bindir) do
util_conflict_executable false
@@ -143,7 +143,7 @@ gem 'other', version
end
def test_check_executable_overwrite_other_gem
- installer = setup_base_installer
+ installer = setup_base_installer(false)
util_conflict_executable true
@@ -287,7 +287,7 @@ gem 'other', version
end
def test_ensure_loadable_spec_security_policy
- skip 'openssl is missing' unless defined?(OpenSSL::SSL)
+ skip 'openssl is missing' unless Gem::HAVE_OPENSSL
_, a_gem = util_gem 'a', 2 do |s|
s.add_dependency 'garbage ~> 5'
@@ -345,7 +345,7 @@ gem 'other', version
options = {
:bin_dir => bin_dir,
- :install_dir => "/non/existent"
+ :install_dir => "/non/existent",
}
inst = Gem::Installer.at '', options
@@ -794,10 +794,35 @@ gem 'other', version
assert_equal spec, installer.install
- assert !File.exist?(system_path), 'plugin not written to user plugins_dir'
+ assert !File.exist?(system_path), 'plugin incorrectly written to system plugins_dir'
assert File.exist?(user_path), 'plugin not written to user plugins_dir'
end
+ def test_generate_plugins_with_build_root
+ spec = quick_gem 'a' do |s|
+ write_file File.join(@tempdir, 'lib', 'rubygems_plugin.rb') do |io|
+ io.write "puts __FILE__"
+ end
+
+ s.files += %w[lib/rubygems_plugin.rb]
+ end
+
+ util_build_gem spec
+
+ File.chmod(0555, Gem.plugindir)
+ system_path = File.join(Gem.plugindir, 'a_plugin.rb')
+
+ build_root = File.join(@tempdir, 'build_root')
+ build_root_path = File.join(build_root, Gem.plugindir.gsub(/^[a-zA-Z]:/, ''), 'a_plugin.rb')
+
+ installer = Gem::Installer.at spec.cache_file, :build_root => build_root
+
+ assert_equal spec, installer.install
+
+ assert !File.exist?(system_path), 'plugin written incorrect written to system plugins_dir'
+ assert File.exist?(build_root_path), 'plugin not written to build_root'
+ end
+
def test_keeps_plugins_up_to_date
# NOTE: version a-2 is already installed by setup hooks
@@ -1134,7 +1159,7 @@ gem 'other', version
Gem::Package.build @spec
end
end
- installer = Gem::Installer.at @gem
+ installer = Gem::Installer.at @gem, :force => true
build_rake_in do
use_ui @ui do
assert_equal @spec, installer.install
@@ -1156,6 +1181,15 @@ gem 'other', version
assert_path_exists gem_dir
end
+ def test_install_build_root
+ build_root = File.join(@tempdir, 'build_root')
+
+ @gem = setup_base_gem
+ installer = Gem::Installer.at @gem, :build_root => build_root
+
+ assert_equal @spec, installer.install
+ end
+
def test_install_missing_dirs
installer = setup_base_installer
@@ -1337,7 +1371,7 @@ gem 'other', version
# reinstall the gem, this is also the same as pristine
use_ui @ui do
- installer = Gem::Installer.at path
+ installer = Gem::Installer.at path, :force => true
installer.install
end
@@ -1537,6 +1571,7 @@ gem 'other', version
installer = setup_base_installer
@spec.add_dependency 'b', '> 5'
installer = util_setup_gem
+ installer.force = false
use_ui @ui do
assert_raises Gem::InstallError do
@@ -1781,13 +1816,24 @@ gem 'other', version
def test_process_options_build_root
build_root = File.join @tempdir, 'build_root'
+ bin_dir = File.join(build_root, @gemhome.gsub(/^[a-zA-Z]:/, ''), 'bin')
+ gem_home = File.join(build_root, @gemhome.gsub(/^[a-zA-Z]:/, ''))
+ plugins_dir = File.join(build_root, @gemhome.gsub(/^[a-zA-Z]:/, ''), 'plugins')
@gem = setup_base_gem
- installer = Gem::Installer.at @gem, :build_root => build_root
+ installer = use_ui(@ui) { Gem::Installer.at @gem, :build_root => build_root }
+
+ assert_equal build_root, installer.build_root
+ assert_equal bin_dir, installer.bin_dir
+ assert_equal gem_home, installer.gem_home
- assert_equal Pathname(build_root), installer.build_root
- assert_equal File.join(build_root, @gemhome, 'bin'), installer.bin_dir
- assert_equal File.join(build_root, @gemhome), installer.gem_home
+ errors = @ui.error.split("\n")
+
+ assert_equal "WARNING: You build with buildroot.", errors.shift
+ assert_equal " Build root: #{build_root}", errors.shift
+ assert_equal " Bin dir: #{bin_dir}", errors.shift
+ assert_equal " Gem home: #{gem_home}", errors.shift
+ assert_equal " Plugins dir: #{plugins_dir}", errors.shift
end
def test_shebang_arguments
@@ -2169,6 +2215,23 @@ gem 'other', version
assert_equal ['exe/executable'], default_spec.files
end
+ def test_default_gem_to_specific_install_dir
+ @gem = setup_base_gem
+ installer = util_installer @spec, "#{@gemhome}2"
+ installer.options[:install_as_default] = true
+
+ use_ui @ui do
+ installer.install
+ end
+
+ assert_directory_exists File.join("#{@gemhome}2", 'specifications')
+ assert_directory_exists File.join("#{@gemhome}2", 'specifications', 'default')
+
+ default_spec = eval File.read File.join("#{@gemhome}2", 'specifications', 'default', 'a-2.gemspec')
+ assert_equal Gem::Version.new("2"), default_spec.version
+ assert_equal ['bin/executable'], default_spec.files
+ end
+
def test_package_attribute
gem = quick_gem 'c' do |spec|
util_make_exec spec, '#!/usr/bin/ruby', 'exe'
diff --git a/test/rubygems/test_gem_package.rb b/test/rubygems/test_gem_package.rb
index 5e9c3b7b81..fd28f9a2a5 100644
--- a/test/rubygems/test_gem_package.rb
+++ b/test/rubygems/test_gem_package.rb
@@ -95,7 +95,7 @@ class TestGemPackage < Gem::Package::TarTestCase
'SHA256' => {
'metadata.gz' => metadata_sha256,
'data.tar.gz' => Digest::SHA256.hexdigest(tar),
- }
+ },
}
assert_equal expected, YAML.load(checksums)
@@ -252,7 +252,7 @@ class TestGemPackage < Gem::Package::TarTestCase
end
def test_build_auto_signed
- skip 'openssl is missing' unless defined?(OpenSSL::SSL)
+ skip 'openssl is missing' unless Gem::HAVE_OPENSSL
FileUtils.mkdir_p File.join(Gem.user_home, '.gem')
@@ -295,7 +295,7 @@ class TestGemPackage < Gem::Package::TarTestCase
end
def test_build_auto_signed_encrypted_key
- skip 'openssl is missing' unless defined?(OpenSSL::SSL)
+ skip 'openssl is missing' unless Gem::HAVE_OPENSSL
FileUtils.mkdir_p File.join(Gem.user_home, '.gem')
@@ -364,7 +364,7 @@ class TestGemPackage < Gem::Package::TarTestCase
end
def test_build_signed
- skip 'openssl is missing' unless defined?(OpenSSL::SSL)
+ skip 'openssl is missing' unless Gem::HAVE_OPENSSL
spec = Gem::Specification.new 'build', '1'
spec.summary = 'build'
@@ -401,7 +401,7 @@ class TestGemPackage < Gem::Package::TarTestCase
end
def test_build_signed_encrypted_key
- skip 'openssl is missing' unless defined?(OpenSSL::SSL)
+ skip 'openssl is missing' unless Gem::HAVE_OPENSSL
spec = Gem::Specification.new 'build', '1'
spec.summary = 'build'
@@ -957,7 +957,7 @@ class TestGemPackage < Gem::Package::TarTestCase
end
def test_verify_security_policy
- skip 'openssl is missing' unless defined?(OpenSSL::SSL)
+ skip 'openssl is missing' unless Gem::HAVE_OPENSSL
package = Gem::Package.new @gem
package.security_policy = Gem::Security::HighSecurity
@@ -974,7 +974,7 @@ class TestGemPackage < Gem::Package::TarTestCase
end
def test_verify_security_policy_low_security
- skip 'openssl is missing' unless defined?(OpenSSL::SSL)
+ skip 'openssl is missing' unless Gem::HAVE_OPENSSL
@spec.cert_chain = [PUBLIC_CERT.to_pem]
@spec.signing_key = PRIVATE_KEY
@@ -994,7 +994,7 @@ class TestGemPackage < Gem::Package::TarTestCase
end
def test_verify_security_policy_checksum_missing
- skip 'openssl is missing' unless defined?(OpenSSL::SSL)
+ skip 'openssl is missing' unless Gem::HAVE_OPENSSL
@spec.cert_chain = [PUBLIC_CERT.to_pem]
@spec.signing_key = PRIVATE_KEY
diff --git a/test/rubygems/test_gem_package_old.rb b/test/rubygems/test_gem_package_old.rb
index f2b1719dc3..8c4c20006b 100644
--- a/test/rubygems/test_gem_package_old.rb
+++ b/test/rubygems/test_gem_package_old.rb
@@ -23,7 +23,7 @@ unless Gem.java_platform? # jruby can't require the simple_gem file
end
def test_contents_security_policy
- skip 'openssl is missing' unless defined?(OpenSSL::SSL)
+ skip 'openssl is missing' unless Gem::HAVE_OPENSSL
@package.security_policy = Gem::Security::AlmostNoSecurity
@@ -44,7 +44,7 @@ unless Gem.java_platform? # jruby can't require the simple_gem file
end
def test_extract_files_security_policy
- skip 'openssl is missing' unless defined?(OpenSSL::SSL)
+ skip 'openssl is missing' unless Gem::HAVE_OPENSSL
@package.security_policy = Gem::Security::AlmostNoSecurity
@@ -58,7 +58,7 @@ unless Gem.java_platform? # jruby can't require the simple_gem file
end
def test_spec_security_policy
- skip 'openssl is missing' unless defined?(OpenSSL::SSL)
+ skip 'openssl is missing' unless Gem::HAVE_OPENSSL
@package.security_policy = Gem::Security::AlmostNoSecurity
@@ -68,7 +68,7 @@ unless Gem.java_platform? # jruby can't require the simple_gem file
end
def test_verify
- skip 'openssl is missing' unless defined?(OpenSSL::SSL)
+ skip 'openssl is missing' unless Gem::HAVE_OPENSSL
assert @package.verify
diff --git a/test/rubygems/test_gem_package_tar_writer.rb b/test/rubygems/test_gem_package_tar_writer.rb
index e31efdd55f..25dac5f148 100644
--- a/test/rubygems/test_gem_package_tar_writer.rb
+++ b/test/rubygems/test_gem_package_tar_writer.rb
@@ -117,7 +117,7 @@ class TestGemPackageTarWriter < Gem::Package::TarTestCase
end
def test_add_file_signer
- skip 'openssl is missing' unless defined?(OpenSSL::SSL)
+ skip 'openssl is missing' unless Gem::HAVE_OPENSSL
signer = Gem::Security::Signer.new PRIVATE_KEY, [PUBLIC_CERT]
diff --git a/test/rubygems/test_gem_platform.rb b/test/rubygems/test_gem_platform.rb
index 030033d2af..83a6f24de4 100644
--- a/test/rubygems/test_gem_platform.rb
+++ b/test/rubygems/test_gem_platform.rb
@@ -11,10 +11,69 @@ class TestGemPlatform < Gem::TestCase
end
def test_self_match
- assert Gem::Platform.match(nil), 'nil == ruby'
- assert Gem::Platform.match(Gem::Platform.local), 'exact match'
- assert Gem::Platform.match(Gem::Platform.local.to_s), '=~ match'
- assert Gem::Platform.match(Gem::Platform::RUBY), 'ruby'
+ Gem::Deprecate.skip_during do
+ assert Gem::Platform.match(nil), 'nil == ruby'
+ assert Gem::Platform.match(Gem::Platform.local), 'exact match'
+ assert Gem::Platform.match(Gem::Platform.local.to_s), '=~ match'
+ assert Gem::Platform.match(Gem::Platform::RUBY), 'ruby'
+ end
+ end
+
+ def test_self_match_gem?
+ assert Gem::Platform.match_gem?(nil, 'json'), 'nil == ruby'
+ assert Gem::Platform.match_gem?(Gem::Platform.local, 'json'), 'exact match'
+ assert Gem::Platform.match_gem?(Gem::Platform.local.to_s, 'json'), '=~ match'
+ assert Gem::Platform.match_gem?(Gem::Platform::RUBY, 'json'), 'ruby'
+ end
+
+ def test_self_match_spec?
+ make_spec = -> platform do
+ util_spec 'mygem-for-platform-match_spec', '1' do |s|
+ s.platform = platform
+ end
+ end
+
+ assert Gem::Platform.match_spec?(make_spec.call(nil)), 'nil == ruby'
+ assert Gem::Platform.match_spec?(make_spec.call(Gem::Platform.local)), 'exact match'
+ assert Gem::Platform.match_spec?(make_spec.call(Gem::Platform.local.to_s)), '=~ match'
+ assert Gem::Platform.match_spec?(make_spec.call(Gem::Platform::RUBY)), 'ruby'
+ end
+
+ def test_self_match_spec_with_match_gem_override
+ make_spec = -> name, platform do
+ util_spec name, '1' do |s|
+ s.platform = platform
+ end
+ end
+
+ class << Gem::Platform
+ alias_method :original_match_gem?, :match_gem?
+ def match_gem?(platform, gem_name)
+ # e.g., sassc and libv8 are such gems, their native extensions do not use the Ruby C API
+ if gem_name == 'gem-with-ruby-impl-independent-precompiled-ext'
+ match_platforms?(platform, [Gem::Platform::RUBY, Gem::Platform.local])
+ else
+ match_platforms?(platform, Gem.platforms)
+ end
+ end
+ end
+
+ platforms = Gem.platforms
+ Gem.platforms = [Gem::Platform::RUBY]
+ begin
+ assert_equal true, Gem::Platform.match_spec?(make_spec.call('mygem', Gem::Platform::RUBY))
+ assert_equal false, Gem::Platform.match_spec?(make_spec.call('mygem', Gem::Platform.local))
+
+ name = 'gem-with-ruby-impl-independent-precompiled-ext'
+ assert_equal true, Gem::Platform.match_spec?(make_spec.call(name, Gem::Platform.local))
+ ensure
+ Gem.platforms = platforms
+ class << Gem::Platform
+ remove_method :match_gem?
+ alias_method :match_gem?, :original_match_gem? # rubocop:disable Lint/DuplicateMethods
+ remove_method :original_match_gem?
+ end
+ end
end
def test_self_new
diff --git a/test/rubygems/test_gem_remote_fetcher.rb b/test/rubygems/test_gem_remote_fetcher.rb
index f6ca00589f..371998497b 100644
--- a/test/rubygems/test_gem_remote_fetcher.rb
+++ b/test/rubygems/test_gem_remote_fetcher.rb
@@ -2,13 +2,9 @@
require 'rubygems/test_case'
require 'webrick'
-begin
- require 'webrick/https'
-rescue LoadError => e
- raise unless e.path == 'openssl'
-end
+require 'webrick/https' if Gem::HAVE_OPENSSL
-unless defined?(OpenSSL::SSL)
+unless Gem::HAVE_OPENSSL
warn 'Skipping Gem::RemoteFetcher tests. openssl not found.'
end
@@ -667,7 +663,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
def test_fetch_s3_config_creds
Gem.configuration[:s3_source] = {
- 'my-bucket' => {:id => 'testuser', :secret => 'testpass'}
+ 'my-bucket' => {:id => 'testuser', :secret => 'testpass'},
}
url = 's3://my-bucket/gems/specs.4.8.gz'
Time.stub :now, Time.at(1561353581) do
@@ -679,7 +675,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
def test_fetch_s3_config_creds_with_region
Gem.configuration[:s3_source] = {
- 'my-bucket' => {:id => 'testuser', :secret => 'testpass', :region => 'us-west-2'}
+ 'my-bucket' => {:id => 'testuser', :secret => 'testpass', :region => 'us-west-2'},
}
url = 's3://my-bucket/gems/specs.4.8.gz'
Time.stub :now, Time.at(1561353581) do
@@ -691,7 +687,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
def test_fetch_s3_config_creds_with_token
Gem.configuration[:s3_source] = {
- 'my-bucket' => {:id => 'testuser', :secret => 'testpass', :security_token => 'testtoken'}
+ 'my-bucket' => {:id => 'testuser', :secret => 'testpass', :security_token => 'testtoken'},
}
url = 's3://my-bucket/gems/specs.4.8.gz'
Time.stub :now, Time.at(1561353581) do
@@ -706,7 +702,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
ENV['AWS_SECRET_ACCESS_KEY'] = 'testpass'
ENV['AWS_SESSION_TOKEN'] = nil
Gem.configuration[:s3_source] = {
- 'my-bucket' => {:provider => 'env'}
+ 'my-bucket' => {:provider => 'env'},
}
url = 's3://my-bucket/gems/specs.4.8.gz'
Time.stub :now, Time.at(1561353581) do
@@ -722,7 +718,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
ENV['AWS_SECRET_ACCESS_KEY'] = 'testpass'
ENV['AWS_SESSION_TOKEN'] = nil
Gem.configuration[:s3_source] = {
- 'my-bucket' => {:provider => 'env', :region => 'us-west-2'}
+ 'my-bucket' => {:provider => 'env', :region => 'us-west-2'},
}
url = 's3://my-bucket/gems/specs.4.8.gz'
Time.stub :now, Time.at(1561353581) do
@@ -738,7 +734,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
ENV['AWS_SECRET_ACCESS_KEY'] = 'testpass'
ENV['AWS_SESSION_TOKEN'] = 'testtoken'
Gem.configuration[:s3_source] = {
- 'my-bucket' => {:provider => 'env'}
+ 'my-bucket' => {:provider => 'env'},
}
url = 's3://my-bucket/gems/specs.4.8.gz'
Time.stub :now, Time.at(1561353581) do
@@ -758,7 +754,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
def test_fetch_s3_instance_profile_creds
Gem.configuration[:s3_source] = {
- 'my-bucket' => {:provider => 'instance_profile'}
+ 'my-bucket' => {:provider => 'instance_profile'},
}
url = 's3://my-bucket/gems/specs.4.8.gz'
@@ -772,7 +768,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
def test_fetch_s3_instance_profile_creds_with_region
Gem.configuration[:s3_source] = {
- 'my-bucket' => {:provider => 'instance_profile', :region => 'us-west-2'}
+ 'my-bucket' => {:provider => 'instance_profile', :region => 'us-west-2'},
}
url = 's3://my-bucket/gems/specs.4.8.gz'
@@ -786,7 +782,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
def test_fetch_s3_instance_profile_creds_with_token
Gem.configuration[:s3_source] = {
- 'my-bucket' => {:provider => 'instance_profile'}
+ 'my-bucket' => {:provider => 'instance_profile'},
}
url = 's3://my-bucket/gems/specs.4.8.gz'
@@ -816,7 +812,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
def test_fetch_s3_no_host
Gem.configuration[:s3_source] = {
- 'my-bucket' => {:id => 'testuser', :secret => 'testpass'}
+ 'my-bucket' => {:id => 'testuser', :secret => 'testpass'},
}
url = 's3://other-bucket/gems/specs.4.8.gz'
@@ -1062,7 +1058,7 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
:SSLCertificate => cert('ssl_cert.pem'),
:SSLPrivateKey => key('ssl_key.pem'),
:SSLVerifyClient => nil,
- :SSLCertName => nil
+ :SSLCertName => nil,
}.merge(config))
server.mount_proc("/yaml") do |req, res|
res.body = "--- true\n"
@@ -1145,4 +1141,4 @@ PeIQQkFng2VVot/WAQbv3ePqWq07g1BBcwIBAg==
def key(filename)
OpenSSL::PKey::RSA.new(File.read(File.join(__dir__, filename)))
end
-end if defined?(OpenSSL::SSL)
+end if Gem::HAVE_OPENSSL
diff --git a/test/rubygems/test_gem_request.rb b/test/rubygems/test_gem_request.rb
index 82b95b47c3..47e5f97074 100644
--- a/test/rubygems/test_gem_request.rb
+++ b/test/rubygems/test_gem_request.rb
@@ -4,7 +4,7 @@ require 'rubygems/request'
require 'ostruct'
require 'base64'
-unless defined?(OpenSSL::SSL)
+unless Gem::HAVE_OPENSSL
warn 'Skipping Gem::Request tests. openssl not found.'
end
@@ -506,4 +506,4 @@ ERROR: Certificate is an invalid CA certificate
@response
end
end
-end if defined?(OpenSSL::SSL)
+end if Gem::HAVE_OPENSSL
diff --git a/test/rubygems/test_gem_request_set_lockfile.rb b/test/rubygems/test_gem_request_set_lockfile.rb
index e7921d6b26..44a47f2e00 100644
--- a/test/rubygems/test_gem_request_set_lockfile.rb
+++ b/test/rubygems/test_gem_request_set_lockfile.rb
@@ -51,7 +51,7 @@ class TestGemRequestSetLockfile < Gem::TestCase
expected = [
'DEPENDENCIES',
' a',
- nil
+ nil,
]
assert_equal expected, out
@@ -78,7 +78,7 @@ class TestGemRequestSetLockfile < Gem::TestCase
expected = [
'DEPENDENCIES',
' a (~> 2.0)',
- nil
+ nil,
]
assert_equal expected, out
@@ -111,7 +111,7 @@ class TestGemRequestSetLockfile < Gem::TestCase
' a (2)',
' b',
' b (2)',
- nil
+ nil,
]
assert_equal expected, out
@@ -139,7 +139,7 @@ class TestGemRequestSetLockfile < Gem::TestCase
'PLATFORMS',
' ruby',
' x86-darwin-8',
- nil
+ nil,
]
assert_equal expected, out
diff --git a/test/rubygems/test_gem_resolver_api_set.rb b/test/rubygems/test_gem_resolver_api_set.rb
index 6942b8587d..4b545b1b9a 100644
--- a/test/rubygems/test_gem_resolver_api_set.rb
+++ b/test/rubygems/test_gem_resolver_api_set.rb
@@ -39,7 +39,7 @@ class TestGemResolverAPISet < Gem::TestCase
{ :name => 'a',
:number => '1',
:platform => 'ruby',
- :dependencies => [], },
+ :dependencies => [] },
]
@fetcher.data["#{@dep_uri}?gems=a"] = Marshal.dump data
@@ -49,7 +49,7 @@ class TestGemResolverAPISet < Gem::TestCase
a_dep = @DR::DependencyRequest.new dep('a'), nil
expected = [
- @DR::APISpecification.new(set, data.first)
+ @DR::APISpecification.new(set, data.first),
]
assert_equal expected, set.find_all(a_dep)
@@ -62,7 +62,7 @@ class TestGemResolverAPISet < Gem::TestCase
{ :name => 'a',
:number => '1',
:platform => 'ruby',
- :dependencies => [], },
+ :dependencies => [] },
]
@fetcher.data["#{@dep_uri}?gems=a"] = Marshal.dump data
@@ -74,7 +74,7 @@ class TestGemResolverAPISet < Gem::TestCase
set.prefetch [a_dep]
expected = [
- @DR::APISpecification.new(set, data.first)
+ @DR::APISpecification.new(set, data.first),
]
assert_equal expected, set.find_all(a_dep)
@@ -114,7 +114,7 @@ class TestGemResolverAPISet < Gem::TestCase
{ :name => 'a',
:number => '1',
:platform => 'ruby',
- :dependencies => [], },
+ :dependencies => [] },
]
@fetcher.data["#{@dep_uri}?gems=a,b"] = Marshal.dump data
@@ -138,7 +138,7 @@ class TestGemResolverAPISet < Gem::TestCase
{ :name => 'a',
:number => '1',
:platform => 'ruby',
- :dependencies => [], },
+ :dependencies => [] },
]
@fetcher.data["#{@dep_uri}?gems=a"] = Marshal.dump data
@@ -163,7 +163,7 @@ class TestGemResolverAPISet < Gem::TestCase
{ :name => 'a',
:number => '1',
:platform => 'ruby',
- :dependencies => [], },
+ :dependencies => [] },
]
@fetcher.data["#{@dep_uri}?gems=a,b"] = Marshal.dump data
@@ -187,7 +187,7 @@ class TestGemResolverAPISet < Gem::TestCase
{ :name => 'a',
:number => '1',
:platform => 'ruby',
- :dependencies => [], },
+ :dependencies => [] },
]
@fetcher.data["#{@dep_uri}?gems=a,b"] = Marshal.dump data
diff --git a/test/rubygems/test_gem_resolver_conflict.rb b/test/rubygems/test_gem_resolver_conflict.rb
index 5a3ed80670..ff5fe9bae3 100644
--- a/test/rubygems/test_gem_resolver_conflict.rb
+++ b/test/rubygems/test_gem_resolver_conflict.rb
@@ -73,7 +73,7 @@ class TestGemResolverConflict < Gem::TestCase
expected = [
'net-ssh (>= 2.0.13), 2.2.2 activated',
- 'rye (= 0.9.8), 0.9.8 activated'
+ 'rye (= 0.9.8), 0.9.8 activated',
]
assert_equal expected, conflict.request_path(child.requester)
diff --git a/test/rubygems/test_gem_resolver_vendor_set.rb b/test/rubygems/test_gem_resolver_vendor_set.rb
index 99e2c5ae5f..d14b877b45 100644
--- a/test/rubygems/test_gem_resolver_vendor_set.rb
+++ b/test/rubygems/test_gem_resolver_vendor_set.rb
@@ -51,7 +51,7 @@ class TestGemResolverVendorSet < Gem::TestCase
source = Gem::Source::Vendor.new directory
expected = [
- Gem::Resolver::VendorSpecification.new(@set, spec, source)
+ Gem::Resolver::VendorSpecification.new(@set, spec, source),
]
assert_equal expected, found
diff --git a/test/rubygems/test_gem_security.rb b/test/rubygems/test_gem_security.rb
index 4d07887d36..92f9e55b21 100644
--- a/test/rubygems/test_gem_security.rb
+++ b/test/rubygems/test_gem_security.rb
@@ -2,7 +2,7 @@
require 'rubygems/test_case'
require 'rubygems/security'
-unless defined?(OpenSSL::SSL)
+unless Gem::HAVE_OPENSSL
warn 'Skipping Gem::Security tests. openssl not found.'
end
@@ -309,4 +309,4 @@ class TestGemSecurity < Gem::TestCase
assert_equal key.to_pem, key_from_file.to_pem
end
-end if defined?(OpenSSL::SSL) && !Gem.java_platform?
+end if Gem::HAVE_OPENSSL && !Gem.java_platform?
diff --git a/test/rubygems/test_gem_security_policy.rb b/test/rubygems/test_gem_security_policy.rb
index 86100d7c74..85e4590655 100644
--- a/test/rubygems/test_gem_security_policy.rb
+++ b/test/rubygems/test_gem_security_policy.rb
@@ -2,7 +2,7 @@
require 'rubygems/test_case'
-unless defined?(OpenSSL::SSL)
+unless Gem::HAVE_OPENSSL
warn 'Skipping Gem::Security::Policy tests. openssl not found.'
end
@@ -532,4 +532,4 @@ class TestGemSecurityPolicy < Gem::TestCase
return digests, signatures
end
-end if defined?(OpenSSL::SSL)
+end if Gem::HAVE_OPENSSL
diff --git a/test/rubygems/test_gem_security_signer.rb b/test/rubygems/test_gem_security_signer.rb
index 050748a8b5..8a09f97f26 100644
--- a/test/rubygems/test_gem_security_signer.rb
+++ b/test/rubygems/test_gem_security_signer.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'rubygems/test_case'
-unless defined?(OpenSSL::SSL)
+unless Gem::HAVE_OPENSSL
warn 'Skipping Gem::Security::Signer tests. openssl not found.'
end
@@ -214,4 +214,4 @@ toqvglr0kdbknSRRjBVLK6tsgr07aLT9gNP7mTW2PA==
signer.sign 'hello'
end
end
-end if defined?(OpenSSL::SSL)
+end if Gem::HAVE_OPENSSL
diff --git a/test/rubygems/test_gem_security_trust_dir.rb b/test/rubygems/test_gem_security_trust_dir.rb
index 64871f7bd3..201de9d36b 100644
--- a/test/rubygems/test_gem_security_trust_dir.rb
+++ b/test/rubygems/test_gem_security_trust_dir.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
require 'rubygems/test_case'
-unless defined?(OpenSSL::SSL)
+unless Gem::HAVE_OPENSSL
warn 'Skipping Gem::Security::TrustDir tests. openssl not found.'
end
@@ -95,4 +95,4 @@ class TestGemSecurityTrustDir < Gem::TestCase
assert_equal mask, File.stat(@dest_dir).mode unless win_platform?
end
-end if defined?(OpenSSL::SSL)
+end if Gem::HAVE_OPENSSL
diff --git a/test/rubygems/test_gem_source.rb b/test/rubygems/test_gem_source.rb
index 599d490d95..09c510f096 100644
--- a/test/rubygems/test_gem_source.rb
+++ b/test/rubygems/test_gem_source.rb
@@ -240,6 +240,11 @@ class TestGemSource < Gem::TestCase
refute rubygems_source.typo_squatting?("rubysertgems.org")
end
+ def test_typo_squatting_false_positive
+ rubygems_source = Gem::Source.new("https://rubygems.org")
+ refute rubygems_source.typo_squatting?("rubygems.org")
+ end
+
def test_typo_squatting_custom_distance_threshold
rubygems_source = Gem::Source.new("https://rubgems.org")
distance_threshold = 5
diff --git a/test/rubygems/test_gem_source_subpath_problem.rb b/test/rubygems/test_gem_source_subpath_problem.rb
new file mode 100644
index 0000000000..eb92b7d404
--- /dev/null
+++ b/test/rubygems/test_gem_source_subpath_problem.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+require 'rubygems/test_case'
+require 'rubygems/source'
+
+class TestGemSourceSubpathProblem < Gem::TestCase
+ def tuple(*args)
+ Gem::NameTuple.new(*args)
+ end
+
+ def setup
+ super
+
+ @gem_repo = "http://gems.example.com/private"
+
+ spec_fetcher
+
+ @source = Gem::Source.new(@gem_repo)
+
+ util_make_gems
+ end
+
+ def test_dependency_resolver_set
+ response = Net::HTTPResponse.new '1.1', 200, 'OK'
+ response.uri = URI('http://example') if response.respond_to? :uri
+
+ @fetcher.data["#{@gem_repo}/api/v1/dependencies"] = response
+
+ set = @source.dependency_resolver_set
+
+ assert_kind_of Gem::Resolver::APISet, set
+ end
+
+ def test_fetch_spec
+ @fetcher.data["#{@gem_repo}/#{Gem::MARSHAL_SPEC_DIR}#{@a1.spec_name}.rz"] = Zlib::Deflate.deflate(Marshal.dump(@a1))
+
+ spec = @source.fetch_spec tuple('a', Gem::Version.new(1), 'ruby')
+ assert_equal @a1.full_name, spec.full_name
+ end
+
+ def test_load_specs
+ @fetcher.data["#{@gem_repo}/latest_specs.#{Gem.marshal_version}.gz"] = util_gzip(Marshal.dump([
+ Gem::NameTuple.new(@a1.name, @a1.version, 'ruby'),
+ Gem::NameTuple.new(@b2.name, @b2.version, 'ruby'),
+ ]))
+
+ released = @source.load_specs(:latest).map {|spec| spec.full_name }
+ assert_equal %W[a-1 b-2], released
+ end
+end
diff --git a/test/rubygems/test_gem_specification.rb b/test/rubygems/test_gem_specification.rb
index f635be859b..76072184bd 100644
--- a/test/rubygems/test_gem_specification.rb
+++ b/test/rubygems/test_gem_specification.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require 'benchmark'
require 'rubygems/test_case'
+require 'date'
require 'pathname'
require 'stringio'
require 'rubygems/ext'
@@ -1999,7 +2000,7 @@ dependencies: []
test_cases = {
'i386-mswin32' => 'a-1-x86-mswin32-60',
'i386-mswin32_80' => 'a-1-x86-mswin32-80',
- 'i386-mingw32' => 'a-1-x86-mingw32'
+ 'i386-mingw32' => 'a-1-x86-mingw32',
}
test_cases.each do |arch, expected|
@@ -3035,7 +3036,7 @@ Please report a bug if this causes problems.
specification.define_singleton_method(:find_all_by_name) do |dep_name|
[
specification.new {|s| s.name = "z", s.version = Gem::Version.new("1") },
- specification.new {|s| s.name = "z", s.version = Gem::Version.new("2") }
+ specification.new {|s| s.name = "z", s.version = Gem::Version.new("2") },
]
end
@@ -3578,7 +3579,7 @@ Did you mean 'Ruby'?
"one" => "two",
"home" => "three",
"homepage_uri" => "https://example.com/user/repo",
- "funding_uri" => "https://example.com/donate"
+ "funding_uri" => "https://example.com/donate",
}
end
@@ -3838,7 +3839,7 @@ end
default_gem_spec = new_default_spec("default", "2.0.0.0",
nil, "default/gem.rb")
- spec_path = File.join(@default_spec_dir, default_gem_spec.spec_name)
+ spec_path = File.join(@gemhome, "specifications", "default", default_gem_spec.spec_name)
write_file(spec_path) do |file|
file.print(default_gem_spec.to_ruby)
end
diff --git a/test/rubygems/test_gem_uninstaller.rb b/test/rubygems/test_gem_uninstaller.rb
index 1c084c4c5d..1ca4991d1e 100644
--- a/test/rubygems/test_gem_uninstaller.rb
+++ b/test/rubygems/test_gem_uninstaller.rb
@@ -177,7 +177,7 @@ class TestGemUninstaller < Gem::InstallerTestCase
@spec.files += %w[lib/rubygems_plugin.rb]
- Gem::Installer.at(Gem::Package.build(@spec)).install
+ Gem::Installer.at(Gem::Package.build(@spec), :force => true).install
plugin_path = File.join Gem.plugindir, 'a_plugin.rb'
assert File.exist?(plugin_path), 'plugin not written'
@@ -194,7 +194,7 @@ class TestGemUninstaller < Gem::InstallerTestCase
@spec.files += %w[lib/rubygems_plugin.rb]
- Gem::Installer.at(Gem::Package.build(@spec)).install
+ Gem::Installer.at(Gem::Package.build(@spec), :force => true).install
plugin_path = File.join Gem.plugindir, 'a_plugin.rb'
assert File.exist?(plugin_path), 'plugin not written'
@@ -212,7 +212,7 @@ class TestGemUninstaller < Gem::InstallerTestCase
@spec.files += %w[lib/rubygems_plugin.rb]
- Gem::Installer.at(Gem::Package.build(@spec)).install
+ Gem::Installer.at(Gem::Package.build(@spec), :force => true).install
plugin_path = File.join Gem.plugindir, 'a_plugin.rb'
assert File.exist?(plugin_path), 'plugin not written'
@@ -314,7 +314,7 @@ create_makefile '#{@spec.name}'
use_ui @ui do
path = Gem::Package.build @spec
- installer = Gem::Installer.at path
+ installer = Gem::Installer.at path, :force => true
installer.install
end
@@ -633,19 +633,19 @@ create_makefile '#{@spec.name}'
plugin_path = File.join Gem.plugindir, 'a_plugin.rb'
@spec.version = '1'
- Gem::Installer.at(Gem::Package.build(@spec)).install
+ Gem::Installer.at(Gem::Package.build(@spec), :force => true).install
refute File.exist?(plugin_path), 'version without plugin installed, but plugin written'
@spec.files += %w[lib/rubygems_plugin.rb]
@spec.version = '2'
- Gem::Installer.at(Gem::Package.build(@spec)).install
+ Gem::Installer.at(Gem::Package.build(@spec), :force => true).install
assert File.exist?(plugin_path), 'version with plugin installed, but plugin not written'
assert_match %r{\Arequire.*a-2/lib/rubygems_plugin\.rb}, File.read(plugin_path), 'written plugin has incorrect content'
@spec.version = '3'
- Gem::Installer.at(Gem::Package.build(@spec)).install
+ Gem::Installer.at(Gem::Package.build(@spec), :force => true).install
assert File.exist?(plugin_path), 'version with plugin installed, but plugin removed'
assert_match %r{\Arequire.*a-3/lib/rubygems_plugin\.rb}, File.read(plugin_path), 'old version installed, but plugin updated'
diff --git a/test/rubygems/test_gem_validator.rb b/test/rubygems/test_gem_validator.rb
index d4159d59e2..5158543fa9 100644
--- a/test/rubygems/test_gem_validator.rb
+++ b/test/rubygems/test_gem_validator.rb
@@ -26,7 +26,7 @@ class TestGemValidator < Gem::TestCase
@spec.file_name => [
Gem::Validator::ErrorData.new('lib/b.rb', 'Missing file'),
Gem::Validator::ErrorData.new('lib/c.rb', 'Extra file'),
- ]
+ ],
}
assert_equal expected, alien
diff --git a/test/rubygems/test_gem_version_option.rb b/test/rubygems/test_gem_version_option.rb
index 396fc6277c..49a8513dbe 100644
--- a/test/rubygems/test_gem_version_option.rb
+++ b/test/rubygems/test_gem_version_option.rb
@@ -56,7 +56,7 @@ class TestGemVersionOption < Gem::TestCase
@cmd.handle_options %w[--platform ruby]
expected = [
- Gem::Platform::RUBY
+ Gem::Platform::RUBY,
]
assert_equal expected, Gem.platforms
diff --git a/test/rubygems/test_require.rb b/test/rubygems/test_require.rb
index 2b6620cc65..8c7d06edc3 100644
--- a/test/rubygems/test_require.rb
+++ b/test/rubygems/test_require.rb
@@ -120,7 +120,7 @@ class TestGemRequire < Gem::TestCase
c1 = new_default_spec "c", "1", nil, "c/c.rb"
c2 = new_default_spec "c", "2", nil, "c/c.rb"
- install_default_specs c1, c2, b1, a1
+ install_default_gems c1, c2, b1, a1
dir = Dir.mktmpdir("test_require", @tempdir)
dash_i_arg = File.join dir, 'lib'
@@ -405,8 +405,8 @@ class TestGemRequire < Gem::TestCase
# Remove an old default gem version directly from disk as if someone ran
# gem cleanup.
- FileUtils.rm_rf(File.join @default_dir, "#{b1.full_name}")
- FileUtils.rm_rf(File.join @default_spec_dir, "#{b1.full_name}.gemspec")
+ FileUtils.rm_rf(File.join @gemhome, "#{b1.full_name}")
+ FileUtils.rm_rf(File.join @gemhome, "specifications", "default", "#{b1.full_name}.gemspec")
# Require gems that have not been removed.
assert_require 'a/b'
@@ -433,7 +433,7 @@ class TestGemRequire < Gem::TestCase
def test_default_gem_only
default_gem_spec = new_default_spec("default", "2.0.0.0",
nil, "default/gem.rb")
- install_default_specs(default_gem_spec)
+ install_default_gems(default_gem_spec)
assert_require "default/gem"
assert_equal %w[default-2.0.0.0], loaded_spec_names
end
@@ -441,7 +441,7 @@ class TestGemRequire < Gem::TestCase
def test_default_gem_require_activates_just_once
default_gem_spec = new_default_spec("default", "2.0.0.0",
nil, "default/gem.rb")
- install_default_specs(default_gem_spec)
+ install_default_gems(default_gem_spec)
assert_require "default/gem"
@@ -506,7 +506,7 @@ class TestGemRequire < Gem::TestCase
def test_default_gem_and_normal_gem
default_gem_spec = new_default_spec("default", "2.0.0.0",
nil, "default/gem.rb")
- install_default_specs(default_gem_spec)
+ install_default_gems(default_gem_spec)
normal_gem_spec = util_spec("default", "3.0", nil,
"lib/default/gem.rb")
install_specs(normal_gem_spec)
@@ -544,11 +544,11 @@ class TestGemRequire < Gem::TestCase
def test_default_gem_prerelease
default_gem_spec = new_default_spec("default", "2.0.0",
nil, "default/gem.rb")
- install_default_specs(default_gem_spec)
+ install_default_gems(default_gem_spec)
normal_gem_higher_prerelease_spec = util_spec("default", "3.0.0.rc2", nil,
"lib/default/gem.rb")
- install_default_specs(normal_gem_higher_prerelease_spec)
+ install_default_gems(normal_gem_higher_prerelease_spec)
assert_require "default/gem"
assert_equal %w[default-3.0.0.rc2], loaded_spec_names
@@ -586,7 +586,7 @@ class TestGemRequire < Gem::TestCase
def test_require_when_gem_defined
default_gem_spec = new_default_spec("default", "2.0.0.0",
nil, "default/gem.rb")
- install_default_specs(default_gem_spec)
+ install_default_gems(default_gem_spec)
c = Class.new do
def self.gem(*args)
raise "received #gem with #{args.inspect}"
@@ -677,6 +677,47 @@ class TestGemRequire < Gem::TestCase
end
end
end
+
+ def test_no_crash_when_overriding_warn_with_warning_module
+ skip "https://github.com/oracle/truffleruby/issues/2109" if RUBY_ENGINE == "truffleruby"
+
+ Dir.mktmpdir("warn_test") do |dir|
+ File.write(dir + "/main.rb", "module Warning; def warn(str); super; end; end; warn 'Foo Bar'")
+ _, err = capture_subprocess_io do
+ system(*ruby_with_rubygems_in_load_path, "-w", "--disable=gems", "-C", dir, "main.rb")
+ end
+ assert_match(/Foo Bar\n$/, err)
+ _, err = capture_subprocess_io do
+ system(*ruby_with_rubygems_in_load_path, "-w", "--enable=gems", "-C", dir, "main.rb")
+ end
+ assert_match(/Foo Bar\n$/, err)
+ end
+ end
+
+ def test_expected_backtrace_location_when_inheriting_from_basic_object_and_including_kernel
+ Dir.mktmpdir("warn_test") do |dir|
+ File.write(dir + "/main.rb", "\nrequire 'sub'\n")
+ File.write(dir + "/sub.rb", <<-'RUBY')
+ require 'rubygems'
+ class C < BasicObject
+ include ::Kernel
+ def deprecated
+ warn "This is a deprecated method", uplevel: 2
+ end
+ end
+ C.new.deprecated
+ RUBY
+
+ _, err = capture_subprocess_io do
+ system(*ruby_with_rubygems_in_load_path, "-w", "--disable=gems", "-C", dir, "-I", dir, "main.rb")
+ end
+ assert_match(/main\.rb:2: warning: This is a deprecated method$/, err)
+ _, err = capture_subprocess_io do
+ system(*ruby_with_rubygems_in_load_path, "-w", "--enable=gems", "-C", dir, "-I", dir, "main.rb")
+ end
+ assert_match(/main\.rb:2: warning: This is a deprecated method$/, err)
+ end
+ end
end
private