bundler 2.6.9 → 4.0.11

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 (191) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +2229 -1879
  3. data/README.md +11 -11
  4. data/bundler.gemspec +5 -5
  5. data/lib/bundler/build_metadata.rb +10 -11
  6. data/lib/bundler/capistrano.rb +1 -19
  7. data/lib/bundler/checksum.rb +6 -0
  8. data/lib/bundler/cli/add.rb +10 -0
  9. data/lib/bundler/cli/cache.rb +1 -12
  10. data/lib/bundler/cli/common.rb +21 -4
  11. data/lib/bundler/cli/config.rb +1 -2
  12. data/lib/bundler/cli/console.rb +5 -0
  13. data/lib/bundler/cli/exec.rb +29 -4
  14. data/lib/bundler/cli/gem.rb +90 -64
  15. data/lib/bundler/cli/install.rb +15 -90
  16. data/lib/bundler/cli/issue.rb +2 -2
  17. data/lib/bundler/cli/list.rb +33 -2
  18. data/lib/bundler/cli/lock.rb +5 -5
  19. data/lib/bundler/cli/outdated.rb +1 -1
  20. data/lib/bundler/cli/plugin.rb +5 -1
  21. data/lib/bundler/cli/pristine.rb +5 -1
  22. data/lib/bundler/cli/show.rb +3 -7
  23. data/lib/bundler/cli/update.rb +5 -5
  24. data/lib/bundler/cli.rb +162 -127
  25. data/lib/bundler/compact_index_client.rb +1 -6
  26. data/lib/bundler/current_ruby.rb +17 -5
  27. data/lib/bundler/definition.rb +201 -117
  28. data/lib/bundler/dependency.rb +1 -1
  29. data/lib/bundler/deployment.rb +1 -64
  30. data/lib/bundler/digest.rb +1 -1
  31. data/lib/bundler/dsl.rb +48 -48
  32. data/lib/bundler/environment_preserver.rb +1 -0
  33. data/lib/bundler/errors.rb +1 -5
  34. data/lib/bundler/feature_flag.rb +5 -35
  35. data/lib/bundler/fetcher/compact_index.rb +1 -1
  36. data/lib/bundler/fetcher/dependency.rb +9 -2
  37. data/lib/bundler/fetcher/downloader.rb +33 -8
  38. data/lib/bundler/fetcher/gem_remote_fetcher.rb +6 -0
  39. data/lib/bundler/fetcher.rb +49 -19
  40. data/lib/bundler/friendly_errors.rb +4 -3
  41. data/lib/bundler/index.rb +7 -9
  42. data/lib/bundler/injector.rb +1 -2
  43. data/lib/bundler/inline.rb +9 -1
  44. data/lib/bundler/installer/gem_installer.rb +14 -11
  45. data/lib/bundler/installer/parallel_installer.rb +74 -36
  46. data/lib/bundler/installer.rb +6 -19
  47. data/lib/bundler/lazy_specification.rb +47 -20
  48. data/lib/bundler/lockfile_generator.rb +17 -2
  49. data/lib/bundler/lockfile_parser.rb +19 -6
  50. data/lib/bundler/man/bundle-add.1 +1 -1
  51. data/lib/bundler/man/bundle-binstubs.1 +3 -6
  52. data/lib/bundler/man/bundle-binstubs.1.ronn +4 -6
  53. data/lib/bundler/man/bundle-cache.1 +2 -14
  54. data/lib/bundler/man/bundle-cache.1.ronn +1 -14
  55. data/lib/bundler/man/bundle-check.1 +2 -5
  56. data/lib/bundler/man/bundle-check.1.ronn +0 -5
  57. data/lib/bundler/man/bundle-clean.1 +1 -1
  58. data/lib/bundler/man/bundle-config.1 +206 -148
  59. data/lib/bundler/man/bundle-config.1.ronn +136 -119
  60. data/lib/bundler/man/bundle-console.1 +1 -1
  61. data/lib/bundler/man/bundle-doctor.1 +43 -4
  62. data/lib/bundler/man/bundle-doctor.1.ronn +48 -4
  63. data/lib/bundler/man/bundle-env.1 +1 -1
  64. data/lib/bundler/man/bundle-exec.1 +2 -5
  65. data/lib/bundler/man/bundle-exec.1.ronn +1 -5
  66. data/lib/bundler/man/bundle-fund.1 +1 -1
  67. data/lib/bundler/man/bundle-gem.1 +64 -44
  68. data/lib/bundler/man/bundle-gem.1.ronn +10 -9
  69. data/lib/bundler/man/bundle-help.1 +1 -1
  70. data/lib/bundler/man/bundle-info.1 +1 -1
  71. data/lib/bundler/man/bundle-init.1 +1 -1
  72. data/lib/bundler/man/bundle-install.1 +16 -59
  73. data/lib/bundler/man/bundle-install.1.ronn +27 -108
  74. data/lib/bundler/man/bundle-issue.1 +1 -1
  75. data/lib/bundler/man/bundle-licenses.1 +1 -1
  76. data/lib/bundler/man/bundle-list.1 +6 -1
  77. data/lib/bundler/man/bundle-list.1.ronn +5 -0
  78. data/lib/bundler/man/bundle-lock.1 +1 -1
  79. data/lib/bundler/man/bundle-open.1 +1 -1
  80. data/lib/bundler/man/bundle-outdated.1 +1 -1
  81. data/lib/bundler/man/bundle-platform.1 +1 -1
  82. data/lib/bundler/man/bundle-plugin.1 +33 -15
  83. data/lib/bundler/man/bundle-plugin.1.ronn +36 -15
  84. data/lib/bundler/man/bundle-pristine.1 +1 -1
  85. data/lib/bundler/man/bundle-remove.1 +2 -8
  86. data/lib/bundler/man/bundle-remove.1.ronn +1 -8
  87. data/lib/bundler/man/bundle-show.1 +2 -5
  88. data/lib/bundler/man/bundle-show.1.ronn +0 -4
  89. data/lib/bundler/man/bundle-update.1 +5 -5
  90. data/lib/bundler/man/bundle-update.1.ronn +4 -4
  91. data/lib/bundler/man/bundle-version.1 +1 -1
  92. data/lib/bundler/man/bundle.1 +1 -10
  93. data/lib/bundler/man/bundle.1.ronn +0 -9
  94. data/lib/bundler/man/gemfile.5 +32 -1
  95. data/lib/bundler/man/gemfile.5.ronn +28 -0
  96. data/lib/bundler/man/index.txt +0 -2
  97. data/lib/bundler/match_platform.rb +31 -12
  98. data/lib/bundler/materialization.rb +3 -3
  99. data/lib/bundler/plugin/api/source.rb +8 -0
  100. data/lib/bundler/plugin/index.rb +6 -0
  101. data/lib/bundler/plugin/installer.rb +2 -11
  102. data/lib/bundler/plugin/source_list.rb +1 -1
  103. data/lib/bundler/plugin.rb +7 -4
  104. data/lib/bundler/resolver/package.rb +2 -1
  105. data/lib/bundler/resolver/strategy.rb +6 -3
  106. data/lib/bundler/resolver.rb +20 -4
  107. data/lib/bundler/retry.rb +30 -4
  108. data/lib/bundler/ruby_dsl.rb +17 -12
  109. data/lib/bundler/ruby_version.rb +1 -3
  110. data/lib/bundler/rubygems_ext.rb +117 -121
  111. data/lib/bundler/rubygems_gem_installer.rb +5 -4
  112. data/lib/bundler/rubygems_integration.rb +13 -12
  113. data/lib/bundler/runtime.rb +14 -3
  114. data/lib/bundler/self_manager.rb +34 -43
  115. data/lib/bundler/settings/validator.rb +5 -21
  116. data/lib/bundler/settings.rb +17 -32
  117. data/lib/bundler/shared_helpers.rb +12 -18
  118. data/lib/bundler/source/gemspec.rb +4 -0
  119. data/lib/bundler/source/git/git_proxy.rb +55 -29
  120. data/lib/bundler/source/git.rb +2 -3
  121. data/lib/bundler/source/metadata.rb +4 -0
  122. data/lib/bundler/source/path.rb +12 -7
  123. data/lib/bundler/source/rubygems.rb +59 -43
  124. data/lib/bundler/source/rubygems_aggregate.rb +4 -1
  125. data/lib/bundler/source.rb +3 -1
  126. data/lib/bundler/source_list.rb +5 -50
  127. data/lib/bundler/source_map.rb +8 -7
  128. data/lib/bundler/spec_set.rb +9 -14
  129. data/lib/bundler/stub_specification.rb +1 -0
  130. data/lib/bundler/templates/Executable +0 -11
  131. data/lib/bundler/templates/newgem/CODE_OF_CONDUCT.md.tt +7 -129
  132. data/lib/bundler/templates/newgem/Cargo.toml.tt +6 -0
  133. data/lib/bundler/templates/newgem/Rakefile.tt +5 -0
  134. data/lib/bundler/templates/newgem/circleci/config.yml.tt +12 -0
  135. data/lib/bundler/templates/newgem/ext/newgem/Cargo.toml.tt +8 -1
  136. data/lib/bundler/templates/newgem/ext/newgem/build.rs.tt +5 -0
  137. data/lib/bundler/templates/newgem/ext/newgem/extconf-go.rb.tt +11 -0
  138. data/lib/bundler/templates/newgem/ext/newgem/go.mod.tt +5 -0
  139. data/lib/bundler/templates/newgem/ext/newgem/newgem-go.c.tt +2 -0
  140. data/lib/bundler/templates/newgem/ext/newgem/newgem.go.tt +31 -0
  141. data/lib/bundler/templates/newgem/ext/newgem/src/lib.rs.tt +13 -2
  142. data/lib/bundler/templates/newgem/github/workflows/build-gems.yml.tt +69 -0
  143. data/lib/bundler/templates/newgem/github/workflows/main.yml.tt +12 -1
  144. data/lib/bundler/templates/newgem/gitlab-ci.yml.tt +9 -0
  145. data/lib/bundler/templates/newgem/lib/newgem.rb.tt +1 -1
  146. data/lib/bundler/templates/newgem/newgem.gemspec.tt +17 -11
  147. data/lib/bundler/templates/newgem/spec/newgem_spec.rb.tt +8 -0
  148. data/lib/bundler/templates/newgem/test/minitest/test_newgem.rb.tt +6 -0
  149. data/lib/bundler/ui/shell.rb +12 -8
  150. data/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb +26 -23
  151. data/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb +1 -1
  152. data/lib/bundler/vendor/connection_pool/lib/connection_pool.rb +50 -6
  153. data/lib/bundler/vendor/fileutils/lib/fileutils.rb +57 -52
  154. data/lib/bundler/vendor/net-http-persistent/README.rdoc +1 -1
  155. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/timed_stack_multi.rb +2 -1
  156. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb +84 -42
  157. data/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb +42 -6
  158. data/lib/bundler/vendor/thor/lib/thor/parser/options.rb +1 -1
  159. data/lib/bundler/vendor/thor/lib/thor/runner.rb +2 -2
  160. data/lib/bundler/vendor/thor/lib/thor/shell/basic.rb +3 -7
  161. data/lib/bundler/vendor/thor/lib/thor/version.rb +1 -1
  162. data/lib/bundler/vendor/thor/lib/thor.rb +1 -1
  163. data/lib/bundler/vendor/uri/lib/uri/common.rb +57 -15
  164. data/lib/bundler/vendor/uri/lib/uri/file.rb +1 -1
  165. data/lib/bundler/vendor/uri/lib/uri/generic.rb +34 -21
  166. data/lib/bundler/vendor/uri/lib/uri/http.rb +12 -0
  167. data/lib/bundler/vendor/uri/lib/uri/rfc2396_parser.rb +9 -8
  168. data/lib/bundler/vendor/uri/lib/uri/version.rb +2 -2
  169. data/lib/bundler/version.rb +10 -2
  170. data/lib/bundler/vlad.rb +1 -14
  171. data/lib/bundler/worker.rb +12 -4
  172. data/lib/bundler.rb +20 -40
  173. metadata +13 -25
  174. data/lib/bundler/cli/inject.rb +0 -60
  175. data/lib/bundler/cli/viz.rb +0 -31
  176. data/lib/bundler/gem_helpers.rb +0 -144
  177. data/lib/bundler/graph.rb +0 -152
  178. data/lib/bundler/man/bundle-inject.1 +0 -31
  179. data/lib/bundler/man/bundle-inject.1.ronn +0 -32
  180. data/lib/bundler/man/bundle-viz.1 +0 -30
  181. data/lib/bundler/man/bundle-viz.1.ronn +0 -36
  182. data/lib/bundler/similarity_detector.rb +0 -63
  183. data/lib/bundler/templates/Executable.bundler +0 -109
  184. data/lib/bundler/vendor/connection_pool/.document +0 -1
  185. data/lib/bundler/vendor/fileutils/.document +0 -1
  186. data/lib/bundler/vendor/net-http-persistent/.document +0 -1
  187. data/lib/bundler/vendor/pub_grub/.document +0 -1
  188. data/lib/bundler/vendor/securerandom/.document +0 -1
  189. data/lib/bundler/vendor/thor/.document +0 -1
  190. data/lib/bundler/vendor/tsort/.document +0 -1
  191. data/lib/bundler/vendor/uri/.document +0 -1
@@ -11,7 +11,7 @@ module Bundler
11
11
  end
12
12
 
13
13
  class CurrentRuby
14
- ALL_RUBY_VERSIONS = (18..27).to_a.concat((30..35).to_a).freeze
14
+ ALL_RUBY_VERSIONS = [*18..27, *30..34, *40..41].freeze
15
15
  KNOWN_MINOR_VERSIONS = ALL_RUBY_VERSIONS.map {|v| v.digits.reverse.join(".") }.freeze
16
16
  KNOWN_MAJOR_VERSIONS = ALL_RUBY_VERSIONS.map {|v| v.digits.last.to_s }.uniq.freeze
17
17
  PLATFORM_MAP = {
@@ -32,7 +32,7 @@ module Bundler
32
32
  end.freeze
33
33
 
34
34
  def ruby?
35
- return true if Bundler::GemHelpers.generic_local_platform_is_ruby?
35
+ return true if Bundler::MatchPlatform.generic_local_platform_is_ruby?
36
36
 
37
37
  !windows? && (RUBY_ENGINE == "ruby" || RUBY_ENGINE == "rbx" || RUBY_ENGINE == "maglev" || RUBY_ENGINE == "truffleruby")
38
38
  end
@@ -50,7 +50,10 @@ module Bundler
50
50
  end
51
51
 
52
52
  def maglev?
53
- RUBY_ENGINE == "maglev"
53
+ removed_message =
54
+ "`CurrentRuby#maglev?` was removed with no replacement. Please use the " \
55
+ "built-in Ruby `RUBY_ENGINE` constant to check the Ruby implementation you are running on."
56
+ SharedHelpers.feature_removed!(removed_message)
54
57
  end
55
58
 
56
59
  def truffleruby?
@@ -71,12 +74,21 @@ module Bundler
71
74
  RUBY_VERSION.start_with?("#{version}.")
72
75
  end
73
76
 
74
- all_platforms = PLATFORM_MAP.keys << "maglev"
75
- all_platforms.each do |platform|
77
+ PLATFORM_MAP.keys.each do |platform|
76
78
  define_method(:"#{platform}_#{trimmed_version}?") do
77
79
  send(:"#{platform}?") && send(:"on_#{trimmed_version}?")
78
80
  end
79
81
  end
82
+
83
+ define_method(:"maglev_#{trimmed_version}?") do
84
+ removed_message =
85
+ "`CurrentRuby##{__method__}` was removed with no replacement. Please use the " \
86
+ "built-in Ruby `RUBY_ENGINE` and `RUBY_VERSION` constants to perform a similar check."
87
+
88
+ SharedHelpers.feature_removed!(removed_message)
89
+
90
+ send(:"maglev?") && send(:"on_#{trimmed_version}?")
91
+ end
80
92
  end
81
93
  end
82
94
  end
@@ -1,16 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "lockfile_parser"
4
+ require_relative "worker"
4
5
 
5
6
  module Bundler
6
7
  class Definition
7
- include GemHelpers
8
-
9
8
  class << self
10
9
  # Do not create or modify a lockfile (Makes #lock a noop)
11
10
  attr_accessor :no_lock
12
11
  end
13
12
 
13
+ attr_writer :lockfile
14
+
14
15
  attr_reader(
15
16
  :dependencies,
16
17
  :locked_checksums,
@@ -62,6 +63,7 @@ module Bundler
62
63
 
63
64
  if unlock == true
64
65
  @unlocking_all = true
66
+ strict = false
65
67
  @unlocking_bundler = false
66
68
  @unlocking = unlock
67
69
  @sources_to_unlock = []
@@ -70,6 +72,7 @@ module Bundler
70
72
  conservative = false
71
73
  else
72
74
  @unlocking_all = false
75
+ strict = unlock.delete(:strict)
73
76
  @unlocking_bundler = unlock.delete(:bundler)
74
77
  @unlocking = unlock.any? {|_k, v| !Array(v).empty? }
75
78
  @sources_to_unlock = unlock.delete(:sources) || []
@@ -99,7 +102,7 @@ module Bundler
99
102
 
100
103
  if lockfile_exists?
101
104
  @lockfile_contents = Bundler.read_file(lockfile)
102
- @locked_gems = LockfileParser.new(@lockfile_contents)
105
+ @locked_gems = LockfileParser.new(@lockfile_contents, strict: strict)
103
106
  @locked_platforms = @locked_gems.platforms
104
107
  @most_specific_locked_platform = @locked_gems.most_specific_locked_platform
105
108
  @platforms = @locked_platforms.dup
@@ -107,6 +110,7 @@ module Bundler
107
110
  @locked_ruby_version = @locked_gems.ruby_version
108
111
  @locked_deps = @locked_gems.dependencies
109
112
  @originally_locked_specs = SpecSet.new(@locked_gems.specs)
113
+ @originally_locked_sources = @locked_gems.sources
110
114
  @locked_checksums = @locked_gems.checksums
111
115
 
112
116
  if @unlocking_all
@@ -114,7 +118,16 @@ module Bundler
114
118
  @locked_sources = []
115
119
  else
116
120
  @locked_specs = @originally_locked_specs
117
- @locked_sources = @locked_gems.sources
121
+ @locked_sources = @originally_locked_sources
122
+ end
123
+
124
+ locked_gem_sources = @originally_locked_sources.select {|s| s.is_a?(Source::Rubygems) }
125
+ multisource_lockfile = locked_gem_sources.size == 1 && locked_gem_sources.first.multiple_remotes?
126
+
127
+ if multisource_lockfile
128
+ msg = "Your lockfile contains a single rubygems source section with multiple remotes, which is insecure. Make sure you run `bundle install` in non frozen mode and commit the result to make your lockfile secure."
129
+
130
+ Bundler::SharedHelpers.feature_removed! msg
118
131
  end
119
132
  else
120
133
  @locked_gems = nil
@@ -123,22 +136,10 @@ module Bundler
123
136
  @platforms = []
124
137
  @locked_deps = {}
125
138
  @locked_specs = SpecSet.new([])
126
- @originally_locked_specs = @locked_specs
127
139
  @locked_sources = []
128
- @locked_checksums = Bundler.feature_flag.lockfile_checksums?
129
- end
130
-
131
- locked_gem_sources = @locked_sources.select {|s| s.is_a?(Source::Rubygems) }
132
- @multisource_allowed = locked_gem_sources.size == 1 && locked_gem_sources.first.multiple_remotes? && Bundler.frozen_bundle?
133
-
134
- if @multisource_allowed
135
- unless sources.aggregate_global_source?
136
- msg = "Your lockfile contains a single rubygems source section with multiple remotes, which is insecure. Make sure you run `bundle install` in non frozen mode and commit the result to make your lockfile secure."
137
-
138
- Bundler::SharedHelpers.major_deprecation 2, msg
139
- end
140
-
141
- @sources.merged_gem_lockfile_sections!(locked_gem_sources.first)
140
+ @originally_locked_specs = @locked_specs
141
+ @originally_locked_sources = @locked_sources
142
+ @locked_checksums = Bundler.settings[:lockfile_checksums]
142
143
  end
143
144
 
144
145
  @unlocking_ruby ||= if @ruby_version && locked_ruby_version_object
@@ -189,12 +190,14 @@ module Bundler
189
190
  def setup_domain!(options = {})
190
191
  prefer_local! if options[:"prefer-local"]
191
192
 
193
+ sources.cached!
194
+
192
195
  if options[:add_checksums] || (!options[:local] && install_needed?)
193
- remotely!
196
+ sources.remote!
194
197
  true
195
198
  else
196
199
  Bundler.settings.set_command_option(:jobs, 1) unless install_needed? # to avoid the overhead of Bundler::Worker
197
- with_cache!
200
+ sources.local!
198
201
  false
199
202
  end
200
203
  end
@@ -246,6 +249,7 @@ module Bundler
246
249
  end
247
250
 
248
251
  def missing_specs
252
+ preload_git_sources
249
253
  resolve.missing_specs_for(requested_dependencies)
250
254
  end
251
255
 
@@ -282,12 +286,17 @@ module Bundler
282
286
  end
283
287
 
284
288
  def filter_relevant(dependencies)
285
- platforms_array = [generic_local_platform].freeze
286
289
  dependencies.select do |d|
287
- d.should_include? && !d.gem_platforms(platforms_array).empty?
290
+ relevant_deps?(d)
288
291
  end
289
292
  end
290
293
 
294
+ def relevant_deps?(dep)
295
+ platforms_array = [Bundler.generic_local_platform].freeze
296
+
297
+ dep.should_include? && !dep.gem_platforms(platforms_array).empty?
298
+ end
299
+
291
300
  def locked_dependencies
292
301
  @locked_deps.values
293
302
  end
@@ -367,12 +376,50 @@ module Bundler
367
376
 
368
377
  msg = "`Definition#lock` was passed a target file argument. #{suggestion}"
369
378
 
370
- Bundler::SharedHelpers.major_deprecation 2, msg
379
+ Bundler::SharedHelpers.feature_removed! msg
371
380
  end
372
381
 
373
382
  write_lock(target_lockfile, preserve_unknown_sections)
374
383
  end
375
384
 
385
+ def write_lock(file, preserve_unknown_sections)
386
+ return if Definition.no_lock || !lockfile || file.nil?
387
+
388
+ contents = to_lock
389
+
390
+ # Convert to \r\n if the existing lock has them
391
+ # i.e., Windows with `git config core.autocrlf=true`
392
+ contents.gsub!(/\n/, "\r\n") if @lockfile_contents.match?("\r\n")
393
+
394
+ if @locked_bundler_version
395
+ locked_major = @locked_bundler_version.segments.first
396
+ current_major = bundler_version_to_lock.segments.first
397
+
398
+ updating_major = locked_major < current_major
399
+ end
400
+
401
+ preserve_unknown_sections ||= !updating_major && (Bundler.frozen_bundle? || !(unlocking? || @unlocking_bundler))
402
+
403
+ if File.exist?(file) && lockfiles_equal?(@lockfile_contents, contents, preserve_unknown_sections)
404
+ return if Bundler.frozen_bundle?
405
+ SharedHelpers.filesystem_access(file) { FileUtils.touch(file) }
406
+ return
407
+ end
408
+
409
+ if Bundler.frozen_bundle?
410
+ Bundler.ui.error "Cannot write a changed lockfile while frozen."
411
+ return
412
+ end
413
+
414
+ begin
415
+ SharedHelpers.filesystem_access(file) do |p|
416
+ File.open(p, "wb") {|f| f.puts(contents) }
417
+ end
418
+ rescue ReadOnlyFileSystemError
419
+ raise ProductionError, lockfile_changes_summary("file system is read-only")
420
+ end
421
+ end
422
+
376
423
  def locked_ruby_version
377
424
  return unless ruby_version
378
425
  if @unlocking_ruby || !@locked_ruby_version
@@ -456,8 +503,8 @@ module Bundler
456
503
  return if current_platform_locked? || @platforms.include?(Gem::Platform::RUBY)
457
504
 
458
505
  raise ProductionError, "Your bundle only supports platforms #{@platforms.map(&:to_s)} " \
459
- "but your local platform is #{local_platform}. " \
460
- "Add the current platform to the lockfile with\n`bundle lock --add-platform #{local_platform}` and try again."
506
+ "but your local platform is #{Bundler.local_platform}. " \
507
+ "Add the current platform to the lockfile with\n`bundle lock --add-platform #{Bundler.local_platform}` and try again."
461
508
  end
462
509
 
463
510
  def normalize_platforms
@@ -492,14 +539,25 @@ module Bundler
492
539
  @unlocking
493
540
  end
494
541
 
495
- attr_writer :source_requirements
496
-
497
542
  def add_checksums
543
+ require "rubygems/package"
544
+
498
545
  @locked_checksums = true
499
546
 
500
547
  setup_domain!(add_checksums: true)
501
548
 
502
- specs # force materialization to real specifications, so that checksums are fetched
549
+ # force materialization to real specifications, so that checksums are fetched
550
+ specs.each do |spec|
551
+ next unless spec.source.is_a?(Bundler::Source::Rubygems)
552
+ # Checksum was fetched from the compact index API.
553
+ next if !spec.source.checksum_store.missing?(spec) && !spec.source.checksum_store.empty?(spec)
554
+ # The gem isn't installed, can't compute the checksum.
555
+ next unless spec.loaded_from
556
+
557
+ package = Gem::Package.new(spec.source.cached_built_in_gem(spec))
558
+ checksum = Checksum.from_gem_package(package)
559
+ spec.source.checksum_store.register(spec, checksum)
560
+ end
503
561
  end
504
562
 
505
563
  private
@@ -533,7 +591,7 @@ module Bundler
533
591
 
534
592
  return unless added.any? || deleted.any? || changed.any? || resolve_needed?
535
593
 
536
- msg = String.new("#{change_reason.capitalize.strip}, but ")
594
+ msg = String.new("#{change_reason[0].upcase}#{change_reason[1..-1].strip}, but ")
537
595
  msg << "the lockfile " unless msg.start_with?("Your lockfile")
538
596
  msg << "can't be updated because #{update_refused_reason}"
539
597
  msg << "\n\nYou have added to the Gemfile:\n" << added.join("\n") if added.any?
@@ -559,6 +617,7 @@ module Bundler
559
617
  @missing_lockfile_dep ||
560
618
  @unlocking_bundler ||
561
619
  @locked_spec_with_missing_checksums ||
620
+ @locked_spec_with_empty_checksums ||
562
621
  @locked_spec_with_missing_deps ||
563
622
  @locked_spec_with_invalid_deps
564
623
  end
@@ -568,53 +627,15 @@ module Bundler
568
627
  end
569
628
 
570
629
  def should_add_extra_platforms?
571
- !lockfile_exists? && generic_local_platform_is_ruby? && !Bundler.settings[:force_ruby_platform]
630
+ !lockfile_exists? && Bundler::MatchPlatform.generic_local_platform_is_ruby? && !Bundler.settings[:force_ruby_platform]
572
631
  end
573
632
 
574
633
  def lockfile_exists?
575
634
  lockfile && File.exist?(lockfile)
576
635
  end
577
636
 
578
- def write_lock(file, preserve_unknown_sections)
579
- return if Definition.no_lock || file.nil?
580
-
581
- contents = to_lock
582
-
583
- # Convert to \r\n if the existing lock has them
584
- # i.e., Windows with `git config core.autocrlf=true`
585
- contents.gsub!(/\n/, "\r\n") if @lockfile_contents.match?("\r\n")
586
-
587
- if @locked_bundler_version
588
- locked_major = @locked_bundler_version.segments.first
589
- current_major = bundler_version_to_lock.segments.first
590
-
591
- updating_major = locked_major < current_major
592
- end
593
-
594
- preserve_unknown_sections ||= !updating_major && (Bundler.frozen_bundle? || !(unlocking? || @unlocking_bundler))
595
-
596
- if File.exist?(file) && lockfiles_equal?(@lockfile_contents, contents, preserve_unknown_sections)
597
- return if Bundler.frozen_bundle?
598
- SharedHelpers.filesystem_access(file) { FileUtils.touch(file) }
599
- return
600
- end
601
-
602
- if Bundler.frozen_bundle?
603
- Bundler.ui.error "Cannot write a changed lockfile while frozen."
604
- return
605
- end
606
-
607
- begin
608
- SharedHelpers.filesystem_access(file) do |p|
609
- File.open(p, "wb") {|f| f.puts(contents) }
610
- end
611
- rescue ReadOnlyFileSystemError
612
- raise ProductionError, lockfile_changes_summary("file system is read-only")
613
- end
614
- end
615
-
616
637
  def resolver
617
- @resolver ||= Resolver.new(resolution_base, gem_version_promoter, @most_specific_locked_platform)
638
+ @resolver ||= new_resolver(resolution_base)
618
639
  end
619
640
 
620
641
  def expanded_dependencies
@@ -632,8 +653,7 @@ module Bundler
632
653
  @resolution_base ||= begin
633
654
  last_resolve = converge_locked_specs
634
655
  remove_invalid_platforms!
635
- new_resolution_platforms = @current_platform_missing ? @new_platforms + [local_platform] : @new_platforms
636
- base = Resolver::Base.new(source_requirements, expanded_dependencies, last_resolve, @platforms, locked_specs: @originally_locked_specs, unlock: @unlocking_all || @gems_to_unlock, prerelease: gem_version_promoter.pre?, prefer_local: @prefer_local, new_platforms: new_resolution_platforms)
656
+ base = new_resolution_base(last_resolve: last_resolve, unlock: @unlocking_all || @gems_to_unlock)
637
657
  base = additional_base_requirements_to_prevent_downgrades(base)
638
658
  base = additional_base_requirements_to_force_updates(base)
639
659
  base
@@ -645,20 +665,12 @@ module Bundler
645
665
  end
646
666
 
647
667
  def materialize(dependencies)
648
- # Tracks potential endless loops trying to re-resolve.
649
- # TODO: Remove as dead code if not reports are received in a while
650
- incorrect_spec = nil
651
-
652
668
  specs = begin
653
669
  resolve.materialize(dependencies)
654
670
  rescue IncorrectLockfileDependencies => e
655
671
  raise if Bundler.frozen_bundle?
656
672
 
657
- spec = e.spec
658
- raise "Infinite loop while fixing lockfile dependencies" if incorrect_spec == spec
659
-
660
- incorrect_spec = spec
661
- reresolve_without([spec])
673
+ reresolve_without([e.spec])
662
674
  retry
663
675
  end
664
676
 
@@ -738,9 +750,8 @@ module Bundler
738
750
  end
739
751
 
740
752
  def start_resolution
741
- local_platform_needed_for_resolvability = @most_specific_non_local_locked_platform && !@platforms.include?(local_platform)
742
- @platforms << local_platform if local_platform_needed_for_resolvability
743
- add_platform(Gem::Platform::RUBY) if RUBY_ENGINE == "truffleruby"
753
+ local_platform_needed_for_resolvability = @most_specific_non_local_locked_platform && !@platforms.include?(Bundler.local_platform)
754
+ @platforms << Bundler.local_platform if local_platform_needed_for_resolvability
744
755
 
745
756
  result = SpecSet.new(resolver.start)
746
757
 
@@ -758,7 +769,7 @@ module Bundler
758
769
  if result.incomplete_for_platform?(current_dependencies, @most_specific_non_local_locked_platform)
759
770
  @platforms.delete(@most_specific_non_local_locked_platform)
760
771
  elsif local_platform_needed_for_resolvability
761
- @platforms.delete(local_platform)
772
+ @platforms.delete(Bundler.local_platform)
762
773
  end
763
774
  end
764
775
 
@@ -772,22 +783,22 @@ module Bundler
772
783
  end
773
784
 
774
785
  def precompute_source_requirements_for_indirect_dependencies?
775
- sources.non_global_rubygems_sources.all?(&:dependency_api_available?) && !sources.aggregate_global_source?
786
+ sources.non_global_rubygems_sources.all?(&:dependency_api_available?)
776
787
  end
777
788
 
778
789
  def current_platform_locked?
779
790
  @platforms.any? do |bundle_platform|
780
- generic_local_platform == bundle_platform || local_platform === bundle_platform
791
+ Bundler.generic_local_platform == bundle_platform || Bundler.local_platform === bundle_platform
781
792
  end
782
793
  end
783
794
 
784
795
  def add_current_platform
785
- return if @platforms.include?(local_platform)
796
+ return if @platforms.include?(Bundler.local_platform)
786
797
 
787
798
  @most_specific_non_local_locked_platform = find_most_specific_locked_platform
788
799
  return if @most_specific_non_local_locked_platform
789
800
 
790
- @platforms << local_platform
801
+ @platforms << Bundler.local_platform
791
802
  true
792
803
  end
793
804
 
@@ -848,6 +859,7 @@ module Bundler
848
859
  [@missing_lockfile_dep, "your lockfile is missing \"#{@missing_lockfile_dep}\""],
849
860
  [@unlocking_bundler, "an update to the version of Bundler itself was requested"],
850
861
  [@locked_spec_with_missing_checksums, "your lockfile is missing a CHECKSUMS entry for \"#{@locked_spec_with_missing_checksums}\""],
862
+ [@locked_spec_with_empty_checksums, "your lockfile has an empty CHECKSUMS entry for \"#{@locked_spec_with_empty_checksums}\""],
851
863
  [@locked_spec_with_missing_deps, "your lockfile includes \"#{@locked_spec_with_missing_deps}\" but not some of its dependencies"],
852
864
  [@locked_spec_with_invalid_deps, "your lockfile does not satisfy dependencies of \"#{@locked_spec_with_invalid_deps}\""],
853
865
  ].select(&:first).map(&:last).join(", ")
@@ -907,13 +919,23 @@ module Bundler
907
919
  @locked_spec_with_invalid_deps = nil
908
920
  @locked_spec_with_missing_deps = nil
909
921
  @locked_spec_with_missing_checksums = nil
922
+ @locked_spec_with_empty_checksums = nil
910
923
 
911
924
  missing_deps = []
912
925
  missing_checksums = []
926
+ empty_checksums = []
913
927
  invalid = []
914
928
 
915
929
  @locked_specs.each do |s|
916
- missing_checksums << s if @locked_checksums && s.source.checksum_store.missing?(s)
930
+ if @locked_checksums
931
+ checksum_store = s.source.checksum_store
932
+
933
+ if checksum_store.missing?(s)
934
+ missing_checksums << s
935
+ elsif checksum_store.empty?(s)
936
+ empty_checksums << s
937
+ end
938
+ end
917
939
 
918
940
  validation = @locked_specs.validate_deps(s)
919
941
 
@@ -922,6 +944,7 @@ module Bundler
922
944
  end
923
945
 
924
946
  @locked_spec_with_missing_checksums = missing_checksums.first.name if missing_checksums.any?
947
+ @locked_spec_with_empty_checksums = empty_checksums.first.name if empty_checksums.any?
925
948
 
926
949
  if missing_deps.any?
927
950
  @locked_specs.delete(missing_deps)
@@ -951,7 +974,7 @@ module Bundler
951
974
  sources.all_sources.each do |source|
952
975
  # has to be done separately, because we want to keep the locked checksum
953
976
  # store for a source, even when doing a full update
954
- if @locked_checksums && @locked_gems && locked_source = @locked_gems.sources.find {|s| s == source && !s.equal?(source) }
977
+ if @locked_checksums && @locked_gems && locked_source = @originally_locked_sources.find {|s| s == source && !s.equal?(source) }
955
978
  source.checksum_store.merge!(locked_source.checksum_store)
956
979
  end
957
980
  # If the source is unlockable and the current command allows an unlock of
@@ -965,6 +988,8 @@ module Bundler
965
988
  end
966
989
  end
967
990
 
991
+ sources.metadata_source.checksum_store.merge!(@locked_gems.metadata_source.checksum_store) if @locked_gems
992
+
968
993
  changes
969
994
  end
970
995
 
@@ -972,10 +997,11 @@ module Bundler
972
997
  @missing_lockfile_dep = nil
973
998
  @changed_dependencies = []
974
999
 
975
- current_dependencies.each do |dep|
1000
+ @dependencies.each do |dep|
976
1001
  if dep.source
977
1002
  dep.source = sources.get(dep.source)
978
1003
  end
1004
+ next unless relevant_deps?(dep)
979
1005
 
980
1006
  name = dep.name
981
1007
 
@@ -1033,26 +1059,43 @@ module Bundler
1033
1059
 
1034
1060
  specs.each do |s|
1035
1061
  name = s.name
1062
+ next if @gems_to_unlock.include?(name)
1063
+
1036
1064
  dep = @dependencies.find {|d| s.satisfies?(d) }
1037
1065
  lockfile_source = s.source
1038
1066
 
1039
1067
  if dep
1040
- gemfile_source = dep.source || default_source
1041
-
1042
- deps << dep if !dep.source || lockfile_source.include?(dep.source) || new_deps.include?(dep)
1068
+ replacement_source = dep.source
1043
1069
 
1044
- # Replace the locked dependency's source with the equivalent source from the Gemfile
1045
- s.source = gemfile_source
1070
+ deps << dep if !replacement_source || lockfile_source.include?(replacement_source) || new_deps.include?(dep)
1046
1071
  else
1047
- # Replace the locked dependency's source with the default source, if the locked source is no longer in the Gemfile
1048
- s.source = default_source unless sources.get(lockfile_source)
1072
+ parent_dep = @dependencies.find do |d|
1073
+ next unless d.source && d.source != lockfile_source
1074
+ next if d.source.is_a?(Source::Gemspec)
1075
+
1076
+ parent_locked_specs = @originally_locked_specs[d.name]
1077
+
1078
+ parent_locked_specs.any? do |parent_spec|
1079
+ parent_spec.runtime_dependencies.any? {|rd| rd.name == s.name }
1080
+ end
1081
+ end
1082
+
1083
+ if parent_dep && parent_dep.source.is_a?(Source::Path) && parent_dep.source.specs[s]&.any?
1084
+ replacement_source = parent_dep.source
1085
+ else
1086
+ replacement_source = sources.get(lockfile_source)
1087
+ end
1049
1088
  end
1050
1089
 
1090
+ # Replace the locked dependency's source with the equivalent source from the Gemfile
1091
+ s.source = replacement_source || default_source
1092
+ next if s.source_changed?
1093
+
1051
1094
  source = s.source
1052
1095
  next if @sources_to_unlock.include?(source.name)
1053
1096
 
1054
1097
  # Path sources have special logic
1055
- if source.instance_of?(Source::Path) || source.instance_of?(Source::Gemspec) || (source.instance_of?(Source::Git) && !@gems_to_unlock.include?(name) && deps.include?(dep))
1098
+ if source.is_a?(Source::Path)
1056
1099
  new_spec = source.specs[s].first
1057
1100
  if new_spec
1058
1101
  s.runtime_dependencies.replace(new_spec.runtime_dependencies)
@@ -1080,15 +1123,50 @@ module Bundler
1080
1123
  @source_requirements ||= find_source_requirements
1081
1124
  end
1082
1125
 
1126
+ def preload_git_source_worker
1127
+ workers = Bundler.settings.installation_parallelization
1128
+
1129
+ @preload_git_source_worker ||= Bundler::Worker.new(workers, "Git source preloading", ->(source, _) { source.specs })
1130
+ end
1131
+
1132
+ def preload_git_sources
1133
+ if Gem.ruby_version < Gem::Version.new("3.3")
1134
+ # Ruby 3.2 has a bug that incorrectly triggers a circular dependency warning. This version will continue to
1135
+ # fetch git repositories one by one.
1136
+ return
1137
+ end
1138
+
1139
+ begin
1140
+ needed_git_sources.each {|source| preload_git_source_worker.enq(source) }
1141
+ ensure
1142
+ preload_git_source_worker.stop
1143
+ end
1144
+ end
1145
+
1146
+ # Git sources needed for the requested groups (excludes sources only used by --without groups)
1147
+ def needed_git_sources
1148
+ needed_deps = dependencies_for(requested_groups)
1149
+ sources.git_sources.select do |source|
1150
+ needed_deps.any? {|d| d.source == source }
1151
+ end
1152
+ end
1153
+
1154
+ # Git sources that should be excluded (only used by --without groups)
1155
+ def excluded_git_sources
1156
+ sources.git_sources - needed_git_sources
1157
+ end
1158
+
1083
1159
  def find_source_requirements
1160
+ preload_git_sources
1161
+
1084
1162
  # Record the specs available in each gem's source, so that those
1085
1163
  # specs will be available later when the resolver knows where to
1086
1164
  # look for that gemspec (or its dependencies)
1087
1165
  source_requirements = if precompute_source_requirements_for_indirect_dependencies?
1088
- all_requirements = source_map.all_requirements
1166
+ all_requirements = source_map.all_requirements(excluded_git_sources)
1089
1167
  { default: default_source }.merge(all_requirements)
1090
1168
  else
1091
- { default: Source::RubygemsAggregate.new(sources, source_map) }.merge(source_map.direct_requirements)
1169
+ { default: Source::RubygemsAggregate.new(sources, source_map, excluded_git_sources) }.merge(source_map.direct_requirements)
1092
1170
  end
1093
1171
  source_requirements.merge!(source_map.locked_requirements) if nothing_changed?
1094
1172
  metadata_dependencies.each do |dep|
@@ -1132,9 +1210,9 @@ module Bundler
1132
1210
  end
1133
1211
 
1134
1212
  def additional_base_requirements_to_prevent_downgrades(resolution_base)
1135
- return resolution_base unless @locked_gems && !sources.expired_sources?(@locked_gems.sources)
1213
+ return resolution_base unless @locked_gems
1136
1214
  @originally_locked_specs.each do |locked_spec|
1137
- next if locked_spec.source.is_a?(Source::Path)
1215
+ next if locked_spec.source.is_a?(Source::Path) || locked_spec.source_changed?
1138
1216
 
1139
1217
  name = locked_spec.name
1140
1218
  next if @changed_dependencies.include?(name)
@@ -1146,7 +1224,7 @@ module Bundler
1146
1224
 
1147
1225
  def additional_base_requirements_to_force_updates(resolution_base)
1148
1226
  return resolution_base if @explicit_unlocks.empty?
1149
- full_update = dup_for_full_unlock.resolve
1227
+ full_update = SpecSet.new(new_resolver_for_full_update.start)
1150
1228
  @explicit_unlocks.each do |name|
1151
1229
  version = full_update.version_for(name)
1152
1230
  resolution_base.base_requirements[name] = Gem::Requirement.new("= #{version}") if version
@@ -1154,21 +1232,10 @@ module Bundler
1154
1232
  resolution_base
1155
1233
  end
1156
1234
 
1157
- def dup_for_full_unlock
1158
- unlocked_definition = self.class.new(@lockfile, @dependencies, @sources, true, @ruby_version, @optional_groups, @gemfiles)
1159
- unlocked_definition.source_requirements = source_requirements
1160
- unlocked_definition.gem_version_promoter.tap do |gvp|
1161
- gvp.level = gem_version_promoter.level
1162
- gvp.strict = gem_version_promoter.strict
1163
- gvp.pre = gem_version_promoter.pre
1164
- end
1165
- unlocked_definition
1166
- end
1167
-
1168
1235
  def remove_invalid_platforms!
1169
1236
  return if Bundler.frozen_bundle?
1170
1237
 
1171
- skips = (@new_platforms + [local_platform]).uniq
1238
+ skips = (@new_platforms + [Bundler.local_platform]).uniq
1172
1239
 
1173
1240
  # We should probably avoid removing non-ruby platforms, since that means
1174
1241
  # lockfile will no longer install on those platforms, so a error to give
@@ -1183,5 +1250,22 @@ module Bundler
1183
1250
  def source_map
1184
1251
  @source_map ||= SourceMap.new(sources, dependencies, @locked_specs)
1185
1252
  end
1253
+
1254
+ def new_resolver_for_full_update
1255
+ new_resolver(unlocked_resolution_base)
1256
+ end
1257
+
1258
+ def unlocked_resolution_base
1259
+ new_resolution_base(last_resolve: SpecSet.new([]), unlock: true)
1260
+ end
1261
+
1262
+ def new_resolution_base(last_resolve:, unlock:)
1263
+ new_resolution_platforms = @current_platform_missing ? @new_platforms + [Bundler.local_platform] : @new_platforms
1264
+ 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)
1265
+ end
1266
+
1267
+ def new_resolver(base)
1268
+ Resolver.new(base, gem_version_promoter, @most_specific_locked_platform)
1269
+ end
1186
1270
  end
1187
1271
  end
@@ -99,7 +99,7 @@ module Bundler
99
99
  return RUBY_PLATFORM_ARRAY if force_ruby_platform
100
100
  return valid_platforms if platforms.empty?
101
101
 
102
- valid_platforms.select {|p| expanded_platforms.include?(GemHelpers.generic(p)) }
102
+ valid_platforms.select {|p| expanded_platforms.include?(Gem::Platform.generic(p)) }
103
103
  end
104
104
 
105
105
  def expanded_platforms