rails_preflight 0.1.0 → 0.1.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 02fcbcfa3f7d98bbc550334fcf9bd65919fbb2eb5266088823b87c0fd7465ae3
4
- data.tar.gz: 42c66fa2d33b67e47fed5a36a7127eba476c34b57705d0f3c2591dea9fa1f5eb
3
+ metadata.gz: e083ff064d2ee34d2b8862a68c3ea41ca63c1f39107237adec73647e8755b1ec
4
+ data.tar.gz: 770f2d5ac5a2b6b5a8bd491517c735be6c3874b45fdbafa7b25f5d1035af78ab
5
5
  SHA512:
6
- metadata.gz: ab95dfb109b6fee6cd50d1014792f85d4e941ba7871734766b834844bea2a2bfb15e526e426f7a19a97be7abd58339141bfea037365d9e0b34a3e6f5063b8226
7
- data.tar.gz: 1606dfaa001672ed19d45469592f212979228c2efb6500b9e7f8b8017d9977817e1814b25045f79b3ada23a9d3d9fe32b09fbe71e74c3b606e5e69e761ea7731
6
+ metadata.gz: 7c3486a813bd9c02965df146309c21966bc0c463905ea89c06760b74714b32e0ab5f75c2eabdaa20211e68c88f13aba0c03ddf368132d6a740ea772ccc34b9ce
7
+ data.tar.gz: 9d31b9cf5a20aedfbac6452d437760d0f509a169a44837ba9d651364b9d05516d770bac23a165b4f1b327d56be8400a414fc3107ec4f14c10f51e84724f52628
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.1 (2026-10-09)
4
+
5
+ - rubygems.org lookups also trust the certificates RubyGems ships for rubygems.org, so they work wherever `gem install` does, even when Ruby's OpenSSL can't find the system certificates
6
+ - Failed lookups are reported once per distinct error, under their own "rubygems.org lookup" entry with the gem names listed once, instead of the same error repeated for every gem under a second "Private Gems" card
7
+ - A certificate failure says what to do: behind a VPN or proxy that inspects HTTPS, point `SSL_CERT_FILE` at a bundle that includes its root certificate, or pass `--offline`
8
+ - The terminal warns when lookups fail
9
+
3
10
  ## 0.1.0 (2026-10-09)
4
11
 
5
12
  First release. Point it at a Rails 5.0 – 8.1 app and a target version: it writes a report into the app as HTML, Markdown or JSON, plus a short terminal summary. Read-only and static.
@@ -280,7 +280,7 @@ module RailsPreflight
280
280
  <div class="limits">
281
281
  <% @summary.fetch(:cant_see, []).each do |entry| %>
282
282
  <div class="limit">
283
- <h3><%= h(entry[:section]) %></h3>
283
+ <h3><%= h(entry[:title] || entry[:section]) %></h3>
284
284
  <p><%= code_html(entry[:message]) %></p>
285
285
  <% names = Array(entry[:names]) %>
286
286
  <% if names.any? %><p class="names"><%= h(names.join(" · ")) %></p><% end %>
@@ -477,7 +477,10 @@ module RailsPreflight
477
477
  h(text).gsub(/(?<![\w&;])&#39;(.+?)&#39;(?![\w&])/) { "<code>#{$1}</code>" }
478
478
  end
479
479
 
480
+ # A titled entry (the rubygems.org lookup) says what to do in its own message.
480
481
  def cant_see_next(entry)
482
+ return if entry[:title]
483
+
481
484
  case entry[:section]
482
485
  when "Private Gems" then "Check each gemspec's Rails dependency, and bump them in the same step."
483
486
  when "Gem Compatibility"
@@ -246,9 +246,18 @@ module RailsPreflight
246
246
  if mixed_gems.any?
247
247
  result[:checks] << { message: "Looked up #{mixed_gems.size} gems on rubygems.org, since Gemfile.lock lists them under both rubygems.org and #{mixed_remotes.join(', ')}. Pass --offline to skip.", status: :passed, fix_effort: :low }
248
248
  end
249
- if inconclusive.any?
250
- names = inconclusive.map { |name, error| "#{name} (#{error})" }
251
- result[:checks] << { message: "Could not verify #{inconclusive.size} gems against rubygems.org", status: :warning, kind: :unknown, names: names, fix_effort: :unknown }
249
+ # One entry per distinct error: a missing certificate fails every lookup the same way.
250
+ inconclusive.group_by { |_, error| error }.each do |error, failed|
251
+ gems = failed.map(&:first).sort
252
+ @out.puts "Couldn't reach rubygems.org for #{gems.size} gems (#{error}). See \"Couldn't check\" in the report."
253
+ # A VPN or proxy that inspects HTTPS re-signs certificates with a root the OS trusts but Ruby's OpenSSL doesn't read.
254
+ advice = if error.include?("certificate verify failed")
255
+ "Behind a VPN or proxy that inspects HTTPS, point SSL_CERT_FILE at a certificate bundle that includes its root certificate, or pass --offline."
256
+ else
257
+ "Run it again once rubygems.org is reachable, or pass --offline."
258
+ end
259
+ result[:checks] << { message: "Couldn't reach rubygems.org for #{gems.size} #{gems.size == 1 ? 'gem' : 'gems'} (#{error}), so whether #{gems.size == 1 ? "it's private and when it was" : "they're private and when they were"} last released is unknown. #{advice}",
260
+ title: "rubygems.org lookup", status: :warning, kind: :unknown, names: gems, fix_effort: :unknown }
252
261
  result[:status] = :warning
253
262
  end
254
263
  end
@@ -272,6 +281,7 @@ module RailsPreflight
272
281
  found = {}
273
282
  inconclusive = {}
274
283
  lock = Mutex.new
284
+ store = rubygems_cert_store
275
285
 
276
286
  Array.new([names.size, 8].min) do
277
287
  Thread.new do
@@ -279,14 +289,16 @@ module RailsPreflight
279
289
  begin
280
290
  url = URI("https://rubygems.org/api/v1/gems/#{name}.json")
281
291
  # Certificate errors are reported as inconclusive, never silently trusted.
282
- response = Net::HTTP.start(url.host, url.port, use_ssl: true, open_timeout: 5, read_timeout: 5) do |http|
292
+ response = Net::HTTP.start(url.host, url.port, use_ssl: true, cert_store: store, open_timeout: 5, read_timeout: 5) do |http|
283
293
  http.request(Net::HTTP::Get.new(url))
284
294
  end
285
295
  raise "HTTP #{response.code}" unless %w[200 404].include?(response.code) # e.g. 429 when rate limited
286
296
  info = response.code == "200" && (JSON.parse(response.body.to_s) rescue {})
287
297
  lock.synchronize { found[name] = info }
288
298
  rescue StandardError => e
289
- lock.synchronize { inconclusive[name] = "#{e.class.name}: #{e.message}" }
299
+ # The peer address changes per connection; without it, the same failure reads the same for every gem.
300
+ # The peer address changes per connection; without it, the same failure reads the same for every gem.
301
+ lock.synchronize { inconclusive[name] = "#{e.class.name}: #{e.message.sub(/ peeraddr=\S+/, '')}" }
290
302
  end
291
303
  end
292
304
  end
@@ -295,6 +307,20 @@ module RailsPreflight
295
307
  [found, inconclusive]
296
308
  end
297
309
 
310
+ # The system's certificates plus the ones RubyGems ships for rubygems.org, which is why `gem install` works on a
311
+ # machine whose OpenSSL can't find the system store. Verification stays on.
312
+ def rubygems_cert_store
313
+ require 'rubygems/request'
314
+ OpenSSL::X509::Store.new.tap do |store|
315
+ store.set_default_paths
316
+ Gem::Request.get_cert_files.each do |file|
317
+ store.add_file(file)
318
+ rescue OpenSSL::X509::StoreError
319
+ # already in the system store
320
+ end
321
+ end
322
+ end
323
+
298
324
  # From the lockfile: :public, :private (git, path, or only non-rubygems.org remotes), or, when one GEM
299
325
  # section lists rubygems.org next to another remote (Bundler before 2.2), whatever the Gemfile says; :mixed if it doesn't.
300
326
  def gem_origin(spec)
@@ -1,3 +1,3 @@
1
1
  module RailsPreflight
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_preflight
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Syed Aslam