bundler 2.6.3 → 2.6.9

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 (104) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +119 -6
  3. data/README.md +1 -1
  4. data/lib/bundler/build_metadata.rb +2 -2
  5. data/lib/bundler/checksum.rb +22 -12
  6. data/lib/bundler/cli/console.rb +8 -6
  7. data/lib/bundler/cli/doctor/diagnose.rb +167 -0
  8. data/lib/bundler/cli/doctor/ssl.rb +249 -0
  9. data/lib/bundler/cli/doctor.rb +27 -151
  10. data/lib/bundler/cli/info.rb +4 -4
  11. data/lib/bundler/cli/inject.rb +2 -2
  12. data/lib/bundler/cli/issue.rb +3 -3
  13. data/lib/bundler/cli/lock.rb +2 -1
  14. data/lib/bundler/cli/show.rb +1 -1
  15. data/lib/bundler/cli.rb +2 -11
  16. data/lib/bundler/compact_index_client/cache.rb +1 -1
  17. data/lib/bundler/compact_index_client/parser.rb +1 -1
  18. data/lib/bundler/compact_index_client/updater.rb +2 -1
  19. data/lib/bundler/current_ruby.rb +23 -33
  20. data/lib/bundler/definition.rb +220 -184
  21. data/lib/bundler/dependency.rb +92 -47
  22. data/lib/bundler/dsl.rb +84 -80
  23. data/lib/bundler/endpoint_specification.rb +10 -3
  24. data/lib/bundler/errors.rb +22 -0
  25. data/lib/bundler/friendly_errors.rb +1 -1
  26. data/lib/bundler/gem_helpers.rb +4 -10
  27. data/lib/bundler/gem_version_promoter.rb +0 -2
  28. data/lib/bundler/injector.rb +9 -9
  29. data/lib/bundler/installer.rb +2 -2
  30. data/lib/bundler/lazy_specification.rb +67 -45
  31. data/lib/bundler/lockfile_parser.rb +8 -5
  32. data/lib/bundler/man/bundle-add.1 +1 -1
  33. data/lib/bundler/man/bundle-binstubs.1 +1 -1
  34. data/lib/bundler/man/bundle-cache.1 +1 -1
  35. data/lib/bundler/man/bundle-check.1 +1 -1
  36. data/lib/bundler/man/bundle-clean.1 +1 -1
  37. data/lib/bundler/man/bundle-config.1 +6 -6
  38. data/lib/bundler/man/bundle-config.1.ronn +9 -4
  39. data/lib/bundler/man/bundle-console.1 +1 -1
  40. data/lib/bundler/man/bundle-doctor.1 +1 -1
  41. data/lib/bundler/man/bundle-env.1 +1 -1
  42. data/lib/bundler/man/bundle-exec.1 +3 -3
  43. data/lib/bundler/man/bundle-exec.1.ronn +2 -2
  44. data/lib/bundler/man/bundle-fund.1 +1 -1
  45. data/lib/bundler/man/bundle-gem.1 +1 -1
  46. data/lib/bundler/man/bundle-help.1 +1 -1
  47. data/lib/bundler/man/bundle-info.1 +1 -1
  48. data/lib/bundler/man/bundle-init.1 +1 -1
  49. data/lib/bundler/man/bundle-inject.1 +1 -1
  50. data/lib/bundler/man/bundle-install.1 +1 -1
  51. data/lib/bundler/man/bundle-issue.1 +1 -1
  52. data/lib/bundler/man/bundle-licenses.1 +1 -1
  53. data/lib/bundler/man/bundle-list.1 +1 -1
  54. data/lib/bundler/man/bundle-lock.1 +1 -1
  55. data/lib/bundler/man/bundle-open.1 +1 -1
  56. data/lib/bundler/man/bundle-outdated.1 +1 -1
  57. data/lib/bundler/man/bundle-platform.1 +1 -1
  58. data/lib/bundler/man/bundle-plugin.1 +1 -1
  59. data/lib/bundler/man/bundle-pristine.1 +1 -1
  60. data/lib/bundler/man/bundle-remove.1 +1 -1
  61. data/lib/bundler/man/bundle-show.1 +1 -1
  62. data/lib/bundler/man/bundle-update.1 +1 -1
  63. data/lib/bundler/man/bundle-version.1 +1 -1
  64. data/lib/bundler/man/bundle-viz.1 +1 -1
  65. data/lib/bundler/man/bundle.1 +1 -1
  66. data/lib/bundler/man/gemfile.5 +1 -1
  67. data/lib/bundler/match_metadata.rb +13 -0
  68. data/lib/bundler/plugin/api/source.rb +1 -1
  69. data/lib/bundler/plugin/index.rb +1 -1
  70. data/lib/bundler/plugin/installer/path.rb +8 -0
  71. data/lib/bundler/plugin.rb +1 -1
  72. data/lib/bundler/resolver/candidate.rb +12 -9
  73. data/lib/bundler/resolver/package.rb +7 -3
  74. data/lib/bundler/resolver/spec_group.rb +1 -25
  75. data/lib/bundler/resolver/strategy.rb +40 -0
  76. data/lib/bundler/resolver.rb +29 -27
  77. data/lib/bundler/rubygems_ext.rb +97 -81
  78. data/lib/bundler/rubygems_integration.rb +2 -3
  79. data/lib/bundler/runtime.rb +27 -29
  80. data/lib/bundler/shared_helpers.rb +4 -0
  81. data/lib/bundler/source/gemspec.rb +1 -4
  82. data/lib/bundler/source/git/git_proxy.rb +14 -3
  83. data/lib/bundler/source/git.rb +5 -1
  84. data/lib/bundler/source/path.rb +2 -2
  85. data/lib/bundler/source/rubygems/remote.rb +11 -3
  86. data/lib/bundler/source/rubygems.rb +19 -4
  87. data/lib/bundler/source.rb +2 -0
  88. data/lib/bundler/source_list.rb +33 -11
  89. data/lib/bundler/spec_set.rb +98 -40
  90. data/lib/bundler/templates/newgem/Gemfile.tt +1 -0
  91. data/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb +53 -3
  92. data/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb +1 -1
  93. data/lib/bundler/vendor/connection_pool/lib/connection_pool.rb +11 -0
  94. data/lib/bundler/vendor/pub_grub/lib/pub_grub/basic_package_source.rb +4 -24
  95. data/lib/bundler/vendor/pub_grub/lib/pub_grub/strategy.rb +42 -0
  96. data/lib/bundler/vendor/pub_grub/lib/pub_grub/version_range.rb +20 -8
  97. data/lib/bundler/vendor/pub_grub/lib/pub_grub/version_solver.rb +17 -29
  98. data/lib/bundler/vendor/uri/lib/uri/common.rb +7 -3
  99. data/lib/bundler/vendor/uri/lib/uri/generic.rb +12 -11
  100. data/lib/bundler/vendor/uri/lib/uri/rfc2396_parser.rb +6 -6
  101. data/lib/bundler/vendor/uri/lib/uri/version.rb +1 -1
  102. data/lib/bundler/version.rb +1 -1
  103. metadata +7 -4
  104. data/lib/bundler/compact_index_client/gem_parser.rb +0 -32
@@ -58,17 +58,28 @@ module Bundler
58
58
  # @param ruby_version [Bundler::RubyVersion, nil] Requested Ruby Version
59
59
  # @param optional_groups [Array(String)] A list of optional groups
60
60
  def initialize(lockfile, dependencies, sources, unlock, ruby_version = nil, optional_groups = [], gemfiles = [])
61
- if [true, false].include?(unlock)
61
+ unlock ||= {}
62
+
63
+ if unlock == true
64
+ @unlocking_all = true
62
65
  @unlocking_bundler = false
63
66
  @unlocking = unlock
67
+ @sources_to_unlock = []
68
+ @unlocking_ruby = false
69
+ @explicit_unlocks = []
70
+ conservative = false
64
71
  else
72
+ @unlocking_all = false
65
73
  @unlocking_bundler = unlock.delete(:bundler)
66
74
  @unlocking = unlock.any? {|_k, v| !Array(v).empty? }
75
+ @sources_to_unlock = unlock.delete(:sources) || []
76
+ @unlocking_ruby = unlock.delete(:ruby)
77
+ @explicit_unlocks = unlock.delete(:gems) || []
78
+ conservative = unlock.delete(:conservative)
67
79
  end
68
80
 
69
81
  @dependencies = dependencies
70
82
  @sources = sources
71
- @unlock = unlock
72
83
  @optional_groups = optional_groups
73
84
  @prefer_local = false
74
85
  @specs = nil
@@ -83,7 +94,8 @@ module Bundler
83
94
 
84
95
  @locked_ruby_version = nil
85
96
  @new_platforms = []
86
- @removed_platform = nil
97
+ @removed_platforms = []
98
+ @originally_invalid_platforms = []
87
99
 
88
100
  if lockfile_exists?
89
101
  @lockfile_contents = Bundler.read_file(lockfile)
@@ -93,29 +105,24 @@ module Bundler
93
105
  @platforms = @locked_platforms.dup
94
106
  @locked_bundler_version = @locked_gems.bundler_version
95
107
  @locked_ruby_version = @locked_gems.ruby_version
96
- @originally_locked_deps = @locked_gems.dependencies
108
+ @locked_deps = @locked_gems.dependencies
97
109
  @originally_locked_specs = SpecSet.new(@locked_gems.specs)
98
110
  @locked_checksums = @locked_gems.checksums
99
111
 
100
- if unlock != true
101
- @locked_deps = @originally_locked_deps
102
- @locked_specs = @originally_locked_specs
103
- @locked_sources = @locked_gems.sources
104
- else
105
- @unlock = {}
106
- @locked_deps = {}
112
+ if @unlocking_all
107
113
  @locked_specs = SpecSet.new([])
108
114
  @locked_sources = []
115
+ else
116
+ @locked_specs = @originally_locked_specs
117
+ @locked_sources = @locked_gems.sources
109
118
  end
110
119
  else
111
- @unlock = {}
112
- @locked_gems = nil
120
+ @locked_gems = nil
113
121
  @locked_platforms = []
114
122
  @most_specific_locked_platform = nil
115
123
  @platforms = []
116
124
  @locked_deps = {}
117
125
  @locked_specs = SpecSet.new([])
118
- @originally_locked_deps = {}
119
126
  @originally_locked_specs = @locked_specs
120
127
  @locked_sources = []
121
128
  @locked_checksums = Bundler.feature_flag.lockfile_checksums?
@@ -134,21 +141,17 @@ module Bundler
134
141
  @sources.merged_gem_lockfile_sections!(locked_gem_sources.first)
135
142
  end
136
143
 
137
- @sources_to_unlock = @unlock.delete(:sources) || []
138
- @unlock[:ruby] ||= if @ruby_version && locked_ruby_version_object
144
+ @unlocking_ruby ||= if @ruby_version && locked_ruby_version_object
139
145
  @ruby_version.diff(locked_ruby_version_object)
140
146
  end
141
- @unlocking ||= @unlock[:ruby] ||= (!@locked_ruby_version ^ !@ruby_version)
147
+ @unlocking ||= @unlocking_ruby ||= (!@locked_ruby_version ^ !@ruby_version)
142
148
 
143
149
  @current_platform_missing = add_current_platform unless Bundler.frozen_bundle?
144
150
 
145
- converge_path_sources_to_gemspec_sources
146
- @path_changes = converge_paths
147
151
  @source_changes = converge_sources
152
+ @path_changes = converge_paths
148
153
 
149
- @explicit_unlocks = @unlock.delete(:gems) || []
150
-
151
- if @unlock[:conservative]
154
+ if conservative
152
155
  @gems_to_unlock = @explicit_unlocks.any? ? @explicit_unlocks : @dependencies.map(&:name)
153
156
  else
154
157
  eager_unlock = @explicit_unlocks.map {|name| Dependency.new(name, ">= 0") }
@@ -220,6 +223,8 @@ module Bundler
220
223
 
221
224
  def prefer_local!
222
225
  @prefer_local = true
226
+
227
+ sources.prefer_local!
223
228
  end
224
229
 
225
230
  # For given dependency list returns a SpecSet with Gemspec of all the required
@@ -252,7 +257,7 @@ module Bundler
252
257
  rescue BundlerError => e
253
258
  @resolve = nil
254
259
  @resolver = nil
255
- @resolution_packages = nil
260
+ @resolution_base = nil
256
261
  @source_requirements = nil
257
262
  @specs = nil
258
263
 
@@ -325,18 +330,14 @@ module Bundler
325
330
  SpecSet.new(filter_specs(@locked_specs, @dependencies - deleted_deps))
326
331
  else
327
332
  Bundler.ui.debug "Found no changes, using resolution from the lockfile"
328
- if @removed_platform || @locked_gems.may_include_redundant_platform_specific_gems?
333
+ if @removed_platforms.any? || @locked_gems.may_include_redundant_platform_specific_gems?
329
334
  SpecSet.new(filter_specs(@locked_specs, @dependencies))
330
335
  else
331
336
  @locked_specs
332
337
  end
333
338
  end
334
339
  else
335
- if lockfile_exists?
336
- Bundler.ui.debug "Found changes from the lockfile, re-resolving dependencies because #{change_reason}"
337
- else
338
- Bundler.ui.debug "Resolving dependencies because there's no lockfile"
339
- end
340
+ Bundler.ui.debug resolve_needed_reason
340
341
 
341
342
  start_resolution
342
343
  end
@@ -374,7 +375,7 @@ module Bundler
374
375
 
375
376
  def locked_ruby_version
376
377
  return unless ruby_version
377
- if @unlock[:ruby] || !@locked_ruby_version
378
+ if @unlocking_ruby || !@locked_ruby_version
378
379
  Bundler::RubyVersion.system
379
380
  else
380
381
  @locked_ruby_version
@@ -407,51 +408,18 @@ module Bundler
407
408
 
408
409
  raise ProductionError, "Frozen mode is set, but there's no lockfile" unless lockfile_exists?
409
410
 
410
- added = []
411
- deleted = []
412
- changed = []
413
-
414
- new_platforms = @platforms - @locked_platforms
415
- deleted_platforms = @locked_platforms - @platforms
416
- added.concat new_platforms.map {|p| "* platform: #{p}" }
417
- deleted.concat deleted_platforms.map {|p| "* platform: #{p}" }
418
-
419
- added.concat new_deps.map {|d| "* #{pretty_dep(d)}" } if new_deps.any?
420
- deleted.concat deleted_deps.map {|d| "* #{pretty_dep(d)}" } if deleted_deps.any?
421
-
422
- both_sources = Hash.new {|h, k| h[k] = [] }
423
- current_dependencies.each {|d| both_sources[d.name][0] = d }
424
- current_locked_dependencies.each {|d| both_sources[d.name][1] = d }
425
-
426
- both_sources.each do |name, (dep, lock_dep)|
427
- next if dep.nil? || lock_dep.nil?
428
-
429
- gemfile_source = dep.source || default_source
430
- lock_source = lock_dep.source || default_source
431
- next if lock_source.include?(gemfile_source)
432
-
433
- gemfile_source_name = dep.source ? gemfile_source.to_gemfile : "no specified source"
434
- lockfile_source_name = lock_dep.source ? lock_source.to_gemfile : "no specified source"
435
- changed << "* #{name} from `#{lockfile_source_name}` to `#{gemfile_source_name}`"
436
- end
437
-
438
- reason = nothing_changed? ? "some dependencies were deleted from your gemfile" : change_reason
439
- msg = String.new
440
- msg << "#{reason.capitalize.strip}, but the lockfile can't be updated because frozen mode is set"
441
- msg << "\n\nYou have added to the Gemfile:\n" << added.join("\n") if added.any?
442
- msg << "\n\nYou have deleted from the Gemfile:\n" << deleted.join("\n") if deleted.any?
443
- msg << "\n\nYou have changed in the Gemfile:\n" << changed.join("\n") if changed.any?
444
- msg << "\n\nRun `bundle install` elsewhere and add the updated #{SharedHelpers.relative_gemfile_path} to version control.\n"
411
+ msg = lockfile_changes_summary("frozen mode is set")
412
+ return unless msg
445
413
 
446
414
  unless explicit_flag
447
415
  suggested_command = unless Bundler.settings.locations("frozen").keys.include?(:env)
448
416
  "bundle config set frozen false"
449
417
  end
450
- msg << "If this is a development machine, remove the #{SharedHelpers.relative_lockfile_path} " \
418
+ msg << "\n\nIf this is a development machine, remove the #{SharedHelpers.relative_lockfile_path} " \
451
419
  "freeze by running `#{suggested_command}`." if suggested_command
452
420
  end
453
421
 
454
- raise ProductionError, msg if added.any? || deleted.any? || changed.any? || !nothing_changed?
422
+ raise ProductionError, msg
455
423
  end
456
424
 
457
425
  def validate_runtime!
@@ -485,7 +453,7 @@ module Bundler
485
453
  end
486
454
 
487
455
  def validate_platforms!
488
- return if current_platform_locked?
456
+ return if current_platform_locked? || @platforms.include?(Gem::Platform::RUBY)
489
457
 
490
458
  raise ProductionError, "Your bundle only supports platforms #{@platforms.map(&:to_s)} " \
491
459
  "but your local platform is #{local_platform}. " \
@@ -493,7 +461,7 @@ module Bundler
493
461
  end
494
462
 
495
463
  def normalize_platforms
496
- @platforms = resolve.normalize_platforms!(current_dependencies, platforms)
464
+ resolve.normalize_platforms!(current_dependencies, platforms)
497
465
 
498
466
  @resolve = SpecSet.new(resolve.for(current_dependencies, @platforms))
499
467
  end
@@ -506,10 +474,10 @@ module Bundler
506
474
  end
507
475
 
508
476
  def remove_platform(platform)
509
- removed_platform = @platforms.delete(Gem::Platform.new(platform))
510
- @removed_platform ||= removed_platform
511
- return if removed_platform
512
- raise InvalidOption, "Unable to remove the platform `#{platform}` since the only platforms are #{@platforms.join ", "}"
477
+ raise InvalidOption, "Unable to remove the platform `#{platform}` since the only platforms are #{@platforms.join ", "}" unless @platforms.include?(platform)
478
+
479
+ @removed_platforms << platform
480
+ @platforms.delete(platform)
513
481
  end
514
482
 
515
483
  def nothing_changed?
@@ -536,6 +504,45 @@ module Bundler
536
504
 
537
505
  private
538
506
 
507
+ def lockfile_changes_summary(update_refused_reason)
508
+ added = []
509
+ deleted = []
510
+ changed = []
511
+
512
+ added.concat @new_platforms.map {|p| "* platform: #{p}" }
513
+ deleted.concat @removed_platforms.map {|p| "* platform: #{p}" }
514
+
515
+ added.concat new_deps.map {|d| "* #{pretty_dep(d)}" } if new_deps.any?
516
+ deleted.concat deleted_deps.map {|d| "* #{pretty_dep(d)}" } if deleted_deps.any?
517
+
518
+ both_sources = Hash.new {|h, k| h[k] = [] }
519
+ current_dependencies.each {|d| both_sources[d.name][0] = d }
520
+ current_locked_dependencies.each {|d| both_sources[d.name][1] = d }
521
+
522
+ both_sources.each do |name, (dep, lock_dep)|
523
+ next if dep.nil? || lock_dep.nil?
524
+
525
+ gemfile_source = dep.source || default_source
526
+ lock_source = lock_dep.source || default_source
527
+ next if lock_source.include?(gemfile_source)
528
+
529
+ gemfile_source_name = dep.source ? gemfile_source.to_gemfile : "no specified source"
530
+ lockfile_source_name = lock_dep.source ? lock_source.to_gemfile : "no specified source"
531
+ changed << "* #{name} from `#{lockfile_source_name}` to `#{gemfile_source_name}`"
532
+ end
533
+
534
+ return unless added.any? || deleted.any? || changed.any? || resolve_needed?
535
+
536
+ msg = String.new("#{change_reason.capitalize.strip}, but ")
537
+ msg << "the lockfile " unless msg.start_with?("Your lockfile")
538
+ msg << "can't be updated because #{update_refused_reason}"
539
+ msg << "\n\nYou have added to the Gemfile:\n" << added.join("\n") if added.any?
540
+ msg << "\n\nYou have deleted from the Gemfile:\n" << deleted.join("\n") if deleted.any?
541
+ msg << "\n\nYou have changed in the Gemfile:\n" << changed.join("\n") if changed.any?
542
+ msg << "\n\nRun `bundle install` elsewhere and add the updated #{SharedHelpers.relative_lockfile_path} to version control.\n" unless unlocking?
543
+ msg
544
+ end
545
+
539
546
  def install_needed?
540
547
  resolve_needed? || missing_specs?
541
548
  end
@@ -551,6 +558,7 @@ module Bundler
551
558
  @local_changes ||
552
559
  @missing_lockfile_dep ||
553
560
  @unlocking_bundler ||
561
+ @locked_spec_with_missing_checksums ||
554
562
  @locked_spec_with_missing_deps ||
555
563
  @locked_spec_with_invalid_deps
556
564
  end
@@ -596,13 +604,17 @@ module Bundler
596
604
  return
597
605
  end
598
606
 
599
- SharedHelpers.filesystem_access(file) do |p|
600
- File.open(p, "wb") {|f| f.puts(contents) }
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")
601
613
  end
602
614
  end
603
615
 
604
616
  def resolver
605
- @resolver ||= Resolver.new(resolution_packages, gem_version_promoter, @most_specific_locked_platform)
617
+ @resolver ||= Resolver.new(resolution_base, gem_version_promoter, @most_specific_locked_platform)
606
618
  end
607
619
 
608
620
  def expanded_dependencies
@@ -616,14 +628,15 @@ module Bundler
616
628
  [Dependency.new("bundler", @unlocking_bundler)] + dependencies
617
629
  end
618
630
 
619
- def resolution_packages
620
- @resolution_packages ||= begin
631
+ def resolution_base
632
+ @resolution_base ||= begin
621
633
  last_resolve = converge_locked_specs
622
634
  remove_invalid_platforms!
623
- packages = Resolver::Base.new(source_requirements, expanded_dependencies, last_resolve, @platforms, locked_specs: @originally_locked_specs, unlock: @gems_to_unlock, prerelease: gem_version_promoter.pre?, prefer_local: @prefer_local)
624
- packages = additional_base_requirements_to_prevent_downgrades(packages, last_resolve)
625
- packages = additional_base_requirements_to_force_updates(packages)
626
- packages
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)
637
+ base = additional_base_requirements_to_prevent_downgrades(base)
638
+ base = additional_base_requirements_to_force_updates(base)
639
+ base
627
640
  end
628
641
  end
629
642
 
@@ -639,6 +652,8 @@ module Bundler
639
652
  specs = begin
640
653
  resolve.materialize(dependencies)
641
654
  rescue IncorrectLockfileDependencies => e
655
+ raise if Bundler.frozen_bundle?
656
+
642
657
  spec = e.spec
643
658
  raise "Infinite loop while fixing lockfile dependencies" if incorrect_spec == spec
644
659
 
@@ -696,8 +711,7 @@ module Bundler
696
711
  still_incomplete_specs = resolve.incomplete_specs
697
712
 
698
713
  if still_incomplete_specs == incomplete_specs
699
- package = resolution_packages.get_package(incomplete_specs.first.name)
700
- resolver.raise_not_found! package
714
+ resolver.raise_incomplete! incomplete_specs
701
715
  end
702
716
 
703
717
  incomplete_specs = still_incomplete_specs
@@ -719,12 +733,12 @@ module Bundler
719
733
  end
720
734
 
721
735
  def reresolve_without(incomplete_specs)
722
- resolution_packages.delete(incomplete_specs)
736
+ resolution_base.delete(incomplete_specs)
723
737
  @resolve = start_resolution
724
738
  end
725
739
 
726
740
  def start_resolution
727
- local_platform_needed_for_resolvability = @most_specific_non_local_locked_ruby_platform && !@platforms.include?(local_platform)
741
+ local_platform_needed_for_resolvability = @most_specific_non_local_locked_platform && !@platforms.include?(local_platform)
728
742
  @platforms << local_platform if local_platform_needed_for_resolvability
729
743
  add_platform(Gem::Platform::RUBY) if RUBY_ENGINE == "truffleruby"
730
744
 
@@ -732,15 +746,27 @@ module Bundler
732
746
 
733
747
  @resolved_bundler_version = result.find {|spec| spec.name == "bundler" }&.version
734
748
 
735
- if @most_specific_non_local_locked_ruby_platform
736
- if spec_set_incomplete_for_platform?(result, @most_specific_non_local_locked_ruby_platform)
737
- @platforms.delete(@most_specific_non_local_locked_ruby_platform)
749
+ @new_platforms.each do |platform|
750
+ incomplete_specs = result.incomplete_specs_for_platform(current_dependencies, platform)
751
+
752
+ if incomplete_specs.any?
753
+ resolver.raise_incomplete! incomplete_specs
754
+ end
755
+ end
756
+
757
+ if @most_specific_non_local_locked_platform
758
+ if result.incomplete_for_platform?(current_dependencies, @most_specific_non_local_locked_platform)
759
+ @platforms.delete(@most_specific_non_local_locked_platform)
738
760
  elsif local_platform_needed_for_resolvability
739
761
  @platforms.delete(local_platform)
740
762
  end
741
763
  end
742
764
 
743
- @platforms = result.add_extra_platforms!(platforms) if should_add_extra_platforms?
765
+ if should_add_extra_platforms?
766
+ result.add_extra_platforms!(platforms)
767
+ elsif @originally_invalid_platforms.any?
768
+ result.add_originally_invalid_platforms!(platforms, @originally_invalid_platforms)
769
+ end
744
770
 
745
771
  SpecSet.new(result.for(dependencies, @platforms | [Gem::Platform::RUBY]))
746
772
  end
@@ -751,52 +777,78 @@ module Bundler
751
777
 
752
778
  def current_platform_locked?
753
779
  @platforms.any? do |bundle_platform|
754
- MatchPlatform.platforms_match?(bundle_platform, local_platform)
780
+ generic_local_platform == bundle_platform || local_platform === bundle_platform
755
781
  end
756
782
  end
757
783
 
758
784
  def add_current_platform
759
785
  return if @platforms.include?(local_platform)
760
786
 
761
- @most_specific_non_local_locked_ruby_platform = find_most_specific_locked_ruby_platform
762
- return if @most_specific_non_local_locked_ruby_platform
787
+ @most_specific_non_local_locked_platform = find_most_specific_locked_platform
788
+ return if @most_specific_non_local_locked_platform
763
789
 
764
790
  @platforms << local_platform
765
791
  true
766
792
  end
767
793
 
768
- def find_most_specific_locked_ruby_platform
769
- return unless generic_local_platform_is_ruby? && current_platform_locked?
794
+ def find_most_specific_locked_platform
795
+ return unless current_platform_locked?
770
796
 
771
797
  @most_specific_locked_platform
772
798
  end
773
799
 
774
- def change_reason
775
- if unlocking?
776
- unlock_targets = if @gems_to_unlock.any?
777
- ["gems", @gems_to_unlock]
778
- elsif @sources_to_unlock.any?
779
- ["sources", @sources_to_unlock]
800
+ def resolve_needed_reason
801
+ if lockfile_exists?
802
+ if unlocking?
803
+ "Re-resolving dependencies because #{unlocking_reason}"
804
+ else
805
+ "Found changes from the lockfile, re-resolving dependencies because #{lockfile_changed_reason}"
780
806
  end
807
+ else
808
+ "Resolving dependencies because there's no lockfile"
809
+ end
810
+ end
781
811
 
782
- unlock_reason = if unlock_targets
783
- "#{unlock_targets.first}: (#{unlock_targets.last.join(", ")})"
812
+ def change_reason
813
+ if resolve_needed?
814
+ if unlocking?
815
+ unlocking_reason
784
816
  else
785
- @unlock[:ruby] ? "ruby" : ""
817
+ lockfile_changed_reason
786
818
  end
819
+ else
820
+ "some dependencies were deleted from your gemfile"
821
+ end
822
+ end
823
+
824
+ def unlocking_reason
825
+ unlock_targets = if @gems_to_unlock.any?
826
+ ["gems", @gems_to_unlock]
827
+ elsif @sources_to_unlock.any?
828
+ ["sources", @sources_to_unlock]
829
+ end
787
830
 
788
- return "bundler is unlocking #{unlock_reason}"
831
+ unlock_reason = if unlock_targets
832
+ "#{unlock_targets.first}: (#{unlock_targets.last.join(", ")})"
833
+ else
834
+ @unlocking_ruby ? "ruby" : ""
789
835
  end
836
+
837
+ "bundler is unlocking #{unlock_reason}"
838
+ end
839
+
840
+ def lockfile_changed_reason
790
841
  [
791
842
  [@source_changes, "the list of sources changed"],
792
843
  [@dependency_changes, "the dependencies in your gemfile changed"],
793
- [@current_platform_missing, "your lockfile does not include the current platform"],
794
- [@new_platforms.any?, "you added a new platform to your gemfile"],
844
+ [@current_platform_missing, "your lockfile is missing the current platform"],
845
+ [@new_platforms.any?, "you are adding a new platform to your lockfile"],
795
846
  [@path_changes, "the gemspecs for path gems changed"],
796
847
  [@local_changes, "the gemspecs for git local gems changed"],
797
- [@missing_lockfile_dep, "your lock file is missing \"#{@missing_lockfile_dep}\""],
848
+ [@missing_lockfile_dep, "your lockfile is missing \"#{@missing_lockfile_dep}\""],
798
849
  [@unlocking_bundler, "an update to the version of Bundler itself was requested"],
799
- [@locked_spec_with_missing_deps, "your lock file includes \"#{@locked_spec_with_missing_deps}\" but not some of its dependencies"],
850
+ [@locked_spec_with_missing_checksums, "your lockfile is missing a CHECKSUMS entry for \"#{@locked_spec_with_missing_checksums}\""],
851
+ [@locked_spec_with_missing_deps, "your lockfile includes \"#{@locked_spec_with_missing_deps}\" but not some of its dependencies"],
800
852
  [@locked_spec_with_invalid_deps, "your lockfile does not satisfy dependencies of \"#{@locked_spec_with_invalid_deps}\""],
801
853
  ].select(&:first).map(&:last).join(", ")
802
854
  end
@@ -813,8 +865,8 @@ module Bundler
813
865
  !locked || dependencies_for_source_changed?(source, locked) || specs_for_source_changed?(source)
814
866
  end
815
867
 
816
- def dependencies_for_source_changed?(source, locked_source = source)
817
- deps_for_source = @dependencies.select {|s| s.source == source }
868
+ def dependencies_for_source_changed?(source, locked_source)
869
+ deps_for_source = @dependencies.select {|dep| dep.source == source }
818
870
  locked_deps_for_source = locked_dependencies.select {|dep| dep.source == locked_source }
819
871
 
820
872
  deps_for_source.uniq.sort != locked_deps_for_source.sort
@@ -822,7 +874,7 @@ module Bundler
822
874
 
823
875
  def specs_for_source_changed?(source)
824
876
  locked_index = Index.new
825
- locked_index.use(@locked_specs.select {|s| source.can_lock?(s) })
877
+ locked_index.use(@locked_specs.select {|s| s.replace_source_with!(source) })
826
878
 
827
879
  !locked_index.subset?(source.specs)
828
880
  rescue PathError, GitError => e
@@ -852,29 +904,29 @@ module Bundler
852
904
  end
853
905
 
854
906
  def check_lockfile
855
- @missing_lockfile_dep = nil
856
-
857
907
  @locked_spec_with_invalid_deps = nil
858
908
  @locked_spec_with_missing_deps = nil
909
+ @locked_spec_with_missing_checksums = nil
859
910
 
860
- missing = []
911
+ missing_deps = []
912
+ missing_checksums = []
861
913
  invalid = []
862
914
 
863
915
  @locked_specs.each do |s|
916
+ missing_checksums << s if @locked_checksums && s.source.checksum_store.missing?(s)
917
+
864
918
  validation = @locked_specs.validate_deps(s)
865
919
 
866
- missing << s if validation == :missing
920
+ missing_deps << s if validation == :missing
867
921
  invalid << s if validation == :invalid
868
922
  end
869
923
 
870
- if missing.any?
871
- @locked_specs.delete(missing)
924
+ @locked_spec_with_missing_checksums = missing_checksums.first.name if missing_checksums.any?
872
925
 
873
- @locked_spec_with_missing_deps = missing.first.name
874
- elsif !@dependency_changes
875
- @missing_lockfile_dep = current_dependencies.find do |d|
876
- @locked_specs[d.name].empty? && d.name != "bundler"
877
- end&.name
926
+ if missing_deps.any?
927
+ @locked_specs.delete(missing_deps)
928
+
929
+ @locked_spec_with_missing_deps = missing_deps.first.name
878
930
  end
879
931
 
880
932
  if invalid.any?
@@ -890,24 +942,6 @@ module Bundler
890
942
  end
891
943
  end
892
944
 
893
- def converge_path_source_to_gemspec_source(source)
894
- return source unless source.instance_of?(Source::Path)
895
- gemspec_source = sources.path_sources.find {|s| s.is_a?(Source::Gemspec) && s.as_path_source == source }
896
- gemspec_source || source
897
- end
898
-
899
- def converge_path_sources_to_gemspec_sources
900
- @locked_sources.map! do |source|
901
- converge_path_source_to_gemspec_source(source)
902
- end
903
- @locked_specs.each do |spec|
904
- spec.source &&= converge_path_source_to_gemspec_source(spec.source)
905
- end
906
- @locked_deps.each do |_, dep|
907
- dep.source &&= converge_path_source_to_gemspec_source(dep.source)
908
- end
909
- end
910
-
911
945
  def converge_sources
912
946
  # Replace the sources from the Gemfile with the sources from the Gemfile.lock,
913
947
  # if they exist in the Gemfile.lock and are `==`. If you can't find an equivalent
@@ -935,32 +969,39 @@ module Bundler
935
969
  end
936
970
 
937
971
  def converge_dependencies
938
- changes = false
972
+ @missing_lockfile_dep = nil
973
+ @changed_dependencies = []
939
974
 
940
- @dependencies.each do |dep|
975
+ current_dependencies.each do |dep|
941
976
  if dep.source
942
977
  dep.source = sources.get(dep.source)
943
978
  end
944
979
 
945
- unless locked_dep = @originally_locked_deps[dep.name]
946
- changes = true
947
- next
980
+ name = dep.name
981
+
982
+ dep_changed = @locked_deps[name].nil?
983
+
984
+ unless name == "bundler"
985
+ locked_specs = @originally_locked_specs[name]
986
+
987
+ if locked_specs.empty?
988
+ @missing_lockfile_dep = name if dep_changed == false
989
+ else
990
+ if locked_specs.map(&:source).uniq.size > 1
991
+ @locked_specs.delete(locked_specs.select {|s| s.source != dep.source })
992
+ end
993
+
994
+ unless dep.matches_spec?(locked_specs.first)
995
+ @gems_to_unlock << name
996
+ dep_changed = true
997
+ end
998
+ end
948
999
  end
949
1000
 
950
- # Gem::Dependency#== matches Gem::Dependency#type. As the lockfile
951
- # doesn't carry a notion of the dependency type, if you use
952
- # add_development_dependency in a gemspec that's loaded with the gemspec
953
- # directive, the lockfile dependencies and resolved dependencies end up
954
- # with a mismatch on #type. Work around that by setting the type on the
955
- # dep from the lockfile.
956
- locked_dep.instance_variable_set(:@type, dep.type)
957
-
958
- # We already know the name matches from the hash lookup
959
- # so we only need to check the requirement now
960
- changes ||= dep.requirement != locked_dep.requirement
1001
+ @changed_dependencies << name if dep_changed
961
1002
  end
962
1003
 
963
- changes
1004
+ @changed_dependencies.any?
964
1005
  end
965
1006
 
966
1007
  # Remove elements from the locked specs that are expired. This will most
@@ -1022,11 +1063,6 @@ module Bundler
1022
1063
  end
1023
1064
  end
1024
1065
 
1025
- if dep.nil? && requested_dep = requested_dependencies.find {|d| name == d.name }
1026
- @gems_to_unlock << name
1027
- deps << requested_dep
1028
- end
1029
-
1030
1066
  converged << s
1031
1067
  end
1032
1068
 
@@ -1095,23 +1131,27 @@ module Bundler
1095
1131
  current == proposed
1096
1132
  end
1097
1133
 
1098
- def additional_base_requirements_to_prevent_downgrades(resolution_packages, last_resolve)
1099
- return resolution_packages unless @locked_gems && !sources.expired_sources?(@locked_gems.sources)
1100
- converge_specs(@originally_locked_specs - last_resolve).each do |locked_spec|
1134
+ def additional_base_requirements_to_prevent_downgrades(resolution_base)
1135
+ return resolution_base unless @locked_gems && !sources.expired_sources?(@locked_gems.sources)
1136
+ @originally_locked_specs.each do |locked_spec|
1101
1137
  next if locked_spec.source.is_a?(Source::Path)
1102
- resolution_packages.base_requirements[locked_spec.name] = Gem::Requirement.new(">= #{locked_spec.version}")
1138
+
1139
+ name = locked_spec.name
1140
+ next if @changed_dependencies.include?(name)
1141
+
1142
+ resolution_base.base_requirements[name] = Gem::Requirement.new(">= #{locked_spec.version}")
1103
1143
  end
1104
- resolution_packages
1144
+ resolution_base
1105
1145
  end
1106
1146
 
1107
- def additional_base_requirements_to_force_updates(resolution_packages)
1108
- return resolution_packages if @explicit_unlocks.empty?
1147
+ def additional_base_requirements_to_force_updates(resolution_base)
1148
+ return resolution_base if @explicit_unlocks.empty?
1109
1149
  full_update = dup_for_full_unlock.resolve
1110
1150
  @explicit_unlocks.each do |name|
1111
1151
  version = full_update.version_for(name)
1112
- resolution_packages.base_requirements[name] = Gem::Requirement.new("= #{version}") if version
1152
+ resolution_base.base_requirements[name] = Gem::Requirement.new("= #{version}") if version
1113
1153
  end
1114
- resolution_packages
1154
+ resolution_base
1115
1155
  end
1116
1156
 
1117
1157
  def dup_for_full_unlock
@@ -1128,20 +1168,16 @@ module Bundler
1128
1168
  def remove_invalid_platforms!
1129
1169
  return if Bundler.frozen_bundle?
1130
1170
 
1131
- platforms.reverse_each do |platform|
1132
- next if local_platform == platform ||
1133
- @new_platforms.include?(platform) ||
1134
- @path_changes ||
1135
- @dependency_changes ||
1136
- @locked_spec_with_invalid_deps ||
1137
- !spec_set_incomplete_for_platform?(@originally_locked_specs, platform)
1171
+ skips = (@new_platforms + [local_platform]).uniq
1138
1172
 
1139
- remove_platform(platform)
1140
- end
1141
- end
1173
+ # We should probably avoid removing non-ruby platforms, since that means
1174
+ # lockfile will no longer install on those platforms, so a error to give
1175
+ # heads up to the user may be better. However, we have tests expecting
1176
+ # non ruby platform autoremoval to work, so leaving that in place for
1177
+ # now.
1178
+ skips |= platforms - [Gem::Platform::RUBY] if @dependency_changes
1142
1179
 
1143
- def spec_set_incomplete_for_platform?(spec_set, platform)
1144
- spec_set.incomplete_for_platform?(current_dependencies, platform)
1180
+ @originally_invalid_platforms = @originally_locked_specs.remove_invalid_platforms!(current_dependencies, platforms, skips: skips)
1145
1181
  end
1146
1182
 
1147
1183
  def source_map