Skip to content

Upgrade Ruby to 3.3 and oc-id to Rails 8.1 - #4247

Open
tas50 wants to merge 1 commit into
chef:mainfrom
tas50:upgrade-ruby-3.4-rails-8.1
Open

Upgrade Ruby to 3.3 and oc-id to Rails 8.1#4247
tas50 wants to merge 1 commit into
chef:mainfrom
tas50:upgrade-ruby-3.4-rails-8.1

Conversation

@tas50

@tas50 tas50 commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

What

Moves the embedded Ruby from 3.1.7 → 3.3 and oc-id from Rails 7.2.3.1 → 8.1.3.1, and fixes the Ruby and Rails deprecations that surfaced.

Why Ruby 3.3 (and not 3.2 or 3.4)

Rails 8.1 requires Ruby >= 3.2, so the interpreter had to move regardless. 3.2 and 3.4 are both out:

  • Ruby 3.2 is itself EOL (March 2026) — it would have been an upgrade onto another unsupported runtime — and Habitat has nothing for it beyond a stale, x86_64-only core/ruby32/3.2.2.
  • Ruby 3.4 is not reachable by either build path as configured today:
    • omnibus-software's config/software/ruby.rb declares checksums only up to 3.3.1 — there are no 3.4.x definitions, so override :ruby cannot name one.
    • Habitat Builder has no core/ruby3_4 in the LTS-2024 refresh channel that all three plans pin via HAB_REFRESH_CHANNEL; it exists only in base / base-2025 / base-20260716.

What each path can actually reach:

omnibus (override :ruby) Habitat (LTS-2024)
ruby3_1 3.1.7 (current) 3.1.7 x86_64 / 3.1.3 aarch64
ruby3_3 3.3.1 (max) 3.3.0, both arches
ruby3_4 ✗ no definitions ✗ not in channel

So this lands on 3.3.1 for omnibus and core/ruby3_3/3.3.0 for Habitat — the highest the current toolchain can build, both satisfying Rails 8.1.

⚠️ The patch levels are stale — worth a decision

Current Ruby 3.3 is 3.3.12; we ship 3.3.1/3.3.0 (early-2024 releases). Ruby 3.3 is supported until ~March 2027, but these specific patches miss two years of 3.3.x fixes. Getting to a current patch needs one of:

  1. Add version("3.3.12") (+ checksum) to omnibus-software's ruby.rb, and a Habitat refresh-channel migration off LTS-2024 — the latter re-resolves every core dep in all three plans (sqitch, postgresql14-client, openssl, glibc, gecode3, node…), which is a platform refresh deserving its own PR and test cycle.
  2. Accept 3.3.1/3.3.0 now and treat the above as follow-up.

This PR takes option 2 so the Rails 8.1 work isn't blocked behind a platform refresh. Happy to split it differently.

Why this touches three components

The omnibus package ships one embedded Ruby that oc-id, chef-server-ctl and oc_erchef all share — override :ruby is global, so there's no way to move oc-id alone. The interpreter bump covers all three; the Rails work stays scoped to oc-id.

Updated: omnibus_overrides.rb, all three habitat/plan.sh files (package dep plus ruby/3.1.0ruby/3.3.0 gem paths), the oc_erchef run hook, the chef-server-ctl secrets-bootstrap shebang, automate_build.sh, the Brakeman workflow, dev/defaults.yml, the license notice, and a core/ruby3_3 entry in .license_scout.yml.

src/oc_erchef/Gemfile_habitat.lock needs no change: it's platform-generic (PLATFORMS: ruby) and its three gems all support Ruby 3.3.

CI

The oc-id Buildkite job now selects Ruby 3.4.2 before make install ci. chefes/a1-buildkite ships only 3.1.0, 3.1.6 and 3.4.2 via asdf; the 3.1.x default can't run Rails 8.1, and 3.4.2 is the only usable one that doesn't require compiling a Ruby from source. That's a minor ahead of what we ship, so the suite is verified on 3.3 as well (below). If the image ever gains a 3.3.x, prefer it.

Gemfile changes

Only what was needed to let Rails 8.1 resolve:

Change Reason
rails8.1.3.1 the upgrade
rspec-rails ~> 6.0~> 8.0 6.x caps Rails below 8.1 — this was the constraint blocking resolution
dropped public_suffix < 7.0 pin existed only for Ruby 3.1 (both oc-id and chef-server-ctl)
bigdecimal '3.1.3'~> 3.1 exact pin was Dependabot churn on a Ruby 3.1-era pin, not a deliberate hold
added ostruct see deprecations below

Both lockfiles were re-resolved conservatively, so nothing moved except the Rails family and the gems above. Worth noting:

  • nokogiri stays at 1.18.9, matching the pin in omnibus_overrides.rb. A full re-resolve drifted it to 1.19.4 along with minitest 6 / erb 6 / rdoc 8 / connection_pool 3; the conservative resolve avoids all of that.
  • The rack / rexml / net-imap CVE floors are intact (rack >= 3.2.5, rexml >= 3.4.2, net-imap >= 0.5.15). This needed care: the omnibus/ submodule supplying safe_versions.rb doesn't clone, and the Gemfile fails open when it's missing — a naive re-resolve would have silently dropped those floors to >= 0. I reconstructed the file locally from the values already in the lockfiles so resolution couldn't regress them, then verified the floors afterwards.
  • BUNDLED WITH moves 2.3.272.5.22, matching the bundler that ships with Ruby 3.3.

Deprecations fixed

  • Net::HTTPServerExceptionNet::HTTPClientException (13 sites). Ruby 3.4 warns on every reference. The old name has been an alias for the new one since Ruby 2.6, so this is behaviour-preserving — including in the specs that construct one.
  • ostruct declared explicitly. config 4.x requires ostruct without declaring it, and Ruby warns that ostruct stops being a default gem in Ruby 4.0. config 5.x declares it, but that's a major bump beyond this change.
  • config.fixture_pathfixture_paths in spec_helper.rb. The singular form no longer exists in rspec-rails 8 or ActiveRecord 8.1.
  • config.serve_static_files / config.static_cache_controlconfig.public_file_server.*. Renamed in Rails 5.0 and absent from Rails 8.1 entirely — Railtie::Configuration#method_missing stores unknown config in a hash and forgets it, so these have been silent no-ops for years with no warning. public_file_server.enabled already defaults to true, so this restores the intent without changing behaviour.

One behaviour change worth a look

config.action_dispatch.show_exceptions = false:none in config/environments/test.rb.

ExceptionWrapper#show? treats anything that isn't :none or :rescuable as "show", so false has been meaning :all — the opposite of the comment above it ("Raise exceptions instead of rendering exception templates"). Rails 7.2's implementation is byte-identical, so this is a pre-existing bug, not something the upgrade introduces; the test env has been rendering exception pages instead of raising.

The full suite passes with it corrected, so I fixed it rather than freezing the wrong behaviour in with an explicit :all. Flagging it as the one change here that alters runtime behaviour — say the word if you'd rather keep :all and fix it separately.

Deliberately not changed

config.cache_classes is still a live, non-deprecated attribute in Rails 8.1 (enable_reloading is literally defined as !cache_classes), so it was left alone despite reading like legacy config.

Verification

Against PostgreSQL 13:

Suite Ruby 3.1.7 / Rails 7.2 (baseline) Ruby 3.3 / Rails 8.1 Ruby 3.4 / Rails 8.1
oc-id (rake bk:ci, matching CI) 150 examples, 0 failures 150 examples, 0 failures 150 examples, 0 failures
chef-server-ctl (bundle exec rspec) 79 examples, 0 failures 79 examples, 0 failures

Both Ruby lines are covered because we ship 3.3 but CI runs 3.4.2. assets:precompile succeeds and the runs are clean of Rails deprecation warnings. The oc-id lockfile also installs frozen on x86_64-linux, the CI/omnibus platform.

Not verified locally: the omnibus and Habitat builds themselves — both need the private chef-server-omnibus-config submodule, which doesn't clone. The plan/override edits are mechanical version bumps and I checked the target packages exist (core/ruby3_3/3.3.0 in LTS-2024 for both arches; ruby 3.3.1 in omnibus-software), but the first real build is the test.

Rails 7.2 is end-of-life. Rails 8.1 requires Ruby >= 3.2, and Ruby 3.1
is itself past EOL (March 2025), so the embedded Ruby moves too.

Ruby 3.3 rather than 3.2 or 3.4. Ruby 3.2 reached EOL in March 2026, so
it would have been an upgrade onto another unsupported runtime, and it
has no Habitat package at all beyond a stale x86_64-only
core/ruby32/3.2.2. Ruby 3.4 is not reachable by either build path as
they are configured today:

  * omnibus-software's config/software/ruby.rb only declares checksums
    up to 3.3.1 -- there are no 3.4.x definitions, so `override :ruby`
    cannot name one.
  * Habitat Builder has no core/ruby3_4 in the LTS-2024 refresh channel
    that all three plans pin via HAB_REFRESH_CHANNEL; it exists only in
    the base/base-2025 channels. core/ruby3_3/3.3.0 is in LTS-2024 for
    both x86_64 and aarch64.

So this lands on the highest Ruby the current toolchain can build:
3.3.1 for omnibus, core/ruby3_3/3.3.0 for Habitat. Both satisfy Rails
8.1. See the PR for what it would take to get to a current 3.3 patch.

The omnibus package ships a single embedded Ruby shared by every
component, so the interpreter bump necessarily covers oc-id,
chef-server-ctl and oc_erchef together: omnibus_overrides.rb, all three
habitat/plan.sh files (package dep and the ruby/3.1.0 -> ruby/3.3.0 gem
paths), the oc_erchef run hook, the chef-server-ctl secrets-bootstrap
shebang, automate_build.sh, the Brakeman workflow, dev/defaults.yml, the
license notice, and a core/ruby3_3 entry in .license_scout.yml.

The oc-id Buildkite job selects Ruby 3.4.2. The chefes/a1-buildkite
image ships only 3.1.0, 3.1.6 and 3.4.2 via asdf, and the 3.1.x default
cannot run Rails 8.1; 3.4.2 is the only usable one that does not require
compiling a Ruby from source. The suite is verified on 3.3 as well.

Gemfile changes are the minimum needed to let Rails 8.1 resolve:
rspec-rails ~> 6.0 capped Rails below 8.1, and the public_suffix < 7.0
and exact bigdecimal pins existed only for Ruby 3.1. Both lockfiles were
re-resolved conservatively, so nothing moved except the Rails family and
those gems -- nokogiri in particular stays on the version pinned in
omnibus_overrides.rb, and the rack/rexml/net-imap CVE floors are intact.

Deprecations fixed:

  * Net::HTTPServerException -> Net::HTTPClientException (13 sites).
    Ruby 3.4 warns on every reference and the old name is already an
    alias for the new one, so this is behaviour-preserving.
  * config gem 4.x requires ostruct without declaring it, which Ruby
    warns about because ostruct stops being a default gem in Ruby 4.0.
    Declared explicitly (config 5.x declares it, but that is a major
    bump beyond this change).
  * spec_helper's config.fixture_path -> fixture_paths; the singular
    form no longer exists in rspec-rails 8 or ActiveRecord 8.1.
  * config.serve_static_files / config.static_cache_control ->
    config.public_file_server.*. These were renamed in Rails 5.0 and
    have been silently swallowed by Railtie::Configuration#method_missing
    ever since. public_file_server.enabled already defaults to true, so
    this restores the intent without changing behaviour.
  * config.action_dispatch.show_exceptions false -> :none. Anything that
    is not :none or :rescuable means :all, so the test environment has
    been rendering exception pages rather than raising, contrary to the
    comment above it. This is a pre-existing bug -- Rails 7.2 behaves
    identically -- and the full suite passes with it corrected.

config.cache_classes was deliberately left alone: it is still a live,
non-deprecated attribute in Rails 8.1, with enable_reloading defined as
its inverse.

Verified against PostgreSQL 13: oc-id 150 examples / 0 failures via
`rake bk:ci` on both Ruby 3.3 and 3.4, matching the Ruby 3.1 / Rails 7.2
baseline exactly; chef-server-ctl 79 examples / 0 failures. The oc-id
lockfile also installs frozen on x86_64-linux, the CI/omnibus platform.

Signed-off-by: Tim Smith <tim@mondoo.com>
@tas50
tas50 requested review from a team as code owners September 8, 2026 01:07
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