bundler 2.2.11 → 2.3.6

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 (174) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +414 -5
  3. data/README.md +1 -1
  4. data/bundler.gemspec +2 -3
  5. data/exe/bundle +7 -8
  6. data/lib/bundler/.document +1 -0
  7. data/lib/bundler/build_metadata.rb +2 -2
  8. data/lib/bundler/cli/cache.rb +1 -1
  9. data/lib/bundler/cli/check.rb +4 -2
  10. data/lib/bundler/cli/common.rb +15 -2
  11. data/lib/bundler/cli/doctor.rb +24 -5
  12. data/lib/bundler/cli/exec.rb +1 -6
  13. data/lib/bundler/cli/gem.rb +130 -26
  14. data/lib/bundler/cli/info.rb +16 -4
  15. data/lib/bundler/cli/install.rb +12 -27
  16. data/lib/bundler/cli/issue.rb +4 -3
  17. data/lib/bundler/cli/list.rb +7 -1
  18. data/lib/bundler/cli/lock.rb +5 -1
  19. data/lib/bundler/cli/open.rb +1 -2
  20. data/lib/bundler/cli/outdated.rb +10 -11
  21. data/lib/bundler/cli/platform.rb +1 -1
  22. data/lib/bundler/cli/remove.rb +1 -2
  23. data/lib/bundler/cli/update.rb +17 -8
  24. data/lib/bundler/cli.rb +41 -55
  25. data/lib/bundler/compact_index_client/cache.rb +0 -9
  26. data/lib/bundler/compact_index_client/updater.rb +10 -11
  27. data/lib/bundler/compact_index_client.rb +2 -8
  28. data/lib/bundler/current_ruby.rb +5 -4
  29. data/lib/bundler/definition.rb +147 -290
  30. data/lib/bundler/dependency.rb +5 -7
  31. data/lib/bundler/digest.rb +71 -0
  32. data/lib/bundler/dsl.rb +67 -66
  33. data/lib/bundler/endpoint_specification.rb +21 -11
  34. data/lib/bundler/env.rb +1 -1
  35. data/lib/bundler/environment_preserver.rb +4 -1
  36. data/lib/bundler/errors.rb +19 -3
  37. data/lib/bundler/feature_flag.rb +0 -4
  38. data/lib/bundler/fetcher/compact_index.rb +10 -15
  39. data/lib/bundler/fetcher/downloader.rb +9 -6
  40. data/lib/bundler/fetcher/index.rb +0 -27
  41. data/lib/bundler/fetcher.rb +10 -16
  42. data/lib/bundler/friendly_errors.rb +5 -32
  43. data/lib/bundler/gem_helper.rb +21 -16
  44. data/lib/bundler/index.rb +2 -7
  45. data/lib/bundler/injector.rb +12 -3
  46. data/lib/bundler/inline.rb +2 -1
  47. data/lib/bundler/installer/gem_installer.rb +4 -22
  48. data/lib/bundler/installer/parallel_installer.rb +36 -15
  49. data/lib/bundler/installer/standalone.rb +14 -9
  50. data/lib/bundler/installer.rb +8 -17
  51. data/lib/bundler/lazy_specification.rb +23 -2
  52. data/lib/bundler/lockfile_generator.rb +1 -1
  53. data/lib/bundler/lockfile_parser.rb +16 -45
  54. data/lib/bundler/man/bundle-add.1 +10 -2
  55. data/lib/bundler/man/bundle-add.1.ronn +7 -1
  56. data/lib/bundler/man/bundle-binstubs.1 +1 -1
  57. data/lib/bundler/man/bundle-cache.1 +1 -1
  58. data/lib/bundler/man/bundle-check.1 +1 -1
  59. data/lib/bundler/man/bundle-clean.1 +1 -1
  60. data/lib/bundler/man/bundle-config.1 +23 -15
  61. data/lib/bundler/man/bundle-config.1.ronn +24 -17
  62. data/lib/bundler/man/bundle-doctor.1 +1 -1
  63. data/lib/bundler/man/bundle-exec.1 +1 -1
  64. data/lib/bundler/man/bundle-gem.1 +14 -1
  65. data/lib/bundler/man/bundle-gem.1.ronn +16 -0
  66. data/lib/bundler/man/bundle-info.1 +1 -1
  67. data/lib/bundler/man/bundle-init.1 +1 -1
  68. data/lib/bundler/man/bundle-inject.1 +1 -1
  69. data/lib/bundler/man/bundle-install.1 +2 -2
  70. data/lib/bundler/man/bundle-install.1.ronn +2 -2
  71. data/lib/bundler/man/bundle-list.1 +1 -1
  72. data/lib/bundler/man/bundle-lock.1 +1 -1
  73. data/lib/bundler/man/bundle-open.1 +1 -1
  74. data/lib/bundler/man/bundle-outdated.1 +1 -1
  75. data/lib/bundler/man/bundle-platform.1 +1 -1
  76. data/lib/bundler/man/bundle-pristine.1 +1 -1
  77. data/lib/bundler/man/bundle-remove.1 +1 -1
  78. data/lib/bundler/man/bundle-show.1 +1 -1
  79. data/lib/bundler/man/bundle-update.1 +5 -5
  80. data/lib/bundler/man/bundle-update.1.ronn +5 -4
  81. data/lib/bundler/man/bundle-viz.1 +1 -1
  82. data/lib/bundler/man/bundle.1 +1 -1
  83. data/lib/bundler/man/gemfile.5 +28 -2
  84. data/lib/bundler/man/gemfile.5.ronn +9 -1
  85. data/lib/bundler/plugin/api/source.rb +22 -0
  86. data/lib/bundler/plugin/index.rb +4 -1
  87. data/lib/bundler/plugin/installer.rb +10 -10
  88. data/lib/bundler/plugin/source_list.rb +4 -0
  89. data/lib/bundler/plugin.rb +28 -8
  90. data/lib/bundler/process_lock.rb +1 -1
  91. data/lib/bundler/psyched_yaml.rb +1 -13
  92. data/lib/bundler/remote_specification.rb +7 -0
  93. data/lib/bundler/resolver/spec_group.rb +1 -25
  94. data/lib/bundler/resolver.rb +55 -147
  95. data/lib/bundler/retry.rb +1 -1
  96. data/lib/bundler/ruby_version.rb +1 -1
  97. data/lib/bundler/rubygems_ext.rb +30 -8
  98. data/lib/bundler/rubygems_gem_installer.rb +68 -1
  99. data/lib/bundler/rubygems_integration.rb +43 -60
  100. data/lib/bundler/runtime.rb +18 -11
  101. data/lib/bundler/self_manager.rb +168 -0
  102. data/lib/bundler/settings.rb +96 -20
  103. data/lib/bundler/setup.rb +2 -2
  104. data/lib/bundler/shared_helpers.rb +4 -19
  105. data/lib/bundler/source/git/git_proxy.rb +8 -6
  106. data/lib/bundler/source/git.rb +22 -4
  107. data/lib/bundler/source/metadata.rb +1 -5
  108. data/lib/bundler/source/path/installer.rb +1 -1
  109. data/lib/bundler/source/path.rb +3 -1
  110. data/lib/bundler/source/rubygems.rb +111 -106
  111. data/lib/bundler/source/rubygems_aggregate.rb +68 -0
  112. data/lib/bundler/source.rb +21 -0
  113. data/lib/bundler/source_list.rb +100 -60
  114. data/lib/bundler/source_map.rb +58 -0
  115. data/lib/bundler/spec_set.rb +17 -31
  116. data/lib/bundler/stub_specification.rb +8 -0
  117. data/lib/bundler/templates/Executable.bundler +7 -7
  118. data/lib/bundler/templates/Gemfile +0 -2
  119. data/lib/bundler/templates/gems.rb +0 -3
  120. data/lib/bundler/templates/newgem/Gemfile.tt +5 -2
  121. data/lib/bundler/templates/newgem/README.md.tt +5 -3
  122. data/lib/bundler/templates/newgem/Rakefile.tt +15 -2
  123. data/lib/bundler/templates/newgem/github/workflows/main.yml.tt +15 -6
  124. data/lib/bundler/templates/newgem/newgem.gemspec.tt +18 -16
  125. data/lib/bundler/templates/newgem/sig/newgem.rbs.tt +8 -0
  126. data/lib/bundler/templates/newgem/standard.yml.tt +3 -0
  127. data/lib/bundler/templates/newgem/test/minitest/{newgem_test.rb.tt → test_newgem.rb.tt} +1 -1
  128. data/lib/bundler/ui/shell.rb +1 -1
  129. data/lib/bundler/vendor/.document +1 -0
  130. data/lib/bundler/vendor/connection_pool/LICENSE +20 -0
  131. data/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb +19 -21
  132. data/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb +1 -1
  133. data/lib/bundler/vendor/connection_pool/lib/connection_pool/wrapper.rb +57 -0
  134. data/lib/bundler/vendor/connection_pool/lib/connection_pool.rb +39 -74
  135. data/lib/bundler/vendor/fileutils/LICENSE.txt +22 -0
  136. data/lib/bundler/vendor/molinillo/LICENSE +9 -0
  137. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb +2 -2
  138. data/lib/bundler/vendor/molinillo/lib/molinillo/modules/specification_provider.rb +1 -1
  139. data/lib/bundler/vendor/net-http-persistent/README.rdoc +82 -0
  140. data/lib/bundler/vendor/thor/LICENSE.md +20 -0
  141. data/lib/bundler/vendor/thor/lib/thor/actions/file_manipulation.rb +5 -5
  142. data/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb +1 -2
  143. data/lib/bundler/vendor/thor/lib/thor/actions.rb +6 -2
  144. data/lib/bundler/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb +6 -0
  145. data/lib/bundler/vendor/thor/lib/thor/error.rb +9 -4
  146. data/lib/bundler/vendor/thor/lib/thor/parser/options.rb +19 -1
  147. data/lib/bundler/vendor/thor/lib/thor/shell/basic.rb +22 -4
  148. data/lib/bundler/vendor/thor/lib/thor/shell.rb +1 -1
  149. data/lib/bundler/vendor/thor/lib/thor/util.rb +1 -1
  150. data/lib/bundler/vendor/thor/lib/thor/version.rb +1 -1
  151. data/lib/bundler/vendor/tmpdir/lib/tmpdir.rb +1 -1
  152. data/lib/bundler/vendor/tsort/LICENSE.txt +22 -0
  153. data/lib/bundler/vendor/tsort/lib/tsort.rb +453 -0
  154. data/lib/bundler/vendor/uri/LICENSE.txt +22 -0
  155. data/lib/bundler/vendor/uri/lib/uri/common.rb +17 -80
  156. data/lib/bundler/vendor/uri/lib/uri/ftp.rb +0 -1
  157. data/lib/bundler/vendor/uri/lib/uri/generic.rb +5 -6
  158. data/lib/bundler/vendor/uri/lib/uri/http.rb +0 -1
  159. data/lib/bundler/vendor/uri/lib/uri/https.rb +0 -1
  160. data/lib/bundler/vendor/uri/lib/uri/ldap.rb +1 -1
  161. data/lib/bundler/vendor/uri/lib/uri/mailto.rb +0 -1
  162. data/lib/bundler/vendor/uri/lib/uri/rfc2396_parser.rb +1 -14
  163. data/lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb +1 -12
  164. data/lib/bundler/vendor/uri/lib/uri/version.rb +1 -1
  165. data/lib/bundler/vendor/uri/lib/uri/ws.rb +84 -0
  166. data/lib/bundler/vendor/uri/lib/uri/wss.rb +22 -0
  167. data/lib/bundler/vendor/uri/lib/uri.rb +0 -1
  168. data/lib/bundler/vendored_tsort.rb +4 -0
  169. data/lib/bundler/version.rb +1 -1
  170. data/lib/bundler/worker.rb +19 -4
  171. data/lib/bundler.rb +28 -31
  172. metadata +27 -9
  173. data/lib/bundler/gemdeps.rb +0 -29
  174. data/lib/bundler/vendor/connection_pool/lib/connection_pool/monotonic_time.rb +0 -66
@@ -6,6 +6,11 @@ module Bundler
6
6
  class Definition
7
7
  include GemHelpers
8
8
 
9
+ class << self
10
+ # Do not create or modify a lockfile (Makes #lock a noop)
11
+ attr_accessor :no_lock
12
+ end
13
+
9
14
  attr_reader(
10
15
  :dependencies,
11
16
  :locked_deps,
@@ -56,10 +61,8 @@ module Bundler
56
61
  @unlocking_bundler = false
57
62
  @unlocking = unlock
58
63
  else
59
- unlock = unlock.dup
60
64
  @unlocking_bundler = unlock.delete(:bundler)
61
- unlock.delete_if {|_k, v| Array(v).empty? }
62
- @unlocking = !unlock.empty?
65
+ @unlocking = unlock.any? {|_k, v| !Array(v).empty? }
63
66
  end
64
67
 
65
68
  @dependencies = dependencies
@@ -75,7 +78,6 @@ module Bundler
75
78
  @lockfile_contents = String.new
76
79
  @locked_bundler_version = nil
77
80
  @locked_ruby_version = nil
78
- @locked_specs_incomplete_for_platform = false
79
81
  @new_platform = nil
80
82
 
81
83
  if lockfile && File.exist?(lockfile)
@@ -106,7 +108,19 @@ module Bundler
106
108
  @locked_platforms = []
107
109
  end
108
110
 
109
- @unlock[:gems] ||= []
111
+ locked_gem_sources = @locked_sources.select {|s| s.is_a?(Source::Rubygems) }
112
+ @multisource_allowed = locked_gem_sources.size == 1 && locked_gem_sources.first.multiple_remotes? && Bundler.frozen_bundle?
113
+
114
+ if @multisource_allowed
115
+ unless sources.aggregate_global_source?
116
+ msg = "Your lockfile contains a single rubygems source section with multiple remotes, which is insecure. Make sure you run `bundle install` in non frozen mode and commit the result to make your lockfile secure."
117
+
118
+ Bundler::SharedHelpers.major_deprecation 2, msg
119
+ end
120
+
121
+ @sources.merged_gem_lockfile_sections!(locked_gem_sources.first)
122
+ end
123
+
110
124
  @unlock[:sources] ||= []
111
125
  @unlock[:ruby] ||= if @ruby_version && locked_ruby_version_object
112
126
  @ruby_version.diff(locked_ruby_version_object)
@@ -119,14 +133,18 @@ module Bundler
119
133
  @path_changes = converge_paths
120
134
  @source_changes = converge_sources
121
135
 
122
- unless @unlock[:lock_shared_dependencies]
123
- eager_unlock = expand_dependencies(@unlock[:gems], true)
124
- @unlock[:gems] = @locked_specs.for(eager_unlock, [], false, false, false).map(&:name)
136
+ if @unlock[:conservative]
137
+ @unlock[:gems] ||= @dependencies.map(&:name)
138
+ else
139
+ eager_unlock = expand_dependencies(@unlock[:gems] || [], true)
140
+ @unlock[:gems] = @locked_specs.for(eager_unlock, false, false).map(&:name)
125
141
  end
126
142
 
127
143
  @dependency_changes = converge_dependencies
128
144
  @local_changes = converge_locals
129
145
 
146
+ @locked_specs_incomplete_for_platform = !@locked_specs.for(requested_dependencies & expand_dependencies(locked_dependencies), true, true)
147
+
130
148
  @requires = compute_requires
131
149
  end
132
150
 
@@ -145,17 +163,21 @@ module Bundler
145
163
  end
146
164
  end
147
165
 
166
+ def resolve_only_locally!
167
+ @remote = false
168
+ sources.local_only!
169
+ resolve
170
+ end
171
+
148
172
  def resolve_with_cache!
149
- raise "Specs already loaded" if @specs
150
173
  sources.cached!
151
- specs
174
+ resolve
152
175
  end
153
176
 
154
177
  def resolve_remotely!
155
- return if @specs
156
178
  @remote = true
157
179
  sources.remote!
158
- specs
180
+ resolve
159
181
  end
160
182
 
161
183
  # For given dependency list returns a SpecSet with Gemspec of all the required
@@ -165,25 +187,7 @@ module Bundler
165
187
  #
166
188
  # @return [Bundler::SpecSet]
167
189
  def specs
168
- @specs ||= begin
169
- begin
170
- specs = resolve.materialize(requested_dependencies)
171
- rescue GemNotFound => e # Handle yanked gem
172
- gem_name, gem_version = extract_gem_info(e)
173
- locked_gem = @locked_specs[gem_name].last
174
- raise if locked_gem.nil? || locked_gem.version.to_s != gem_version || !@remote
175
- raise GemNotFound, "Your bundle is locked to #{locked_gem}, but that version could not " \
176
- "be found in any of the sources listed in your Gemfile. If you haven't changed sources, " \
177
- "that means the author of #{locked_gem} has removed it. You'll need to update your bundle " \
178
- "to a version other than #{locked_gem} that hasn't been removed in order to install."
179
- end
180
- unless specs["bundler"].any?
181
- bundler = sources.metadata_source.specs.search(Gem::Dependency.new("bundler", VERSION)).last
182
- specs["bundler"] = bundler
183
- end
184
-
185
- specs
186
- end
190
+ @specs ||= materialize(requested_dependencies)
187
191
  end
188
192
 
189
193
  def new_specs
@@ -195,9 +199,7 @@ module Bundler
195
199
  end
196
200
 
197
201
  def missing_specs
198
- missing = []
199
- resolve.materialize(requested_dependencies, missing)
200
- missing
202
+ resolve.materialize(requested_dependencies).missing_specs
201
203
  end
202
204
 
203
205
  def missing_specs?
@@ -206,7 +208,6 @@ module Bundler
206
208
  Bundler.ui.debug "The definition is missing #{missing.map(&:full_name)}"
207
209
  true
208
210
  rescue BundlerError => e
209
- @index = nil
210
211
  @resolve = nil
211
212
  @specs = nil
212
213
  @gem_version_promoter = nil
@@ -216,17 +217,11 @@ module Bundler
216
217
  end
217
218
 
218
219
  def requested_specs
219
- @requested_specs ||= begin
220
- groups = requested_groups
221
- groups.map!(&:to_sym)
222
- specs_for(groups)
223
- end
220
+ specs_for(requested_groups)
224
221
  end
225
222
 
226
223
  def requested_dependencies
227
- groups = requested_groups
228
- groups.map!(&:to_sym)
229
- dependencies_for(groups)
224
+ dependencies_for(requested_groups)
230
225
  end
231
226
 
232
227
  def current_dependencies
@@ -235,15 +230,22 @@ module Bundler
235
230
  end
236
231
  end
237
232
 
233
+ def locked_dependencies
234
+ @locked_deps.values
235
+ end
236
+
238
237
  def specs_for(groups)
238
+ return specs if groups.empty?
239
239
  deps = dependencies_for(groups)
240
- specs.for(expand_dependencies(deps))
240
+ materialize(deps)
241
241
  end
242
242
 
243
243
  def dependencies_for(groups)
244
- current_dependencies.reject do |d|
244
+ groups.map!(&:to_sym)
245
+ deps = current_dependencies.reject do |d|
245
246
  (d.groups & groups).empty?
246
247
  end
248
+ expand_dependencies(deps)
247
249
  end
248
250
 
249
251
  # Resolve all the dependencies specified in Gemfile. It ensures that
@@ -263,60 +265,12 @@ module Bundler
263
265
  else
264
266
  # Run a resolve against the locally available gems
265
267
  Bundler.ui.debug("Found changes from the lockfile, re-resolving dependencies because #{change_reason}")
266
- expanded_dependencies = expand_dependencies(dependencies + metadata_dependencies, @remote)
267
- Resolver.resolve(expanded_dependencies, index, source_requirements, last_resolve, gem_version_promoter, additional_base_requirements_for_resolve, platforms)
268
+ expanded_dependencies = expand_dependencies(dependencies + metadata_dependencies, true)
269
+ Resolver.resolve(expanded_dependencies, source_requirements, last_resolve, gem_version_promoter, additional_base_requirements_for_resolve, platforms)
268
270
  end
269
271
  end
270
272
  end
271
273
 
272
- def index
273
- @index ||= Index.build do |idx|
274
- dependency_names = @dependencies.map(&:name)
275
-
276
- sources.all_sources.each do |source|
277
- source.dependency_names = dependency_names - pinned_spec_names(source)
278
- idx.add_source source.specs
279
- dependency_names.concat(source.unmet_deps).uniq!
280
- end
281
-
282
- double_check_for_index(idx, dependency_names)
283
- end
284
- end
285
-
286
- # Suppose the gem Foo depends on the gem Bar. Foo exists in Source A. Bar has some versions that exist in both
287
- # sources A and B. At this point, the API request will have found all the versions of Bar in source A,
288
- # but will not have found any versions of Bar from source B, which is a problem if the requested version
289
- # of Foo specifically depends on a version of Bar that is only found in source B. This ensures that for
290
- # each spec we found, we add all possible versions from all sources to the index.
291
- def double_check_for_index(idx, dependency_names)
292
- pinned_names = pinned_spec_names
293
- loop do
294
- idxcount = idx.size
295
-
296
- names = :names # do this so we only have to traverse to get dependency_names from the index once
297
- unmet_dependency_names = lambda do
298
- return names unless names == :names
299
- new_names = sources.all_sources.map(&:dependency_names_to_double_check)
300
- return names = nil if new_names.compact!
301
- names = new_names.flatten(1).concat(dependency_names)
302
- names.uniq!
303
- names -= pinned_names
304
- names
305
- end
306
-
307
- sources.all_sources.each do |source|
308
- source.double_check_for(unmet_dependency_names)
309
- end
310
-
311
- break if idxcount == idx.size
312
- end
313
- end
314
- private :double_check_for_index
315
-
316
- def has_rubygems_remotes?
317
- sources.rubygems_sources.any? {|s| s.remotes.any? }
318
- end
319
-
320
274
  def spec_git_paths
321
275
  sources.git_sources.map {|s| File.realpath(s.path) if File.exist?(s.path) }.compact
322
276
  end
@@ -326,6 +280,8 @@ module Bundler
326
280
  end
327
281
 
328
282
  def lock(file, preserve_unknown_sections = false)
283
+ return if Definition.no_lock
284
+
329
285
  contents = to_lock
330
286
 
331
287
  # Convert to \r\n if the existing lock has them
@@ -336,10 +292,7 @@ module Bundler
336
292
  locked_major = @locked_bundler_version.segments.first
337
293
  current_major = Gem::Version.create(Bundler::VERSION).segments.first
338
294
 
339
- if updating_major = locked_major < current_major
340
- Bundler.ui.warn "Warning: the lockfile is being updated to Bundler #{current_major}, " \
341
- "after which you will be unable to return to Bundler #{@locked_bundler_version.segments.first}."
342
- end
295
+ updating_major = locked_major < current_major
343
296
  end
344
297
 
345
298
  preserve_unknown_sections ||= !updating_major && (Bundler.frozen_bundle? || !(unlocking? || @unlocking_bundler))
@@ -356,14 +309,6 @@ module Bundler
356
309
  end
357
310
  end
358
311
 
359
- def locked_bundler_version
360
- if @locked_bundler_version && @locked_bundler_version < Gem::Version.new(Bundler::VERSION)
361
- new_version = Bundler::VERSION
362
- end
363
-
364
- new_version || @locked_bundler_version || Bundler::VERSION
365
- end
366
-
367
312
  def locked_ruby_version
368
313
  return unless ruby_version
369
314
  if @unlock[:ruby] || !@locked_ruby_version
@@ -415,44 +360,31 @@ module Bundler
415
360
  added.concat new_platforms.map {|p| "* platform: #{p}" }
416
361
  deleted.concat deleted_platforms.map {|p| "* platform: #{p}" }
417
362
 
418
- gemfile_sources = sources.lock_sources
419
-
420
- new_sources = gemfile_sources - @locked_sources
421
- deleted_sources = @locked_sources - gemfile_sources
363
+ new_deps = @dependencies - locked_dependencies
364
+ deleted_deps = locked_dependencies - @dependencies
422
365
 
423
- new_deps = @dependencies - @locked_deps.values
424
- deleted_deps = @locked_deps.values - @dependencies
366
+ added.concat new_deps.map {|d| "* #{pretty_dep(d)}" } if new_deps.any?
367
+ deleted.concat deleted_deps.map {|d| "* #{pretty_dep(d)}" } if deleted_deps.any?
425
368
 
426
- # Check if it is possible that the source is only changed thing
427
- if (new_deps.empty? && deleted_deps.empty?) && (!new_sources.empty? && !deleted_sources.empty?)
428
- new_sources.reject! {|source| (source.path? && source.path.exist?) || equivalent_rubygems_remotes?(source) }
429
- deleted_sources.reject! {|source| (source.path? && source.path.exist?) || equivalent_rubygems_remotes?(source) }
430
- end
369
+ both_sources = Hash.new {|h, k| h[k] = [] }
370
+ @dependencies.each {|d| both_sources[d.name][0] = d }
431
371
 
432
- if @locked_sources != gemfile_sources
433
- if new_sources.any?
434
- added.concat new_sources.map {|source| "* source: #{source}" }
435
- end
372
+ locked_dependencies.each do |d|
373
+ next if !Bundler.feature_flag.bundler_3_mode? && @locked_specs[d.name].empty?
436
374
 
437
- if deleted_sources.any?
438
- deleted.concat deleted_sources.map {|source| "* source: #{source}" }
439
- end
375
+ both_sources[d.name][1] = d
440
376
  end
441
377
 
442
- added.concat new_deps.map {|d| "* #{pretty_dep(d)}" } if new_deps.any?
443
- if deleted_deps.any?
444
- deleted.concat deleted_deps.map {|d| "* #{pretty_dep(d)}" }
445
- end
378
+ both_sources.each do |name, (dep, lock_dep)|
379
+ next if dep.nil? || lock_dep.nil?
446
380
 
447
- both_sources = Hash.new {|h, k| h[k] = [] }
448
- @dependencies.each {|d| both_sources[d.name][0] = d }
449
- @locked_deps.each {|name, d| both_sources[name][1] = d.source }
381
+ gemfile_source = dep.source || sources.default_source
382
+ lock_source = lock_dep.source || sources.default_source
383
+ next if lock_source.include?(gemfile_source)
450
384
 
451
- both_sources.each do |name, (dep, lock_source)|
452
- next if lock_source.nil? || (dep && lock_source.can_lock?(dep))
453
- gemfile_source_name = (dep && dep.source) || "no specified source"
454
- lockfile_source_name = lock_source
455
- changed << "* #{name} from `#{gemfile_source_name}` to `#{lockfile_source_name}`"
385
+ gemfile_source_name = dep.source ? gemfile_source.identifier : "no specified source"
386
+ lockfile_source_name = lock_dep.source ? lock_source.identifier : "no specified source"
387
+ changed << "* #{name} from `#{lockfile_source_name}` to `#{gemfile_source_name}`"
456
388
  end
457
389
 
458
390
  reason = change_reason
@@ -519,14 +451,6 @@ module Bundler
519
451
  end
520
452
  end
521
453
 
522
- def find_resolved_spec(current_spec)
523
- specs.find_by_name_and_platform(current_spec.name, current_spec.platform)
524
- end
525
-
526
- def find_indexed_specs(current_spec)
527
- index[current_spec.name].select {|spec| spec.match_platform(current_spec.platform) }.sort_by(&:version)
528
- end
529
-
530
454
  attr_reader :sources
531
455
  private :sources
532
456
 
@@ -540,8 +464,38 @@ module Bundler
540
464
 
541
465
  private
542
466
 
467
+ def materialize(dependencies)
468
+ specs = resolve.materialize(dependencies)
469
+ missing_specs = specs.missing_specs
470
+
471
+ if missing_specs.any?
472
+ missing_specs.each do |s|
473
+ locked_gem = @locked_specs[s.name].last
474
+ next if locked_gem.nil? || locked_gem.version != s.version || !@remote
475
+ raise GemNotFound, "Your bundle is locked to #{locked_gem} from #{locked_gem.source}, but that version can " \
476
+ "no longer be found in that source. That means the author of #{locked_gem} has removed it. " \
477
+ "You'll need to update your bundle to a version other than #{locked_gem} that hasn't been " \
478
+ "removed in order to install."
479
+ end
480
+
481
+ raise GemNotFound, "Could not find #{missing_specs.map(&:full_name).join(", ")} in any of the sources"
482
+ end
483
+
484
+ unless specs["bundler"].any?
485
+ bundler = sources.metadata_source.specs.search(Gem::Dependency.new("bundler", VERSION)).last
486
+ specs["bundler"] = bundler
487
+ end
488
+
489
+ specs
490
+ end
491
+
492
+ def precompute_source_requirements_for_indirect_dependencies?
493
+ @remote && sources.non_global_rubygems_sources.all?(&:dependency_api_available?) && !sources.aggregate_global_source?
494
+ end
495
+
543
496
  def current_ruby_platform_locked?
544
497
  return false unless generic_local_platform == Gem::Platform::RUBY
498
+ return false if Bundler.settings[:force_ruby_platform] && !@platforms.include?(Gem::Platform::RUBY)
545
499
 
546
500
  current_platform_locked?
547
501
  end
@@ -592,9 +546,9 @@ module Bundler
592
546
 
593
547
  def dependencies_for_source_changed?(source, locked_source = source)
594
548
  deps_for_source = @dependencies.select {|s| s.source == source }
595
- locked_deps_for_source = @locked_deps.values.select {|dep| dep.source == locked_source }
549
+ locked_deps_for_source = locked_dependencies.select {|dep| dep.source == locked_source }
596
550
 
597
- deps_for_source.sort != locked_deps_for_source.sort
551
+ deps_for_source.uniq.sort != locked_deps_for_source.sort
598
552
  end
599
553
 
600
554
  def specs_for_source_changed?(source)
@@ -653,36 +607,11 @@ module Bundler
653
607
  end
654
608
  end
655
609
 
656
- def converge_rubygems_sources
657
- return false if Bundler.feature_flag.disable_multisource?
658
-
659
- changes = false
660
-
661
- # Get the RubyGems sources from the Gemfile.lock
662
- locked_gem_sources = @locked_sources.select {|s| s.is_a?(Source::Rubygems) }
663
- # Get the RubyGems remotes from the Gemfile
664
- actual_remotes = sources.rubygems_remotes
665
-
666
- # If there is a RubyGems source in both
667
- if !locked_gem_sources.empty? && !actual_remotes.empty?
668
- locked_gem_sources.each do |locked_gem|
669
- # Merge the remotes from the Gemfile into the Gemfile.lock
670
- changes |= locked_gem.replace_remotes(actual_remotes, Bundler.settings[:allow_deployment_source_credential_changes])
671
- end
672
- end
673
-
674
- changes
675
- end
676
-
677
610
  def converge_sources
678
- changes = false
679
-
680
- changes |= converge_rubygems_sources
681
-
682
611
  # Replace the sources from the Gemfile with the sources from the Gemfile.lock,
683
612
  # if they exist in the Gemfile.lock and are `==`. If you can't find an equivalent
684
613
  # source in the Gemfile.lock, use the one from the Gemfile.
685
- changes |= sources.replace_sources!(@locked_sources)
614
+ changes = sources.replace_sources!(@locked_sources)
686
615
 
687
616
  sources.all_sources.each do |source|
688
617
  # If the source is unlockable and the current command allows an unlock of
@@ -700,25 +629,14 @@ module Bundler
700
629
  end
701
630
 
702
631
  def converge_dependencies
703
- frozen = Bundler.frozen_bundle?
704
- (@dependencies + @locked_deps.values).each do |dep|
705
- locked_source = @locked_deps[dep.name]
706
- # This is to make sure that if bundler is installing in deployment mode and
707
- # after locked_source and sources don't match, we still use locked_source.
708
- if frozen && !locked_source.nil? &&
709
- locked_source.respond_to?(:source) && locked_source.source.instance_of?(Source::Path) && locked_source.source.path.exist?
710
- dep.source = locked_source.source
711
- elsif dep.source
632
+ changes = false
633
+
634
+ @dependencies.each do |dep|
635
+ if dep.source
712
636
  dep.source = sources.get(dep.source)
713
637
  end
714
- end
715
638
 
716
- changes = false
717
- # We want to know if all match, but don't want to check all entries
718
- # This means we need to return false if any dependency doesn't match
719
- # the lock or doesn't exist in the lock.
720
- @dependencies.each do |dependency|
721
- unless locked_dep = @locked_deps[dependency.name]
639
+ unless locked_dep = @locked_deps[dep.name]
722
640
  changes = true
723
641
  next
724
642
  end
@@ -729,11 +647,11 @@ module Bundler
729
647
  # directive, the lockfile dependencies and resolved dependencies end up
730
648
  # with a mismatch on #type. Work around that by setting the type on the
731
649
  # dep from the lockfile.
732
- locked_dep.instance_variable_set(:@type, dependency.type)
650
+ locked_dep.instance_variable_set(:@type, dep.type)
733
651
 
734
652
  # We already know the name matches from the hash lookup
735
653
  # so we only need to check the requirement now
736
- changes ||= dependency.requirement != locked_dep.requirement
654
+ changes ||= dep.requirement != locked_dep.requirement
737
655
  end
738
656
 
739
657
  changes
@@ -743,47 +661,37 @@ module Bundler
743
661
  # commonly happen if the Gemfile has changed since the lockfile was last
744
662
  # generated
745
663
  def converge_locked_specs
746
- deps = []
664
+ resolve = converge_specs(@locked_specs)
747
665
 
748
- # Build a list of dependencies that are the same in the Gemfile
749
- # and Gemfile.lock. If the Gemfile modified a dependency, but
750
- # the gem in the Gemfile.lock still satisfies it, this is fine
751
- # too.
752
- @dependencies.each do |dep|
753
- locked_dep = @locked_deps[dep.name]
666
+ diff = nil
754
667
 
755
- # If the locked_dep doesn't match the dependency we're looking for then we ignore the locked_dep
756
- locked_dep = nil unless locked_dep == dep
757
-
758
- if in_locked_deps?(dep, locked_dep) || satisfies_locked_spec?(dep)
759
- deps << dep
760
- elsif dep.source.is_a?(Source::Path) && dep.current_platform? && (!locked_dep || dep.source != locked_dep.source)
761
- @locked_specs.each do |s|
762
- @unlock[:gems] << s.name if s.source == dep.source
763
- end
668
+ # Now, we unlock any sources that do not have anymore gems pinned to it
669
+ sources.all_sources.each do |source|
670
+ next unless source.respond_to?(:unlock!)
764
671
 
765
- dep.source.unlock! if dep.source.respond_to?(:unlock!)
766
- dep.source.specs.each {|s| @unlock[:gems] << s.name }
672
+ unless resolve.any? {|s| s.source == source }
673
+ diff ||= @locked_specs.to_a - resolve.to_a
674
+ source.unlock! if diff.any? {|s| s.source == source }
767
675
  end
768
676
  end
769
677
 
770
- unlock_source_unlocks_spec = Bundler.feature_flag.unlock_source_unlocks_spec?
678
+ resolve
679
+ end
771
680
 
681
+ def converge_specs(specs)
682
+ deps = []
772
683
  converged = []
773
- @locked_specs.each do |s|
684
+ specs.each do |s|
774
685
  # Replace the locked dependency's source with the equivalent source from the Gemfile
775
686
  dep = @dependencies.find {|d| s.satisfies?(d) }
776
- s.source = (dep && dep.source) || sources.get(s.source)
777
687
 
778
- # Don't add a spec to the list if its source is expired. For example,
779
- # if you change a Git gem to RubyGems.
780
- next if s.source.nil?
781
- next if @unlock[:sources].include?(s.source.name)
688
+ if dep && (!dep.source || s.source.include?(dep.source))
689
+ deps << dep
690
+ end
782
691
 
783
- # XXX This is a backwards-compatibility fix to preserve the ability to
784
- # unlock a single gem by passing its name via `--source`. See issue #3759
785
- # TODO: delete in Bundler 2
786
- next if unlock_source_unlocks_spec && @unlock[:sources].include?(s.name)
692
+ s.source = (dep && dep.source) || sources.get(s.source) || sources.default_source unless Bundler.frozen_bundle?
693
+
694
+ next if @unlock[:sources].include?(s.source.name)
787
695
 
788
696
  # If the spec is from a path source and it doesn't exist anymore
789
697
  # then we unlock it.
@@ -795,8 +703,8 @@ module Bundler
795
703
  rescue PathError, GitError
796
704
  # if we won't need the source (according to the lockfile),
797
705
  # don't error if the path/git source isn't available
798
- next if @locked_specs.
799
- for(requested_dependencies, [], false, true, false).
706
+ next if specs.
707
+ for(requested_dependencies, false, true).
800
708
  none? {|locked_spec| locked_spec.source == s.source }
801
709
 
802
710
  raise
@@ -811,36 +719,15 @@ module Bundler
811
719
  s.dependencies.replace(new_spec.dependencies)
812
720
  end
813
721
 
814
- converged << s
815
- end
816
-
817
- resolve = SpecSet.new(converged)
818
- @locked_specs_incomplete_for_platform = !resolve.for(expand_dependencies(requested_dependencies & deps), @unlock[:gems], true, true)
819
- resolve = resolve.for(expand_dependencies(deps, true), @unlock[:gems], false, false, false)
820
- diff = nil
821
-
822
- # Now, we unlock any sources that do not have anymore gems pinned to it
823
- sources.all_sources.each do |source|
824
- next unless source.respond_to?(:unlock!)
825
-
826
- unless resolve.any? {|s| s.source == source }
827
- diff ||= @locked_specs.to_a - resolve.to_a
828
- source.unlock! if diff.any? {|s| s.source == source }
722
+ if dep.nil? && requested_dependencies.find {|d| s.name == d.name }
723
+ @unlock[:gems] << s.name
724
+ else
725
+ converged << s
829
726
  end
830
727
  end
831
728
 
832
- resolve
833
- end
834
-
835
- def in_locked_deps?(dep, locked_dep)
836
- # Because the lockfile can't link a dep to a specific remote, we need to
837
- # treat sources as equivalent anytime the locked dep has all the remotes
838
- # that the Gemfile dep does.
839
- locked_dep && locked_dep.source && dep.source && locked_dep.source.include?(dep.source)
840
- end
841
-
842
- def satisfies_locked_spec?(dep)
843
- @locked_specs[dep].any? {|s| s.satisfies?(dep) && (!dep.source || s.source.include?(dep.source)) }
729
+ resolve = SpecSet.new(converged)
730
+ SpecSet.new(resolve.for(expand_dependencies(deps, true), false, false).reject{|s| @unlock[:gems].include?(s.name) })
844
731
  end
845
732
 
846
733
  def metadata_dependencies
@@ -887,38 +774,22 @@ module Bundler
887
774
  end
888
775
 
889
776
  def source_requirements
890
- # Load all specs from remote sources
891
- index
892
-
893
777
  # Record the specs available in each gem's source, so that those
894
778
  # specs will be available later when the resolver knows where to
895
779
  # look for that gemspec (or its dependencies)
896
- default = sources.default_source
897
- source_requirements = { :default => default }
898
- default = nil unless Bundler.feature_flag.disable_multisource?
899
- dependencies.each do |dep|
900
- next unless source = dep.source || default
901
- source_requirements[dep.name] = source
780
+ source_requirements = if precompute_source_requirements_for_indirect_dependencies?
781
+ { :default => sources.default_source }.merge(source_map.all_requirements)
782
+ else
783
+ { :default => Source::RubygemsAggregate.new(sources, source_map) }.merge(source_map.direct_requirements)
902
784
  end
903
785
  metadata_dependencies.each do |dep|
904
786
  source_requirements[dep.name] = sources.metadata_source
905
787
  end
906
- source_requirements[:default_bundler] = source_requirements["bundler"] || source_requirements[:default]
788
+ source_requirements[:default_bundler] = source_requirements["bundler"] || sources.default_source
907
789
  source_requirements["bundler"] = sources.metadata_source # needs to come last to override
908
790
  source_requirements
909
791
  end
910
792
 
911
- def pinned_spec_names(skip = nil)
912
- pinned_names = []
913
- default = Bundler.feature_flag.disable_multisource? && sources.default_source
914
- @dependencies.each do |dep|
915
- next unless dep_source = dep.source || default
916
- next if dep_source == skip
917
- pinned_names << dep.name
918
- end
919
- pinned_names
920
- end
921
-
922
793
  def requested_groups
923
794
  groups - Bundler.settings[:without] - @optional_groups + Bundler.settings[:with]
924
795
  end
@@ -936,12 +807,6 @@ module Bundler
936
807
  current == proposed
937
808
  end
938
809
 
939
- def extract_gem_info(error)
940
- # This method will extract the error message like "Could not find foo-1.2.3 in any of the sources"
941
- # to an array. The first element will be the gem name (e.g. foo), the second will be the version number.
942
- error.message.scan(/Could not find (\w+)-(\d+(?:\.\d+)+)/).flatten
943
- end
944
-
945
810
  def compute_requires
946
811
  dependencies.reduce({}) do |requires, dep|
947
812
  next requires unless dep.should_include?
@@ -954,24 +819,16 @@ module Bundler
954
819
  end
955
820
 
956
821
  def additional_base_requirements_for_resolve
957
- return [] unless @locked_gems && Bundler.feature_flag.only_update_to_newer_versions?
958
- dependencies_by_name = dependencies.inject({}) {|memo, dep| memo.update(dep.name => dep) }
959
- @locked_gems.specs.reduce({}) do |requirements, locked_spec|
822
+ return [] unless @locked_gems && unlocking? && !sources.expired_sources?(@locked_gems.sources)
823
+ converge_specs(@locked_gems.specs).map do |locked_spec|
960
824
  name = locked_spec.name
961
- dependency = dependencies_by_name[name]
962
- next requirements unless dependency
963
- next requirements if @locked_gems.dependencies[name] != dependency
964
- next requirements if dependency.source.is_a?(Source::Path)
965
825
  dep = Gem::Dependency.new(name, ">= #{locked_spec.version}")
966
- requirements[name] = DepProxy.get_proxy(dep, locked_spec.platform)
967
- requirements
968
- end.values
826
+ DepProxy.get_proxy(dep, locked_spec.platform)
827
+ end
969
828
  end
970
829
 
971
- def equivalent_rubygems_remotes?(source)
972
- return false unless source.is_a?(Source::Rubygems)
973
-
974
- Bundler.settings[:allow_deployment_source_credential_changes] && source.equivalent_remotes?(sources.rubygems_remotes)
830
+ def source_map
831
+ @source_map ||= SourceMap.new(sources, dependencies)
975
832
  end
976
833
  end
977
834
  end