1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

Merge RubyGems/Bundler master from 8459ebd6ad65ce3397233416dc64083ae7572bb9

This commit is contained in:
Hiroshi SHIBATA 2021-07-13 20:58:08 +09:00
parent 29f6f79e73
commit 896bbb9fad
Notes: git 2021-07-14 10:48:43 +09:00
45 changed files with 537 additions and 448 deletions

View file

@ -236,8 +236,9 @@ module Bundler
end end
if warning if warning
user_home = tmp_home_path(warning) Bundler.ui.warn "#{warning}\n"
Bundler.ui.warn "#{warning}\nBundler will use `#{user_home}' as your home directory temporarily.\n" user_home = tmp_home_path
Bundler.ui.warn "Bundler will use `#{user_home}' as your home directory temporarily.\n"
user_home user_home
else else
Pathname.new(home) Pathname.new(home)
@ -684,15 +685,13 @@ EOF
Bundler.rubygems.clear_paths Bundler.rubygems.clear_paths
end end
def tmp_home_path(warning) def tmp_home_path
Kernel.send(:require, "tmpdir") Kernel.send(:require, "tmpdir")
SharedHelpers.filesystem_access(Dir.tmpdir) do SharedHelpers.filesystem_access(Dir.tmpdir) do
path = Bundler.tmp path = Bundler.tmp
at_exit { Bundler.rm_rf(path) } at_exit { Bundler.rm_rf(path) }
path path
end end
rescue RuntimeError => e
raise e.exception("#{warning}\nBundler also failed to create a temporary home directory':\n#{e}")
end end
# @param env [Hash] # @param env [Hash]

View file

@ -138,7 +138,7 @@ module Bundler
@unlock[:gems] ||= @dependencies.map(&:name) @unlock[:gems] ||= @dependencies.map(&:name)
else else
eager_unlock = expand_dependencies(@unlock[:gems] || [], true) eager_unlock = expand_dependencies(@unlock[:gems] || [], true)
@unlock[:gems] = @locked_specs.for(eager_unlock, [], false, false, false).map(&:name) @unlock[:gems] = @locked_specs.for(eager_unlock, false, false, false).map(&:name)
end end
@dependency_changes = converge_dependencies @dependency_changes = converge_dependencies
@ -190,9 +190,7 @@ module Bundler
# #
# @return [Bundler::SpecSet] # @return [Bundler::SpecSet]
def specs def specs
@specs ||= begin @specs ||= add_bundler_to(resolve.materialize(requested_dependencies))
begin
specs = resolve.materialize(requested_dependencies)
rescue GemNotFound => e # Handle yanked gem rescue GemNotFound => e # Handle yanked gem
gem_name, gem_version = extract_gem_info(e) gem_name, gem_version = extract_gem_info(e)
locked_gem = @locked_specs[gem_name].last locked_gem = @locked_specs[gem_name].last
@ -202,14 +200,6 @@ module Bundler
"You'll need to update your bundle to a version other than #{locked_gem} that hasn't been " \ "You'll need to update your bundle to a version other than #{locked_gem} that hasn't been " \
"removed in order to install." "removed in order to install."
end end
unless specs["bundler"].any?
bundler = sources.metadata_source.specs.search(Gem::Dependency.new("bundler", VERSION)).last
specs["bundler"] = bundler
end
specs
end
end
def new_specs def new_specs
specs - @locked_specs specs - @locked_specs
@ -240,17 +230,11 @@ module Bundler
end end
def requested_specs def requested_specs
@requested_specs ||= begin specs_for(requested_groups)
groups = requested_groups
groups.map!(&:to_sym)
specs_for(groups)
end
end end
def requested_dependencies def requested_dependencies
groups = requested_groups dependencies_for(requested_groups)
groups.map!(&:to_sym)
dependencies_for(groups)
end end
def current_dependencies def current_dependencies
@ -260,11 +244,13 @@ module Bundler
end end
def specs_for(groups) def specs_for(groups)
groups = requested_groups if groups.empty?
deps = dependencies_for(groups) deps = dependencies_for(groups)
SpecSet.new(specs.for(expand_dependencies(deps))) add_bundler_to(resolve.materialize(expand_dependencies(deps)))
end end
def dependencies_for(groups) def dependencies_for(groups)
groups.map!(&:to_sym)
current_dependencies.reject do |d| current_dependencies.reject do |d|
(d.groups & groups).empty? (d.groups & groups).empty?
end end
@ -514,6 +500,15 @@ module Bundler
private private
def add_bundler_to(specs)
unless specs["bundler"].any?
bundler = sources.metadata_source.specs.search(Gem::Dependency.new("bundler", VERSION)).last
specs["bundler"] = bundler
end
specs
end
def precompute_source_requirements_for_indirect_dependencies? def precompute_source_requirements_for_indirect_dependencies?
sources.non_global_rubygems_sources.all?(&:dependency_api_available?) && !sources.aggregate_global_source? sources.non_global_rubygems_sources.all?(&:dependency_api_available?) && !sources.aggregate_global_source?
end end
@ -742,7 +737,7 @@ module Bundler
# if we won't need the source (according to the lockfile), # if we won't need the source (according to the lockfile),
# don't error if the path/git source isn't available # don't error if the path/git source isn't available
next if @locked_specs. next if @locked_specs.
for(requested_dependencies, [], false, true, false). for(requested_dependencies, false, true, false).
none? {|locked_spec| locked_spec.source == s.source } none? {|locked_spec| locked_spec.source == s.source }
raise raise
@ -761,8 +756,8 @@ module Bundler
end end
resolve = SpecSet.new(converged) resolve = SpecSet.new(converged)
@locked_specs_incomplete_for_platform = !resolve.for(expand_dependencies(requested_dependencies & deps), @unlock[:gems], true, true) @locked_specs_incomplete_for_platform = !resolve.for(expand_dependencies(requested_dependencies & deps), true, true)
resolve = SpecSet.new(resolve.for(expand_dependencies(deps, true), [], false, false, false).reject{|s| @unlock[:gems].include?(s.name) }) resolve = SpecSet.new(resolve.for(expand_dependencies(deps, true), false, false, false).reject{|s| @unlock[:gems].include?(s.name) })
diff = nil diff = nil
# Now, we unlock any sources that do not have anymore gems pinned to it # Now, we unlock any sources that do not have anymore gems pinned to it

View file

@ -102,9 +102,9 @@ module Bundler
# if there's already a dependency with this name we try to prefer one # if there's already a dependency with this name we try to prefer one
if current = @dependencies.find {|d| d.name == dep.name } if current = @dependencies.find {|d| d.name == dep.name }
deleted_dep = @dependencies.delete(current) if current.type == :development deleted_dep = @dependencies.delete(current) if current.type == :development
return if deleted_dep
if current.requirement != dep.requirement if current.requirement != dep.requirement
unless deleted_dep
return if dep.type == :development return if dep.type == :development
update_prompt = "" update_prompt = ""
@ -122,8 +122,6 @@ module Bundler
raise GemfileError, "You cannot specify the same gem twice with different version requirements.\n" \ raise GemfileError, "You cannot specify the same gem twice with different version requirements.\n" \
"You specified: #{current.name} (#{current.requirement}) and #{dep.name} (#{dep.requirement})" \ "You specified: #{current.name} (#{current.requirement}) and #{dep.name} (#{dep.requirement})" \
"#{update_prompt}" "#{update_prompt}"
end
else else
Bundler.ui.warn "Your Gemfile lists the gem #{current.name} (#{current.requirement}) more than once.\n" \ Bundler.ui.warn "Your Gemfile lists the gem #{current.name} (#{current.requirement}) more than once.\n" \
"You should probably keep only one of them.\n" \ "You should probably keep only one of them.\n" \
@ -132,14 +130,12 @@ module Bundler
end end
if current.source != dep.source if current.source != dep.source
unless deleted_dep
return if dep.type == :development return if dep.type == :development
raise GemfileError, "You cannot specify the same gem twice coming from different sources.\n" \ raise GemfileError, "You cannot specify the same gem twice coming from different sources.\n" \
"You specified that #{dep.name} (#{dep.requirement}) should come from " \ "You specified that #{dep.name} (#{dep.requirement}) should come from " \
"#{current.source || "an unspecified source"} and #{dep.source}\n" "#{current.source || "an unspecified source"} and #{dep.source}\n"
end end
end end
end
@dependencies << dep @dependencies << dep
end end

View file

@ -195,11 +195,7 @@ module Bundler
if base # allow all platforms when searching from a lockfile if base # allow all platforms when searching from a lockfile
dependency.matches_spec?(spec) dependency.matches_spec?(spec)
else else
if Gem::Platform.respond_to? :match_spec?
dependency.matches_spec?(spec) && Gem::Platform.match_spec?(spec) dependency.matches_spec?(spec) && Gem::Platform.match_spec?(spec)
else
dependency.matches_spec?(spec) && Gem::Platform.match(spec.platform)
end
end end
end end

View file

@ -3,7 +3,7 @@
module Bundler module Bundler
class Standalone class Standalone
def initialize(groups, definition) def initialize(groups, definition)
@specs = groups.empty? ? definition.requested_specs : definition.specs_for(groups.map(&:to_sym)) @specs = definition.specs_for(groups)
end end
def generate def generate

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.add_global_rubygems_remote(rubygems_source) if rubygems_source Array(rubygems_source).each {|remote| source_list.add_global_rubygems_remote(remote) } if rubygems_source
deps = names.map {|name| Dependency.new name, version } deps = names.map {|name| Dependency.new name, version }

View file

@ -176,20 +176,36 @@ module Gem
end end
end end
require "rubygems/platform"
class Platform class Platform
JAVA = Gem::Platform.new("java") unless defined?(JAVA) JAVA = Gem::Platform.new("java") unless defined?(JAVA)
MSWIN = Gem::Platform.new("mswin32") unless defined?(MSWIN) MSWIN = Gem::Platform.new("mswin32") unless defined?(MSWIN)
MSWIN64 = Gem::Platform.new("mswin64") unless defined?(MSWIN64) MSWIN64 = Gem::Platform.new("mswin64") unless defined?(MSWIN64)
MINGW = Gem::Platform.new("x86-mingw32") unless defined?(MINGW) MINGW = Gem::Platform.new("x86-mingw32") unless defined?(MINGW)
X64_MINGW = Gem::Platform.new("x64-mingw32") unless defined?(X64_MINGW) X64_MINGW = Gem::Platform.new("x64-mingw32") unless defined?(X64_MINGW)
undef_method :hash if method_defined? :hash
def hash
@cpu.hash ^ @os.hash ^ @version.hash
end end
undef_method :eql? if method_defined? :eql? Platform.singleton_class.module_eval do
alias_method :eql?, :== unless Platform.singleton_methods.include?(:match_spec?)
def match_spec?(spec)
match_gem?(spec.platform, spec.name)
end
def match_gem?(platform, gem_name)
match_platforms?(platform, Gem.platforms)
end
private
def match_platforms?(platform, platforms)
platforms.any? do |local_platform|
platform.nil? ||
local_platform == platform ||
(local_platform != Gem::Platform::RUBY && local_platform =~ platform)
end
end
end
end end
require "rubygems/util" require "rubygems/util"

View file

@ -12,12 +12,10 @@ module Bundler
def setup(*groups) def setup(*groups)
@definition.ensure_equivalent_gemfile_and_lockfile if Bundler.frozen_bundle? @definition.ensure_equivalent_gemfile_and_lockfile if Bundler.frozen_bundle?
groups.map!(&:to_sym)
# Has to happen first # Has to happen first
clean_load_path clean_load_path
specs = groups.any? ? @definition.specs_for(groups) : requested_specs specs = @definition.specs_for(groups)
SharedHelpers.set_bundle_environment SharedHelpers.set_bundle_environment
Bundler.rubygems.replace_entrypoints(specs) Bundler.rubygems.replace_entrypoints(specs)

View file

@ -428,12 +428,8 @@ module Bundler
def global_config_file def global_config_file
if ENV["BUNDLE_CONFIG"] && !ENV["BUNDLE_CONFIG"].empty? if ENV["BUNDLE_CONFIG"] && !ENV["BUNDLE_CONFIG"].empty?
Pathname.new(ENV["BUNDLE_CONFIG"]) Pathname.new(ENV["BUNDLE_CONFIG"])
else elsif Bundler.rubygems.user_home && !Bundler.rubygems.user_home.empty?
begin Pathname.new(Bundler.rubygems.user_home).join(".bundle/config")
Bundler.user_bundle_path("config")
rescue PermissionError, GenericSystemCallError
nil
end
end end
end end

View file

@ -11,15 +11,14 @@ module Bundler
@specs = specs @specs = specs
end end
def for(dependencies, skip = [], check = false, match_current_platform = false, raise_on_missing = true) def for(dependencies, check = false, match_current_platform = false, raise_on_missing = true)
handled = [] handled = []
deps = dependencies.dup deps = dependencies.dup
specs = [] specs = []
skip += ["bundler"]
loop do loop do
break unless dep = deps.shift break unless dep = deps.shift
next if handled.include?(dep) || skip.include?(dep.name) next if handled.any?{|d| d.name == dep.name && (match_current_platform || d.__platform == dep.__platform) } || dep.name == "bundler"
handled << dep handled << dep
@ -73,7 +72,7 @@ module Bundler
end end
def materialize(deps, missing_specs = nil) def materialize(deps, missing_specs = nil)
materialized = self.for(deps, [], false, true, !missing_specs) materialized = self.for(deps, false, true, !missing_specs)
materialized.group_by(&:source).each do |source, specs| materialized.group_by(&:source).each do |source, specs|
next unless specs.any?{|s| s.is_a?(LazySpecification) } next unless specs.any?{|s| s.is_a?(LazySpecification) }
@ -195,7 +194,7 @@ module Bundler
def spec_for_dependency(dep, match_current_platform) def spec_for_dependency(dep, match_current_platform)
specs_for_platforms = lookup[dep.name] specs_for_platforms = lookup[dep.name]
if match_current_platform if match_current_platform
GemHelpers.select_best_platform_match(specs_for_platforms, Bundler.local_platform) GemHelpers.select_best_platform_match(specs_for_platforms.select{|s| Gem::Platform.match_spec?(s) }, Bundler.local_platform)
else else
GemHelpers.select_best_platform_match(specs_for_platforms, dep.__platform) GemHelpers.select_best_platform_match(specs_for_platforms, dep.__platform)
end end

View file

@ -1,14 +1,18 @@
require_relative 'connection_pool/version' require "timeout"
require_relative 'connection_pool/timed_stack' require_relative "connection_pool/version"
class Bundler::ConnectionPool
class Error < ::RuntimeError; end
class PoolShuttingDownError < ::Bundler::ConnectionPool::Error; end
class TimeoutError < ::Timeout::Error; end
end
# Generic connection pool class for e.g. sharing a limited number of network connections # Generic connection pool class for sharing a limited number of objects or network connections
# among many threads. Note: Connections are lazily created. # among many threads. Note: pool elements are lazily created.
# #
# Example usage with block (faster): # Example usage with block (faster):
# #
# @pool = Bundler::ConnectionPool.new { Redis.new } # @pool = Bundler::ConnectionPool.new { Redis.new }
#
# @pool.with do |redis| # @pool.with do |redis|
# redis.lpop('my-list') if redis.llen('my-list') > 0 # redis.lpop('my-list') if redis.llen('my-list') > 0
# end # end
@ -34,29 +38,23 @@ require_relative 'connection_pool/timed_stack'
class Bundler::ConnectionPool class Bundler::ConnectionPool
DEFAULTS = {size: 5, timeout: 5} DEFAULTS = {size: 5, timeout: 5}
class Error < RuntimeError
end
def self.wrap(options, &block) def self.wrap(options, &block)
Wrapper.new(options, &block) Wrapper.new(options, &block)
end end
def initialize(options = {}, &block) def initialize(options = {}, &block)
raise ArgumentError, 'Connection pool requires a block' unless block raise ArgumentError, "Connection pool requires a block" unless block
options = DEFAULTS.merge(options) options = DEFAULTS.merge(options)
@size = options.fetch(:size) @size = Integer(options.fetch(:size))
@timeout = options.fetch(:timeout) @timeout = options.fetch(:timeout)
@available = TimedStack.new(@size, &block) @available = TimedStack.new(@size, &block)
@key = :"current-#{@available.object_id}" @key = :"pool-#{@available.object_id}"
@key_count = :"current-#{@available.object_id}-count" @key_count = :"pool-#{@available.object_id}-count"
end end
if Thread.respond_to?(:handle_interrupt)
# MRI
def with(options = {}) def with(options = {})
Thread.handle_interrupt(Exception => :never) do Thread.handle_interrupt(Exception => :never) do
conn = checkout(options) conn = checkout(options)
@ -69,20 +67,7 @@ if Thread.respond_to?(:handle_interrupt)
end end
end end
end end
alias then with
else
# jruby 1.7.x
def with(options = {})
conn = checkout(options)
begin
yield conn
ensure
checkin
end
end
end
def checkout(options = {}) def checkout(options = {})
if ::Thread.current[@key] if ::Thread.current[@key]
@ -99,63 +84,43 @@ end
if ::Thread.current[@key_count] == 1 if ::Thread.current[@key_count] == 1
@available.push(::Thread.current[@key]) @available.push(::Thread.current[@key])
::Thread.current[@key] = nil ::Thread.current[@key] = nil
::Thread.current[@key_count] = nil
else else
::Thread.current[@key_count] -= 1 ::Thread.current[@key_count] -= 1
end end
else else
raise Bundler::ConnectionPool::Error, 'no connections are checked out' raise Bundler::ConnectionPool::Error, "no connections are checked out"
end end
nil nil
end end
##
# Shuts down the Bundler::ConnectionPool by passing each connection to +block+ and
# then removing it from the pool. Attempting to checkout a connection after
# shutdown will raise +Bundler::ConnectionPool::PoolShuttingDownError+.
def shutdown(&block) def shutdown(&block)
@available.shutdown(&block) @available.shutdown(&block)
end end
# Size of this connection pool ##
def size # Reloads the Bundler::ConnectionPool by passing each connection to +block+ and then
@size # removing it the pool. Subsequent checkouts will create new connections as
# needed.
def reload(&block)
@available.shutdown(reload: true, &block)
end end
# Size of this connection pool
attr_reader :size
# Number of pool entries available for checkout at this instant. # Number of pool entries available for checkout at this instant.
def available def available
@available.length @available.length
end end
private
class Wrapper < ::BasicObject
METHODS = [:with, :pool_shutdown]
def initialize(options = {}, &block)
@pool = options.fetch(:pool) { ::Bundler::ConnectionPool.new(options, &block) }
end end
def with(&block) require_relative "connection_pool/timed_stack"
@pool.with(&block) require_relative "connection_pool/wrapper"
end
def pool_shutdown(&block)
@pool.shutdown(&block)
end
def pool_size
@pool.size
end
def pool_available
@pool.available
end
def respond_to?(id, *args)
METHODS.include?(id) || with { |c| c.respond_to?(id, *args) }
end
def method_missing(name, *args, &block)
with do |connection|
connection.send(name, *args, &block)
end
end
end
end

View file

@ -1,66 +0,0 @@
# Global monotonic clock from Concurrent Ruby 1.0.
# Copyright (c) Jerry D'Antonio -- released under the MIT license.
# Slightly modified; used with permission.
# https://github.com/ruby-concurrency/concurrent-ruby
require 'thread'
class Bundler::ConnectionPool
class_definition = Class.new do
if defined?(Process::CLOCK_MONOTONIC)
# @!visibility private
def get_time
Process.clock_gettime(Process::CLOCK_MONOTONIC)
end
elsif defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
# @!visibility private
def get_time
java.lang.System.nanoTime() / 1_000_000_000.0
end
else
# @!visibility private
def initialize
@mutex = Thread::Mutex.new
@last_time = Time.now.to_f
end
# @!visibility private
def get_time
@mutex.synchronize do
now = Time.now.to_f
if @last_time < now
@last_time = now
else # clock has moved back in time
@last_time += 0.000_001
end
end
end
end
end
##
# Clock that cannot be set and represents monotonic time since
# some unspecified starting point.
#
# @!visibility private
GLOBAL_MONOTONIC_CLOCK = class_definition.new
private_constant :GLOBAL_MONOTONIC_CLOCK
class << self
##
# Returns the current time a tracked by the application monotonic clock.
#
# @return [Float] The current monotonic time when `since` not given else
# the elapsed monotonic time between `since` and the current time
def monotonic_time
GLOBAL_MONOTONIC_CLOCK.get_time
end
end
end

View file

@ -1,13 +1,3 @@
require 'thread'
require 'timeout'
require_relative 'monotonic_time'
##
# Raised when you attempt to retrieve a connection from a pool that has been
# shut down.
class Bundler::ConnectionPool::PoolShuttingDownError < RuntimeError; end
## ##
# The TimedStack manages a pool of homogeneous connections (or any resource # The TimedStack manages a pool of homogeneous connections (or any resource
# you wish to manage). Connections are created lazily up to a given maximum # you wish to manage). Connections are created lazily up to a given maximum
@ -25,7 +15,7 @@ class Bundler::ConnectionPool::PoolShuttingDownError < RuntimeError; end
# #
# conn = ts.pop # conn = ts.pop
# ts.pop timeout: 5 # ts.pop timeout: 5
# #=> raises Timeout::Error after 5 seconds # #=> raises Bundler::ConnectionPool::TimeoutError after 5 seconds
class Bundler::ConnectionPool::TimedStack class Bundler::ConnectionPool::TimedStack
attr_reader :max attr_reader :max
@ -59,12 +49,12 @@ class Bundler::ConnectionPool::TimedStack
@resource.broadcast @resource.broadcast
end end
end end
alias_method :<<, :push alias << push
## ##
# Retrieves a connection from the stack. If a connection is available it is # Retrieves a connection from the stack. If a connection is available it is
# immediately returned. If no connection is available within the given # immediately returned. If no connection is available within the given
# timeout a Timeout::Error is raised. # timeout a Bundler::ConnectionPool::TimeoutError is raised.
# #
# +:timeout+ is the only checked entry in +options+ and is preferred over # +:timeout+ is the only checked entry in +options+ and is preferred over
# the +timeout+ argument (which will be removed in a future release). Other # the +timeout+ argument (which will be removed in a future release). Other
@ -74,7 +64,7 @@ class Bundler::ConnectionPool::TimedStack
options, timeout = timeout, 0.5 if Hash === timeout options, timeout = timeout, 0.5 if Hash === timeout
timeout = options.fetch :timeout, timeout timeout = options.fetch :timeout, timeout
deadline = Bundler::ConnectionPool.monotonic_time + timeout deadline = current_time + timeout
@mutex.synchronize do @mutex.synchronize do
loop do loop do
raise Bundler::ConnectionPool::PoolShuttingDownError if @shutdown_block raise Bundler::ConnectionPool::PoolShuttingDownError if @shutdown_block
@ -83,18 +73,20 @@ class Bundler::ConnectionPool::TimedStack
connection = try_create(options) connection = try_create(options)
return connection if connection return connection if connection
to_wait = deadline - Bundler::ConnectionPool.monotonic_time to_wait = deadline - current_time
raise Timeout::Error, "Waited #{timeout} sec" if to_wait <= 0 raise Bundler::ConnectionPool::TimeoutError, "Waited #{timeout} sec" if to_wait <= 0
@resource.wait(@mutex, to_wait) @resource.wait(@mutex, to_wait)
end end
end end
end end
## ##
# Shuts down the TimedStack which prevents connections from being checked # Shuts down the TimedStack by passing each connection to +block+ and then
# out. The +block+ is called once for each connection on the stack. # removing it from the pool. Attempting to checkout a connection after
# shutdown will raise +Bundler::ConnectionPool::PoolShuttingDownError+ unless
# +:reload+ is +true+.
def shutdown(&block) def shutdown(reload: false, &block)
raise ArgumentError, "shutdown must receive a block" unless block_given? raise ArgumentError, "shutdown must receive a block" unless block_given?
@mutex.synchronize do @mutex.synchronize do
@ -102,6 +94,7 @@ class Bundler::ConnectionPool::TimedStack
@resource.broadcast @resource.broadcast
shutdown_connections shutdown_connections
@shutdown_block = nil if reload
end end
end end
@ -121,6 +114,10 @@ class Bundler::ConnectionPool::TimedStack
private private
def current_time
Process.clock_gettime(Process::CLOCK_MONOTONIC)
end
## ##
# This is an extension point for TimedStack and is called with a mutex. # This is an extension point for TimedStack and is called with a mutex.
# #
@ -149,6 +146,7 @@ class Bundler::ConnectionPool::TimedStack
conn = fetch_connection(options) conn = fetch_connection(options)
@shutdown_block.call(conn) @shutdown_block.call(conn)
end end
@created = 0
end end
## ##

View file

@ -1,3 +1,3 @@
class Bundler::ConnectionPool class Bundler::ConnectionPool
VERSION = "2.2.2" VERSION = "2.3.0"
end end

View file

@ -0,0 +1,57 @@
class Bundler::ConnectionPool
class Wrapper < ::BasicObject
METHODS = [:with, :pool_shutdown, :wrapped_pool]
def initialize(options = {}, &block)
@pool = options.fetch(:pool) { ::Bundler::ConnectionPool.new(options, &block) }
end
def wrapped_pool
@pool
end
def with(&block)
@pool.with(&block)
end
def pool_shutdown(&block)
@pool.shutdown(&block)
end
def pool_size
@pool.size
end
def pool_available
@pool.available
end
def respond_to?(id, *args)
METHODS.include?(id) || with { |c| c.respond_to?(id, *args) }
end
# rubocop:disable Style/MethodMissingSuper
# rubocop:disable Style/MissingRespondToMissing
if ::RUBY_VERSION >= "3.0.0"
def method_missing(name, *args, **kwargs, &block)
with do |connection|
connection.send(name, *args, **kwargs, &block)
end
end
elsif ::RUBY_VERSION >= "2.7.0"
ruby2_keywords def method_missing(name, *args, &block)
with do |connection|
connection.send(name, *args, &block)
end
end
else
def method_missing(name, *args, &block)
with do |connection|
connection.send(name, *args, &block)
end
end
end
# rubocop:enable Style/MethodMissingSuper
# rubocop:enable Style/MissingRespondToMissing
end
end

View file

@ -86,7 +86,6 @@
# License:: # License::
# Copyright (c) 2001 akira yamada <akira@ruby-lang.org> # Copyright (c) 2001 akira yamada <akira@ruby-lang.org>
# You can redistribute it and/or modify it under the same term as Ruby. # You can redistribute it and/or modify it under the same term as Ruby.
# Revision:: $Id$
# #
module Bundler::URI module Bundler::URI

View file

@ -3,7 +3,6 @@
# = uri/common.rb # = uri/common.rb
# #
# Author:: Akira Yamada <akira@ruby-lang.org> # Author:: Akira Yamada <akira@ruby-lang.org>
# Revision:: $Id$
# License:: # License::
# You can redistribute it and/or modify it under the same term as Ruby. # You can redistribute it and/or modify it under the same term as Ruby.
# #
@ -61,82 +60,6 @@ module Bundler::URI
module_function :make_components_hash module_function :make_components_hash
end end
# Module for escaping unsafe characters with codes.
module Escape
#
# == Synopsis
#
# Bundler::URI.escape(str [, unsafe])
#
# == Args
#
# +str+::
# String to replaces in.
# +unsafe+::
# Regexp that matches all symbols that must be replaced with codes.
# By default uses <tt>UNSAFE</tt>.
# When this argument is a String, it represents a character set.
#
# == Description
#
# Escapes the string, replacing all unsafe characters with codes.
#
# This method is obsolete and should not be used. Instead, use
# CGI.escape, Bundler::URI.encode_www_form or Bundler::URI.encode_www_form_component
# depending on your specific use case.
#
# == Usage
#
# require 'bundler/vendor/uri/lib/uri'
#
# enc_uri = Bundler::URI.escape("http://example.com/?a=\11\15")
# # => "http://example.com/?a=%09%0D"
#
# Bundler::URI.unescape(enc_uri)
# # => "http://example.com/?a=\t\r"
#
# Bundler::URI.escape("@?@!", "!?")
# # => "@%3F@%21"
#
def escape(*arg)
warn "Bundler::URI.escape is obsolete", uplevel: 1
DEFAULT_PARSER.escape(*arg)
end
alias encode escape
#
# == Synopsis
#
# Bundler::URI.unescape(str)
#
# == Args
#
# +str+::
# String to unescape.
#
# == Description
#
# This method is obsolete and should not be used. Instead, use
# CGI.unescape, Bundler::URI.decode_www_form or Bundler::URI.decode_www_form_component
# depending on your specific use case.
#
# == Usage
#
# require 'bundler/vendor/uri/lib/uri'
#
# enc_uri = Bundler::URI.escape("http://example.com/?a=\11\15")
# # => "http://example.com/?a=%09%0D"
#
# Bundler::URI.unescape(enc_uri)
# # => "http://example.com/?a=\t\r"
#
def unescape(*arg)
warn "Bundler::URI.unescape is obsolete", uplevel: 1
DEFAULT_PARSER.unescape(*arg)
end
alias decode unescape
end # module Escape
extend Escape
include REGEXP include REGEXP
@@schemes = {} @@schemes = {}
@ -145,6 +68,20 @@ module Bundler::URI
@@schemes @@schemes
end end
#
# Construct a Bundler::URI instance, using the scheme to detect the appropriate class
# from +Bundler::URI.scheme_list+.
#
def self.for(scheme, *arguments, default: Generic)
if scheme
uri_class = @@schemes[scheme.upcase] || default
else
uri_class = default
end
return uri_class.new(scheme, *arguments)
end
# #
# Base class for all Bundler::URI exceptions. # Base class for all Bundler::URI exceptions.
# #
@ -315,7 +252,7 @@ module Bundler::URI
# #
# Returns a Regexp object which matches to Bundler::URI-like strings. # Returns a Regexp object which matches to Bundler::URI-like strings.
# The Regexp object returned by this method includes arbitrary # The Regexp object returned by this method includes arbitrary
# number of capture group (parentheses). Never rely on it's number. # number of capture group (parentheses). Never rely on its number.
# #
# == Usage # == Usage
# #
@ -362,7 +299,7 @@ module Bundler::URI
# If +enc+ is given, convert +str+ to the encoding before percent encoding. # If +enc+ is given, convert +str+ to the encoding before percent encoding.
# #
# This is an implementation of # This is an implementation of
# http://www.w3.org/TR/2013/CR-html5-20130806/forms.html#url-encoded-form-data. # https://www.w3.org/TR/2013/CR-html5-20130806/forms.html#url-encoded-form-data.
# #
# See Bundler::URI.decode_www_form_component, Bundler::URI.encode_www_form. # See Bundler::URI.decode_www_form_component, Bundler::URI.encode_www_form.
def self.encode_www_form_component(str, enc=nil) def self.encode_www_form_component(str, enc=nil)
@ -403,7 +340,7 @@ module Bundler::URI
# This method doesn't handle files. When you send a file, use # This method doesn't handle files. When you send a file, use
# multipart/form-data. # multipart/form-data.
# #
# This refers http://url.spec.whatwg.org/#concept-urlencoded-serializer # This refers https://url.spec.whatwg.org/#concept-urlencoded-serializer
# #
# Bundler::URI.encode_www_form([["q", "ruby"], ["lang", "en"]]) # Bundler::URI.encode_www_form([["q", "ruby"], ["lang", "en"]])
# #=> "q=ruby&lang=en" # #=> "q=ruby&lang=en"

View file

@ -3,7 +3,6 @@
# #
# Author:: Akira Yamada <akira@ruby-lang.org> # Author:: Akira Yamada <akira@ruby-lang.org>
# License:: You can redistribute it and/or modify it under the same term as Ruby. # License:: You can redistribute it and/or modify it under the same term as Ruby.
# Revision:: $Id$
# #
# See Bundler::URI for general documentation # See Bundler::URI for general documentation
# #

View file

@ -4,7 +4,6 @@
# #
# Author:: Akira Yamada <akira@ruby-lang.org> # Author:: Akira Yamada <akira@ruby-lang.org>
# License:: You can redistribute it and/or modify it under the same term as Ruby. # License:: You can redistribute it and/or modify it under the same term as Ruby.
# Revision:: $Id$
# #
# See Bundler::URI for general documentation # See Bundler::URI for general documentation
# #
@ -1098,7 +1097,7 @@ module Bundler::URI
# # => "http://my.example.com/main.rbx?page=1" # # => "http://my.example.com/main.rbx?page=1"
# #
def merge(oth) def merge(oth)
rel = parser.send(:convert_to_uri, oth) rel = parser.__send__(:convert_to_uri, oth)
if rel.absolute? if rel.absolute?
#raise BadURIError, "both Bundler::URI are absolute" if absolute? #raise BadURIError, "both Bundler::URI are absolute" if absolute?
@ -1183,7 +1182,7 @@ module Bundler::URI
# :stopdoc: # :stopdoc:
def route_from0(oth) def route_from0(oth)
oth = parser.send(:convert_to_uri, oth) oth = parser.__send__(:convert_to_uri, oth)
if self.relative? if self.relative?
raise BadURIError, raise BadURIError,
"relative Bundler::URI: #{self}" "relative Bundler::URI: #{self}"
@ -1291,7 +1290,7 @@ module Bundler::URI
# #=> #<Bundler::URI::Generic /main.rbx?page=1> # #=> #<Bundler::URI::Generic /main.rbx?page=1>
# #
def route_to(oth) def route_to(oth)
parser.send(:convert_to_uri, oth).route_from(self) parser.__send__(:convert_to_uri, oth).route_from(self)
end end
# #
@ -1405,7 +1404,7 @@ module Bundler::URI
# Returns an Array of the components defined from the COMPONENT Array. # Returns an Array of the components defined from the COMPONENT Array.
def component_ary def component_ary
component.collect do |x| component.collect do |x|
self.send(x) self.__send__(x)
end end
end end
protected :component_ary protected :component_ary
@ -1430,7 +1429,7 @@ module Bundler::URI
def select(*components) def select(*components)
components.collect do |c| components.collect do |c|
if component.include?(c) if component.include?(c)
self.send(c) self.__send__(c)
else else
raise ArgumentError, raise ArgumentError,
"expected of components of #{self.class} (#{self.class.component.join(', ')})" "expected of components of #{self.class} (#{self.class.component.join(', ')})"

View file

@ -3,7 +3,6 @@
# #
# Author:: Akira Yamada <akira@ruby-lang.org> # Author:: Akira Yamada <akira@ruby-lang.org>
# License:: You can redistribute it and/or modify it under the same term as Ruby. # License:: You can redistribute it and/or modify it under the same term as Ruby.
# Revision:: $Id$
# #
# See Bundler::URI for general documentation # See Bundler::URI for general documentation
# #

View file

@ -3,7 +3,6 @@
# #
# Author:: Akira Yamada <akira@ruby-lang.org> # Author:: Akira Yamada <akira@ruby-lang.org>
# License:: You can redistribute it and/or modify it under the same term as Ruby. # License:: You can redistribute it and/or modify it under the same term as Ruby.
# Revision:: $Id$
# #
# See Bundler::URI for general documentation # See Bundler::URI for general documentation
# #

View file

@ -7,7 +7,6 @@
# License:: # License::
# Bundler::URI::LDAP is copyrighted free software by Takaaki Tateishi and Akira Yamada. # Bundler::URI::LDAP is copyrighted free software by Takaaki Tateishi and Akira Yamada.
# You can redistribute it and/or modify it under the same term as Ruby. # You can redistribute it and/or modify it under the same term as Ruby.
# Revision:: $Id$
# #
# See Bundler::URI for general documentation # See Bundler::URI for general documentation
# #
@ -119,6 +118,7 @@ module Bundler::URI
# Private method to cleanup +dn+ from using the +path+ component attribute. # Private method to cleanup +dn+ from using the +path+ component attribute.
def parse_dn def parse_dn
raise InvalidURIError, 'bad LDAP URL' unless @path
@dn = @path[1..-1] @dn = @path[1..-1]
end end
private :parse_dn private :parse_dn

View file

@ -3,7 +3,6 @@
# #
# Author:: Akira Yamada <akira@ruby-lang.org> # Author:: Akira Yamada <akira@ruby-lang.org>
# License:: You can redistribute it and/or modify it under the same term as Ruby. # License:: You can redistribute it and/or modify it under the same term as Ruby.
# Revision:: $Id$
# #
# See Bundler::URI for general documentation # See Bundler::URI for general documentation
# #

View file

@ -3,7 +3,6 @@
# = uri/common.rb # = uri/common.rb
# #
# Author:: Akira Yamada <akira@ruby-lang.org> # Author:: Akira Yamada <akira@ruby-lang.org>
# Revision:: $Id$
# License:: # License::
# You can redistribute it and/or modify it under the same term as Ruby. # You can redistribute it and/or modify it under the same term as Ruby.
# #
@ -208,20 +207,8 @@ module Bundler::URI
# #=> #<Bundler::URI::LDAP ldap://ldap.example.com/dc=example?user=john> # #=> #<Bundler::URI::LDAP ldap://ldap.example.com/dc=example?user=john>
# #
def parse(uri) def parse(uri)
scheme, userinfo, host, port, Bundler::URI.for(*self.split(uri), self)
registry, path, opaque, query, fragment = self.split(uri)
if scheme && Bundler::URI.scheme_list.include?(scheme.upcase)
Bundler::URI.scheme_list[scheme.upcase].new(scheme, userinfo, host, port,
registry, path, opaque, query,
fragment, self)
else
Generic.new(scheme, userinfo, host, port,
registry, path, opaque, query,
fragment, self)
end end
end
# #
# == Args # == Args

View file

@ -69,18 +69,7 @@ module Bundler::URI
end end
def parse(uri) # :nodoc: def parse(uri) # :nodoc:
scheme, userinfo, host, port, Bundler::URI.for(*self.split(uri), self)
registry, path, opaque, query, fragment = self.split(uri)
scheme_list = Bundler::URI.scheme_list
if scheme && scheme_list.include?(uc = scheme.upcase)
scheme_list[uc].new(scheme, userinfo, host, port,
registry, path, opaque, query,
fragment, self)
else
Generic.new(scheme, userinfo, host, port,
registry, path, opaque, query,
fragment, self)
end
end end

View file

@ -1,6 +1,6 @@
module Bundler::URI module Bundler::URI
# :stopdoc: # :stopdoc:
VERSION_CODE = '001000'.freeze VERSION_CODE = '001001'.freeze
VERSION = VERSION_CODE.scan(/../).collect{|n| n.to_i}.join('.').freeze VERSION = VERSION_CODE.scan(/../).collect{|n| n.to_i}.join('.').freeze
# :startdoc: # :startdoc:
end end

84
lib/bundler/vendor/uri/lib/uri/ws.rb vendored Normal file
View file

@ -0,0 +1,84 @@
# frozen_string_literal: false
# = uri/ws.rb
#
# Author:: Matt Muller <mamuller@amazon.com>
# License:: You can redistribute it and/or modify it under the same term as Ruby.
#
# See Bundler::URI for general documentation
#
require_relative 'generic'
module Bundler::URI
#
# The syntax of WS URIs is defined in RFC6455 section 3.
#
# Note that the Ruby Bundler::URI library allows WS URLs containing usernames and
# passwords. This is not legal as per the RFC, but used to be
# supported in Internet Explorer 5 and 6, before the MS04-004 security
# update. See <URL:http://support.microsoft.com/kb/834489>.
#
class WS < Generic
# A Default port of 80 for Bundler::URI::WS.
DEFAULT_PORT = 80
# An Array of the available components for Bundler::URI::WS.
COMPONENT = %i[
scheme
userinfo host port
path
query
].freeze
#
# == Description
#
# Creates a new Bundler::URI::WS object from components, with syntax checking.
#
# The components accepted are userinfo, host, port, path, and query.
#
# The components should be provided either as an Array, or as a Hash
# with keys formed by preceding the component names with a colon.
#
# If an Array is used, the components must be passed in the
# order <code>[userinfo, host, port, path, query]</code>.
#
# Example:
#
# uri = Bundler::URI::WS.build(host: 'www.example.com', path: '/foo/bar')
#
# uri = Bundler::URI::WS.build([nil, "www.example.com", nil, "/path", "query"])
#
# Currently, if passed userinfo components this method generates
# invalid WS URIs as per RFC 1738.
#
def self.build(args)
tmp = Util.make_components_hash(self, args)
super(tmp)
end
#
# == Description
#
# Returns the full path for a WS Bundler::URI, as required by Net::HTTP::Get.
#
# If the Bundler::URI contains a query, the full path is Bundler::URI#path + '?' + Bundler::URI#query.
# Otherwise, the path is simply Bundler::URI#path.
#
# Example:
#
# uri = Bundler::URI::WS.build(path: '/foo/bar', query: 'test=true')
# uri.request_uri # => "/foo/bar?test=true"
#
def request_uri
return unless @path
url = @query ? "#@path?#@query" : @path.dup
url.start_with?(?/.freeze) ? url : ?/ + url
end
end
@@schemes['WS'] = WS
end

22
lib/bundler/vendor/uri/lib/uri/wss.rb vendored Normal file
View file

@ -0,0 +1,22 @@
# frozen_string_literal: false
# = uri/wss.rb
#
# Author:: Matt Muller <mamuller@amazon.com>
# License:: You can redistribute it and/or modify it under the same term as Ruby.
#
# See Bundler::URI for general documentation
#
require_relative 'ws'
module Bundler::URI
# The default port for WSS URIs is 443, and the scheme is 'wss:' rather
# than 'ws:'. Other than that, WSS URIs are identical to WS URIs;
# see Bundler::URI::WS.
class WSS < WS
# A Default port of 443 for Bundler::URI::WSS
DEFAULT_PORT = 443
end
@@schemes['WSS'] = WSS
end

View file

@ -225,7 +225,7 @@ class Gem::SystemExitException < SystemExit
def initialize(exit_code) def initialize(exit_code)
@exit_code = exit_code @exit_code = exit_code
super "Exiting RubyGems with exit_code #{exit_code}" super exit_code, "Exiting RubyGems with exit_code #{exit_code}"
end end
end end

View file

@ -31,7 +31,8 @@ module Gem::GemcutterUtilities
def add_otp_option def add_otp_option
add_option('--otp CODE', add_option('--otp CODE',
'Digit code for multifactor authentication') do |value, options| 'Digit code for multifactor authentication',
'You can also use the environment variable GEM_HOST_OTP_CODE') do |value, options|
options[:otp] = value options[:otp] = value
end end
end end

View file

@ -32,10 +32,14 @@ class Gem::Package::IOSource < Gem::Package::Source # :nodoc: all
def with_read_io def with_read_io
yield io yield io
ensure
io.rewind
end end
def with_write_io def with_write_io
yield io yield io
ensure
io.rewind
end end
def path def path

View file

@ -249,11 +249,8 @@ EOF
allow(Bundler.rubygems).to receive(:user_home).and_return(path) allow(Bundler.rubygems).to receive(:user_home).and_return(path)
allow(File).to receive(:directory?).with(path).and_return false allow(File).to receive(:directory?).with(path).and_return false
allow(Bundler).to receive(:tmp).and_return(Pathname.new("/tmp/trulyrandom")) allow(Bundler).to receive(:tmp).and_return(Pathname.new("/tmp/trulyrandom"))
message = <<EOF expect(Bundler.ui).to receive(:warn).with("`/home/oggy` is not a directory.\n")
`/home/oggy` is not a directory. expect(Bundler.ui).to receive(:warn).with("Bundler will use `/tmp/trulyrandom' as your home directory temporarily.\n")
Bundler will use `/tmp/trulyrandom' as your home directory temporarily.
EOF
expect(Bundler.ui).to receive(:warn).with(message)
expect(Bundler.user_home).to eq(Pathname("/tmp/trulyrandom")) expect(Bundler.user_home).to eq(Pathname("/tmp/trulyrandom"))
end end
end end
@ -268,11 +265,8 @@ EOF
allow(File).to receive(:writable?).with(path).and_return false allow(File).to receive(:writable?).with(path).and_return false
allow(File).to receive(:directory?).with(dotbundle).and_return false allow(File).to receive(:directory?).with(dotbundle).and_return false
allow(Bundler).to receive(:tmp).and_return(Pathname.new("/tmp/trulyrandom")) allow(Bundler).to receive(:tmp).and_return(Pathname.new("/tmp/trulyrandom"))
message = <<EOF expect(Bundler.ui).to receive(:warn).with("`/home/oggy` is not writable.\n")
`/home/oggy` is not writable. expect(Bundler.ui).to receive(:warn).with("Bundler will use `/tmp/trulyrandom' as your home directory temporarily.\n")
Bundler will use `/tmp/trulyrandom' as your home directory temporarily.
EOF
expect(Bundler.ui).to receive(:warn).with(message)
expect(Bundler.user_home).to eq(Pathname("/tmp/trulyrandom")) expect(Bundler.user_home).to eq(Pathname("/tmp/trulyrandom"))
end end
@ -293,11 +287,8 @@ EOF
it "should issue warning and return a temporary user home" do it "should issue warning and return a temporary user home" do
allow(Bundler.rubygems).to receive(:user_home).and_return(nil) allow(Bundler.rubygems).to receive(:user_home).and_return(nil)
allow(Bundler).to receive(:tmp).and_return(Pathname.new("/tmp/trulyrandom")) allow(Bundler).to receive(:tmp).and_return(Pathname.new("/tmp/trulyrandom"))
message = <<EOF expect(Bundler.ui).to receive(:warn).with("Your home directory is not set.\n")
Your home directory is not set. expect(Bundler.ui).to receive(:warn).with("Bundler will use `/tmp/trulyrandom' as your home directory temporarily.\n")
Bundler will use `/tmp/trulyrandom' as your home directory temporarily.
EOF
expect(Bundler.ui).to receive(:warn).with(message)
expect(Bundler.user_home).to eq(Pathname("/tmp/trulyrandom")) expect(Bundler.user_home).to eq(Pathname("/tmp/trulyrandom"))
end end
end end

View file

@ -64,16 +64,13 @@ that would suck --ehhh=oh geez it looks like i might have broken bundler somehow
describe "#global_config_file" do describe "#global_config_file" do
context "when $HOME is not accessible" do context "when $HOME is not accessible" do
context "when $TMPDIR is not writable" do
it "does not raise" do it "does not raise" do
expect(Bundler.rubygems).to receive(:user_home).twice.and_return(nil) expect(Bundler.rubygems).to receive(:user_home).twice.and_return(nil)
expect(Bundler).to receive(:tmp).twice.and_raise(Errno::EROFS, "Read-only file system @ dir_s_mkdir - /tmp/bundler")
expect(subject.send(:global_config_file)).to be_nil expect(subject.send(:global_config_file)).to be_nil
end end
end end
end end
end
describe "#[]" do describe "#[]" do
context "when the local config file is not found" do context "when the local config file is not found" do

View file

@ -288,6 +288,34 @@ RSpec.describe "bundle check" do
end end
end end
describe "when locked with multiple dependents with different requirements" do
before :each do
build_repo4 do
build_gem "depends_on_rack" do |s|
s.add_dependency "rack", ">= 1.0"
end
build_gem "also_depends_on_rack" do |s|
s.add_dependency "rack", "~> 1.0"
end
build_gem "rack"
end
gemfile <<-G
source "#{file_uri_for(gem_repo4)}"
gem "depends_on_rack"
gem "also_depends_on_rack"
G
bundle "lock"
end
it "shows what is missing with the current Gemfile without duplications" do
bundle :check, :raise_on_error => false
expect(err).to match(/The following gems are missing/)
expect(err).to include("* rack (1.0").once
end
end
describe "when using only scoped rubygems sources" do describe "when using only scoped rubygems sources" do
before do before do
gemfile <<~G gemfile <<~G

View file

@ -24,6 +24,16 @@ RSpec.describe "bundle exec" do
expect(out).to eq("0.9.1") expect(out).to eq("0.9.1")
end end
it "works and prints no warnings when HOME is not writable" do
gemfile <<-G
gem "rack", "0.9.1"
G
bundle "exec rackup", :env => { "HOME" => "/" }
expect(out).to eq("0.9.1")
expect(err).to be_empty
end
it "works when the bins are in ~/.bundle" do it "works when the bins are in ~/.bundle" do
install_gemfile <<-G install_gemfile <<-G
gem "rack" gem "rack"

View file

@ -344,8 +344,7 @@ RSpec.describe "bundle install with gem sources" do
expect(File.exist?(bundled_app_lock)).to eq(true) expect(File.exist?(bundled_app_lock)).to eq(true)
end end
context "throws a warning if a gem is added twice in Gemfile" do it "throws a warning if a gem is added twice in Gemfile without version requirements" do
it "without version requirements" do
install_gemfile <<-G, :raise_on_error => false install_gemfile <<-G, :raise_on_error => false
source "#{file_uri_for(gem_repo2)}" source "#{file_uri_for(gem_repo2)}"
gem "rack" gem "rack"
@ -357,7 +356,7 @@ RSpec.describe "bundle install with gem sources" do
expect(err).to include("While it's not a problem now, it could cause errors if you change the version of one of them later.") expect(err).to include("While it's not a problem now, it could cause errors if you change the version of one of them later.")
end end
it "with same versions" do it "throws a warning if a gem is added twice in Gemfile with same versions" do
install_gemfile <<-G, :raise_on_error => false install_gemfile <<-G, :raise_on_error => false
source "#{file_uri_for(gem_repo2)}" source "#{file_uri_for(gem_repo2)}"
gem "rack", "1.0" gem "rack", "1.0"
@ -368,10 +367,30 @@ RSpec.describe "bundle install with gem sources" do
expect(err).to include("Remove any duplicate entries and specify the gem only once.") expect(err).to include("Remove any duplicate entries and specify the gem only once.")
expect(err).to include("While it's not a problem now, it could cause errors if you change the version of one of them later.") expect(err).to include("While it's not a problem now, it could cause errors if you change the version of one of them later.")
end end
it "does not throw a warning if a gem is added once in Gemfile and also inside a gemspec as a development dependency" do
build_lib "my-gem", :path => bundled_app do |s|
s.add_development_dependency "my-private-gem"
end end
context "throws an error if a gem is added twice in Gemfile" do build_repo2 do
it "when version of one dependency is not specified" do build_gem "my-private-gem"
end
gemfile <<~G
source "#{file_uri_for(gem_repo2)}"
gemspec
gem "my-private-gem", :group => :development
G
bundle :install
expect(err).to be_empty
end
it "throws an error if a gem is added twice in Gemfile when version of one dependency is not specified" do
install_gemfile <<-G, :raise_on_error => false install_gemfile <<-G, :raise_on_error => false
source "#{file_uri_for(gem_repo2)}" source "#{file_uri_for(gem_repo2)}"
gem "rack" gem "rack"
@ -382,7 +401,7 @@ RSpec.describe "bundle install with gem sources" do
expect(err).to include("You specified: rack (>= 0) and rack (= 1.0).") expect(err).to include("You specified: rack (>= 0) and rack (= 1.0).")
end end
it "when different versions of both dependencies are specified" do it "throws an error if a gem is added twice in Gemfile when different versions of both dependencies are specified" do
install_gemfile <<-G, :raise_on_error => false install_gemfile <<-G, :raise_on_error => false
source "#{file_uri_for(gem_repo2)}" source "#{file_uri_for(gem_repo2)}"
gem "rack", "1.0" gem "rack", "1.0"
@ -392,7 +411,6 @@ RSpec.describe "bundle install with gem sources" do
expect(err).to include("You cannot specify the same gem twice with different version requirements") expect(err).to include("You cannot specify the same gem twice with different version requirements")
expect(err).to include("You specified: rack (= 1.0) and rack (= 1.1).") expect(err).to include("You specified: rack (= 1.0) and rack (= 1.1).")
end end
end
it "gracefully handles error when rubygems server is unavailable" do it "gracefully handles error when rubygems server is unavailable" do
skip "networking issue" if Gem.win_platform? skip "networking issue" if Gem.win_platform?

View file

@ -307,42 +307,42 @@ RSpec.describe "bundle gem" do
skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core? skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
bundle "gem #{gem_name} --linter=rubocop" bundle "gem #{gem_name} --linter=rubocop"
bundle_exec_rubocop bundle_exec_rubocop
expect(err).to be_empty expect(last_command).to be_success
end end
it "has no rubocop offenses when using --ext and --linter=rubocop flag", :readline do it "has no rubocop offenses when using --ext and --linter=rubocop flag", :readline do
skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core? skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
bundle "gem #{gem_name} --ext --linter=rubocop" bundle "gem #{gem_name} --ext --linter=rubocop"
bundle_exec_rubocop bundle_exec_rubocop
expect(err).to be_empty expect(last_command).to be_success
end end
it "has no rubocop offenses when using --ext, --test=minitest, and --linter=rubocop flag", :readline do it "has no rubocop offenses when using --ext, --test=minitest, and --linter=rubocop flag", :readline do
skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core? skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
bundle "gem #{gem_name} --ext --test=minitest --linter=rubocop" bundle "gem #{gem_name} --ext --test=minitest --linter=rubocop"
bundle_exec_rubocop bundle_exec_rubocop
expect(err).to be_empty expect(last_command).to be_success
end end
it "has no rubocop offenses when using --ext, --test=rspec, and --linter=rubocop flag", :readline do it "has no rubocop offenses when using --ext, --test=rspec, and --linter=rubocop flag", :readline do
skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core? skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
bundle "gem #{gem_name} --ext --test=rspec --linter=rubocop" bundle "gem #{gem_name} --ext --test=rspec --linter=rubocop"
bundle_exec_rubocop bundle_exec_rubocop
expect(err).to be_empty expect(last_command).to be_success
end end
it "has no rubocop offenses when using --ext, --ext=test-unit, and --linter=rubocop flag", :readline do it "has no rubocop offenses when using --ext, --ext=test-unit, and --linter=rubocop flag", :readline do
skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core? skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
bundle "gem #{gem_name} --ext --test=test-unit --linter=rubocop" bundle "gem #{gem_name} --ext --test=test-unit --linter=rubocop"
bundle_exec_rubocop bundle_exec_rubocop
expect(err).to be_empty expect(last_command).to be_success
end end
it "has no standard offenses when using --linter=standard flag", :readline do it "has no standard offenses when using --linter=standard flag", :readline do
skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core? skip "ruby_core has an 'ast.rb' file that gets in the middle and breaks this spec" if ruby_core?
bundle "gem #{gem_name} --linter=standard" bundle "gem #{gem_name} --linter=standard"
bundle_exec_standardrb bundle_exec_standardrb
expect(err).to be_empty expect(last_command).to be_success
end end
shared_examples_for "CI config is absent" do shared_examples_for "CI config is absent" do

View file

@ -249,6 +249,38 @@ RSpec.describe "bundle install with specific platforms" do
end end
end end
it "installs sorbet-static, which does not provide a pure ruby variant, just fine on truffleruby", :truffleruby do
build_repo2 do
build_gem("sorbet-static", "0.5.6403") {|s| s.platform = "x86_64-linux" }
build_gem("sorbet-static", "0.5.6403") {|s| s.platform = "universal-darwin-20" }
end
gemfile <<~G
source "#{file_uri_for(gem_repo2)}"
gem "sorbet-static", "0.5.6403"
G
lockfile <<~L
GEM
remote: #{file_uri_for(gem_repo2)}/
specs:
sorbet-static (0.5.6403-universal-darwin-20)
sorbet-static (0.5.6403-x86_64-linux)
PLATFORMS
ruby
DEPENDENCIES
sorbet-static (= 0.5.6403)
BUNDLED WITH
#{Bundler::VERSION}
L
bundle "install --verbose"
end
private private
def setup_multiplatform_gem def setup_multiplatform_gem

View file

@ -22,6 +22,13 @@ RSpec.describe "bundler plugin install" do
plugin_should_be_installed("foo") plugin_should_be_installed("foo")
end end
it "installs from sources configured as Gem.sources without any flags" do
bundle "plugin install foo", :env => { "BUNDLER_SPEC_GEM_SOURCES" => file_uri_for(gem_repo2).to_s }
expect(out).to include("Installed plugin foo")
plugin_should_be_installed("foo")
end
context "plugin is already installed" do context "plugin is already installed" do
before do before do
bundle "plugin install foo --source #{file_uri_for(gem_repo2)}" bundle "plugin install foo --source #{file_uri_for(gem_repo2)}"

View file

@ -196,21 +196,6 @@ RSpec.describe "real world edgecases", :realworld => true do
expect(lockfile).to include(rubygems_version("paperclip", "~> 5.1.0")) expect(lockfile).to include(rubygems_version("paperclip", "~> 5.1.0"))
end end
# https://github.com/rubygems/bundler/issues/1500
it "does not fail install because of gem plugins" do
realworld_system_gems("open_gem --version 1.4.2", "rake --version 0.9.2")
gemfile <<-G
source "https://rubygems.org"
gem 'rack', '1.0.1'
G
bundle "config set --local path vendor/bundle"
bundle :install
expect(err).not_to include("Could not find rake")
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'

View file

@ -644,6 +644,25 @@ RSpec.describe "Bundler.setup" do
expect(err).to be_empty expect(err).to be_empty
end end
it "doesn't re-resolve when a pre-release bundler is used and a dependency includes a dependency on bundler" do
system_gems "bundler-9.99.9.beta1"
build_repo4 do
build_gem "depends_on_bundler", "1.0" do |s|
s.add_dependency "bundler", ">= 1.5.0"
end
end
install_gemfile <<~G
source "#{file_uri_for(gem_repo4)}"
gem "depends_on_bundler"
G
ruby "require '#{system_gem_path("gems/bundler-9.99.9.beta1/lib/bundler.rb")}'; Bundler.setup", :env => { "DEBUG" => "1" }
expect(out).to include("Found no changes, using resolution from the lockfile")
expect(err).to be_empty
end
it "remembers --without and does not include groups passed to Bundler.setup" do it "remembers --without and does not include groups passed to Bundler.setup" do
bundle "config set --local without rails" bundle "config set --local without rails"
install_gemfile <<-G install_gemfile <<-G

View file

@ -133,6 +133,19 @@ class BundlerVCRHTTP < Net::HTTP
end end
end end
def start_with_vcr
if ENV["BUNDLER_SPEC_PRE_RECORDED"]
raise IOError, "HTTP session already opened" if @started
@socket = nil
@started = true
else
start_without_vcr
end
end
alias_method :start_without_vcr, :start
alias_method :start, :start_with_vcr
def request_with_vcr(request, *args, &block) def request_with_vcr(request, *args, &block)
handler = request.instance_eval do handler = request.instance_eval do
remove_instance_variable(:@__vcr_request_handler) if defined?(@__vcr_request_handler) remove_instance_variable(:@__vcr_request_handler) if defined?(@__vcr_request_handler)

View file

@ -28,6 +28,10 @@ module Gem
end end
end end
if ENV["BUNDLER_SPEC_GEM_SOURCES"]
@sources = [ENV["BUNDLER_SPEC_GEM_SOURCES"]]
end
# We only need this hack for rubygems versions without the BundlerVersionFinder # We only need this hack for rubygems versions without the BundlerVersionFinder
if Gem.rubygems_version < Gem::Version.new("2.7.0") if Gem.rubygems_version < Gem::Version.new("2.7.0")
@path_to_default_spec_map.delete_if do |_path, spec| @path_to_default_spec_map.delete_if do |_path, spec|

View file

@ -0,0 +1,11 @@
# frozen_string_literal: true
require_relative 'helper'
require 'rubygems'
class TestExit < Gem::TestCase
def test_exit
system(*ruby_with_rubygems_in_load_path, "-e", "raise Gem::SystemExitException.new(2)")
assert_equal 2, $?.exitstatus
end
end

View file

@ -1145,6 +1145,13 @@ class TestGemPackage < Gem::Package::TarTestCase
end end
end end
def test_contents_from_io
io = StringIO.new Gem.read_binary @gem
package = Gem::Package.new io
assert_equal %w[lib/code.rb], package.contents
end
def util_tar def util_tar
tar_io = StringIO.new tar_io = StringIO.new