Skip to content

[OMBU-821] Upgrade to Ruby 4.0.6 - #19

Merged
JuanVqz merged 4 commits into
mainfrom
feature/OMBU-821-ruby-4
Aug 20, 2026
Merged

[OMBU-821] Upgrade to Ruby 4.0.6#19
JuanVqz merged 4 commits into
mainfrom
feature/OMBU-821-ruby-4

Conversation

@JuanVqz

@JuanVqz JuanVqz commented Aug 20, 2026

Copy link
Copy Markdown
Member

Moves the app to Ruby 4.0.6 and clears the dependency debt that was blocking a current Heroku stack.

Changes

  • Ruby 3.3.12 → 4.0.6. Only gem change needed is ostruct, which Ruby 4.0 dropped from the default gems while railties still requires it.
  • Dropped unused gems: 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.
  • puma 4.3.10 → 8.0.2. 4.3.10 was exposed to CVE-2022-24790 (CVSS 9.1) and CVE-2022-23634 (CVSS 8.0).
  • rack pinned to ~> 2.2 plus a full dependency update. Known advisories go from 61 findings across 14 gems to 10 across 3.

Verification

bundle exec rake green on Ruby 4.0.6 for both Gemfile (Rails 7.1) and Gemfile.next (Rails 7.2). Suite grew from 10 runs / 15 assertions to 21 runs / 37 assertions, covering the root route, report rendering, base58 short_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_type repairs that by re-reading the raw body. Rack 3 no longer rewinds rack.input after form parsing, so that read comes back empty and legacy clients 400. The two new create_report_test cases fail if the pin is lifted before the repair is fixed.

Not addressed

  • The remaining 10 advisories are all Rails 7.1.x and need 7.2.3.2+. Gemfile.next already passes on 7.2.
  • sass-rails cannot simply be dropped: it transitively supplies sprockets-rails, which config/application.rb requires, and sprockets 4.4 autoloads sassc regardless. It needs replacing with dartsass-rails, best done alongside the styling work.
  • The heroku-26 build is unverified against a real deploy. The heroku/nodejs buildpack was removed since nothing needs Node now.

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.
@JuanVqz
JuanVqz temporarily deployed to ips-feature-ombu-821-ru-alafut August 20, 2026 20:08 Inactive
@JuanVqz
JuanVqz merged commit 0acd960 into main Aug 20, 2026
2 checks passed
@JuanVqz
JuanVqz deleted the feature/OMBU-821-ruby-4 branch August 20, 2026 20:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant