bundler 2.2.24 → 2.2.33

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +149 -1
  3. data/README.md +1 -1
  4. data/lib/bundler/build_metadata.rb +2 -2
  5. data/lib/bundler/cli/cache.rb +1 -1
  6. data/lib/bundler/cli/doctor.rb +4 -3
  7. data/lib/bundler/cli/exec.rb +1 -6
  8. data/lib/bundler/cli/gem.rb +90 -10
  9. data/lib/bundler/cli/info.rb +16 -4
  10. data/lib/bundler/cli/install.rb +4 -17
  11. data/lib/bundler/cli/issue.rb +4 -3
  12. data/lib/bundler/cli/list.rb +7 -1
  13. data/lib/bundler/cli/open.rb +1 -2
  14. data/lib/bundler/cli/remove.rb +1 -2
  15. data/lib/bundler/cli/update.rb +3 -3
  16. data/lib/bundler/cli.rb +16 -10
  17. data/lib/bundler/compact_index_client/updater.rb +0 -5
  18. data/lib/bundler/compact_index_client.rb +2 -2
  19. data/lib/bundler/definition.rb +84 -150
  20. data/lib/bundler/digest.rb +71 -0
  21. data/lib/bundler/dsl.rb +32 -4
  22. data/lib/bundler/environment_preserver.rb +4 -1
  23. data/lib/bundler/errors.rb +19 -3
  24. data/lib/bundler/fetcher.rb +2 -1
  25. data/lib/bundler/friendly_errors.rb +5 -30
  26. data/lib/bundler/gem_helper.rb +6 -17
  27. data/lib/bundler/installer/gem_installer.rb +3 -16
  28. data/lib/bundler/installer/standalone.rb +13 -8
  29. data/lib/bundler/installer.rb +0 -1
  30. data/lib/bundler/lazy_specification.rb +17 -1
  31. data/lib/bundler/lockfile_parser.rb +2 -0
  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 +3 -3
  38. data/lib/bundler/man/bundle-config.1.ronn +2 -2
  39. data/lib/bundler/man/bundle-doctor.1 +1 -1
  40. data/lib/bundler/man/bundle-exec.1 +1 -1
  41. data/lib/bundler/man/bundle-gem.1 +14 -1
  42. data/lib/bundler/man/bundle-gem.1.ronn +16 -0
  43. data/lib/bundler/man/bundle-info.1 +1 -1
  44. data/lib/bundler/man/bundle-init.1 +1 -1
  45. data/lib/bundler/man/bundle-inject.1 +1 -1
  46. data/lib/bundler/man/bundle-install.1 +1 -1
  47. data/lib/bundler/man/bundle-list.1 +1 -1
  48. data/lib/bundler/man/bundle-lock.1 +1 -1
  49. data/lib/bundler/man/bundle-open.1 +1 -1
  50. data/lib/bundler/man/bundle-outdated.1 +1 -1
  51. data/lib/bundler/man/bundle-platform.1 +1 -1
  52. data/lib/bundler/man/bundle-pristine.1 +1 -1
  53. data/lib/bundler/man/bundle-remove.1 +1 -1
  54. data/lib/bundler/man/bundle-show.1 +1 -1
  55. data/lib/bundler/man/bundle-update.1 +1 -1
  56. data/lib/bundler/man/bundle-viz.1 +1 -1
  57. data/lib/bundler/man/bundle.1 +1 -1
  58. data/lib/bundler/man/gemfile.5 +27 -1
  59. data/lib/bundler/man/gemfile.5.ronn +8 -0
  60. data/lib/bundler/plugin/api/source.rb +1 -0
  61. data/lib/bundler/plugin/installer.rb +2 -0
  62. data/lib/bundler/plugin.rb +23 -6
  63. data/lib/bundler/resolver.rb +8 -17
  64. data/lib/bundler/rubygems_ext.rb +4 -0
  65. data/lib/bundler/rubygems_gem_installer.rb +25 -5
  66. data/lib/bundler/rubygems_integration.rb +28 -9
  67. data/lib/bundler/runtime.rb +17 -8
  68. data/lib/bundler/settings.rb +13 -1
  69. data/lib/bundler/setup.rb +2 -2
  70. data/lib/bundler/shared_helpers.rb +2 -10
  71. data/lib/bundler/source/git/git_proxy.rb +8 -6
  72. data/lib/bundler/source/git.rb +22 -4
  73. data/lib/bundler/source/rubygems.rb +70 -81
  74. data/lib/bundler/source/rubygems_aggregate.rb +4 -0
  75. data/lib/bundler/source.rb +4 -0
  76. data/lib/bundler/source_list.rb +22 -31
  77. data/lib/bundler/spec_set.rb +14 -36
  78. data/lib/bundler/templates/Executable.bundler +6 -6
  79. data/lib/bundler/templates/newgem/Gemfile.tt +5 -2
  80. data/lib/bundler/templates/newgem/Rakefile.tt +5 -1
  81. data/lib/bundler/templates/newgem/github/workflows/main.yml.tt +13 -2
  82. data/lib/bundler/templates/newgem/newgem.gemspec.tt +16 -14
  83. data/lib/bundler/templates/newgem/sig/newgem.rbs.tt +8 -0
  84. data/lib/bundler/templates/newgem/standard.yml.tt +2 -0
  85. data/lib/bundler/vendor/.document +1 -0
  86. data/lib/bundler/vendor/connection_pool/LICENSE +20 -0
  87. data/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb +19 -21
  88. data/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb +1 -1
  89. data/lib/bundler/vendor/connection_pool/lib/connection_pool/wrapper.rb +57 -0
  90. data/lib/bundler/vendor/connection_pool/lib/connection_pool.rb +39 -74
  91. data/lib/bundler/vendor/fileutils/LICENSE.txt +22 -0
  92. data/lib/bundler/vendor/molinillo/LICENSE +9 -0
  93. data/lib/bundler/vendor/molinillo/lib/molinillo/dependency_graph.rb +2 -2
  94. data/lib/bundler/vendor/net-http-persistent/README.rdoc +82 -0
  95. data/lib/bundler/vendor/thor/LICENSE.md +20 -0
  96. data/lib/bundler/vendor/tsort/LICENSE.txt +22 -0
  97. data/lib/bundler/vendor/tsort/lib/tsort.rb +453 -0
  98. data/lib/bundler/vendor/uri/LICENSE.txt +22 -0
  99. data/lib/bundler/vendored_tsort.rb +4 -0
  100. data/lib/bundler/version.rb +1 -1
  101. data/lib/bundler/worker.rb +19 -4
  102. data/lib/bundler.rb +13 -22
  103. metadata +20 -7
  104. data/lib/bundler/vendor/connection_pool/lib/connection_pool/monotonic_time.rb +0 -66
data/lib/bundler/cli.rb CHANGED
@@ -73,14 +73,6 @@ module Bundler
73
73
  Bundler.ui = UI::Shell.new(options)
74
74
  Bundler.ui.level = "debug" if options["verbose"]
75
75
  unprinted_warnings.each {|w| Bundler.ui.warn(w) }
76
-
77
- if ENV["RUBYGEMS_GEMDEPS"] && !ENV["RUBYGEMS_GEMDEPS"].empty?
78
- Bundler.ui.warn(
79
- "The RUBYGEMS_GEMDEPS environment variable is set. This enables RubyGems' " \
80
- "experimental Gemfile mode, which may conflict with Bundler and cause unexpected errors. " \
81
- "To remove this warning, unset RUBYGEMS_GEMDEPS.", :wrap => true
82
- )
83
- end
84
76
  end
85
77
 
86
78
  check_unknown_options!(:except => [:config, :exec])
@@ -192,6 +184,7 @@ module Bundler
192
184
  method_option "install", :type => :boolean, :banner =>
193
185
  "Runs 'bundle install' after removing the gems from the Gemfile"
194
186
  def remove(*gems)
187
+ SharedHelpers.major_deprecation(2, "The `--install` flag has been deprecated. `bundle install` is triggered by default.") if ARGV.include?("--install")
195
188
  require_relative "cli/remove"
196
189
  Remove.new(gems, options).run
197
190
  end
@@ -338,6 +331,7 @@ module Bundler
338
331
 
339
332
  desc "info GEM [OPTIONS]", "Show information for the given gem"
340
333
  method_option "path", :type => :boolean, :banner => "Print full path to gem"
334
+ method_option "version", :type => :boolean, :banner => "Print gem version"
341
335
  def info(gem_name)
342
336
  require_relative "cli/info"
343
337
  Info.new(options, gem_name).run
@@ -456,6 +450,12 @@ module Bundler
456
450
  "do in future versions. Instead please use `bundle config set cache_all true`, " \
457
451
  "and stop using this flag" if ARGV.include?("--all")
458
452
 
453
+ SharedHelpers.major_deprecation 2,
454
+ "The `--path` flag is deprecated because its semantics are unclear. " \
455
+ "Use `bundle config cache_path` to configure the path of your cache of gems, " \
456
+ "and `bundle config path` to configure the path where your gems are installed, " \
457
+ "and stop using this flag" if ARGV.include?("--path")
458
+
459
459
  require_relative "cli/cache"
460
460
  Cache.new(options).run
461
461
  end
@@ -463,7 +463,7 @@ module Bundler
463
463
  map aliases_for("cache")
464
464
 
465
465
  desc "exec [OPTIONS]", "Run the command in context of the bundle"
466
- method_option :keep_file_descriptors, :type => :boolean, :default => false
466
+ method_option :keep_file_descriptors, :type => :boolean, :default => true
467
467
  method_option :gemfile, :type => :string, :required => false
468
468
  long_desc <<-D
469
469
  Exec runs a command, providing it access to the gems in the bundle. While using
@@ -471,6 +471,10 @@ module Bundler
471
471
  into the system wide RubyGems repository.
472
472
  D
473
473
  def exec(*args)
474
+ if ARGV.include?("--no-keep-file-descriptors")
475
+ SharedHelpers.major_deprecation(2, "The `--no-keep-file-descriptors` has been deprecated. `bundle exec` no longer mess with your file descriptors. Close them in the exec'd script if you need to")
476
+ end
477
+
474
478
  require_relative "cli/exec"
475
479
  Exec.new(options, args).run
476
480
  end
@@ -549,7 +553,7 @@ module Bundler
549
553
  method_option :version, :type => :boolean, :default => false, :aliases => "-v", :desc => "Set to show each gem version."
550
554
  method_option :without, :type => :array, :default => [], :aliases => "-W", :banner => "GROUP[ GROUP...]", :desc => "Exclude gems that are part of the specified named group."
551
555
  def viz
552
- SharedHelpers.major_deprecation 2, "The `viz` command has been moved to the `bundle-viz` gem, see https://github.com/bundler/bundler-viz"
556
+ SharedHelpers.major_deprecation 2, "The `viz` command has been renamed to `graph` and moved to a plugin. See https://github.com/rubygems/bundler-graph"
553
557
  require_relative "cli/viz"
554
558
  Viz.new(options.dup).run
555
559
  end
@@ -572,6 +576,8 @@ module Bundler
572
576
  :desc => "Generate a test directory for your library, either rspec, minitest or test-unit. Set a default with `bundle config set --global gem.test (rspec|minitest|test-unit)`."
573
577
  method_option :ci, :type => :string, :lazy_default => Bundler.settings["gem.ci"] || "",
574
578
  :desc => "Generate CI configuration, either GitHub Actions, Travis CI, GitLab CI or CircleCI. Set a default with `bundle config set --global gem.ci (github|travis|gitlab|circle)`"
579
+ method_option :linter, :type => :string, :lazy_default => Bundler.settings["gem.linter"] || "",
580
+ :desc => "Add a linter and code formatter, either RuboCop or Standard. Set a default with `bundle config set --global gem.linter (rubocop|standard)`"
575
581
  method_option :github_username, :type => :string, :default => Bundler.settings["gem.github_username"], :banner => "Set your username on GitHub", :desc => "Fill in GitHub username on README so that you don't have to do it manually. Set a default with `bundle config set --global gem.github_username <your_username>`."
576
582
 
577
583
  def gem(name)
@@ -76,11 +76,6 @@ module Bundler
76
76
 
77
77
  update(local_path, remote_path, :retrying)
78
78
  end
79
- rescue Errno::EACCES
80
- raise Bundler::PermissionError,
81
- "Bundler does not have write access to create a temp directory " \
82
- "within #{Dir.tmpdir}. Bundler must have write access to your " \
83
- "systems temp directory to function properly. "
84
79
  rescue Zlib::GzipFile::Error
85
80
  raise Bundler::HTTPError
86
81
  end
@@ -5,7 +5,7 @@ require "set"
5
5
 
6
6
  module Bundler
7
7
  class CompactIndexClient
8
- DEBUG_MUTEX = Mutex.new
8
+ DEBUG_MUTEX = Thread::Mutex.new
9
9
  def self.debug
10
10
  return unless ENV["DEBUG_COMPACT_INDEX"]
11
11
  DEBUG_MUTEX.synchronize { warn("[#{self}] #{yield}") }
@@ -25,7 +25,7 @@ module Bundler
25
25
  @endpoints = Set.new
26
26
  @info_checksums_by_name = {}
27
27
  @parsed_checksums = false
28
- @mutex = Mutex.new
28
+ @mutex = Thread::Mutex.new
29
29
  end
30
30
 
31
31
  def execution_mode=(block)
@@ -73,7 +73,6 @@ module Bundler
73
73
  @lockfile_contents = String.new
74
74
  @locked_bundler_version = nil
75
75
  @locked_ruby_version = nil
76
- @locked_specs_incomplete_for_platform = false
77
76
  @new_platform = nil
78
77
 
79
78
  if lockfile && File.exist?(lockfile)
@@ -133,12 +132,14 @@ module Bundler
133
132
  @unlock[:gems] ||= @dependencies.map(&:name)
134
133
  else
135
134
  eager_unlock = expand_dependencies(@unlock[:gems] || [], true)
136
- @unlock[:gems] = @locked_specs.for(eager_unlock, false, false, false).map(&:name)
135
+ @unlock[:gems] = @locked_specs.for(eager_unlock, false, false).map(&:name)
137
136
  end
138
137
 
139
138
  @dependency_changes = converge_dependencies
140
139
  @local_changes = converge_locals
141
140
 
141
+ @locked_specs_incomplete_for_platform = !@locked_specs.for(requested_dependencies & expand_dependencies(locked_dependencies), true, true)
142
+
142
143
  @requires = compute_requires
143
144
  end
144
145
 
@@ -157,10 +158,6 @@ module Bundler
157
158
  end
158
159
  end
159
160
 
160
- def multisource_allowed?
161
- @multisource_allowed
162
- end
163
-
164
161
  def resolve_only_locally!
165
162
  @remote = false
166
163
  sources.local_only!
@@ -185,15 +182,7 @@ module Bundler
185
182
  #
186
183
  # @return [Bundler::SpecSet]
187
184
  def specs
188
- @specs ||= add_bundler_to(resolve.materialize(requested_dependencies))
189
- rescue GemNotFound => e # Handle yanked gem
190
- gem_name, gem_version = extract_gem_info(e)
191
- locked_gem = @locked_specs[gem_name].last
192
- raise if locked_gem.nil? || locked_gem.version.to_s != gem_version || !@remote
193
- raise GemNotFound, "Your bundle is locked to #{locked_gem} from #{locked_gem.source}, but that version can " \
194
- "no longer be found in that source. That means the author of #{locked_gem} has removed it. " \
195
- "You'll need to update your bundle to a version other than #{locked_gem} that hasn't been " \
196
- "removed in order to install."
185
+ @specs ||= materialize(requested_dependencies)
197
186
  end
198
187
 
199
188
  def new_specs
@@ -205,9 +194,7 @@ module Bundler
205
194
  end
206
195
 
207
196
  def missing_specs
208
- missing = []
209
- resolve.materialize(requested_dependencies, missing)
210
- missing
197
+ resolve.materialize(requested_dependencies).missing_specs
211
198
  end
212
199
 
213
200
  def missing_specs?
@@ -238,17 +225,22 @@ module Bundler
238
225
  end
239
226
  end
240
227
 
228
+ def locked_dependencies
229
+ @locked_deps.values
230
+ end
231
+
241
232
  def specs_for(groups)
242
- groups = requested_groups if groups.empty?
233
+ return specs if groups.empty?
243
234
  deps = dependencies_for(groups)
244
- add_bundler_to(resolve.materialize(expand_dependencies(deps)))
235
+ materialize(deps)
245
236
  end
246
237
 
247
238
  def dependencies_for(groups)
248
239
  groups.map!(&:to_sym)
249
- current_dependencies.reject do |d|
240
+ deps = current_dependencies.reject do |d|
250
241
  (d.groups & groups).empty?
251
242
  end
243
+ expand_dependencies(deps)
252
244
  end
253
245
 
254
246
  # Resolve all the dependencies specified in Gemfile. It ensures that
@@ -274,10 +266,6 @@ module Bundler
274
266
  end
275
267
  end
276
268
 
277
- def has_rubygems_remotes?
278
- sources.rubygems_sources.any? {|s| s.remotes.any? }
279
- end
280
-
281
269
  def spec_git_paths
282
270
  sources.git_sources.map {|s| File.realpath(s.path) if File.exist?(s.path) }.compact
283
271
  end
@@ -376,44 +364,26 @@ module Bundler
376
364
  added.concat new_platforms.map {|p| "* platform: #{p}" }
377
365
  deleted.concat deleted_platforms.map {|p| "* platform: #{p}" }
378
366
 
379
- gemfile_sources = sources.lock_sources
380
-
381
- new_sources = gemfile_sources - @locked_sources
382
- deleted_sources = @locked_sources - gemfile_sources
383
-
384
- new_deps = @dependencies - @locked_deps.values
385
- deleted_deps = @locked_deps.values - @dependencies
386
-
387
- # Check if it is possible that the source is only changed thing
388
- if (new_deps.empty? && deleted_deps.empty?) && (!new_sources.empty? && !deleted_sources.empty?)
389
- new_sources.reject! {|source| (source.path? && source.path.exist?) || equivalent_rubygems_remotes?(source) }
390
- deleted_sources.reject! {|source| (source.path? && source.path.exist?) || equivalent_rubygems_remotes?(source) }
391
- end
392
-
393
- if @locked_sources != gemfile_sources
394
- if new_sources.any?
395
- added.concat new_sources.map {|source| "* source: #{source}" }
396
- end
397
-
398
- if deleted_sources.any?
399
- deleted.concat deleted_sources.map {|source| "* source: #{source}" }
400
- end
401
- end
367
+ new_deps = @dependencies - locked_dependencies
368
+ deleted_deps = locked_dependencies - @dependencies
402
369
 
403
370
  added.concat new_deps.map {|d| "* #{pretty_dep(d)}" } if new_deps.any?
404
- if deleted_deps.any?
405
- deleted.concat deleted_deps.map {|d| "* #{pretty_dep(d)}" }
406
- end
371
+ deleted.concat deleted_deps.map {|d| "* #{pretty_dep(d)}" } if deleted_deps.any?
407
372
 
408
373
  both_sources = Hash.new {|h, k| h[k] = [] }
409
374
  @dependencies.each {|d| both_sources[d.name][0] = d }
410
- @locked_deps.each {|name, d| both_sources[name][1] = d.source }
375
+ locked_dependencies.each {|d| both_sources[d.name][1] = d }
376
+
377
+ both_sources.each do |name, (dep, lock_dep)|
378
+ next if dep.nil? || lock_dep.nil?
411
379
 
412
- both_sources.each do |name, (dep, lock_source)|
413
- next if lock_source.nil? || (dep && lock_source.can_lock?(dep))
414
- gemfile_source_name = (dep && dep.source) || "no specified source"
415
- lockfile_source_name = lock_source
416
- changed << "* #{name} from `#{gemfile_source_name}` to `#{lockfile_source_name}`"
380
+ gemfile_source = dep.source || sources.default_source
381
+ lock_source = lock_dep.source || sources.default_source
382
+ next if lock_source.include?(gemfile_source)
383
+
384
+ gemfile_source_name = dep.source ? gemfile_source.identifier : "no specified source"
385
+ lockfile_source_name = lock_dep.source ? lock_source.identifier : "no specified source"
386
+ changed << "* #{name} from `#{lockfile_source_name}` to `#{gemfile_source_name}`"
417
387
  end
418
388
 
419
389
  reason = change_reason
@@ -493,7 +463,23 @@ module Bundler
493
463
 
494
464
  private
495
465
 
496
- def add_bundler_to(specs)
466
+ def materialize(dependencies)
467
+ specs = resolve.materialize(dependencies)
468
+ missing_specs = specs.missing_specs
469
+
470
+ if missing_specs.any?
471
+ missing_specs.each do |s|
472
+ locked_gem = @locked_specs[s.name].last
473
+ next if locked_gem.nil? || locked_gem.version != s.version || !@remote
474
+ raise GemNotFound, "Your bundle is locked to #{locked_gem} from #{locked_gem.source}, but that version can " \
475
+ "no longer be found in that source. That means the author of #{locked_gem} has removed it. " \
476
+ "You'll need to update your bundle to a version other than #{locked_gem} that hasn't been " \
477
+ "removed in order to install."
478
+ end
479
+
480
+ raise GemNotFound, "Could not find #{missing_specs.map(&:full_name).join(", ")} in any of the sources"
481
+ end
482
+
497
483
  unless specs["bundler"].any?
498
484
  bundler = sources.metadata_source.specs.search(Gem::Dependency.new("bundler", VERSION)).last
499
485
  specs["bundler"] = bundler
@@ -503,7 +489,7 @@ module Bundler
503
489
  end
504
490
 
505
491
  def precompute_source_requirements_for_indirect_dependencies?
506
- sources.non_global_rubygems_sources.all?(&:dependency_api_available?) && !sources.aggregate_global_source?
492
+ @remote && sources.non_global_rubygems_sources.all?(&:dependency_api_available?) && !sources.aggregate_global_source?
507
493
  end
508
494
 
509
495
  def current_ruby_platform_locked?
@@ -558,7 +544,7 @@ module Bundler
558
544
 
559
545
  def dependencies_for_source_changed?(source, locked_source = source)
560
546
  deps_for_source = @dependencies.select {|s| s.source == source }
561
- locked_deps_for_source = @locked_deps.values.select {|dep| dep.source == locked_source }
547
+ locked_deps_for_source = locked_dependencies.select {|dep| dep.source == locked_source }
562
548
 
563
549
  deps_for_source.uniq.sort != locked_deps_for_source.sort
564
550
  end
@@ -641,25 +627,14 @@ module Bundler
641
627
  end
642
628
 
643
629
  def converge_dependencies
644
- frozen = Bundler.frozen_bundle?
645
- (@dependencies + @locked_deps.values).each do |dep|
646
- locked_source = @locked_deps[dep.name]
647
- # This is to make sure that if bundler is installing in deployment mode and
648
- # after locked_source and sources don't match, we still use locked_source.
649
- if frozen && !locked_source.nil? &&
650
- locked_source.respond_to?(:source) && locked_source.source.instance_of?(Source::Path) && locked_source.source.path.exist?
651
- dep.source = locked_source.source
652
- elsif dep.source
630
+ changes = false
631
+
632
+ @dependencies.each do |dep|
633
+ if dep.source
653
634
  dep.source = sources.get(dep.source)
654
635
  end
655
- end
656
636
 
657
- changes = false
658
- # We want to know if all match, but don't want to check all entries
659
- # This means we need to return false if any dependency doesn't match
660
- # the lock or doesn't exist in the lock.
661
- @dependencies.each do |dependency|
662
- unless locked_dep = @locked_deps[dependency.name]
637
+ unless locked_dep = @locked_deps[dep.name]
663
638
  changes = true
664
639
  next
665
640
  end
@@ -670,11 +645,11 @@ module Bundler
670
645
  # directive, the lockfile dependencies and resolved dependencies end up
671
646
  # with a mismatch on #type. Work around that by setting the type on the
672
647
  # dep from the lockfile.
673
- locked_dep.instance_variable_set(:@type, dependency.type)
648
+ locked_dep.instance_variable_set(:@type, dep.type)
674
649
 
675
650
  # We already know the name matches from the hash lookup
676
651
  # so we only need to check the requirement now
677
- changes ||= dependency.requirement != locked_dep.requirement
652
+ changes ||= dep.requirement != locked_dep.requirement
678
653
  end
679
654
 
680
655
  changes
@@ -684,39 +659,36 @@ module Bundler
684
659
  # commonly happen if the Gemfile has changed since the lockfile was last
685
660
  # generated
686
661
  def converge_locked_specs
687
- deps = []
662
+ resolve = converge_specs(@locked_specs)
688
663
 
689
- # Build a list of dependencies that are the same in the Gemfile
690
- # and Gemfile.lock. If the Gemfile modified a dependency, but
691
- # the gem in the Gemfile.lock still satisfies it, this is fine
692
- # too.
693
- @dependencies.each do |dep|
694
- locked_dep = @locked_deps[dep.name]
664
+ diff = nil
695
665
 
696
- # If the locked_dep doesn't match the dependency we're looking for then we ignore the locked_dep
697
- locked_dep = nil unless locked_dep == dep
698
-
699
- if in_locked_deps?(dep, locked_dep) || satisfies_locked_spec?(dep)
700
- deps << dep
701
- elsif dep.source.is_a?(Source::Path) && dep.current_platform? && (!locked_dep || dep.source != locked_dep.source)
702
- @locked_specs.each do |s|
703
- @unlock[:gems] << s.name if s.source == dep.source
704
- end
666
+ # Now, we unlock any sources that do not have anymore gems pinned to it
667
+ sources.all_sources.each do |source|
668
+ next unless source.respond_to?(:unlock!)
705
669
 
706
- dep.source.unlock! if dep.source.respond_to?(:unlock!)
707
- dep.source.specs.each {|s| @unlock[:gems] << s.name }
670
+ unless resolve.any? {|s| s.source == source }
671
+ diff ||= @locked_specs.to_a - resolve.to_a
672
+ source.unlock! if diff.any? {|s| s.source == source }
708
673
  end
709
674
  end
710
675
 
676
+ resolve
677
+ end
678
+
679
+ def converge_specs(specs)
680
+ deps = []
711
681
  converged = []
712
- @locked_specs.each do |s|
682
+ specs.each do |s|
713
683
  # Replace the locked dependency's source with the equivalent source from the Gemfile
714
684
  dep = @dependencies.find {|d| s.satisfies?(d) }
715
- s.source = (dep && dep.source) || sources.get(s.source)
716
685
 
717
- # Don't add a spec to the list if its source is expired. For example,
718
- # if you change a Git gem to RubyGems.
719
- next if s.source.nil?
686
+ if dep && (!dep.source || s.source.include?(dep.source))
687
+ deps << dep
688
+ end
689
+
690
+ s.source = (dep && dep.source) || sources.get(s.source) || sources.default_source unless Bundler.frozen_bundle?
691
+
720
692
  next if @unlock[:sources].include?(s.source.name)
721
693
 
722
694
  # If the spec is from a path source and it doesn't exist anymore
@@ -729,8 +701,8 @@ module Bundler
729
701
  rescue PathError, GitError
730
702
  # if we won't need the source (according to the lockfile),
731
703
  # don't error if the path/git source isn't available
732
- next if @locked_specs.
733
- for(requested_dependencies, false, true, false).
704
+ next if specs.
705
+ for(requested_dependencies, false, true).
734
706
  none? {|locked_spec| locked_spec.source == s.source }
735
707
 
736
708
  raise
@@ -745,36 +717,15 @@ module Bundler
745
717
  s.dependencies.replace(new_spec.dependencies)
746
718
  end
747
719
 
748
- converged << s
749
- end
750
-
751
- resolve = SpecSet.new(converged)
752
- @locked_specs_incomplete_for_platform = !resolve.for(expand_dependencies(requested_dependencies & deps), true, true)
753
- resolve = SpecSet.new(resolve.for(expand_dependencies(deps, true), false, false, false).reject{|s| @unlock[:gems].include?(s.name) })
754
- diff = nil
755
-
756
- # Now, we unlock any sources that do not have anymore gems pinned to it
757
- sources.all_sources.each do |source|
758
- next unless source.respond_to?(:unlock!)
759
-
760
- unless resolve.any? {|s| s.source == source }
761
- diff ||= @locked_specs.to_a - resolve.to_a
762
- source.unlock! if diff.any? {|s| s.source == source }
720
+ if dep.nil? && requested_dependencies.find {|d| s.name == d.name }
721
+ @unlock[:gems] << s.name
722
+ else
723
+ converged << s
763
724
  end
764
725
  end
765
726
 
766
- resolve
767
- end
768
-
769
- def in_locked_deps?(dep, locked_dep)
770
- # Because the lockfile can't link a dep to a specific remote, we need to
771
- # treat sources as equivalent anytime the locked dep has all the remotes
772
- # that the Gemfile dep does.
773
- locked_dep && locked_dep.source && dep.source && locked_dep.source.include?(dep.source)
774
- end
775
-
776
- def satisfies_locked_spec?(dep)
777
- @locked_specs[dep].any? {|s| s.satisfies?(dep) && (!dep.source || s.source.include?(dep.source)) }
727
+ resolve = SpecSet.new(converged)
728
+ SpecSet.new(resolve.for(expand_dependencies(deps, true), false, false).reject{|s| @unlock[:gems].include?(s.name) })
778
729
  end
779
730
 
780
731
  def metadata_dependencies
@@ -854,12 +805,6 @@ module Bundler
854
805
  current == proposed
855
806
  end
856
807
 
857
- def extract_gem_info(error)
858
- # This method will extract the error message like "Could not find foo-1.2.3 in any of the sources"
859
- # to an array. The first element will be the gem name (e.g. foo), the second will be the version number.
860
- error.message.scan(/Could not find (\w+)-(\d+(?:\.\d+)+)/).flatten
861
- end
862
-
863
808
  def compute_requires
864
809
  dependencies.reduce({}) do |requires, dep|
865
810
  next requires unless dep.should_include?
@@ -873,22 +818,11 @@ module Bundler
873
818
 
874
819
  def additional_base_requirements_for_resolve
875
820
  return [] unless @locked_gems && unlocking? && !sources.expired_sources?(@locked_gems.sources)
876
- dependencies_by_name = dependencies.inject({}) {|memo, dep| memo.update(dep.name => dep) }
877
- @locked_gems.specs.reduce({}) do |requirements, locked_spec|
821
+ converge_specs(@locked_gems.specs).map do |locked_spec|
878
822
  name = locked_spec.name
879
- dependency = dependencies_by_name[name]
880
- next requirements if @locked_gems.dependencies[name] != dependency
881
- next requirements if dependency && dependency.source.is_a?(Source::Path)
882
823
  dep = Gem::Dependency.new(name, ">= #{locked_spec.version}")
883
- requirements[name] = DepProxy.get_proxy(dep, locked_spec.platform)
884
- requirements
885
- end.values
886
- end
887
-
888
- def equivalent_rubygems_remotes?(source)
889
- return false unless source.is_a?(Source::Rubygems)
890
-
891
- Bundler.settings[:allow_deployment_source_credential_changes] && source.equivalent_remotes?(sources.rubygems_remotes)
824
+ DepProxy.get_proxy(dep, locked_spec.platform)
825
+ end
892
826
  end
893
827
 
894
828
  def source_map
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This code was extracted from https://github.com/Solistra/ruby-digest which is under public domain
4
+ module Bundler
5
+ module Digest
6
+ # The initial constant values for the 32-bit constant words A, B, C, D, and
7
+ # E, respectively.
8
+ SHA1_WORDS = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0].freeze
9
+
10
+ # The 8-bit field used for bitwise `AND` masking. Defaults to `0xFFFFFFFF`.
11
+ SHA1_MASK = 0xFFFFFFFF
12
+
13
+ class << self
14
+ def sha1(string)
15
+ unless string.is_a?(String)
16
+ raise TypeError, "can't convert #{string.class.inspect} into String"
17
+ end
18
+
19
+ buffer = string.b
20
+
21
+ words = SHA1_WORDS.dup
22
+ generate_split_buffer(buffer) do |chunk|
23
+ w = []
24
+ chunk.each_slice(4) do |a, b, c, d|
25
+ w << (((a << 8 | b) << 8 | c) << 8 | d)
26
+ end
27
+ a, b, c, d, e = *words
28
+ (16..79).each do |i|
29
+ w[i] = SHA1_MASK & rotate((w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16]), 1)
30
+ end
31
+ 0.upto(79) do |i|
32
+ case i
33
+ when 0..19
34
+ f = ((b & c) | (~b & d))
35
+ k = 0x5A827999
36
+ when 20..39
37
+ f = (b ^ c ^ d)
38
+ k = 0x6ED9EBA1
39
+ when 40..59
40
+ f = ((b & c) | (b & d) | (c & d))
41
+ k = 0x8F1BBCDC
42
+ when 60..79
43
+ f = (b ^ c ^ d)
44
+ k = 0xCA62C1D6
45
+ end
46
+ t = SHA1_MASK & (SHA1_MASK & rotate(a, 5) + f + e + k + w[i])
47
+ a, b, c, d, e = t, a, SHA1_MASK & rotate(b, 30), c, d # rubocop:disable Style/ParallelAssignment
48
+ end
49
+ mutated = [a, b, c, d, e]
50
+ words.map!.with_index {|word, index| SHA1_MASK & (word + mutated[index]) }
51
+ end
52
+
53
+ words.pack("N*").unpack("H*").first
54
+ end
55
+
56
+ private
57
+
58
+ def generate_split_buffer(string, &block)
59
+ size = string.bytesize * 8
60
+ buffer = string.bytes << 128
61
+ buffer << 0 while buffer.size % 64 != 56
62
+ buffer.concat([size].pack("Q>").bytes)
63
+ buffer.each_slice(64, &block)
64
+ end
65
+
66
+ def rotate(value, spaces)
67
+ value << spaces | value >> (32 - spaces)
68
+ end
69
+ end
70
+ end
71
+ end
data/lib/bundler/dsl.rb CHANGED
@@ -18,6 +18,8 @@ module Bundler
18
18
  VALID_KEYS = %w[group groups git path glob name branch ref tag require submodules
19
19
  platform platforms type source install_if gemfile].freeze
20
20
 
21
+ GITHUB_PULL_REQUEST_URL = %r{\Ahttps://github\.com/([A-Za-z0-9_\-\.]+/[A-Za-z0-9_\-\.]+)/pull/(\d+)\z}.freeze
22
+
21
23
  attr_reader :gemspecs
22
24
  attr_accessor :dependencies
23
25
 
@@ -278,8 +280,17 @@ module Bundler
278
280
  warn_deprecated_git_source(:github, <<-'RUBY'.strip, 'Change any "reponame" :github sources to "username/reponame".')
279
281
  "https://github.com/#{repo_name}.git"
280
282
  RUBY
281
- repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
282
- "https://github.com/#{repo_name}.git"
283
+ if repo_name =~ GITHUB_PULL_REQUEST_URL
284
+ {
285
+ "git" => "https://github.com/#{$1}.git",
286
+ "branch" => "refs/pull/#{$2}/head",
287
+ "ref" => nil,
288
+ "tag" => nil,
289
+ }
290
+ else
291
+ repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
292
+ "https://github.com/#{repo_name}.git"
293
+ end
283
294
  end
284
295
 
285
296
  git_source(:gist) do |repo_name|
@@ -365,7 +376,11 @@ repo_name ||= user_name
365
376
 
366
377
  git_name = (git_names & opts.keys).last
367
378
  if @git_sources[git_name]
368
- opts["git"] = @git_sources[git_name].call(opts[git_name])
379
+ git_opts = @git_sources[git_name].call(opts[git_name])
380
+ git_opts = { "git" => git_opts } if git_opts.is_a?(String)
381
+ opts.merge!(git_opts) do |key, _gemfile_value, _git_source_value|
382
+ raise GemfileError, %(The :#{key} option can't be used with `#{git_name}: #{opts[git_name].inspect}`)
383
+ end
369
384
  end
370
385
 
371
386
  %w[git path].each do |type|
@@ -447,8 +462,21 @@ repo_name ||= user_name
447
462
  end
448
463
 
449
464
  def check_rubygems_source_safety
450
- return unless @sources.aggregate_global_source?
465
+ if @sources.implicit_global_source?
466
+ implicit_global_source_warning
467
+ elsif @sources.aggregate_global_source?
468
+ multiple_global_source_warning
469
+ end
470
+ end
471
+
472
+ def implicit_global_source_warning
473
+ Bundler::SharedHelpers.major_deprecation 2, "This Gemfile does not include an explicit global source. " \
474
+ "Not using an explicit global source may result in a different lockfile being generated depending on " \
475
+ "the gems you have installed locally before bundler is run. " \
476
+ "Instead, define a global source in your Gemfile like this: source \"https://rubygems.org\"."
477
+ end
451
478
 
479
+ def multiple_global_source_warning
452
480
  if Bundler.feature_flag.bundler_3_mode?
453
481
  msg = "This Gemfile contains multiple primary sources. " \
454
482
  "Each source after the first must include a block to indicate which gems " \
@@ -38,7 +38,10 @@ module Bundler
38
38
 
39
39
  # Replaces `ENV` with the bundler environment variables backed up
40
40
  def replace_with_backup
41
- ENV.replace(backup) unless Gem.win_platform?
41
+ unless Gem.win_platform?
42
+ ENV.replace(backup)
43
+ return
44
+ end
42
45
 
43
46
  # Fallback logic for Windows below to workaround
44
47
  # https://bugs.ruby-lang.org/issues/16798. Can be dropped once all