bundler 2.6.5 → 2.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1172 -1024
  3. data/README.md +7 -7
  4. data/bundler.gemspec +2 -2
  5. data/lib/bundler/build_metadata.rb +10 -11
  6. data/lib/bundler/checksum.rb +22 -12
  7. data/lib/bundler/cli/common.rb +1 -1
  8. data/lib/bundler/cli/config.rb +2 -2
  9. data/lib/bundler/cli/doctor/diagnose.rb +167 -0
  10. data/lib/bundler/cli/doctor/ssl.rb +249 -0
  11. data/lib/bundler/cli/doctor.rb +27 -155
  12. data/lib/bundler/cli/gem.rb +62 -30
  13. data/lib/bundler/cli/inject.rb +2 -2
  14. data/lib/bundler/cli/install.rb +5 -5
  15. data/lib/bundler/cli/issue.rb +2 -2
  16. data/lib/bundler/cli/lock.rb +2 -1
  17. data/lib/bundler/cli/outdated.rb +1 -1
  18. data/lib/bundler/cli/update.rb +3 -3
  19. data/lib/bundler/cli.rb +26 -49
  20. data/lib/bundler/compact_index_client/cache.rb +1 -1
  21. data/lib/bundler/compact_index_client/parser.rb +1 -1
  22. data/lib/bundler/compact_index_client/updater.rb +2 -1
  23. data/lib/bundler/compact_index_client.rb +1 -5
  24. data/lib/bundler/current_ruby.rb +27 -3
  25. data/lib/bundler/definition.rb +184 -151
  26. data/lib/bundler/dependency.rb +1 -1
  27. data/lib/bundler/dsl.rb +35 -26
  28. data/lib/bundler/errors.rb +18 -0
  29. data/lib/bundler/feature_flag.rb +15 -12
  30. data/lib/bundler/fetcher/dependency.rb +2 -1
  31. data/lib/bundler/fetcher/downloader.rb +33 -7
  32. data/lib/bundler/fetcher.rb +49 -19
  33. data/lib/bundler/friendly_errors.rb +3 -2
  34. data/lib/bundler/index.rb +7 -2
  35. data/lib/bundler/injector.rb +9 -9
  36. data/lib/bundler/installer.rb +6 -5
  37. data/lib/bundler/lazy_specification.rb +38 -19
  38. data/lib/bundler/lockfile_parser.rb +29 -10
  39. data/lib/bundler/man/bundle-add.1 +1 -1
  40. data/lib/bundler/man/bundle-binstubs.1 +1 -1
  41. data/lib/bundler/man/bundle-cache.1 +1 -1
  42. data/lib/bundler/man/bundle-check.1 +1 -1
  43. data/lib/bundler/man/bundle-clean.1 +1 -1
  44. data/lib/bundler/man/bundle-config.1 +175 -129
  45. data/lib/bundler/man/bundle-config.1.ronn +93 -88
  46. data/lib/bundler/man/bundle-console.1 +1 -1
  47. data/lib/bundler/man/bundle-doctor.1 +43 -4
  48. data/lib/bundler/man/bundle-doctor.1.ronn +48 -4
  49. data/lib/bundler/man/bundle-env.1 +1 -1
  50. data/lib/bundler/man/bundle-exec.1 +3 -3
  51. data/lib/bundler/man/bundle-exec.1.ronn +2 -2
  52. data/lib/bundler/man/bundle-fund.1 +1 -1
  53. data/lib/bundler/man/bundle-gem.1 +67 -44
  54. data/lib/bundler/man/bundle-gem.1.ronn +8 -4
  55. data/lib/bundler/man/bundle-help.1 +1 -1
  56. data/lib/bundler/man/bundle-info.1 +1 -1
  57. data/lib/bundler/man/bundle-init.1 +1 -1
  58. data/lib/bundler/man/bundle-inject.1 +2 -2
  59. data/lib/bundler/man/bundle-inject.1.ronn +1 -1
  60. data/lib/bundler/man/bundle-install.1 +4 -4
  61. data/lib/bundler/man/bundle-install.1.ronn +3 -4
  62. data/lib/bundler/man/bundle-issue.1 +1 -1
  63. data/lib/bundler/man/bundle-licenses.1 +1 -1
  64. data/lib/bundler/man/bundle-list.1 +1 -1
  65. data/lib/bundler/man/bundle-lock.1 +1 -1
  66. data/lib/bundler/man/bundle-open.1 +1 -1
  67. data/lib/bundler/man/bundle-outdated.1 +1 -1
  68. data/lib/bundler/man/bundle-platform.1 +1 -1
  69. data/lib/bundler/man/bundle-plugin.1 +1 -1
  70. data/lib/bundler/man/bundle-pristine.1 +1 -1
  71. data/lib/bundler/man/bundle-remove.1 +1 -1
  72. data/lib/bundler/man/bundle-show.1 +1 -1
  73. data/lib/bundler/man/bundle-update.1 +5 -5
  74. data/lib/bundler/man/bundle-update.1.ronn +4 -4
  75. data/lib/bundler/man/bundle-version.1 +1 -1
  76. data/lib/bundler/man/bundle-viz.1 +1 -1
  77. data/lib/bundler/man/bundle.1 +1 -1
  78. data/lib/bundler/man/gemfile.5 +1 -1
  79. data/lib/bundler/match_platform.rb +31 -12
  80. data/lib/bundler/materialization.rb +2 -2
  81. data/lib/bundler/plugin/api/source.rb +1 -1
  82. data/lib/bundler/plugin/index.rb +1 -1
  83. data/lib/bundler/plugin/installer/path.rb +8 -0
  84. data/lib/bundler/plugin.rb +1 -1
  85. data/lib/bundler/resolver/candidate.rb +12 -9
  86. data/lib/bundler/resolver/package.rb +1 -1
  87. data/lib/bundler/resolver/strategy.rb +40 -0
  88. data/lib/bundler/resolver.rb +18 -27
  89. data/lib/bundler/rubygems_ext.rb +131 -120
  90. data/lib/bundler/rubygems_integration.rb +11 -6
  91. data/lib/bundler/runtime.rb +9 -6
  92. data/lib/bundler/self_manager.rb +32 -42
  93. data/lib/bundler/settings/validator.rb +0 -23
  94. data/lib/bundler/settings.rb +4 -6
  95. data/lib/bundler/shared_helpers.rb +10 -4
  96. data/lib/bundler/source/gemspec.rb +1 -4
  97. data/lib/bundler/source/git/git_proxy.rb +17 -6
  98. data/lib/bundler/source/git.rb +5 -1
  99. data/lib/bundler/source/path.rb +9 -2
  100. data/lib/bundler/source/rubygems/remote.rb +11 -3
  101. data/lib/bundler/source_list.rb +30 -16
  102. data/lib/bundler/source_map.rb +1 -1
  103. data/lib/bundler/spec_set.rb +55 -16
  104. data/lib/bundler/templates/Executable +0 -11
  105. data/lib/bundler/templates/newgem/github/workflows/main.yml.tt +2 -0
  106. data/lib/bundler/templates/newgem/newgem.gemspec.tt +6 -5
  107. data/lib/bundler/ui/shell.rb +2 -2
  108. data/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb +53 -3
  109. data/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb +1 -1
  110. data/lib/bundler/vendor/connection_pool/lib/connection_pool.rb +11 -0
  111. data/lib/bundler/vendor/net-http-persistent/README.rdoc +1 -1
  112. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/timed_stack_multi.rb +2 -1
  113. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb +81 -42
  114. data/lib/bundler/vendor/pub_grub/lib/pub_grub/basic_package_source.rb +4 -24
  115. data/lib/bundler/vendor/pub_grub/lib/pub_grub/strategy.rb +42 -0
  116. data/lib/bundler/vendor/pub_grub/lib/pub_grub/version_range.rb +20 -8
  117. data/lib/bundler/vendor/pub_grub/lib/pub_grub/version_solver.rb +17 -29
  118. data/lib/bundler/vendor/uri/lib/uri/common.rb +7 -3
  119. data/lib/bundler/vendor/uri/lib/uri/generic.rb +12 -11
  120. data/lib/bundler/vendor/uri/lib/uri/rfc2396_parser.rb +6 -6
  121. data/lib/bundler/vendor/uri/lib/uri/version.rb +1 -1
  122. data/lib/bundler/version.rb +10 -2
  123. data/lib/bundler/worker.rb +1 -1
  124. data/lib/bundler.rb +14 -12
  125. metadata +9 -16
  126. data/lib/bundler/compact_index_client/gem_parser.rb +0 -32
  127. data/lib/bundler/gem_helpers.rb +0 -144
  128. data/lib/bundler/templates/Executable.bundler +0 -109
  129. data/lib/bundler/vendor/connection_pool/.document +0 -1
  130. data/lib/bundler/vendor/fileutils/.document +0 -1
  131. data/lib/bundler/vendor/net-http-persistent/.document +0 -1
  132. data/lib/bundler/vendor/pub_grub/.document +0 -1
  133. data/lib/bundler/vendor/securerandom/.document +0 -1
  134. data/lib/bundler/vendor/thor/.document +0 -1
  135. data/lib/bundler/vendor/tsort/.document +0 -1
  136. data/lib/bundler/vendor/uri/.document +0 -1
@@ -4,15 +4,12 @@ module Bundler
4
4
  class Source
5
5
  class Gemspec < Path
6
6
  attr_reader :gemspec
7
+ attr_writer :checksum_store
7
8
 
8
9
  def initialize(options)
9
10
  super
10
11
  @gemspec = options["gemspec"]
11
12
  end
12
-
13
- def as_path_source
14
- Path.new(options)
15
- end
16
13
  end
17
14
  end
18
15
  end
@@ -147,6 +147,12 @@ module Bundler
147
147
  end
148
148
  end
149
149
 
150
+ def installed_to?(destination)
151
+ # if copy_to is interrupted, it may leave a partially installed directory that
152
+ # contains .git but no other files -- consider this not to be installed
153
+ Dir.exist?(destination) && (Dir.children(destination) - [".git"]).any?
154
+ end
155
+
150
156
  private
151
157
 
152
158
  def git_remote_fetch(args)
@@ -179,7 +185,8 @@ module Bundler
179
185
  _, err, status = capture(command, nil)
180
186
  return extra_ref if status.success?
181
187
 
182
- if err.include?("Could not find remote branch")
188
+ if err.include?("Could not find remote branch") || # git up to 2.49
189
+ err.include?("Remote branch #{branch_option} not found") # git 2.49 or higher
183
190
  raise MissingGitRevisionError.new(command_with_no_credentials, nil, explicit_ref, credential_filtered_uri)
184
191
  else
185
192
  idx = command.index("--depth")
@@ -256,7 +263,7 @@ module Bundler
256
263
  end
257
264
 
258
265
  def not_pinned?
259
- branch || tag || ref.nil?
266
+ branch_option || ref.nil?
260
267
  end
261
268
 
262
269
  def pinned_to_full_sha?
@@ -298,8 +305,8 @@ module Bundler
298
305
  end
299
306
 
300
307
  def has_revision_cached?
301
- return unless @revision && path.exist?
302
- git("cat-file", "-e", @revision, dir: path)
308
+ return unless commit && path.exist?
309
+ git("cat-file", "-e", commit, dir: path)
303
310
  true
304
311
  rescue GitError
305
312
  false
@@ -401,7 +408,7 @@ module Bundler
401
408
  def capture3_args_for(cmd, dir)
402
409
  return ["git", *cmd] unless dir
403
410
 
404
- if Bundler.feature_flag.bundler_3_mode? || supports_minus_c?
411
+ if Bundler.feature_flag.bundler_4_mode? || supports_minus_c?
405
412
  ["git", "-C", dir.to_s, *cmd]
406
413
  else
407
414
  ["git", *cmd, { chdir: dir.to_s }]
@@ -420,7 +427,7 @@ module Bundler
420
427
  # anyways.
421
428
  return args if @revision
422
429
 
423
- args += ["--branch", branch || tag] if branch || tag
430
+ args += ["--branch", branch_option] if branch_option
424
431
  args
425
432
  end
426
433
 
@@ -436,6 +443,10 @@ module Bundler
436
443
  extra_args
437
444
  end
438
445
 
446
+ def branch_option
447
+ branch || tag
448
+ end
449
+
439
450
  def full_clone?
440
451
  depth.nil?
441
452
  end
@@ -360,7 +360,11 @@ module Bundler
360
360
  end
361
361
 
362
362
  def locked_revision_checked_out?
363
- locked_revision && locked_revision == revision && install_path.exist?
363
+ locked_revision && locked_revision == revision && installed?
364
+ end
365
+
366
+ def installed?
367
+ git_proxy.installed_to?(install_path)
364
368
  end
365
369
 
366
370
  def base_name
@@ -60,8 +60,8 @@ module Bundler
60
60
  end
61
61
 
62
62
  def eql?(other)
63
- return unless other.class == self.class
64
- expanded_original_path == other.expanded_original_path &&
63
+ [Gemspec, Path].include?(other.class) &&
64
+ expanded_original_path == other.expanded_original_path &&
65
65
  version == other.version
66
66
  end
67
67
 
@@ -167,6 +167,13 @@ module Bundler
167
167
  next unless spec = load_gemspec(file)
168
168
  spec.source = self
169
169
 
170
+ # The ignore attribute is for ignoring installed gems that don't
171
+ # have extensions correctly compiled for activation. In the case of
172
+ # path sources, there's a single version of each gem in the path
173
+ # source available to Bundler, so we always certainly want to
174
+ # consider that for activation and never makes sense to ignore it.
175
+ spec.ignored = false
176
+
170
177
  # Validation causes extension_dir to be calculated, which depends
171
178
  # on #source, so we validate here instead of load_gemspec
172
179
  validate_spec(spec)
@@ -16,6 +16,9 @@ module Bundler
16
16
  @anonymized_uri = remove_auth(@uri).freeze
17
17
  end
18
18
 
19
+ MAX_CACHE_SLUG_HOST_SIZE = 255 - 1 - 32 # 255 minus dot minus MD5 length
20
+ private_constant :MAX_CACHE_SLUG_HOST_SIZE
21
+
19
22
  # @return [String] A slug suitable for use as a cache key for this
20
23
  # remote.
21
24
  #
@@ -28,10 +31,15 @@ module Bundler
28
31
  host = cache_uri.to_s.start_with?("file://") ? nil : cache_uri.host
29
32
 
30
33
  uri_parts = [host, cache_uri.user, cache_uri.port, cache_uri.path]
31
- uri_digest = SharedHelpers.digest(:MD5).hexdigest(uri_parts.compact.join("."))
34
+ uri_parts.compact!
35
+ uri_digest = SharedHelpers.digest(:MD5).hexdigest(uri_parts.join("."))
36
+
37
+ uri_parts.pop
38
+ host_parts = uri_parts.join(".")
39
+ return uri_digest if host_parts.empty?
32
40
 
33
- uri_parts[-1] = uri_digest
34
- uri_parts.compact.join(".")
41
+ shortened_host_parts = host_parts[0...MAX_CACHE_SLUG_HOST_SIZE]
42
+ [shortened_host_parts, uri_digest].join(".")
35
43
  end
36
44
  end
37
45
 
@@ -103,7 +103,7 @@ module Bundler
103
103
  end
104
104
 
105
105
  def get(source)
106
- source_list_for(source).find {|s| equivalent_source?(source, s) }
106
+ source_list_for(source).find {|s| s.include?(source) }
107
107
  end
108
108
 
109
109
  def lock_sources
@@ -173,39 +173,57 @@ module Bundler
173
173
 
174
174
  def map_sources(replacement_sources)
175
175
  rubygems = @rubygems_sources.map do |source|
176
- replace_rubygems_source(replacement_sources, source) || source
176
+ replace_rubygems_source(replacement_sources, source)
177
177
  end
178
178
 
179
179
  git, plugin = [@git_sources, @plugin_sources].map do |sources|
180
180
  sources.map do |source|
181
- replacement_sources.find {|s| s == source } || source
181
+ replace_source(replacement_sources, source)
182
182
  end
183
183
  end
184
184
 
185
185
  path = @path_sources.map do |source|
186
- replacement_sources.find {|s| s == (source.is_a?(Source::Gemspec) ? source.as_path_source : source) } || source
186
+ replace_path_source(replacement_sources, source)
187
187
  end
188
188
 
189
189
  [rubygems, path, git, plugin]
190
190
  end
191
191
 
192
192
  def global_replacement_source(replacement_sources)
193
- replacement_source = replace_rubygems_source(replacement_sources, global_rubygems_source)
194
- return global_rubygems_source unless replacement_source
195
-
196
- replacement_source.local!
197
- replacement_source
193
+ replace_rubygems_source(replacement_sources, global_rubygems_source, &:local!)
198
194
  end
199
195
 
200
196
  def replace_rubygems_source(replacement_sources, gemfile_source)
197
+ replace_source(replacement_sources, gemfile_source) do |replacement_source|
198
+ # locked sources never include credentials so always prefer remotes from the gemfile
199
+ replacement_source.remotes = gemfile_source.remotes
200
+
201
+ yield replacement_source if block_given?
202
+
203
+ replacement_source
204
+ end
205
+ end
206
+
207
+ def replace_source(replacement_sources, gemfile_source)
201
208
  replacement_source = replacement_sources.find {|s| s == gemfile_source }
202
- return unless replacement_source
209
+ return gemfile_source unless replacement_source
210
+
211
+ replacement_source = yield(replacement_source) if block_given?
203
212
 
204
- # locked sources never include credentials so always prefer remotes from the gemfile
205
- replacement_source.remotes = gemfile_source.remotes
206
213
  replacement_source
207
214
  end
208
215
 
216
+ def replace_path_source(replacement_sources, gemfile_source)
217
+ replace_source(replacement_sources, gemfile_source) do |replacement_source|
218
+ if gemfile_source.is_a?(Source::Gemspec)
219
+ gemfile_source.checksum_store = replacement_source.checksum_store
220
+ gemfile_source
221
+ else
222
+ replacement_source
223
+ end
224
+ end
225
+ end
226
+
209
227
  def different_sources?(lock_sources, replacement_sources)
210
228
  !equivalent_sources?(lock_sources, replacement_sources)
211
229
  end
@@ -247,9 +265,5 @@ module Bundler
247
265
  def equivalent_sources?(lock_sources, replacement_sources)
248
266
  lock_sources.sort_by(&:identifier) == replacement_sources.sort_by(&:identifier)
249
267
  end
250
-
251
- def equivalent_source?(source, other_source)
252
- source == other_source
253
- end
254
268
  end
255
269
  end
@@ -23,7 +23,7 @@ module Bundler
23
23
  if previous_source.nil?
24
24
  requirements[indirect_dependency_name] = source
25
25
  else
26
- no_ambiguous_sources = Bundler.feature_flag.bundler_3_mode?
26
+ no_ambiguous_sources = Bundler.feature_flag.bundler_4_mode?
27
27
 
28
28
  msg = ["The gem '#{indirect_dependency_name}' was found in multiple relevant sources."]
29
29
  msg.concat [previous_source, source].map {|s| " * #{s}" }.sort
@@ -29,9 +29,10 @@ module Bundler
29
29
  end
30
30
 
31
31
  def normalize_platforms!(deps, platforms)
32
- complete_platforms = add_extra_platforms!(platforms)
32
+ remove_invalid_platforms!(deps, platforms)
33
+ add_extra_platforms!(platforms)
33
34
 
34
- complete_platforms.map do |platform|
35
+ platforms.map! do |platform|
35
36
  next platform if platform == Gem::Platform::RUBY
36
37
 
37
38
  begin
@@ -44,26 +45,48 @@ module Bundler
44
45
  next platform if incomplete_for_platform?(deps, less_specific_platform)
45
46
 
46
47
  less_specific_platform
47
- end.uniq
48
+ end.uniq!
49
+ end
50
+
51
+ def add_originally_invalid_platforms!(platforms, originally_invalid_platforms)
52
+ originally_invalid_platforms.each do |originally_invalid_platform|
53
+ platforms << originally_invalid_platform if complete_platform(originally_invalid_platform)
54
+ end
55
+ end
56
+
57
+ def remove_invalid_platforms!(deps, platforms, skips: [])
58
+ invalid_platforms = []
59
+
60
+ platforms.reject! do |platform|
61
+ next false if skips.include?(platform)
62
+
63
+ invalid = incomplete_for_platform?(deps, platform)
64
+ invalid_platforms << platform if invalid
65
+ invalid
66
+ end
67
+
68
+ invalid_platforms
48
69
  end
49
70
 
50
71
  def add_extra_platforms!(platforms)
51
- return platforms.concat([Gem::Platform::RUBY]).uniq if @specs.empty?
72
+ if @specs.empty?
73
+ platforms.concat([Gem::Platform::RUBY]).uniq
74
+ return
75
+ end
52
76
 
53
77
  new_platforms = all_platforms.select do |platform|
54
78
  next if platforms.include?(platform)
55
- next unless GemHelpers.generic(platform) == Gem::Platform::RUBY
79
+ next unless Gem::Platform.generic(platform) == Gem::Platform::RUBY
56
80
 
57
81
  complete_platform(platform)
58
82
  end
59
- return platforms if new_platforms.empty?
83
+ return if new_platforms.empty?
60
84
 
61
85
  platforms.concat(new_platforms)
86
+ return if new_platforms.include?(Bundler.local_platform)
62
87
 
63
88
  less_specific_platform = new_platforms.find {|platform| platform != Gem::Platform::RUBY && Bundler.local_platform === platform && platform === Bundler.local_platform }
64
89
  platforms.delete(Bundler.local_platform) if less_specific_platform
65
-
66
- platforms
67
90
  end
68
91
 
69
92
  def validate_deps(s)
@@ -122,12 +145,15 @@ module Bundler
122
145
  end
123
146
 
124
147
  def incomplete_for_platform?(deps, platform)
125
- return false if @specs.empty?
148
+ incomplete_specs_for_platform(deps, platform).any?
149
+ end
150
+
151
+ def incomplete_specs_for_platform(deps, platform)
152
+ return [] if @specs.empty?
126
153
 
127
154
  validation_set = self.class.new(@specs)
128
155
  validation_set.for(deps, [platform])
129
-
130
- validation_set.incomplete_specs.any?
156
+ validation_set.incomplete_specs
131
157
  end
132
158
 
133
159
  def missing_specs_for(deps)
@@ -153,11 +179,13 @@ module Bundler
153
179
  end
154
180
 
155
181
  def -(other)
182
+ SharedHelpers.major_deprecation 2, "SpecSet#- has been removed with no replacement"
183
+
156
184
  SpecSet.new(to_a - other.to_a)
157
185
  end
158
186
 
159
187
  def find_by_name_and_platform(name, platform)
160
- @specs.detect {|spec| spec.name == name && spec.match_platform(platform) }
188
+ @specs.detect {|spec| spec.name == name && spec.installable_on_platform?(platform) }
161
189
  end
162
190
 
163
191
  def specs_with_additional_variants_from(other)
@@ -173,7 +201,7 @@ module Bundler
173
201
  end
174
202
 
175
203
  def version_for(name)
176
- self[name].first&.version
204
+ exemplary_spec(name)&.version
177
205
  end
178
206
 
179
207
  def what_required(spec)
@@ -184,6 +212,8 @@ module Bundler
184
212
  end
185
213
 
186
214
  def <<(spec)
215
+ SharedHelpers.major_deprecation 2, "SpecSet#<< has been removed with no replacement"
216
+
187
217
  @specs << spec
188
218
  end
189
219
 
@@ -254,7 +284,7 @@ module Bundler
254
284
  valid_platform = lookup.all? do |_, specs|
255
285
  spec = specs.first
256
286
  matching_specs = spec.source.specs.search([spec.name, spec.version])
257
- platform_spec = GemHelpers.select_best_platform_match(matching_specs, platform).find do |s|
287
+ platform_spec = MatchPlatform.select_best_platform_match(matching_specs, platform).find do |s|
258
288
  valid?(s)
259
289
  end
260
290
 
@@ -278,8 +308,13 @@ module Bundler
278
308
  end
279
309
 
280
310
  def additional_variants_from(other)
281
- other.select do |spec|
282
- version_for(spec.name) == spec.version && valid_dependencies?(spec)
311
+ other.select do |other_spec|
312
+ spec = exemplary_spec(other_spec.name)
313
+ next unless spec
314
+
315
+ selected = spec.version == other_spec.version && valid_dependencies?(other_spec)
316
+ other_spec.source = spec.source if selected
317
+ selected
283
318
  end
284
319
  end
285
320
 
@@ -356,5 +391,9 @@ module Bundler
356
391
  hash[key] ||= []
357
392
  hash[key] << value
358
393
  end
394
+
395
+ def exemplary_spec(name)
396
+ self[name].first
397
+ end
359
398
  end
360
399
  end
@@ -10,17 +10,6 @@
10
10
 
11
11
  ENV["BUNDLE_GEMFILE"] ||= File.expand_path("<%= relative_gemfile_path %>", __dir__)
12
12
 
13
- bundle_binstub = File.expand_path("bundle", __dir__)
14
-
15
- if File.file?(bundle_binstub)
16
- if File.read(bundle_binstub, 300).include?("This file was generated by Bundler")
17
- load(bundle_binstub)
18
- else
19
- abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
20
- Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
21
- end
22
- end
23
-
24
13
  require "rubygems"
25
14
  require "bundler/setup"
26
15
 
@@ -18,6 +18,8 @@ jobs:
18
18
 
19
19
  steps:
20
20
  - uses: actions/checkout@v4
21
+ with:
22
+ persist-credentials: false
21
23
  <%- if config[:ext] == 'rust' -%>
22
24
  - name: Set up Ruby & Rust
23
25
  uses: oxidize-rb/actions/setup-ruby-and-rust@v1
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
10
10
 
11
11
  spec.summary = "TODO: Write a short summary, because RubyGems requires one."
12
12
  spec.description = "TODO: Write a longer description or delete this line."
13
- spec.homepage = "TODO: Put your gem's website or public repo URL here."
13
+ spec.homepage = "<%= config[:homepage_uri] %>"
14
14
  <%- if config[:mit] -%>
15
15
  spec.license = "MIT"
16
16
  <%- end -%>
@@ -20,10 +20,11 @@ Gem::Specification.new do |spec|
20
20
  <%- end -%>
21
21
 
22
22
  spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
23
-
24
23
  spec.metadata["homepage_uri"] = spec.homepage
25
- spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
26
- spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
24
+ spec.metadata["source_code_uri"] = "<%= config[:source_code_uri] %>"
25
+ <%- if config[:changelog] -%>
26
+ spec.metadata["changelog_uri"] = "<%= config[:changelog_uri] %>"
27
+ <%- end -%>
27
28
 
28
29
  # Specify which files should be added to the gem when it is released.
29
30
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
@@ -31,7 +32,7 @@ Gem::Specification.new do |spec|
31
32
  spec.files = IO.popen(%w[git ls-files -z], chdir: __dir__, err: IO::NULL) do |ls|
32
33
  ls.readlines("\x0", chomp: true).reject do |f|
33
34
  (f == gemspec) ||
34
- f.start_with?(*%w[bin/ test/ spec/ features/ .git <%= config[:ci_config_path] %>appveyor Gemfile])
35
+ f.start_with?(*%w[<%= config[:ignore_paths].join(" ") %>])
35
36
  end
36
37
  end
37
38
  spec.bindir = "exe"
@@ -80,11 +80,11 @@ module Bundler
80
80
  end
81
81
 
82
82
  def ask(msg)
83
- @shell.ask(msg)
83
+ @shell.ask(msg, :green)
84
84
  end
85
85
 
86
86
  def yes?(msg)
87
- @shell.yes?(msg)
87
+ @shell.yes?(msg, :green)
88
88
  end
89
89
 
90
90
  def no?(msg)
@@ -41,6 +41,7 @@ class Bundler::ConnectionPool::TimedStack
41
41
  def push(obj, options = {})
42
42
  @mutex.synchronize do
43
43
  if @shutdown_block
44
+ @created -= 1 unless @created == 0
44
45
  @shutdown_block.call(obj)
45
46
  else
46
47
  store_connection obj, options
@@ -98,6 +99,26 @@ class Bundler::ConnectionPool::TimedStack
98
99
  end
99
100
  end
100
101
 
102
+ ##
103
+ # Reaps connections that were checked in more than +idle_seconds+ ago.
104
+ def reap(idle_seconds, &block)
105
+ raise ArgumentError, "reap must receive a block" unless block
106
+ raise ArgumentError, "idle_seconds must be a number" unless idle_seconds.is_a?(Numeric)
107
+ raise Bundler::ConnectionPool::PoolShuttingDownError if @shutdown_block
108
+
109
+ idle.times do
110
+ conn =
111
+ @mutex.synchronize do
112
+ raise Bundler::ConnectionPool::PoolShuttingDownError if @shutdown_block
113
+
114
+ reserve_idle_connection(idle_seconds)
115
+ end
116
+ break unless conn
117
+
118
+ block.call(conn)
119
+ end
120
+ end
121
+
101
122
  ##
102
123
  # Returns +true+ if there are no available connections.
103
124
 
@@ -112,6 +133,12 @@ class Bundler::ConnectionPool::TimedStack
112
133
  @max - @created + @que.length
113
134
  end
114
135
 
136
+ ##
137
+ # The number of connections created and available on the stack.
138
+ def idle
139
+ @que.length
140
+ end
141
+
115
142
  private
116
143
 
117
144
  def current_time
@@ -133,7 +160,7 @@ class Bundler::ConnectionPool::TimedStack
133
160
  # This method must return a connection from the stack.
134
161
 
135
162
  def fetch_connection(options = nil)
136
- @que.pop
163
+ @que.pop&.first
137
164
  end
138
165
 
139
166
  ##
@@ -144,9 +171,32 @@ class Bundler::ConnectionPool::TimedStack
144
171
  def shutdown_connections(options = nil)
145
172
  while connection_stored?(options)
146
173
  conn = fetch_connection(options)
174
+ @created -= 1 unless @created == 0
147
175
  @shutdown_block.call(conn)
148
176
  end
149
- @created = 0
177
+ end
178
+
179
+ ##
180
+ # This is an extension point for TimedStack and is called with a mutex.
181
+ #
182
+ # This method returns the oldest idle connection if it has been idle for more than idle_seconds.
183
+ # This requires that the stack is kept in order of checked in time (oldest first).
184
+
185
+ def reserve_idle_connection(idle_seconds)
186
+ return unless idle_connections?(idle_seconds)
187
+
188
+ @created -= 1 unless @created == 0
189
+
190
+ @que.shift.first
191
+ end
192
+
193
+ ##
194
+ # This is an extension point for TimedStack and is called with a mutex.
195
+ #
196
+ # Returns true if the first connection in the stack has been idle for more than idle_seconds
197
+
198
+ def idle_connections?(idle_seconds)
199
+ connection_stored? && (current_time - @que.first.last > idle_seconds)
150
200
  end
151
201
 
152
202
  ##
@@ -155,7 +205,7 @@ class Bundler::ConnectionPool::TimedStack
155
205
  # This method must return +obj+ to the stack.
156
206
 
157
207
  def store_connection(obj, options = nil)
158
- @que.push obj
208
+ @que.push [obj, current_time]
159
209
  end
160
210
 
161
211
  ##
@@ -1,3 +1,3 @@
1
1
  class Bundler::ConnectionPool
2
- VERSION = "2.4.1"
2
+ VERSION = "2.5.0"
3
3
  end
@@ -160,6 +160,12 @@ class Bundler::ConnectionPool
160
160
  @available.shutdown(reload: true, &block)
161
161
  end
162
162
 
163
+ ## Reaps idle connections that have been idle for over +idle_seconds+.
164
+ # +idle_seconds+ defaults to 60.
165
+ def reap(idle_seconds = 60, &block)
166
+ @available.reap(idle_seconds, &block)
167
+ end
168
+
163
169
  # Size of this connection pool
164
170
  attr_reader :size
165
171
  # Automatically drop all connections after fork
@@ -169,6 +175,11 @@ class Bundler::ConnectionPool
169
175
  def available
170
176
  @available.length
171
177
  end
178
+
179
+ # Number of pool entries created and idle in the pool.
180
+ def idle
181
+ @available.idle
182
+ end
172
183
  end
173
184
 
174
185
  require_relative "connection_pool/timed_stack"
@@ -1,7 +1,7 @@
1
1
  = net-http-persistent
2
2
 
3
3
  home :: https://github.com/drbrain/net-http-persistent
4
- rdoc :: https://rdoc.info/gems/net-http-persistent
4
+ rdoc :: https://rubydoc.info/gems/net-http-persistent
5
5
 
6
6
  == DESCRIPTION:
7
7
 
@@ -63,7 +63,8 @@ class Gem::Net::HTTP::Persistent::TimedStackMulti < Bundler::ConnectionPool::Tim
63
63
  if @created >= @max && @enqueued >= 1
64
64
  oldest, = @lru.first
65
65
  @lru.delete oldest
66
- @ques[oldest].pop
66
+ connection = @ques[oldest].pop
67
+ connection.close if connection.respond_to?(:close)
67
68
 
68
69
  @created -= 1
69
70
  end