bundler 4.0.19 → 4.1.0.beta1

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 (196) hide show
  1. checksums.yaml +4 -4
  2. data/{lib/bundler/vendor/connection_pool/LICENSE → MIT.txt} +8 -6
  3. data/bundler.gemspec +24 -7
  4. data/lib/bundler/build_metadata.rb +2 -2
  5. data/lib/bundler/checksum.rb +40 -25
  6. data/lib/bundler/ci_detector.rb +1 -1
  7. data/lib/bundler/cli/add.rb +3 -4
  8. data/lib/bundler/cli/cache.rb +4 -0
  9. data/lib/bundler/cli/clean.rb +5 -0
  10. data/lib/bundler/cli/common.rb +37 -1
  11. data/lib/bundler/cli/config.rb +4 -2
  12. data/lib/bundler/cli/doctor/diagnose.rb +1 -1
  13. data/lib/bundler/cli/exec.rb +15 -1
  14. data/lib/bundler/cli/gem.rb +0 -17
  15. data/lib/bundler/cli/init.rb +4 -0
  16. data/lib/bundler/cli/install.rb +4 -5
  17. data/lib/bundler/cli/lock.rb +1 -2
  18. data/lib/bundler/cli/outdated.rb +45 -10
  19. data/lib/bundler/cli/pristine.rb +2 -1
  20. data/lib/bundler/cli/update.rb +20 -11
  21. data/lib/bundler/cli.rb +12 -9
  22. data/lib/bundler/definition.rb +60 -13
  23. data/lib/bundler/dependency.rb +4 -0
  24. data/lib/bundler/dsl.rb +78 -12
  25. data/lib/bundler/endpoint_specification.rb +51 -14
  26. data/lib/bundler/env.rb +7 -24
  27. data/lib/bundler/errors.rb +34 -5
  28. data/lib/bundler/fetcher/compact_index.rb +14 -5
  29. data/lib/bundler/fetcher/connection_pools.rb +150 -0
  30. data/lib/bundler/fetcher/downloader.rb +40 -16
  31. data/lib/bundler/fetcher.rb +8 -47
  32. data/lib/bundler/gem_helper.rb +2 -2
  33. data/lib/bundler/injector.rb +3 -3
  34. data/lib/bundler/inline.rb +44 -4
  35. data/lib/bundler/installer/parallel_installer.rb +25 -30
  36. data/lib/bundler/installer.rb +20 -6
  37. data/lib/bundler/lazy_specification.rb +44 -17
  38. data/lib/bundler/lockfile_generator.rb +24 -2
  39. data/lib/bundler/lockfile_parser.rb +43 -5
  40. data/lib/bundler/man/bundle-add.1 +7 -4
  41. data/lib/bundler/man/bundle-add.1.ronn +8 -4
  42. data/lib/bundler/man/bundle-binstubs.1 +1 -1
  43. data/lib/bundler/man/bundle-cache.1 +2 -2
  44. data/lib/bundler/man/bundle-cache.1.ronn +2 -2
  45. data/lib/bundler/man/bundle-check.1 +1 -1
  46. data/lib/bundler/man/bundle-clean.1 +1 -1
  47. data/lib/bundler/man/bundle-config.1 +38 -6
  48. data/lib/bundler/man/bundle-config.1.ronn +143 -10
  49. data/lib/bundler/man/bundle-console.1 +1 -1
  50. data/lib/bundler/man/bundle-doctor.1 +1 -1
  51. data/lib/bundler/man/bundle-env.1 +1 -1
  52. data/lib/bundler/man/bundle-exec.1 +1 -1
  53. data/lib/bundler/man/bundle-fund.1 +1 -1
  54. data/lib/bundler/man/bundle-gem.1 +1 -1
  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-install.1 +2 -2
  59. data/lib/bundler/man/bundle-install.1.ronn +4 -4
  60. data/lib/bundler/man/bundle-issue.1 +1 -1
  61. data/lib/bundler/man/bundle-licenses.1 +1 -1
  62. data/lib/bundler/man/bundle-list.1 +1 -1
  63. data/lib/bundler/man/bundle-lock.1 +2 -2
  64. data/lib/bundler/man/bundle-lock.1.ronn +3 -2
  65. data/lib/bundler/man/bundle-open.1 +1 -1
  66. data/lib/bundler/man/bundle-outdated.1 +2 -2
  67. data/lib/bundler/man/bundle-outdated.1.ronn +7 -1
  68. data/lib/bundler/man/bundle-platform.1 +5 -1
  69. data/lib/bundler/man/bundle-platform.1.ronn +4 -0
  70. data/lib/bundler/man/bundle-plugin.1 +1 -1
  71. data/lib/bundler/man/bundle-pristine.1 +1 -1
  72. data/lib/bundler/man/bundle-remove.1 +1 -1
  73. data/lib/bundler/man/bundle-show.1 +1 -1
  74. data/lib/bundler/man/bundle-update.1 +2 -2
  75. data/lib/bundler/man/bundle-update.1.ronn +2 -2
  76. data/lib/bundler/man/bundle-version.1 +1 -1
  77. data/lib/bundler/man/bundle.1 +1 -1
  78. data/lib/bundler/man/gemfile.5 +46 -2
  79. data/lib/bundler/man/gemfile.5.ronn +56 -1
  80. data/lib/bundler/match_metadata.rb +20 -0
  81. data/lib/bundler/match_platform.rb +25 -2
  82. data/lib/bundler/match_remote_metadata.rb +21 -6
  83. data/lib/bundler/materialization.rb +2 -1
  84. data/lib/bundler/override.rb +69 -0
  85. data/lib/bundler/plugin/dsl.rb +3 -5
  86. data/lib/bundler/plugin/events.rb +55 -13
  87. data/lib/bundler/plugin/index.rb +87 -11
  88. data/lib/bundler/plugin/installer/path.rb +1 -1
  89. data/lib/bundler/plugin/installer.rb +13 -5
  90. data/lib/bundler/plugin.rb +58 -30
  91. data/lib/bundler/remote_specification.rb +6 -3
  92. data/lib/bundler/resolver/base.rb +2 -1
  93. data/lib/bundler/resolver/incompatibility.rb +1 -1
  94. data/lib/bundler/resolver.rb +73 -23
  95. data/lib/bundler/ruby_version.rb +0 -2
  96. data/lib/bundler/rubygems_ext.rb +71 -1
  97. data/lib/bundler/rubygems_gem_installer.rb +41 -2
  98. data/lib/bundler/rubygems_integration.rb +7 -2
  99. data/lib/bundler/runtime.rb +79 -12
  100. data/lib/bundler/settings.rb +373 -30
  101. data/lib/bundler/shared_helpers.rb +17 -1
  102. data/lib/bundler/source/git/git_proxy.rb +65 -4
  103. data/lib/bundler/source/git.rb +25 -14
  104. data/lib/bundler/source/path/installer.rb +1 -1
  105. data/lib/bundler/source/path.rb +1 -1
  106. data/lib/bundler/source/rubygems/remote.rb +10 -5
  107. data/lib/bundler/source/rubygems.rb +49 -13
  108. data/lib/bundler/spec_set.rb +16 -4
  109. data/lib/bundler/stub_specification.rb +5 -2
  110. data/lib/bundler/templates/newgem/Gemfile.tt +4 -4
  111. data/lib/bundler/templates/newgem/newgem.gemspec.tt +3 -3
  112. data/lib/bundler/templates/newgem/spec/newgem_spec.rb.tt +1 -1
  113. data/lib/bundler/templates/newgem/test/minitest/test_newgem.rb.tt +1 -1
  114. data/lib/bundler/ui/shell.rb +0 -4
  115. data/lib/bundler/ui/silent.rb +0 -4
  116. data/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb +35 -2
  117. data/lib/bundler/vendor/thor/lib/thor/base.rb +2 -1
  118. data/lib/bundler/vendor/thor/lib/thor/shell/basic.rb +14 -6
  119. data/lib/bundler/vendor/thor/lib/thor/shell/color.rb +2 -0
  120. data/lib/bundler/vendor/thor/lib/thor/version.rb +1 -1
  121. data/lib/bundler/vendor/thor/lib/thor.rb +34 -1
  122. data/lib/bundler/vendored_pub_grub.rb +5 -2
  123. data/lib/bundler/vendored_securerandom.rb +11 -8
  124. data/lib/bundler/vendored_uri.rb +14 -14
  125. data/lib/bundler/version.rb +1 -1
  126. data/lib/bundler/worker.rb +3 -2
  127. data/lib/bundler.rb +16 -6
  128. data/lib/rubygems/compact_index_client/cache.rb +122 -0
  129. data/lib/rubygems/compact_index_client/cache_file.rb +147 -0
  130. data/lib/rubygems/compact_index_client/http_fetcher.rb +88 -0
  131. data/lib/rubygems/compact_index_client/parser.rb +90 -0
  132. data/lib/rubygems/compact_index_client/updater.rb +113 -0
  133. data/lib/rubygems/compact_index_client.rb +111 -0
  134. data/lib/rubygems/cooldown_settings.rb +57 -0
  135. data/lib/rubygems/credential_store/native/linux.rb +97 -0
  136. data/lib/rubygems/credential_store/native/macos.rb +88 -0
  137. data/lib/rubygems/credential_store/native/windows.rb +130 -0
  138. data/lib/rubygems/credential_store.rb +332 -0
  139. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/assignment.rb +1 -1
  140. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/basic_package_source.rb +7 -7
  141. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/failure_writer.rb +1 -1
  142. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/incompatibility.rb +4 -4
  143. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/package.rb +1 -1
  144. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/partial_solution.rb +1 -1
  145. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/rubygems.rb +3 -3
  146. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/solve_failure.rb +1 -1
  147. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/static_package_source.rb +3 -3
  148. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/strategy.rb +2 -2
  149. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/term.rb +1 -1
  150. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/version.rb +1 -1
  151. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/version_constraint.rb +3 -3
  152. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/version_range.rb +1 -1
  153. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/version_solver.rb +2 -2
  154. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/version_union.rb +1 -1
  155. data/lib/rubygems/vendor/pub_grub/lib/pub_grub.rb +57 -0
  156. data/lib/{bundler → rubygems}/vendor/securerandom/lib/securerandom.rb +10 -6
  157. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/common.rb +119 -119
  158. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/file.rb +13 -13
  159. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/ftp.rb +24 -24
  160. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/generic.rb +132 -132
  161. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/http.rb +18 -18
  162. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/https.rb +4 -4
  163. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/ldap.rb +14 -14
  164. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/ldaps.rb +4 -4
  165. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/mailto.rb +21 -21
  166. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/rfc2396_parser.rb +39 -39
  167. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/rfc3986_parser.rb +14 -14
  168. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/version.rb +1 -1
  169. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/ws.rb +12 -12
  170. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/wss.rb +4 -4
  171. data/lib/{bundler → rubygems}/vendor/uri/lib/uri.rb +35 -31
  172. data/lib/rubygems/yaml_serializer.rb +880 -0
  173. metadata +79 -70
  174. data/CHANGELOG.md +0 -5749
  175. data/LICENSE.md +0 -22
  176. data/README.md +0 -58
  177. data/lib/bundler/compact_index_client/cache.rb +0 -107
  178. data/lib/bundler/compact_index_client/cache_file.rb +0 -148
  179. data/lib/bundler/compact_index_client/parser.rb +0 -87
  180. data/lib/bundler/compact_index_client/updater.rb +0 -105
  181. data/lib/bundler/compact_index_client.rb +0 -92
  182. data/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb +0 -227
  183. data/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb +0 -3
  184. data/lib/bundler/vendor/connection_pool/lib/connection_pool/wrapper.rb +0 -56
  185. data/lib/bundler/vendor/connection_pool/lib/connection_pool.rb +0 -230
  186. data/lib/bundler/vendor/net-http-persistent/README.rdoc +0 -82
  187. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/connection.rb +0 -41
  188. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/pool.rb +0 -65
  189. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/timed_stack_multi.rb +0 -80
  190. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb +0 -1153
  191. data/lib/bundler/vendor/pub_grub/lib/pub_grub.rb +0 -31
  192. data/lib/bundler/vendored_persistent.rb +0 -11
  193. data/lib/bundler/yaml_serializer.rb +0 -98
  194. /data/lib/{bundler → rubygems}/vendor/pub_grub/LICENSE.txt +0 -0
  195. /data/lib/{bundler → rubygems}/vendor/securerandom/COPYING +0 -0
  196. /data/lib/{bundler → rubygems}/vendor/uri/COPYING +0 -0
@@ -105,7 +105,8 @@ module Bundler
105
105
  def filesystem_access(path, action = :write, &block)
106
106
  yield(path.dup)
107
107
  rescue Errno::EACCES => e
108
- raise unless e.message.include?(path.to_s) || action == :create
108
+ path_basename = File.basename(path.to_s)
109
+ raise unless e.message.include?(path_basename) || action == :create
109
110
 
110
111
  raise PermissionError.new(path, action)
111
112
  rescue Errno::EAGAIN
@@ -231,6 +232,21 @@ module Bundler
231
232
  destination
232
233
  end
233
234
 
235
+ # Globs for entries matching +glob+ inside of +base_path+, returning
236
+ # absolute paths. Glob metacharacters in +base_path+ are not treated as
237
+ # part of the pattern, and matched entries are joined literally, so an
238
+ # entry starting with `~` is not expanded into the home directory.
239
+ #
240
+ # Bundler runs against whatever RubyGems the host provides, so this cannot
241
+ # delegate to Gem::Util.glob_files_in_dir, which only gained the literal
242
+ # join in RubyGems 4.1.
243
+ def glob_files_in_dir(glob, base_path)
244
+ expanded_path = nil
245
+ Dir.glob(glob, base: base_path).map! do |f|
246
+ File.join(expanded_path ||= File.expand_path(base_path), f)
247
+ end
248
+ end
249
+
234
250
  private
235
251
 
236
252
  def validate_bundle_path
@@ -193,7 +193,13 @@ module Bundler
193
193
  return out if status.success?
194
194
 
195
195
  if err.include?("couldn't find remote ref") || err.include?("not our ref")
196
- raise MissingGitRevisionError.new(command_with_no_credentials, path, commit || explicit_ref, credential_filtered_uri)
196
+ default_branch = renamed_remote_default_branch if tracking_remote_default_branch?
197
+ if default_branch
198
+ out = follow_remote_default_branch(args, default_branch)
199
+ return out if out
200
+ end
201
+
202
+ raise MissingGitRevisionError.new(command_with_no_credentials, path, commit || explicit_ref || current_branch, credential_filtered_uri)
197
203
  else
198
204
  if shallow?
199
205
  args -= depth_args
@@ -298,6 +304,61 @@ module Bundler
298
304
  branch_option || ref.nil?
299
305
  end
300
306
 
307
+ def tracking_remote_default_branch?
308
+ explicit_ref.nil? && commit.nil?
309
+ end
310
+
311
+ # Returns nil on any failure, leaving HEAD untouched so the caller reports
312
+ # the original fetch failure. Runs inside the retry block, so it must not
313
+ # touch the caller's command locals.
314
+ def follow_remote_default_branch(args, default_branch)
315
+ reference = "refs/heads/#{default_branch}"
316
+ command = fetch_command(args, "#{reference}:#{reference}")
317
+ check_allowed(command)
318
+
319
+ out, err, status = capture(command, path)
320
+ unless status.success?
321
+ Bundler.ui.debug "Could not fetch #{reference} from #{credential_filtered_uri}: #{err}"
322
+ return
323
+ end
324
+
325
+ previous_branch = current_branch
326
+ begin
327
+ git "symbolic-ref", "HEAD", reference, dir: path
328
+ rescue GitError => e
329
+ Bundler.ui.debug "Could not repoint the cached clone at #{reference}: #{e.message}"
330
+ return
331
+ end
332
+ @current_branch = nil
333
+ Bundler.ui.warn "#{credential_filtered_uri} no longer has #{previous_branch}, " \
334
+ "now following its default branch #{default_branch}"
335
+ out
336
+ end
337
+
338
+ # The cached clone's HEAD branch is gone from the remote, so the remote's
339
+ # own idea of its default branch is the only thing left to follow.
340
+ def renamed_remote_default_branch
341
+ default_branch = remote_default_branch
342
+ return if default_branch.nil? || default_branch == current_branch
343
+
344
+ default_branch
345
+ end
346
+
347
+ def remote_default_branch
348
+ command = ["ls-remote", "--symref", "--", configured_uri, "HEAD"]
349
+ check_allowed(command)
350
+
351
+ out, err, status = capture(command, path)
352
+ unless status.success?
353
+ Bundler.ui.debug "Could not ask #{credential_filtered_uri} for its default branch: #{err}"
354
+ return
355
+ end
356
+
357
+ # A remote is free to advertise a ref name that is not valid UTF-8, and
358
+ # matching that as text raises out of the GitError family.
359
+ out.b[%r{^ref:\s+refs/heads/(.+?)\s+HEAD}, 1]
360
+ end
361
+
301
362
  def pinned_to_full_sha?
302
363
  full_sha_revision?(ref)
303
364
  end
@@ -369,7 +430,7 @@ module Bundler
369
430
  def configured_uri
370
431
  @configured_uri ||= if /https?:/.match?(uri)
371
432
  remote = Gem::URI(uri)
372
- config_auth = Bundler.settings[remote.to_s] || Bundler.settings[remote.host]
433
+ config_auth = Bundler.settings.credentials_for(remote)
373
434
  remote.userinfo ||= config_auth
374
435
  remote.to_s
375
436
  else
@@ -465,8 +526,8 @@ module Bundler
465
526
  args
466
527
  end
467
528
 
468
- def fetch_command(args)
469
- ["fetch", "--force", "--quiet", "--no-tags", *args, "--", configured_uri, refspec].compact
529
+ def fetch_command(args, spec = refspec)
530
+ ["fetch", "--force", "--quiet", "--no-tags", *args, "--", configured_uri, spec].compact
470
531
  end
471
532
 
472
533
  def clone_command(args)
@@ -30,6 +30,7 @@ module Bundler
30
30
 
31
31
  @copied = false
32
32
  @local = false
33
+ @cached_app_cache_path = nil
33
34
  end
34
35
 
35
36
  def remote!
@@ -91,9 +92,9 @@ module Bundler
91
92
 
92
93
  def to_s
93
94
  begin
94
- at = humanized_ref || current_branch
95
-
96
- rev = "at #{at}@#{shortref_for_display(revision)}"
95
+ at = humanized_ref
96
+ at = "#{at}@" if at
97
+ rev = "at #{at}#{shortref_for_display(revision)}"
97
98
  rescue GitError
98
99
  ""
99
100
  end
@@ -191,8 +192,13 @@ module Bundler
191
192
  set_cache_path!(app_cache_path) if use_app_cache?
192
193
 
193
194
  if requires_checkout? && !@copied
194
- fetch unless use_app_cache?
195
- checkout
195
+ Plugin.hook(Plugin::Events::GIT_BEFORE_FETCH, self)
196
+ begin
197
+ fetch unless use_app_cache?
198
+ checkout
199
+ ensure
200
+ Plugin.hook(Plugin::Events::GIT_AFTER_FETCH, self)
201
+ end
196
202
  end
197
203
 
198
204
  local_specs
@@ -272,6 +278,11 @@ module Bundler
272
278
 
273
279
  app_cache_path = app_cache_path(custom_path)
274
280
 
281
+ # When several gems share a single git source, this is called once per
282
+ # gem. Copying the repository is expensive for large repos, so skip it
283
+ # if we already populated this cache during the same command.
284
+ return if @cached_app_cache_path == app_cache_path
285
+
275
286
  migrate = try_migrate ? bare_repo?(app_cache_path) : false
276
287
 
277
288
  set_cache_path!(nil) if migrate
@@ -283,6 +294,8 @@ module Bundler
283
294
  git_proxy.checkout if migrate || requires_checkout?
284
295
  git_proxy.copy_to(app_cache_path, @submodules)
285
296
  serialize_gemspecs_in(app_cache_path)
297
+
298
+ @cached_app_cache_path = app_cache_path
286
299
  end
287
300
 
288
301
  def checkout
@@ -320,7 +333,7 @@ module Bundler
320
333
 
321
334
  def serialize_gemspecs_in(destination)
322
335
  destination = destination.expand_path(Bundler.root) if destination.relative?
323
- Gem::Util.glob_files_in_dir(@glob, destination.to_s).each do |spec_path|
336
+ SharedHelpers.glob_files_in_dir(@glob, destination.to_s).each do |spec_path|
324
337
  # Evaluate gemspecs and cache the result. Gemspecs
325
338
  # in git might require git or other dependencies.
326
339
  # The gemspecs we cache should already be evaluated.
@@ -405,10 +418,6 @@ module Bundler
405
418
  options["revision"]
406
419
  end
407
420
 
408
- def cached?
409
- cache_path.exist?
410
- end
411
-
412
421
  def git_proxy
413
422
  @git_proxy ||= GitProxy.new(cache_path, uri, options, locked_revision, self)
414
423
  end
@@ -423,10 +432,12 @@ module Bundler
423
432
  def validate_spec(_spec); end
424
433
 
425
434
  def load_gemspec(file)
426
- dirname = Pathname.new(file).dirname
427
- SharedHelpers.chdir(dirname.to_s) do
428
- stub = Gem::StubSpecification.gemspec_stub(file, install_path.parent, install_path.parent)
429
- stub.full_gem_path = dirname.expand_path(root).to_s
435
+ # Expand the path before the chdir below, since resolving it inside the
436
+ # block would base it on the gemspec directory instead of `root`.
437
+ gemspec_path = Pathname.new(file).expand_path(root)
438
+ SharedHelpers.chdir(gemspec_path.dirname.to_s) do
439
+ stub = Gem::StubSpecification.gemspec_stub(gemspec_path.to_s, install_path.parent, install_path.parent)
440
+ stub.full_gem_path = gemspec_path.dirname.to_s
430
441
  StubSpecification.from_stub(stub)
431
442
  end
432
443
  end
@@ -24,7 +24,7 @@ module Bundler
24
24
  def post_install
25
25
  run_hooks(:pre_install)
26
26
 
27
- unless @disable_extensions
27
+ unless @disable_extensions || Bundler.settings[:no_build_extension]
28
28
  build_extensions
29
29
  run_hooks(:post_build)
30
30
  end
@@ -161,7 +161,7 @@ module Bundler
161
161
 
162
162
  if File.directory?(expanded_path)
163
163
  # We sort depth-first since `<<` will override the earlier-found specs
164
- Gem::Util.glob_files_in_dir(@glob, expanded_path).sort_by {|p| -p.split(File::SEPARATOR).size }.each do |file|
164
+ SharedHelpers.glob_files_in_dir(@glob, expanded_path).sort_by {|p| -p.split(File::SEPARATOR).size }.each do |file|
165
165
  next unless spec = load_gemspec(file)
166
166
  spec.source = self
167
167
 
@@ -18,12 +18,17 @@ module Bundler
18
18
  end
19
19
 
20
20
  # Returns the cooldown days that apply to this remote, resolving the
21
- # precedence CLI > config > Gemfile per-source. Returns nil if no
22
- # cooldown applies.
21
+ # precedence CLI > config > Gemfile per-source and then raising the
22
+ # result to RubyGems' own setting. Returns nil if no cooldown applies.
23
+ #
24
+ # The resolver asks once per candidate spec, so the settings lookup is
25
+ # memoized. That snapshots the value: a Remote created before a
26
+ # settings change keeps the old one. Nothing in Bundler changes the
27
+ # cooldown setting after the sources are built (the CLI flag is applied
28
+ # before the Definition exists), so build a new Remote if you need to.
23
29
  def effective_cooldown
24
- override = Bundler.settings[:cooldown]
25
- return override if override
26
- @cooldown
30
+ return @effective_cooldown if defined?(@effective_cooldown)
31
+ @effective_cooldown = Bundler.settings.cooldown_for(@cooldown)
27
32
  end
28
33
 
29
34
  MAX_CACHE_SLUG_HOST_SIZE = 255 - 1 - 32 # 255 minus dot minus MD5 length
@@ -25,6 +25,7 @@ module Bundler
25
25
  @checksum_store = Checksum::Store.new
26
26
  @gem_installers = {}
27
27
  @gem_installers_mutex = Mutex.new
28
+ @remote_spec_for_mutex = Mutex.new
28
29
  @remote_specs_mutex = Mutex.new
29
30
 
30
31
  cooldown = options["cooldown"]
@@ -196,6 +197,8 @@ module Bundler
196
197
  "the security policy didn't allow it, with the message: #{e.message}"
197
198
  end
198
199
 
200
+ s.content_address = spec.content_address if spec.content_address
201
+
199
202
  spec.__swap__(s)
200
203
  end
201
204
 
@@ -230,8 +233,12 @@ module Bundler
230
233
  spec.post_install_message
231
234
  end
232
235
 
236
+ def uncached?(spec)
237
+ caches.none? {|cache| File.exist?(package_path(cache, spec)) }
238
+ end
239
+
233
240
  def cache(spec, custom_path = nil)
234
- cached_path = Bundler.settings[:cache_all_platforms] ? fetch_gem_if_possible(spec) : cached_gem(spec)
241
+ cached_path = fetch_gem_if_possible(spec)
235
242
  raise GemNotFound, "Missing gem file '#{spec.file_name}'." unless cached_path
236
243
  return if File.dirname(cached_path) == Bundler.app_cache.to_s
237
244
  Bundler.ui.info " * #{File.basename(cached_path)}"
@@ -406,7 +413,7 @@ module Bundler
406
413
  @cached_specs ||= begin
407
414
  idx = Index.new
408
415
 
409
- Gem::Util.glob_files_in_dir("*.gem", cache_path.to_s).each do |gemfile|
416
+ SharedHelpers.glob_files_in_dir("*.gem", cache_path.to_s).each do |gemfile|
410
417
  s ||= Bundler.rubygems.spec_from_gem(gemfile)
411
418
  s.source = self
412
419
  idx << s
@@ -437,12 +444,14 @@ module Bundler
437
444
  # Looks up a single spec in the remote sources, fetching only its own
438
445
  # name when the full remote index is not already materialized.
439
446
  def remote_spec_for(spec)
440
- return remote_specs.search(spec).first if @remote_specs || api_fetchers.empty?
447
+ @remote_spec_for_mutex.synchronize do
448
+ return remote_specs.search(spec).first if @remote_specs || api_fetchers.empty?
441
449
 
442
- index = Index.build do |idx|
443
- fetch_names(api_fetchers, [spec.name], idx)
450
+ index = Index.build do |idx|
451
+ fetch_names(api_fetchers, [spec.name], idx)
452
+ end
453
+ index.search(spec).first
444
454
  end
445
- index.search(spec).first
446
455
  end
447
456
 
448
457
  def fetch_names(fetchers, dependency_names, index)
@@ -462,10 +471,24 @@ module Bundler
462
471
  if spec.remote
463
472
  fetch_gem(spec, previous_spec)
464
473
  else
465
- cached_gem(spec)
474
+ cached_gem(spec) || refetch_gem(spec, previous_spec)
466
475
  end
467
476
  end
468
477
 
478
+ # An installed gem materializes without a remote, so a reinstall forced by
479
+ # `--redownload` has to look the gem up in the remote index again before it
480
+ # can download the archive that the cache no longer holds.
481
+ def refetch_gem(spec, previous_spec = nil)
482
+ return unless @allow_remote
483
+
484
+ remote_spec = remote_spec_for(spec)
485
+ return unless remote_spec && remote_spec.full_name == spec.full_name
486
+
487
+ path = fetch_gem(remote_spec, previous_spec)
488
+ spec.remote = remote_spec.remote
489
+ path
490
+ end
491
+
469
492
  def fetch_gem(spec, previous_spec = nil)
470
493
  spec.fetch_platform
471
494
 
@@ -545,8 +568,13 @@ module Bundler
545
568
  Bundler.ui.confirm("Fetching #{version_message(spec, previous_spec)}")
546
569
  gem_remote_fetcher = remote_fetchers.fetch(spec.remote).gem_remote_fetcher
547
570
 
548
- Gem.time("Downloaded #{spec.name} in", 0, true) do
549
- Bundler.rubygems.download_gem(spec, uri, download_cache_path, gem_remote_fetcher)
571
+ Plugin.hook(Plugin::Events::GEM_BEFORE_FETCH, spec)
572
+ begin
573
+ Gem.time("Downloaded #{spec.name} in", 0, true) do
574
+ Bundler.rubygems.download_gem(spec, uri, download_cache_path, gem_remote_fetcher)
575
+ end
576
+ ensure
577
+ Plugin.hook(Plugin::Events::GEM_AFTER_FETCH, spec)
550
578
  end
551
579
  end
552
580
 
@@ -566,7 +594,12 @@ module Bundler
566
594
  return unless remote = spec.remote
567
595
  return unless cache_slug = remote.cache_slug
568
596
 
569
- Bundler.user_cache.join("gems", cache_slug)
597
+ if Gem.respond_to?(:global_gem_cache_path)
598
+ Pathname.new(Gem.global_gem_cache_path).join(cache_slug)
599
+ else
600
+ # Fall back to old location for older RubyGems versions
601
+ Bundler.user_cache.join("gems", cache_slug)
602
+ end
570
603
  end
571
604
 
572
605
  def extension_cache_slug(spec)
@@ -574,11 +607,11 @@ module Bundler
574
607
  remote.cache_slug
575
608
  end
576
609
 
577
- # We are using a mutex to reaed and write from/to the hash.
610
+ # We are using a mutex to read and write from/to the hash.
578
611
  # The reason this double synchronization was added is for performance
579
612
  # and to lock the mutex for the shortest possible amount of time. Otherwise,
580
613
  # all threads are fighting over this mutex and when it gets acquired it gets locked
581
- # until a threads finishes downloading a gem, leaving the other threads waiting
614
+ # until a thread finishes downloading a gem, leaving the other threads waiting
582
615
  # doing nothing.
583
616
  def rubygems_gem_installer(spec, options)
584
617
  @gem_installers_mutex.synchronize { @gem_installers[spec.name] } || begin
@@ -589,6 +622,7 @@ module Bundler
589
622
 
590
623
  installer = Bundler::RubyGemsGemInstaller.at(
591
624
  path,
625
+ content_address: (spec.content_address if Gem::ContentAddress.content_addressed?(spec, validate_ruby_abi: false)),
592
626
  security_policy: Bundler.rubygems.security_policies[Bundler.settings["trust-policy"]],
593
627
  install_dir: rubygems_dir.to_s,
594
628
  bin_dir: Bundler.system_bindir.to_s,
@@ -596,7 +630,9 @@ module Bundler
596
630
  wrappers: true,
597
631
  env_shebang: true,
598
632
  build_args: options[:build_args],
599
- bundler_extension_cache_path: extension_cache_path(spec)
633
+ bundler_extension_cache_path: extension_cache_path(spec),
634
+ build_extension: Bundler.settings[:no_build_extension] ? false : nil,
635
+ install_plugin: Bundler.settings[:no_install_plugin] ? false : nil
600
636
  )
601
637
  @gem_installers_mutex.synchronize { @gem_installers[spec.name] ||= installer }
602
638
  end
@@ -178,7 +178,7 @@ module Bundler
178
178
  end
179
179
 
180
180
  def find_by_name_and_platform(name, platform)
181
- @specs.detect {|spec| spec.name == name && spec.installable_on_platform?(platform) }
181
+ lookup[name]&.detect {|spec| spec.installable_on_platform?(platform) }
182
182
  end
183
183
 
184
184
  def specs_with_additional_variants_from(other)
@@ -274,13 +274,25 @@ module Bundler
274
274
 
275
275
  valid_platform = lookup.all? do |_, specs|
276
276
  spec = specs.first
277
+ # The matching candidates returned by source.specs.search are remote
278
+ # specs that do not carry the override list themselves. Borrow it from
279
+ # the LazySpec we are validating so platform-variant validation honors
280
+ # the same overrides the install/resolve path already applies.
281
+ overrides = spec.is_a?(LazySpecification) ? Array(spec.overrides) : []
277
282
  matching_specs = spec.source.specs.search([spec.name, spec.version])
278
283
  platform_spec = MatchPlatform.select_best_platform_match(matching_specs, platform).find do |s|
279
- valid?(s)
284
+ s.matches_current_metadata_with_overrides?(overrides) && valid_dependencies?(s)
280
285
  end
281
286
 
282
287
  if platform_spec
283
- new_specs << LazySpecification.from_spec(platform_spec) unless specs.include?(platform_spec)
288
+ unless specs.include?(platform_spec)
289
+ new_lazy = LazySpecification.from_spec(platform_spec)
290
+ # Carry the overrides forward so a follow-up complete_platform
291
+ # call that picks this synthesized variant as its exemplar still
292
+ # honors the user's override list.
293
+ new_lazy.overrides = overrides if overrides.any?
294
+ new_specs << new_lazy
295
+ end
284
296
  true
285
297
  else
286
298
  false
@@ -314,7 +326,7 @@ module Bundler
314
326
  end
315
327
 
316
328
  def sorted
317
- @sorted ||= ([@specs.find {|s| s.name == "rake" }] + tsort).compact.uniq
329
+ @sorted ||= ([lookup["rake"]&.first] + tsort).compact.uniq
318
330
  rescue Gem::TSort::Cyclic => error
319
331
  cgems = extract_circular_gems(error)
320
332
  raise CyclicDependencyError, "Your bundle requires gems that depend" \
@@ -4,7 +4,8 @@ module Bundler
4
4
  class StubSpecification < RemoteSpecification
5
5
  def self.from_stub(stub)
6
6
  return stub if stub.is_a?(Bundler::StubSpecification)
7
- spec = new(stub.name, stub.version, stub.platform, nil)
7
+ content_address = stub.content_address
8
+ spec = new(stub.name, stub.version, stub.platform, nil, content_address: content_address)
8
9
  spec.stub = stub
9
10
  spec
10
11
  end
@@ -126,7 +127,9 @@ module Bundler
126
127
  def _remote_specification
127
128
  @_remote_specification ||= begin
128
129
  rs = stub.to_spec
129
- if rs.equal?(self) # happens when to_spec gets the spec from Gem.loaded_specs
130
+ # Gem::StubSpecification#to_spec may return an activated specification
131
+ # with the same name and version from a different gem installation.
132
+ if rs.equal?(self) || rs.loaded_from != loaded_from
130
133
  rs = Gem::Specification.load(loaded_from)
131
134
  Bundler.rubygems.stub_set_spec(stub, rs)
132
135
  end
@@ -6,19 +6,19 @@ source "https://rubygems.org"
6
6
  gemspec
7
7
 
8
8
  gem "irb"
9
- gem "rake", "~> 13.0"
9
+ gem "rake", ">= 13.0"
10
10
  <%- if config[:ext] -%>
11
11
 
12
12
  gem "rake-compiler"
13
13
  <%- end -%>
14
14
  <%- if config[:test] -%>
15
15
 
16
- gem "<%= config[:test] %>", "~> <%= config[:test_framework_version] %>"
16
+ gem "<%= config[:test] %>"
17
17
  <%- end -%>
18
18
  <%- if config[:linter] == "rubocop" -%>
19
19
 
20
- gem "rubocop", "~> <%= config[:linter_version] %>"
20
+ gem "rubocop"
21
21
  <%- elsif config[:linter] == "standard" -%>
22
22
 
23
- gem "standard", "~> <%= config[:linter_version] %>"
23
+ gem "standard"
24
24
  <%- end -%>
@@ -45,12 +45,12 @@ Gem::Specification.new do |spec|
45
45
  <%- end -%>
46
46
 
47
47
  # Uncomment to register a new dependency of your gem
48
- # spec.add_dependency "example-gem", "~> 1.0"
48
+ # spec.add_dependency "example-gem", ">= 1.0"
49
49
  <%- if config[:ext] == 'rust' -%>
50
- spec.add_dependency "rb_sys", "~> 0.9.128"
50
+ spec.add_dependency "rb_sys", ">= 0.9.128"
51
51
  <%- end -%>
52
52
  <%- if config[:ext] == 'go' -%>
53
- spec.add_dependency "go_gem", "~> 0.2"
53
+ spec.add_dependency "go_gem", ">= 0.2"
54
54
  <%- end -%>
55
55
 
56
56
  # For more information and examples about making a new gem, check out our
@@ -9,7 +9,7 @@ RSpec.describe <%= config[:constant_name] %> do
9
9
  it "can call into Rust" do
10
10
  result = <%= config[:constant_name] %>.hello("world")
11
11
 
12
- expect(result).to be("Hello earth, from Rust!")
12
+ expect(result).to eq("Hello world, from Rust!")
13
13
  end
14
14
  <%- else -%>
15
15
  it "does something useful" do
@@ -9,7 +9,7 @@ class <%= config[:minitest_constant_name] %> < Minitest::Test
9
9
 
10
10
  <%- if config[:ext] == 'rust' -%>
11
11
  def test_hello_world
12
- assert_equal "Hello earth, from Rust!", <%= config[:constant_name] %>.hello("world")
12
+ assert_equal "Hello world, from Rust!", <%= config[:constant_name] %>.hello("world")
13
13
  end
14
14
  <%- else -%>
15
15
  def test_it_does_something_useful
@@ -76,10 +76,6 @@ module Bundler
76
76
  level("debug")
77
77
  end
78
78
 
79
- def quiet?
80
- level("quiet")
81
- end
82
-
83
79
  def ask(msg)
84
80
  @shell.ask(msg, :green)
85
81
  end
@@ -45,10 +45,6 @@ module Bundler
45
45
  false
46
46
  end
47
47
 
48
- def quiet?
49
- false
50
- end
51
-
52
48
  def warn?
53
49
  false
54
50
  end
@@ -2,6 +2,38 @@ require_relative "empty_directory"
2
2
 
3
3
  class Bundler::Thor
4
4
  module Actions
5
+ WARNINGS = {unchanged_no_flag: "File unchanged! Either the supplied flag value not found or the content has already been inserted!"}
6
+
7
+ # Injects the given content into a file, raising an error if the contents of
8
+ # the file are not changed. Different from gsub_file, this method is reversible.
9
+ #
10
+ # ==== Parameters
11
+ # destination<String>:: Relative path to the destination root
12
+ # data<String>:: Data to add to the file. Can be given as a block.
13
+ # config<Hash>:: give :verbose => false to not log the status and the flag
14
+ # for injection (:after or :before) or :force => true for
15
+ # insert two or more times the same content.
16
+ #
17
+ # ==== Examples
18
+ #
19
+ # insert_into_file "config/environment.rb", "config.gem :thor", :after => "Rails::Initializer.run do |config|\n"
20
+ #
21
+ # insert_into_file "config/environment.rb", :after => "Rails::Initializer.run do |config|\n" do
22
+ # gems = ask "Which gems would you like to add?"
23
+ # gems.split(" ").map{ |gem| " config.gem :#{gem}" }.join("\n")
24
+ # end
25
+ #
26
+ def insert_into_file!(destination, *args, &block)
27
+ data = block_given? ? block : args.shift
28
+
29
+ config = args.shift || {}
30
+ config[:after] = /\z/ unless config.key?(:before) || config.key?(:after)
31
+ config = config.merge({error_on_no_change: true})
32
+
33
+ action InjectIntoFile.new(self, destination, data, config)
34
+ end
35
+ alias_method :inject_into_file!, :insert_into_file!
36
+
5
37
  # Injects the given content into a file. Different from gsub_file, this
6
38
  # method is reversible.
7
39
  #
@@ -21,8 +53,6 @@ class Bundler::Thor
21
53
  # gems.split(" ").map{ |gem| " config.gem :#{gem}" }.join("\n")
22
54
  # end
23
55
  #
24
- WARNINGS = {unchanged_no_flag: "File unchanged! Either the supplied flag value not found or the content has already been inserted!"}
25
-
26
56
  def insert_into_file(destination, *args, &block)
27
57
  data = block_given? ? block : args.shift
28
58
 
@@ -47,6 +77,7 @@ class Bundler::Thor
47
77
 
48
78
  @replacement = data.is_a?(Proc) ? data.call : data
49
79
  @flag = Regexp.escape(@flag) unless @flag.is_a?(Regexp)
80
+ @error_on_no_change = @config.fetch(:error_on_no_change, false)
50
81
  end
51
82
 
52
83
  def invoke!
@@ -59,6 +90,8 @@ class Bundler::Thor
59
90
  if exists?
60
91
  if replace!(/#{flag}/, content, config[:force])
61
92
  say_status(:invoke)
93
+ elsif @error_on_no_change
94
+ raise Bundler::Thor::Error, "The content of #{destination} did not change"
62
95
  elsif replacement_present?
63
96
  say_status(:unchanged, color: :blue)
64
97
  else
@@ -13,8 +13,9 @@ class Bundler::Thor
13
13
  autoload :RakeCompat, File.expand_path("rake_compat", __dir__)
14
14
  autoload :Group, File.expand_path("group", __dir__)
15
15
 
16
- # Shortcuts for help.
16
+ # Shortcuts for help and tree commands.
17
17
  HELP_MAPPINGS = %w(-h -? --help -D)
18
+ TREE_MAPPINGS = %w(-t --tree)
18
19
 
19
20
  # Bundler::Thor methods that should not be overwritten by the user.
20
21
  THOR_RESERVED_WORDS = %w(invoke shell options behavior root destination_root relative_root