bundler 2.6.9 → 4.0.10

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/CHANGELOG.md +2212 -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 +89 -63
  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 +199 -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 +63 -17
  46. data/lib/bundler/installer.rb +6 -19
  47. data/lib/bundler/lazy_specification.rb +47 -20
  48. data/lib/bundler/lockfile_generator.rb +1 -1
  49. data/lib/bundler/lockfile_parser.rb +11 -5
  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 +135 -118
  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/path.rb +12 -7
  122. data/lib/bundler/source/rubygems.rb +59 -43
  123. data/lib/bundler/source/rubygems_aggregate.rb +4 -1
  124. data/lib/bundler/source.rb +3 -1
  125. data/lib/bundler/source_list.rb +5 -50
  126. data/lib/bundler/source_map.rb +8 -7
  127. data/lib/bundler/spec_set.rb +9 -14
  128. data/lib/bundler/stub_specification.rb +1 -0
  129. data/lib/bundler/templates/Executable +0 -11
  130. data/lib/bundler/templates/newgem/CODE_OF_CONDUCT.md.tt +7 -129
  131. data/lib/bundler/templates/newgem/Cargo.toml.tt +6 -0
  132. data/lib/bundler/templates/newgem/Rakefile.tt +5 -0
  133. data/lib/bundler/templates/newgem/circleci/config.yml.tt +12 -0
  134. data/lib/bundler/templates/newgem/ext/newgem/Cargo.toml.tt +8 -1
  135. data/lib/bundler/templates/newgem/ext/newgem/build.rs.tt +5 -0
  136. data/lib/bundler/templates/newgem/ext/newgem/extconf-go.rb.tt +11 -0
  137. data/lib/bundler/templates/newgem/ext/newgem/go.mod.tt +5 -0
  138. data/lib/bundler/templates/newgem/ext/newgem/newgem-go.c.tt +2 -0
  139. data/lib/bundler/templates/newgem/ext/newgem/newgem.go.tt +31 -0
  140. data/lib/bundler/templates/newgem/ext/newgem/src/lib.rs.tt +13 -2
  141. data/lib/bundler/templates/newgem/github/workflows/build-gems.yml.tt +69 -0
  142. data/lib/bundler/templates/newgem/github/workflows/main.yml.tt +12 -1
  143. data/lib/bundler/templates/newgem/gitlab-ci.yml.tt +9 -0
  144. data/lib/bundler/templates/newgem/lib/newgem.rb.tt +1 -1
  145. data/lib/bundler/templates/newgem/newgem.gemspec.tt +10 -10
  146. data/lib/bundler/templates/newgem/spec/newgem_spec.rb.tt +8 -0
  147. data/lib/bundler/templates/newgem/test/minitest/test_newgem.rb.tt +6 -0
  148. data/lib/bundler/ui/shell.rb +12 -8
  149. data/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb +26 -23
  150. data/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb +1 -1
  151. data/lib/bundler/vendor/connection_pool/lib/connection_pool.rb +50 -6
  152. data/lib/bundler/vendor/fileutils/lib/fileutils.rb +57 -52
  153. data/lib/bundler/vendor/net-http-persistent/README.rdoc +1 -1
  154. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/timed_stack_multi.rb +2 -1
  155. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb +84 -42
  156. data/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb +42 -6
  157. data/lib/bundler/vendor/thor/lib/thor/parser/options.rb +1 -1
  158. data/lib/bundler/vendor/thor/lib/thor/runner.rb +2 -2
  159. data/lib/bundler/vendor/thor/lib/thor/shell/basic.rb +3 -7
  160. data/lib/bundler/vendor/thor/lib/thor/version.rb +1 -1
  161. data/lib/bundler/vendor/thor/lib/thor.rb +1 -1
  162. data/lib/bundler/vendor/uri/lib/uri/common.rb +57 -15
  163. data/lib/bundler/vendor/uri/lib/uri/file.rb +1 -1
  164. data/lib/bundler/vendor/uri/lib/uri/generic.rb +34 -21
  165. data/lib/bundler/vendor/uri/lib/uri/http.rb +12 -0
  166. data/lib/bundler/vendor/uri/lib/uri/rfc2396_parser.rb +9 -8
  167. data/lib/bundler/vendor/uri/lib/uri/version.rb +2 -2
  168. data/lib/bundler/version.rb +10 -2
  169. data/lib/bundler/vlad.rb +1 -14
  170. data/lib/bundler/worker.rb +12 -4
  171. data/lib/bundler.rb +20 -40
  172. metadata +13 -25
  173. data/lib/bundler/cli/inject.rb +0 -60
  174. data/lib/bundler/cli/viz.rb +0 -31
  175. data/lib/bundler/gem_helpers.rb +0 -144
  176. data/lib/bundler/graph.rb +0 -152
  177. data/lib/bundler/man/bundle-inject.1 +0 -31
  178. data/lib/bundler/man/bundle-inject.1.ronn +0 -32
  179. data/lib/bundler/man/bundle-viz.1 +0 -30
  180. data/lib/bundler/man/bundle-viz.1.ronn +0 -36
  181. data/lib/bundler/similarity_detector.rb +0 -63
  182. data/lib/bundler/templates/Executable.bundler +0 -109
  183. data/lib/bundler/vendor/connection_pool/.document +0 -1
  184. data/lib/bundler/vendor/fileutils/.document +0 -1
  185. data/lib/bundler/vendor/net-http-persistent/.document +0 -1
  186. data/lib/bundler/vendor/pub_grub/.document +0 -1
  187. data/lib/bundler/vendor/securerandom/.document +0 -1
  188. data/lib/bundler/vendor/thor/.document +0 -1
  189. data/lib/bundler/vendor/tsort/.document +0 -1
  190. 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
@@ -972,10 +995,11 @@ module Bundler
972
995
  @missing_lockfile_dep = nil
973
996
  @changed_dependencies = []
974
997
 
975
- current_dependencies.each do |dep|
998
+ @dependencies.each do |dep|
976
999
  if dep.source
977
1000
  dep.source = sources.get(dep.source)
978
1001
  end
1002
+ next unless relevant_deps?(dep)
979
1003
 
980
1004
  name = dep.name
981
1005
 
@@ -1033,26 +1057,43 @@ module Bundler
1033
1057
 
1034
1058
  specs.each do |s|
1035
1059
  name = s.name
1060
+ next if @gems_to_unlock.include?(name)
1061
+
1036
1062
  dep = @dependencies.find {|d| s.satisfies?(d) }
1037
1063
  lockfile_source = s.source
1038
1064
 
1039
1065
  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)
1066
+ replacement_source = dep.source
1043
1067
 
1044
- # Replace the locked dependency's source with the equivalent source from the Gemfile
1045
- s.source = gemfile_source
1068
+ deps << dep if !replacement_source || lockfile_source.include?(replacement_source) || new_deps.include?(dep)
1046
1069
  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)
1070
+ parent_dep = @dependencies.find do |d|
1071
+ next unless d.source && d.source != lockfile_source
1072
+ next if d.source.is_a?(Source::Gemspec)
1073
+
1074
+ parent_locked_specs = @originally_locked_specs[d.name]
1075
+
1076
+ parent_locked_specs.any? do |parent_spec|
1077
+ parent_spec.runtime_dependencies.any? {|rd| rd.name == s.name }
1078
+ end
1079
+ end
1080
+
1081
+ if parent_dep && parent_dep.source.is_a?(Source::Path) && parent_dep.source.specs[s]&.any?
1082
+ replacement_source = parent_dep.source
1083
+ else
1084
+ replacement_source = sources.get(lockfile_source)
1085
+ end
1049
1086
  end
1050
1087
 
1088
+ # Replace the locked dependency's source with the equivalent source from the Gemfile
1089
+ s.source = replacement_source || default_source
1090
+ next if s.source_changed?
1091
+
1051
1092
  source = s.source
1052
1093
  next if @sources_to_unlock.include?(source.name)
1053
1094
 
1054
1095
  # 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))
1096
+ if source.is_a?(Source::Path)
1056
1097
  new_spec = source.specs[s].first
1057
1098
  if new_spec
1058
1099
  s.runtime_dependencies.replace(new_spec.runtime_dependencies)
@@ -1080,15 +1121,50 @@ module Bundler
1080
1121
  @source_requirements ||= find_source_requirements
1081
1122
  end
1082
1123
 
1124
+ def preload_git_source_worker
1125
+ workers = Bundler.settings.installation_parallelization
1126
+
1127
+ @preload_git_source_worker ||= Bundler::Worker.new(workers, "Git source preloading", ->(source, _) { source.specs })
1128
+ end
1129
+
1130
+ def preload_git_sources
1131
+ if Gem.ruby_version < Gem::Version.new("3.3")
1132
+ # Ruby 3.2 has a bug that incorrectly triggers a circular dependency warning. This version will continue to
1133
+ # fetch git repositories one by one.
1134
+ return
1135
+ end
1136
+
1137
+ begin
1138
+ needed_git_sources.each {|source| preload_git_source_worker.enq(source) }
1139
+ ensure
1140
+ preload_git_source_worker.stop
1141
+ end
1142
+ end
1143
+
1144
+ # Git sources needed for the requested groups (excludes sources only used by --without groups)
1145
+ def needed_git_sources
1146
+ needed_deps = dependencies_for(requested_groups)
1147
+ sources.git_sources.select do |source|
1148
+ needed_deps.any? {|d| d.source == source }
1149
+ end
1150
+ end
1151
+
1152
+ # Git sources that should be excluded (only used by --without groups)
1153
+ def excluded_git_sources
1154
+ sources.git_sources - needed_git_sources
1155
+ end
1156
+
1083
1157
  def find_source_requirements
1158
+ preload_git_sources
1159
+
1084
1160
  # Record the specs available in each gem's source, so that those
1085
1161
  # specs will be available later when the resolver knows where to
1086
1162
  # look for that gemspec (or its dependencies)
1087
1163
  source_requirements = if precompute_source_requirements_for_indirect_dependencies?
1088
- all_requirements = source_map.all_requirements
1164
+ all_requirements = source_map.all_requirements(excluded_git_sources)
1089
1165
  { default: default_source }.merge(all_requirements)
1090
1166
  else
1091
- { default: Source::RubygemsAggregate.new(sources, source_map) }.merge(source_map.direct_requirements)
1167
+ { default: Source::RubygemsAggregate.new(sources, source_map, excluded_git_sources) }.merge(source_map.direct_requirements)
1092
1168
  end
1093
1169
  source_requirements.merge!(source_map.locked_requirements) if nothing_changed?
1094
1170
  metadata_dependencies.each do |dep|
@@ -1132,9 +1208,9 @@ module Bundler
1132
1208
  end
1133
1209
 
1134
1210
  def additional_base_requirements_to_prevent_downgrades(resolution_base)
1135
- return resolution_base unless @locked_gems && !sources.expired_sources?(@locked_gems.sources)
1211
+ return resolution_base unless @locked_gems
1136
1212
  @originally_locked_specs.each do |locked_spec|
1137
- next if locked_spec.source.is_a?(Source::Path)
1213
+ next if locked_spec.source.is_a?(Source::Path) || locked_spec.source_changed?
1138
1214
 
1139
1215
  name = locked_spec.name
1140
1216
  next if @changed_dependencies.include?(name)
@@ -1146,7 +1222,7 @@ module Bundler
1146
1222
 
1147
1223
  def additional_base_requirements_to_force_updates(resolution_base)
1148
1224
  return resolution_base if @explicit_unlocks.empty?
1149
- full_update = dup_for_full_unlock.resolve
1225
+ full_update = SpecSet.new(new_resolver_for_full_update.start)
1150
1226
  @explicit_unlocks.each do |name|
1151
1227
  version = full_update.version_for(name)
1152
1228
  resolution_base.base_requirements[name] = Gem::Requirement.new("= #{version}") if version
@@ -1154,21 +1230,10 @@ module Bundler
1154
1230
  resolution_base
1155
1231
  end
1156
1232
 
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
1233
  def remove_invalid_platforms!
1169
1234
  return if Bundler.frozen_bundle?
1170
1235
 
1171
- skips = (@new_platforms + [local_platform]).uniq
1236
+ skips = (@new_platforms + [Bundler.local_platform]).uniq
1172
1237
 
1173
1238
  # We should probably avoid removing non-ruby platforms, since that means
1174
1239
  # lockfile will no longer install on those platforms, so a error to give
@@ -1183,5 +1248,22 @@ module Bundler
1183
1248
  def source_map
1184
1249
  @source_map ||= SourceMap.new(sources, dependencies, @locked_specs)
1185
1250
  end
1251
+
1252
+ def new_resolver_for_full_update
1253
+ new_resolver(unlocked_resolution_base)
1254
+ end
1255
+
1256
+ def unlocked_resolution_base
1257
+ new_resolution_base(last_resolve: SpecSet.new([]), unlock: true)
1258
+ end
1259
+
1260
+ def new_resolution_base(last_resolve:, unlock:)
1261
+ new_resolution_platforms = @current_platform_missing ? @new_platforms + [Bundler.local_platform] : @new_platforms
1262
+ 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)
1263
+ end
1264
+
1265
+ def new_resolver(base)
1266
+ Resolver.new(base, gem_version_promoter, @most_specific_locked_platform)
1267
+ end
1186
1268
  end
1187
1269
  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