bundler 2.2.26 → 2.2.30

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of bundler might be problematic. Click here for more details.

Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +50 -1
  3. data/README.md +1 -1
  4. data/lib/bundler/build_metadata.rb +2 -2
  5. data/lib/bundler/cli/check.rb +1 -1
  6. data/lib/bundler/cli/gem.rb +19 -2
  7. data/lib/bundler/cli/info.rb +11 -4
  8. data/lib/bundler/cli/issue.rb +4 -3
  9. data/lib/bundler/cli/remove.rb +1 -2
  10. data/lib/bundler/cli.rb +1 -0
  11. data/lib/bundler/compact_index_client.rb +2 -2
  12. data/lib/bundler/definition.rb +16 -6
  13. data/lib/bundler/digest.rb +71 -0
  14. data/lib/bundler/errors.rb +18 -2
  15. data/lib/bundler/fetcher.rb +2 -1
  16. data/lib/bundler/friendly_errors.rb +5 -30
  17. data/lib/bundler/gem_helper.rb +6 -17
  18. data/lib/bundler/installer.rb +0 -1
  19. data/lib/bundler/plugin/installer.rb +2 -0
  20. data/lib/bundler/plugin.rb +23 -6
  21. data/lib/bundler/rubygems_ext.rb +4 -0
  22. data/lib/bundler/rubygems_gem_installer.rb +20 -4
  23. data/lib/bundler/rubygems_integration.rb +28 -9
  24. data/lib/bundler/runtime.rb +1 -1
  25. data/lib/bundler/settings.rb +9 -1
  26. data/lib/bundler/source/git.rb +22 -4
  27. data/lib/bundler/source/rubygems.rb +43 -72
  28. data/lib/bundler/source.rb +2 -0
  29. data/lib/bundler/source_list.rb +4 -0
  30. data/lib/bundler/spec_set.rb +1 -1
  31. data/lib/bundler/templates/newgem/github/workflows/main.yml.tt +2 -1
  32. data/lib/bundler/templates/newgem/newgem.gemspec.tt +1 -1
  33. data/lib/bundler/vendor/connection_pool/LICENSE +20 -0
  34. data/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb +19 -21
  35. data/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb +1 -1
  36. data/lib/bundler/vendor/connection_pool/lib/connection_pool/wrapper.rb +57 -0
  37. data/lib/bundler/vendor/connection_pool/lib/connection_pool.rb +39 -74
  38. data/lib/bundler/vendor/fileutils/LICENSE.txt +22 -0
  39. data/lib/bundler/vendor/molinillo/LICENSE +9 -0
  40. data/lib/bundler/vendor/net-http-persistent/README.rdoc +82 -0
  41. data/lib/bundler/vendor/thor/LICENSE.md +20 -0
  42. data/lib/bundler/vendor/uri/LICENSE.txt +22 -0
  43. data/lib/bundler/version.rb +1 -1
  44. data/lib/bundler/worker.rb +2 -2
  45. data/lib/bundler.rb +13 -18
  46. metadata +14 -7
  47. data/lib/bundler/vendor/connection_pool/lib/connection_pool/monotonic_time.rb +0 -66
@@ -16,10 +16,12 @@ module Bundler
16
16
  spec.loaded_from = spec_file
17
17
 
18
18
  # Completely remove any previous gem files
19
- FileUtils.rm_rf gem_dir
20
- FileUtils.rm_rf spec.extension_dir
19
+ strict_rm_rf gem_dir
20
+ strict_rm_rf spec.extension_dir
21
21
 
22
- FileUtils.mkdir_p gem_dir, :mode => 0o755
22
+ SharedHelpers.filesystem_access(gem_dir, :create) do
23
+ FileUtils.mkdir_p gem_dir, :mode => 0o755
24
+ end
23
25
 
24
26
  extract_files
25
27
 
@@ -31,7 +33,10 @@ module Bundler
31
33
  generate_plugins
32
34
 
33
35
  write_spec
34
- write_cache_file
36
+
37
+ SharedHelpers.filesystem_access("#{gem_home}/cache", :write) do
38
+ write_cache_file
39
+ end
35
40
 
36
41
  say spec.post_install_message unless spec.post_install_message.nil?
37
42
 
@@ -87,6 +92,17 @@ module Bundler
87
92
 
88
93
  private
89
94
 
95
+ def strict_rm_rf(dir)
96
+ # FileUtils.rm_rf should probably rise in case of permission issues like
97
+ # `rm -rf` does. However, it fails to delete the folder silently due to
98
+ # https://github.com/ruby/fileutils/issues/57. It should probably be fixed
99
+ # inside `fileutils` but for now I`m checking whether the folder was
100
+ # removed after it completes, and raising otherwise.
101
+ FileUtils.rm_rf dir
102
+
103
+ raise PermissionError.new(dir, :delete) if File.directory?(dir)
104
+ end
105
+
90
106
  def validate_bundler_checksum(checksum)
91
107
  return true if Bundler.settings[:disable_checksum_validation]
92
108
  return true unless checksum
@@ -34,10 +34,12 @@ module Bundler
34
34
  end
35
35
 
36
36
  def build_args
37
+ require "rubygems/command"
37
38
  Gem::Command.build_args
38
39
  end
39
40
 
40
41
  def build_args=(args)
42
+ require "rubygems/command"
41
43
  Gem::Command.build_args = args
42
44
  end
43
45
 
@@ -84,16 +86,12 @@ module Bundler
84
86
  def spec_missing_extensions?(spec, default = true)
85
87
  return spec.missing_extensions? if spec.respond_to?(:missing_extensions?)
86
88
 
87
- return false if spec_default_gem?(spec)
89
+ return false if spec.default_gem?
88
90
  return false if spec.extensions.empty?
89
91
 
90
92
  default
91
93
  end
92
94
 
93
- def spec_default_gem?(spec)
94
- spec.respond_to?(:default_gem?) && spec.default_gem?
95
- end
96
-
97
95
  def spec_matches_for_glob(spec, glob)
98
96
  return spec.matches_for_glob(glob) if spec.respond_to?(:matches_for_glob)
99
97
 
@@ -502,14 +500,15 @@ module Bundler
502
500
  end
503
501
 
504
502
  def fetch_specs(remote, name)
503
+ require "rubygems/remote_fetcher"
505
504
  path = remote.uri.to_s + "#{name}.#{Gem.marshal_version}.gz"
506
505
  fetcher = gem_remote_fetcher
507
506
  fetcher.headers = { "X-Gemfile-Source" => remote.original_uri.to_s } if remote.original_uri
508
507
  string = fetcher.fetch_path(path)
509
508
  Bundler.load_marshal(string)
510
- rescue Gem::RemoteFetcher::FetchError => e
509
+ rescue Gem::RemoteFetcher::FetchError
511
510
  # it's okay for prerelease to fail
512
- raise e unless name == "prerelease_specs"
511
+ raise unless name == "prerelease_specs"
513
512
  end
514
513
 
515
514
  def fetch_all_remote_specs(remote)
@@ -519,12 +518,32 @@ module Bundler
519
518
  specs.concat(pres)
520
519
  end
521
520
 
522
- def download_gem(spec, uri, path)
521
+ def download_gem(spec, uri, cache_dir)
522
+ require "rubygems/remote_fetcher"
523
523
  uri = Bundler.settings.mirror_for(uri)
524
524
  fetcher = gem_remote_fetcher
525
525
  fetcher.headers = { "X-Gemfile-Source" => spec.remote.original_uri.to_s } if spec.remote.original_uri
526
526
  Bundler::Retry.new("download gem from #{uri}").attempts do
527
- fetcher.download(spec, uri, path)
527
+ gem_file_name = spec.file_name
528
+ local_gem_path = File.join cache_dir, gem_file_name
529
+ return if File.exist? local_gem_path
530
+
531
+ begin
532
+ remote_gem_path = uri + "gems/#{gem_file_name}"
533
+ remote_gem_path = remote_gem_path.to_s if provides?("< 3.2.0.rc.1")
534
+
535
+ SharedHelpers.filesystem_access(local_gem_path) do
536
+ fetcher.cache_update_path remote_gem_path, local_gem_path
537
+ end
538
+ rescue Gem::RemoteFetcher::FetchError
539
+ raise if spec.original_platform == spec.platform
540
+
541
+ original_gem_file_name = "#{spec.original_name}.gem"
542
+ raise if gem_file_name == original_gem_file_name
543
+
544
+ gem_file_name = original_gem_file_name
545
+ retry
546
+ end
528
547
  end
529
548
  rescue Gem::RemoteFetcher::FetchError => e
530
549
  raise Bundler::HTTPError, "Could not download gem from #{uri} due to underlying error <#{e.message}>"
@@ -291,7 +291,7 @@ module Bundler
291
291
  return unless activated_spec = Bundler.rubygems.loaded_specs(spec.name)
292
292
  return if activated_spec.version == spec.version
293
293
 
294
- suggestion = if Bundler.rubygems.spec_default_gem?(activated_spec)
294
+ suggestion = if activated_spec.default_gem?
295
295
  "Since #{spec.name} is a default gem, you can either remove your dependency on it" \
296
296
  " or try updating to a newer version of bundler that supports #{spec.name} as a default gem."
297
297
  else
@@ -419,7 +419,15 @@ module Bundler
419
419
  elsif is_credential(key)
420
420
  "[REDACTED]"
421
421
  elsif is_userinfo(converted)
422
- converted.gsub(/:.*$/, ":[REDACTED]")
422
+ username, pass = converted.split(":", 2)
423
+
424
+ if pass == "x-oauth-basic"
425
+ username = "[REDACTED]"
426
+ else
427
+ pass = "[REDACTED]"
428
+ end
429
+
430
+ [username, pass].join(":")
423
431
  else
424
432
  converted
425
433
  end
@@ -42,7 +42,7 @@ module Bundler
42
42
  %w[ref branch tag submodules].each do |opt|
43
43
  out << " #{opt}: #{options[opt]}\n" if options[opt]
44
44
  end
45
- out << " glob: #{@glob}\n" unless @glob == DEFAULT_GLOB
45
+ out << " glob: #{@glob}\n" unless default_glob?
46
46
  out << " specs:\n"
47
47
  end
48
48
 
@@ -75,12 +75,20 @@ module Bundler
75
75
  git_proxy.branch
76
76
  end
77
77
 
78
- rev = " (at #{at}@#{shortref_for_display(revision)})"
78
+ rev = "at #{at}@#{shortref_for_display(revision)}"
79
79
  rescue GitError
80
80
  ""
81
81
  end
82
82
 
83
- "#{@safe_uri}#{rev}"
83
+ specifiers = [rev, glob_for_display].compact
84
+ suffix =
85
+ if specifiers.any?
86
+ " (#{specifiers.join(", ")})"
87
+ else
88
+ ""
89
+ end
90
+
91
+ "#{@safe_uri}#{suffix}"
84
92
  end
85
93
 
86
94
  def name
@@ -282,6 +290,14 @@ module Bundler
282
290
  ref[0..11]
283
291
  end
284
292
 
293
+ def glob_for_display
294
+ default_glob? ? nil : "glob: #{@glob}"
295
+ end
296
+
297
+ def default_glob?
298
+ @glob == DEFAULT_GLOB
299
+ end
300
+
285
301
  def uri_hash
286
302
  if uri =~ %r{^\w+://(\w+@)?}
287
303
  # Downcase the domain component of the URI
@@ -291,7 +307,9 @@ module Bundler
291
307
  # If there is no URI scheme, assume it is an ssh/git URI
292
308
  input = uri
293
309
  end
294
- SharedHelpers.digest(:SHA1).hexdigest(input)
310
+ # We use SHA1 here for historical reason and to preserve backward compatibility.
311
+ # But a transition to a simpler mangling algorithm would be welcome.
312
+ Bundler::Digest.sha1(input)
295
313
  end
296
314
 
297
315
  def cached_revision
@@ -26,6 +26,13 @@ module Bundler
26
26
  Array(options["remotes"]).reverse_each {|r| add_remote(r) }
27
27
  end
28
28
 
29
+ def local_only!
30
+ @specs = nil
31
+ @allow_local = true
32
+ @allow_cached = false
33
+ @allow_remote = false
34
+ end
35
+
29
36
  def local!
30
37
  return if @allow_local
31
38
 
@@ -128,7 +135,7 @@ module Bundler
128
135
  force = opts[:force]
129
136
  ensure_builtin_gems_cached = opts[:ensure_builtin_gems_cached]
130
137
 
131
- if ensure_builtin_gems_cached && builtin_gem?(spec)
138
+ if ensure_builtin_gems_cached && spec.default_gem?
132
139
  if !cached_path(spec)
133
140
  cached_built_in_gem(spec) unless spec.remote
134
141
  force = true
@@ -137,7 +144,7 @@ module Bundler
137
144
  end
138
145
  end
139
146
 
140
- if (installed?(spec) || Plugin.installed?(spec.name)) && !force
147
+ if installed?(spec) && !force
141
148
  print_using_message "Using #{version_message(spec)}"
142
149
  return nil # no post-install message
143
150
  end
@@ -167,6 +174,7 @@ module Bundler
167
174
  Bundler.ui.confirm message
168
175
 
169
176
  path = cached_gem(spec)
177
+ raise GemNotFound, "Could not find #{spec.file_name} for installation" unless path
170
178
  if requires_sudo?
171
179
  install_path = Bundler.tmp(spec.full_name)
172
180
  bin_path = install_path.join("bin")
@@ -226,12 +234,8 @@ module Bundler
226
234
  end
227
235
 
228
236
  def cache(spec, custom_path = nil)
229
- if builtin_gem?(spec)
230
- cached_path = cached_built_in_gem(spec)
231
- else
232
- cached_path = cached_gem(spec)
233
- end
234
- raise GemNotFound, "Missing gem file '#{spec.full_name}.gem'." unless cached_path
237
+ cached_path = cached_gem(spec)
238
+ raise GemNotFound, "Missing gem file '#{spec.file_name}'." unless cached_path
235
239
  return if File.dirname(cached_path) == Bundler.app_cache.to_s
236
240
  Bundler.ui.info " * #{File.basename(cached_path)}"
237
241
  FileUtils.cp(cached_path, Bundler.app_cache(custom_path))
@@ -345,14 +349,17 @@ module Bundler
345
349
  end
346
350
 
347
351
  def cached_gem(spec)
348
- cached_gem = cached_path(spec)
349
- unless cached_gem
350
- raise Bundler::GemNotFound, "Could not find #{spec.file_name} for installation"
352
+ if spec.default_gem?
353
+ cached_built_in_gem(spec)
354
+ else
355
+ cached_path(spec)
351
356
  end
352
- cached_gem
353
357
  end
354
358
 
355
359
  def cached_path(spec)
360
+ global_cache_path = download_cache_path(spec)
361
+ @caches << global_cache_path if global_cache_path
362
+
356
363
  possibilities = @caches.map {|p| "#{p}/#{spec.file_name}" }
357
364
  possibilities.find {|p| File.exist?(p) }
358
365
  end
@@ -452,19 +459,26 @@ module Bundler
452
459
 
453
460
  spec.fetch_platform
454
461
 
455
- download_path = requires_sudo? ? Bundler.tmp(spec.full_name) : rubygems_dir
456
- gem_path = "#{rubygems_dir}/cache/#{spec.full_name}.gem"
462
+ cache_path = download_cache_path(spec) || default_cache_path_for(rubygems_dir)
463
+ gem_path = "#{cache_path}/#{spec.file_name}"
464
+
465
+ if requires_sudo?
466
+ download_path = Bundler.tmp(spec.full_name)
467
+ download_cache_path = default_cache_path_for(download_path)
468
+ else
469
+ download_cache_path = cache_path
470
+ end
457
471
 
458
- SharedHelpers.filesystem_access("#{download_path}/cache") do |p|
472
+ SharedHelpers.filesystem_access(download_cache_path) do |p|
459
473
  FileUtils.mkdir_p(p)
460
474
  end
461
- download_gem(spec, download_path)
475
+ download_gem(spec, download_cache_path)
462
476
 
463
477
  if requires_sudo?
464
- SharedHelpers.filesystem_access("#{rubygems_dir}/cache") do |p|
478
+ SharedHelpers.filesystem_access(cache_path) do |p|
465
479
  Bundler.mkdir_p(p)
466
480
  end
467
- Bundler.sudo "mv #{download_path}/cache/#{spec.full_name}.gem #{gem_path}"
481
+ Bundler.sudo "mv #{download_cache_path}/#{spec.file_name} #{gem_path}"
468
482
  end
469
483
 
470
484
  gem_path
@@ -472,16 +486,8 @@ module Bundler
472
486
  Bundler.rm_rf(download_path) if requires_sudo?
473
487
  end
474
488
 
475
- def builtin_gem?(spec)
476
- # Ruby 2.1, where all included gems have this summary
477
- return true if spec.summary =~ /is bundled with Ruby/
478
-
479
- # Ruby 2.0, where gemspecs are stored in specifications/default/
480
- spec.loaded_from && spec.loaded_from.include?("specifications/default/")
481
- end
482
-
483
489
  def installed?(spec)
484
- installed_specs[spec].any?
490
+ installed_specs[spec].any? && !spec.deleted_gem?
485
491
  end
486
492
 
487
493
  def requires_sudo?
@@ -492,6 +498,10 @@ module Bundler
492
498
  Bundler.rubygems.gem_dir
493
499
  end
494
500
 
501
+ def default_cache_path_for(dir)
502
+ "#{dir}/cache"
503
+ end
504
+
495
505
  def cache_path
496
506
  Bundler.app_cache
497
507
  end
@@ -504,52 +514,13 @@ module Bundler
504
514
  # @param [Specification] spec
505
515
  # the spec we want to download or retrieve from the cache.
506
516
  #
507
- # @param [String] download_path
517
+ # @param [String] download_cache_path
508
518
  # the local directory the .gem will end up in.
509
519
  #
510
- def download_gem(spec, download_path)
511
- local_path = File.join(download_path, "cache/#{spec.full_name}.gem")
512
-
513
- if (cache_path = download_cache_path(spec)) && cache_path.file?
514
- SharedHelpers.filesystem_access(local_path) do
515
- FileUtils.cp(cache_path, local_path)
516
- end
517
- else
518
- uri = spec.remote.uri
519
- Bundler.ui.confirm("Fetching #{version_message(spec)}")
520
- rubygems_local_path = Bundler.rubygems.download_gem(spec, uri, download_path)
521
-
522
- # older rubygems return varying file:// variants depending on version
523
- rubygems_local_path = rubygems_local_path.gsub(/\Afile:/, "") unless Bundler.rubygems.provides?(">= 3.2.0.rc.2")
524
- rubygems_local_path = rubygems_local_path.gsub(%r{\A//}, "") if Bundler.rubygems.provides?("< 3.1.0")
525
-
526
- if rubygems_local_path != local_path
527
- SharedHelpers.filesystem_access(local_path) do
528
- FileUtils.mv(rubygems_local_path, local_path)
529
- end
530
- end
531
- cache_globally(spec, local_path)
532
- end
533
- end
534
-
535
- # Checks if the requested spec exists in the global cache. If it does
536
- # not, we create the relevant global cache subdirectory if it does not
537
- # exist and copy the spec from the local cache to the global cache.
538
- #
539
- # @param [Specification] spec
540
- # the spec we want to copy to the global cache.
541
- #
542
- # @param [String] local_cache_path
543
- # the local directory from which we want to copy the .gem.
544
- #
545
- def cache_globally(spec, local_cache_path)
546
- return unless cache_path = download_cache_path(spec)
547
- return if cache_path.exist?
548
-
549
- SharedHelpers.filesystem_access(cache_path.dirname, &:mkpath)
550
- SharedHelpers.filesystem_access(cache_path) do
551
- FileUtils.cp(local_cache_path, cache_path)
552
- end
520
+ def download_gem(spec, download_cache_path)
521
+ uri = spec.remote.uri
522
+ Bundler.ui.confirm("Fetching #{version_message(spec)}")
523
+ Bundler.rubygems.download_gem(spec, uri, download_cache_path)
553
524
  end
554
525
 
555
526
  # Returns the global cache path of the calling Rubygems::Source object.
@@ -568,7 +539,7 @@ module Bundler
568
539
  return unless remote = spec.remote
569
540
  return unless cache_slug = remote.cache_slug
570
541
 
571
- Bundler.user_cache.join("gems", cache_slug, spec.file_name)
542
+ Bundler.user_cache.join("gems", cache_slug)
572
543
  end
573
544
 
574
545
  def extension_cache_slug(spec)
@@ -36,6 +36,8 @@ module Bundler
36
36
 
37
37
  def local!; end
38
38
 
39
+ def local_only!; end
40
+
39
41
  def cached!; end
40
42
 
41
43
  def remote!; end
@@ -136,6 +136,10 @@ module Bundler
136
136
  different_sources?(lock_sources, replacement_sources)
137
137
  end
138
138
 
139
+ def local_only!
140
+ all_sources.each(&:local_only!)
141
+ end
142
+
139
143
  def cached!
140
144
  all_sources.each(&:cached!)
141
145
  end
@@ -24,7 +24,7 @@ module Bundler
24
24
 
25
25
  specs_for_dep = spec_for_dependency(dep, match_current_platform)
26
26
  if specs_for_dep.any?
27
- specs += specs_for_dep
27
+ match_current_platform ? specs += specs_for_dep : specs |= specs_for_dep
28
28
 
29
29
  specs_for_dep.first.dependencies.each do |d|
30
30
  next if d.type == :development
@@ -2,7 +2,8 @@ name: Ruby
2
2
 
3
3
  on:
4
4
  push:
5
- - <%= config[:git_default_branch] %>
5
+ branches:
6
+ - <%= config[:git_default_branch] %>
6
7
 
7
8
  pull_request:
8
9
 
@@ -16,7 +16,7 @@ Gem::Specification.new do |spec|
16
16
  <%- end -%>
17
17
  spec.required_ruby_version = ">= <%= config[:required_ruby_version] %>"
18
18
 
19
- spec.metadata["allowed_push_host"] = "TODO: Set to 'https://mygemserver.com'"
19
+ spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
20
20
 
21
21
  spec.metadata["homepage_uri"] = spec.homepage
22
22
  spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Mike Perham
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -1,13 +1,3 @@
1
- require 'thread'
2
- require 'timeout'
3
- require_relative 'monotonic_time'
4
-
5
- ##
6
- # Raised when you attempt to retrieve a connection from a pool that has been
7
- # shut down.
8
-
9
- class Bundler::ConnectionPool::PoolShuttingDownError < RuntimeError; end
10
-
11
1
  ##
12
2
  # The TimedStack manages a pool of homogeneous connections (or any resource
13
3
  # you wish to manage). Connections are created lazily up to a given maximum
@@ -25,7 +15,7 @@ class Bundler::ConnectionPool::PoolShuttingDownError < RuntimeError; end
25
15
  #
26
16
  # conn = ts.pop
27
17
  # ts.pop timeout: 5
28
- # #=> raises Timeout::Error after 5 seconds
18
+ # #=> raises Bundler::ConnectionPool::TimeoutError after 5 seconds
29
19
 
30
20
  class Bundler::ConnectionPool::TimedStack
31
21
  attr_reader :max
@@ -39,8 +29,8 @@ class Bundler::ConnectionPool::TimedStack
39
29
  @created = 0
40
30
  @que = []
41
31
  @max = size
42
- @mutex = Mutex.new
43
- @resource = ConditionVariable.new
32
+ @mutex = Thread::Mutex.new
33
+ @resource = Thread::ConditionVariable.new
44
34
  @shutdown_block = nil
45
35
  end
46
36
 
@@ -59,12 +49,12 @@ class Bundler::ConnectionPool::TimedStack
59
49
  @resource.broadcast
60
50
  end
61
51
  end
62
- alias_method :<<, :push
52
+ alias << push
63
53
 
64
54
  ##
65
55
  # Retrieves a connection from the stack. If a connection is available it is
66
56
  # immediately returned. If no connection is available within the given
67
- # timeout a Timeout::Error is raised.
57
+ # timeout a Bundler::ConnectionPool::TimeoutError is raised.
68
58
  #
69
59
  # +:timeout+ is the only checked entry in +options+ and is preferred over
70
60
  # the +timeout+ argument (which will be removed in a future release). Other
@@ -74,7 +64,7 @@ class Bundler::ConnectionPool::TimedStack
74
64
  options, timeout = timeout, 0.5 if Hash === timeout
75
65
  timeout = options.fetch :timeout, timeout
76
66
 
77
- deadline = Bundler::ConnectionPool.monotonic_time + timeout
67
+ deadline = current_time + timeout
78
68
  @mutex.synchronize do
79
69
  loop do
80
70
  raise Bundler::ConnectionPool::PoolShuttingDownError if @shutdown_block
@@ -83,18 +73,20 @@ class Bundler::ConnectionPool::TimedStack
83
73
  connection = try_create(options)
84
74
  return connection if connection
85
75
 
86
- to_wait = deadline - Bundler::ConnectionPool.monotonic_time
87
- raise Timeout::Error, "Waited #{timeout} sec" if to_wait <= 0
76
+ to_wait = deadline - current_time
77
+ raise Bundler::ConnectionPool::TimeoutError, "Waited #{timeout} sec" if to_wait <= 0
88
78
  @resource.wait(@mutex, to_wait)
89
79
  end
90
80
  end
91
81
  end
92
82
 
93
83
  ##
94
- # Shuts down the TimedStack which prevents connections from being checked
95
- # out. The +block+ is called once for each connection on the stack.
84
+ # Shuts down the TimedStack by passing each connection to +block+ and then
85
+ # removing it from the pool. Attempting to checkout a connection after
86
+ # shutdown will raise +Bundler::ConnectionPool::PoolShuttingDownError+ unless
87
+ # +:reload+ is +true+.
96
88
 
97
- def shutdown(&block)
89
+ def shutdown(reload: false, &block)
98
90
  raise ArgumentError, "shutdown must receive a block" unless block_given?
99
91
 
100
92
  @mutex.synchronize do
@@ -102,6 +94,7 @@ class Bundler::ConnectionPool::TimedStack
102
94
  @resource.broadcast
103
95
 
104
96
  shutdown_connections
97
+ @shutdown_block = nil if reload
105
98
  end
106
99
  end
107
100
 
@@ -121,6 +114,10 @@ class Bundler::ConnectionPool::TimedStack
121
114
 
122
115
  private
123
116
 
117
+ def current_time
118
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
119
+ end
120
+
124
121
  ##
125
122
  # This is an extension point for TimedStack and is called with a mutex.
126
123
  #
@@ -149,6 +146,7 @@ class Bundler::ConnectionPool::TimedStack
149
146
  conn = fetch_connection(options)
150
147
  @shutdown_block.call(conn)
151
148
  end
149
+ @created = 0
152
150
  end
153
151
 
154
152
  ##
@@ -1,3 +1,3 @@
1
1
  class Bundler::ConnectionPool
2
- VERSION = "2.2.2"
2
+ VERSION = "2.3.0"
3
3
  end
@@ -0,0 +1,57 @@
1
+ class Bundler::ConnectionPool
2
+ class Wrapper < ::BasicObject
3
+ METHODS = [:with, :pool_shutdown, :wrapped_pool]
4
+
5
+ def initialize(options = {}, &block)
6
+ @pool = options.fetch(:pool) { ::Bundler::ConnectionPool.new(options, &block) }
7
+ end
8
+
9
+ def wrapped_pool
10
+ @pool
11
+ end
12
+
13
+ def with(&block)
14
+ @pool.with(&block)
15
+ end
16
+
17
+ def pool_shutdown(&block)
18
+ @pool.shutdown(&block)
19
+ end
20
+
21
+ def pool_size
22
+ @pool.size
23
+ end
24
+
25
+ def pool_available
26
+ @pool.available
27
+ end
28
+
29
+ def respond_to?(id, *args)
30
+ METHODS.include?(id) || with { |c| c.respond_to?(id, *args) }
31
+ end
32
+
33
+ # rubocop:disable Style/MethodMissingSuper
34
+ # rubocop:disable Style/MissingRespondToMissing
35
+ if ::RUBY_VERSION >= "3.0.0"
36
+ def method_missing(name, *args, **kwargs, &block)
37
+ with do |connection|
38
+ connection.send(name, *args, **kwargs, &block)
39
+ end
40
+ end
41
+ elsif ::RUBY_VERSION >= "2.7.0"
42
+ ruby2_keywords def method_missing(name, *args, &block)
43
+ with do |connection|
44
+ connection.send(name, *args, &block)
45
+ end
46
+ end
47
+ else
48
+ def method_missing(name, *args, &block)
49
+ with do |connection|
50
+ connection.send(name, *args, &block)
51
+ end
52
+ end
53
+ end
54
+ # rubocop:enable Style/MethodMissingSuper
55
+ # rubocop:enable Style/MissingRespondToMissing
56
+ end
57
+ end