[OMBU-821] Upgrade to Ruby 4.0.6 - #19
Merged
Merged
Conversation
None of these were reached by the app:
foundation-rails no stylesheet imports it; its only reference was a
`//= require foundation` in a JS manifest that no view
loads and sprockets never compiles
jbuilder no .jbuilder templates exist; the one JSON response
uses plain `render json:`
webpacker no view has javascript_pack_tag; its pack is orphaned
rails_best_practices no references anywhere
byebug, test-unit unused; the suite is Rails minitest
Precompiled CSS digests are byte-identical before and after, confirming
foundation contributed nothing to the output. `assets:precompile` now also
exits cleanly instead of ending in webpacker's "Compilation failed".
Removing webpacker takes the whole Node toolchain with it: package.json,
yarn.lock, app/javascript, config/webpack*, bin/webpack*, bin/yarn, the
node/yarn steps in both CI jobs, the heroku/nodejs buildpack, and a dangling
`assets.paths << Rails.root.join('node_modules')`. That toolchain pinned the
app to Node 14 via node-sass 4.x and webpack 4, which was blocking a move to
a current Heroku stack.
Note config/application.rb still requires "rails/test_unit/railtie" — that is
Rails' own test railtie, not the test-unit gem.
Verified on Ruby 3.3.12: `bundle exec rake` green on Gemfile (Rails 7.1) and
Gemfile.next (Rails 7.2).
Ruby 4.0 dropped ostruct from the default gems while railties still requires
it, so the app does not boot without declaring it. That is the only gem change
the upgrade needs — puma, sass-rails, pg and the rest all install and run
unchanged.
test/models/report_test.rb hardcoded the pre-3.4 Hash#inspect format. Ruby 3.4
added spaces around `=>`, and the validation message interpolates the entry
directly, so the expectation now builds the string from the entries themselves
and will survive the next format change. Its assert_equal arguments were also
reversed (actual, expected), which made the failure diff read backwards.
Adds coverage for paths that had none, so the upgrade is verified against
behaviour rather than just a green boot:
docs_test the root route and homepage had no test at all
show_report_test fastest-entry bolding, the high-stddev warning
appearing and not appearing, the times-slower column
only for comparison reports, and 404 for an unknown id
report_test additions base58 short_id encoding, the round trip through
find_from_short_id, and the ArgumentError path
Suite goes from 10 runs / 15 assertions to 19 runs / 31 assertions, green on
Ruby 4.0.6 for both Rails 7.1 and 7.2.
puma 4.3.10 was exposed to CVE-2022-24790 (CVSS 9.1) and CVE-2022-23634 (CVSS 8.0). Rather than take the minimum patch, this goes to the current 8.0.2: it needs Ruby >= 3.0 and depends only on nio4r, so it imposes no rack constraint, and config/puma.rb uses only options still valid in 8.x. Verified by booting a real server, since the suite uses Rack::Test and never exercises puma: GET / 200 POST /reports 200 {"id":"3"} raw JSON body, form content type GET /:id 200 renders the report with the compare column rack is pinned to the 2.x line, because a bare `bundle update` otherwise resolves rack 3 and breaks report ingestion while the rest of the suite stays green. The Gemfile comment records the mechanism. Choosing the pin over fixing fix_missing_json_content_type is deliberate for now: benchmark-ips is an external gem we do not control, and the repair belongs in a change of its own. With rack held at 2.x, updating the rest is safe and clears almost everything: known advisories drop from 61 findings across 14 gems to 10 across 3. rack 2.2.24, nokogiri 1.19.4, concurrent-ruby 1.3.8, websocket-driver 0.8.2, erb 6.0.7, crass 1.0.7, net-imap 0.6.6, mail 2.9.1, loofah 2.25.2 and rails-html-sanitizer 1.7.1 are all now on patched versions. The remaining 10 are all Rails 7.1.x itself (activestorage, activesupport, actionview) and have no 7.1 fix; they need 7.2.3.2+. Gemfile.next already passes on 7.2, so that upgrade is the next step.
Replaces the long comment above the rack pin with tests, so the constraint is enforced by the suite instead of described in prose. benchmark-ips 2.15.0 (2026-05-21) started sending Content-Type: application/json. Before that, it set a body with no content type and Net::HTTP supplied application/x-www-form-urlencoded, so Rails parsed the JSON as form data and ReportsController#fix_missing_json_content_type had to repair it by re-reading the raw body. That repair only works while rack rewinds rack.input after form parsing. rack 2.x does; rack 3 does not, so the read comes back empty, the rescue swallows the parse error, and the request 400s. Confirmed by temporarily resolving rack 3: both legacy tests fail and the json-content-type test still passes. So the pin is specifically about clients on benchmark-ips < 2.15.0, and the test names say which client each shape belongs to. Lift the pin once the repair no longer depends on the rewind. 21 runs, 37 assertions, green on Ruby 4.0.6 for Rails 7.1 and 7.2.
This was referenced Aug 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Moves the app to Ruby 4.0.6 and clears the dependency debt that was blocking a current Heroku stack.
Changes
ostruct, which Ruby 4.0 dropped from the default gems while railties still requires it.foundation-rails,jbuilder,webpacker,rails_best_practices,byebug,test-unit. None were reached by the app. Removing webpacker takes the whole Node toolchain with it, so Node 14 is no longer pinned anywhere — that was the blocker for heroku-26.~> 2.2plus a full dependency update. Known advisories go from 61 findings across 14 gems to 10 across 3.Verification
bundle exec rakegreen on Ruby 4.0.6 for bothGemfile(Rails 7.1) andGemfile.next(Rails 7.2). Suite grew from 10 runs / 15 assertions to 21 runs / 37 assertions, covering the root route, report rendering, base58short_id, and both benchmark-ips request shapes — none of which had tests.puma was verified by booting a real server and posting a report end to end, since the suite uses Rack::Test and never exercises puma.
Why rack is pinned
benchmark-ips < 2.15.0 sends a JSON body with a form content type, and
ReportsController#fix_missing_json_content_typerepairs that by re-reading the raw body. Rack 3 no longer rewindsrack.inputafter form parsing, so that read comes back empty and legacy clients 400. The two newcreate_report_testcases fail if the pin is lifted before the repair is fixed.Not addressed
Gemfile.nextalready passes on 7.2.sass-railscannot simply be dropped: it transitively suppliessprockets-rails, whichconfig/application.rbrequires, and sprockets 4.4 autoloadssasscregardless. It needs replacing withdartsass-rails, best done alongside the styling work.heroku/nodejsbuildpack was removed since nothing needs Node now.