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
data/lib/bundler/cli.rb CHANGED
@@ -276,10 +276,12 @@ module Bundler
276
276
  method_option "with", type: :array, banner: "Include gems that are part of the specified named group (removed)."
277
277
  method_option "cooldown", type: :numeric, banner: "Only consider gem versions published at least N days ago. Use 0 to disable."
278
278
  def install
279
- %w[clean deployment frozen no-prune path shebang without with].each do |option|
279
+ %w[clean deployment frozen path shebang without with].each do |option|
280
280
  remembered_flag_deprecation(option)
281
281
  end
282
282
 
283
+ remembered_flag_deprecation("no-prune", option_name: "keep_outdated_cache")
284
+
283
285
  print_remembered_flag_deprecation("--system", "path.system", "true") if ARGV.include?("--system")
284
286
 
285
287
  remembered_flag_deprecation("deployment", negative: true)
@@ -317,7 +319,7 @@ module Bundler
317
319
  method_option "source", type: :array, banner: "Update a specific source (and all gems associated with it)"
318
320
  method_option "force", type: :boolean, aliases: "--redownload", banner: "Force reinstalling every gem, even if already installed"
319
321
  method_option "ruby", type: :boolean, banner: "Update ruby specified in Gemfile.lock"
320
- method_option "bundler", type: :string, lazy_default: ">= #{Bundler::VERSION}", banner: "Update the locked version of bundler"
322
+ method_option "bundler", type: :string, lazy_default: "> 0.a", banner: "Update the locked version of bundler"
321
323
  method_option "patch", type: :boolean, banner: "Prefer updating only to next patch version"
322
324
  method_option "minor", type: :boolean, banner: "Prefer updating only to next minor version"
323
325
  method_option "major", type: :boolean, banner: "Prefer updating to next major version (default)"
@@ -405,7 +407,8 @@ module Bundler
405
407
  method_option "glob", type: :string, banner: "The location of a dependency's .gemspec, expanded within Ruby (single quotes recommended)"
406
408
  method_option "quiet", type: :boolean, banner: "Only output warnings and errors."
407
409
  method_option "skip-install", type: :boolean, banner: "Adds gem to the Gemfile but does not install it"
408
- method_option "optimistic", type: :boolean, banner: "Adds optimistic declaration of version to gem"
410
+ method_option "optimistic", type: :boolean, banner: "Ignored (now default behavior)"
411
+ method_option "pessimistic", type: :boolean, banner: "Adds pessimistic declaration of version to gem"
409
412
  method_option "strict", type: :boolean, banner: "Adds strict declaration of version to gem"
410
413
  method_option "cooldown", type: :numeric, banner: "Only consider gem versions published at least N days ago. Use 0 to disable."
411
414
  def add(*gems)
@@ -472,9 +475,8 @@ module Bundler
472
475
  print_remembered_flag_deprecation("--all", "cache_all", "true") if ARGV.include?("--all")
473
476
  print_remembered_flag_deprecation("--no-all", "cache_all", "false") if ARGV.include?("--no-all")
474
477
 
475
- %w[frozen no-prune].each do |option|
476
- remembered_flag_deprecation(option)
477
- end
478
+ remembered_flag_deprecation("frozen")
479
+ remembered_flag_deprecation("no-prune", option_name: "keep_outdated_cache")
478
480
 
479
481
  if flag_passed?("--path")
480
482
  removed_message =
@@ -642,7 +644,7 @@ module Bundler
642
644
  method_option "pre", type: :boolean, banner: "If updating, always choose the highest allowed version, regardless of prerelease status"
643
645
  method_option "strict", type: :boolean, banner: "If updating, do not allow any gem to be updated past latest --patch | --minor | --major"
644
646
  method_option "conservative", type: :boolean, banner: "If updating, use bundle install conservative update behavior and do not allow shared dependencies to be updated"
645
- method_option "bundler", type: :string, lazy_default: ">= #{Bundler::VERSION}", banner: "Update the locked version of bundler"
647
+ method_option "bundler", type: :string, lazy_default: "> 0.a", banner: "Update the locked version of bundler"
646
648
  method_option "cooldown", type: :numeric, banner: "Only consider gem versions published at least N days ago. Use 0 to disable."
647
649
  def lock
648
650
  require_relative "cli/lock"
@@ -759,9 +761,6 @@ module Bundler
759
761
  config[:current_command] = original_command
760
762
  end
761
763
 
762
- def current_command=(command)
763
- end
764
-
765
764
  def print_command
766
765
  return unless Bundler.ui.debug?
767
766
  cmd = current_command
@@ -787,9 +786,13 @@ module Bundler
787
786
  return unless SharedHelpers.md5_available?
788
787
 
789
788
  require_relative "vendored_uri"
789
+ require "rubygems/compact_index_client"
790
+ if Gem::CompactIndexClient.respond_to?(:filesystem_access=)
791
+ Gem::CompactIndexClient.filesystem_access = SharedHelpers.method(:filesystem_access)
792
+ end
790
793
  remote = Source::Rubygems::Remote.new(Gem::URI("https://rubygems.org"))
791
794
  cache_path = Bundler.user_cache.join("compact_index", remote.cache_slug)
792
- latest = Bundler::CompactIndexClient.new(cache_path).latest_version("bundler")
795
+ latest = Gem::CompactIndexClient.new(cache_path).latest_version("bundler")
793
796
  return unless latest
794
797
 
795
798
  current = Gem::Version.new(VERSION)
@@ -10,13 +10,14 @@ module Bundler
10
10
  attr_accessor :no_lock
11
11
  end
12
12
 
13
- attr_writer :lockfile
13
+ attr_writer :lockfile, :overrides
14
14
 
15
15
  attr_reader(
16
16
  :dependencies,
17
17
  :locked_checksums,
18
18
  :locked_deps,
19
19
  :locked_gems,
20
+ :overrides,
20
21
  :platforms,
21
22
  :ruby_version,
22
23
  :lockfile,
@@ -37,7 +38,10 @@ module Bundler
37
38
 
38
39
  raise GemfileNotFound, "#{gemfile} not found" unless gemfile.file?
39
40
 
40
- Dsl.evaluate(gemfile, lockfile, unlock)
41
+ Plugin.hook(Plugin::Events::GEM_BEFORE_EVAL, gemfile, lockfile)
42
+ Dsl.evaluate(gemfile, lockfile, unlock).tap do |definition|
43
+ Plugin.hook(Plugin::Events::GEM_AFTER_EVAL, definition)
44
+ end
41
45
  end
42
46
 
43
47
  #
@@ -58,7 +62,7 @@ module Bundler
58
62
  # to be updated or true if all gems should be updated
59
63
  # @param ruby_version [Bundler::RubyVersion, nil] Requested Ruby Version
60
64
  # @param optional_groups [Array(String)] A list of optional groups
61
- def initialize(lockfile, dependencies, sources, unlock, ruby_version = nil, optional_groups = [], gemfiles = [])
65
+ def initialize(lockfile, dependencies, sources, unlock, ruby_version = nil, optional_groups = [], gemfiles = [], overrides = [])
62
66
  unlock ||= {}
63
67
 
64
68
  if unlock == true
@@ -88,6 +92,7 @@ module Bundler
88
92
  @specs = nil
89
93
  @ruby_version = ruby_version
90
94
  @gemfiles = gemfiles
95
+ @overrides = overrides
91
96
 
92
97
  @lockfile = lockfile
93
98
  @lockfile_contents = String.new
@@ -109,6 +114,7 @@ module Bundler
109
114
  @locked_bundler_version = @locked_gems.bundler_version
110
115
  @locked_ruby_version = @locked_gems.ruby_version
111
116
  @locked_deps = @locked_gems.dependencies
117
+ Override.attach(@locked_gems.specs, @overrides)
112
118
  @originally_locked_specs = SpecSet.new(@locked_gems.specs)
113
119
  @originally_locked_sources = @locked_gems.sources
114
120
  @locked_checksums = @locked_gems.checksums
@@ -192,7 +198,7 @@ module Bundler
192
198
 
193
199
  sources.cached!
194
200
 
195
- if options[:add_checksums] || (!options[:local] && (install_needed? || @locked_spec_with_empty_checksums))
201
+ if options[:add_checksums] || (!options[:local] && (install_needed? || refetch_needed?(options)))
196
202
  sources.remote!
197
203
  true
198
204
  else
@@ -509,12 +515,6 @@ module Bundler
509
515
  "Your Ruby version is #{actual}, but your Gemfile specified #{expected}"
510
516
  when :engine_version
511
517
  "Your #{Bundler::RubyVersion.system.engine} version is #{actual}, but your Gemfile specified #{ruby_version.engine} #{expected}"
512
- when :patchlevel
513
- if !expected.is_a?(String)
514
- "The Ruby patchlevel in your Gemfile must be a string"
515
- else
516
- "Your Ruby patchlevel is #{actual}, but your Gemfile specified #{expected}"
517
- end
518
518
  end
519
519
 
520
520
  raise RubyVersionMismatch, msg
@@ -627,6 +627,17 @@ module Bundler
627
627
  resolve_needed? || missing_specs?
628
628
  end
629
629
 
630
+ # Reinstalling installs from the cached archive, and `bundle cache` copies it
631
+ # into the app cache, so a cache emptied by the `prune` setting or by hand has
632
+ # to go back to the remotes to refill it.
633
+ def refetch_needed?(options)
634
+ return false unless options[:force] || options["cache-archives"]
635
+
636
+ resolve.for(requested_dependencies, [Bundler.local_platform]).any? do |spec|
637
+ spec.source.is_a?(Source::Rubygems) && spec.source.uncached?(spec)
638
+ end
639
+ end
640
+
630
641
  def something_changed?
631
642
  return true unless lockfile_exists?
632
643
 
@@ -639,7 +650,7 @@ module Bundler
639
650
  @missing_lockfile_dep ||
640
651
  @unlocking_bundler ||
641
652
  @locked_spec_with_missing_checksums ||
642
- empty_checksums_actionable? ||
653
+ @locked_spec_with_empty_checksums ||
643
654
  @locked_spec_with_missing_deps ||
644
655
  @locked_spec_with_invalid_deps
645
656
  end
@@ -648,16 +659,6 @@ module Bundler
648
659
  unlocking? || something_changed?
649
660
  end
650
661
 
651
- # Only a remote fetch can fill an empty CHECKSUMS entry, so it justifies a
652
- # resolution only when one is coming. Resolving locally for it would repeat
653
- # on every `Bundler.setup` without changing the lockfile. Frozen mode still
654
- # has to refuse the entry.
655
- def empty_checksums_actionable?
656
- return false unless @locked_spec_with_empty_checksums
657
-
658
- Bundler.frozen_bundle? || !sources.local_mode?
659
- end
660
-
661
662
  def should_add_extra_platforms?
662
663
  !lockfile_exists? && Bundler::MatchPlatform.generic_local_platform_is_ruby? && !Bundler.settings[:force_ruby_platform]
663
664
  end
@@ -671,7 +672,20 @@ module Bundler
671
672
  end
672
673
 
673
674
  def expanded_dependencies
674
- dependencies_with_bundler + metadata_dependencies
675
+ apply_overrides_to(dependencies_with_bundler) + metadata_dependencies
676
+ end
677
+
678
+ def apply_overrides_to(deps)
679
+ return deps if @overrides.empty?
680
+ deps.map {|dep| apply_override_to(dep) }
681
+ end
682
+
683
+ def apply_override_to(dep)
684
+ override = Override.find_for(@overrides, dep.name, :version)
685
+ return dep unless override
686
+ new_dep = dep.dup
687
+ new_dep.instance_variable_set(:@requirement, override.apply_to(dep.requirement))
688
+ new_dep
675
689
  end
676
690
 
677
691
  def dependencies_with_bundler
@@ -1070,7 +1084,7 @@ module Bundler
1070
1084
  @locked_specs.delete(locked_specs.select {|s| s.source != dep.source })
1071
1085
  end
1072
1086
 
1073
- unless dep.matches_spec?(locked_specs.first)
1087
+ unless apply_override_to(dep).matches_spec?(locked_specs.first)
1074
1088
  @gems_to_unlock << name
1075
1089
  dep_changed = true
1076
1090
  end
@@ -1080,9 +1094,32 @@ module Bundler
1080
1094
  @changed_dependencies << name if dep_changed
1081
1095
  end
1082
1096
 
1097
+ converge_overrides_outside_dependencies
1098
+
1083
1099
  @changed_dependencies.any?
1084
1100
  end
1085
1101
 
1102
+ def converge_overrides_outside_dependencies
1103
+ @overrides.each do |override|
1104
+ # :all overrides are intentionally not pre-unlocked. They take effect on
1105
+ # fresh resolution (no lockfile) or when the user runs `bundle update`.
1106
+ # Forcing a full re-resolve from a single :all directive would surprise
1107
+ # users with unrelated dependency churn.
1108
+ next unless override.target.is_a?(String)
1109
+
1110
+ name = override.target
1111
+ next if @changed_dependencies.include?(name)
1112
+ next if @originally_locked_specs[name].empty?
1113
+ # version: overrides on direct deps are detected in the per-dep
1114
+ # converge_dependencies loop via apply_override_to + matches_spec?.
1115
+ # Other fields are not visible there, so they always reach here.
1116
+ next if override.field == :version && @dependencies.any? {|d| d.name == name }
1117
+
1118
+ @gems_to_unlock << name
1119
+ @changed_dependencies << name
1120
+ end
1121
+ end
1122
+
1086
1123
  # Remove elements from the locked specs that are expired. This will most
1087
1124
  # commonly happen if the Gemfile has changed since the lockfile was last
1088
1125
  # generated
@@ -1318,7 +1355,7 @@ module Bundler
1318
1355
 
1319
1356
  def new_resolution_base(last_resolve:, unlock:)
1320
1357
  new_resolution_platforms = @current_platform_missing ? @new_platforms + [Bundler.local_platform] : @new_platforms
1321
- Resolver::Base.new(source_requirements, expanded_dependencies, last_resolve, @platforms, locked_specs: @originally_locked_specs, unlock: unlock, prerelease: gem_version_promoter.pre?, prefer_local: @prefer_local, new_platforms: new_resolution_platforms, explicit_unlocks: @explicit_unlocks)
1358
+ Resolver::Base.new(source_requirements, expanded_dependencies, last_resolve, @platforms, locked_specs: @originally_locked_specs, unlock: unlock, prerelease: gem_version_promoter.pre?, prefer_local: @prefer_local, new_platforms: new_resolution_platforms, overrides: @overrides, explicit_unlocks: @explicit_unlocks)
1322
1359
  end
1323
1360
 
1324
1361
  def new_resolver(base)
@@ -118,6 +118,10 @@ module Bundler
118
118
  !gemspec_dev_dep?
119
119
  end
120
120
 
121
+ def plugin?
122
+ @plugin ||= @options.fetch("plugin", false)
123
+ end
124
+
121
125
  def current_env?
122
126
  return true unless env
123
127
  if env.is_a?(Hash)
data/lib/bundler/dsl.rb CHANGED
@@ -22,7 +22,7 @@ module Bundler
22
22
  GITHUB_PULL_REQUEST_URL = %r{\Ahttps://github\.com/([A-Za-z0-9_\-\.]+/[A-Za-z0-9_\-\.]+)/pull/(\d+)\z}
23
23
  GITLAB_MERGE_REQUEST_URL = %r{\Ahttps://gitlab\.com/([A-Za-z0-9_\-\./]+)/-/merge_requests/(\d+)\z}
24
24
 
25
- attr_reader :gemspecs, :gemfile
25
+ attr_reader :gemspecs, :gemfile, :overrides
26
26
  attr_accessor :dependencies
27
27
 
28
28
  def initialize
@@ -40,6 +40,7 @@ module Bundler
40
40
  @gemfile = nil
41
41
  @gemfiles = []
42
42
  @lockfile = nil
43
+ @overrides = []
43
44
  add_git_sources
44
45
  end
45
46
 
@@ -123,9 +124,15 @@ module Bundler
123
124
 
124
125
  if options.key?("type")
125
126
  options["type"] = options["type"].to_s
126
- unless Plugin.source?(options["type"])
127
+ unless (source_plugin = Plugin.source_plugin(options["type"]))
127
128
  raise InvalidOption, "No plugin sources available for #{options["type"]}"
128
129
  end
130
+ # Implicitly add a dependency on source plugins who are named bundler-source-<type>,
131
+ # and aren't already mentioned in the Gemfile.
132
+ # See also Plugin::DSL#source
133
+ if source_plugin.start_with?("bundler-source-") && !@dependencies.any? {|d| d.name == source_plugin }
134
+ plugin(source_plugin)
135
+ end
129
136
 
130
137
  unless block_given?
131
138
  raise InvalidOption, "You need to pass a block to #source with :type option"
@@ -188,10 +195,32 @@ module Bundler
188
195
  with_source(git_source) { yield }
189
196
  end
190
197
 
198
+ SUPPORTED_OVERRIDE_FIELDS = [:version, :required_ruby_version, :required_rubygems_version].freeze
199
+ SUPPORTED_OVERRIDE_SYMBOL_OPERATIONS = [:ignore_upper].freeze
200
+
201
+ def override(target, **operations)
202
+ validate_override_target!(target)
203
+
204
+ if target == :all && operations.key?(:version)
205
+ raise ArgumentError, "`override :all, version:` is not allowed; version requirements are per-gem"
206
+ end
207
+
208
+ operations.each do |field, operation|
209
+ validate_override_field!(field)
210
+ validate_override_operation!(operation)
211
+ validate_override_uniqueness!(target, field)
212
+ end
213
+
214
+ source_location = caller_locations(1, 1)&.first
215
+ operations.each do |field, operation|
216
+ @overrides << Override.new(target, field, operation, source_location: source_location)
217
+ end
218
+ end
219
+
191
220
  def to_definition(lockfile, unlock)
192
221
  check_primary_source_safety
193
222
  lockfile = @lockfile unless @lockfile.nil?
194
- Definition.new(lockfile, @dependencies, @sources, unlock, @ruby_version, @optional_groups, @gemfiles)
223
+ Definition.new(lockfile, @dependencies, @sources, unlock, @ruby_version, @optional_groups, @gemfiles, @overrides)
195
224
  end
196
225
 
197
226
  def group(*args, &blk)
@@ -233,8 +262,15 @@ module Bundler
233
262
  @env = old
234
263
  end
235
264
 
236
- def plugin(*args)
237
- # Pass on
265
+ def plugin(name, *args)
266
+ options = args.last.is_a?(Hash) ? args.pop.dup : {}
267
+ version = args || [">= 0"]
268
+
269
+ normalize_options(name, version, options)
270
+ options["plugin"] = true
271
+ options["require"] = false
272
+
273
+ add_dependency(name, version, options)
238
274
  end
239
275
 
240
276
  def method_missing(name, *args)
@@ -248,6 +284,39 @@ module Bundler
248
284
 
249
285
  private
250
286
 
287
+ def validate_override_target!(target)
288
+ return if target == :all
289
+ return if target.is_a?(String)
290
+ raise ArgumentError, "override target must be :all or a gem name string, got #{target.inspect}"
291
+ end
292
+
293
+ def validate_override_field!(field)
294
+ return if SUPPORTED_OVERRIDE_FIELDS.include?(field)
295
+ supported = SUPPORTED_OVERRIDE_FIELDS.map {|f| "`#{f}:`" }.join(", ")
296
+ raise ArgumentError, "unsupported override field `#{field}:`; supported fields: #{supported}"
297
+ end
298
+
299
+ def validate_override_operation!(operation)
300
+ case operation
301
+ when String
302
+ Gem::Requirement.new(operation)
303
+ when nil
304
+ # ok
305
+ when Symbol
306
+ return if SUPPORTED_OVERRIDE_SYMBOL_OPERATIONS.include?(operation)
307
+ raise ArgumentError, "unsupported override operation: #{operation.inspect}"
308
+ else
309
+ raise ArgumentError, "override operation must be a String, Symbol, or nil, got #{operation.inspect}"
310
+ end
311
+ rescue Gem::Requirement::BadRequirementError => e
312
+ raise ArgumentError, "invalid override version requirement #{operation.inspect}: #{e.message}"
313
+ end
314
+
315
+ def validate_override_uniqueness!(target, field)
316
+ return unless @overrides.any? {|o| o.target == target && o.field == field }
317
+ raise ArgumentError, "duplicate override for #{target.inspect} `#{field}:`"
318
+ end
319
+
251
320
  def add_dependency(name, version = nil, options = {})
252
321
  options["gemfile"] = @gemfile
253
322
  options["source"] ||= @source
@@ -262,13 +331,10 @@ module Bundler
262
331
 
263
332
  gemspec_dep = [dep, current].find(&:gemspec_dev_dep?)
264
333
  if gemspec_dep
265
- require_relative "vendor/pub_grub/lib/pub_grub/version_range"
266
- require_relative "vendor/pub_grub/lib/pub_grub/version_constraint"
267
- require_relative "vendor/pub_grub/lib/pub_grub/version_union"
268
- require_relative "vendor/pub_grub/lib/pub_grub/rubygems"
334
+ require_relative "vendored_pub_grub"
269
335
 
270
- current_gemspec_range = PubGrub::RubyGems.requirement_to_range(current.requirement)
271
- next_gemspec_range = PubGrub::RubyGems.requirement_to_range(dep.requirement)
336
+ current_gemspec_range = Gem::PubGrub::RubyGems.requirement_to_range(current.requirement)
337
+ next_gemspec_range = Gem::PubGrub::RubyGems.requirement_to_range(dep.requirement)
272
338
 
273
339
  if current_gemspec_range.intersects?(next_gemspec_range)
274
340
  dep = Dependency.new(name, current.requirement.as_list + dep.requirement.as_list, options)
@@ -5,24 +5,33 @@ module Bundler
5
5
  class EndpointSpecification < Gem::Specification
6
6
  include MatchRemoteMetadata
7
7
 
8
- attr_reader :name, :version, :platform, :checksum, :created_at
8
+ attr_reader :name, :version, :platform, :checksum, :created_at, :content_address
9
9
  attr_writer :dependencies
10
10
  attr_accessor :remote, :locked_platform
11
11
 
12
- def initialize(name, version, platform, spec_fetcher, dependencies, metadata = nil)
12
+ def initialize(name, version, suffix, spec_fetcher, dependencies, metadata = nil)
13
13
  super()
14
14
  @name = name
15
15
  @version = Gem::Version.create version
16
- @platform = Gem::Platform.new(platform)
17
16
  @spec_fetcher = spec_fetcher
18
17
  @dependencies = nil
19
18
  @unbuilt_dependencies = dependencies
19
+ @content_address = nil
20
+ @required_platform = nil
20
21
 
21
22
  @loaded_from = nil
22
23
  @remote_specification = nil
23
24
  @locked_platform = nil
24
25
 
25
26
  parse_metadata(metadata)
27
+
28
+ if Gem::ContentAddress.content_addressed_row?(suffix, @required_platform, @required_ruby_version)
29
+ @content_address = suffix
30
+ @platform = @required_platform
31
+ @required_rubygems_version ||= Gem::Requirement.default
32
+ else
33
+ @platform = Gem::Platform.new(suffix)
34
+ end
26
35
  end
27
36
 
28
37
  def insecurely_materialized?
@@ -147,18 +156,21 @@ module Bundler
147
156
  private
148
157
 
149
158
  def _remote_specification
150
- @_remote_specification ||= @spec_fetcher.fetch_spec([@name, @version, @platform])
159
+ suffix = @content_address || @platform
160
+ @_remote_specification ||= @spec_fetcher.fetch_spec([@name, @version, suffix])
151
161
  end
152
162
 
153
163
  def local_specification_path
154
- "#{base_dir}/specifications/#{full_name}.gemspec"
164
+ File.join(Gem::SpecificationRecord.specification_dir_for(self, base_dir),
165
+ "#{full_name}.gemspec")
155
166
  end
156
167
 
157
168
  def parse_metadata(data)
169
+ @created_at = nil
170
+
158
171
  unless data
159
172
  @required_ruby_version = nil
160
173
  @required_rubygems_version = nil
161
- @created_at = nil
162
174
  return
163
175
  end
164
176
 
@@ -181,22 +193,47 @@ module Bundler
181
193
  when "ruby"
182
194
  @required_ruby_version = Gem::Requirement.new(v)
183
195
  when "created_at"
184
- value = v.is_a?(Array) ? v.last : v
185
- if value.is_a?(String)
186
- @created_at = begin
187
- Time.new(value)
188
- rescue ArgumentError
189
- nil
190
- end
191
- end
196
+ @created_at = parse_created_at(v.is_a?(Array) ? v.last : v)&.freeze
197
+ when "platform"
198
+ @required_platform = required_platform_from(Array(v).last)
192
199
  end
193
200
  end
194
201
  rescue StandardError => e
195
202
  raise GemspecError, "There was an error parsing the metadata for the gem #{name} (#{version}): #{e.class}\n#{e}\nThe metadata was #{data.inspect}"
196
203
  end
197
204
 
205
+ # Matches an ISO 8601 time zone designator at the end of a timestamp.
206
+ TIME_ZONE_SUFFIX = /(?:Z|z|[+-]\d{2}(?::?\d{2})?)\z/
207
+ private_constant :TIME_ZONE_SUFFIX
208
+
209
+ # See Gem::Cooldown::FOUR_DIGIT_YEAR.
210
+ FOUR_DIGIT_YEAR = /\A\d{4}-/
211
+ private_constant :FOUR_DIGIT_YEAR
212
+
213
+ # A timestamp without a time zone offset is read as UTC, because reading
214
+ # it as local time would shift the cooldown window by the environment's
215
+ # offset. Unparsable values and years outside four digits become nil so
216
+ # the cooldown fails open.
217
+ def parse_created_at(value)
218
+ return unless value.is_a?(String) && value.match?(FOUR_DIGIT_YEAR)
219
+
220
+ require "time"
221
+ begin
222
+ Time.iso8601(value.match?(TIME_ZONE_SUFFIX) ? value : "#{value}Z")
223
+ rescue ArgumentError
224
+ nil
225
+ end
226
+ end
227
+
198
228
  def build_dependency(name, requirements)
199
229
  Dependency.new(name, requirements)
200
230
  end
231
+
232
+ def required_platform_from(value)
233
+ value = value.to_s
234
+ return if value.empty?
235
+
236
+ Gem::Platform.new(value)
237
+ end
201
238
  end
202
239
  end
data/lib/bundler/env.rb CHANGED
@@ -17,9 +17,11 @@ module Bundler
17
17
  append_formatted_table("Environment", environment, out)
18
18
  append_formatted_table("Bundler Build Metadata", BuildMetadata.to_h, out)
19
19
 
20
- unless Bundler.settings.all.empty?
20
+ settings = Bundler.settings.all_including_stored_credentials
21
+
22
+ unless settings.empty?
21
23
  out << "\n## Bundler settings\n\n```\n"
22
- Bundler.settings.all.each do |setting|
24
+ settings.each do |setting|
23
25
  out << setting << "\n"
24
26
  Bundler.settings.pretty_values_for(setting).each do |line|
25
27
  out << " " << line << "\n"
@@ -78,17 +80,6 @@ module Bundler
78
80
  "not installed"
79
81
  end
80
82
 
81
- def self.version_of(script)
82
- return "not installed" unless Bundler.which(script)
83
- `#{script} --version`.chomp
84
- end
85
-
86
- def self.chruby_version
87
- return "not installed" unless Bundler.which("chruby-exec")
88
- `chruby-exec -- chruby --version`.
89
- sub(/.*^chruby: (#{Gem::Version::VERSION_PATTERN}).*/m, '\1')
90
- end
91
-
92
83
  def self.environment
93
84
  out = []
94
85
 
@@ -110,16 +101,8 @@ module Bundler
110
101
  out << [" Cert File", OpenSSL::X509::DEFAULT_CERT_FILE] if defined?(OpenSSL::X509::DEFAULT_CERT_FILE)
111
102
  out << [" Cert Dir", OpenSSL::X509::DEFAULT_CERT_DIR] if defined?(OpenSSL::X509::DEFAULT_CERT_DIR)
112
103
  end
113
- out << ["Tools"]
114
- out << [" Git", git_version]
115
- out << [" RVM", ENV.fetch("rvm_version") { version_of("rvm") }]
116
- out << [" rbenv", version_of("rbenv")]
117
- out << [" chruby", chruby_version]
118
-
119
- %w[rubygems-bundler open_gem].each do |name|
120
- specs = Bundler.rubygems.find_name(name)
121
- out << [" #{name}", "(#{specs.map(&:version).join(",")})"] unless specs.empty?
122
- end
104
+ out << ["Git", git_version]
105
+
123
106
  if (exe = caller_locations.last.absolute_path)&.match? %r{(exe|bin)/bundler?\z}
124
107
  shebang = File.read(exe).lines.first
125
108
  shebang.sub!(/^#!\s*/, "")
@@ -143,6 +126,6 @@ module Bundler
143
126
  out << "```\n"
144
127
  end
145
128
 
146
- private_class_method :read_file, :ruby_version, :git_version, :append_formatted_table, :version_of, :chruby_version
129
+ private_class_method :read_file, :ruby_version, :git_version, :append_formatted_table
147
130
  end
148
131
  end
@@ -131,7 +131,8 @@ module Bundler
131
131
  attr_reader :orig_exception
132
132
 
133
133
  def initialize(orig_exception, msg)
134
- full_message = msg + "\nGem Load Error is: #{orig_exception.message}\n"\
134
+ full_message = msg + "\nGem Load Error is:
135
+ #{orig_exception.full_message(highlight: false)}\n"\
135
136
  "Backtrace for gem load error is:\n"\
136
137
  "#{orig_exception.backtrace.join("\n")}\n"\
137
138
  "Bundler Error Backtrace:\n"
@@ -221,7 +222,9 @@ module Bundler
221
222
  class DirectoryRemovalError < BundlerError
222
223
  def initialize(orig_exception, msg)
223
224
  full_message = "#{msg}.\n" \
224
- "The underlying error was #{orig_exception.class}: #{orig_exception.message}, with backtrace:\n" \
225
+ "The underlying error was #{orig_exception.class}:
226
+ #{orig_exception.full_message(highlight: false)},
227
+ with backtrace:\n" \
225
228
  " #{orig_exception.backtrace.join("\n ")}\n\n" \
226
229
  "Bundler Error Backtrace:"
227
230
  super(full_message)
@@ -262,14 +265,40 @@ module Bundler
262
265
  class InvalidArgumentError < BundlerError; status_code(40); end
263
266
 
264
267
  class IncorrectLockfileDependencies < BundlerError
265
- attr_reader :spec
268
+ attr_reader :spec, :actual_dependencies, :lockfile_dependencies
266
269
 
267
- def initialize(spec)
270
+ def initialize(spec, actual_dependencies = nil, lockfile_dependencies = nil)
268
271
  @spec = spec
272
+ @actual_dependencies = actual_dependencies
273
+ @lockfile_dependencies = lockfile_dependencies
269
274
  end
270
275
 
271
276
  def message
272
- "Bundler found incorrect dependencies in the lockfile for #{spec.full_name}"
277
+ lines = ["Bundler found incorrect dependencies in the lockfile for #{spec.full_name}", ""]
278
+
279
+ if @actual_dependencies && @lockfile_dependencies
280
+ actual_by_name = @actual_dependencies.each_with_object({}) {|d, h| h[d.name] = d }
281
+ lockfile_by_name = @lockfile_dependencies.each_with_object({}) {|d, h| h[d.name] = d }
282
+
283
+ (actual_by_name.keys | lockfile_by_name.keys).sort.each do |name|
284
+ actual = actual_by_name[name]
285
+ lockfile = lockfile_by_name[name]
286
+ next if actual && lockfile && actual.requirement == lockfile.requirement
287
+
288
+ if actual && lockfile
289
+ lines << " #{name}: gemspec specifies #{actual.requirement}, lockfile has #{lockfile.requirement}"
290
+ elsif actual
291
+ lines << " #{name}: gemspec specifies #{actual.requirement}, not in lockfile"
292
+ else
293
+ lines << " #{name}: not in gemspec, lockfile has #{lockfile.requirement}"
294
+ end
295
+ end
296
+
297
+ lines << ""
298
+ end
299
+
300
+ lines << "Please run `bundle install` to regenerate the lockfile."
301
+ lines.join("\n")
273
302
  end
274
303
 
275
304
  status_code(41)