bundler 2.4.22 → 2.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (156) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +63 -0
  3. data/bundler.gemspec +4 -2
  4. data/exe/bundle +1 -10
  5. data/lib/bundler/build_metadata.rb +3 -3
  6. data/lib/bundler/capistrano.rb +1 -1
  7. data/lib/bundler/checksum.rb +254 -0
  8. data/lib/bundler/ci_detector.rb +75 -0
  9. data/lib/bundler/cli/add.rb +3 -3
  10. data/lib/bundler/cli/binstubs.rb +4 -4
  11. data/lib/bundler/cli/cache.rb +1 -1
  12. data/lib/bundler/cli/check.rb +1 -1
  13. data/lib/bundler/cli/common.rb +9 -1
  14. data/lib/bundler/cli/config.rb +8 -7
  15. data/lib/bundler/cli/console.rb +3 -2
  16. data/lib/bundler/cli/doctor.rb +2 -2
  17. data/lib/bundler/cli/exec.rb +1 -1
  18. data/lib/bundler/cli/gem.rb +28 -23
  19. data/lib/bundler/cli/info.rb +2 -13
  20. data/lib/bundler/cli/install.rb +5 -4
  21. data/lib/bundler/cli/issue.rb +1 -1
  22. data/lib/bundler/cli/lock.rb +4 -4
  23. data/lib/bundler/cli/open.rb +1 -1
  24. data/lib/bundler/cli/outdated.rb +6 -6
  25. data/lib/bundler/cli/plugin.rb +7 -14
  26. data/lib/bundler/cli/pristine.rb +38 -30
  27. data/lib/bundler/cli/show.rb +2 -2
  28. data/lib/bundler/cli/update.rb +5 -5
  29. data/lib/bundler/cli.rb +215 -263
  30. data/lib/bundler/compact_index_client/cache.rb +29 -9
  31. data/lib/bundler/compact_index_client/cache_file.rb +153 -0
  32. data/lib/bundler/compact_index_client/gem_parser.rb +7 -3
  33. data/lib/bundler/compact_index_client/updater.rb +79 -81
  34. data/lib/bundler/compact_index_client.rb +14 -7
  35. data/lib/bundler/constants.rb +1 -1
  36. data/lib/bundler/current_ruby.rb +5 -21
  37. data/lib/bundler/definition.rb +42 -15
  38. data/lib/bundler/dependency.rb +16 -12
  39. data/lib/bundler/digest.rb +2 -2
  40. data/lib/bundler/dsl.rb +46 -30
  41. data/lib/bundler/endpoint_specification.rb +5 -1
  42. data/lib/bundler/env.rb +1 -3
  43. data/lib/bundler/errors.rb +43 -0
  44. data/lib/bundler/fetcher/base.rb +3 -1
  45. data/lib/bundler/fetcher/compact_index.rb +4 -4
  46. data/lib/bundler/fetcher/downloader.rb +13 -11
  47. data/lib/bundler/fetcher/gem_remote_fetcher.rb +16 -0
  48. data/lib/bundler/fetcher/index.rb +1 -1
  49. data/lib/bundler/fetcher.rb +28 -25
  50. data/lib/bundler/friendly_errors.rb +5 -5
  51. data/lib/bundler/gem_helper.rb +1 -1
  52. data/lib/bundler/gem_helpers.rb +5 -2
  53. data/lib/bundler/graph.rb +9 -9
  54. data/lib/bundler/index.rb +1 -2
  55. data/lib/bundler/injector.rb +1 -1
  56. data/lib/bundler/inline.rb +3 -3
  57. data/lib/bundler/installer/gem_installer.rb +5 -5
  58. data/lib/bundler/installer/parallel_installer.rb +16 -8
  59. data/lib/bundler/installer/standalone.rb +2 -3
  60. data/lib/bundler/installer.rb +9 -9
  61. data/lib/bundler/lazy_specification.rb +24 -17
  62. data/lib/bundler/lockfile_generator.rb +9 -0
  63. data/lib/bundler/lockfile_parser.rb +81 -10
  64. data/lib/bundler/man/bundle-add.1 +3 -26
  65. data/lib/bundler/man/bundle-binstubs.1 +4 -16
  66. data/lib/bundler/man/bundle-cache.1 +3 -24
  67. data/lib/bundler/man/bundle-check.1 +3 -12
  68. data/lib/bundler/man/bundle-clean.1 +3 -10
  69. data/lib/bundler/man/bundle-config.1 +20 -211
  70. data/lib/bundler/man/bundle-config.1.ronn +6 -0
  71. data/lib/bundler/man/bundle-console.1 +4 -22
  72. data/lib/bundler/man/bundle-doctor.1 +4 -18
  73. data/lib/bundler/man/bundle-exec.1 +12 -73
  74. data/lib/bundler/man/bundle-gem.1 +13 -49
  75. data/lib/bundler/man/bundle-help.1 +3 -7
  76. data/lib/bundler/man/bundle-info.1 +3 -9
  77. data/lib/bundler/man/bundle-init.1 +3 -12
  78. data/lib/bundler/man/bundle-inject.1 +6 -19
  79. data/lib/bundler/man/bundle-install.1 +27 -125
  80. data/lib/bundler/man/bundle-install.1.ronn +1 -0
  81. data/lib/bundler/man/bundle-list.1 +4 -19
  82. data/lib/bundler/man/bundle-lock.1 +5 -29
  83. data/lib/bundler/man/bundle-open.1 +7 -27
  84. data/lib/bundler/man/bundle-outdated.1 +3 -55
  85. data/lib/bundler/man/bundle-outdated.1.ronn +1 -0
  86. data/lib/bundler/man/bundle-platform.1 +5 -27
  87. data/lib/bundler/man/bundle-plugin.1 +3 -29
  88. data/lib/bundler/man/bundle-pristine.1 +5 -16
  89. data/lib/bundler/man/bundle-remove.1 +4 -14
  90. data/lib/bundler/man/bundle-show.1 +3 -10
  91. data/lib/bundler/man/bundle-update.1 +18 -137
  92. data/lib/bundler/man/bundle-version.1 +3 -16
  93. data/lib/bundler/man/bundle-viz.1 +4 -16
  94. data/lib/bundler/man/bundle.1 +5 -44
  95. data/lib/bundler/man/gemfile.5 +24 -301
  96. data/lib/bundler/man/gemfile.5.ronn +4 -0
  97. data/lib/bundler/match_metadata.rb +4 -0
  98. data/lib/bundler/match_platform.rb +1 -1
  99. data/lib/bundler/plugin/api/source.rb +3 -2
  100. data/lib/bundler/plugin/installer.rb +1 -1
  101. data/lib/bundler/plugin.rb +3 -3
  102. data/lib/bundler/resolver/base.rb +1 -1
  103. data/lib/bundler/resolver/incompatibility.rb +1 -1
  104. data/lib/bundler/resolver/spec_group.rb +1 -4
  105. data/lib/bundler/resolver.rb +16 -16
  106. data/lib/bundler/ruby_dsl.rb +20 -12
  107. data/lib/bundler/ruby_version.rb +1 -1
  108. data/lib/bundler/rubygems_ext.rb +24 -50
  109. data/lib/bundler/rubygems_gem_installer.rb +6 -56
  110. data/lib/bundler/rubygems_integration.rb +25 -94
  111. data/lib/bundler/runtime.rb +2 -2
  112. data/lib/bundler/self_manager.rb +23 -7
  113. data/lib/bundler/settings.rb +27 -7
  114. data/lib/bundler/setup.rb +4 -1
  115. data/lib/bundler/shared_helpers.rb +35 -13
  116. data/lib/bundler/source/git/git_proxy.rb +15 -15
  117. data/lib/bundler/source/git.rb +4 -3
  118. data/lib/bundler/source/metadata.rb +15 -15
  119. data/lib/bundler/source/path.rb +7 -6
  120. data/lib/bundler/source/rubygems.rb +21 -14
  121. data/lib/bundler/source.rb +2 -0
  122. data/lib/bundler/spec_set.rb +38 -10
  123. data/lib/bundler/stub_specification.rb +1 -0
  124. data/lib/bundler/templates/Executable.bundler +1 -1
  125. data/lib/bundler/templates/newgem/README.md.tt +3 -3
  126. data/lib/bundler/templates/newgem/Rakefile.tt +2 -6
  127. data/lib/bundler/templates/newgem/ext/newgem/Cargo.toml.tt +1 -1
  128. data/lib/bundler/templates/newgem/standard.yml.tt +1 -1
  129. data/lib/bundler/ui/shell.rb +1 -1
  130. data/lib/bundler/vendor/connection_pool/.document +1 -0
  131. data/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb +1 -1
  132. data/lib/bundler/vendor/connection_pool/lib/connection_pool.rb +53 -6
  133. data/lib/bundler/vendor/fileutils/.document +1 -0
  134. data/lib/bundler/vendor/fileutils/lib/fileutils.rb +8 -20
  135. data/lib/bundler/vendor/net-http-persistent/.document +1 -0
  136. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/connection.rb +3 -3
  137. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/pool.rb +2 -2
  138. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/timed_stack_multi.rb +1 -1
  139. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb +34 -34
  140. data/lib/bundler/vendor/pub_grub/.document +1 -0
  141. data/lib/bundler/vendor/thor/.document +1 -0
  142. data/lib/bundler/vendor/tsort/.document +1 -0
  143. data/lib/bundler/vendor/tsort/lib/tsort.rb +3 -0
  144. data/lib/bundler/vendor/uri/.document +1 -0
  145. data/lib/bundler/vendor/uri/lib/uri/common.rb +256 -132
  146. data/lib/bundler/vendor/uri/lib/uri/generic.rb +1 -0
  147. data/lib/bundler/vendor/uri/lib/uri/rfc3986_parser.rb +95 -31
  148. data/lib/bundler/vendor/uri/lib/uri/version.rb +1 -1
  149. data/lib/bundler/vendored_net_http.rb +8 -0
  150. data/lib/bundler/vendored_persistent.rb +0 -4
  151. data/lib/bundler/vendored_timeout.rb +8 -0
  152. data/lib/bundler/version.rb +1 -1
  153. data/lib/bundler/vlad.rb +1 -1
  154. data/lib/bundler/yaml_serializer.rb +3 -3
  155. data/lib/bundler.rb +41 -32
  156. metadata +18 -5
@@ -18,7 +18,8 @@ module Bundler
18
18
  :platforms,
19
19
  :ruby_version,
20
20
  :lockfile,
21
- :gemfiles
21
+ :gemfiles,
22
+ :locked_checksums
22
23
  )
23
24
 
24
25
  # Given a gemfile and lockfile creates a Bundler definition
@@ -84,7 +85,7 @@ module Bundler
84
85
  @new_platform = nil
85
86
  @removed_platform = nil
86
87
 
87
- if lockfile && File.exist?(lockfile)
88
+ if lockfile_exists?
88
89
  @lockfile_contents = Bundler.read_file(lockfile)
89
90
  @locked_gems = LockfileParser.new(@lockfile_contents)
90
91
  @locked_platforms = @locked_gems.platforms
@@ -92,6 +93,7 @@ module Bundler
92
93
  @locked_bundler_version = @locked_gems.bundler_version
93
94
  @locked_ruby_version = @locked_gems.ruby_version
94
95
  @originally_locked_specs = SpecSet.new(@locked_gems.specs)
96
+ @locked_checksums = @locked_gems.checksums
95
97
 
96
98
  if unlock != true
97
99
  @locked_deps = @locked_gems.dependencies
@@ -112,6 +114,7 @@ module Bundler
112
114
  @originally_locked_specs = @locked_specs
113
115
  @locked_sources = []
114
116
  @locked_platforms = []
117
+ @locked_checksums = nil
115
118
  end
116
119
 
117
120
  locked_gem_sources = @locked_sources.select {|s| s.is_a?(Source::Rubygems) }
@@ -245,8 +248,9 @@ module Bundler
245
248
  end
246
249
 
247
250
  def filter_relevant(dependencies)
251
+ platforms_array = [generic_local_platform].freeze
248
252
  dependencies.select do |d|
249
- d.should_include? && !d.gem_platforms([generic_local_platform]).empty?
253
+ d.should_include? && !d.gem_platforms(platforms_array).empty?
250
254
  end
251
255
  end
252
256
 
@@ -270,9 +274,15 @@ module Bundler
270
274
 
271
275
  def dependencies_for(groups)
272
276
  groups.map!(&:to_sym)
273
- current_dependencies.reject do |d|
274
- (d.groups & groups).empty?
277
+ deps = current_dependencies # always returns a new array
278
+ deps.select! do |d|
279
+ if RUBY_VERSION >= "3.1"
280
+ d.groups.intersect?(groups)
281
+ else
282
+ !(d.groups & groups).empty?
283
+ end
275
284
  end
285
+ deps
276
286
  end
277
287
 
278
288
  # Resolve all the dependencies specified in Gemfile. It ensures that
@@ -302,6 +312,10 @@ module Bundler
302
312
  end
303
313
  end
304
314
 
315
+ def should_complete_platforms?
316
+ !lockfile_exists? && generic_local_platform_is_ruby? && !Bundler.settings[:force_ruby_platform]
317
+ end
318
+
305
319
  def spec_git_paths
306
320
  sources.git_sources.map {|s| File.realpath(s.path) if File.exist?(s.path) }.compact
307
321
  end
@@ -328,7 +342,11 @@ module Bundler
328
342
 
329
343
  preserve_unknown_sections ||= !updating_major && (Bundler.frozen_bundle? || !(unlocking? || @unlocking_bundler))
330
344
 
331
- return if file && File.exist?(file) && lockfiles_equal?(@lockfile_contents, contents, preserve_unknown_sections)
345
+ if file && File.exist?(file) && lockfiles_equal?(@lockfile_contents, contents, preserve_unknown_sections)
346
+ return if Bundler.frozen_bundle?
347
+ SharedHelpers.filesystem_access(file) { FileUtils.touch(file) }
348
+ return
349
+ end
332
350
 
333
351
  if Bundler.frozen_bundle?
334
352
  Bundler.ui.error "Cannot write a changed lockfile while frozen."
@@ -491,6 +509,10 @@ module Bundler
491
509
 
492
510
  private
493
511
 
512
+ def lockfile_exists?
513
+ lockfile && File.exist?(lockfile)
514
+ end
515
+
494
516
  def resolver
495
517
  @resolver ||= Resolver.new(resolution_packages, gem_version_promoter)
496
518
  end
@@ -510,7 +532,7 @@ module Bundler
510
532
  @resolution_packages ||= begin
511
533
  last_resolve = converge_locked_specs
512
534
  remove_invalid_platforms!(current_dependencies)
513
- packages = Resolver::Base.new(source_requirements, expanded_dependencies, last_resolve, @platforms, :locked_specs => @originally_locked_specs, :unlock => @unlock[:gems], :prerelease => gem_version_promoter.pre?)
535
+ packages = Resolver::Base.new(source_requirements, expanded_dependencies, last_resolve, @platforms, locked_specs: @originally_locked_specs, unlock: @unlock[:gems], prerelease: gem_version_promoter.pre?)
514
536
  additional_base_requirements_for_resolve(packages, last_resolve)
515
537
  end
516
538
  end
@@ -567,11 +589,12 @@ module Bundler
567
589
  end
568
590
 
569
591
  def start_resolution
570
- result = resolver.start
592
+ result = SpecSet.new(resolver.start)
571
593
 
572
594
  @resolved_bundler_version = result.find {|spec| spec.name == "bundler" }&.version
595
+ @platforms = result.complete_platforms!(platforms) if should_complete_platforms?
573
596
 
574
- SpecSet.new(SpecSet.new(result).for(dependencies, false, @platforms))
597
+ SpecSet.new(result.for(dependencies, false, @platforms))
575
598
  end
576
599
 
577
600
  def precompute_source_requirements_for_indirect_dependencies?
@@ -592,7 +615,7 @@ module Bundler
592
615
  end
593
616
 
594
617
  def current_ruby_platform_locked?
595
- return false unless generic_local_platform == Gem::Platform::RUBY
618
+ return false unless generic_local_platform_is_ruby?
596
619
  return false if Bundler.settings[:force_ruby_platform] && !@platforms.include?(Gem::Platform::RUBY)
597
620
 
598
621
  current_platform_locked?
@@ -657,8 +680,7 @@ module Bundler
657
680
  locked_index = Index.new
658
681
  locked_index.use(@locked_specs.select {|s| source.can_lock?(s) })
659
682
 
660
- # order here matters, since Index#== is checking source.specs.include?(locked_index)
661
- locked_index != source.specs
683
+ !locked_index.subset?(source.specs)
662
684
  rescue PathError, GitError => e
663
685
  Bundler.ui.debug "Assuming that #{source} has not changed since fetching its specs errored (#{e})"
664
686
  false
@@ -750,6 +772,11 @@ module Bundler
750
772
  changes = sources.replace_sources!(@locked_sources)
751
773
 
752
774
  sources.all_sources.each do |source|
775
+ # has to be done separately, because we want to keep the locked checksum
776
+ # store for a source, even when doing a full update
777
+ if @locked_checksums && @locked_gems && locked_source = @locked_gems.sources.find {|s| s == source && !s.equal?(source) }
778
+ source.checksum_store.merge!(locked_source.checksum_store)
779
+ end
753
780
  # If the source is unlockable and the current command allows an unlock of
754
781
  # the source (for example, you are doing a `bundle update <foo>` of a git-pinned
755
782
  # gem), unlock it. For git sources, this means to unlock the revision, which
@@ -893,9 +920,9 @@ module Bundler
893
920
  source_requirements = if precompute_source_requirements_for_indirect_dependencies?
894
921
  all_requirements = source_map.all_requirements
895
922
  all_requirements = pin_locally_available_names(all_requirements) if @prefer_local
896
- { :default => default_source }.merge(all_requirements)
923
+ { default: default_source }.merge(all_requirements)
897
924
  else
898
- { :default => Source::RubygemsAggregate.new(sources, source_map) }.merge(source_map.direct_requirements)
925
+ { default: Source::RubygemsAggregate.new(sources, source_map) }.merge(source_map.direct_requirements)
899
926
  end
900
927
  source_requirements.merge!(source_map.locked_requirements) unless @remote
901
928
  metadata_dependencies.each do |dep|
@@ -959,7 +986,7 @@ module Bundler
959
986
  def remove_invalid_platforms!(dependencies)
960
987
  return if Bundler.frozen_bundle?
961
988
 
962
- platforms.each do |platform|
989
+ platforms.reverse_each do |platform|
963
990
  next if local_platform == platform ||
964
991
  (@new_platform && platforms.last == platform) ||
965
992
  @path_changes ||
@@ -9,18 +9,19 @@ module Bundler
9
9
  attr_reader :autorequire
10
10
  attr_reader :groups, :platforms, :gemfile, :path, :git, :github, :branch, :ref
11
11
 
12
- ALL_RUBY_VERSIONS = ((18..27).to_a + (30..33).to_a).freeze
12
+ ALL_RUBY_VERSIONS = (18..27).to_a.concat((30..34).to_a).freeze
13
13
  PLATFORM_MAP = {
14
- :ruby => [Gem::Platform::RUBY, ALL_RUBY_VERSIONS],
15
- :mri => [Gem::Platform::RUBY, ALL_RUBY_VERSIONS],
16
- :rbx => [Gem::Platform::RUBY],
17
- :truffleruby => [Gem::Platform::RUBY],
18
- :jruby => [Gem::Platform::JAVA, [18, 19]],
19
- :windows => [Gem::Platform::WINDOWS, ALL_RUBY_VERSIONS],
20
- :mswin => [Gem::Platform::MSWIN, ALL_RUBY_VERSIONS],
21
- :mswin64 => [Gem::Platform::MSWIN64, ALL_RUBY_VERSIONS - [18]],
22
- :mingw => [Gem::Platform::MINGW, ALL_RUBY_VERSIONS],
23
- :x64_mingw => [Gem::Platform::X64_MINGW, ALL_RUBY_VERSIONS - [18, 19]],
14
+ ruby: [Gem::Platform::RUBY, ALL_RUBY_VERSIONS],
15
+ mri: [Gem::Platform::RUBY, ALL_RUBY_VERSIONS],
16
+ rbx: [Gem::Platform::RUBY],
17
+ truffleruby: [Gem::Platform::RUBY],
18
+ jruby: [Gem::Platform::JAVA, [18, 19]],
19
+ windows: [Gem::Platform::WINDOWS, ALL_RUBY_VERSIONS],
20
+ # deprecated
21
+ mswin: [Gem::Platform::MSWIN, ALL_RUBY_VERSIONS],
22
+ mswin64: [Gem::Platform::MSWIN64, ALL_RUBY_VERSIONS - [18]],
23
+ mingw: [Gem::Platform::MINGW, ALL_RUBY_VERSIONS],
24
+ x64_mingw: [Gem::Platform::X64_MINGW, ALL_RUBY_VERSIONS - [18, 19]],
24
25
  }.each_with_object({}) do |(platform, spec), hash|
25
26
  hash[platform] = spec[0]
26
27
  spec[1]&.each {|version| hash[:"#{platform}_#{version}"] = spec[0] }
@@ -47,10 +48,13 @@ module Bundler
47
48
  @autorequire = Array(options["require"] || []) if options.key?("require")
48
49
  end
49
50
 
51
+ RUBY_PLATFORM_ARRAY = [Gem::Platform::RUBY].freeze
52
+ private_constant :RUBY_PLATFORM_ARRAY
53
+
50
54
  # Returns the platforms this dependency is valid for, in the same order as
51
55
  # passed in the `valid_platforms` parameter
52
56
  def gem_platforms(valid_platforms)
53
- return [Gem::Platform::RUBY] if force_ruby_platform
57
+ return RUBY_PLATFORM_ARRAY if force_ruby_platform
54
58
  return valid_platforms if @platforms.empty?
55
59
 
56
60
  valid_platforms.select {|p| expanded_platforms.include?(GemHelpers.generic(p)) }
@@ -26,7 +26,7 @@ module Bundler
26
26
  end
27
27
  a, b, c, d, e = *words
28
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)
29
+ w[i] = SHA1_MASK & rotate((w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16]), 1)
30
30
  end
31
31
  0.upto(79) do |i|
32
32
  case i
@@ -50,7 +50,7 @@ module Bundler
50
50
  words.map!.with_index {|word, index| SHA1_MASK & (word + mutated[index]) }
51
51
  end
52
52
 
53
- words.pack("N*").unpack("H*").first
53
+ words.pack("N*").unpack1("H*")
54
54
  end
55
55
 
56
56
  private
data/lib/bundler/dsl.rb CHANGED
@@ -18,9 +18,9 @@ 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 force_ruby_platform].freeze
20
20
 
21
- GITHUB_PULL_REQUEST_URL = %r{\Ahttps://github\.com/([A-Za-z0-9_\-\.]+/[A-Za-z0-9_\-\.]+)/pull/(\d+)\z}.freeze
21
+ GITHUB_PULL_REQUEST_URL = %r{\Ahttps://github\.com/([A-Za-z0-9_\-\.]+/[A-Za-z0-9_\-\.]+)/pull/(\d+)\z}
22
22
 
23
- attr_reader :gemspecs
23
+ attr_reader :gemspecs, :gemfile
24
24
  attr_accessor :dependencies
25
25
 
26
26
  def initialize
@@ -46,7 +46,7 @@ module Bundler
46
46
  @gemfile = expanded_gemfile_path
47
47
  @gemfiles << expanded_gemfile_path
48
48
  contents ||= Bundler.read_file(@gemfile.to_s)
49
- instance_eval(contents.dup.tap {|x| x.untaint if RUBY_VERSION < "2.7" }, gemfile.to_s, 1)
49
+ instance_eval(contents, gemfile.to_s, 1)
50
50
  rescue Exception => e # rubocop:disable Lint/RescueException
51
51
  message = "There was an error " \
52
52
  "#{e.is_a?(GemfileEvalError) ? "evaluating" : "parsing"} " \
@@ -76,11 +76,11 @@ module Bundler
76
76
 
77
77
  @gemspecs << spec
78
78
 
79
- gem spec.name, :name => spec.name, :path => path, :glob => glob
79
+ gem spec.name, name: spec.name, path: path, glob: glob
80
80
 
81
81
  group(development_group) do
82
82
  spec.development_dependencies.each do |dep|
83
- gem dep.name, *(dep.requirement.as_list + [:type => :development])
83
+ gem dep.name, *(dep.requirement.as_list + [type: :development])
84
84
  end
85
85
  end
86
86
  when 0
@@ -102,38 +102,45 @@ module Bundler
102
102
 
103
103
  # if there's already a dependency with this name we try to prefer one
104
104
  if current = @dependencies.find {|d| d.name == dep.name }
105
+ # Always prefer the dependency from the Gemfile
105
106
  deleted_dep = @dependencies.delete(current) if current.type == :development
106
107
 
107
- unless deleted_dep
108
- if current.requirement != dep.requirement
109
- return if dep.type == :development
108
+ if current.requirement != dep.requirement
109
+ current_requirement_open = current.requirements_list.include?(">= 0")
110
110
 
111
+ if current.type == :development
112
+ unless current_requirement_open || dep.type == :development
113
+ Bundler.ui.warn "A gemspec development dependency (#{dep.name}, #{current.requirement}) is being overridden by a Gemfile dependency (#{dep.name}, #{dep.requirement}).\n" \
114
+ "This behaviour may change in the future. Please remove either of them, or make sure they both have the same requirement\n" \
115
+ end
116
+ else
111
117
  update_prompt = ""
112
118
 
113
119
  if File.basename(@gemfile) == Injector::INJECTED_GEMS
114
- if dep.requirements_list.include?(">= 0") && !current.requirements_list.include?(">= 0")
120
+ if dep.requirements_list.include?(">= 0") && !current_requirement_open
115
121
  update_prompt = ". Gem already added"
116
122
  else
117
123
  update_prompt = ". If you want to update the gem version, run `bundle update #{current.name}`"
118
124
 
119
- update_prompt += ". You may also need to change the version requirement specified in the Gemfile if it's too restrictive." unless current.requirements_list.include?(">= 0")
125
+ update_prompt += ". You may also need to change the version requirement specified in the Gemfile if it's too restrictive." unless current_requirement_open
120
126
  end
121
127
  end
122
128
 
123
129
  raise GemfileError, "You cannot specify the same gem twice with different version requirements.\n" \
124
- "You specified: #{current.name} (#{current.requirement}) and #{dep.name} (#{dep.requirement})" \
125
- "#{update_prompt}"
126
- elsif current.source != dep.source
127
- return if dep.type == :development
128
- raise GemfileError, "You cannot specify the same gem twice coming from different sources.\n" \
129
- "You specified that #{dep.name} (#{dep.requirement}) should come from " \
130
- "#{current.source || "an unspecified source"} and #{dep.source}\n"
131
- else
132
- Bundler.ui.warn "Your Gemfile lists the gem #{current.name} (#{current.requirement}) more than once.\n" \
133
- "You should probably keep only one of them.\n" \
134
- "Remove any duplicate entries and specify the gem only once.\n" \
135
- "While it's not a problem now, it could cause errors if you change the version of one of them later."
130
+ "You specified: #{current.name} (#{current.requirement}) and #{dep.name} (#{dep.requirement})" \
131
+ "#{update_prompt}"
136
132
  end
133
+ elsif current.type == :development || dep.type == :development
134
+ return if deleted_dep.nil?
135
+ elsif current.source != dep.source
136
+ raise GemfileError, "You cannot specify the same gem twice coming from different sources.\n" \
137
+ "You specified that #{dep.name} (#{dep.requirement}) should come from " \
138
+ "#{current.source || "an unspecified source"} and #{dep.source}\n"
139
+ else
140
+ Bundler.ui.warn "Your Gemfile lists the gem #{current.name} (#{current.requirement}) more than once.\n" \
141
+ "You should probably keep only one of them.\n" \
142
+ "Remove any duplicate entries and specify the gem only once.\n" \
143
+ "While it's not a problem now, it could cause errors if you change the version of one of them later."
137
144
  end
138
145
  end
139
146
 
@@ -397,13 +404,11 @@ module Bundler
397
404
  end
398
405
 
399
406
  def validate_keys(command, opts, valid_keys)
400
- invalid_keys = opts.keys - valid_keys
401
-
402
- git_source = opts.keys & @git_sources.keys.map(&:to_s)
403
- if opts["branch"] && !(opts["git"] || opts["github"] || git_source.any?)
407
+ if opts["branch"] && !(opts["git"] || opts["github"] || (opts.keys & @git_sources.keys.map(&:to_s)).any?)
404
408
  raise GemfileError, %(The `branch` option for `#{command}` is not allowed. Only gems with a git source can specify a branch)
405
409
  end
406
410
 
411
+ invalid_keys = opts.keys - valid_keys
407
412
  return true unless invalid_keys.any?
408
413
 
409
414
  message = String.new
@@ -422,9 +427,13 @@ module Bundler
422
427
  def normalize_source(source)
423
428
  case source
424
429
  when :gemcutter, :rubygems, :rubyforge
425
- Bundler::SharedHelpers.major_deprecation 2, "The source :#{source} is deprecated because HTTP " \
426
- "requests are insecure.\nPlease change your source to 'https://" \
427
- "rubygems.org' if possible, or 'http://rubygems.org' if not."
430
+ message =
431
+ "The source :#{source} is deprecated because HTTP requests are insecure.\n" \
432
+ "Please change your source to 'https://rubygems.org' if possible, or 'http://rubygems.org' if not."
433
+ removed_message =
434
+ "The source :#{source} is disallowed because HTTP requests are insecure.\n" \
435
+ "Please change your source to 'https://rubygems.org' if possible, or 'http://rubygems.org' if not."
436
+ Bundler::SharedHelpers.major_deprecation 2, message, removed_message: removed_message
428
437
  "http://rubygems.org"
429
438
  when String
430
439
  source
@@ -469,10 +478,17 @@ module Bundler
469
478
  "should come from that source"
470
479
  raise GemfileEvalError, msg
471
480
  else
472
- Bundler::SharedHelpers.major_deprecation 2, "Your Gemfile contains multiple global sources. " \
481
+ message =
482
+ "Your Gemfile contains multiple global sources. " \
473
483
  "Using `source` more than once without a block is a security risk, and " \
474
484
  "may result in installing unexpected gems. To resolve this warning, use " \
475
485
  "a block to indicate which gems should come from the secondary source."
486
+ removed_message =
487
+ "Your Gemfile contains multiple global sources. " \
488
+ "Using `source` more than once without a block is a security risk, and " \
489
+ "may result in installing unexpected gems. To resolve this error, use " \
490
+ "a block to indicate which gems should come from the secondary source."
491
+ Bundler::SharedHelpers.major_deprecation 2, message, removed_message: removed_message
476
492
  end
477
493
  end
478
494
 
@@ -125,7 +125,11 @@ module Bundler
125
125
  next unless v
126
126
  case k.to_s
127
127
  when "checksum"
128
- @checksum = v.last
128
+ begin
129
+ @checksum = Checksum.from_api(v.last, @spec_fetcher.uri)
130
+ rescue ArgumentError => e
131
+ raise ArgumentError, "Invalid checksum for #{full_name}: #{e.message}"
132
+ end
129
133
  when "rubygems"
130
134
  @required_rubygems_version = Gem::Requirement.new(v)
131
135
  when "ruby"
data/lib/bundler/env.rb CHANGED
@@ -69,9 +69,7 @@ module Bundler
69
69
  end
70
70
 
71
71
  def self.ruby_version
72
- str = String.new(RUBY_VERSION)
73
- str << "p#{RUBY_PATCHLEVEL}" if defined? RUBY_PATCHLEVEL
74
- str << " (#{RUBY_RELEASE_DATE} revision #{RUBY_REVISION}) [#{Gem::Platform.local}]"
72
+ "#{RUBY_VERSION}p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE} revision #{RUBY_REVISION}) [#{Gem::Platform.local}]"
75
73
  end
76
74
 
77
75
  def self.git_version
@@ -52,6 +52,49 @@ module Bundler
52
52
  class GemfileEvalError < GemfileError; end
53
53
  class MarshalError < StandardError; end
54
54
 
55
+ class ChecksumMismatchError < SecurityError
56
+ def initialize(lock_name, existing, checksum)
57
+ @lock_name = lock_name
58
+ @existing = existing
59
+ @checksum = checksum
60
+ end
61
+
62
+ def message
63
+ <<~MESSAGE
64
+ Bundler found mismatched checksums. This is a potential security risk.
65
+ #{@lock_name} #{@existing.to_lock}
66
+ from #{@existing.sources.join("\n and ")}
67
+ #{@lock_name} #{@checksum.to_lock}
68
+ from #{@checksum.sources.join("\n and ")}
69
+
70
+ #{mismatch_resolution_instructions}
71
+ To ignore checksum security warnings, disable checksum validation with
72
+ `bundle config set --local disable_checksum_validation true`
73
+ MESSAGE
74
+ end
75
+
76
+ def mismatch_resolution_instructions
77
+ removable, remote = [@existing, @checksum].partition(&:removable?)
78
+ case removable.size
79
+ when 0
80
+ msg = +"Mismatched checksums each have an authoritative source:\n"
81
+ msg << " 1. #{@existing.sources.reject(&:removable?).map(&:to_s).join(" and ")}\n"
82
+ msg << " 2. #{@checksum.sources.reject(&:removable?).map(&:to_s).join(" and ")}\n"
83
+ msg << "You may need to alter your Gemfile sources to resolve this issue.\n"
84
+ when 1
85
+ msg = +"If you trust #{remote.first.sources.first}, to resolve this issue you can:\n"
86
+ msg << removable.first.removal_instructions
87
+ when 2
88
+ msg = +"To resolve this issue you can either:\n"
89
+ msg << @checksum.removal_instructions
90
+ msg << "or if you are sure that the new checksum from #{@checksum.sources.first} is correct:\n"
91
+ msg << @existing.removal_instructions
92
+ end
93
+ end
94
+
95
+ status_code(37)
96
+ end
97
+
55
98
  class PermissionError < BundlerError
56
99
  def initialize(path, permission_type = :write)
57
100
  @path = path
@@ -6,12 +6,14 @@ module Bundler
6
6
  attr_reader :downloader
7
7
  attr_reader :display_uri
8
8
  attr_reader :remote
9
+ attr_reader :gem_remote_fetcher
9
10
 
10
- def initialize(downloader, remote, display_uri)
11
+ def initialize(downloader, remote, display_uri, gem_remote_fetcher)
11
12
  raise "Abstract class" if self.class == Base
12
13
  @downloader = downloader
13
14
  @remote = remote
14
15
  @display_uri = display_uri
16
+ @gem_remote_fetcher = gem_remote_fetcher
15
17
  end
16
18
 
17
19
  def remote_uri
@@ -13,7 +13,7 @@ module Bundler
13
13
  undef_method(method_name)
14
14
  define_method(method_name) do |*args, &blk|
15
15
  method.bind(self).call(*args, &blk)
16
- rescue NetworkDownError, CompactIndexClient::Updater::MisMatchedChecksumError => e
16
+ rescue NetworkDownError, CompactIndexClient::Updater::MismatchedChecksumError => e
17
17
  raise HTTPError, e.message
18
18
  rescue AuthenticationRequiredError, BadAuthenticationError
19
19
  # Fail since we got a 401 from the server.
@@ -44,7 +44,7 @@ module Bundler
44
44
  @bundle_worker = nil # reset it. Not sure if necessary
45
45
  serial_compact_index_client.dependencies(remaining_gems)
46
46
  end
47
- next_gems = deps.map {|d| d[3].map(&:first).flatten(1) }.flatten(1).uniq
47
+ next_gems = deps.flat_map {|d| d[3].flat_map(&:first) }.uniq
48
48
  deps.each {|dep| gem_info << dep }
49
49
  complete_gems.concat(deps.map(&:first)).uniq!
50
50
  remaining_gems = next_gems - complete_gems
@@ -62,7 +62,7 @@ module Bundler
62
62
  end
63
63
  # Read info file checksums out of /versions, so we can know if gems are up to date
64
64
  compact_index_client.update_and_parse_checksums!
65
- rescue CompactIndexClient::Updater::MisMatchedChecksumError => e
65
+ rescue CompactIndexClient::Updater::MismatchedChecksumError => e
66
66
  Bundler.ui.debug(e.message)
67
67
  nil
68
68
  end
@@ -121,7 +121,7 @@ module Bundler
121
121
  rescue NetworkDownError => e
122
122
  raise unless Bundler.feature_flag.allow_offline_install? && headers["If-None-Match"]
123
123
  ui.warn "Using the cached data for the new index because of a network error: #{e}"
124
- Net::HTTPNotModified.new(nil, nil, nil)
124
+ Gem::Net::HTTPNotModified.new(nil, nil, nil)
125
125
  end
126
126
  end
127
127
  end
@@ -20,33 +20,35 @@ module Bundler
20
20
  Bundler.ui.debug("HTTP #{response.code} #{response.message} #{filtered_uri}")
21
21
 
22
22
  case response
23
- when Net::HTTPSuccess, Net::HTTPNotModified
23
+ when Gem::Net::HTTPSuccess, Gem::Net::HTTPNotModified
24
24
  response
25
- when Net::HTTPRedirection
25
+ when Gem::Net::HTTPRedirection
26
26
  new_uri = Bundler::URI.parse(response["location"])
27
27
  if new_uri.host == uri.host
28
28
  new_uri.user = uri.user
29
29
  new_uri.password = uri.password
30
30
  end
31
31
  fetch(new_uri, headers, counter + 1)
32
- when Net::HTTPRequestedRangeNotSatisfiable
32
+ when Gem::Net::HTTPRequestedRangeNotSatisfiable
33
33
  new_headers = headers.dup
34
34
  new_headers.delete("Range")
35
35
  new_headers["Accept-Encoding"] = "gzip"
36
36
  fetch(uri, new_headers)
37
- when Net::HTTPRequestEntityTooLarge
37
+ when Gem::Net::HTTPRequestEntityTooLarge
38
38
  raise FallbackError, response.body
39
- when Net::HTTPTooManyRequests
39
+ when Gem::Net::HTTPTooManyRequests
40
40
  raise TooManyRequestsError, response.body
41
- when Net::HTTPUnauthorized
41
+ when Gem::Net::HTTPUnauthorized
42
42
  raise BadAuthenticationError, uri.host if uri.userinfo
43
43
  raise AuthenticationRequiredError, uri.host
44
- when Net::HTTPForbidden
44
+ when Gem::Net::HTTPForbidden
45
45
  raise AuthenticationForbiddenError, uri.host
46
- when Net::HTTPNotFound
47
- raise FallbackError, "Net::HTTPNotFound: #{filtered_uri}"
46
+ when Gem::Net::HTTPNotFound
47
+ raise FallbackError, "Gem::Net::HTTPNotFound: #{filtered_uri}"
48
48
  else
49
- raise HTTPError, "#{response.class}#{": #{response.body}" unless response.body.empty?}"
49
+ message = "Gem::#{response.class.name.gsub(/\AGem::/, "")}"
50
+ message += ": #{response.body}" unless response.body.empty?
51
+ raise HTTPError, message
50
52
  end
51
53
  end
52
54
 
@@ -56,7 +58,7 @@ module Bundler
56
58
  filtered_uri = URICredentialsFilter.credential_filtered_uri(uri)
57
59
 
58
60
  Bundler.ui.debug "HTTP GET #{filtered_uri}"
59
- req = Net::HTTP::Get.new uri.request_uri, headers
61
+ req = Gem::Net::HTTP::Get.new uri.request_uri, headers
60
62
  if uri.user
61
63
  user = CGI.unescape(uri.user)
62
64
  password = uri.password ? CGI.unescape(uri.password) : nil
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rubygems/remote_fetcher"
4
+
5
+ module Bundler
6
+ class Fetcher
7
+ class GemRemoteFetcher < Gem::RemoteFetcher
8
+ def request(*args)
9
+ super do |req|
10
+ req.delete("User-Agent") if headers["User-Agent"]
11
+ yield req if block_given?
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -6,7 +6,7 @@ module Bundler
6
6
  class Fetcher
7
7
  class Index < Base
8
8
  def specs(_gem_names)
9
- Bundler.rubygems.fetch_all_remote_specs(remote)
9
+ Bundler.rubygems.fetch_all_remote_specs(remote, gem_remote_fetcher)
10
10
  rescue Gem::RemoteFetcher::FetchError => e
11
11
  case e.message
12
12
  when /certificate verify failed/