bundler 2.2.17 → 2.2.22

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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +70 -0
  3. data/bundler.gemspec +2 -3
  4. data/lib/bundler.rb +2 -1
  5. data/lib/bundler/build_metadata.rb +2 -2
  6. data/lib/bundler/cli.rb +13 -33
  7. data/lib/bundler/cli/check.rb +4 -2
  8. data/lib/bundler/cli/doctor.rb +11 -1
  9. data/lib/bundler/cli/install.rb +7 -8
  10. data/lib/bundler/cli/lock.rb +5 -1
  11. data/lib/bundler/cli/outdated.rb +9 -10
  12. data/lib/bundler/cli/update.rb +8 -3
  13. data/lib/bundler/current_ruby.rb +4 -4
  14. data/lib/bundler/definition.rb +38 -127
  15. data/lib/bundler/dsl.rb +3 -11
  16. data/lib/bundler/feature_flag.rb +0 -3
  17. data/lib/bundler/fetcher/compact_index.rb +1 -1
  18. data/lib/bundler/fetcher/downloader.rb +1 -2
  19. data/lib/bundler/fetcher/index.rb +0 -1
  20. data/lib/bundler/friendly_errors.rb +2 -4
  21. data/lib/bundler/index.rb +1 -2
  22. data/lib/bundler/installer.rb +5 -12
  23. data/lib/bundler/lockfile_parser.rb +2 -20
  24. data/lib/bundler/man/bundle-add.1 +1 -1
  25. data/lib/bundler/man/bundle-binstubs.1 +1 -1
  26. data/lib/bundler/man/bundle-cache.1 +1 -1
  27. data/lib/bundler/man/bundle-check.1 +1 -1
  28. data/lib/bundler/man/bundle-clean.1 +1 -1
  29. data/lib/bundler/man/bundle-config.1 +1 -10
  30. data/lib/bundler/man/bundle-config.1.ronn +0 -11
  31. data/lib/bundler/man/bundle-doctor.1 +1 -1
  32. data/lib/bundler/man/bundle-exec.1 +1 -1
  33. data/lib/bundler/man/bundle-gem.1 +1 -1
  34. data/lib/bundler/man/bundle-info.1 +1 -1
  35. data/lib/bundler/man/bundle-init.1 +1 -1
  36. data/lib/bundler/man/bundle-inject.1 +1 -1
  37. data/lib/bundler/man/bundle-install.1 +1 -1
  38. data/lib/bundler/man/bundle-list.1 +1 -1
  39. data/lib/bundler/man/bundle-lock.1 +1 -1
  40. data/lib/bundler/man/bundle-open.1 +1 -1
  41. data/lib/bundler/man/bundle-outdated.1 +1 -1
  42. data/lib/bundler/man/bundle-platform.1 +1 -1
  43. data/lib/bundler/man/bundle-pristine.1 +1 -1
  44. data/lib/bundler/man/bundle-remove.1 +1 -1
  45. data/lib/bundler/man/bundle-show.1 +1 -1
  46. data/lib/bundler/man/bundle-update.1 +4 -4
  47. data/lib/bundler/man/bundle-update.1.ronn +3 -3
  48. data/lib/bundler/man/bundle-viz.1 +1 -1
  49. data/lib/bundler/man/bundle.1 +1 -1
  50. data/lib/bundler/man/gemfile.5 +1 -1
  51. data/lib/bundler/plugin/api/source.rb +14 -0
  52. data/lib/bundler/plugin/installer.rb +1 -1
  53. data/lib/bundler/resolver.rb +15 -96
  54. data/lib/bundler/resolver/spec_group.rb +0 -24
  55. data/lib/bundler/rubygems_ext.rb +2 -2
  56. data/lib/bundler/rubygems_integration.rb +4 -3
  57. data/lib/bundler/settings.rb +21 -4
  58. data/lib/bundler/source.rb +11 -0
  59. data/lib/bundler/source/rubygems.rb +22 -22
  60. data/lib/bundler/source/rubygems_aggregate.rb +64 -0
  61. data/lib/bundler/source_list.rb +69 -27
  62. data/lib/bundler/source_map.rb +58 -0
  63. data/lib/bundler/spec_set.rb +2 -6
  64. data/lib/bundler/templates/newgem/newgem.gemspec.tt +2 -2
  65. data/lib/bundler/version.rb +1 -1
  66. metadata +5 -3
@@ -526,13 +526,14 @@ module Bundler
526
526
  Bundler::Retry.new("download gem from #{uri}").attempts do
527
527
  fetcher.download(spec, uri, path)
528
528
  end
529
+ rescue Gem::RemoteFetcher::FetchError => e
530
+ raise Bundler::HTTPError, "Could not download gem from #{uri} due to underlying error <#{e.message}>"
529
531
  end
530
532
 
531
533
  def gem_remote_fetcher
532
- require "resolv"
534
+ require "rubygems/remote_fetcher"
533
535
  proxy = configuration[:http_proxy]
534
- dns = Resolv::DNS.new
535
- Gem::RemoteFetcher.new(proxy, dns)
536
+ Gem::RemoteFetcher.new(proxy)
536
537
  end
537
538
 
538
539
  def gem_from_path(path, policy = nil)
@@ -16,12 +16,10 @@ module Bundler
16
16
  clean
17
17
  default_install_uses_path
18
18
  deployment
19
- deployment_means_frozen
20
19
  disable_checksum_validation
21
20
  disable_exec_load
22
21
  disable_local_branch_check
23
22
  disable_local_revision_check
24
- disable_multisource
25
23
  disable_shared_gems
26
24
  disable_version_check
27
25
  force_ruby_platform
@@ -46,7 +44,6 @@ module Bundler
46
44
  silence_deprecations
47
45
  silence_root_warning
48
46
  suppress_install_using_messages
49
- unlock_source_unlocks_spec
50
47
  update_requires_all_flag
51
48
  use_gem_version_promoter_for_major_updates
52
49
  ].freeze
@@ -211,6 +208,13 @@ module Bundler
211
208
  locations
212
209
  end
213
210
 
211
+ def processor_count
212
+ require "etc"
213
+ Etc.nprocessors
214
+ rescue StandardError
215
+ 1
216
+ end
217
+
214
218
  # for legacy reasons, in Bundler 2, we do not respect :disable_shared_gems
215
219
  def path
216
220
  configs.each do |_level, settings|
@@ -443,7 +447,20 @@ module Bundler
443
447
  valid_file = file.exist? && !file.size.zero?
444
448
  return {} unless valid_file
445
449
  require_relative "yaml_serializer"
446
- YAMLSerializer.load file.read
450
+ YAMLSerializer.load(file.read).inject({}) do |config, (k, v)|
451
+ new_k = k
452
+
453
+ if k.include?("-")
454
+ Bundler.ui.warn "Your #{file} config includes `#{k}`, which contains the dash character (`-`).\n" \
455
+ "This is deprecated, because configuration through `ENV` should be possible, but `ENV` keys cannot include dashes.\n" \
456
+ "Please edit #{file} and replace any dashes in configuration keys with a triple underscore (`___`)."
457
+
458
+ new_k = k.gsub("-", "___")
459
+ end
460
+
461
+ config[new_k] = v
462
+ config
463
+ end
447
464
  end
448
465
  end
449
466
 
@@ -7,6 +7,7 @@ module Bundler
7
7
  autoload :Metadata, File.expand_path("source/metadata", __dir__)
8
8
  autoload :Path, File.expand_path("source/path", __dir__)
9
9
  autoload :Rubygems, File.expand_path("source/rubygems", __dir__)
10
+ autoload :RubygemsAggregate, File.expand_path("source/rubygems_aggregate", __dir__)
10
11
 
11
12
  attr_accessor :dependency_names
12
13
 
@@ -35,10 +36,16 @@ module Bundler
35
36
 
36
37
  def local!; end
37
38
 
39
+ def local_only!; end
40
+
38
41
  def cached!; end
39
42
 
40
43
  def remote!; end
41
44
 
45
+ def add_dependency_names(names)
46
+ @dependency_names = Array(dependency_names) | Array(names)
47
+ end
48
+
42
49
  # it's possible that gems from one source depend on gems from some
43
50
  # other source, so now we download gemspecs and iterate over those
44
51
  # dependencies, looking for gems we don't have info on yet.
@@ -48,6 +55,10 @@ module Bundler
48
55
  specs.dependency_names
49
56
  end
50
57
 
58
+ def spec_names
59
+ specs.spec_names
60
+ end
61
+
51
62
  def include?(other)
52
63
  other == self
53
64
  end
@@ -26,6 +26,12 @@ module Bundler
26
26
  Array(options["remotes"]).reverse_each {|r| add_remote(r) }
27
27
  end
28
28
 
29
+ def local_only!
30
+ @specs = nil
31
+ @allow_local = true
32
+ @allow_remote = false
33
+ end
34
+
29
35
  def local!
30
36
  return if @allow_local
31
37
 
@@ -61,13 +67,13 @@ module Bundler
61
67
  o.is_a?(Rubygems) && (o.credless_remotes - credless_remotes).empty?
62
68
  end
63
69
 
64
- def disable_multisource?
65
- @remotes.size <= 1
70
+ def multiple_remotes?
71
+ @remotes.size > 1
66
72
  end
67
73
 
68
74
  def can_lock?(spec)
69
- return super if disable_multisource?
70
- spec.source.is_a?(Rubygems)
75
+ return super unless multiple_remotes?
76
+ include?(spec.source)
71
77
  end
72
78
 
73
79
  def options
@@ -246,21 +252,16 @@ module Bundler
246
252
  other_remotes.map(&method(:remove_auth)) == @remotes.map(&method(:remove_auth))
247
253
  end
248
254
 
249
- def replace_remotes(other_remotes, allow_equivalent = false)
250
- return false if other_remotes == @remotes
251
-
252
- equivalent = allow_equivalent && equivalent_remotes?(other_remotes)
253
-
254
- @remotes = []
255
- other_remotes.reverse_each do |r|
256
- add_remote r.to_s
255
+ def spec_names
256
+ if @allow_remote && dependency_api_available?
257
+ remote_specs.spec_names
258
+ else
259
+ []
257
260
  end
258
-
259
- !equivalent
260
261
  end
261
262
 
262
263
  def unmet_deps
263
- if @allow_remote && api_fetchers.any?
264
+ if @allow_remote && dependency_api_available?
264
265
  remote_specs.unmet_dependency_names
265
266
  else
266
267
  []
@@ -276,7 +277,7 @@ module Bundler
276
277
 
277
278
  def double_check_for(unmet_dependency_names)
278
279
  return unless @allow_remote
279
- return unless api_fetchers.any?
280
+ return unless dependency_api_available?
280
281
 
281
282
  unmet_dependency_names = unmet_dependency_names.call
282
283
  unless unmet_dependency_names.nil?
@@ -298,17 +299,20 @@ module Bundler
298
299
  remote_specs.each do |spec|
299
300
  case spec
300
301
  when EndpointSpecification, Gem::Specification, StubSpecification, LazySpecification
301
- names.concat(spec.runtime_dependencies)
302
+ names.concat(spec.runtime_dependencies.map(&:name))
302
303
  when RemoteSpecification # from the full index
303
304
  return nil
304
305
  else
305
306
  raise "unhandled spec type (#{spec.inspect})"
306
307
  end
307
308
  end
308
- names.map!(&:name) if names
309
309
  names
310
310
  end
311
311
 
312
+ def dependency_api_available?
313
+ api_fetchers.any?
314
+ end
315
+
312
316
  protected
313
317
 
314
318
  def credless_remotes
@@ -387,10 +391,6 @@ module Bundler
387
391
  next if gemfile =~ /^bundler\-[\d\.]+?\.gem/
388
392
  s ||= Bundler.rubygems.spec_from_gem(gemfile)
389
393
  s.source = self
390
- if Bundler.rubygems.spec_missing_extensions?(s, false)
391
- Bundler.ui.debug "Source #{self} is ignoring #{s} because it is missing extensions"
392
- next
393
- end
394
394
  idx << s
395
395
  end
396
396
 
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bundler
4
+ class Source
5
+ class RubygemsAggregate
6
+ attr_reader :source_map, :sources
7
+
8
+ def initialize(sources, source_map)
9
+ @sources = sources
10
+ @source_map = source_map
11
+
12
+ @index = build_index
13
+ end
14
+
15
+ def specs
16
+ @index
17
+ end
18
+
19
+ def to_s
20
+ "any of the sources"
21
+ end
22
+
23
+ private
24
+
25
+ def build_index
26
+ Index.build do |idx|
27
+ dependency_names = source_map.pinned_spec_names
28
+
29
+ sources.all_sources.each do |source|
30
+ source.dependency_names = dependency_names - source_map.pinned_spec_names(source)
31
+ idx.add_source source.specs
32
+ dependency_names.concat(source.unmet_deps).uniq!
33
+ end
34
+
35
+ double_check_for_index(idx, dependency_names)
36
+ end
37
+ end
38
+
39
+ # Suppose the gem Foo depends on the gem Bar. Foo exists in Source A. Bar has some versions that exist in both
40
+ # sources A and B. At this point, the API request will have found all the versions of Bar in source A,
41
+ # but will not have found any versions of Bar from source B, which is a problem if the requested version
42
+ # of Foo specifically depends on a version of Bar that is only found in source B. This ensures that for
43
+ # each spec we found, we add all possible versions from all sources to the index.
44
+ def double_check_for_index(idx, dependency_names)
45
+ pinned_names = source_map.pinned_spec_names
46
+
47
+ names = :names # do this so we only have to traverse to get dependency_names from the index once
48
+ unmet_dependency_names = lambda do
49
+ return names unless names == :names
50
+ new_names = sources.all_sources.map(&:dependency_names_to_double_check)
51
+ return names = nil if new_names.compact!
52
+ names = new_names.flatten(1).concat(dependency_names)
53
+ names.uniq!
54
+ names -= pinned_names
55
+ names
56
+ end
57
+
58
+ sources.all_sources.each do |source|
59
+ source.double_check_for(unmet_dependency_names)
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -21,15 +21,20 @@ module Bundler
21
21
  @rubygems_sources = []
22
22
  @metadata_source = Source::Metadata.new
23
23
 
24
- @disable_multisource = true
24
+ @merged_gem_lockfile_sections = false
25
25
  end
26
26
 
27
- def disable_multisource?
28
- @disable_multisource
27
+ def merged_gem_lockfile_sections?
28
+ @merged_gem_lockfile_sections
29
29
  end
30
30
 
31
- def merged_gem_lockfile_sections!
32
- @disable_multisource = false
31
+ def merged_gem_lockfile_sections!(replacement_source)
32
+ @merged_gem_lockfile_sections = true
33
+ @global_rubygems_source = replacement_source
34
+ end
35
+
36
+ def aggregate_global_source?
37
+ global_rubygems_source.multiple_remotes?
33
38
  end
34
39
 
35
40
  def add_path_source(options = {})
@@ -49,18 +54,17 @@ module Bundler
49
54
  end
50
55
 
51
56
  def add_rubygems_source(options = {})
52
- add_source_to_list Source::Rubygems.new(options), @rubygems_sources
57
+ new_source = Source::Rubygems.new(options)
58
+ return @global_rubygems_source if @global_rubygems_source == new_source
59
+
60
+ add_source_to_list new_source, @rubygems_sources
53
61
  end
54
62
 
55
63
  def add_plugin_source(source, options = {})
56
64
  add_source_to_list Plugin.source(source).new(options), @plugin_sources
57
65
  end
58
66
 
59
- def global_rubygems_source=(uri)
60
- @global_rubygems_source ||= rubygems_aggregate_class.new("remotes" => uri, "allow_local" => true)
61
- end
62
-
63
- def add_rubygems_remote(uri)
67
+ def add_global_rubygems_remote(uri)
64
68
  global_rubygems_source.add_remote(uri)
65
69
  global_rubygems_source
66
70
  end
@@ -70,7 +74,11 @@ module Bundler
70
74
  end
71
75
 
72
76
  def rubygems_sources
73
- @rubygems_sources + [global_rubygems_source]
77
+ non_global_rubygems_sources + [global_rubygems_source]
78
+ end
79
+
80
+ def non_global_rubygems_sources
81
+ @rubygems_sources
74
82
  end
75
83
 
76
84
  def rubygems_remotes
@@ -81,36 +89,50 @@ module Bundler
81
89
  path_sources + git_sources + plugin_sources + rubygems_sources + [metadata_source]
82
90
  end
83
91
 
92
+ def non_default_explicit_sources
93
+ all_sources - [default_source, metadata_source]
94
+ end
95
+
84
96
  def get(source)
85
97
  source_list_for(source).find {|s| equal_source?(source, s) || equivalent_source?(source, s) }
86
98
  end
87
99
 
88
100
  def lock_sources
89
- lock_sources = (path_sources + git_sources + plugin_sources).sort_by(&:to_s)
90
- if disable_multisource?
91
- lock_sources + rubygems_sources.sort_by(&:to_s).uniq
101
+ lock_other_sources + lock_rubygems_sources
102
+ end
103
+
104
+ def lock_other_sources
105
+ (path_sources + git_sources + plugin_sources).sort_by(&:to_s)
106
+ end
107
+
108
+ def lock_rubygems_sources
109
+ if merged_gem_lockfile_sections?
110
+ [combine_rubygems_sources]
92
111
  else
93
- lock_sources << combine_rubygems_sources
112
+ rubygems_sources.sort_by(&:to_s)
94
113
  end
95
114
  end
96
115
 
97
116
  # Returns true if there are changes
98
117
  def replace_sources!(replacement_sources)
99
- return true if replacement_sources.empty?
118
+ return false if replacement_sources.empty?
100
119
 
101
- [path_sources, git_sources, plugin_sources].each do |source_list|
102
- source_list.map! do |source|
103
- replacement_sources.find {|s| s == source } || source
104
- end
105
- end
120
+ @path_sources, @git_sources, @plugin_sources = map_sources(replacement_sources)
106
121
 
107
- replacement_rubygems = !disable_multisource? &&
108
- replacement_sources.detect {|s| s.is_a?(Source::Rubygems) }
109
- @global_rubygems_source = replacement_rubygems if replacement_rubygems
122
+ different_sources?(lock_sources, replacement_sources)
123
+ end
124
+
125
+ # Returns true if there are changes
126
+ def expired_sources?(replacement_sources)
127
+ return false if replacement_sources.empty?
128
+
129
+ lock_sources = dup_with_replaced_sources(replacement_sources).lock_sources
110
130
 
111
- return true if !equal_sources?(lock_sources, replacement_sources) && !equivalent_sources?(lock_sources, replacement_sources)
131
+ different_sources?(lock_sources, replacement_sources)
132
+ end
112
133
 
113
- false
134
+ def local_only!
135
+ all_sources.each(&:local_only!)
114
136
  end
115
137
 
116
138
  def cached!
@@ -123,6 +145,24 @@ module Bundler
123
145
 
124
146
  private
125
147
 
148
+ def dup_with_replaced_sources(replacement_sources)
149
+ new_source_list = dup
150
+ new_source_list.replace_sources!(replacement_sources)
151
+ new_source_list
152
+ end
153
+
154
+ def map_sources(replacement_sources)
155
+ [path_sources, git_sources, plugin_sources].map do |sources|
156
+ sources.map do |source|
157
+ replacement_sources.find {|s| s == source } || source
158
+ end
159
+ end
160
+ end
161
+
162
+ def different_sources?(lock_sources, replacement_sources)
163
+ !equal_sources?(lock_sources, replacement_sources) && !equivalent_sources?(lock_sources, replacement_sources)
164
+ end
165
+
126
166
  def rubygems_aggregate_class
127
167
  Source::Rubygems
128
168
  end
@@ -162,6 +202,8 @@ module Bundler
162
202
  end
163
203
 
164
204
  def equal_source?(source, other_source)
205
+ return source.include?(other_source) if source.is_a?(Source::Rubygems) && other_source.is_a?(Source::Rubygems) && !merged_gem_lockfile_sections?
206
+
165
207
  source == other_source
166
208
  end
167
209
 
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bundler
4
+ class SourceMap
5
+ attr_reader :sources, :dependencies
6
+
7
+ def initialize(sources, dependencies)
8
+ @sources = sources
9
+ @dependencies = dependencies
10
+ end
11
+
12
+ def pinned_spec_names(skip = nil)
13
+ direct_requirements.reject {|_, source| source == skip }.keys
14
+ end
15
+
16
+ def all_requirements
17
+ requirements = direct_requirements.dup
18
+
19
+ unmet_deps = sources.non_default_explicit_sources.map do |source|
20
+ (source.spec_names - pinned_spec_names).each do |indirect_dependency_name|
21
+ previous_source = requirements[indirect_dependency_name]
22
+ if previous_source.nil?
23
+ requirements[indirect_dependency_name] = source
24
+ else
25
+ no_ambiguous_sources = Bundler.feature_flag.bundler_3_mode?
26
+
27
+ msg = ["The gem '#{indirect_dependency_name}' was found in multiple relevant sources."]
28
+ msg.concat [previous_source, source].map {|s| " * #{s}" }.sort
29
+ msg << "You #{no_ambiguous_sources ? :must : :should} add this gem to the source block for the source you wish it to be installed from."
30
+ msg = msg.join("\n")
31
+
32
+ raise SecurityError, msg if no_ambiguous_sources
33
+ Bundler.ui.warn "Warning: #{msg}"
34
+ end
35
+ end
36
+
37
+ source.unmet_deps
38
+ end
39
+
40
+ sources.default_source.add_dependency_names(unmet_deps.flatten - requirements.keys)
41
+
42
+ requirements
43
+ end
44
+
45
+ def direct_requirements
46
+ @direct_requirements ||= begin
47
+ requirements = {}
48
+ default = sources.default_source
49
+ dependencies.each do |dep|
50
+ dep_source = dep.source || default
51
+ dep_source.add_dependency_names(dep.name)
52
+ requirements[dep.name] = dep_source
53
+ end
54
+ requirements
55
+ end
56
+ end
57
+ end
58
+ end