bundler 2.6.6 → 2.6.7

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 (33) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -6
  3. data/lib/bundler/build_metadata.rb +2 -2
  4. data/lib/bundler/checksum.rb +21 -11
  5. data/lib/bundler/compact_index_client/cache.rb +1 -1
  6. data/lib/bundler/compact_index_client/parser.rb +1 -1
  7. data/lib/bundler/definition.rb +53 -47
  8. data/lib/bundler/dsl.rb +2 -3
  9. data/lib/bundler/lazy_specification.rb +9 -1
  10. data/lib/bundler/lockfile_parser.rb +8 -5
  11. data/lib/bundler/plugin/api/source.rb +1 -1
  12. data/lib/bundler/plugin/installer/path.rb +8 -0
  13. data/lib/bundler/plugin.rb +1 -1
  14. data/lib/bundler/resolver/candidate.rb +1 -1
  15. data/lib/bundler/resolver/strategy.rb +40 -0
  16. data/lib/bundler/resolver.rb +11 -22
  17. data/lib/bundler/rubygems_ext.rb +15 -0
  18. data/lib/bundler/runtime.rb +8 -5
  19. data/lib/bundler/source/gemspec.rb +1 -4
  20. data/lib/bundler/source/git/git_proxy.rb +8 -3
  21. data/lib/bundler/source/path.rb +2 -2
  22. data/lib/bundler/source_list.rb +29 -11
  23. data/lib/bundler/spec_set.rb +11 -4
  24. data/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb +53 -3
  25. data/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb +1 -1
  26. data/lib/bundler/vendor/connection_pool/lib/connection_pool.rb +11 -0
  27. data/lib/bundler/vendor/pub_grub/lib/pub_grub/basic_package_source.rb +4 -24
  28. data/lib/bundler/vendor/pub_grub/lib/pub_grub/strategy.rb +42 -0
  29. data/lib/bundler/vendor/pub_grub/lib/pub_grub/version_range.rb +20 -8
  30. data/lib/bundler/vendor/pub_grub/lib/pub_grub/version_solver.rb +17 -29
  31. data/lib/bundler/version.rb +1 -1
  32. metadata +5 -4
  33. data/lib/bundler/compact_index_client/gem_parser.rb +0 -32
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 04be45da6f2c0f4d36af4bbed990919e3fafcba6d1ae0f29adff18b814bbd343
4
- data.tar.gz: 4b2dc4d27575de59e07cfe3cbb397248c60288b0da5a3f857ef5f2ad738b46b9
3
+ metadata.gz: 9135fec12672acb616058b986f9ee528f8dbaf5b5452a413a93bf4188f381813
4
+ data.tar.gz: 8b5ffbe95febae6b17210c94972b43ecf3009c665720e976156a8ecbb4d0cc8b
5
5
  SHA512:
6
- metadata.gz: a1f8470b8caa25f59bdef6c9a62b938292b6e8dc2a1799c93971f931e774aee8c766dc503007900dbd8e743a6396dad8803ea92f6d47261fb82748d46705d865
7
- data.tar.gz: 50c0f2dabb906fe79566eea2024f1c346469026ae57936cc1c0c4d4bbaeab0321e0261c82128ad2c36985f998dbc0db21f1311c4bef2fb097ecd24d641136450
6
+ metadata.gz: 246ae795176220dde699c7d808bc876d4cbf0180b4eb58521efb6785ab699266ffbfbee550380e4e98c002a74cfca386eb7e238d37286775b7a870b4521abcda
7
+ data.tar.gz: '040083539b5e2bd6d968a80de1314c65bc46bb3652189dc0849b73653aed0825993770adc5e7eca99099078be3aba7b47cc34fe10e4c8bfa5fc6927328aaf12f'
data/CHANGELOG.md CHANGED
@@ -1,3 +1,22 @@
1
+ # 2.6.7 (April 3, 2025)
2
+
3
+ ## Enhancements:
4
+
5
+ - Fix crash when server compact index API implementation only lists versions [#8594](https://github.com/rubygems/rubygems/pull/8594)
6
+ - Fix lockfile when a gem ends up accidentally under two different sources [#8579](https://github.com/rubygems/rubygems/pull/8579)
7
+ - Refuse to install and print an error in frozen mode if some entries are missing in CHECKSUMS lockfile section [#8563](https://github.com/rubygems/rubygems/pull/8563)
8
+ - Support git 2.49 [#8581](https://github.com/rubygems/rubygems/pull/8581)
9
+ - Improve wording of a few messages [#8570](https://github.com/rubygems/rubygems/pull/8570)
10
+
11
+ ## Bug fixes:
12
+
13
+ - Fix `bundle add` sometimes generating invalid lockfiles [#8586](https://github.com/rubygems/rubygems/pull/8586)
14
+
15
+ ## Performance:
16
+
17
+ - Implement pub_grub strategy interface [#8589](https://github.com/rubygems/rubygems/pull/8589)
18
+ - Update vendored pub_grub [#8571](https://github.com/rubygems/rubygems/pull/8571)
19
+
1
20
  # 2.6.6 (March 13, 2025)
2
21
 
3
22
  ## Enhancements:
@@ -1318,7 +1337,7 @@
1318
1337
  - Enable parallel installation on Windows by default [#4822](https://github.com/rubygems/rubygems/pull/4822)
1319
1338
  - More logging when compact index is not used and we fallback to other APIs [#4546](https://github.com/rubygems/rubygems/pull/4546)
1320
1339
  - `bundle gem` generated MiniTest file and class now start with 'test' [#3893](https://github.com/rubygems/rubygems/pull/3893)
1321
- - Add `Bundler::Definition.no_lock` accessor for skipping lock file creation/update [#3401](https://github.com/rubygems/rubygems/pull/3401)
1340
+ - Add `Bundler::Definition.no_lock` accessor for skipping lockfile creation/update [#3401](https://github.com/rubygems/rubygems/pull/3401)
1322
1341
 
1323
1342
  ## Bug fixes:
1324
1343
 
@@ -2060,7 +2079,7 @@
2060
2079
  - Fix `bundle outdated --group NAME` when the group is listed second in the Gemfile ([#6116](https://github.com/rubygems/bundler/pull/6116))
2061
2080
  - Improve conflict resolution messages by not calling "ruby" a gem when conflict happens in the `required_ruby_version`, and by filtering out requirements that didn't contribute to the conflict ([#6647](https://github.com/rubygems/bundler/pull/6647))
2062
2081
  - Avoid fetching and rebuilding git gems whenever any gem is changed in the Gemfile ([#6711](https://github.com/rubygems/bundler/pull/6711))
2063
- - Include the exact bundler version in the lock file in the suggested command when bundler warns about version mismatches of itself [#6971](https://github.com/rubygems/bundler/pull/6971)
2082
+ - Include the exact bundler version in the lockfile in the suggested command when bundler warns about version mismatches of itself [#6971](https://github.com/rubygems/bundler/pull/6971)
2064
2083
  - Fix plugins being installed every time a command is run #[#6978](https://github.com/rubygems/bundler/pull/6978)
2065
2084
  - Fallback to sequentially fetching specs on 429s [#6728](https://github.com/rubygems/bundler/pull/6728)
2066
2085
  - Make `bundle clean` also clean native extensions for gems with a git source [#7058](https://github.com/rubygems/bundler/pull/7058)
@@ -3525,7 +3544,7 @@ Changes
3525
3544
 
3526
3545
  ## Bug fixes:
3527
3546
 
3528
- - Revert gem source sorting in lock files (@indirect)
3547
+ - Revert gem source sorting in lockfiles (@indirect)
3529
3548
 
3530
3549
  # 1.7.1 (August 20, 2014)
3531
3550
 
@@ -3625,7 +3644,7 @@ Changes
3625
3644
  - redirects across hosts now work on rubies without OpenSSL ([#2686](https://github.com/rubygems/bundler/issues/2686), @grddev)
3626
3645
  - gemspecs now handle filenames with newlines ([#2634](https://github.com/rubygems/bundler/issues/2634), @jasonmp85)
3627
3646
  - support escaped characters in usernames and passwords (@punkie)
3628
- - no more exception on `update GEM` without lock file (@simi)
3647
+ - no more exception on `update GEM` without lockfile (@simi)
3629
3648
  - allow long config values ([#2823](https://github.com/rubygems/bundler/issues/2823), @kgrz)
3630
3649
  - cache successfully even locked to gems shipped with Ruby ([#2869](https://github.com/rubygems/bundler/issues/2869), @aughr)
3631
3650
  - respect NO_PROXY even if a proxy is configured ([#2878](https://github.com/rubygems/bundler/issues/2878), @stlay)
@@ -3773,7 +3792,7 @@ Changes
3773
3792
 
3774
3793
  ## Bug fixes:
3775
3794
 
3776
- - make gemspec path option preserve relative paths in lock file (@bwillis)
3795
+ - make gemspec path option preserve relative paths in lockfile (@bwillis)
3777
3796
  - use umask when creating binstubs ([#1618](https://github.com/rubygems/bundler/issues/1618), @v-yarotsky)
3778
3797
  - warn if graphviz is not installed ([#2435](https://github.com/rubygems/bundler/issues/2435), @Agis-)
3779
3798
  - show git errors while loading gemspecs
@@ -4662,7 +4681,7 @@ Changes
4662
4681
  - Skeleton gemspec now works with older versions of git
4663
4682
  - Fix shell quoting and ref fetching in GemHelper
4664
4683
  - Disable colored output in --deployment
4665
- - Preserve line endings in lock file
4684
+ - Preserve line endings in lockfile
4666
4685
 
4667
4686
  ## Features:
4668
4687
 
@@ -4,8 +4,8 @@ module Bundler
4
4
  # Represents metadata from when the Bundler gem was built.
5
5
  module BuildMetadata
6
6
  # begin ivars
7
- @built_at = "2025-03-13".freeze
8
- @git_commit_sha = "25cf0763954".freeze
7
+ @built_at = "1980-01-02".freeze
8
+ @git_commit_sha = "32896b3570e".freeze
9
9
  @release = true
10
10
  # end ivars
11
11
 
@@ -190,7 +190,7 @@ module Bundler
190
190
  def replace(spec, checksum)
191
191
  return unless checksum
192
192
 
193
- lock_name = spec.name_tuple.lock_name
193
+ lock_name = spec.lock_name
194
194
  @store_mutex.synchronize do
195
195
  existing = fetch_checksum(lock_name, checksum.algo)
196
196
  if !existing || existing.same_source?(checksum)
@@ -201,10 +201,12 @@ module Bundler
201
201
  end
202
202
  end
203
203
 
204
- def register(spec, checksum)
205
- return unless checksum
204
+ def missing?(spec)
205
+ @store[spec.lock_name].nil?
206
+ end
206
207
 
207
- register_checksum(spec.name_tuple.lock_name, checksum)
208
+ def register(spec, checksum)
209
+ register_checksum(spec.lock_name, checksum)
208
210
  end
209
211
 
210
212
  def merge!(other)
@@ -216,9 +218,9 @@ module Bundler
216
218
  end
217
219
 
218
220
  def to_lock(spec)
219
- lock_name = spec.name_tuple.lock_name
221
+ lock_name = spec.lock_name
220
222
  checksums = @store[lock_name]
221
- if checksums
223
+ if checksums&.any?
222
224
  "#{lock_name} #{checksums.values.map(&:to_lock).sort.join(",")}"
223
225
  else
224
226
  lock_name
@@ -229,11 +231,15 @@ module Bundler
229
231
 
230
232
  def register_checksum(lock_name, checksum)
231
233
  @store_mutex.synchronize do
232
- existing = fetch_checksum(lock_name, checksum.algo)
233
- if existing
234
- merge_checksum(lock_name, checksum, existing)
234
+ if checksum
235
+ existing = fetch_checksum(lock_name, checksum.algo)
236
+ if existing
237
+ merge_checksum(lock_name, checksum, existing)
238
+ else
239
+ store_checksum(lock_name, checksum)
240
+ end
235
241
  else
236
- store_checksum(lock_name, checksum)
242
+ init_checksum(lock_name)
237
243
  end
238
244
  end
239
245
  end
@@ -243,7 +249,11 @@ module Bundler
243
249
  end
244
250
 
245
251
  def store_checksum(lock_name, checksum)
246
- (@store[lock_name] ||= {})[checksum.algo] = checksum
252
+ init_checksum(lock_name)[checksum.algo] = checksum
253
+ end
254
+
255
+ def init_checksum(lock_name)
256
+ @store[lock_name] ||= {}
247
257
  end
248
258
 
249
259
  def fetch_checksum(lock_name, algo)
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "gem_parser"
3
+ require "rubygems/resolver/api_set/gem_parser"
4
4
 
5
5
  module Bundler
6
6
  class CompactIndexClient
@@ -64,7 +64,7 @@ module Bundler
64
64
  end
65
65
 
66
66
  def gem_parser
67
- @gem_parser ||= GemParser.new
67
+ @gem_parser ||= Gem::Resolver::APISet::GemParser.new
68
68
  end
69
69
 
70
70
  # This is mostly the same as `split(" ", 3)` but it avoids allocating extra objects.
@@ -95,6 +95,7 @@ module Bundler
95
95
  @locked_ruby_version = nil
96
96
  @new_platforms = []
97
97
  @removed_platforms = []
98
+ @originally_invalid_platforms = []
98
99
 
99
100
  if lockfile_exists?
100
101
  @lockfile_contents = Bundler.read_file(lockfile)
@@ -147,9 +148,8 @@ module Bundler
147
148
 
148
149
  @current_platform_missing = add_current_platform unless Bundler.frozen_bundle?
149
150
 
150
- converge_path_sources_to_gemspec_sources
151
- @path_changes = converge_paths
152
151
  @source_changes = converge_sources
152
+ @path_changes = converge_paths
153
153
 
154
154
  if conservative
155
155
  @gems_to_unlock = @explicit_unlocks.any? ? @explicit_unlocks : @dependencies.map(&:name)
@@ -539,12 +539,13 @@ module Bundler
539
539
 
540
540
  reason = resolve_needed? ? change_reason : "some dependencies were deleted from your gemfile"
541
541
 
542
- msg = String.new
543
- msg << "#{reason.capitalize.strip}, but the lockfile can't be updated because #{update_refused_reason}"
542
+ msg = String.new("#{reason.capitalize.strip}, but ")
543
+ msg << "the lockfile " unless msg.start_with?("Your lockfile")
544
+ msg << "can't be updated because #{update_refused_reason}"
544
545
  msg << "\n\nYou have added to the Gemfile:\n" << added.join("\n") if added.any?
545
546
  msg << "\n\nYou have deleted from the Gemfile:\n" << deleted.join("\n") if deleted.any?
546
547
  msg << "\n\nYou have changed in the Gemfile:\n" << changed.join("\n") if changed.any?
547
- msg << "\n\nRun `bundle install` elsewhere and add the updated #{SharedHelpers.relative_gemfile_path} to version control.\n" unless unlocking?
548
+ msg << "\n\nRun `bundle install` elsewhere and add the updated #{SharedHelpers.relative_lockfile_path} to version control.\n" unless unlocking?
548
549
  msg
549
550
  end
550
551
 
@@ -563,6 +564,7 @@ module Bundler
563
564
  @local_changes ||
564
565
  @missing_lockfile_dep ||
565
566
  @unlocking_bundler ||
567
+ @locked_spec_with_missing_checksums ||
566
568
  @locked_spec_with_missing_deps ||
567
569
  @locked_spec_with_invalid_deps
568
570
  end
@@ -759,7 +761,11 @@ module Bundler
759
761
  end
760
762
  end
761
763
 
762
- @platforms = result.add_extra_platforms!(platforms) if should_add_extra_platforms?
764
+ if should_add_extra_platforms?
765
+ result.add_extra_platforms!(platforms)
766
+ elsif @originally_invalid_platforms.any?
767
+ result.add_originally_invalid_platforms!(platforms, @originally_invalid_platforms)
768
+ end
763
769
 
764
770
  SpecSet.new(result.for(dependencies, @platforms | [Gem::Platform::RUBY]))
765
771
  end
@@ -809,13 +815,14 @@ module Bundler
809
815
  [
810
816
  [@source_changes, "the list of sources changed"],
811
817
  [@dependency_changes, "the dependencies in your gemfile changed"],
812
- [@current_platform_missing, "your lockfile does not include the current platform"],
818
+ [@current_platform_missing, "your lockfile is missing the current platform"],
813
819
  [@new_platforms.any?, "you are adding a new platform to your lockfile"],
814
820
  [@path_changes, "the gemspecs for path gems changed"],
815
821
  [@local_changes, "the gemspecs for git local gems changed"],
816
- [@missing_lockfile_dep, "your lock file is missing \"#{@missing_lockfile_dep}\""],
822
+ [@missing_lockfile_dep, "your lockfile is missing \"#{@missing_lockfile_dep}\""],
817
823
  [@unlocking_bundler, "an update to the version of Bundler itself was requested"],
818
- [@locked_spec_with_missing_deps, "your lock file includes \"#{@locked_spec_with_missing_deps}\" but not some of its dependencies"],
824
+ [@locked_spec_with_missing_checksums, "your lockfile is missing a CHECKSUMS entry for \"#{@locked_spec_with_missing_checksums}\""],
825
+ [@locked_spec_with_missing_deps, "your lockfile includes \"#{@locked_spec_with_missing_deps}\" but not some of its dependencies"],
819
826
  [@locked_spec_with_invalid_deps, "your lockfile does not satisfy dependencies of \"#{@locked_spec_with_invalid_deps}\""],
820
827
  ].select(&:first).map(&:last).join(", ")
821
828
  end
@@ -832,8 +839,8 @@ module Bundler
832
839
  !locked || dependencies_for_source_changed?(source, locked) || specs_for_source_changed?(source)
833
840
  end
834
841
 
835
- def dependencies_for_source_changed?(source, locked_source = source)
836
- deps_for_source = @dependencies.select {|s| s.source == source }
842
+ def dependencies_for_source_changed?(source, locked_source)
843
+ deps_for_source = @dependencies.select {|dep| dep.source == source }
837
844
  locked_deps_for_source = locked_dependencies.select {|dep| dep.source == locked_source }
838
845
 
839
846
  deps_for_source.uniq.sort != locked_deps_for_source.sort
@@ -841,7 +848,7 @@ module Bundler
841
848
 
842
849
  def specs_for_source_changed?(source)
843
850
  locked_index = Index.new
844
- locked_index.use(@locked_specs.select {|s| source.can_lock?(s) })
851
+ locked_index.use(@locked_specs.select {|s| s.replace_source_with!(source) })
845
852
 
846
853
  !locked_index.subset?(source.specs)
847
854
  rescue PathError, GitError => e
@@ -873,21 +880,27 @@ module Bundler
873
880
  def check_lockfile
874
881
  @locked_spec_with_invalid_deps = nil
875
882
  @locked_spec_with_missing_deps = nil
883
+ @locked_spec_with_missing_checksums = nil
876
884
 
877
- missing = []
885
+ missing_deps = []
886
+ missing_checksums = []
878
887
  invalid = []
879
888
 
880
889
  @locked_specs.each do |s|
890
+ missing_checksums << s if @locked_checksums && s.source.checksum_store.missing?(s)
891
+
881
892
  validation = @locked_specs.validate_deps(s)
882
893
 
883
- missing << s if validation == :missing
894
+ missing_deps << s if validation == :missing
884
895
  invalid << s if validation == :invalid
885
896
  end
886
897
 
887
- if missing.any?
888
- @locked_specs.delete(missing)
898
+ @locked_spec_with_missing_checksums = missing_checksums.first.name if missing_checksums.any?
889
899
 
890
- @locked_spec_with_missing_deps = missing.first.name
900
+ if missing_deps.any?
901
+ @locked_specs.delete(missing_deps)
902
+
903
+ @locked_spec_with_missing_deps = missing_deps.first.name
891
904
  end
892
905
 
893
906
  if invalid.any?
@@ -903,24 +916,6 @@ module Bundler
903
916
  end
904
917
  end
905
918
 
906
- def converge_path_source_to_gemspec_source(source)
907
- return source unless source.instance_of?(Source::Path)
908
- gemspec_source = sources.path_sources.find {|s| s.is_a?(Source::Gemspec) && s.as_path_source == source }
909
- gemspec_source || source
910
- end
911
-
912
- def converge_path_sources_to_gemspec_sources
913
- @locked_sources.map! do |source|
914
- converge_path_source_to_gemspec_source(source)
915
- end
916
- @locked_specs.each do |spec|
917
- spec.source &&= converge_path_source_to_gemspec_source(spec.source)
918
- end
919
- @locked_deps.each do |_, dep|
920
- dep.source &&= converge_path_source_to_gemspec_source(dep.source)
921
- end
922
- end
923
-
924
919
  def converge_sources
925
920
  # Replace the sources from the Gemfile with the sources from the Gemfile.lock,
926
921
  # if they exist in the Gemfile.lock and are `==`. If you can't find an equivalent
@@ -963,11 +958,17 @@ module Bundler
963
958
  unless name == "bundler"
964
959
  locked_specs = @originally_locked_specs[name]
965
960
 
966
- if locked_specs.any? && !dep.matches_spec?(locked_specs.first)
967
- @gems_to_unlock << name
968
- dep_changed = true
969
- elsif locked_specs.empty? && dep_changed == false
970
- @missing_lockfile_dep = name
961
+ if locked_specs.empty?
962
+ @missing_lockfile_dep = name if dep_changed == false
963
+ else
964
+ if locked_specs.map(&:source).uniq.size > 1
965
+ @locked_specs.delete(locked_specs.select {|s| s.source != dep.source })
966
+ end
967
+
968
+ unless dep.matches_spec?(locked_specs.first)
969
+ @gems_to_unlock << name
970
+ dep_changed = true
971
+ end
971
972
  end
972
973
  end
973
974
 
@@ -1141,16 +1142,21 @@ module Bundler
1141
1142
  def remove_invalid_platforms!
1142
1143
  return if Bundler.frozen_bundle?
1143
1144
 
1144
- platforms.reverse_each do |platform|
1145
+ @originally_invalid_platforms = platforms.select do |platform|
1145
1146
  next if local_platform == platform ||
1146
- @new_platforms.include?(platform) ||
1147
- @path_changes ||
1148
- @dependency_changes ||
1149
- @locked_spec_with_invalid_deps ||
1150
- !spec_set_incomplete_for_platform?(@originally_locked_specs, platform)
1147
+ @new_platforms.include?(platform)
1151
1148
 
1152
- remove_platform(platform)
1149
+ # We should probably avoid removing non-ruby platforms, since that means
1150
+ # lockfile will no longer install on those platforms, so a error to give
1151
+ # heads up to the user may be better. However, we have tests expecting
1152
+ # non ruby platform autoremoval to work, so leaving that in place for
1153
+ # now.
1154
+ next if @dependency_changes && platform != Gem::Platform::RUBY
1155
+
1156
+ spec_set_incomplete_for_platform?(@originally_locked_specs, platform)
1153
1157
  end
1158
+
1159
+ @platforms -= @originally_invalid_platforms
1154
1160
  end
1155
1161
 
1156
1162
  def spec_set_incomplete_for_platform?(spec_set, platform)
data/lib/bundler/dsl.rb CHANGED
@@ -77,7 +77,7 @@ module Bundler
77
77
 
78
78
  @gemspecs << spec
79
79
 
80
- path path, "glob" => glob, "name" => spec.name do
80
+ path path, "glob" => glob, "name" => spec.name, "gemspec" => spec do
81
81
  add_dependency spec.name
82
82
  end
83
83
 
@@ -141,8 +141,7 @@ module Bundler
141
141
  def path(path, options = {}, &blk)
142
142
  source_options = normalize_hash(options).merge(
143
143
  "path" => Pathname.new(path),
144
- "root_path" => gemfile_root,
145
- "gemspec" => gemspecs.find {|g| g.name == options["name"] }
144
+ "root_path" => gemfile_root
146
145
  )
147
146
 
148
147
  source_options["global"] = true unless block_given?
@@ -175,6 +175,14 @@ module Bundler
175
175
  @force_ruby_platform = true
176
176
  end
177
177
 
178
+ def replace_source_with!(gemfile_source)
179
+ return unless gemfile_source.can_lock?(self)
180
+
181
+ @source = gemfile_source
182
+
183
+ true
184
+ end
185
+
178
186
  private
179
187
 
180
188
  def use_exact_resolved_specifications?
@@ -196,7 +204,7 @@ module Bundler
196
204
 
197
205
  # If in frozen mode, we fallback to a non-installable candidate because by
198
206
  # doing this we avoid re-resolving and potentially end up changing the
199
- # lock file, which is not allowed. In that case, we will give a proper error
207
+ # lockfile, which is not allowed. In that case, we will give a proper error
200
208
  # about the mismatch higher up the stack, right before trying to install the
201
209
  # bad gem.
202
210
  def choose_compatible(candidates, fallback_to_non_installable: Bundler.frozen_bundle?)
@@ -239,7 +239,6 @@ module Bundler
239
239
  spaces = $1
240
240
  return unless spaces.size == 2
241
241
  checksums = $6
242
- return unless checksums
243
242
  name = $2
244
243
  version = $3
245
244
  platform = $4
@@ -249,10 +248,14 @@ module Bundler
249
248
  full_name = Gem::NameTuple.new(name, version, platform).full_name
250
249
  return unless spec = @specs[full_name]
251
250
 
252
- checksums.split(",") do |lock_checksum|
253
- column = line.index(lock_checksum) + 1
254
- checksum = Checksum.from_lock(lock_checksum, "#{@lockfile_path}:#{@pos.line}:#{column}")
255
- spec.source.checksum_store.register(spec, checksum)
251
+ if checksums
252
+ checksums.split(",") do |lock_checksum|
253
+ column = line.index(lock_checksum) + 1
254
+ checksum = Checksum.from_lock(lock_checksum, "#{@lockfile_path}:#{@pos.line}:#{column}")
255
+ spec.source.checksum_store.register(spec, checksum)
256
+ end
257
+ else
258
+ spec.source.checksum_store.register(spec, nil)
256
259
  end
257
260
  end
258
261
 
@@ -67,7 +67,7 @@ module Bundler
67
67
  # to check out same version of gem later.
68
68
  #
69
69
  # There options are passed when the source plugin is created from the
70
- # lock file.
70
+ # lockfile.
71
71
  #
72
72
  # @return [Hash]
73
73
  def options_to_lock
@@ -8,6 +8,14 @@ module Bundler
8
8
  SharedHelpers.in_bundle? ? Bundler.root : Plugin.root
9
9
  end
10
10
 
11
+ def eql?(other)
12
+ return unless other.class == self.class
13
+ expanded_original_path == other.expanded_original_path &&
14
+ version == other.version
15
+ end
16
+
17
+ alias_method :==, :eql?
18
+
11
19
  def generate_bin(spec, disable_extensions = false)
12
20
  # Need to find a way without code duplication
13
21
  # For now, we can ignore this
@@ -195,7 +195,7 @@ module Bundler
195
195
  @sources[name]
196
196
  end
197
197
 
198
- # @param [Hash] The options that are present in the lock file
198
+ # @param [Hash] The options that are present in the lockfile
199
199
  # @return [API::Source] the instance of the class that handles the source
200
200
  # type passed in locked_opts
201
201
  def from_lock(locked_opts)
@@ -17,7 +17,7 @@ module Bundler
17
17
  # Some candidates may also keep some information explicitly about the
18
18
  # package they refer to. These candidates are referred to as "canonical" and
19
19
  # are used when materializing resolution results back into RubyGems
20
- # specifications that can be installed, written to lock files, and so on.
20
+ # specifications that can be installed, written to lockfiles, and so on.
21
21
  #
22
22
  class Candidate
23
23
  include Comparable
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bundler
4
+ class Resolver
5
+ class Strategy
6
+ def initialize(source)
7
+ @source = source
8
+ end
9
+
10
+ def next_package_and_version(unsatisfied)
11
+ package, range = next_term_to_try_from(unsatisfied)
12
+
13
+ [package, most_preferred_version_of(package, range).first]
14
+ end
15
+
16
+ private
17
+
18
+ def next_term_to_try_from(unsatisfied)
19
+ unsatisfied.min_by do |package, range|
20
+ matching_versions = @source.versions_for(package, range)
21
+ higher_versions = @source.versions_for(package, range.upper_invert)
22
+
23
+ [matching_versions.count <= 1 ? 0 : 1, higher_versions.count]
24
+ end
25
+ end
26
+
27
+ def most_preferred_version_of(package, range)
28
+ versions = @source.versions_for(package, range)
29
+
30
+ # Conditional avoids (among other things) calling
31
+ # sort_versions_by_preferred with the root package
32
+ if versions.size > 1
33
+ @source.sort_versions_by_preferred(package, versions)
34
+ else
35
+ versions
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -12,6 +12,7 @@ module Bundler
12
12
  require_relative "resolver/candidate"
13
13
  require_relative "resolver/incompatibility"
14
14
  require_relative "resolver/root"
15
+ require_relative "resolver/strategy"
15
16
 
16
17
  include GemHelpers
17
18
 
@@ -78,7 +79,7 @@ module Bundler
78
79
  end
79
80
 
80
81
  def solve_versions(root:, logger:)
81
- solver = PubGrub::VersionSolver.new(source: self, root: root, logger: logger)
82
+ solver = PubGrub::VersionSolver.new(source: self, root: root, strategy: Strategy.new(self), logger: logger)
82
83
  result = solver.solve
83
84
  resolved_specs = result.flat_map {|package, version| version.to_specs(package, @most_specific_locked_platform) }
84
85
  SpecSet.new(resolved_specs).specs_with_additional_variants_from(@base.locked_specs)
@@ -167,15 +168,7 @@ module Bundler
167
168
  end
168
169
 
169
170
  def versions_for(package, range=VersionRange.any)
170
- versions = select_sorted_versions(package, range)
171
-
172
- # Conditional avoids (among other things) calling
173
- # sort_versions_by_preferred with the root package
174
- if versions.size > 1
175
- sort_versions_by_preferred(package, versions)
176
- else
177
- versions
178
- end
171
+ range.select_versions(@sorted_versions[package])
179
172
  end
180
173
 
181
174
  def no_versions_incompatibility_for(package, unsatisfied_term)
@@ -284,8 +277,8 @@ module Bundler
284
277
  ruby_group = Resolver::SpecGroup.new(ruby_specs)
285
278
 
286
279
  unless ruby_group.empty?
287
- platform_specs.each do |specs|
288
- ruby_group.merge(Resolver::SpecGroup.new(specs))
280
+ platform_specs.each do |s|
281
+ ruby_group.merge(Resolver::SpecGroup.new(s))
289
282
  end
290
283
 
291
284
  groups << Resolver::Candidate.new(version, group: ruby_group, priority: -1)
@@ -355,6 +348,10 @@ module Bundler
355
348
  raise GemNotFound, message
356
349
  end
357
350
 
351
+ def sort_versions_by_preferred(package, versions)
352
+ @gem_version_promoter.sort_versions(package, versions)
353
+ end
354
+
358
355
  private
359
356
 
360
357
  def filtered_versions_for(package)
@@ -414,10 +411,6 @@ module Bundler
414
411
  requirement.satisfied_by?(spec.version) || spec.source.is_a?(Source::Gemspec)
415
412
  end
416
413
 
417
- def sort_versions_by_preferred(package, versions)
418
- @gem_version_promoter.sort_versions(package, versions)
419
- end
420
-
421
414
  def repository_for(package)
422
415
  source_for(package.name)
423
416
  end
@@ -433,7 +426,7 @@ module Bundler
433
426
  next [dep_package, dep_constraint] if name == "bundler"
434
427
 
435
428
  dep_range = dep_constraint.range
436
- versions = select_sorted_versions(dep_package, dep_range)
429
+ versions = versions_for(dep_package, dep_range)
437
430
  if versions.empty?
438
431
  if dep_package.ignores_prereleases? || dep_package.prefer_local?
439
432
  @all_versions.delete(dep_package)
@@ -441,7 +434,7 @@ module Bundler
441
434
  end
442
435
  dep_package.consider_prereleases! if dep_package.ignores_prereleases?
443
436
  dep_package.consider_remote_versions! if dep_package.prefer_local?
444
- versions = select_sorted_versions(dep_package, dep_range)
437
+ versions = versions_for(dep_package, dep_range)
445
438
  end
446
439
 
447
440
  if versions.empty? && select_all_versions(dep_package, dep_range).any?
@@ -456,10 +449,6 @@ module Bundler
456
449
  end.to_h
457
450
  end
458
451
 
459
- def select_sorted_versions(package, range)
460
- range.select_versions(@sorted_versions[package])
461
- end
462
-
463
452
  def select_all_versions(package, range)
464
453
  range.select_versions(@all_versions[package])
465
454
  end