bundler 4.0.21 → 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 (190) hide show
  1. checksums.yaml +4 -4
  2. data/{lib/bundler/vendor/connection_pool/LICENSE → MIT.txt} +8 -6
  3. data/bundler.gemspec +23 -6
  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/gem.rb +0 -17
  13. data/lib/bundler/cli/install.rb +4 -5
  14. data/lib/bundler/cli/lock.rb +1 -2
  15. data/lib/bundler/cli/outdated.rb +8 -3
  16. data/lib/bundler/cli/pristine.rb +2 -1
  17. data/lib/bundler/cli/update.rb +21 -12
  18. data/lib/bundler/cli.rb +14 -11
  19. data/lib/bundler/definition.rb +61 -24
  20. data/lib/bundler/dependency.rb +4 -0
  21. data/lib/bundler/dsl.rb +77 -11
  22. data/lib/bundler/endpoint_specification.rb +51 -14
  23. data/lib/bundler/env.rb +7 -24
  24. data/lib/bundler/errors.rb +34 -5
  25. data/lib/bundler/fetcher/compact_index.rb +14 -5
  26. data/lib/bundler/fetcher/connection_pools.rb +150 -0
  27. data/lib/bundler/fetcher/downloader.rb +31 -16
  28. data/lib/bundler/fetcher.rb +8 -47
  29. data/lib/bundler/injector.rb +3 -3
  30. data/lib/bundler/inline.rb +44 -4
  31. data/lib/bundler/installer/parallel_installer.rb +25 -30
  32. data/lib/bundler/installer.rb +20 -6
  33. data/lib/bundler/lazy_specification.rb +44 -17
  34. data/lib/bundler/lockfile_generator.rb +24 -2
  35. data/lib/bundler/lockfile_parser.rb +43 -5
  36. data/lib/bundler/man/bundle-add.1 +7 -4
  37. data/lib/bundler/man/bundle-add.1.ronn +8 -4
  38. data/lib/bundler/man/bundle-binstubs.1 +1 -1
  39. data/lib/bundler/man/bundle-cache.1 +2 -2
  40. data/lib/bundler/man/bundle-cache.1.ronn +2 -2
  41. data/lib/bundler/man/bundle-check.1 +1 -1
  42. data/lib/bundler/man/bundle-clean.1 +1 -1
  43. data/lib/bundler/man/bundle-config.1 +38 -6
  44. data/lib/bundler/man/bundle-config.1.ronn +143 -10
  45. data/lib/bundler/man/bundle-console.1 +1 -1
  46. data/lib/bundler/man/bundle-doctor.1 +1 -1
  47. data/lib/bundler/man/bundle-env.1 +1 -1
  48. data/lib/bundler/man/bundle-exec.1 +1 -1
  49. data/lib/bundler/man/bundle-fund.1 +1 -1
  50. data/lib/bundler/man/bundle-gem.1 +1 -1
  51. data/lib/bundler/man/bundle-help.1 +1 -1
  52. data/lib/bundler/man/bundle-info.1 +1 -1
  53. data/lib/bundler/man/bundle-init.1 +1 -1
  54. data/lib/bundler/man/bundle-install.1 +2 -2
  55. data/lib/bundler/man/bundle-install.1.ronn +4 -4
  56. data/lib/bundler/man/bundle-issue.1 +1 -1
  57. data/lib/bundler/man/bundle-licenses.1 +1 -1
  58. data/lib/bundler/man/bundle-list.1 +1 -1
  59. data/lib/bundler/man/bundle-lock.1 +3 -3
  60. data/lib/bundler/man/bundle-lock.1.ronn +5 -5
  61. data/lib/bundler/man/bundle-open.1 +1 -1
  62. data/lib/bundler/man/bundle-outdated.1 +1 -1
  63. data/lib/bundler/man/bundle-platform.1 +1 -1
  64. data/lib/bundler/man/bundle-plugin.1 +1 -1
  65. data/lib/bundler/man/bundle-pristine.1 +1 -1
  66. data/lib/bundler/man/bundle-remove.1 +1 -1
  67. data/lib/bundler/man/bundle-show.1 +1 -1
  68. data/lib/bundler/man/bundle-update.1 +3 -3
  69. data/lib/bundler/man/bundle-update.1.ronn +3 -5
  70. data/lib/bundler/man/bundle-version.1 +1 -1
  71. data/lib/bundler/man/bundle.1 +1 -1
  72. data/lib/bundler/man/gemfile.5 +45 -1
  73. data/lib/bundler/man/gemfile.5.ronn +53 -0
  74. data/lib/bundler/match_metadata.rb +20 -0
  75. data/lib/bundler/match_platform.rb +25 -2
  76. data/lib/bundler/match_remote_metadata.rb +21 -6
  77. data/lib/bundler/materialization.rb +2 -1
  78. data/lib/bundler/override.rb +69 -0
  79. data/lib/bundler/plugin/dsl.rb +3 -5
  80. data/lib/bundler/plugin/events.rb +55 -13
  81. data/lib/bundler/plugin/index.rb +87 -11
  82. data/lib/bundler/plugin/installer/path.rb +1 -1
  83. data/lib/bundler/plugin/installer.rb +13 -5
  84. data/lib/bundler/plugin.rb +58 -30
  85. data/lib/bundler/remote_specification.rb +6 -3
  86. data/lib/bundler/resolver/base.rb +2 -1
  87. data/lib/bundler/resolver/incompatibility.rb +1 -1
  88. data/lib/bundler/resolver.rb +73 -23
  89. data/lib/bundler/ruby_version.rb +0 -2
  90. data/lib/bundler/rubygems_ext.rb +71 -1
  91. data/lib/bundler/rubygems_gem_installer.rb +41 -2
  92. data/lib/bundler/rubygems_integration.rb +7 -2
  93. data/lib/bundler/runtime.rb +69 -2
  94. data/lib/bundler/self_manager.rb +9 -15
  95. data/lib/bundler/settings.rb +373 -30
  96. data/lib/bundler/shared_helpers.rb +2 -1
  97. data/lib/bundler/source/git/git_proxy.rb +2 -10
  98. data/lib/bundler/source/git.rb +15 -6
  99. data/lib/bundler/source/path/installer.rb +1 -1
  100. data/lib/bundler/source/rubygems/remote.rb +10 -5
  101. data/lib/bundler/source/rubygems.rb +48 -12
  102. data/lib/bundler/spec_set.rb +16 -4
  103. data/lib/bundler/stub_specification.rb +2 -1
  104. data/lib/bundler/templates/newgem/Gemfile.tt +4 -4
  105. data/lib/bundler/templates/newgem/newgem.gemspec.tt +3 -3
  106. data/lib/bundler/templates/newgem/spec/newgem_spec.rb.tt +1 -1
  107. data/lib/bundler/templates/newgem/test/minitest/test_newgem.rb.tt +1 -1
  108. data/lib/bundler/ui/shell.rb +0 -4
  109. data/lib/bundler/ui/silent.rb +0 -4
  110. data/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb +35 -2
  111. data/lib/bundler/vendor/thor/lib/thor/base.rb +2 -1
  112. data/lib/bundler/vendor/thor/lib/thor/shell/basic.rb +14 -6
  113. data/lib/bundler/vendor/thor/lib/thor/shell/color.rb +2 -0
  114. data/lib/bundler/vendor/thor/lib/thor/version.rb +1 -1
  115. data/lib/bundler/vendor/thor/lib/thor.rb +34 -1
  116. data/lib/bundler/vendored_pub_grub.rb +5 -2
  117. data/lib/bundler/vendored_securerandom.rb +11 -8
  118. data/lib/bundler/vendored_uri.rb +14 -14
  119. data/lib/bundler/version.rb +1 -1
  120. data/lib/bundler/worker.rb +3 -2
  121. data/lib/bundler.rb +1 -1
  122. data/lib/rubygems/compact_index_client/cache.rb +122 -0
  123. data/lib/rubygems/compact_index_client/cache_file.rb +147 -0
  124. data/lib/rubygems/compact_index_client/http_fetcher.rb +88 -0
  125. data/lib/rubygems/compact_index_client/parser.rb +90 -0
  126. data/lib/rubygems/compact_index_client/updater.rb +113 -0
  127. data/lib/rubygems/compact_index_client.rb +111 -0
  128. data/lib/rubygems/cooldown_settings.rb +57 -0
  129. data/lib/rubygems/credential_store/native/linux.rb +97 -0
  130. data/lib/rubygems/credential_store/native/macos.rb +88 -0
  131. data/lib/rubygems/credential_store/native/windows.rb +130 -0
  132. data/lib/rubygems/credential_store.rb +332 -0
  133. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/assignment.rb +1 -1
  134. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/basic_package_source.rb +7 -7
  135. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/failure_writer.rb +1 -1
  136. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/incompatibility.rb +4 -4
  137. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/package.rb +1 -1
  138. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/partial_solution.rb +1 -1
  139. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/rubygems.rb +3 -3
  140. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/solve_failure.rb +1 -1
  141. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/static_package_source.rb +3 -3
  142. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/strategy.rb +2 -2
  143. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/term.rb +1 -1
  144. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/version.rb +1 -1
  145. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/version_constraint.rb +3 -3
  146. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/version_range.rb +1 -1
  147. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/version_solver.rb +2 -2
  148. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/version_union.rb +1 -1
  149. data/lib/rubygems/vendor/pub_grub/lib/pub_grub.rb +57 -0
  150. data/lib/{bundler → rubygems}/vendor/securerandom/lib/securerandom.rb +10 -6
  151. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/common.rb +119 -119
  152. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/file.rb +13 -13
  153. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/ftp.rb +24 -24
  154. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/generic.rb +132 -132
  155. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/http.rb +18 -18
  156. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/https.rb +4 -4
  157. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/ldap.rb +14 -14
  158. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/ldaps.rb +4 -4
  159. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/mailto.rb +21 -21
  160. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/rfc2396_parser.rb +39 -39
  161. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/rfc3986_parser.rb +14 -14
  162. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/version.rb +1 -1
  163. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/ws.rb +12 -12
  164. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/wss.rb +4 -4
  165. data/lib/{bundler → rubygems}/vendor/uri/lib/uri.rb +35 -31
  166. data/lib/rubygems/yaml_serializer.rb +880 -0
  167. metadata +77 -68
  168. data/CHANGELOG.md +0 -5784
  169. data/LICENSE.md +0 -22
  170. data/README.md +0 -58
  171. data/lib/bundler/compact_index_client/cache.rb +0 -107
  172. data/lib/bundler/compact_index_client/cache_file.rb +0 -148
  173. data/lib/bundler/compact_index_client/parser.rb +0 -87
  174. data/lib/bundler/compact_index_client/updater.rb +0 -105
  175. data/lib/bundler/compact_index_client.rb +0 -92
  176. data/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb +0 -227
  177. data/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb +0 -3
  178. data/lib/bundler/vendor/connection_pool/lib/connection_pool/wrapper.rb +0 -56
  179. data/lib/bundler/vendor/connection_pool/lib/connection_pool.rb +0 -230
  180. data/lib/bundler/vendor/net-http-persistent/README.rdoc +0 -82
  181. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/connection.rb +0 -41
  182. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/pool.rb +0 -65
  183. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/timed_stack_multi.rb +0 -80
  184. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb +0 -1153
  185. data/lib/bundler/vendor/pub_grub/lib/pub_grub.rb +0 -31
  186. data/lib/bundler/vendored_persistent.rb +0 -11
  187. data/lib/bundler/yaml_serializer.rb +0 -98
  188. /data/lib/{bundler → rubygems}/vendor/pub_grub/LICENSE.txt +0 -0
  189. /data/lib/{bundler → rubygems}/vendor/securerandom/COPYING +0 -0
  190. /data/lib/{bundler → rubygems}/vendor/uri/COPYING +0 -0
@@ -430,7 +430,7 @@ module Bundler
430
430
  def configured_uri
431
431
  @configured_uri ||= if /https?:/.match?(uri)
432
432
  remote = Gem::URI(uri)
433
- config_auth = Bundler.settings[remote.to_s] || Bundler.settings[remote.host]
433
+ config_auth = Bundler.settings.credentials_for(remote)
434
434
  remote.userinfo ||= config_auth
435
435
  remote.to_s
436
436
  else
@@ -507,15 +507,7 @@ module Bundler
507
507
 
508
508
  return ["git", *opts, *cmd] unless dir
509
509
 
510
- # With safe.bareRepository=explicit, git refuses to discover a bare
511
- # repository from -C, so the cache clone is named with --git-dir.
512
- # Working trees, like a local override, still go through -C.
513
- location = bare_repo?(dir) ? "--git-dir" : "-C"
514
- ["git", location, dir.to_s, *opts, *cmd]
515
- end
516
-
517
- def bare_repo?(dir)
518
- File.exist?(File.join(dir, "objects")) && File.exist?(File.join(dir, "HEAD"))
510
+ ["git", "-C", dir.to_s, *opts, *cmd]
519
511
  end
520
512
 
521
513
  def extra_clone_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!
@@ -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
@@ -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
@@ -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
@@ -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)}"
@@ -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
@@ -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
@@ -311,13 +311,11 @@ class Bundler::Thor
311
311
  end
312
312
 
313
313
  def show_diff(destination, content) #:nodoc:
314
- diff_cmd = ENV["THOR_DIFF"] || ENV["RAILS_DIFF"] || "diff -u"
315
-
316
314
  require "tempfile"
317
315
  Tempfile.open(File.basename(destination), File.dirname(destination), binmode: true) do |temp|
318
316
  temp.write content
319
317
  temp.rewind
320
- system %(#{diff_cmd} "#{destination}" "#{temp.path}")
318
+ system(*diff_tool, destination, temp.path)
321
319
  end
322
320
  end
323
321
 
@@ -369,15 +367,25 @@ class Bundler::Thor
369
367
 
370
368
  def merge(destination, content) #:nodoc:
371
369
  require "tempfile"
372
- Tempfile.open([File.basename(destination), File.extname(destination)], File.dirname(destination)) do |temp|
370
+ Tempfile.open([File.basename(destination), File.extname(destination)], File.dirname(destination), binmode: true) do |temp|
373
371
  temp.write content
374
372
  temp.rewind
375
- system(merge_tool, temp.path, destination)
373
+ system(*merge_tool, temp.path, destination)
376
374
  end
377
375
  end
378
376
 
379
377
  def merge_tool #:nodoc:
380
- @merge_tool ||= ENV["THOR_MERGE"] || "git difftool --no-index"
378
+ @merge_tool ||= begin
379
+ require "shellwords"
380
+ Shellwords.split(ENV["THOR_MERGE"] || "git difftool --no-index")
381
+ end
382
+ end
383
+
384
+ def diff_tool #:nodoc:
385
+ @diff_cmd ||= begin
386
+ require "shellwords"
387
+ Shellwords.split(ENV["THOR_DIFF"] || ENV["RAILS_DIFF"] || "diff -u")
388
+ end
381
389
  end
382
390
  end
383
391
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative "basic"
2
4
 
3
5
  class Bundler::Thor
@@ -1,3 +1,3 @@
1
1
  class Bundler::Thor
2
- VERSION = "1.4.0"
2
+ VERSION = "1.5.0"
3
3
  end
@@ -625,7 +625,7 @@ class Bundler::Thor
625
625
  # alias name.
626
626
  def find_command_possibilities(meth)
627
627
  len = meth.to_s.length
628
- possibilities = all_commands.reject { |_k, c| c.hidden? }.merge(map).keys.select { |n| meth == n[0, len] }.sort
628
+ possibilities = all_commands.reject {|k, v| v.is_a?(HiddenCommand) }.merge(map).keys.select { |n| meth == n[0, len] }.sort
629
629
  unique_possibilities = possibilities.map { |k| map[k] || k }.uniq
630
630
 
631
631
  if possibilities.include?(meth)
@@ -671,4 +671,37 @@ class Bundler::Thor
671
671
  self.class.help(shell, subcommand)
672
672
  end
673
673
  end
674
+
675
+ map TREE_MAPPINGS => :tree
676
+
677
+ desc "tree", "Print a tree of all available commands"
678
+ def tree
679
+ build_command_tree(self.class, "")
680
+ end
681
+
682
+ private
683
+
684
+ def build_command_tree(klass, indent)
685
+ # Print current class name if it's not the root Bundler::Thor class
686
+ unless klass == Bundler::Thor
687
+ say "#{indent}#{klass.namespace || 'default'}", :blue
688
+ indent = "#{indent} "
689
+ end
690
+
691
+ # Print all commands for this class
692
+ visible_commands = klass.commands.reject { |_, cmd| cmd.hidden? || cmd.name == "help" }
693
+ commands_count = visible_commands.count
694
+ visible_commands.sort.each_with_index do |(command_name, command), i|
695
+ description = command.description.split("\n").first || ""
696
+ icon = i == (commands_count - 1) ? "└─" : "├─"
697
+ say "#{indent}#{icon} ", nil, false
698
+ say command_name, :green, false
699
+ say " (#{description})" unless description.empty?
700
+ end
701
+
702
+ # Print all subcommands (from registered Bundler::Thor subclasses)
703
+ klass.subcommand_classes.each do |_, subclass|
704
+ build_command_tree(subclass, indent)
705
+ end
706
+ end
674
707
  end
@@ -1,4 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Bundler; end
4
- require_relative "vendor/pub_grub/lib/pub_grub"
3
+ # Reuse RubyGems' vendored PubGrub (Gem::PubGrub). The Bundler gem ships a copy
4
+ # under lib/rubygems/vendor, so this resolves even on RubyGems versions that
5
+ # predate it.
6
+
7
+ require "rubygems/vendor/pub_grub/lib/pub_grub"
@@ -1,12 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Use RubyGems vendored copy when available. Otherwise fallback to Bundler
4
- # vendored copy. The vendored copy in Bundler can be removed once support for
5
- # RubyGems 3.5.18 is dropped.
3
+ # Reuse RubyGems' vendored SecureRandom (Gem::SecureRandom). The Bundler gem
4
+ # ships a copy under lib/rubygems/vendor, so this resolves even on RubyGems
5
+ # versions that predate it. Fall back to the stdlib only when no vendored copy
6
+ # is available at all.
6
7
 
7
- begin
8
- require "rubygems/vendored_securerandom"
9
- rescue LoadError
10
- require_relative "vendor/securerandom/lib/securerandom"
11
- Gem::SecureRandom = Bundler::SecureRandom
8
+ unless defined?(Gem::SecureRandom)
9
+ begin
10
+ require "rubygems/vendor/securerandom/lib/securerandom"
11
+ rescue LoadError
12
+ require "securerandom"
13
+ Gem::SecureRandom = SecureRandom
14
+ end
12
15
  end
@@ -1,21 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Bundler; end
3
+ # Reuse RubyGems' vendored URI (Gem::URI). The Bundler gem ships a copy under
4
+ # lib/rubygems/vendor, so this resolves even on RubyGems versions that predate
5
+ # it. Fall back to the stdlib only when no vendored copy is available at all.
4
6
 
5
- # Use RubyGems vendored copy when available. Otherwise fallback to Bundler
6
- # vendored copy. The vendored copy in Bundler can be removed once support for
7
- # RubyGems 3.5 is dropped.
7
+ unless defined?(Gem::URI)
8
+ begin
9
+ require "rubygems/vendor/uri/lib/uri"
10
+ rescue LoadError
11
+ require "uri"
12
+ Gem::URI = URI
8
13
 
9
- begin
10
- require "rubygems/vendor/uri/lib/uri"
11
- rescue LoadError
12
- require_relative "vendor/uri/lib/uri"
13
- Gem::URI = Bundler::URI
14
-
15
- module Gem
16
- def URI(uri) # rubocop:disable Naming/MethodName
17
- Bundler::URI(uri)
14
+ module Gem
15
+ def URI(uri) # rubocop:disable Naming/MethodName
16
+ Kernel.URI(uri)
17
+ end
18
+ module_function :URI
18
19
  end
19
- module_function :URI
20
20
  end
21
21
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: false
2
2
 
3
3
  module Bundler
4
- VERSION = "4.0.21".freeze
4
+ VERSION = "4.1.0.beta1".freeze
5
5
 
6
6
  def self.bundler_major_version
7
7
  @bundler_major_version ||= gem_version.segments.first