diff --git a/lib/bundler.rb b/lib/bundler.rb index a7d5d1e64a6349..b88bab8f0af26a 100644 --- a/lib/bundler.rb +++ b/lib/bundler.rb @@ -236,8 +236,9 @@ def user_home end if warning - user_home = tmp_home_path(warning) - Bundler.ui.warn "#{warning}\nBundler will use `#{user_home}' as your home directory temporarily.\n" + Bundler.ui.warn "#{warning}\n" + user_home = tmp_home_path + Bundler.ui.warn "Bundler will use `#{user_home}' as your home directory temporarily.\n" user_home else Pathname.new(home) @@ -684,15 +685,13 @@ def configure_gem_home Bundler.rubygems.clear_paths end - def tmp_home_path(warning) + def tmp_home_path Kernel.send(:require, "tmpdir") SharedHelpers.filesystem_access(Dir.tmpdir) do path = Bundler.tmp at_exit { Bundler.rm_rf(path) } path end - rescue RuntimeError => e - raise e.exception("#{warning}\nBundler also failed to create a temporary home directory':\n#{e}") end # @param env [Hash] diff --git a/lib/bundler/definition.rb b/lib/bundler/definition.rb index 8998e3b3ae2028..b585b06df10247 100644 --- a/lib/bundler/definition.rb +++ b/lib/bundler/definition.rb @@ -138,7 +138,7 @@ def initialize(lockfile, dependencies, sources, unlock, ruby_version = nil, opti @unlock[:gems] ||= @dependencies.map(&:name) else 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 @dependency_changes = converge_dependencies @@ -190,25 +190,15 @@ def resolve_remotely! # # @return [Bundler::SpecSet] def specs - @specs ||= begin - begin - specs = resolve.materialize(requested_dependencies) - rescue GemNotFound => e # Handle yanked gem - gem_name, gem_version = extract_gem_info(e) - locked_gem = @locked_specs[gem_name].last - raise if locked_gem.nil? || locked_gem.version.to_s != gem_version || !@remote - raise GemNotFound, "Your bundle is locked to #{locked_gem} from #{locked_gem.source}, but that version can " \ - "no longer be found in that source. That means the author of #{locked_gem} has removed it. " \ - "You'll need to update your bundle to a version other than #{locked_gem} that hasn't been " \ - "removed in order to install." - end - unless specs["bundler"].any? - bundler = sources.metadata_source.specs.search(Gem::Dependency.new("bundler", VERSION)).last - specs["bundler"] = bundler - end - - specs - end + @specs ||= add_bundler_to(resolve.materialize(requested_dependencies)) + rescue GemNotFound => e # Handle yanked gem + gem_name, gem_version = extract_gem_info(e) + locked_gem = @locked_specs[gem_name].last + raise if locked_gem.nil? || locked_gem.version.to_s != gem_version || !@remote + raise GemNotFound, "Your bundle is locked to #{locked_gem} from #{locked_gem.source}, but that version can " \ + "no longer be found in that source. That means the author of #{locked_gem} has removed it. " \ + "You'll need to update your bundle to a version other than #{locked_gem} that hasn't been " \ + "removed in order to install." end def new_specs @@ -240,17 +230,11 @@ def missing_specs? end def requested_specs - @requested_specs ||= begin - groups = requested_groups - groups.map!(&:to_sym) - specs_for(groups) - end + specs_for(requested_groups) end def requested_dependencies - groups = requested_groups - groups.map!(&:to_sym) - dependencies_for(groups) + dependencies_for(requested_groups) end def current_dependencies @@ -260,11 +244,13 @@ def current_dependencies end def specs_for(groups) + groups = requested_groups if groups.empty? deps = dependencies_for(groups) - SpecSet.new(specs.for(expand_dependencies(deps))) + add_bundler_to(resolve.materialize(expand_dependencies(deps))) end def dependencies_for(groups) + groups.map!(&:to_sym) current_dependencies.reject do |d| (d.groups & groups).empty? end @@ -514,6 +500,15 @@ def unlocking? 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? sources.non_global_rubygems_sources.all?(&:dependency_api_available?) && !sources.aggregate_global_source? end @@ -742,7 +737,7 @@ def converge_locked_specs # if we won't need the source (according to the lockfile), # don't error if the path/git source isn't available next if @locked_specs. - for(requested_dependencies, [], false, true, false). + for(requested_dependencies, false, true, false). none? {|locked_spec| locked_spec.source == s.source } raise @@ -761,8 +756,8 @@ def converge_locked_specs end resolve = SpecSet.new(converged) - @locked_specs_incomplete_for_platform = !resolve.for(expand_dependencies(requested_dependencies & deps), @unlock[:gems], true, true) - resolve = SpecSet.new(resolve.for(expand_dependencies(deps, true), [], false, false, false).reject{|s| @unlock[:gems].include?(s.name) }) + @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) }) diff = nil # Now, we unlock any sources that do not have anymore gems pinned to it diff --git a/lib/bundler/dsl.rb b/lib/bundler/dsl.rb index dc72bf0d9399b9..1605210e55b2b7 100644 --- a/lib/bundler/dsl.rb +++ b/lib/bundler/dsl.rb @@ -102,28 +102,26 @@ def gem(name, *args) # if there's already a dependency with this name we try to prefer one if current = @dependencies.find {|d| d.name == dep.name } deleted_dep = @dependencies.delete(current) if current.type == :development + return if deleted_dep if current.requirement != dep.requirement - unless deleted_dep - return if dep.type == :development + return if dep.type == :development - update_prompt = "" + update_prompt = "" - if File.basename(@gemfile) == Injector::INJECTED_GEMS - if dep.requirements_list.include?(">= 0") && !current.requirements_list.include?(">= 0") - update_prompt = ". Gem already added" - else - update_prompt = ". If you want to update the gem version, run `bundle update #{current.name}`" + if File.basename(@gemfile) == Injector::INJECTED_GEMS + if dep.requirements_list.include?(">= 0") && !current.requirements_list.include?(">= 0") + update_prompt = ". Gem already added" + else + update_prompt = ". If you want to update the gem version, run `bundle update #{current.name}`" - update_prompt += ". You may also need to change the version requirement specified in the Gemfile if it's too restrictive." unless current.requirements_list.include?(">= 0") - end + update_prompt += ". You may also need to change the version requirement specified in the Gemfile if it's too restrictive." unless current.requirements_list.include?(">= 0") end - - 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})" \ - "#{update_prompt}" end + 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})" \ + "#{update_prompt}" else 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" \ @@ -132,12 +130,10 @@ def gem(name, *args) end if current.source != dep.source - unless deleted_dep - return if dep.type == :development - raise GemfileError, "You cannot specify the same gem twice coming from different sources.\n" \ - "You specified that #{dep.name} (#{dep.requirement}) should come from " \ - "#{current.source || "an unspecified source"} and #{dep.source}\n" - end + return if dep.type == :development + raise GemfileError, "You cannot specify the same gem twice coming from different sources.\n" \ + "You specified that #{dep.name} (#{dep.requirement}) should come from " \ + "#{current.source || "an unspecified source"} and #{dep.source}\n" end end diff --git a/lib/bundler/index.rb b/lib/bundler/index.rb index 36520c0a43d2eb..8930fca6d0fdc1 100644 --- a/lib/bundler/index.rb +++ b/lib/bundler/index.rb @@ -195,11 +195,7 @@ def search_by_dependency(dependency, base = nil) if base # allow all platforms when searching from a lockfile dependency.matches_spec?(spec) else - if Gem::Platform.respond_to? :match_spec? - dependency.matches_spec?(spec) && Gem::Platform.match_spec?(spec) - else - dependency.matches_spec?(spec) && Gem::Platform.match(spec.platform) - end + dependency.matches_spec?(spec) && Gem::Platform.match_spec?(spec) end end diff --git a/lib/bundler/installer/standalone.rb b/lib/bundler/installer/standalone.rb index 2a3f5cfe35855c..f16135cb481d54 100644 --- a/lib/bundler/installer/standalone.rb +++ b/lib/bundler/installer/standalone.rb @@ -3,7 +3,7 @@ module Bundler class Standalone def initialize(groups, definition) - @specs = groups.empty? ? definition.requested_specs : definition.specs_for(groups.map(&:to_sym)) + @specs = definition.specs_for(groups) end def generate diff --git a/lib/bundler/plugin/installer.rb b/lib/bundler/plugin/installer.rb index 4cb7ddf5d69af2..54ce8528cf03f0 100644 --- a/lib/bundler/plugin/installer.rb +++ b/lib/bundler/plugin/installer.rb @@ -77,7 +77,7 @@ def install_all_sources(names, version, git_source_options, rubygems_source) source_list = SourceList.new 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 } diff --git a/lib/bundler/rubygems_ext.rb b/lib/bundler/rubygems_ext.rb index 9828fc885c9488..26132078268204 100644 --- a/lib/bundler/rubygems_ext.rb +++ b/lib/bundler/rubygems_ext.rb @@ -176,20 +176,36 @@ def hash end end + require "rubygems/platform" + class Platform JAVA = Gem::Platform.new("java") unless defined?(JAVA) MSWIN = Gem::Platform.new("mswin32") unless defined?(MSWIN) MSWIN64 = Gem::Platform.new("mswin64") unless defined?(MSWIN64) MINGW = Gem::Platform.new("x86-mingw32") unless defined?(MINGW) X64_MINGW = Gem::Platform.new("x64-mingw32") unless defined?(X64_MINGW) + end - undef_method :hash if method_defined? :hash - def hash - @cpu.hash ^ @os.hash ^ @version.hash - end + Platform.singleton_class.module_eval do + unless Platform.singleton_methods.include?(:match_spec?) + def match_spec?(spec) + match_gem?(spec.platform, spec.name) + end - undef_method :eql? if method_defined? :eql? - alias_method :eql?, :== + 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 require "rubygems/util" diff --git a/lib/bundler/runtime.rb b/lib/bundler/runtime.rb index e259b590bfc748..287fa1cfe94b8b 100644 --- a/lib/bundler/runtime.rb +++ b/lib/bundler/runtime.rb @@ -12,12 +12,10 @@ def initialize(root, definition) def setup(*groups) @definition.ensure_equivalent_gemfile_and_lockfile if Bundler.frozen_bundle? - groups.map!(&:to_sym) - # Has to happen first clean_load_path - specs = groups.any? ? @definition.specs_for(groups) : requested_specs + specs = @definition.specs_for(groups) SharedHelpers.set_bundle_environment Bundler.rubygems.replace_entrypoints(specs) diff --git a/lib/bundler/settings.rb b/lib/bundler/settings.rb index 95da31a76568e7..de42cc16affb40 100644 --- a/lib/bundler/settings.rb +++ b/lib/bundler/settings.rb @@ -428,12 +428,8 @@ def printable_value(value, key) def global_config_file if ENV["BUNDLE_CONFIG"] && !ENV["BUNDLE_CONFIG"].empty? Pathname.new(ENV["BUNDLE_CONFIG"]) - else - begin - Bundler.user_bundle_path("config") - rescue PermissionError, GenericSystemCallError - nil - end + elsif Bundler.rubygems.user_home && !Bundler.rubygems.user_home.empty? + Pathname.new(Bundler.rubygems.user_home).join(".bundle/config") end end diff --git a/lib/bundler/spec_set.rb b/lib/bundler/spec_set.rb index af16984454ee2f..1a8906c47ee41a 100644 --- a/lib/bundler/spec_set.rb +++ b/lib/bundler/spec_set.rb @@ -11,15 +11,14 @@ def initialize(specs) @specs = specs 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 = [] deps = dependencies.dup specs = [] - skip += ["bundler"] loop do 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 @@ -73,7 +72,7 @@ def to_hash end 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| next unless specs.any?{|s| s.is_a?(LazySpecification) } @@ -195,7 +194,7 @@ def tsort_each_node def spec_for_dependency(dep, match_current_platform) specs_for_platforms = lookup[dep.name] 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 GemHelpers.select_best_platform_match(specs_for_platforms, dep.__platform) end diff --git a/lib/bundler/vendor/connection_pool/lib/connection_pool.rb b/lib/bundler/vendor/connection_pool/lib/connection_pool.rb index fbcd26c7655566..984c1c3dcb08cf 100644 --- a/lib/bundler/vendor/connection_pool/lib/connection_pool.rb +++ b/lib/bundler/vendor/connection_pool/lib/connection_pool.rb @@ -1,14 +1,18 @@ -require_relative 'connection_pool/version' -require_relative 'connection_pool/timed_stack' +require "timeout" +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 -# among many threads. Note: Connections are lazily created. +# Generic connection pool class for sharing a limited number of objects or network connections +# among many threads. Note: pool elements are lazily created. # # Example usage with block (faster): # # @pool = Bundler::ConnectionPool.new { Redis.new } -# # @pool.with do |redis| # redis.lpop('my-list') if redis.llen('my-list') > 0 # end @@ -34,29 +38,23 @@ class Bundler::ConnectionPool DEFAULTS = {size: 5, timeout: 5} - class Error < RuntimeError - end - def self.wrap(options, &block) Wrapper.new(options, &block) end 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) - @size = options.fetch(:size) + @size = Integer(options.fetch(:size)) @timeout = options.fetch(:timeout) @available = TimedStack.new(@size, &block) - @key = :"current-#{@available.object_id}" - @key_count = :"current-#{@available.object_id}-count" + @key = :"pool-#{@available.object_id}" + @key_count = :"pool-#{@available.object_id}-count" end -if Thread.respond_to?(:handle_interrupt) - - # MRI def with(options = {}) Thread.handle_interrupt(Exception => :never) do conn = checkout(options) @@ -69,28 +67,15 @@ def with(options = {}) end end end - -else - - # jruby 1.7.x - def with(options = {}) - conn = checkout(options) - begin - yield conn - ensure - checkin - end - end - -end + alias then with def checkout(options = {}) if ::Thread.current[@key] - ::Thread.current[@key_count]+= 1 + ::Thread.current[@key_count] += 1 ::Thread.current[@key] else - ::Thread.current[@key_count]= 1 - ::Thread.current[@key]= @available.pop(options[:timeout] || @timeout) + ::Thread.current[@key_count] = 1 + ::Thread.current[@key] = @available.pop(options[:timeout] || @timeout) end end @@ -98,64 +83,44 @@ def checkin if ::Thread.current[@key] if ::Thread.current[@key_count] == 1 @available.push(::Thread.current[@key]) - ::Thread.current[@key]= nil + ::Thread.current[@key] = nil + ::Thread.current[@key_count] = nil else - ::Thread.current[@key_count]-= 1 + ::Thread.current[@key_count] -= 1 end else - raise Bundler::ConnectionPool::Error, 'no connections are checked out' + raise Bundler::ConnectionPool::Error, "no connections are checked out" end nil 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) @available.shutdown(&block) end - # Size of this connection pool - def size - @size + ## + # Reloads the Bundler::ConnectionPool by passing each connection to +block+ and then + # removing it the pool. Subsequent checkouts will create new connections as + # needed. + + def reload(&block) + @available.shutdown(reload: true, &block) end + # Size of this connection pool + attr_reader :size + # Number of pool entries available for checkout at this instant. def available @available.length end - - private - - class Wrapper < ::BasicObject - METHODS = [:with, :pool_shutdown] - - def initialize(options = {}, &block) - @pool = options.fetch(:pool) { ::Bundler::ConnectionPool.new(options, &block) } - 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 - - def method_missing(name, *args, &block) - with do |connection| - connection.send(name, *args, &block) - end - end - end end + +require_relative "connection_pool/timed_stack" +require_relative "connection_pool/wrapper" diff --git a/lib/bundler/vendor/connection_pool/lib/connection_pool/monotonic_time.rb b/lib/bundler/vendor/connection_pool/lib/connection_pool/monotonic_time.rb deleted file mode 100644 index 8210ab4c415993..00000000000000 --- a/lib/bundler/vendor/connection_pool/lib/connection_pool/monotonic_time.rb +++ /dev/null @@ -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 diff --git a/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb b/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb index cff8477c1fd36d..a7b1cf06a8f8df 100644 --- a/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb +++ b/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb @@ -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 # 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 # ts.pop timeout: 5 -# #=> raises Timeout::Error after 5 seconds +# #=> raises Bundler::ConnectionPool::TimeoutError after 5 seconds class Bundler::ConnectionPool::TimedStack attr_reader :max @@ -59,12 +49,12 @@ def push(obj, options = {}) @resource.broadcast end end - alias_method :<<, :push + alias << push ## # Retrieves a connection from the stack. If a connection is available it is # 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 # the +timeout+ argument (which will be removed in a future release). Other @@ -74,7 +64,7 @@ def pop(timeout = 0.5, options = {}) options, timeout = timeout, 0.5 if Hash === timeout timeout = options.fetch :timeout, timeout - deadline = Bundler::ConnectionPool.monotonic_time + timeout + deadline = current_time + timeout @mutex.synchronize do loop do raise Bundler::ConnectionPool::PoolShuttingDownError if @shutdown_block @@ -83,18 +73,20 @@ def pop(timeout = 0.5, options = {}) connection = try_create(options) return connection if connection - to_wait = deadline - Bundler::ConnectionPool.monotonic_time - raise Timeout::Error, "Waited #{timeout} sec" if to_wait <= 0 + to_wait = deadline - current_time + raise Bundler::ConnectionPool::TimeoutError, "Waited #{timeout} sec" if to_wait <= 0 @resource.wait(@mutex, to_wait) end end end ## - # Shuts down the TimedStack which prevents connections from being checked - # out. The +block+ is called once for each connection on the stack. + # Shuts down the TimedStack 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+ unless + # +:reload+ is +true+. - def shutdown(&block) + def shutdown(reload: false, &block) raise ArgumentError, "shutdown must receive a block" unless block_given? @mutex.synchronize do @@ -102,6 +94,7 @@ def shutdown(&block) @resource.broadcast shutdown_connections + @shutdown_block = nil if reload end end @@ -121,6 +114,10 @@ def length private + def current_time + Process.clock_gettime(Process::CLOCK_MONOTONIC) + end + ## # This is an extension point for TimedStack and is called with a mutex. # @@ -149,6 +146,7 @@ def shutdown_connections(options = nil) conn = fetch_connection(options) @shutdown_block.call(conn) end + @created = 0 end ## diff --git a/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb b/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb index b149c0e242e87d..56ebf69902f9c0 100644 --- a/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb +++ b/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb @@ -1,3 +1,3 @@ class Bundler::ConnectionPool - VERSION = "2.2.2" + VERSION = "2.3.0" end diff --git a/lib/bundler/vendor/connection_pool/lib/connection_pool/wrapper.rb b/lib/bundler/vendor/connection_pool/lib/connection_pool/wrapper.rb new file mode 100644 index 00000000000000..880170c06b04cd --- /dev/null +++ b/lib/bundler/vendor/connection_pool/lib/connection_pool/wrapper.rb @@ -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 diff --git a/lib/bundler/vendor/uri/lib/uri.rb b/lib/bundler/vendor/uri/lib/uri.rb index 00c01bd07bf949..0e574dd2b191f7 100644 --- a/lib/bundler/vendor/uri/lib/uri.rb +++ b/lib/bundler/vendor/uri/lib/uri.rb @@ -86,7 +86,6 @@ # License:: # Copyright (c) 2001 akira yamada # You can redistribute it and/or modify it under the same term as Ruby. -# Revision:: $Id$ # module Bundler::URI diff --git a/lib/bundler/vendor/uri/lib/uri/common.rb b/lib/bundler/vendor/uri/lib/uri/common.rb index cc1ab86c2f4eb1..6539e1810f990b 100644 --- a/lib/bundler/vendor/uri/lib/uri/common.rb +++ b/lib/bundler/vendor/uri/lib/uri/common.rb @@ -3,7 +3,6 @@ # = uri/common.rb # # Author:: Akira Yamada -# Revision:: $Id$ # License:: # You can redistribute it and/or modify it under the same term as Ruby. # @@ -61,82 +60,6 @@ def make_components_hash(klass, array_hash) module_function :make_components_hash 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 UNSAFE. - # 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 @@schemes = {} @@ -145,6 +68,20 @@ def self.scheme_list @@schemes 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. # @@ -315,7 +252,7 @@ def self.extract(str, schemes = nil, &block) # # Returns a Regexp object which matches to Bundler::URI-like strings. # 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 # @@ -362,7 +299,7 @@ def self.regexp(schemes = nil) # If +enc+ is given, convert +str+ to the encoding before percent encoding. # # 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. def self.encode_www_form_component(str, enc=nil) @@ -403,7 +340,7 @@ def self.decode_www_form_component(str, enc=Encoding::UTF_8) # This method doesn't handle files. When you send a file, use # 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"]]) # #=> "q=ruby&lang=en" diff --git a/lib/bundler/vendor/uri/lib/uri/ftp.rb b/lib/bundler/vendor/uri/lib/uri/ftp.rb index ad39f57d7b6ee4..2252e405d58f12 100644 --- a/lib/bundler/vendor/uri/lib/uri/ftp.rb +++ b/lib/bundler/vendor/uri/lib/uri/ftp.rb @@ -3,7 +3,6 @@ # # Author:: Akira Yamada # License:: You can redistribute it and/or modify it under the same term as Ruby. -# Revision:: $Id$ # # See Bundler::URI for general documentation # diff --git a/lib/bundler/vendor/uri/lib/uri/generic.rb b/lib/bundler/vendor/uri/lib/uri/generic.rb index 56b09e1d7f79ff..f29ba6cf18b757 100644 --- a/lib/bundler/vendor/uri/lib/uri/generic.rb +++ b/lib/bundler/vendor/uri/lib/uri/generic.rb @@ -4,7 +4,6 @@ # # Author:: Akira Yamada # License:: You can redistribute it and/or modify it under the same term as Ruby. -# Revision:: $Id$ # # See Bundler::URI for general documentation # @@ -1098,7 +1097,7 @@ def merge!(oth) # # => "http://my.example.com/main.rbx?page=1" # def merge(oth) - rel = parser.send(:convert_to_uri, oth) + rel = parser.__send__(:convert_to_uri, oth) if rel.absolute? #raise BadURIError, "both Bundler::URI are absolute" if absolute? @@ -1183,7 +1182,7 @@ def route_from_path(src, dst) # :stopdoc: def route_from0(oth) - oth = parser.send(:convert_to_uri, oth) + oth = parser.__send__(:convert_to_uri, oth) if self.relative? raise BadURIError, "relative Bundler::URI: #{self}" @@ -1291,7 +1290,7 @@ def route_from(oth) # #=> # # def route_to(oth) - parser.send(:convert_to_uri, oth).route_from(self) + parser.__send__(:convert_to_uri, oth).route_from(self) end # @@ -1405,7 +1404,7 @@ def eql?(oth) # Returns an Array of the components defined from the COMPONENT Array. def component_ary component.collect do |x| - self.send(x) + self.__send__(x) end end protected :component_ary @@ -1430,7 +1429,7 @@ def component_ary def select(*components) components.collect do |c| if component.include?(c) - self.send(c) + self.__send__(c) else raise ArgumentError, "expected of components of #{self.class} (#{self.class.component.join(', ')})" diff --git a/lib/bundler/vendor/uri/lib/uri/http.rb b/lib/bundler/vendor/uri/lib/uri/http.rb index b6ca1c51de3226..50d7e427a52b70 100644 --- a/lib/bundler/vendor/uri/lib/uri/http.rb +++ b/lib/bundler/vendor/uri/lib/uri/http.rb @@ -3,7 +3,6 @@ # # Author:: Akira Yamada # License:: You can redistribute it and/or modify it under the same term as Ruby. -# Revision:: $Id$ # # See Bundler::URI for general documentation # diff --git a/lib/bundler/vendor/uri/lib/uri/https.rb b/lib/bundler/vendor/uri/lib/uri/https.rb index 78dc6bf532d2fb..4fd4e9af7bbabc 100644 --- a/lib/bundler/vendor/uri/lib/uri/https.rb +++ b/lib/bundler/vendor/uri/lib/uri/https.rb @@ -3,7 +3,6 @@ # # Author:: Akira Yamada # License:: You can redistribute it and/or modify it under the same term as Ruby. -# Revision:: $Id$ # # See Bundler::URI for general documentation # diff --git a/lib/bundler/vendor/uri/lib/uri/ldap.rb b/lib/bundler/vendor/uri/lib/uri/ldap.rb index b707bedb971ad6..6e9e1918f63464 100644 --- a/lib/bundler/vendor/uri/lib/uri/ldap.rb +++ b/lib/bundler/vendor/uri/lib/uri/ldap.rb @@ -7,7 +7,6 @@ # License:: # 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. -# Revision:: $Id$ # # See Bundler::URI for general documentation # @@ -119,6 +118,7 @@ def initialize(*arg) # Private method to cleanup +dn+ from using the +path+ component attribute. def parse_dn + raise InvalidURIError, 'bad LDAP URL' unless @path @dn = @path[1..-1] end private :parse_dn diff --git a/lib/bundler/vendor/uri/lib/uri/mailto.rb b/lib/bundler/vendor/uri/lib/uri/mailto.rb index 5b2a4765c812d4..ff7ab7e114cd90 100644 --- a/lib/bundler/vendor/uri/lib/uri/mailto.rb +++ b/lib/bundler/vendor/uri/lib/uri/mailto.rb @@ -3,7 +3,6 @@ # # Author:: Akira Yamada # License:: You can redistribute it and/or modify it under the same term as Ruby. -# Revision:: $Id$ # # See Bundler::URI for general documentation # diff --git a/lib/bundler/vendor/uri/lib/uri/rfc2396_parser.rb b/lib/bundler/vendor/uri/lib/uri/rfc2396_parser.rb index a0d62ede640e14..e48e164f4c13e7 100644 --- a/lib/bundler/vendor/uri/lib/uri/rfc2396_parser.rb +++ b/lib/bundler/vendor/uri/lib/uri/rfc2396_parser.rb @@ -3,7 +3,6 @@ # = uri/common.rb # # Author:: Akira Yamada -# Revision:: $Id$ # License:: # You can redistribute it and/or modify it under the same term as Ruby. # @@ -208,21 +207,9 @@ def split(uri) # #=> # # def parse(uri) - scheme, userinfo, host, port, - 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 + Bundler::URI.for(*self.split(uri), self) end - # # == Args # diff --git a/lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb b/lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb index 07ef4391c02b46..2029cfd05677db 100644 --- a/lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb +++ b/lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb @@ -69,18 +69,7 @@ def split(uri) #:nodoc: end def parse(uri) # :nodoc: - scheme, userinfo, host, port, - 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 + Bundler::URI.for(*self.split(uri), self) end diff --git a/lib/bundler/vendor/uri/lib/uri/version.rb b/lib/bundler/vendor/uri/lib/uri/version.rb index 56177ef1949d50..f2bb0ebad26db2 100644 --- a/lib/bundler/vendor/uri/lib/uri/version.rb +++ b/lib/bundler/vendor/uri/lib/uri/version.rb @@ -1,6 +1,6 @@ module Bundler::URI # :stopdoc: - VERSION_CODE = '001000'.freeze + VERSION_CODE = '001001'.freeze VERSION = VERSION_CODE.scan(/../).collect{|n| n.to_i}.join('.').freeze # :startdoc: end diff --git a/lib/bundler/vendor/uri/lib/uri/ws.rb b/lib/bundler/vendor/uri/lib/uri/ws.rb new file mode 100644 index 00000000000000..58e08bf98e7f6e --- /dev/null +++ b/lib/bundler/vendor/uri/lib/uri/ws.rb @@ -0,0 +1,84 @@ +# frozen_string_literal: false +# = uri/ws.rb +# +# Author:: Matt Muller +# 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 . + # + 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 [userinfo, host, port, path, query]. + # + # 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 diff --git a/lib/bundler/vendor/uri/lib/uri/wss.rb b/lib/bundler/vendor/uri/lib/uri/wss.rb new file mode 100644 index 00000000000000..3827053c7bd36c --- /dev/null +++ b/lib/bundler/vendor/uri/lib/uri/wss.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: false +# = uri/wss.rb +# +# Author:: Matt Muller +# 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 diff --git a/lib/rubygems/exceptions.rb b/lib/rubygems/exceptions.rb index 55755ddfba31cd..185efdb6ab58dc 100644 --- a/lib/rubygems/exceptions.rb +++ b/lib/rubygems/exceptions.rb @@ -225,7 +225,7 @@ class Gem::SystemExitException < SystemExit def initialize(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 diff --git a/lib/rubygems/gemcutter_utilities.rb b/lib/rubygems/gemcutter_utilities.rb index 00e68916c4202a..f4658810416eca 100644 --- a/lib/rubygems/gemcutter_utilities.rb +++ b/lib/rubygems/gemcutter_utilities.rb @@ -31,7 +31,8 @@ def add_key_option def add_otp_option 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 end end diff --git a/lib/rubygems/package/io_source.rb b/lib/rubygems/package/io_source.rb index 7d7383110b93d3..03d7714524e584 100644 --- a/lib/rubygems/package/io_source.rb +++ b/lib/rubygems/package/io_source.rb @@ -32,10 +32,14 @@ def present? def with_read_io yield io + ensure + io.rewind end def with_write_io yield io + ensure + io.rewind end def path diff --git a/spec/bundler/bundler/bundler_spec.rb b/spec/bundler/bundler/bundler_spec.rb index 56ef4ce75ad46b..d164b5d3c69980 100644 --- a/spec/bundler/bundler/bundler_spec.rb +++ b/spec/bundler/bundler/bundler_spec.rb @@ -249,11 +249,8 @@ allow(Bundler.rubygems).to receive(:user_home).and_return(path) allow(File).to receive(:directory?).with(path).and_return false allow(Bundler).to receive(:tmp).and_return(Pathname.new("/tmp/trulyrandom")) - message = <= 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 before do gemfile <<~G diff --git a/spec/bundler/commands/exec_spec.rb b/spec/bundler/commands/exec_spec.rb index 0d104ad30456dc..39430d52a4987d 100644 --- a/spec/bundler/commands/exec_spec.rb +++ b/spec/bundler/commands/exec_spec.rb @@ -24,6 +24,16 @@ expect(out).to eq("0.9.1") 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 install_gemfile <<-G gem "rack" diff --git a/spec/bundler/commands/install_spec.rb b/spec/bundler/commands/install_spec.rb index c91864dbb82f3e..3c0c35d84484a6 100644 --- a/spec/bundler/commands/install_spec.rb +++ b/spec/bundler/commands/install_spec.rb @@ -344,54 +344,72 @@ expect(File.exist?(bundled_app_lock)).to eq(true) end - context "throws a warning if a gem is added twice in Gemfile" do - it "without version requirements" do - install_gemfile <<-G, :raise_on_error => false - source "#{file_uri_for(gem_repo2)}" - gem "rack" - gem "rack" - G + it "throws a warning if a gem is added twice in Gemfile without version requirements" do + install_gemfile <<-G, :raise_on_error => false + source "#{file_uri_for(gem_repo2)}" + gem "rack" + gem "rack" + G - expect(err).to include("Your Gemfile lists the gem rack (>= 0) more than 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.") - end + expect(err).to include("Your Gemfile lists the gem rack (>= 0) more than 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.") + end - it "with same versions" do - install_gemfile <<-G, :raise_on_error => false - source "#{file_uri_for(gem_repo2)}" - gem "rack", "1.0" - gem "rack", "1.0" - G + it "throws a warning if a gem is added twice in Gemfile with same versions" do + install_gemfile <<-G, :raise_on_error => false + source "#{file_uri_for(gem_repo2)}" + gem "rack", "1.0" + gem "rack", "1.0" + G - expect(err).to include("Your Gemfile lists the gem rack (= 1.0) more than 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.") - end + expect(err).to include("Your Gemfile lists the gem rack (= 1.0) more than 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.") end - context "throws an error if a gem is added twice in Gemfile" do - it "when version of one dependency is not specified" do - install_gemfile <<-G, :raise_on_error => false - source "#{file_uri_for(gem_repo2)}" - gem "rack" - gem "rack", "1.0" - G + 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 - expect(err).to include("You cannot specify the same gem twice with different version requirements") - expect(err).to include("You specified: rack (>= 0) and rack (= 1.0).") + build_repo2 do + build_gem "my-private-gem" end - it "when different versions of both dependencies are specified" do - install_gemfile <<-G, :raise_on_error => false - source "#{file_uri_for(gem_repo2)}" - gem "rack", "1.0" - gem "rack", "1.1" - G + gemfile <<~G + source "#{file_uri_for(gem_repo2)}" - 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).") - end + 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 + source "#{file_uri_for(gem_repo2)}" + gem "rack" + gem "rack", "1.0" + G + + expect(err).to include("You cannot specify the same gem twice with different version requirements") + expect(err).to include("You specified: rack (>= 0) and rack (= 1.0).") + end + + 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 + source "#{file_uri_for(gem_repo2)}" + gem "rack", "1.0" + gem "rack", "1.1" + G + + 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).") end it "gracefully handles error when rubygems server is unavailable" do diff --git a/spec/bundler/commands/newgem_spec.rb b/spec/bundler/commands/newgem_spec.rb index 00bd009c5a8403..56fea008eccf15 100644 --- a/spec/bundler/commands/newgem_spec.rb +++ b/spec/bundler/commands/newgem_spec.rb @@ -307,42 +307,42 @@ def bundle_exec_standardrb 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_exec_rubocop - expect(err).to be_empty + expect(last_command).to be_success end 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? bundle "gem #{gem_name} --ext --linter=rubocop" bundle_exec_rubocop - expect(err).to be_empty + expect(last_command).to be_success end 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? bundle "gem #{gem_name} --ext --test=minitest --linter=rubocop" bundle_exec_rubocop - expect(err).to be_empty + expect(last_command).to be_success end 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? bundle "gem #{gem_name} --ext --test=rspec --linter=rubocop" bundle_exec_rubocop - expect(err).to be_empty + expect(last_command).to be_success end 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? bundle "gem #{gem_name} --ext --test=test-unit --linter=rubocop" bundle_exec_rubocop - expect(err).to be_empty + expect(last_command).to be_success end 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? bundle "gem #{gem_name} --linter=standard" bundle_exec_standardrb - expect(err).to be_empty + expect(last_command).to be_success end shared_examples_for "CI config is absent" do diff --git a/spec/bundler/install/gemfile/specific_platform_spec.rb b/spec/bundler/install/gemfile/specific_platform_spec.rb index c6e526a95ed1f2..a6d8318fe4b66e 100644 --- a/spec/bundler/install/gemfile/specific_platform_spec.rb +++ b/spec/bundler/install/gemfile/specific_platform_spec.rb @@ -249,6 +249,38 @@ 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 def setup_multiplatform_gem diff --git a/spec/bundler/plugins/install_spec.rb b/spec/bundler/plugins/install_spec.rb index 308f9c79fc3a22..2175610b103182 100644 --- a/spec/bundler/plugins/install_spec.rb +++ b/spec/bundler/plugins/install_spec.rb @@ -22,6 +22,13 @@ plugin_should_be_installed("foo") 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 before do bundle "plugin install foo --source #{file_uri_for(gem_repo2)}" diff --git a/spec/bundler/realworld/edgecases_spec.rb b/spec/bundler/realworld/edgecases_spec.rb index 556a11d2f32947..f031e2f354a67e 100644 --- a/spec/bundler/realworld/edgecases_spec.rb +++ b/spec/bundler/realworld/edgecases_spec.rb @@ -196,21 +196,6 @@ def rubygems_version(name, requirement) expect(lockfile).to include(rubygems_version("paperclip", "~> 5.1.0")) 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 install_gemfile <<-G, :standalone => true, :raise_on_error => false source 'https://rubygems.org' diff --git a/spec/bundler/runtime/setup_spec.rb b/spec/bundler/runtime/setup_spec.rb index d8ba569f0abb96..380db991364d53 100644 --- a/spec/bundler/runtime/setup_spec.rb +++ b/spec/bundler/runtime/setup_spec.rb @@ -644,6 +644,25 @@ def clean_load_path(lp) expect(err).to be_empty 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 bundle "config set --local without rails" install_gemfile <<-G diff --git a/spec/bundler/support/artifice/vcr.rb b/spec/bundler/support/artifice/vcr.rb index 88c33d93dcac5b..0d51201bef837a 100644 --- a/spec/bundler/support/artifice/vcr.rb +++ b/spec/bundler/support/artifice/vcr.rb @@ -133,6 +133,19 @@ def binwrite(path, contents) 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) handler = request.instance_eval do remove_instance_variable(:@__vcr_request_handler) if defined?(@__vcr_request_handler) diff --git a/spec/bundler/support/hax.rb b/spec/bundler/support/hax.rb index ddb62f7d531162..aaf8c748940251 100644 --- a/spec/bundler/support/hax.rb +++ b/spec/bundler/support/hax.rb @@ -28,6 +28,10 @@ def finish_resolve 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 if Gem.rubygems_version < Gem::Version.new("2.7.0") @path_to_default_spec_map.delete_if do |_path, spec| diff --git a/test/rubygems/test_exit.rb b/test/rubygems/test_exit.rb new file mode 100644 index 00000000000000..9557fe5d063131 --- /dev/null +++ b/test/rubygems/test_exit.rb @@ -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 diff --git a/test/rubygems/test_gem_package.rb b/test/rubygems/test_gem_package.rb index dbb037fd80ed91..3fa2c1911c45ef 100644 --- a/test/rubygems/test_gem_package.rb +++ b/test/rubygems/test_gem_package.rb @@ -1145,6 +1145,13 @@ def test_spec_from_io_raises_gem_error_for_io_not_at_start 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 tar_io = StringIO.new