bundler 2.2.0 → 2.2.5

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of bundler might be problematic. Click here for more details.

Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +67 -20
  3. data/bundler.gemspec +1 -1
  4. data/lib/bundler.rb +8 -2
  5. data/lib/bundler/build_metadata.rb +2 -2
  6. data/lib/bundler/cli.rb +3 -6
  7. data/lib/bundler/cli/gem.rb +2 -0
  8. data/lib/bundler/cli/install.rb +14 -5
  9. data/lib/bundler/cli/outdated.rb +2 -2
  10. data/lib/bundler/cli/update.rb +1 -1
  11. data/lib/bundler/compact_index_client/cache.rb +5 -13
  12. data/lib/bundler/compact_index_client/gem_parser.rb +28 -0
  13. data/lib/bundler/compact_index_client/updater.rb +0 -8
  14. data/lib/bundler/definition.rb +24 -21
  15. data/lib/bundler/dependency.rb +3 -1
  16. data/lib/bundler/gem_helper.rb +3 -3
  17. data/lib/bundler/gem_helpers.rb +30 -24
  18. data/lib/bundler/lazy_specification.rb +16 -3
  19. data/{man → lib/bundler/man}/bundle-add.1 +0 -0
  20. data/{man → lib/bundler/man}/bundle-binstubs.1 +0 -0
  21. data/{man → lib/bundler/man}/bundle-cache.1 +0 -0
  22. data/{man → lib/bundler/man}/bundle-check.1 +0 -0
  23. data/{man → lib/bundler/man}/bundle-clean.1 +0 -0
  24. data/{man → lib/bundler/man}/bundle-config.1 +0 -0
  25. data/{man → lib/bundler/man}/bundle-doctor.1 +0 -0
  26. data/{man → lib/bundler/man}/bundle-exec.1 +0 -0
  27. data/{man → lib/bundler/man}/bundle-gem.1 +0 -0
  28. data/{man → lib/bundler/man}/bundle-info.1 +0 -0
  29. data/{man → lib/bundler/man}/bundle-init.1 +0 -0
  30. data/{man → lib/bundler/man}/bundle-inject.1 +0 -0
  31. data/{man → lib/bundler/man}/bundle-install.1 +0 -0
  32. data/{man → lib/bundler/man}/bundle-list.1 +0 -0
  33. data/{man → lib/bundler/man}/bundle-lock.1 +0 -0
  34. data/{man → lib/bundler/man}/bundle-open.1 +0 -0
  35. data/{man → lib/bundler/man}/bundle-outdated.1 +0 -0
  36. data/{man → lib/bundler/man}/bundle-platform.1 +0 -0
  37. data/{man → lib/bundler/man}/bundle-pristine.1 +0 -0
  38. data/{man → lib/bundler/man}/bundle-remove.1 +0 -0
  39. data/{man → lib/bundler/man}/bundle-show.1 +0 -0
  40. data/{man → lib/bundler/man}/bundle-update.1 +0 -0
  41. data/{man → lib/bundler/man}/bundle-viz.1 +0 -0
  42. data/{man → lib/bundler/man}/bundle.1 +0 -0
  43. data/{man → lib/bundler/man}/gemfile.5 +0 -0
  44. data/{man → lib/bundler/man}/index.txt +0 -0
  45. data/lib/bundler/resolver.rb +29 -27
  46. data/lib/bundler/resolver/spec_group.rb +19 -25
  47. data/lib/bundler/rubygems_integration.rb +0 -6
  48. data/lib/bundler/source/git.rb +18 -16
  49. data/lib/bundler/source/git/git_proxy.rb +54 -49
  50. data/lib/bundler/source/path/installer.rb +2 -0
  51. data/lib/bundler/source/rubygems.rb +10 -1
  52. data/lib/bundler/spec_set.rb +5 -8
  53. data/lib/bundler/stub_specification.rb +0 -2
  54. data/lib/bundler/templates/newgem/Gemfile.tt +1 -1
  55. data/lib/bundler/templates/newgem/newgem.gemspec.tt +1 -1
  56. data/lib/bundler/templates/newgem/rubocop.yml.tt +3 -0
  57. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb +8 -1
  58. data/lib/bundler/version.rb +1 -1
  59. metadata +33 -32
@@ -118,7 +118,7 @@ module Bundler
118
118
  end
119
119
  @unlocking ||= @unlock[:ruby] ||= (!@locked_ruby_version ^ !@ruby_version)
120
120
 
121
- add_current_platform unless Bundler.frozen_bundle?
121
+ add_current_platform unless current_ruby_platform_locked? || Bundler.frozen_bundle?
122
122
 
123
123
  converge_path_sources_to_gemspec_sources
124
124
  @path_changes = converge_paths
@@ -157,7 +157,7 @@ module Bundler
157
157
  end
158
158
 
159
159
  def resolve_remotely!
160
- raise "Specs already loaded" if @specs
160
+ return if @specs
161
161
  @remote = true
162
162
  sources.remote!
163
163
  specs
@@ -269,6 +269,7 @@ module Bundler
269
269
  else
270
270
  # Run a resolve against the locally available gems
271
271
  Bundler.ui.debug("Found changes from the lockfile, re-resolving dependencies because #{change_reason}")
272
+ expanded_dependencies = expand_dependencies(dependencies + metadata_dependencies, @remote)
272
273
  last_resolve.merge Resolver.resolve(expanded_dependencies, index, source_requirements, last_resolve, gem_version_promoter, additional_base_requirements_for_resolve, platforms)
273
274
  end
274
275
 
@@ -505,15 +506,11 @@ module Bundler
505
506
  end
506
507
 
507
508
  def validate_platforms!
508
- return if @platforms.any? do |bundle_platform|
509
- Bundler.rubygems.platforms.any? do |local_platform|
510
- MatchPlatform.platforms_match?(bundle_platform, local_platform)
511
- end
512
- end
509
+ return if current_platform_locked?
513
510
 
514
511
  raise ProductionError, "Your bundle only supports platforms #{@platforms.map(&:to_s)} " \
515
- "but your local platforms are #{Bundler.rubygems.platforms.map(&:to_s)}, and " \
516
- "there's no compatible match between those two lists."
512
+ "but your local platform is #{Bundler.local_platform}. " \
513
+ "Add the current platform to the lockfile with `bundle lock --add-platform #{Bundler.local_platform}` and try again."
517
514
  end
518
515
 
519
516
  def add_platform(platform)
@@ -526,6 +523,12 @@ module Bundler
526
523
  raise InvalidOption, "Unable to remove the platform `#{platform}` since the only platforms are #{@platforms.join ", "}"
527
524
  end
528
525
 
526
+ def most_specific_locked_platform
527
+ @platforms.min_by do |bundle_platform|
528
+ platform_specificity_match(bundle_platform, local_platform)
529
+ end
530
+ end
531
+
529
532
  def find_resolved_spec(current_spec)
530
533
  specs.find_by_name_and_platform(current_spec.name, current_spec.platform)
531
534
  end
@@ -547,12 +550,20 @@ module Bundler
547
550
 
548
551
  private
549
552
 
550
- def add_current_platform
551
- current_platforms.each {|platform| add_platform(platform) }
553
+ def current_ruby_platform_locked?
554
+ return false unless generic_local_platform == Gem::Platform::RUBY
555
+
556
+ current_platform_locked?
552
557
  end
553
558
 
554
- def current_platforms
555
- [local_platform, generic_local_platform].uniq
559
+ def current_platform_locked?
560
+ @platforms.any? do |bundle_platform|
561
+ MatchPlatform.platforms_match?(bundle_platform, Bundler.local_platform)
562
+ end
563
+ end
564
+
565
+ def add_current_platform
566
+ add_platform(local_platform)
556
567
  end
557
568
 
558
569
  def change_reason
@@ -847,14 +858,6 @@ module Bundler
847
858
  @locked_specs[dep].any? {|s| s.satisfies?(dep) && (!dep.source || s.source.include?(dep.source)) }
848
859
  end
849
860
 
850
- # This list of dependencies is only used in #resolve, so it's OK to add
851
- # the metadata dependencies here
852
- def expanded_dependencies
853
- @expanded_dependencies ||= begin
854
- expand_dependencies(dependencies + metadata_dependencies, @remote)
855
- end
856
- end
857
-
858
861
  def metadata_dependencies
859
862
  @metadata_dependencies ||= begin
860
863
  ruby_versions = ruby_version_requirements(@ruby_version)
@@ -96,9 +96,11 @@ module Bundler
96
96
  def gem_platforms(valid_platforms)
97
97
  return valid_platforms if @platforms.empty?
98
98
 
99
+ valid_generic_platforms = valid_platforms.map {|p| [p, GemHelpers.generic(p)] }.to_h
99
100
  @gem_platforms ||= expanded_platforms.compact.uniq
100
101
 
101
- valid_platforms & @gem_platforms
102
+ filtered_generic_platforms = valid_generic_platforms.values & @gem_platforms
103
+ valid_generic_platforms.select {|_, v| filtered_generic_platforms.include?(v) }.keys
102
104
  end
103
105
 
104
106
  def expanded_platforms
@@ -117,13 +117,13 @@ module Bundler
117
117
  def git_push(remote = nil)
118
118
  remote ||= default_remote
119
119
  perform_git_push remote
120
- perform_git_push "#{remote} #{version_tag}"
120
+ perform_git_push "#{remote} refs/tags/#{version_tag}"
121
121
  Bundler.ui.confirm "Pushed git commits and release tag."
122
122
  end
123
123
 
124
124
  def default_remote
125
- current_branch = sh(%w[git rev-parse --abbrev-ref HEAD]).strip
126
- return "origin" if current_branch.empty?
125
+ # We can replace this with `git branch --show-current` once we drop support for git < 2.22.0
126
+ current_branch = sh(%w[git rev-parse --abbrev-ref HEAD]).gsub(%r{\Aheads/}, "").strip
127
127
 
128
128
  remote_for_branch = sh(%W[git config --get branch.#{current_branch}.remote]).strip
129
129
  return "origin" if remote_for_branch.empty?
@@ -35,41 +35,33 @@ module Bundler
35
35
 
36
36
  def platform_specificity_match(spec_platform, user_platform)
37
37
  spec_platform = Gem::Platform.new(spec_platform)
38
- return PlatformMatch::EXACT_MATCH if spec_platform == user_platform
39
- return PlatformMatch::WORST_MATCH if spec_platform.nil? || spec_platform == Gem::Platform::RUBY || user_platform == Gem::Platform::RUBY
40
-
41
- PlatformMatch.new(
42
- PlatformMatch.os_match(spec_platform, user_platform),
43
- PlatformMatch.cpu_match(spec_platform, user_platform),
44
- PlatformMatch.platform_version_match(spec_platform, user_platform)
45
- )
38
+
39
+ PlatformMatch.specificity_score(spec_platform, user_platform)
46
40
  end
47
41
  module_function :platform_specificity_match
48
42
 
49
43
  def select_best_platform_match(specs, platform)
50
- specs.select {|spec| spec.match_platform(platform) }.
51
- min_by {|spec| platform_specificity_match(spec.platform, platform) }
44
+ matching = specs.select {|spec| spec.match_platform(platform) }
45
+ exact = matching.select {|spec| spec.platform == platform }
46
+ return exact if exact.any?
47
+
48
+ sorted_matching = matching.sort_by {|spec| platform_specificity_match(spec.platform, platform) }
49
+ exemplary_spec = sorted_matching.first
50
+
51
+ sorted_matching.take_while{|spec| same_specificity(platform, spec, exemplary_spec) && same_deps(spec, exemplary_spec) }
52
52
  end
53
53
  module_function :select_best_platform_match
54
54
 
55
- PlatformMatch = Struct.new(:os_match, :cpu_match, :platform_version_match)
56
55
  class PlatformMatch
57
- def <=>(other)
58
- return nil unless other.is_a?(PlatformMatch)
56
+ def self.specificity_score(spec_platform, user_platform)
57
+ return -1 if spec_platform == user_platform
58
+ return 1_000_000 if spec_platform.nil? || spec_platform == Gem::Platform::RUBY || user_platform == Gem::Platform::RUBY
59
59
 
60
- m = os_match <=> other.os_match
61
- return m unless m.zero?
62
-
63
- m = cpu_match <=> other.cpu_match
64
- return m unless m.zero?
65
-
66
- m = platform_version_match <=> other.platform_version_match
67
- m
60
+ os_match(spec_platform, user_platform) +
61
+ cpu_match(spec_platform, user_platform) * 10 +
62
+ platform_version_match(spec_platform, user_platform) * 100
68
63
  end
69
64
 
70
- EXACT_MATCH = new(-1, -1, -1).freeze
71
- WORST_MATCH = new(1_000_000, 1_000_000, 1_000_000).freeze
72
-
73
65
  def self.os_match(spec_platform, user_platform)
74
66
  if spec_platform.os == user_platform.os
75
67
  0
@@ -100,5 +92,19 @@ module Bundler
100
92
  end
101
93
  end
102
94
  end
95
+
96
+ def same_specificity(platform, spec, exemplary_spec)
97
+ platform_specificity_match(spec.platform, platform) == platform_specificity_match(exemplary_spec.platform, platform)
98
+ end
99
+ module_function :same_specificity
100
+
101
+ def same_deps(spec, exemplary_spec)
102
+ same_runtime_deps = spec.dependencies.sort == exemplary_spec.dependencies.sort
103
+ return same_runtime_deps unless spec.is_a?(Gem::Specification) && exemplary_spec.is_a?(Gem::Specification)
104
+
105
+ same_metadata_deps = spec.required_ruby_version == exemplary_spec.required_ruby_version && spec.required_rubygems_version == exemplary_spec.required_rubygems_version
106
+ same_runtime_deps && same_metadata_deps
107
+ end
108
+ module_function :same_deps
103
109
  end
104
110
  end
@@ -4,7 +4,7 @@ require_relative "match_platform"
4
4
 
5
5
  module Bundler
6
6
  class LazySpecification
7
- Identifier = Struct.new(:name, :version, :source, :platform, :dependencies)
7
+ Identifier = Struct.new(:name, :version, :platform)
8
8
  class Identifier
9
9
  include Comparable
10
10
  def <=>(other)
@@ -82,7 +82,7 @@ module Bundler
82
82
  search_object = if source.is_a?(Source::Path)
83
83
  Dependency.new(name, version)
84
84
  else
85
- self
85
+ ruby_platform_materializes_to_ruby_platform? ? self : Dependency.new(name, version)
86
86
  end
87
87
  platform_object = Gem::Platform.new(platform)
88
88
  candidates = source.specs.search(search_object)
@@ -108,7 +108,7 @@ module Bundler
108
108
  end
109
109
 
110
110
  def identifier
111
- @__identifier ||= Identifier.new(name, version, source, platform, dependencies)
111
+ @__identifier ||= Identifier.new(name, version, platform)
112
112
  end
113
113
 
114
114
  def git_version
@@ -129,5 +129,18 @@ module Bundler
129
129
 
130
130
  @specification.send(method, *args, &blk)
131
131
  end
132
+
133
+ #
134
+ # For backwards compatibility with existing lockfiles, if the most specific
135
+ # locked platform is RUBY, we keep the previous behaviour of resolving the
136
+ # best platform variant at materiliazation time. For previous bundler
137
+ # versions (before 2.2.0) this was always the case (except when the lockfile
138
+ # only included non-ruby platforms), but we're also keeping this behaviour
139
+ # on newer bundlers unless users generate the lockfile from scratch or
140
+ # explicitly add a more specific platform.
141
+ #
142
+ def ruby_platform_materializes_to_ruby_platform?
143
+ !Bundler.most_specific_locked_platform?(Gem::Platform::RUBY)
144
+ end
132
145
  end
133
146
  end
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -75,7 +75,7 @@ module Bundler
75
75
  return unless debug?
76
76
  debug_info = yield
77
77
  debug_info = debug_info.inspect unless debug_info.is_a?(String)
78
- warn debug_info.split("\n").map {|s| "BUNDLER: " + " " * depth + s }
78
+ puts debug_info.split("\n").map {|s| "BUNDLER: " + " " * depth + s }
79
79
  end
80
80
 
81
81
  def debug?
@@ -106,18 +106,19 @@ module Bundler
106
106
  specification.dependencies_for_activated_platforms
107
107
  end
108
108
 
109
- def search_for(dependency)
110
- platform = dependency.__platform
111
- dependency = dependency.dep unless dependency.is_a? Gem::Dependency
112
- search = @search_for[dependency] ||= begin
109
+ def search_for(dependency_proxy)
110
+ platform = dependency_proxy.__platform
111
+ dependency = dependency_proxy.dep
112
+ @search_for[dependency_proxy] ||= begin
113
+ name = dependency.name
113
114
  index = index_for(dependency)
114
- results = index.search(dependency, @base[dependency.name])
115
+ results = index.search(dependency, @base[name])
115
116
 
116
- if vertex = @base_dg.vertex_named(dependency.name)
117
+ if vertex = @base_dg.vertex_named(name)
117
118
  locked_requirement = vertex.payload.requirement
118
119
  end
119
120
 
120
- if !@prerelease_specified[dependency.name] && (!@use_gvp || locked_requirement.nil?)
121
+ if !@prerelease_specified[name] && (!@use_gvp || locked_requirement.nil?)
121
122
  # Move prereleases to the beginning of the list, so they're considered
122
123
  # last during resolution.
123
124
  pre, results = results.partition {|spec| spec.version.prerelease? }
@@ -145,31 +146,32 @@ module Bundler
145
146
  end
146
147
  # GVP handles major itself, but it's still a bit risky to trust it with it
147
148
  # until we get it settled with new behavior. For 2.x it can take over all cases.
148
- if !@use_gvp
149
+ search = if !@use_gvp
149
150
  spec_groups
150
151
  else
151
152
  @gem_version_promoter.sort_versions(dependency, spec_groups)
152
153
  end
153
- end
154
- selected_sgs = []
155
- search.each do |sg|
156
- next unless sg.for?(platform)
157
- # Add a spec group for "non platform specific spec" as the fallback
158
- # spec group.
159
- sg_ruby = sg.copy_for(Gem::Platform::RUBY)
160
- selected_sgs << sg_ruby if sg_ruby
161
- sg_all_platforms = nil
162
- all_platforms = @platforms + [platform]
163
- self.class.sort_platforms(all_platforms).reverse_each do |other_platform|
164
- if sg_all_platforms.nil?
165
- sg_all_platforms = sg.copy_for(other_platform)
166
- else
167
- sg_all_platforms.activate_platform!(other_platform)
168
- end
154
+ selected_sgs = []
155
+ search.each do |sg|
156
+ next unless sg.for?(platform)
157
+ sg_all_platforms = sg.copy_for(self.class.sort_platforms(@platforms).reverse)
158
+ next unless sg_all_platforms
159
+
160
+ selected_sgs << sg_all_platforms
161
+
162
+ next if sg_all_platforms.activated_platforms == [Gem::Platform::RUBY]
163
+ # Add a spec group for "non platform specific spec" as the fallback
164
+ # spec group.
165
+ sg_ruby = sg.copy_for([Gem::Platform::RUBY])
166
+ next unless sg_ruby
167
+
168
+ sg_ruby_deps = sg_ruby.dependencies_for_activated_platforms.map(&:dep)
169
+ sg_all_platforms_deps = sg_all_platforms.dependencies_for_activated_platforms.map(&:dep)
170
+
171
+ selected_sgs.insert(-2, sg_ruby) if sg_ruby_deps != sg_all_platforms_deps
169
172
  end
170
- selected_sgs << sg_all_platforms
173
+ selected_sgs
171
174
  end
172
- selected_sgs
173
175
  end
174
176
 
175
177
  def index_for(dependency)
@@ -6,7 +6,7 @@ module Bundler
6
6
  include GemHelpers
7
7
 
8
8
  attr_accessor :name, :version, :source
9
- attr_accessor :ignores_bundler_dependencies
9
+ attr_accessor :ignores_bundler_dependencies, :activated_platforms
10
10
 
11
11
  def initialize(all_specs)
12
12
  @all_specs = all_specs
@@ -25,33 +25,29 @@ module Bundler
25
25
 
26
26
  def to_specs
27
27
  @activated_platforms.map do |p|
28
- next unless s = @specs[p]
29
- lazy_spec = LazySpecification.new(name, version, s.platform, source)
30
- lazy_spec.dependencies.replace s.dependencies
31
- lazy_spec
32
- end.compact.uniq
33
- end
28
+ specs = @specs[p]
29
+ next unless specs.any?
34
30
 
35
- def activate_platform!(platform)
36
- return unless for?(platform)
37
- return if @activated_platforms.include?(platform)
38
- @activated_platforms << platform
31
+ specs.map do |s|
32
+ lazy_spec = LazySpecification.new(name, version, s.platform, source)
33
+ lazy_spec.dependencies.replace s.dependencies
34
+ lazy_spec
35
+ end
36
+ end.flatten.compact.uniq
39
37
  end
40
38
 
41
- def copy_for(platform)
39
+ def copy_for(platforms)
40
+ platforms.select! {|p| for?(p) }
41
+ return unless platforms.any?
42
+
42
43
  copied_sg = self.class.new(@all_specs)
43
44
  copied_sg.ignores_bundler_dependencies = @ignores_bundler_dependencies
44
- return nil unless copied_sg.for?(platform)
45
- copied_sg.activate_platform!(platform)
45
+ copied_sg.activated_platforms = platforms
46
46
  copied_sg
47
47
  end
48
48
 
49
- def spec_for(platform)
50
- @specs[platform]
51
- end
52
-
53
49
  def for?(platform)
54
- !spec_for(platform).nil?
50
+ @specs[platform].any?
55
51
  end
56
52
 
57
53
  def to_s
@@ -62,7 +58,7 @@ module Bundler
62
58
  def dependencies_for_activated_platforms
63
59
  dependencies = @activated_platforms.map {|p| __dependencies[p] }
64
60
  metadata_dependencies = @activated_platforms.map do |platform|
65
- metadata_dependencies(@specs[platform], platform)
61
+ metadata_dependencies(@specs[platform].first, platform)
66
62
  end
67
63
  dependencies.concat(metadata_dependencies).flatten
68
64
  end
@@ -98,7 +94,8 @@ module Bundler
98
94
  def __dependencies
99
95
  @dependencies = Hash.new do |dependencies, platform|
100
96
  dependencies[platform] = []
101
- if spec = @specs[platform]
97
+ specs = @specs[platform]
98
+ if spec = specs.first
102
99
  spec.dependencies.each do |dep|
103
100
  next if dep.type == :development
104
101
  next if @ignores_bundler_dependencies && dep.name == "bundler".freeze
@@ -110,10 +107,7 @@ module Bundler
110
107
  end
111
108
 
112
109
  def metadata_dependencies(spec, platform)
113
- return [] unless spec
114
- # Only allow endpoint specifications since they won't hit the network to
115
- # fetch the full gemspec when calling required_ruby_version
116
- return [] if !spec.is_a?(EndpointSpecification) && !spec.is_a?(Gem::Specification)
110
+ return [] unless spec && spec.is_a?(Gem::Specification)
117
111
  dependencies = []
118
112
  if !spec.required_ruby_version.nil? && !spec.required_ruby_version.none?
119
113
  dependencies << DepProxy.new(Gem::Dependency.new("Ruby\0", spec.required_ruby_version), platform)