bundler 4.0.22 → 4.1.0.beta2

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 (200) hide show
  1. checksums.yaml +4 -4
  2. data/{lib/bundler/vendor/connection_pool/LICENSE → MIT.txt} +8 -6
  3. data/bundler.gemspec +23 -6
  4. data/lib/bundler/build_metadata.rb +2 -2
  5. data/lib/bundler/checksum.rb +40 -25
  6. data/lib/bundler/ci_detector.rb +1 -1
  7. data/lib/bundler/cli/add.rb +3 -4
  8. data/lib/bundler/cli/cache.rb +4 -0
  9. data/lib/bundler/cli/clean.rb +5 -0
  10. data/lib/bundler/cli/common.rb +41 -1
  11. data/lib/bundler/cli/config.rb +4 -2
  12. data/lib/bundler/cli/doctor/ssl.rb +4 -8
  13. data/lib/bundler/cli/gem.rb +0 -17
  14. data/lib/bundler/cli/info.rb +1 -1
  15. data/lib/bundler/cli/install.rb +4 -5
  16. data/lib/bundler/cli/lock.rb +15 -5
  17. data/lib/bundler/cli/open.rb +7 -1
  18. data/lib/bundler/cli/outdated.rb +16 -13
  19. data/lib/bundler/cli/pristine.rb +2 -1
  20. data/lib/bundler/cli/show.rb +5 -1
  21. data/lib/bundler/cli/update.rb +20 -11
  22. data/lib/bundler/cli.rb +15 -9
  23. data/lib/bundler/constants.rb +1 -1
  24. data/lib/bundler/definition.rb +102 -15
  25. data/lib/bundler/dependency.rb +4 -0
  26. data/lib/bundler/dsl.rb +137 -13
  27. data/lib/bundler/endpoint_specification.rb +51 -14
  28. data/lib/bundler/env.rb +7 -24
  29. data/lib/bundler/errors.rb +34 -5
  30. data/lib/bundler/fetcher/compact_index.rb +14 -5
  31. data/lib/bundler/fetcher/connection_pools.rb +150 -0
  32. data/lib/bundler/fetcher/downloader.rb +35 -16
  33. data/lib/bundler/fetcher.rb +8 -47
  34. data/lib/bundler/gem_helper.rb +1 -1
  35. data/lib/bundler/injector.rb +3 -3
  36. data/lib/bundler/inline.rb +44 -4
  37. data/lib/bundler/installer/parallel_installer.rb +27 -31
  38. data/lib/bundler/installer.rb +21 -7
  39. data/lib/bundler/lazy_specification.rb +45 -18
  40. data/lib/bundler/lockfile_generator.rb +24 -2
  41. data/lib/bundler/lockfile_parser.rb +44 -6
  42. data/lib/bundler/man/bundle-add.1 +7 -4
  43. data/lib/bundler/man/bundle-add.1.ronn +8 -4
  44. data/lib/bundler/man/bundle-binstubs.1 +1 -1
  45. data/lib/bundler/man/bundle-cache.1 +2 -2
  46. data/lib/bundler/man/bundle-cache.1.ronn +2 -2
  47. data/lib/bundler/man/bundle-check.1 +1 -1
  48. data/lib/bundler/man/bundle-clean.1 +1 -1
  49. data/lib/bundler/man/bundle-config.1 +38 -6
  50. data/lib/bundler/man/bundle-config.1.ronn +143 -10
  51. data/lib/bundler/man/bundle-console.1 +1 -1
  52. data/lib/bundler/man/bundle-doctor.1 +1 -1
  53. data/lib/bundler/man/bundle-env.1 +1 -1
  54. data/lib/bundler/man/bundle-exec.1 +1 -1
  55. data/lib/bundler/man/bundle-fund.1 +1 -1
  56. data/lib/bundler/man/bundle-gem.1 +1 -1
  57. data/lib/bundler/man/bundle-help.1 +1 -1
  58. data/lib/bundler/man/bundle-info.1 +5 -2
  59. data/lib/bundler/man/bundle-info.1.ronn +4 -0
  60. data/lib/bundler/man/bundle-init.1 +1 -1
  61. data/lib/bundler/man/bundle-install.1 +2 -2
  62. data/lib/bundler/man/bundle-install.1.ronn +4 -4
  63. data/lib/bundler/man/bundle-issue.1 +1 -1
  64. data/lib/bundler/man/bundle-licenses.1 +1 -1
  65. data/lib/bundler/man/bundle-list.1 +1 -1
  66. data/lib/bundler/man/bundle-lock.1 +2 -2
  67. data/lib/bundler/man/bundle-lock.1.ronn +3 -2
  68. data/lib/bundler/man/bundle-open.1 +5 -2
  69. data/lib/bundler/man/bundle-open.1.ronn +4 -1
  70. data/lib/bundler/man/bundle-outdated.1 +1 -1
  71. data/lib/bundler/man/bundle-platform.1 +1 -1
  72. data/lib/bundler/man/bundle-plugin.1 +1 -1
  73. data/lib/bundler/man/bundle-pristine.1 +1 -1
  74. data/lib/bundler/man/bundle-remove.1 +1 -1
  75. data/lib/bundler/man/bundle-show.1 +5 -2
  76. data/lib/bundler/man/bundle-show.1.ronn +4 -0
  77. data/lib/bundler/man/bundle-update.1 +2 -2
  78. data/lib/bundler/man/bundle-update.1.ronn +2 -2
  79. data/lib/bundler/man/bundle-version.1 +1 -1
  80. data/lib/bundler/man/bundle.1 +1 -1
  81. data/lib/bundler/man/gemfile.5 +77 -1
  82. data/lib/bundler/man/gemfile.5.ronn +104 -0
  83. data/lib/bundler/match_metadata.rb +20 -0
  84. data/lib/bundler/match_platform.rb +25 -2
  85. data/lib/bundler/match_remote_metadata.rb +21 -6
  86. data/lib/bundler/materialization.rb +5 -2
  87. data/lib/bundler/override.rb +97 -0
  88. data/lib/bundler/plugin/dsl.rb +3 -5
  89. data/lib/bundler/plugin/events.rb +55 -13
  90. data/lib/bundler/plugin/index.rb +87 -11
  91. data/lib/bundler/plugin/installer/path.rb +1 -1
  92. data/lib/bundler/plugin/installer.rb +13 -5
  93. data/lib/bundler/plugin.rb +58 -30
  94. data/lib/bundler/remote_specification.rb +6 -3
  95. data/lib/bundler/resolver/base.rb +2 -1
  96. data/lib/bundler/resolver/incompatibility.rb +1 -1
  97. data/lib/bundler/resolver.rb +97 -23
  98. data/lib/bundler/ruby_version.rb +0 -2
  99. data/lib/bundler/rubygems_ext.rb +71 -1
  100. data/lib/bundler/rubygems_gem_installer.rb +41 -2
  101. data/lib/bundler/rubygems_integration.rb +7 -2
  102. data/lib/bundler/runtime.rb +69 -2
  103. data/lib/bundler/settings.rb +383 -56
  104. data/lib/bundler/shared_helpers.rb +2 -1
  105. data/lib/bundler/source/git/git_proxy.rb +31 -2
  106. data/lib/bundler/source/git.rb +49 -13
  107. data/lib/bundler/source/path/installer.rb +1 -1
  108. data/lib/bundler/source/rubygems/remote.rb +10 -5
  109. data/lib/bundler/source/rubygems.rb +48 -12
  110. data/lib/bundler/source/rubygems_aggregate.rb +1 -1
  111. data/lib/bundler/source_map.rb +7 -3
  112. data/lib/bundler/spec_set.rb +23 -7
  113. data/lib/bundler/stub_specification.rb +2 -1
  114. data/lib/bundler/templates/newgem/Gemfile.tt +4 -4
  115. data/lib/bundler/templates/newgem/newgem.gemspec.tt +3 -3
  116. data/lib/bundler/templates/newgem/spec/newgem_spec.rb.tt +1 -1
  117. data/lib/bundler/templates/newgem/test/minitest/test_newgem.rb.tt +1 -1
  118. data/lib/bundler/ui/shell.rb +0 -4
  119. data/lib/bundler/ui/silent.rb +0 -4
  120. data/lib/bundler/vendor/thor/lib/thor/actions/inject_into_file.rb +35 -2
  121. data/lib/bundler/vendor/thor/lib/thor/base.rb +2 -1
  122. data/lib/bundler/vendor/thor/lib/thor/shell/basic.rb +14 -6
  123. data/lib/bundler/vendor/thor/lib/thor/shell/color.rb +2 -0
  124. data/lib/bundler/vendor/thor/lib/thor/version.rb +1 -1
  125. data/lib/bundler/vendor/thor/lib/thor.rb +34 -1
  126. data/lib/bundler/vendored_pub_grub.rb +5 -2
  127. data/lib/bundler/vendored_securerandom.rb +11 -8
  128. data/lib/bundler/vendored_uri.rb +14 -14
  129. data/lib/bundler/version.rb +1 -1
  130. data/lib/bundler/worker.rb +3 -2
  131. data/lib/bundler.rb +1 -1
  132. data/lib/rubygems/compact_index_client/cache.rb +122 -0
  133. data/lib/rubygems/compact_index_client/cache_file.rb +147 -0
  134. data/lib/rubygems/compact_index_client/http_fetcher.rb +90 -0
  135. data/lib/rubygems/compact_index_client/parser.rb +90 -0
  136. data/lib/rubygems/compact_index_client/updater.rb +113 -0
  137. data/lib/rubygems/compact_index_client.rb +111 -0
  138. data/lib/rubygems/cooldown_settings.rb +57 -0
  139. data/lib/rubygems/credential_store/native/linux.rb +97 -0
  140. data/lib/rubygems/credential_store/native/macos.rb +88 -0
  141. data/lib/rubygems/credential_store/native/windows.rb +130 -0
  142. data/lib/rubygems/credential_store.rb +332 -0
  143. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/assignment.rb +1 -1
  144. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/basic_package_source.rb +7 -7
  145. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/failure_writer.rb +1 -1
  146. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/incompatibility.rb +4 -4
  147. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/package.rb +1 -1
  148. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/partial_solution.rb +1 -1
  149. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/rubygems.rb +3 -3
  150. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/solve_failure.rb +1 -1
  151. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/static_package_source.rb +3 -3
  152. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/strategy.rb +2 -2
  153. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/term.rb +1 -1
  154. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/version.rb +1 -1
  155. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/version_constraint.rb +3 -3
  156. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/version_range.rb +1 -1
  157. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/version_solver.rb +2 -2
  158. data/lib/{bundler → rubygems}/vendor/pub_grub/lib/pub_grub/version_union.rb +1 -1
  159. data/lib/rubygems/vendor/pub_grub/lib/pub_grub.rb +57 -0
  160. data/lib/{bundler → rubygems}/vendor/securerandom/lib/securerandom.rb +10 -6
  161. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/common.rb +119 -119
  162. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/file.rb +13 -13
  163. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/ftp.rb +24 -24
  164. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/generic.rb +132 -132
  165. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/http.rb +18 -18
  166. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/https.rb +4 -4
  167. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/ldap.rb +14 -14
  168. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/ldaps.rb +4 -4
  169. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/mailto.rb +21 -21
  170. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/rfc2396_parser.rb +39 -39
  171. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/rfc3986_parser.rb +14 -14
  172. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/version.rb +1 -1
  173. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/ws.rb +12 -12
  174. data/lib/{bundler → rubygems}/vendor/uri/lib/uri/wss.rb +4 -4
  175. data/lib/{bundler → rubygems}/vendor/uri/lib/uri.rb +35 -31
  176. data/lib/rubygems/yaml_serializer.rb +880 -0
  177. metadata +391 -381
  178. data/CHANGELOG.md +0 -5801
  179. data/LICENSE.md +0 -22
  180. data/README.md +0 -58
  181. data/lib/bundler/compact_index_client/cache.rb +0 -107
  182. data/lib/bundler/compact_index_client/cache_file.rb +0 -148
  183. data/lib/bundler/compact_index_client/parser.rb +0 -87
  184. data/lib/bundler/compact_index_client/updater.rb +0 -105
  185. data/lib/bundler/compact_index_client.rb +0 -92
  186. data/lib/bundler/vendor/connection_pool/lib/connection_pool/timed_stack.rb +0 -227
  187. data/lib/bundler/vendor/connection_pool/lib/connection_pool/version.rb +0 -3
  188. data/lib/bundler/vendor/connection_pool/lib/connection_pool/wrapper.rb +0 -56
  189. data/lib/bundler/vendor/connection_pool/lib/connection_pool.rb +0 -230
  190. data/lib/bundler/vendor/net-http-persistent/README.rdoc +0 -82
  191. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/connection.rb +0 -41
  192. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/pool.rb +0 -65
  193. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent/timed_stack_multi.rb +0 -80
  194. data/lib/bundler/vendor/net-http-persistent/lib/net/http/persistent.rb +0 -1153
  195. data/lib/bundler/vendor/pub_grub/lib/pub_grub.rb +0 -31
  196. data/lib/bundler/vendored_persistent.rb +0 -11
  197. data/lib/bundler/yaml_serializer.rb +0 -98
  198. /data/lib/{bundler → rubygems}/vendor/pub_grub/LICENSE.txt +0 -0
  199. /data/lib/{bundler → rubygems}/vendor/securerandom/COPYING +0 -0
  200. /data/lib/{bundler → rubygems}/vendor/uri/COPYING +0 -0
@@ -5,24 +5,33 @@ module Bundler
5
5
  class EndpointSpecification < Gem::Specification
6
6
  include MatchRemoteMetadata
7
7
 
8
- attr_reader :name, :version, :platform, :checksum, :created_at
8
+ attr_reader :name, :version, :platform, :checksum, :created_at, :content_address
9
9
  attr_writer :dependencies
10
10
  attr_accessor :remote, :locked_platform
11
11
 
12
- def initialize(name, version, platform, spec_fetcher, dependencies, metadata = nil)
12
+ def initialize(name, version, suffix, spec_fetcher, dependencies, metadata = nil)
13
13
  super()
14
14
  @name = name
15
15
  @version = Gem::Version.create version
16
- @platform = Gem::Platform.new(platform)
17
16
  @spec_fetcher = spec_fetcher
18
17
  @dependencies = nil
19
18
  @unbuilt_dependencies = dependencies
19
+ @content_address = nil
20
+ @required_platform = nil
20
21
 
21
22
  @loaded_from = nil
22
23
  @remote_specification = nil
23
24
  @locked_platform = nil
24
25
 
25
26
  parse_metadata(metadata)
27
+
28
+ if Gem::ContentAddress.content_addressed_row?(suffix, @required_platform, @required_ruby_version)
29
+ @content_address = suffix
30
+ @platform = @required_platform
31
+ @required_rubygems_version ||= Gem::Requirement.default
32
+ else
33
+ @platform = Gem::Platform.new(suffix)
34
+ end
26
35
  end
27
36
 
28
37
  def insecurely_materialized?
@@ -147,18 +156,21 @@ module Bundler
147
156
  private
148
157
 
149
158
  def _remote_specification
150
- @_remote_specification ||= @spec_fetcher.fetch_spec([@name, @version, @platform])
159
+ suffix = @content_address || @platform
160
+ @_remote_specification ||= @spec_fetcher.fetch_spec([@name, @version, suffix])
151
161
  end
152
162
 
153
163
  def local_specification_path
154
- "#{base_dir}/specifications/#{full_name}.gemspec"
164
+ File.join(Gem::SpecificationRecord.specification_dir_for(self, base_dir),
165
+ "#{full_name}.gemspec")
155
166
  end
156
167
 
157
168
  def parse_metadata(data)
169
+ @created_at = nil
170
+
158
171
  unless data
159
172
  @required_ruby_version = nil
160
173
  @required_rubygems_version = nil
161
- @created_at = nil
162
174
  return
163
175
  end
164
176
 
@@ -181,22 +193,47 @@ module Bundler
181
193
  when "ruby"
182
194
  @required_ruby_version = Gem::Requirement.new(v)
183
195
  when "created_at"
184
- value = v.is_a?(Array) ? v.last : v
185
- if value.is_a?(String)
186
- @created_at = begin
187
- Time.new(value)
188
- rescue ArgumentError
189
- nil
190
- end
191
- end
196
+ @created_at = parse_created_at(v.is_a?(Array) ? v.last : v)&.freeze
197
+ when "platform"
198
+ @required_platform = required_platform_from(Array(v).last)
192
199
  end
193
200
  end
194
201
  rescue StandardError => e
195
202
  raise GemspecError, "There was an error parsing the metadata for the gem #{name} (#{version}): #{e.class}\n#{e}\nThe metadata was #{data.inspect}"
196
203
  end
197
204
 
205
+ # Matches an ISO 8601 time zone designator at the end of a timestamp.
206
+ TIME_ZONE_SUFFIX = /(?:Z|z|[+-]\d{2}(?::?\d{2})?)\z/
207
+ private_constant :TIME_ZONE_SUFFIX
208
+
209
+ # See Gem::Cooldown::FOUR_DIGIT_YEAR.
210
+ FOUR_DIGIT_YEAR = /\A\d{4}-/
211
+ private_constant :FOUR_DIGIT_YEAR
212
+
213
+ # A timestamp without a time zone offset is read as UTC, because reading
214
+ # it as local time would shift the cooldown window by the environment's
215
+ # offset. Unparsable values and years outside four digits become nil so
216
+ # the cooldown fails open.
217
+ def parse_created_at(value)
218
+ return unless value.is_a?(String) && value.match?(FOUR_DIGIT_YEAR)
219
+
220
+ require "time"
221
+ begin
222
+ Time.iso8601(value.match?(TIME_ZONE_SUFFIX) ? value : "#{value}Z")
223
+ rescue ArgumentError
224
+ nil
225
+ end
226
+ end
227
+
198
228
  def build_dependency(name, requirements)
199
229
  Dependency.new(name, requirements)
200
230
  end
231
+
232
+ def required_platform_from(value)
233
+ value = value.to_s
234
+ return if value.empty?
235
+
236
+ Gem::Platform.new(value)
237
+ end
201
238
  end
202
239
  end
data/lib/bundler/env.rb CHANGED
@@ -17,9 +17,11 @@ module Bundler
17
17
  append_formatted_table("Environment", environment, out)
18
18
  append_formatted_table("Bundler Build Metadata", BuildMetadata.to_h, out)
19
19
 
20
- unless Bundler.settings.all.empty?
20
+ settings = Bundler.settings.all_including_stored_credentials
21
+
22
+ unless settings.empty?
21
23
  out << "\n## Bundler settings\n\n```\n"
22
- Bundler.settings.all.each do |setting|
24
+ settings.each do |setting|
23
25
  out << setting << "\n"
24
26
  Bundler.settings.pretty_values_for(setting).each do |line|
25
27
  out << " " << line << "\n"
@@ -78,17 +80,6 @@ module Bundler
78
80
  "not installed"
79
81
  end
80
82
 
81
- def self.version_of(script)
82
- return "not installed" unless Bundler.which(script)
83
- `#{script} --version`.chomp
84
- end
85
-
86
- def self.chruby_version
87
- return "not installed" unless Bundler.which("chruby-exec")
88
- `chruby-exec -- chruby --version`.
89
- sub(/.*^chruby: (#{Gem::Version::VERSION_PATTERN}).*/m, '\1')
90
- end
91
-
92
83
  def self.environment
93
84
  out = []
94
85
 
@@ -110,16 +101,8 @@ module Bundler
110
101
  out << [" Cert File", OpenSSL::X509::DEFAULT_CERT_FILE] if defined?(OpenSSL::X509::DEFAULT_CERT_FILE)
111
102
  out << [" Cert Dir", OpenSSL::X509::DEFAULT_CERT_DIR] if defined?(OpenSSL::X509::DEFAULT_CERT_DIR)
112
103
  end
113
- out << ["Tools"]
114
- out << [" Git", git_version]
115
- out << [" RVM", ENV.fetch("rvm_version") { version_of("rvm") }]
116
- out << [" rbenv", version_of("rbenv")]
117
- out << [" chruby", chruby_version]
118
-
119
- %w[rubygems-bundler open_gem].each do |name|
120
- specs = Bundler.rubygems.find_name(name)
121
- out << [" #{name}", "(#{specs.map(&:version).join(",")})"] unless specs.empty?
122
- end
104
+ out << ["Git", git_version]
105
+
123
106
  if (exe = caller_locations.last.absolute_path)&.match? %r{(exe|bin)/bundler?\z}
124
107
  shebang = File.read(exe).lines.first
125
108
  shebang.sub!(/^#!\s*/, "")
@@ -143,6 +126,6 @@ module Bundler
143
126
  out << "```\n"
144
127
  end
145
128
 
146
- private_class_method :read_file, :ruby_version, :git_version, :append_formatted_table, :version_of, :chruby_version
129
+ private_class_method :read_file, :ruby_version, :git_version, :append_formatted_table
147
130
  end
148
131
  end
@@ -131,7 +131,8 @@ module Bundler
131
131
  attr_reader :orig_exception
132
132
 
133
133
  def initialize(orig_exception, msg)
134
- full_message = msg + "\nGem Load Error is: #{orig_exception.message}\n"\
134
+ full_message = msg + "\nGem Load Error is:
135
+ #{orig_exception.full_message(highlight: false)}\n"\
135
136
  "Backtrace for gem load error is:\n"\
136
137
  "#{orig_exception.backtrace.join("\n")}\n"\
137
138
  "Bundler Error Backtrace:\n"
@@ -221,7 +222,9 @@ module Bundler
221
222
  class DirectoryRemovalError < BundlerError
222
223
  def initialize(orig_exception, msg)
223
224
  full_message = "#{msg}.\n" \
224
- "The underlying error was #{orig_exception.class}: #{orig_exception.message}, with backtrace:\n" \
225
+ "The underlying error was #{orig_exception.class}:
226
+ #{orig_exception.full_message(highlight: false)},
227
+ with backtrace:\n" \
225
228
  " #{orig_exception.backtrace.join("\n ")}\n\n" \
226
229
  "Bundler Error Backtrace:"
227
230
  super(full_message)
@@ -262,14 +265,40 @@ module Bundler
262
265
  class InvalidArgumentError < BundlerError; status_code(40); end
263
266
 
264
267
  class IncorrectLockfileDependencies < BundlerError
265
- attr_reader :spec
268
+ attr_reader :spec, :actual_dependencies, :lockfile_dependencies
266
269
 
267
- def initialize(spec)
270
+ def initialize(spec, actual_dependencies = nil, lockfile_dependencies = nil)
268
271
  @spec = spec
272
+ @actual_dependencies = actual_dependencies
273
+ @lockfile_dependencies = lockfile_dependencies
269
274
  end
270
275
 
271
276
  def message
272
- "Bundler found incorrect dependencies in the lockfile for #{spec.full_name}"
277
+ lines = ["Bundler found incorrect dependencies in the lockfile for #{spec.full_name}", ""]
278
+
279
+ if @actual_dependencies && @lockfile_dependencies
280
+ actual_by_name = @actual_dependencies.each_with_object({}) {|d, h| h[d.name] = d }
281
+ lockfile_by_name = @lockfile_dependencies.each_with_object({}) {|d, h| h[d.name] = d }
282
+
283
+ (actual_by_name.keys | lockfile_by_name.keys).sort.each do |name|
284
+ actual = actual_by_name[name]
285
+ lockfile = lockfile_by_name[name]
286
+ next if actual && lockfile && actual.requirement == lockfile.requirement
287
+
288
+ if actual && lockfile
289
+ lines << " #{name}: gemspec specifies #{actual.requirement}, lockfile has #{lockfile.requirement}"
290
+ elsif actual
291
+ lines << " #{name}: gemspec specifies #{actual.requirement}, not in lockfile"
292
+ else
293
+ lines << " #{name}: not in gemspec, lockfile has #{lockfile.requirement}"
294
+ end
295
+ end
296
+
297
+ lines << ""
298
+ end
299
+
300
+ lines << "Please run `bundle install` to regenerate the lockfile."
301
+ lines.join("\n")
273
302
  end
274
303
 
275
304
  status_code(41)
@@ -2,6 +2,15 @@
2
2
 
3
3
  require_relative "base"
4
4
  require_relative "../worker"
5
+ require "rubygems/compact_index_client"
6
+
7
+ # Route the client's filesystem access through Bundler's helper so
8
+ # permission problems raise friendly Bundler errors. The respond_to?
9
+ # check covers a host RubyGems that already loaded its own copy of the
10
+ # client from before the hook existed.
11
+ if Gem::CompactIndexClient.respond_to?(:filesystem_access=)
12
+ Gem::CompactIndexClient.filesystem_access = Bundler::SharedHelpers.method(:filesystem_access)
13
+ end
5
14
 
6
15
  module Bundler
7
16
  class Fetcher
@@ -11,12 +20,12 @@ module Bundler
11
20
  undef_method(method_name)
12
21
  define_method(method_name) do |*args, &blk|
13
22
  method.bind_call(self, *args, &blk)
14
- rescue NetworkDownError, CompactIndexClient::Updater::MismatchedChecksumError => e
23
+ rescue NetworkDownError, Gem::CompactIndexClient::Updater::MismatchedChecksumError => e
15
24
  raise HTTPError, e.message
16
25
  rescue AuthenticationRequiredError, BadAuthenticationError
17
26
  # Fail since we got a 401 from the server.
18
27
  raise
19
- rescue HTTPError => e
28
+ rescue HTTPError, Gem::CompactIndexClient::Error => e
20
29
  Bundler.ui.trace(e)
21
30
  nil
22
31
  end
@@ -35,7 +44,7 @@ module Bundler
35
44
  until remaining_gems.empty?
36
45
  log_specs { "Looking up gems #{remaining_gems.inspect}" }
37
46
  deps = fetch_gem_infos(remaining_gems).flatten(1)
38
- next_gems = deps.flat_map {|d| d[CompactIndexClient::INFO_DEPS].flat_map(&:first) }.uniq
47
+ next_gems = deps.flat_map {|d| d[Gem::CompactIndexClient::INFO_DEPS].flat_map(&:first) }.uniq
39
48
  deps.each {|dep| gem_info << dep }
40
49
  complete_gems.concat(deps.map(&:first)).uniq!
41
50
  remaining_gems = next_gems - complete_gems
@@ -53,7 +62,7 @@ module Bundler
53
62
  end
54
63
  # Read info file checksums out of /versions, so we can know if gems are up to date
55
64
  compact_index_client.available?
56
- rescue CompactIndexClient::Updater::MismatchedChecksumError => e
65
+ rescue Gem::CompactIndexClient::Updater::MismatchedChecksumError => e
57
66
  Bundler.ui.debug(e.message)
58
67
  nil
59
68
  end
@@ -75,7 +84,7 @@ module Bundler
75
84
  def compact_index_client
76
85
  @compact_index_client ||=
77
86
  SharedHelpers.filesystem_access(cache_path) do
78
- CompactIndexClient.new(cache_path, client_fetcher)
87
+ Gem::CompactIndexClient.new(cache_path, client_fetcher)
79
88
  end
80
89
  end
81
90
 
@@ -0,0 +1,150 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rubygems/request"
4
+ require "rubygems/remote_fetcher"
5
+
6
+ module Bundler
7
+ class Fetcher
8
+ # Hands out pools of configured Gem::Net::HTTP connections, compatible
9
+ # with the checkout/checkin interface that Gem::Request expects.
10
+ # Connection policy (proxy, SSL, timeouts) follows Bundler settings
11
+ # first, falling back to RubyGems configuration.
12
+ class ConnectionPools
13
+ # A fixed-size pool of connections to a single host, sharing the
14
+ # interface of Gem::Request::HTTPPool.
15
+ class Pool
16
+ attr_reader :cert_files, :proxy_uri
17
+
18
+ def initialize(connections, uri, proxy_uri, size)
19
+ @connections = connections
20
+ @uri = uri
21
+ @proxy_uri = proxy_uri
22
+ @cert_files = connections.cert_files
23
+ @queue = Thread::SizedQueue.new(size)
24
+ size.times { @queue.push(nil) }
25
+ end
26
+
27
+ def checkout
28
+ @queue.pop || @connections.build_connection(@uri, @proxy_uri)
29
+ end
30
+
31
+ def checkin(connection)
32
+ @queue.push(connection)
33
+ end
34
+ end
35
+
36
+ attr_reader :override_headers, :cert_files
37
+
38
+ def initialize(size:, timeout:)
39
+ @size = size
40
+ @timeout = timeout
41
+ @override_headers = {}
42
+ @cert_files = Gem::Request.get_cert_files
43
+ @pools = {}
44
+ @pool_mutex = Thread::Mutex.new
45
+ end
46
+
47
+ # Performs a GET request through Gem::Request, which handles resetting
48
+ # and retrying stale connections, and returns the raw response.
49
+ def request(uri, headers = nil)
50
+ Gem::Request.new(uri, Gem::Net::HTTP::Get, nil, pool_for(uri)).fetch do |request|
51
+ @override_headers.each {|key, value| request[key] = value }
52
+ headers&.each {|key, value| request[key] = value }
53
+ end
54
+ end
55
+
56
+ def pool_for(uri)
57
+ key = [uri.scheme, uri.hostname, uri.port]
58
+ @pool_mutex.synchronize do
59
+ @pools[key] ||= Pool.new(self, uri, proxy_for(uri), @size)
60
+ end
61
+ end
62
+
63
+ # The proxy that will be used for +uri+, or nil. RubyGems configuration
64
+ # takes precedence over the environment, like Gem::RemoteFetcher.
65
+ # Unlike Gem::Request, an https URI falls back to `http_proxy` when no
66
+ # https-specific proxy is set, which is what the previous
67
+ # net-http-persistent based transport did.
68
+ def proxy_for(uri)
69
+ proxy = if config_proxy = Gem.configuration[:http_proxy]
70
+ Gem::Request.proxy_uri(config_proxy)
71
+ else
72
+ env_proxy_for(uri)
73
+ end
74
+ return unless proxy
75
+ # NO_PROXY="*" bypasses the proxy for every host, which
76
+ # Gem::URI::Generic.use_proxy? does not understand.
77
+ return if no_proxy_env.strip == "*"
78
+ return unless Gem::URI::Generic.use_proxy?(uri.hostname, nil, uri.port, no_proxy_env)
79
+ proxy
80
+ end
81
+
82
+ def build_connection(uri, proxy_uri) # :nodoc:
83
+ args = [uri.hostname, uri.port]
84
+ args += if proxy_uri
85
+ [proxy_uri.hostname, proxy_uri.port,
86
+ Gem::UriFormatter.new(proxy_uri.user).unescape,
87
+ Gem::UriFormatter.new(proxy_uri.password).unescape]
88
+ else
89
+ [nil, nil]
90
+ end
91
+
92
+ connection = Gem::Request::ConnectionPools.client.new(*args)
93
+ configure_ssl(connection) if uri.scheme == "https"
94
+ connection.open_timeout = @timeout
95
+ connection.read_timeout = @timeout
96
+ # Stale connections are already resent by Gem::Request#perform_request,
97
+ # and Bundler::Retry retries whole requests, so the Gem::Net::HTTP
98
+ # level retry would only multiply the time spent on a timing out host.
99
+ connection.max_retries = 0
100
+ connection.start
101
+ connection
102
+ end
103
+
104
+ private
105
+
106
+ def env_proxy_for(uri)
107
+ proxy = Gem::Request.get_proxy_from_env(uri.scheme)
108
+ proxy = Gem::Request.get_proxy_from_env("http") if proxy == :no_proxy && uri.scheme == "https"
109
+ proxy == :no_proxy ? nil : proxy
110
+ end
111
+
112
+ def no_proxy_env
113
+ ENV["no_proxy"] || ENV["NO_PROXY"] || ""
114
+ end
115
+
116
+ def configure_ssl(connection)
117
+ connection.use_ssl = true
118
+ connection.verify_mode = Bundler.settings[:ssl_verify_mode] || OpenSSL::SSL::VERIFY_PEER
119
+ connection.cert_store = bundler_cert_store
120
+
121
+ ssl_client_cert = Bundler.settings[:ssl_client_cert] ||
122
+ (Gem.configuration.ssl_client_cert if
123
+ Gem.configuration.respond_to?(:ssl_client_cert))
124
+ return unless ssl_client_cert
125
+
126
+ pem = File.read(ssl_client_cert)
127
+ connection.cert = OpenSSL::X509::Certificate.new(pem)
128
+ connection.key = OpenSSL::PKey.read(pem)
129
+ end
130
+
131
+ def bundler_cert_store
132
+ store = OpenSSL::X509::Store.new
133
+ ssl_ca_cert = Bundler.settings[:ssl_ca_cert] ||
134
+ (Gem.configuration.ssl_ca_cert if
135
+ Gem.configuration.respond_to?(:ssl_ca_cert))
136
+ if ssl_ca_cert
137
+ if File.directory? ssl_ca_cert
138
+ store.add_path ssl_ca_cert
139
+ else
140
+ store.add_file ssl_ca_cert
141
+ end
142
+ else
143
+ store.set_default_paths
144
+ cert_files.each {|c| store.add_file c }
145
+ end
146
+ store
147
+ end
148
+ end
149
+ end
150
+ end
@@ -6,14 +6,19 @@ module Bundler
6
6
  HTTP_NON_RETRYABLE_ERRORS = [
7
7
  SocketError,
8
8
  Errno::EADDRNOTAVAIL,
9
+ Errno::ECONNREFUSED,
10
+ Errno::EHOSTDOWN,
11
+ Errno::EHOSTUNREACH,
9
12
  Errno::ENETDOWN,
10
13
  Errno::ENETUNREACH,
11
- Gem::Net::HTTP::Persistent::Error,
12
- Errno::EHOSTUNREACH,
13
14
  ].freeze
14
15
 
16
+ # The vendored net-http raises Gem::Timeout::Error, but when Gem::Net is
17
+ # the real Net (hosts without a vendored net-http), timeouts are plain
18
+ # Timeout::Error subclasses instead.
15
19
  HTTP_RETRYABLE_ERRORS = [
16
20
  Gem::Timeout::Error,
21
+ *(::Timeout::Error if defined?(::Timeout::Error)),
17
22
  EOFError,
18
23
  Errno::EINVAL,
19
24
  Errno::ECONNRESET,
@@ -25,11 +30,11 @@ module Bundler
25
30
  Zlib::BufError,
26
31
  ].freeze
27
32
 
28
- attr_reader :connection
33
+ attr_reader :connections
29
34
  attr_reader :redirect_limit
30
35
 
31
- def initialize(connection, redirect_limit)
32
- @connection = connection
36
+ def initialize(connections, redirect_limit)
37
+ @connections = connections
33
38
  @redirect_limit = redirect_limit
34
39
  end
35
40
 
@@ -54,6 +59,10 @@ module Bundler
54
59
  if [new_uri.scheme, new_uri.host, new_uri.port] == [uri.scheme, uri.host, uri.port]
55
60
  new_uri.user = uri.user
56
61
  new_uri.password = uri.password
62
+ else
63
+ # see Gem::RemoteFetcher#fetch_http. A nil value removes the header
64
+ # that ConnectionPools adds from its override_headers.
65
+ headers = headers.merge("X-Gemfile-Source" => nil)
57
66
  end
58
67
  fetch(new_uri, headers, counter + 1)
59
68
  when Gem::Net::HTTPRequestedRangeNotSatisfiable
@@ -84,23 +93,25 @@ module Bundler
84
93
  filtered_uri = URICredentialsFilter.credential_filtered_uri(uri)
85
94
 
86
95
  Bundler.ui.debug "HTTP GET #{filtered_uri}"
87
- req = Gem::Net::HTTP::Get.new uri.request_uri, headers
88
- if uri.user
89
- user = CGI.unescape(uri.user)
90
- password = uri.password ? CGI.unescape(uri.password) : nil
91
- req.basic_auth(user, password)
96
+ connections.request(uri, headers)
97
+ rescue Gem::RemoteFetcher::FetchError => e
98
+ Bundler.ui.trace e
99
+
100
+ case e.message
101
+ when /certificate verify failed/
102
+ raise CertificateFailureError.new(uri)
103
+ when /host is down|host down/i
104
+ raise network_down_error(uri, filtered_uri)
105
+ else
106
+ raise HTTPError, "Network error while fetching #{filtered_uri}" \
107
+ " (#{e})"
92
108
  end
93
- connection.request(uri, req)
94
109
  rescue OpenSSL::SSL::SSLError
95
110
  raise CertificateFailureError.new(uri)
96
111
  rescue *HTTP_NON_RETRYABLE_ERRORS => e
97
112
  Bundler.ui.trace e
98
113
 
99
- host = uri.host
100
- host_port = "#{host}:#{uri.port}"
101
- host = host_port if filtered_uri.to_s.include?(host_port)
102
- raise NetworkDownError, "Could not reach host #{host}. Check your network " \
103
- "connection and try again."
114
+ raise network_down_error(uri, filtered_uri)
104
115
  rescue *HTTP_RETRYABLE_ERRORS => e
105
116
  Bundler.ui.trace e
106
117
 
@@ -110,6 +121,14 @@ module Bundler
110
121
 
111
122
  private
112
123
 
124
+ def network_down_error(uri, filtered_uri)
125
+ host = uri.host
126
+ host_port = "#{host}:#{uri.port}"
127
+ host = host_port if filtered_uri.to_s.include?(host_port)
128
+ NetworkDownError.new("Could not reach host #{host}. Check your network " \
129
+ "connection and try again.")
130
+ end
131
+
113
132
  def https?(uri)
114
133
  uri.scheme&.casecmp("https")&.zero?
115
134
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "vendored_persistent"
3
+ require_relative "vendored_net_http"
4
4
  require_relative "vendored_timeout"
5
5
  require_relative "vendored_securerandom"
6
6
  require "zlib"
@@ -10,6 +10,7 @@ module Bundler
10
10
  class Fetcher
11
11
  autoload :Base, File.expand_path("fetcher/base", __dir__)
12
12
  autoload :CompactIndex, File.expand_path("fetcher/compact_index", __dir__)
13
+ autoload :ConnectionPools, File.expand_path("fetcher/connection_pools", __dir__)
13
14
  autoload :Downloader, File.expand_path("fetcher/downloader", __dir__)
14
15
  autoload :Dependency, File.expand_path("fetcher/dependency", __dir__)
15
16
  autoload :Index, File.expand_path("fetcher/index", __dir__)
@@ -137,7 +138,7 @@ module Bundler
137
138
  @remote = remote
138
139
 
139
140
  Socket.do_not_reverse_lookup = true
140
- connection # create persistent connection
141
+ connection # set up the connection pools eagerly so SSL support is checked upfront
141
142
  end
142
143
 
143
144
  def uri
@@ -176,13 +177,13 @@ module Bundler
176
177
  def specs(gem_names, source)
177
178
  index = Bundler::Index.new
178
179
 
179
- fetch_specs(gem_names).each do |name, version, platform, dependencies, metadata|
180
+ fetch_specs(gem_names).each do |name, version, suffix, dependencies, metadata|
180
181
  spec = if dependencies
181
- EndpointSpecification.new(name, version, platform, self, dependencies, metadata).tap do |es|
182
+ EndpointSpecification.new(name, version, suffix, self, dependencies, metadata).tap do |es|
182
183
  source.checksum_store.replace(es, es.checksum)
183
184
  end
184
185
  else
185
- RemoteSpecification.new(name, version, platform, self)
186
+ RemoteSpecification.new(name, version, suffix, self)
186
187
  end
187
188
  spec.source = source
188
189
  spec.remote = @remote
@@ -237,7 +238,7 @@ module Bundler
237
238
  end
238
239
 
239
240
  def http_proxy
240
- return unless uri = connection.proxy_uri
241
+ return unless uri = connection.proxy_for(remote_uri)
241
242
  uri.to_s
242
243
  end
243
244
 
@@ -307,28 +308,7 @@ module Bundler
307
308
  end
308
309
  end
309
310
 
310
- con = Gem::Net::HTTP::Persistent.new name: "bundler", proxy: :ENV
311
- if gem_proxy = Gem.configuration[:http_proxy]
312
- con.proxy = Gem::URI.parse(gem_proxy) if gem_proxy != :no_proxy
313
- end
314
-
315
- if remote_uri.scheme == "https"
316
- con.verify_mode = (Bundler.settings[:ssl_verify_mode] ||
317
- OpenSSL::SSL::VERIFY_PEER)
318
- con.cert_store = bundler_cert_store
319
- end
320
-
321
- ssl_client_cert = Bundler.settings[:ssl_client_cert] ||
322
- (Gem.configuration.ssl_client_cert if
323
- Gem.configuration.respond_to?(:ssl_client_cert))
324
- if ssl_client_cert
325
- pem = File.read(ssl_client_cert)
326
- con.cert = OpenSSL::X509::Certificate.new(pem)
327
- con.key = OpenSSL::PKey.read(pem)
328
- end
329
-
330
- con.read_timeout = Fetcher.api_timeout
331
- con.open_timeout = Fetcher.api_timeout
311
+ con = ConnectionPools.new(size: Bundler.settings.processor_count, timeout: Fetcher.api_timeout)
332
312
  con.override_headers["User-Agent"] = user_agent
333
313
  con.override_headers["X-Gemfile-Source"] = @remote.original_uri.to_s if @remote.original_uri
334
314
  con
@@ -341,25 +321,6 @@ module Bundler
341
321
  paths.find {|path| File.file? path }
342
322
  end
343
323
 
344
- def bundler_cert_store
345
- store = OpenSSL::X509::Store.new
346
- ssl_ca_cert = Bundler.settings[:ssl_ca_cert] ||
347
- (Gem.configuration.ssl_ca_cert if
348
- Gem.configuration.respond_to?(:ssl_ca_cert))
349
- if ssl_ca_cert
350
- if File.directory? ssl_ca_cert
351
- store.add_path ssl_ca_cert
352
- else
353
- store.add_file ssl_ca_cert
354
- end
355
- else
356
- store.set_default_paths
357
- require "rubygems/request"
358
- Gem::Request.get_cert_files.each {|c| store.add_file c }
359
- end
360
- store
361
- end
362
-
363
324
  def remote_uri
364
325
  @remote.uri
365
326
  end
@@ -216,7 +216,7 @@ module Bundler
216
216
  Bundler.ui.debug(cmd)
217
217
  SharedHelpers.chdir(base) do
218
218
  outbuf = IO.popen(cmd, err: [:child, :out], &:read)
219
- status = $?
219
+ status = Process.last_status
220
220
  block&.call(outbuf) if status.success?
221
221
  [outbuf, status]
222
222
  end
@@ -89,10 +89,10 @@ module Bundler
89
89
  def version_prefix
90
90
  if @options[:strict]
91
91
  "= "
92
- elsif @options[:optimistic]
93
- ">= "
94
- else
92
+ elsif @options[:pessimistic]
95
93
  "~> "
94
+ else
95
+ ">= "
96
96
  end
97
97
  end
98
98