Sync RubyGems and Bundler with upstream

This commit is contained in:
Hiroshi SHIBATA 2021-07-07 14:07:29 +09:00
parent 6e2240a2f9
commit c082c6eb7c
Notes: git 2021-07-07 15:32:14 +09:00
52 changed files with 477 additions and 466 deletions

View File

@ -37,7 +37,7 @@ module Bundler
environment_preserver = EnvironmentPreserver.from_env environment_preserver = EnvironmentPreserver.from_env
ORIGINAL_ENV = environment_preserver.restore ORIGINAL_ENV = environment_preserver.restore
environment_preserver.replace_with_backup environment_preserver.replace_with_backup
SUDO_MUTEX = Mutex.new SUDO_MUTEX = Thread::Mutex.new
autoload :Definition, File.expand_path("bundler/definition", __dir__) autoload :Definition, File.expand_path("bundler/definition", __dir__)
autoload :Dependency, File.expand_path("bundler/dependency", __dir__) autoload :Dependency, File.expand_path("bundler/dependency", __dir__)

View File

@ -100,8 +100,11 @@ module Bundler
files_not_readable_or_writable = [] files_not_readable_or_writable = []
files_not_rw_and_owned_by_different_user = [] files_not_rw_and_owned_by_different_user = []
files_not_owned_by_current_user_but_still_rw = [] files_not_owned_by_current_user_but_still_rw = []
broken_symlinks = []
Find.find(Bundler.bundle_path.to_s).each do |f| Find.find(Bundler.bundle_path.to_s).each do |f|
if !File.writable?(f) || !File.readable?(f) if !File.exist?(f)
broken_symlinks << f
elsif !File.writable?(f) || !File.readable?(f)
if File.stat(f).uid != Process.uid if File.stat(f).uid != Process.uid
files_not_rw_and_owned_by_different_user << f files_not_rw_and_owned_by_different_user << f
else else
@ -113,6 +116,13 @@ module Bundler
end end
ok = true ok = true
if broken_symlinks.any?
Bundler.ui.warn "Broken links exist in the Bundler home. Please report them to the offending gem's upstream repo. These files are:\n - #{broken_symlinks.join("\n - ")}"
ok = false
end
if files_not_owned_by_current_user_but_still_rw.any? if files_not_owned_by_current_user_but_still_rw.any?
Bundler.ui.warn "Files exist in the Bundler home that are owned by another " \ Bundler.ui.warn "Files exist in the Bundler home that are owned by another " \
"user, but are still readable/writable. These files are:\n - #{files_not_owned_by_current_user_but_still_rw.join("\n - ")}" "user, but are still readable/writable. These files are:\n - #{files_not_owned_by_current_user_but_still_rw.join("\n - ")}"

View File

@ -104,7 +104,7 @@ module Bundler
private private
def warn_if_root def warn_if_root
return if Bundler.settings[:silence_root_warning] || Bundler::WINDOWS || !Process.uid.zero? return if Bundler.settings[:silence_root_warning] || Gem.win_platform? || !Process.uid.zero?
Bundler.ui.warn "Don't run Bundler as root. Bundler can ask for sudo " \ Bundler.ui.warn "Don't run Bundler as root. Bundler can ask for sudo " \
"if it is needed, and installing your bundle as root will break this " \ "if it is needed, and installing your bundle as root will break this " \
"application for all non-root users on this machine.", :wrap => true "application for all non-root users on this machine.", :wrap => true

View File

@ -5,7 +5,7 @@ require "set"
module Bundler module Bundler
class CompactIndexClient class CompactIndexClient
DEBUG_MUTEX = Mutex.new DEBUG_MUTEX = Thread::Mutex.new
def self.debug def self.debug
return unless ENV["DEBUG_COMPACT_INDEX"] return unless ENV["DEBUG_COMPACT_INDEX"]
DEBUG_MUTEX.synchronize { warn("[#{self}] #{yield}") } DEBUG_MUTEX.synchronize { warn("[#{self}] #{yield}") }
@ -25,7 +25,7 @@ module Bundler
@endpoints = Set.new @endpoints = Set.new
@info_checksums_by_name = {} @info_checksums_by_name = {}
@parsed_checksums = false @parsed_checksums = false
@mutex = Mutex.new @mutex = Thread::Mutex.new
end end
def execution_mode=(block) def execution_mode=(block)

View File

@ -65,19 +65,19 @@ module Bundler
end end
def mswin? def mswin?
Bundler::WINDOWS Gem.win_platform?
end end
def mswin64? def mswin64?
Bundler::WINDOWS && Bundler.local_platform != Gem::Platform::RUBY && Bundler.local_platform.os == "mswin64" && Bundler.local_platform.cpu == "x64" Gem.win_platform? && Bundler.local_platform != Gem::Platform::RUBY && Bundler.local_platform.os == "mswin64" && Bundler.local_platform.cpu == "x64"
end end
def mingw? def mingw?
Bundler::WINDOWS && Bundler.local_platform != Gem::Platform::RUBY && Bundler.local_platform.os == "mingw32" && Bundler.local_platform.cpu != "x64" Gem.win_platform? && Bundler.local_platform != Gem::Platform::RUBY && Bundler.local_platform.os == "mingw32" && Bundler.local_platform.cpu != "x64"
end end
def x64_mingw? def x64_mingw?
Bundler::WINDOWS && Bundler.local_platform != Gem::Platform::RUBY && Bundler.local_platform.os == "mingw32" && Bundler.local_platform.cpu == "x64" Gem.win_platform? && Bundler.local_platform != Gem::Platform::RUBY && Bundler.local_platform.os == "mingw32" && Bundler.local_platform.cpu == "x64"
end end
(KNOWN_MINOR_VERSIONS + KNOWN_MAJOR_VERSIONS).each do |version| (KNOWN_MINOR_VERSIONS + KNOWN_MAJOR_VERSIONS).each do |version|

View File

@ -61,10 +61,8 @@ module Bundler
@unlocking_bundler = false @unlocking_bundler = false
@unlocking = unlock @unlocking = unlock
else else
unlock = unlock.dup
@unlocking_bundler = unlock.delete(:bundler) @unlocking_bundler = unlock.delete(:bundler)
unlock.delete_if {|_k, v| Array(v).empty? } @unlocking = unlock.any? {|_k, v| !Array(v).empty? }
@unlocking = !unlock.empty?
end end
@dependencies = dependencies @dependencies = dependencies
@ -111,8 +109,8 @@ module Bundler
@locked_platforms = [] @locked_platforms = []
end end
@locked_gem_sources = @locked_sources.select {|s| s.is_a?(Source::Rubygems) } locked_gem_sources = @locked_sources.select {|s| s.is_a?(Source::Rubygems) }
@multisource_allowed = @locked_gem_sources.any?(&:multiple_remotes?) && Bundler.frozen_bundle? @multisource_allowed = locked_gem_sources.size == 1 && locked_gem_sources.first.multiple_remotes? && Bundler.frozen_bundle?
if @multisource_allowed if @multisource_allowed
unless sources.aggregate_global_source? unless sources.aggregate_global_source?
@ -121,7 +119,7 @@ module Bundler
Bundler::SharedHelpers.major_deprecation 2, msg Bundler::SharedHelpers.major_deprecation 2, msg
end end
@sources.merged_gem_lockfile_sections! @sources.merged_gem_lockfile_sections!(locked_gem_sources.first)
end end
@unlock[:sources] ||= [] @unlock[:sources] ||= []
@ -506,9 +504,6 @@ module Bundler
attr_reader :sources attr_reader :sources
private :sources private :sources
attr_reader :locked_gem_sources
private :locked_gem_sources
def nothing_changed? def nothing_changed?
!@source_changes && !@dependency_changes && !@new_platform && !@path_changes && !@local_changes && !@locked_specs_incomplete_for_platform !@source_changes && !@dependency_changes && !@new_platform && !@path_changes && !@local_changes && !@locked_specs_incomplete_for_platform
end end
@ -636,35 +631,11 @@ module Bundler
end end
end end
def converge_rubygems_sources
return false unless multisource_allowed?
return false if locked_gem_sources.empty?
# Get the RubyGems remotes from the Gemfile
actual_remotes = sources.rubygems_remotes
return false if actual_remotes.empty?
changes = false
# If there is a RubyGems source in both
locked_gem_sources.each do |locked_gem_source|
# Merge the remotes from the Gemfile into the Gemfile.lock
changes |= locked_gem_source.replace_remotes(actual_remotes, Bundler.settings[:allow_deployment_source_credential_changes])
end
changes
end
def converge_sources def converge_sources
changes = false
changes |= converge_rubygems_sources
# Replace the sources from the Gemfile with the sources from the Gemfile.lock, # Replace the sources from the Gemfile with the sources from the Gemfile.lock,
# if they exist in the Gemfile.lock and are `==`. If you can't find an equivalent # if they exist in the Gemfile.lock and are `==`. If you can't find an equivalent
# source in the Gemfile.lock, use the one from the Gemfile. # source in the Gemfile.lock, use the one from the Gemfile.
changes |= sources.replace_sources!(@locked_sources) changes = sources.replace_sources!(@locked_sources)
sources.all_sources.each do |source| sources.all_sources.each do |source|
# If the source is unlockable and the current command allows an unlock of # If the source is unlockable and the current command allows an unlock of
@ -913,14 +884,13 @@ module Bundler
end end
def additional_base_requirements_for_resolve def additional_base_requirements_for_resolve
return [] unless @locked_gems return [] unless @locked_gems && unlocking? && !sources.expired_sources?(@locked_gems.sources)
dependencies_by_name = dependencies.inject({}) {|memo, dep| memo.update(dep.name => dep) } dependencies_by_name = dependencies.inject({}) {|memo, dep| memo.update(dep.name => dep) }
@locked_gems.specs.reduce({}) do |requirements, locked_spec| @locked_gems.specs.reduce({}) do |requirements, locked_spec|
name = locked_spec.name name = locked_spec.name
dependency = dependencies_by_name[name] dependency = dependencies_by_name[name]
next requirements unless dependency
next requirements if @locked_gems.dependencies[name] != dependency next requirements if @locked_gems.dependencies[name] != dependency
next requirements if dependency.source.is_a?(Source::Path) next requirements if dependency && dependency.source.is_a?(Source::Path)
dep = Gem::Dependency.new(name, ">= #{locked_spec.version}") dep = Gem::Dependency.new(name, ">= #{locked_spec.version}")
requirements[name] = DepProxy.get_proxy(dep, locked_spec.platform) requirements[name] = DepProxy.get_proxy(dep, locked_spec.platform)
requirements requirements

View File

@ -24,9 +24,6 @@ module Bundler
def initialize def initialize
@source = nil @source = nil
@sources = SourceList.new @sources = SourceList.new
@global_rubygems_sources = []
@git_sources = {} @git_sources = {}
@dependencies = [] @dependencies = []
@groups = [] @groups = []
@ -48,7 +45,6 @@ module Bundler
@gemfiles << expanded_gemfile_path @gemfiles << expanded_gemfile_path
contents ||= Bundler.read_file(@gemfile.to_s) contents ||= Bundler.read_file(@gemfile.to_s)
instance_eval(contents.dup.tap{|x| x.untaint if RUBY_VERSION < "2.7" }, gemfile.to_s, 1) instance_eval(contents.dup.tap{|x| x.untaint if RUBY_VERSION < "2.7" }, gemfile.to_s, 1)
check_primary_source_safety
rescue Exception => e # rubocop:disable Lint/RescueException rescue Exception => e # rubocop:disable Lint/RescueException
message = "There was an error " \ message = "There was an error " \
"#{e.is_a?(GemfileEvalError) ? "evaluating" : "parsing"} " \ "#{e.is_a?(GemfileEvalError) ? "evaluating" : "parsing"} " \
@ -168,7 +164,7 @@ module Bundler
elsif block_given? elsif block_given?
with_source(@sources.add_rubygems_source("remotes" => source), &blk) with_source(@sources.add_rubygems_source("remotes" => source), &blk)
else else
@global_rubygems_sources << source @sources.add_global_rubygems_remote(source)
end end
end end
@ -222,6 +218,7 @@ module Bundler
end end
def to_definition(lockfile, unlock) def to_definition(lockfile, unlock)
check_primary_source_safety
Definition.new(lockfile, @dependencies, @sources, unlock, @ruby_version, @optional_groups, @gemfiles) Definition.new(lockfile, @dependencies, @sources, unlock, @ruby_version, @optional_groups, @gemfiles)
end end
@ -453,12 +450,7 @@ repo_name ||= user_name
end end
def check_rubygems_source_safety def check_rubygems_source_safety
@sources.global_rubygems_source = @global_rubygems_sources.shift return unless @sources.aggregate_global_source?
return if @global_rubygems_sources.empty?
@global_rubygems_sources.each do |source|
@sources.add_rubygems_remote(source)
end
if Bundler.feature_flag.bundler_3_mode? if Bundler.feature_flag.bundler_3_mode?
msg = "This Gemfile contains multiple primary sources. " \ msg = "This Gemfile contains multiple primary sources. " \

View File

@ -135,7 +135,7 @@ module Bundler
next next
end end
mode = Bundler::WINDOWS ? "wb:UTF-8" : "w" mode = Gem.win_platform? ? "wb:UTF-8" : "w"
require "erb" require "erb"
content = if RUBY_VERSION >= "2.6" content = if RUBY_VERSION >= "2.6"
ERB.new(template, :trim_mode => "-").result(binding) ERB.new(template, :trim_mode => "-").result(binding)
@ -144,7 +144,7 @@ module Bundler
end end
File.write(binstub_path, content, :mode => mode, :perm => 0o777 & ~File.umask) File.write(binstub_path, content, :mode => mode, :perm => 0o777 & ~File.umask)
if Bundler::WINDOWS || options[:all_platforms] if Gem.win_platform? || options[:all_platforms]
prefix = "@ruby -x \"%~f0\" %*\n@exit /b %ERRORLEVEL%\n\n" prefix = "@ruby -x \"%~f0\" %*\n@exit /b %ERRORLEVEL%\n\n"
File.write("#{binstub_path}.cmd", prefix + content, :mode => mode) File.write("#{binstub_path}.cmd", prefix + content, :mode => mode)
end end
@ -182,7 +182,7 @@ module Bundler
executable_path = Pathname(spec.full_gem_path).join(spec.bindir, executable).relative_path_from(bin_path) executable_path = Pathname(spec.full_gem_path).join(spec.bindir, executable).relative_path_from(bin_path)
executable_path = executable_path executable_path = executable_path
mode = Bundler::WINDOWS ? "wb:UTF-8" : "w" mode = Gem.win_platform? ? "wb:UTF-8" : "w"
require "erb" require "erb"
content = if RUBY_VERSION >= "2.6" content = if RUBY_VERSION >= "2.6"
ERB.new(template, :trim_mode => "-").result(binding) ERB.new(template, :trim_mode => "-").result(binding)
@ -191,7 +191,7 @@ module Bundler
end end
File.write("#{bin_path}/#{executable}", content, :mode => mode, :perm => 0o755) File.write("#{bin_path}/#{executable}", content, :mode => mode, :perm => 0o755)
if Bundler::WINDOWS || options[:all_platforms] if Gem.win_platform? || options[:all_platforms]
prefix = "@ruby -x \"%~f0\" %*\n@exit /b %ERRORLEVEL%\n\n" prefix = "@ruby -x \"%~f0\" %*\n@exit /b %ERRORLEVEL%\n\n"
File.write("#{bin_path}/#{executable}.cmd", prefix + content, :mode => mode) File.write("#{bin_path}/#{executable}.cmd", prefix + content, :mode => mode)
end end

View File

@ -1,16 +1,5 @@
# frozen_string_literal: true # frozen_string_literal: true
#--
# Some versions of the Bundler 1.1 RC series introduced corrupted
# lockfiles. There were two major problems:
#
# * multiple copies of the same GIT section appeared in the lockfile
# * when this happened, those sections got multiple copies of gems
# in those sections.
#
# As a result, Bundler 1.1 contains code that fixes the earlier
# corruption. We will remove this fix-up code in Bundler 1.2.
module Bundler module Bundler
class LockfileParser class LockfileParser
attr_reader :sources, :dependencies, :specs, :platforms, :bundler_version, :ruby_version attr_reader :sources, :dependencies, :specs, :platforms, :bundler_version, :ruby_version
@ -124,12 +113,7 @@ module Bundler
@sources << @current_source @sources << @current_source
when GIT when GIT
@current_source = TYPES[@type].from_lock(@opts) @current_source = TYPES[@type].from_lock(@opts)
# Strip out duplicate GIT sections
if @sources.include?(@current_source)
@current_source = @sources.find {|s| s == @current_source }
else
@sources << @current_source @sources << @current_source
end
when GEM when GEM
@opts["remotes"] = Array(@opts.delete("remote")).reverse @opts["remotes"] = Array(@opts.delete("remote")).reverse
@current_source = TYPES[@type].from_lock(@opts) @current_source = TYPES[@type].from_lock(@opts)
@ -212,9 +196,7 @@ module Bundler
@current_spec = LazySpecification.new(name, version, platform) @current_spec = LazySpecification.new(name, version, platform)
@current_spec.source = @current_source @current_spec.source = @current_source
# Avoid introducing multiple copies of the same spec (caused by @specs[@current_spec.identifier] = @current_spec
# duplicate GIT sections)
@specs[@current_spec.identifier] ||= @current_spec
elsif spaces.size == 6 elsif spaces.size == 6
version = version.split(",").map(&:strip) if version version = version.split(",").map(&:strip) if version
dep = Gem::Dependency.new(name, version) dep = Gem::Dependency.new(name, version)

View File

@ -77,7 +77,7 @@ module Bundler
source_list = SourceList.new source_list = SourceList.new
source_list.add_git_source(git_source_options) if git_source_options source_list.add_git_source(git_source_options) if git_source_options
source_list.global_rubygems_source = rubygems_source if rubygems_source source_list.add_global_rubygems_remote(rubygems_source) if rubygems_source
deps = names.map {|name| Dependency.new name, version } deps = names.map {|name| Dependency.new name, version }

View File

@ -134,6 +134,8 @@ module Gem
class Requirement class Requirement
module OrderIndependentComparison module OrderIndependentComparison
def ==(other) def ==(other)
return unless Gem::Requirement === other
if _requirements_sorted? && other._requirements_sorted? if _requirements_sorted? && other._requirements_sorted?
super super
else else

View File

@ -252,19 +252,6 @@ module Bundler
other_remotes.map(&method(:remove_auth)) == @remotes.map(&method(:remove_auth)) other_remotes.map(&method(:remove_auth)) == @remotes.map(&method(:remove_auth))
end end
def replace_remotes(other_remotes, allow_equivalent = false)
return false if other_remotes == @remotes
equivalent = allow_equivalent && equivalent_remotes?(other_remotes)
@remotes = []
other_remotes.reverse_each do |r|
add_remote r.to_s
end
!equivalent
end
def spec_names def spec_names
if @allow_remote && dependency_api_available? if @allow_remote && dependency_api_available?
remote_specs.spec_names remote_specs.spec_names

View File

@ -28,8 +28,9 @@ module Bundler
@merged_gem_lockfile_sections @merged_gem_lockfile_sections
end end
def merged_gem_lockfile_sections! def merged_gem_lockfile_sections!(replacement_source)
@merged_gem_lockfile_sections = true @merged_gem_lockfile_sections = true
@global_rubygems_source = replacement_source
end end
def aggregate_global_source? def aggregate_global_source?
@ -53,18 +54,17 @@ module Bundler
end end
def add_rubygems_source(options = {}) def add_rubygems_source(options = {})
add_source_to_list Source::Rubygems.new(options), @rubygems_sources new_source = Source::Rubygems.new(options)
return @global_rubygems_source if @global_rubygems_source == new_source
add_source_to_list new_source, @rubygems_sources
end end
def add_plugin_source(source, options = {}) def add_plugin_source(source, options = {})
add_source_to_list Plugin.source(source).new(options), @plugin_sources add_source_to_list Plugin.source(source).new(options), @plugin_sources
end end
def global_rubygems_source=(uri) def add_global_rubygems_remote(uri)
@global_rubygems_source ||= rubygems_aggregate_class.new("remotes" => uri, "allow_local" => true)
end
def add_rubygems_remote(uri)
global_rubygems_source.add_remote(uri) global_rubygems_source.add_remote(uri)
global_rubygems_source global_rubygems_source
end end
@ -109,27 +109,26 @@ module Bundler
if merged_gem_lockfile_sections? if merged_gem_lockfile_sections?
[combine_rubygems_sources] [combine_rubygems_sources]
else else
rubygems_sources.sort_by(&:to_s).uniq rubygems_sources.sort_by(&:to_s)
end end
end end
# Returns true if there are changes # Returns true if there are changes
def replace_sources!(replacement_sources) def replace_sources!(replacement_sources)
return true if replacement_sources.empty? return false if replacement_sources.empty?
[path_sources, git_sources, plugin_sources].each do |source_list| @path_sources, @git_sources, @plugin_sources = map_sources(replacement_sources)
source_list.map! do |source|
replacement_sources.find {|s| s == source } || source different_sources?(lock_sources, replacement_sources)
end
end end
replacement_rubygems = merged_gem_lockfile_sections? && # Returns true if there are changes
replacement_sources.detect {|s| s.is_a?(Source::Rubygems) } def expired_sources?(replacement_sources)
@global_rubygems_source = replacement_rubygems if replacement_rubygems return false if replacement_sources.empty?
return true if !equal_sources?(lock_sources, replacement_sources) && !equivalent_sources?(lock_sources, replacement_sources) lock_sources = dup_with_replaced_sources(replacement_sources).lock_sources
false different_sources?(lock_sources, replacement_sources)
end end
def local_only! def local_only!
@ -146,6 +145,24 @@ module Bundler
private private
def dup_with_replaced_sources(replacement_sources)
new_source_list = dup
new_source_list.replace_sources!(replacement_sources)
new_source_list
end
def map_sources(replacement_sources)
[path_sources, git_sources, plugin_sources].map do |sources|
sources.map do |source|
replacement_sources.find {|s| s == source } || source
end
end
end
def different_sources?(lock_sources, replacement_sources)
!equal_sources?(lock_sources, replacement_sources) && !equivalent_sources?(lock_sources, replacement_sources)
end
def rubygems_aggregate_class def rubygems_aggregate_class
Source::Rubygems Source::Rubygems
end end

View File

@ -27,7 +27,7 @@ class Bundler::ConnectionPool
# @!visibility private # @!visibility private
def initialize def initialize
@mutex = Mutex.new @mutex = Thread::Mutex.new
@last_time = Time.now.to_f @last_time = Time.now.to_f
end end

View File

@ -39,8 +39,8 @@ class Bundler::ConnectionPool::TimedStack
@created = 0 @created = 0
@que = [] @que = []
@max = size @max = size
@mutex = Mutex.new @mutex = Thread::Mutex.new
@resource = ConditionVariable.new @resource = Thread::ConditionVariable.new
@shutdown_block = nil @shutdown_block = nil
end end

View File

@ -21,8 +21,8 @@ module Bundler
# @param func [Proc] job to run in inside the worker pool # @param func [Proc] job to run in inside the worker pool
def initialize(size, name, func) def initialize(size, name, func)
@name = name @name = name
@request_queue = Queue.new @request_queue = Thread::Queue.new
@response_queue = Queue.new @response_queue = Thread::Queue.new
@func = func @func = func
@size = size @size = size
@threads = nil @threads = nil

View File

@ -178,7 +178,7 @@ module Gem
@configuration = nil @configuration = nil
@gemdeps = nil @gemdeps = nil
@loaded_specs = {} @loaded_specs = {}
LOADED_SPECS_MUTEX = Mutex.new LOADED_SPECS_MUTEX = Thread::Mutex.new
@path_to_default_spec_map = {} @path_to_default_spec_map = {}
@platforms = [] @platforms = []
@ruby = nil @ruby = nil

View File

@ -11,10 +11,10 @@ module CoreExtensions
IPV4_DELAY_SECONDS = 0.1 IPV4_DELAY_SECONDS = 0.1
def initialize(host, serv, *rest) def initialize(host, serv, *rest)
mutex = Mutex.new mutex = Thread::Mutex.new
addrs = [] addrs = []
threads = [] threads = []
cond_var = ConditionVariable.new cond_var = Thread::ConditionVariable.new
Addrinfo.foreach(host, serv, nil, :STREAM) do |addr| Addrinfo.foreach(host, serv, nil, :STREAM) do |addr|
Thread.report_on_exception = false if defined? Thread.report_on_exception = () Thread.report_on_exception = false if defined? Thread.report_on_exception = ()

View File

@ -1,23 +1,70 @@
# frozen_string_literal: true # frozen_string_literal: true
## ##
# Provides a single method +deprecate+ to be used to declare when # Provides 3 methods for declaring when something is going away.
# something is going away. #
# +deprecate(name, repl, year, month)+:
# Indicate something may be removed on/after a certain date.
#
# +rubygems_deprecate(name, replacement=:none)+:
# Indicate something will be removed in the next major RubyGems version,
# and (optionally) a replacement for it.
#
# +rubygems_deprecate_command+:
# Indicate a RubyGems command (in +lib/rubygems/commands/*.rb+) will be
# removed in the next RubyGems version.
#
# Also provides +skip_during+ for temporarily turning off deprecation warnings.
# This is intended to be used in the test suite, so deprecation warnings
# don't cause test failures if you need to make sure stderr is otherwise empty.
#
#
# Example usage of +deprecate+ and +rubygems_deprecate+:
# #
# class Legacy # class Legacy
# def self.klass_method # def self.some_class_method
# # ... # # ...
# end # end
# #
# def instance_method # def some_instance_method
# # ...
# end
#
# def some_old_method
# # ... # # ...
# end # end
# #
# extend Gem::Deprecate # extend Gem::Deprecate
# deprecate :instance_method, "X.z", 2011, 4 # deprecate :some_instance_method, "X.z", 2011, 4
# rubygems_deprecate :some_old_method, "Modern#some_new_method"
# #
# class << self # class << self
# extend Gem::Deprecate # extend Gem::Deprecate
# deprecate :klass_method, :none, 2011, 4 # deprecate :some_class_method, :none, 2011, 4
# end
# end
#
#
# Example usage of +rubygems_deprecate_command+:
#
# class Gem::Commands::QueryCommand < Gem::Command
# extend Gem::Deprecate
# rubygems_deprecate_command
#
# # ...
# end
#
#
# Example usage of +skip_during+:
#
# class TestSomething < Gem::Testcase
# def test_some_thing_with_deprecations
# Gem::Deprecate.skip_during do
# actual_stdout, actual_stderr = capture_output do
# Gem.something_deprecated
# end
# assert_empty actual_stdout
# assert_equal(expected, actual_stderr)
# end
# end # end
# end # end

View File

@ -51,6 +51,13 @@ module Gem::GemcutterUtilities
end end
end end
##
# The OTP code from the command options or from the user's configuration.
def otp
options[:otp] || ENV["GEM_HOST_OTP_CODE"]
end
## ##
# The host to connect to either from the RUBYGEMS_HOST environment variable # The host to connect to either from the RUBYGEMS_HOST environment variable
# or from the user's configuration # or from the user's configuration
@ -126,7 +133,7 @@ module Gem::GemcutterUtilities
response = rubygems_api_request(:put, "api/v1/api_key", response = rubygems_api_request(:put, "api/v1/api_key",
sign_in_host, scope: scope) do |request| sign_in_host, scope: scope) do |request|
request.basic_auth email, password request.basic_auth email, password
request["OTP"] = options[:otp] if options[:otp] request["OTP"] = otp if otp
request.body = URI.encode_www_form({:api_key => api_key }.merge(update_scope_params)) request.body = URI.encode_www_form({:api_key => api_key }.merge(update_scope_params))
end end
@ -159,7 +166,7 @@ module Gem::GemcutterUtilities
response = rubygems_api_request(:post, "api/v1/api_key", response = rubygems_api_request(:post, "api/v1/api_key",
sign_in_host, scope: scope) do |request| sign_in_host, scope: scope) do |request|
request.basic_auth email, password request.basic_auth email, password
request["OTP"] = options[:otp] if options[:otp] request["OTP"] = otp if otp
request.body = URI.encode_www_form({ name: key_name }.merge(scope_params)) request.body = URI.encode_www_form({ name: key_name }.merge(scope_params))
end end
@ -224,7 +231,7 @@ module Gem::GemcutterUtilities
request_method = Net::HTTP.const_get method.to_s.capitalize request_method = Net::HTTP.const_get method.to_s.capitalize
Gem::RemoteFetcher.fetcher.request(uri, request_method) do |req| Gem::RemoteFetcher.fetcher.request(uri, request_method) do |req|
req["OTP"] = options[:otp] if options[:otp] req["OTP"] = otp if otp
block.call(req) block.call(req)
end end
end end

View File

@ -68,7 +68,7 @@ class Gem::Installer
@path_warning = false @path_warning = false
@install_lock = Mutex.new @install_lock = Thread::Mutex.new
class << self class << self
## ##

View File

@ -4,7 +4,7 @@ require 'rubygems/request'
require 'rubygems/request/connection_pools' require 'rubygems/request/connection_pools'
require 'rubygems/s3_uri_signer' require 'rubygems/s3_uri_signer'
require 'rubygems/uri_formatter' require 'rubygems/uri_formatter'
require 'rubygems/uri_parsing' require 'rubygems/uri_parser'
require 'rubygems/user_interaction' require 'rubygems/user_interaction'
require 'resolv' require 'resolv'
@ -14,15 +14,12 @@ require 'resolv'
class Gem::RemoteFetcher class Gem::RemoteFetcher
include Gem::UserInteraction include Gem::UserInteraction
include Gem::UriParsing
## ##
# A FetchError exception wraps up the various possible IO and HTTP failures # A FetchError exception wraps up the various possible IO and HTTP failures
# that could happen while downloading from the internet. # that could happen while downloading from the internet.
class FetchError < Gem::Exception class FetchError < Gem::Exception
include Gem::UriParsing
## ##
# The URI which was being accessed when the exception happened. # The URI which was being accessed when the exception happened.
@ -31,7 +28,7 @@ class Gem::RemoteFetcher
def initialize(message, uri) def initialize(message, uri)
super message super message
uri = parse_uri(uri) uri = Gem::UriParser.parse_uri(uri)
@original_uri = uri.dup @original_uri = uri.dup
@ -88,7 +85,7 @@ class Gem::RemoteFetcher
@proxy = proxy @proxy = proxy
@pools = {} @pools = {}
@pool_lock = Mutex.new @pool_lock = Thread::Mutex.new
@cert_files = Gem::Request.get_cert_files @cert_files = Gem::Request.get_cert_files
@headers = headers @headers = headers
@ -133,7 +130,7 @@ class Gem::RemoteFetcher
require "fileutils" require "fileutils"
FileUtils.mkdir_p cache_dir rescue nil unless File.exist? cache_dir FileUtils.mkdir_p cache_dir rescue nil unless File.exist? cache_dir
source_uri = parse_uri(source_uri) source_uri = Gem::UriParser.parse_uri(source_uri)
scheme = source_uri.scheme scheme = source_uri.scheme
@ -228,7 +225,7 @@ class Gem::RemoteFetcher
unless location = response['Location'] unless location = response['Location']
raise FetchError.new("redirecting but no redirect location was given", uri) raise FetchError.new("redirecting but no redirect location was given", uri)
end end
location = parse_uri location location = Gem::UriParser.parse_uri location
if https?(uri) && !https?(location) if https?(uri) && !https?(location)
raise FetchError.new("redirecting to non-https resource: #{location}", uri) raise FetchError.new("redirecting to non-https resource: #{location}", uri)
@ -246,7 +243,7 @@ class Gem::RemoteFetcher
# Downloads +uri+ and returns it as a String. # Downloads +uri+ and returns it as a String.
def fetch_path(uri, mtime = nil, head = false) def fetch_path(uri, mtime = nil, head = false)
uri = parse_uri uri uri = Gem::UriParser.parse_uri uri
unless uri.scheme unless uri.scheme
raise ArgumentError, "uri scheme is invalid: #{uri.scheme.inspect}" raise ArgumentError, "uri scheme is invalid: #{uri.scheme.inspect}"

View File

@ -11,7 +11,7 @@ class Gem::Request::ConnectionPools # :nodoc:
@proxy_uri = proxy_uri @proxy_uri = proxy_uri
@cert_files = cert_files @cert_files = cert_files
@pools = {} @pools = {}
@pool_mutex = Mutex.new @pool_mutex = Thread::Mutex.new
end end
def pool_for(uri) def pool_for(uri)

View File

@ -12,7 +12,7 @@ class Gem::Request::HTTPPool # :nodoc:
@http_args = http_args @http_args = http_args
@cert_files = cert_files @cert_files = cert_files
@proxy_uri = proxy_uri @proxy_uri = proxy_uri
@queue = SizedQueue.new 1 @queue = Thread::SizedQueue.new 1
@queue << nil @queue << nil
end end

View File

@ -151,7 +151,7 @@ class Gem::RequestSet
@prerelease = options[:prerelease] @prerelease = options[:prerelease]
requests = [] requests = []
download_queue = Queue.new download_queue = Thread::Queue.new
# Create a thread-safe list of gems to download # Create a thread-safe list of gems to download
sorted_requests.each do |req| sorted_requests.each do |req|

View File

@ -105,7 +105,7 @@ class Gem::Specification < Gem::BasicSpecification
# rubocop:disable Style/MutableConstant # rubocop:disable Style/MutableConstant
LOAD_CACHE = {} # :nodoc: LOAD_CACHE = {} # :nodoc:
# rubocop:enable Style/MutableConstant # rubocop:enable Style/MutableConstant
LOAD_CACHE_MUTEX = Mutex.new LOAD_CACHE_MUTEX = Thread::Mutex.new
private_constant :LOAD_CACHE if defined? private_constant private_constant :LOAD_CACHE if defined? private_constant

View File

@ -5,10 +5,18 @@
# #
class Gem::UriParser class Gem::UriParser
def self.parse_uri(source_uri)
return source_uri unless source_uri.is_a?(String)
new.parse(source_uri)
end
## ##
# Parses the #uri, raising if it's invalid # Parses the #uri, raising if it's invalid
def parse!(uri) def parse!(uri)
require "uri"
raise URI::InvalidURIError unless uri raise URI::InvalidURIError unless uri
# Always escape URI's to deal with potential spaces and such # Always escape URI's to deal with potential spaces and such

View File

@ -1,23 +0,0 @@
# frozen_string_literal: true
require "rubygems/uri_parser"
module Gem::UriParsing
def parse_uri(source_uri)
return source_uri unless source_uri.is_a?(String)
uri_parser.parse(source_uri)
end
private :parse_uri
def uri_parser
require "uri"
Gem::UriParser.new
end
private :uri_parser
end

View File

@ -543,7 +543,7 @@ class Gem::StreamUI
# A progress reporter that behaves nicely with threaded downloading. # A progress reporter that behaves nicely with threaded downloading.
class ThreadedDownloadReporter class ThreadedDownloadReporter
MUTEX = Mutex.new MUTEX = Thread::Mutex.new
## ##
# The current file name being displayed # The current file name being displayed

View File

@ -2,16 +2,6 @@
require "bundler/cli" require "bundler/cli"
using Module.new {
# Some `man` (e.g., on macOS) always highlights the output even to
# non-tty.
refine Spec::Helpers do
def out
super.gsub(/.[\b]/, "")
end
end
} if RUBY_VERSION >= "2.4"
RSpec.describe "bundle executable" do RSpec.describe "bundle executable" do
it "returns non-zero exit status when passed unrecognized options" do it "returns non-zero exit status when passed unrecognized options" do
bundle "--invalid_argument", :raise_on_error => false bundle "--invalid_argument", :raise_on_error => false
@ -42,49 +32,57 @@ RSpec.describe "bundle executable" do
it "aliases e to exec" do it "aliases e to exec" do
bundle "e --help" bundle "e --help"
expect(out).to include("bundle-exec") expect(out_with_macos_man_workaround).to include("bundle-exec")
end end
it "aliases ex to exec" do it "aliases ex to exec" do
bundle "ex --help" bundle "ex --help"
expect(out).to include("bundle-exec") expect(out_with_macos_man_workaround).to include("bundle-exec")
end end
it "aliases exe to exec" do it "aliases exe to exec" do
bundle "exe --help" bundle "exe --help"
expect(out).to include("bundle-exec") expect(out_with_macos_man_workaround).to include("bundle-exec")
end end
it "aliases c to check" do it "aliases c to check" do
bundle "c --help" bundle "c --help"
expect(out).to include("bundle-check") expect(out_with_macos_man_workaround).to include("bundle-check")
end end
it "aliases i to install" do it "aliases i to install" do
bundle "i --help" bundle "i --help"
expect(out).to include("bundle-install") expect(out_with_macos_man_workaround).to include("bundle-install")
end end
it "aliases ls to list" do it "aliases ls to list" do
bundle "ls --help" bundle "ls --help"
expect(out).to include("bundle-list") expect(out_with_macos_man_workaround).to include("bundle-list")
end end
it "aliases package to cache" do it "aliases package to cache" do
bundle "package --help" bundle "package --help"
expect(out).to include("bundle-cache") expect(out_with_macos_man_workaround).to include("bundle-cache")
end end
it "aliases pack to cache" do it "aliases pack to cache" do
bundle "pack --help" bundle "pack --help"
expect(out).to include("bundle-cache") expect(out_with_macos_man_workaround).to include("bundle-cache")
end
private
# Some `man` (e.g., on macOS) always highlights the output even to
# non-tty.
def out_with_macos_man_workaround
out.gsub(/.[\b]/, "")
end end
end end

View File

@ -204,7 +204,7 @@ RSpec.describe Bundler::Definition do
context "eager unlock" do context "eager unlock" do
let(:source_list) do let(:source_list) do
Bundler::SourceList.new.tap do |source_list| Bundler::SourceList.new.tap do |source_list|
source_list.global_rubygems_source = file_uri_for(gem_repo4) source_list.add_global_rubygems_remote(file_uri_for(gem_repo4))
end end
end end

View File

@ -115,15 +115,15 @@ RSpec.describe Bundler::SourceList do
end end
end end
describe "#add_rubygems_remote", :bundler => "< 3" do describe "#add_global_rubygems_remote" do
let!(:returned_source) { source_list.add_rubygems_remote("https://rubygems.org/") } let!(:returned_source) { source_list.add_global_rubygems_remote("https://rubygems.org/") }
it "returns the aggregate rubygems source" do it "returns the aggregate rubygems source" do
expect(returned_source).to be_instance_of(Bundler::Source::Rubygems) expect(returned_source).to be_instance_of(Bundler::Source::Rubygems)
end end
it "adds the provided remote to the beginning of the aggregate source" do it "adds the provided remote to the beginning of the aggregate source" do
source_list.add_rubygems_remote("https://othersource.org") source_list.add_global_rubygems_remote("https://othersource.org")
expect(returned_source.remotes).to eq [ expect(returned_source.remotes).to eq [
Bundler::URI("https://othersource.org/"), Bundler::URI("https://othersource.org/"),
Bundler::URI("https://rubygems.org/"), Bundler::URI("https://rubygems.org/"),
@ -212,22 +212,22 @@ RSpec.describe Bundler::SourceList do
describe "#path_sources" do describe "#path_sources" do
it "returns an empty array when no path sources have been added" do it "returns an empty array when no path sources have been added" do
source_list.add_rubygems_remote("https://rubygems.org") source_list.add_global_rubygems_remote("https://rubygems.org")
source_list.add_git_source("uri" => "git://host/path.git") source_list.add_git_source("uri" => "git://host/path.git")
expect(source_list.path_sources).to be_empty expect(source_list.path_sources).to be_empty
end end
it "returns path sources in the reverse order that they were added" do it "returns path sources in the reverse order that they were added" do
source_list.add_git_source("uri" => "git://third-git.org/path.git") source_list.add_git_source("uri" => "git://third-git.org/path.git")
source_list.add_rubygems_remote("https://fifth-rubygems.org") source_list.add_global_rubygems_remote("https://fifth-rubygems.org")
source_list.add_path_source("path" => "/third/path/to/gem") source_list.add_path_source("path" => "/third/path/to/gem")
source_list.add_rubygems_remote("https://fourth-rubygems.org") source_list.add_global_rubygems_remote("https://fourth-rubygems.org")
source_list.add_path_source("path" => "/second/path/to/gem") source_list.add_path_source("path" => "/second/path/to/gem")
source_list.add_rubygems_remote("https://third-rubygems.org") source_list.add_global_rubygems_remote("https://third-rubygems.org")
source_list.add_git_source("uri" => "git://second-git.org/path.git") source_list.add_git_source("uri" => "git://second-git.org/path.git")
source_list.add_rubygems_remote("https://second-rubygems.org") source_list.add_global_rubygems_remote("https://second-rubygems.org")
source_list.add_path_source("path" => "/first/path/to/gem") source_list.add_path_source("path" => "/first/path/to/gem")
source_list.add_rubygems_remote("https://first-rubygems.org") source_list.add_global_rubygems_remote("https://first-rubygems.org")
source_list.add_git_source("uri" => "git://first-git.org/path.git") source_list.add_git_source("uri" => "git://first-git.org/path.git")
expect(source_list.path_sources).to eq [ expect(source_list.path_sources).to eq [
@ -240,7 +240,7 @@ RSpec.describe Bundler::SourceList do
describe "#git_sources" do describe "#git_sources" do
it "returns an empty array when no git sources have been added" do it "returns an empty array when no git sources have been added" do
source_list.add_rubygems_remote("https://rubygems.org") source_list.add_global_rubygems_remote("https://rubygems.org")
source_list.add_path_source("path" => "/path/to/gem") source_list.add_path_source("path" => "/path/to/gem")
expect(source_list.git_sources).to be_empty expect(source_list.git_sources).to be_empty
@ -248,15 +248,15 @@ RSpec.describe Bundler::SourceList do
it "returns git sources in the reverse order that they were added" do it "returns git sources in the reverse order that they were added" do
source_list.add_git_source("uri" => "git://third-git.org/path.git") source_list.add_git_source("uri" => "git://third-git.org/path.git")
source_list.add_rubygems_remote("https://fifth-rubygems.org") source_list.add_global_rubygems_remote("https://fifth-rubygems.org")
source_list.add_path_source("path" => "/third/path/to/gem") source_list.add_path_source("path" => "/third/path/to/gem")
source_list.add_rubygems_remote("https://fourth-rubygems.org") source_list.add_global_rubygems_remote("https://fourth-rubygems.org")
source_list.add_path_source("path" => "/second/path/to/gem") source_list.add_path_source("path" => "/second/path/to/gem")
source_list.add_rubygems_remote("https://third-rubygems.org") source_list.add_global_rubygems_remote("https://third-rubygems.org")
source_list.add_git_source("uri" => "git://second-git.org/path.git") source_list.add_git_source("uri" => "git://second-git.org/path.git")
source_list.add_rubygems_remote("https://second-rubygems.org") source_list.add_global_rubygems_remote("https://second-rubygems.org")
source_list.add_path_source("path" => "/first/path/to/gem") source_list.add_path_source("path" => "/first/path/to/gem")
source_list.add_rubygems_remote("https://first-rubygems.org") source_list.add_global_rubygems_remote("https://first-rubygems.org")
source_list.add_git_source("uri" => "git://first-git.org/path.git") source_list.add_git_source("uri" => "git://first-git.org/path.git")
expect(source_list.git_sources).to eq [ expect(source_list.git_sources).to eq [
@ -269,7 +269,7 @@ RSpec.describe Bundler::SourceList do
describe "#plugin_sources" do describe "#plugin_sources" do
it "returns an empty array when no plugin sources have been added" do it "returns an empty array when no plugin sources have been added" do
source_list.add_rubygems_remote("https://rubygems.org") source_list.add_global_rubygems_remote("https://rubygems.org")
source_list.add_path_source("path" => "/path/to/gem") source_list.add_path_source("path" => "/path/to/gem")
expect(source_list.plugin_sources).to be_empty expect(source_list.plugin_sources).to be_empty
@ -279,13 +279,13 @@ RSpec.describe Bundler::SourceList do
source_list.add_plugin_source("new_source", "uri" => "https://third-git.org/path.git") source_list.add_plugin_source("new_source", "uri" => "https://third-git.org/path.git")
source_list.add_git_source("https://new-git.org") source_list.add_git_source("https://new-git.org")
source_list.add_path_source("path" => "/third/path/to/gem") source_list.add_path_source("path" => "/third/path/to/gem")
source_list.add_rubygems_remote("https://fourth-rubygems.org") source_list.add_global_rubygems_remote("https://fourth-rubygems.org")
source_list.add_path_source("path" => "/second/path/to/gem") source_list.add_path_source("path" => "/second/path/to/gem")
source_list.add_rubygems_remote("https://third-rubygems.org") source_list.add_global_rubygems_remote("https://third-rubygems.org")
source_list.add_plugin_source("new_source", "uri" => "git://second-git.org/path.git") source_list.add_plugin_source("new_source", "uri" => "git://second-git.org/path.git")
source_list.add_rubygems_remote("https://second-rubygems.org") source_list.add_global_rubygems_remote("https://second-rubygems.org")
source_list.add_path_source("path" => "/first/path/to/gem") source_list.add_path_source("path" => "/first/path/to/gem")
source_list.add_rubygems_remote("https://first-rubygems.org") source_list.add_global_rubygems_remote("https://first-rubygems.org")
source_list.add_plugin_source("new_source", "uri" => "git://first-git.org/path.git") source_list.add_plugin_source("new_source", "uri" => "git://first-git.org/path.git")
expect(source_list.plugin_sources).to eq [ expect(source_list.plugin_sources).to eq [
@ -339,7 +339,7 @@ RSpec.describe Bundler::SourceList do
describe "#get" do describe "#get" do
context "when it includes an equal source" do context "when it includes an equal source" do
let(:rubygems_source) { Bundler::Source::Rubygems.new("remotes" => ["https://rubygems.org"]) } let(:rubygems_source) { Bundler::Source::Rubygems.new("remotes" => ["https://rubygems.org"]) }
before { @equal_source = source_list.add_rubygems_remote("https://rubygems.org") } before { @equal_source = source_list.add_global_rubygems_remote("https://rubygems.org") }
it "returns the equal source" do it "returns the equal source" do
expect(source_list.get(rubygems_source)).to be @equal_source expect(source_list.get(rubygems_source)).to be @equal_source

View File

@ -32,6 +32,8 @@ RSpec.describe "bundle doctor" do
unwritable_file = double("file") unwritable_file = double("file")
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile) allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
allow(Find).to receive(:find).with(Bundler.bundle_path.to_s) { [unwritable_file] } allow(Find).to receive(:find).with(Bundler.bundle_path.to_s) { [unwritable_file] }
allow(File).to receive(:exist?).and_call_original
allow(File).to receive(:exist?).with(unwritable_file).and_return(true)
allow(File).to receive(:stat).with(unwritable_file) { stat } allow(File).to receive(:stat).with(unwritable_file) { stat }
allow(stat).to receive(:uid) { Process.uid } allow(stat).to receive(:uid) { Process.uid }
allow(File).to receive(:writable?).with(unwritable_file) { true } allow(File).to receive(:writable?).with(unwritable_file) { true }
@ -47,7 +49,6 @@ RSpec.describe "bundle doctor" do
doctor = Bundler::CLI::Doctor.new({}) doctor = Bundler::CLI::Doctor.new({})
expect(doctor).to receive(:bundles_for_gem).exactly(2).times.and_return ["/path/to/rack/rack.bundle"] expect(doctor).to receive(:bundles_for_gem).exactly(2).times.and_return ["/path/to/rack/rack.bundle"]
expect(doctor).to receive(:dylibs).exactly(2).times.and_return ["/usr/lib/libSystem.dylib"] expect(doctor).to receive(:dylibs).exactly(2).times.and_return ["/usr/lib/libSystem.dylib"]
allow(File).to receive(:exist?).and_call_original
allow(File).to receive(:exist?).with("/usr/lib/libSystem.dylib").and_return(true) allow(File).to receive(:exist?).with("/usr/lib/libSystem.dylib").and_return(true)
expect { doctor.run }.not_to(raise_error, @stdout.string) expect { doctor.run }.not_to(raise_error, @stdout.string)
expect(@stdout.string).to be_empty expect(@stdout.string).to be_empty
@ -57,7 +58,6 @@ RSpec.describe "bundle doctor" do
doctor = Bundler::CLI::Doctor.new({}) doctor = Bundler::CLI::Doctor.new({})
expect(doctor).to receive(:bundles_for_gem).exactly(2).times.and_return ["/path/to/rack/rack.bundle"] expect(doctor).to receive(:bundles_for_gem).exactly(2).times.and_return ["/path/to/rack/rack.bundle"]
expect(doctor).to receive(:dylibs).exactly(2).times.and_return ["/usr/local/opt/icu4c/lib/libicui18n.57.1.dylib"] expect(doctor).to receive(:dylibs).exactly(2).times.and_return ["/usr/local/opt/icu4c/lib/libicui18n.57.1.dylib"]
allow(File).to receive(:exist?).and_call_original
allow(File).to receive(:exist?).with("/usr/local/opt/icu4c/lib/libicui18n.57.1.dylib").and_return(false) allow(File).to receive(:exist?).with("/usr/local/opt/icu4c/lib/libicui18n.57.1.dylib").and_return(false)
expect { doctor.run }.to raise_error(Bundler::ProductionError, strip_whitespace(<<-E).strip), @stdout.string expect { doctor.run }.to raise_error(Bundler::ProductionError, strip_whitespace(<<-E).strip), @stdout.string
The following gems are missing OS dependencies: The following gems are missing OS dependencies:
@ -67,12 +67,32 @@ RSpec.describe "bundle doctor" do
end end
end end
context "when home contains broken symlinks" do
before(:each) do
@broken_symlink = double("file")
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
allow(Find).to receive(:find).with(Bundler.bundle_path.to_s) { [@broken_symlink] }
allow(File).to receive(:exist?).and_call_original
allow(File).to receive(:exist?).with(@broken_symlink) { false }
end
it "exits with an error if home contains files that are not readable/writable" do
expect { Bundler::CLI::Doctor.new({}).run }.not_to raise_error
expect(@stdout.string).to include(
"Broken links exist in the Bundler home. Please report them to the offending gem's upstream repo. These files are:\n - #{@unwritable_file}"
)
expect(@stdout.string).not_to include("No issues")
end
end
context "when home contains files that are not readable/writable" do context "when home contains files that are not readable/writable" do
before(:each) do before(:each) do
@stat = double("stat") @stat = double("stat")
@unwritable_file = double("file") @unwritable_file = double("file")
allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile) allow(Bundler::SharedHelpers).to receive(:find_gemfile).and_return(bundled_app_gemfile)
allow(Find).to receive(:find).with(Bundler.bundle_path.to_s) { [@unwritable_file] } allow(Find).to receive(:find).with(Bundler.bundle_path.to_s) { [@unwritable_file] }
allow(File).to receive(:exist?).and_call_original
allow(File).to receive(:exist?).with(@unwritable_file) { true }
allow(File).to receive(:stat).with(@unwritable_file) { @stat } allow(File).to receive(:stat).with(@unwritable_file) { @stat }
end end

View File

@ -241,6 +241,94 @@ RSpec.describe "bundle update" do
expect(the_bundle).to include_gems("slim 3.0.9", "slim-rails 3.1.3", "slim_lint 0.16.1") expect(the_bundle).to include_gems("slim 3.0.9", "slim-rails 3.1.3", "slim_lint 0.16.1")
end end
it "does not go to an older version, even if the version upgrade that could cause another gem to downgrade is activated first" do
build_repo4 do
# countries is processed before country_select by the resolver due to having less spec groups (groups of versions with the same dependencies) (2 vs 3)
build_gem "countries", "2.1.4"
build_gem "countries", "3.1.0"
build_gem "countries", "4.0.0" do |s|
s.add_dependency "sixarm_ruby_unaccent", "~> 1.1"
end
build_gem "country_select", "1.2.0"
build_gem "country_select", "2.1.4" do |s|
s.add_dependency "countries", "~> 2.0"
end
build_gem "country_select", "3.1.1" do |s|
s.add_dependency "countries", "~> 2.0"
end
build_gem "country_select", "5.1.0" do |s|
s.add_dependency "countries", "~> 3.0"
end
build_gem "sixarm_ruby_unaccent", "1.1.0"
end
gemfile <<~G
source "#{file_uri_for(gem_repo4)}"
gem "country_select"
gem "countries"
G
lockfile <<~L
GEM
remote: #{file_uri_for(gem_repo4)}/
specs:
countries (3.1.0)
country_select (5.1.0)
countries (~> 3.0)
PLATFORMS
#{specific_local_platform}
DEPENDENCIES
countries
country_select
BUNDLED WITH
#{Bundler::VERSION}
L
previous_lockfile = lockfile
bundle "lock --update"
expect(lockfile).to eq(previous_lockfile)
end
it "does not downgrade indirect dependencies unnecessarily" do
build_repo4 do
build_gem "a" do |s|
s.add_dependency "b"
s.add_dependency "c"
end
build_gem "b"
build_gem "c"
build_gem "c", "2.0"
end
install_gemfile <<-G, :verbose => true
source "#{file_uri_for(gem_repo4)}"
gem "a"
G
expect(the_bundle).to include_gems("a 1.0", "b 1.0", "c 2.0")
update_repo4 do
build_gem "b", "2.0" do |s|
s.add_dependency "c", "< 2"
end
end
bundle "update", :all => true, :verbose => true
expect(the_bundle).to include_gems("a 1.0", "b 1.0", "c 2.0")
end
it "should still downgrade if forced by the Gemfile" do it "should still downgrade if forced by the Gemfile" do
build_repo4 do build_repo4 do
build_gem "a" build_gem "a"

View File

@ -26,6 +26,38 @@ RSpec.describe "bundle install with gemfile that uses eval_gemfile" do
end end
end end
context "eval-ed Gemfile points to an internal gemspec and uses a scoped source that duplicates the main Gemfile global source" do
before do
build_repo2 do
build_gem "rails", "6.1.3.2"
build_gem "zip-zip", "0.3"
end
create_file bundled_app("gems/Gemfile"), <<-G
gemspec :path => "\#{__dir__}/gunks"
source "#{file_uri_for(gem_repo2)}" do
gem "zip-zip"
end
G
end
it "installs and finds gems correctly" do
install_gemfile <<-G
source "#{file_uri_for(gem_repo2)}"
gem "rails"
eval_gemfile File.join(__dir__, "gems/Gemfile")
G
expect(out).to include("Resolving dependencies")
expect(out).to include("Bundle complete")
expect(the_bundle).to include_gem "rails 6.1.3.2"
end
end
context "eval-ed Gemfile has relative-path gems" do context "eval-ed Gemfile has relative-path gems" do
before do before do
build_lib("a", :path => bundled_app("gems/a")) build_lib("a", :path => bundled_app("gems/a"))

View File

@ -1355,4 +1355,74 @@ RSpec.describe "bundle install with gems on multiple sources" do
expect(the_bundle).not_to be_locked expect(the_bundle).not_to be_locked
end end
end end
context "when upgrading a lockfile suffering from dependency confusion" do
before do
build_repo4 do
build_gem "mime-types", "3.0.0"
end
build_repo2 do
build_gem "capybara", "2.5.0" do |s|
s.add_dependency "mime-types", ">= 1.16"
end
build_gem "mime-types", "3.3.1"
end
gemfile <<~G
source "https://gem.repo2"
gem "capybara", "~> 2.5.0"
source "https://gem.repo4" do
gem "mime-types", "~> 3.0"
end
G
lockfile <<-L
GEM
remote: https://gem.repo2/
remote: https://gem.repo4/
specs:
capybara (2.5.0)
mime-types (>= 1.16)
mime-types (3.3.1)
PLATFORMS
#{specific_local_platform}
DEPENDENCIES
capybara (~> 2.5.0)
mime-types (~> 3.0)!
L
end
it "upgrades the lockfile correctly" do
bundle "lock --update", :artifice => "compact_index"
expect(lockfile).to eq <<~L
GEM
remote: https://gem.repo2/
specs:
capybara (2.5.0)
mime-types (>= 1.16)
GEM
remote: https://gem.repo4/
specs:
mime-types (3.0.0)
PLATFORMS
#{specific_local_platform}
DEPENDENCIES
capybara (~> 2.5.0)
mime-types (~> 3.0)!
BUNDLED WITH
#{Bundler::VERSION}
L
end
end
end end

View File

@ -399,7 +399,7 @@ The checksum of /versions does not match the checksum provided by the server! So
api_request_limit = low_api_request_limit_for(gem_repo2) api_request_limit = low_api_request_limit_for(gem_repo2)
install_gemfile <<-G, :artifice => "compact_index_extra_missing", :env => { "BUNDLER_SPEC_API_REQUEST_LIMIT" => api_request_limit.to_s }.merge(env_for_missing_prerelease_default_gem_activation) install_gemfile <<-G, :artifice => "compact_index_extra_missing", :requires => [api_request_limit_hack_file], :env => { "BUNDLER_SPEC_API_REQUEST_LIMIT" => api_request_limit.to_s }.merge(env_for_missing_prerelease_default_gem_activation)
source "#{source_uri}" source "#{source_uri}"
source "#{source_uri}/extra" do source "#{source_uri}/extra" do
gem "back_deps" gem "back_deps"
@ -421,7 +421,7 @@ The checksum of /versions does not match the checksum provided by the server! So
api_request_limit = low_api_request_limit_for(gem_repo4) api_request_limit = low_api_request_limit_for(gem_repo4)
install_gemfile <<-G, :artifice => "compact_index_extra_api_missing", :env => { "BUNDLER_SPEC_API_REQUEST_LIMIT" => api_request_limit.to_s }.merge(env_for_missing_prerelease_default_gem_activation) install_gemfile <<-G, :artifice => "compact_index_extra_api_missing", :requires => [api_request_limit_hack_file], :env => { "BUNDLER_SPEC_API_REQUEST_LIMIT" => api_request_limit.to_s }.merge(env_for_missing_prerelease_default_gem_activation)
source "#{source_uri}" source "#{source_uri}"
source "#{source_uri}/extra" do source "#{source_uri}/extra" do
gem "back_deps" gem "back_deps"

View File

@ -371,7 +371,7 @@ RSpec.describe "gemcutter's dependency API" do
api_request_limit = low_api_request_limit_for(gem_repo2) api_request_limit = low_api_request_limit_for(gem_repo2)
install_gemfile <<-G, :artifice => "endpoint_extra_missing", :env => { "BUNDLER_SPEC_API_REQUEST_LIMIT" => api_request_limit.to_s }.merge(env_for_missing_prerelease_default_gem_activation) install_gemfile <<-G, :artifice => "endpoint_extra_missing", :requires => [api_request_limit_hack_file], :env => { "BUNDLER_SPEC_API_REQUEST_LIMIT" => api_request_limit.to_s }.merge(env_for_missing_prerelease_default_gem_activation)
source "#{source_uri}" source "#{source_uri}"
source "#{source_uri}/extra" source "#{source_uri}/extra"
gem "back_deps" gem "back_deps"
@ -392,7 +392,7 @@ RSpec.describe "gemcutter's dependency API" do
api_request_limit = low_api_request_limit_for(gem_repo2) api_request_limit = low_api_request_limit_for(gem_repo2)
install_gemfile <<-G, :artifice => "endpoint_extra_missing", :env => { "BUNDLER_SPEC_API_REQUEST_LIMIT" => api_request_limit.to_s }.merge(env_for_missing_prerelease_default_gem_activation) install_gemfile <<-G, :artifice => "endpoint_extra_missing", :requires => [api_request_limit_hack_file], :env => { "BUNDLER_SPEC_API_REQUEST_LIMIT" => api_request_limit.to_s }.merge(env_for_missing_prerelease_default_gem_activation)
source "#{source_uri}" source "#{source_uri}"
source "#{source_uri}/extra" do source "#{source_uri}/extra" do
gem "back_deps" gem "back_deps"

View File

@ -181,8 +181,11 @@ RSpec.describe "global gem caching" do
bundle :install, :artifice => "compact_index_no_gem", :dir => bundled_app2 bundle :install, :artifice => "compact_index_no_gem", :dir => bundled_app2
# activesupport is installed and both are in the global cache # activesupport is installed and both are in the global cache
simulate_bundler_version_when_missing_prerelease_default_gem_activation do
expect(the_bundle).not_to include_gems "rack 1.0.0", :dir => bundled_app2 expect(the_bundle).not_to include_gems "rack 1.0.0", :dir => bundled_app2
expect(the_bundle).to include_gems "activesupport 2.3.5", :dir => bundled_app2 expect(the_bundle).to include_gems "activesupport 2.3.5", :dir => bundled_app2
end
expect(source_global_cache("rack-1.0.0.gem")).to exist expect(source_global_cache("rack-1.0.0.gem")).to exist
expect(source_global_cache("activesupport-2.3.5.gem")).to exist expect(source_global_cache("activesupport-2.3.5.gem")).to exist
end end

View File

@ -1178,83 +1178,6 @@ RSpec.describe "the lockfile format" do
G G
end end
# Some versions of the Bundler 1.1 RC series introduced corrupted
# lockfiles. There were two major problems:
#
# * multiple copies of the same GIT section appeared in the lockfile
# * when this happened, those sections got multiple copies of gems
# in those sections.
it "fixes corrupted lockfiles" do
build_git "omg", :path => lib_path("omg")
revision = revision_for(lib_path("omg"))
gemfile <<-G
source "#{file_uri_for(gem_repo2)}/"
gem "omg", :git => "#{lib_path("omg")}", :branch => 'master'
G
bundle "config set --local path vendor"
bundle :install
expect(the_bundle).to include_gems "omg 1.0"
# Create a Gemfile.lock that has duplicate GIT sections
lockfile <<-L
GIT
remote: #{lib_path("omg")}
revision: #{revision}
branch: master
specs:
omg (1.0)
GIT
remote: #{lib_path("omg")}
revision: #{revision}
branch: master
specs:
omg (1.0)
GEM
remote: #{file_uri_for(gem_repo2)}/
specs:
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
omg!
BUNDLED WITH
#{Bundler::VERSION}
L
FileUtils.rm_rf(bundled_app("vendor"))
bundle "install"
expect(the_bundle).to include_gems "omg 1.0"
# Confirm that duplicate specs do not appear
lockfile_should_be(<<-L)
GIT
remote: #{lib_path("omg")}
revision: #{revision}
branch: master
specs:
omg (1.0)
GEM
remote: #{file_uri_for(gem_repo2)}/
specs:
PLATFORMS
#{lockfile_platforms}
DEPENDENCIES
omg!
BUNDLED WITH
#{Bundler::VERSION}
L
end
it "raises a helpful error message when the lockfile is missing deps" do it "raises a helpful error message when the lockfile is missing deps" do
lockfile <<-L lockfile <<-L
GEM GEM
@ -1326,9 +1249,12 @@ RSpec.describe "the lockfile format" do
expect { bundle "update", :all => true }.to change { File.mtime(bundled_app_lock) } expect { bundle "update", :all => true }.to change { File.mtime(bundled_app_lock) }
expect(File.read(bundled_app_lock)).to match("\r\n") expect(File.read(bundled_app_lock)).to match("\r\n")
simulate_bundler_version_when_missing_prerelease_default_gem_activation do
expect(the_bundle).to include_gems "rack 1.2" expect(the_bundle).to include_gems "rack 1.2"
end end
end end
end
context "when nothing changes" do context "when nothing changes" do
it "preserves Gemfile.lock \\n line endings" do it "preserves Gemfile.lock \\n line endings" do

View File

@ -211,133 +211,6 @@ RSpec.describe "real world edgecases", :realworld => true do
expect(err).to be_empty expect(err).to be_empty
end end
it "checks out git repos when the lockfile is corrupted" do
gemfile <<-G
source "https://rubygems.org"
git_source(:github) {|repo| "https://github.com/\#{repo}.git" }
gem 'activerecord', :github => 'carlhuda/rails-bundler-test', :branch => 'master'
gem 'activesupport', :github => 'carlhuda/rails-bundler-test', :branch => 'master'
gem 'actionpack', :github => 'carlhuda/rails-bundler-test', :branch => 'master'
G
lockfile <<-L
GIT
remote: https://github.com/carlhuda/rails-bundler-test.git
revision: 369e28a87419565f1940815219ea9200474589d4
branch: master
specs:
actionpack (3.2.2)
activemodel (= 3.2.2)
activesupport (= 3.2.2)
builder (~> 3.0.0)
erubis (~> 2.7.0)
journey (~> 1.0.1)
rack (~> 1.4.0)
rack-cache (~> 1.2)
rack-test (~> 0.6.1)
sprockets (~> 2.1.2)
activemodel (3.2.2)
activesupport (= 3.2.2)
builder (~> 3.0.0)
activerecord (3.2.2)
activemodel (= 3.2.2)
activesupport (= 3.2.2)
arel (~> 3.0.2)
tzinfo (~> 0.3.29)
activesupport (3.2.2)
i18n (~> 0.6)
multi_json (~> 1.0)
GIT
remote: https://github.com/carlhuda/rails-bundler-test.git
revision: 369e28a87419565f1940815219ea9200474589d4
branch: master
specs:
actionpack (3.2.2)
activemodel (= 3.2.2)
activesupport (= 3.2.2)
builder (~> 3.0.0)
erubis (~> 2.7.0)
journey (~> 1.0.1)
rack (~> 1.4.0)
rack-cache (~> 1.2)
rack-test (~> 0.6.1)
sprockets (~> 2.1.2)
activemodel (3.2.2)
activesupport (= 3.2.2)
builder (~> 3.0.0)
activerecord (3.2.2)
activemodel (= 3.2.2)
activesupport (= 3.2.2)
arel (~> 3.0.2)
tzinfo (~> 0.3.29)
activesupport (3.2.2)
i18n (~> 0.6)
multi_json (~> 1.0)
GIT
remote: https://github.com/carlhuda/rails-bundler-test.git
revision: 369e28a87419565f1940815219ea9200474589d4
branch: master
specs:
actionpack (3.2.2)
activemodel (= 3.2.2)
activesupport (= 3.2.2)
builder (~> 3.0.0)
erubis (~> 2.7.0)
journey (~> 1.0.1)
rack (~> 1.4.0)
rack-cache (~> 1.2)
rack-test (~> 0.6.1)
sprockets (~> 2.1.2)
activemodel (3.2.2)
activesupport (= 3.2.2)
builder (~> 3.0.0)
activerecord (3.2.2)
activemodel (= 3.2.2)
activesupport (= 3.2.2)
arel (~> 3.0.2)
tzinfo (~> 0.3.29)
activesupport (3.2.2)
i18n (~> 0.6)
multi_json (~> 1.0)
GEM
remote: https://rubygems.org/
specs:
arel (3.0.2)
builder (3.0.0)
erubis (2.7.0)
hike (1.2.1)
i18n (0.6.0)
journey (1.0.3)
multi_json (1.1.0)
rack (1.4.1)
rack-cache (1.2)
rack (>= 0.4)
rack-test (0.6.1)
rack (>= 1.0)
sprockets (2.1.2)
hike (~> 1.2)
rack (~> 1.0)
tilt (~> 1.1, != 1.3.0)
tilt (1.3.3)
tzinfo (0.3.32)
PLATFORMS
ruby
DEPENDENCIES
actionpack!
activerecord!
activesupport!
L
bundle :lock
expect(err).to be_empty
end
it "outputs a helpful error message when gems have invalid gemspecs" do it "outputs a helpful error message when gems have invalid gemspecs" do
install_gemfile <<-G, :standalone => true, :raise_on_error => false install_gemfile <<-G, :standalone => true, :raise_on_error => false
source 'https://rubygems.org' source 'https://rubygems.org'
@ -349,7 +222,7 @@ RSpec.describe "real world edgecases", :realworld => true do
end end
it "doesn't hang on big gemfile" do it "doesn't hang on big gemfile" do
skip "Only for ruby 2.7.3" if RUBY_VERSION != "2.7.3" skip "Only for ruby 2.7.3" if RUBY_VERSION != "2.7.3" || RUBY_PLATFORM =~ /darwin/
gemfile <<~G gemfile <<~G
# frozen_string_literal: true # frozen_string_literal: true
@ -461,7 +334,7 @@ RSpec.describe "real world edgecases", :realworld => true do
end end
it "doesn't hang on tricky gemfile" do it "doesn't hang on tricky gemfile" do
skip "Only for ruby 2.7.3" if RUBY_VERSION != "2.7.3" skip "Only for ruby 2.7.3" if RUBY_VERSION != "2.7.3" || RUBY_PLATFORM =~ /darwin/
gemfile <<~G gemfile <<~G
source 'https://rubygems.org' source 'https://rubygems.org'
@ -487,7 +360,7 @@ RSpec.describe "real world edgecases", :realworld => true do
end end
it "doesn't hang on nix gemfile" do it "doesn't hang on nix gemfile" do
skip "Only for ruby 3.0.1" if RUBY_VERSION != "3.0.1" skip "Only for ruby 3.0.1" if RUBY_VERSION != "3.0.1" || RUBY_PLATFORM =~ /darwin/
gemfile <<~G gemfile <<~G
source "https://rubygems.org" do source "https://rubygems.org" do

View File

@ -4,17 +4,19 @@ require "spec_helper"
RSpec.describe "bundle install with complex dependencies", :realworld => true do RSpec.describe "bundle install with complex dependencies", :realworld => true do
it "resolves quickly" do it "resolves quickly" do
start_time = Time.now gemfile <<-G
install_gemfile <<-G
source 'https://rubygems.org' source 'https://rubygems.org'
gem "actionmailer" gem "actionmailer"
gem "mongoid", ">= 0.10.2" gem "mongoid", ">= 0.10.2"
G G
start_time = Time.now
bundle "lock"
duration = Time.now - start_time duration = Time.now - start_time
expect(duration.to_f).to be < 120 # seconds expect(duration.to_f).to be < 12 # seconds
end end
end end

View File

@ -13,7 +13,7 @@ require "bundler"
require "rspec/core" require "rspec/core"
require "rspec/expectations" require "rspec/expectations"
require "rspec/mocks" require "rspec/mocks"
require "diff/lcs" require "rspec/support/differ"
require_relative "support/builders" require_relative "support/builders"
require_relative "support/build_metadata" require_relative "support/build_metadata"

View File

@ -0,0 +1,16 @@
# frozen_string_literal: true
if ENV["BUNDLER_SPEC_API_REQUEST_LIMIT"]
require_relative "path"
require "bundler/source"
require "bundler/source/rubygems"
module Bundler
class Source
class Rubygems < Source
remove_const :API_REQUEST_LIMIT
API_REQUEST_LIMIT = ENV["BUNDLER_SPEC_API_REQUEST_LIMIT"].to_i
end
end
end
end

View File

@ -7,7 +7,7 @@ Artifice.deactivate
class CompactIndexRateLimited < CompactIndexAPI class CompactIndexRateLimited < CompactIndexAPI
class RequestCounter class RequestCounter
def self.queue def self.queue
@queue ||= Queue.new @queue ||= Thread::Queue.new
end end
def self.size def self.size

View File

@ -8,7 +8,7 @@ require "artifice"
require "sinatra/base" require "sinatra/base"
ALL_REQUESTS = [] # rubocop:disable Style/MutableConstant ALL_REQUESTS = [] # rubocop:disable Style/MutableConstant
ALL_REQUESTS_MUTEX = Mutex.new ALL_REQUESTS_MUTEX = Thread::Mutex.new
at_exit do at_exit do
if expected = ENV["BUNDLER_SPEC_ALL_REQUESTS"] if expected = ENV["BUNDLER_SPEC_ALL_REQUESTS"]

View File

@ -35,28 +35,3 @@ module Gem
end end
end end
end end
if ENV["BUNDLER_SPEC_WINDOWS"] == "true"
require_relative "path"
require "bundler/constants"
module Bundler
remove_const :WINDOWS if defined?(WINDOWS)
WINDOWS = true
end
end
if ENV["BUNDLER_SPEC_API_REQUEST_LIMIT"]
require_relative "path"
require "bundler/source"
require "bundler/source/rubygems"
module Bundler
class Source
class Rubygems < Source
remove_const :API_REQUEST_LIMIT
API_REQUEST_LIMIT = ENV["BUNDLER_SPEC_API_REQUEST_LIMIT"].to_i
end
end
end
end

View File

@ -457,15 +457,11 @@ module Spec
end end
def simulate_windows(platform = mswin) def simulate_windows(platform = mswin)
old = ENV["BUNDLER_SPEC_WINDOWS"]
ENV["BUNDLER_SPEC_WINDOWS"] = "true"
simulate_platform platform do simulate_platform platform do
simulate_bundler_version_when_missing_prerelease_default_gem_activation do simulate_bundler_version_when_missing_prerelease_default_gem_activation do
yield yield
end end
end end
ensure
ENV["BUNDLER_SPEC_WINDOWS"] = old
end end
def simulate_bundler_version_when_missing_prerelease_default_gem_activation def simulate_bundler_version_when_missing_prerelease_default_gem_activation

View File

@ -118,14 +118,14 @@ module Spec
opts[:raise_on_error] = false opts[:raise_on_error] = false
@errors = names.map do |full_name| @errors = names.map do |full_name|
name, version, platform = full_name.split(/\s+/) name, version, platform = full_name.split(/\s+/)
require_path = name == "bundler" ? "#{lib_dir}/bundler" : name.tr("-", "/") require_path = name.tr("-", "/")
version_const = name == "bundler" ? "Bundler::VERSION" : Spec::Builders.constantize(name) version_const = name == "bundler" ? "Bundler::VERSION" : Spec::Builders.constantize(name)
source_const = "#{Spec::Builders.constantize(name)}_SOURCE" source_const = "#{Spec::Builders.constantize(name)}_SOURCE"
ruby <<~R, opts ruby <<~R, opts
require '#{lib_dir}/bundler' require 'bundler'
Bundler.setup(#{groups}) Bundler.setup(#{groups})
require '#{require_path}.rb' require '#{require_path}'
actual_version, actual_platform = #{version_const}.split(/\s+/, 2) actual_version, actual_platform = #{version_const}.split(/\s+/, 2)
unless Gem::Version.new(actual_version) == Gem::Version.new('#{version}') unless Gem::Version.new(actual_version) == Gem::Version.new('#{version}')
puts actual_version puts actual_version
@ -170,7 +170,7 @@ module Spec
name, version = name.split(/\s+/, 2) name, version = name.split(/\s+/, 2)
ruby <<-R, opts ruby <<-R, opts
begin begin
require '#{lib_dir}/bundler' require 'bundler'
Bundler.setup(#{groups}) Bundler.setup(#{groups})
rescue Bundler::GemNotFound, Bundler::GitError rescue Bundler::GemNotFound, Bundler::GitError
exit 0 exit 0

View File

@ -71,6 +71,10 @@ module Spec
@spec_dir ||= source_root.join(ruby_core? ? "spec/bundler" : "spec") @spec_dir ||= source_root.join(ruby_core? ? "spec/bundler" : "spec")
end end
def api_request_limit_hack_file
spec_dir.join("support/api_request_limit_hax.rb")
end
def man_dir def man_dir
@man_dir ||= lib_dir.join("bundler/man") @man_dir ||= lib_dir.join("bundler/man")
end end

View File

@ -1595,7 +1595,7 @@ class Object
metaclass.send :undef_method, name metaclass.send :undef_method, name
metaclass.send :alias_method, name, new_name metaclass.send :alias_method, name, new_name
metaclass.send :undef_method, new_name metaclass.send :undef_method, new_name
end end unless method_defined?(:stub) # lib/resolv/test_dns.rb also has the same method definition
end end
require_relative 'utilities' require_relative 'utilities'

View File

@ -14,6 +14,7 @@ class TestGemGemcutterUtilities < Gem::TestCase
Gem.configuration.disable_default_gem_server = nil Gem.configuration.disable_default_gem_server = nil
ENV['RUBYGEMS_HOST'] = nil ENV['RUBYGEMS_HOST'] = nil
ENV['GEM_HOST_OTP_CODE'] = nil
Gem.configuration.rubygems_api_key = nil Gem.configuration.rubygems_api_key = nil
@cmd = Gem::Command.new '', 'summary' @cmd = Gem::Command.new '', 'summary'
@ -22,6 +23,7 @@ class TestGemGemcutterUtilities < Gem::TestCase
def teardown def teardown
ENV['RUBYGEMS_HOST'] = nil ENV['RUBYGEMS_HOST'] = nil
ENV['GEM_HOST_OTP_CODE'] = nil
Gem.configuration.rubygems_api_key = nil Gem.configuration.rubygems_api_key = nil
credential_teardown credential_teardown
@ -186,6 +188,16 @@ class TestGemGemcutterUtilities < Gem::TestCase
assert_match %r{Access Denied.}, @sign_in_ui.output assert_match %r{Access Denied.}, @sign_in_ui.output
end end
def test_signin_with_env_otp_code
ENV['GEM_HOST_OTP_CODE'] = '111111'
api_key = 'a5fdbb6ba150cbb83aad2bb2fede64cf040453903'
util_sign_in [api_key, 200, 'OK']
assert_match 'Signed in with API key:', @sign_in_ui.output
assert_equal '111111', @fetcher.last_request['OTP']
end
def test_sign_in_with_correct_otp_code def test_sign_in_with_correct_otp_code
api_key = 'a5fdbb6ba150cbb83aad2bb2fede64cf040453903' api_key = 'a5fdbb6ba150cbb83aad2bb2fede64cf040453903'
response_fail = "You have enabled multifactor authentication but your request doesn't have the correct OTP code. Please check it and retry." response_fail = "You have enabled multifactor authentication but your request doesn't have the correct OTP code. Please check it and retry."