Skip to content

Adopt the Rails 7.0 framework defaults - #1536

Open
benrfairless wants to merge 18 commits into
chore/1355-rails-7-0-bumpfrom
chore/1355-rails-7-0-defaults
Open

benrfairless wants to merge 18 commits into
chore/1355-rails-7-0-bumpfrom
chore/1355-rails-7-0-defaults

Conversation

@benrfairless

@benrfairless benrfairless commented Sep 11, 2026 •

Copy link
Copy Markdown
Member

What and why

Adopts the Rails 7.0 framework defaults one flag per commit, ending with config.load_defaults 7.0, mirroring how #1529 adopted the 6.1 defaults. Most flags are inert here and each commit body says why; the ones needing real thought are below.

  • Closes Update morph to Rails 7.0.x with ruby 3.0.x #1355 (with Update Rails to 7.0.8.7 #1535)
  • key_generator_hash_digest_class = SHA256 ships with the cookie rotator from the 7.0 upgrade guide: without it every session and remember-me cookie issued before the deploy becomes unreadable and everyone is logged out mid-soak. Remove the rotator after a soak, once live sessions have been rewritten.
  • cookies_serializer = :json resolves the FIXME from the :hybrid migration, which has been rewriting Marshal cookies as JSON in production since November 2025.
  • cache_format_version is the one deliberate hold-back, kept at 6.1 explicitly in application.rb: Rails 6.1 cannot read 7.0-format cache entries and production memcached outlives a cap production deploy:rollback, so the new format would break rollback during the soak. Flip it after the soak, or in the Rails 7.1 hop where the 7.0 format becomes the floor.
  • raise_on_open_redirects needed one exemption: the Discourse SSO endpoint, whose whole job is redirecting to help.morph.io, gets allow_other_host: true. Everything else redirects internally (Sign out redirects to unsafe client supplied request.referer #1438 hardened the sign-out redirect separately).
  • The Active Storage flags are dropped rather than enabled (Remove dead Active Storage configuration #1497 removed Active Storage).

Type of change

  • Chore

How this was checked

In the containerised dev environment: CI profile 854 examples / 0 failures (coverage 81.46% against the 80.05 threshold), rubocop and haml-lint clean, srb tc unchanged at 101 (not CI-enforced), zero Rails deprecation warnings under the #1498 guard. The open-redirect flag initially broke the 22 Discourse SSO specs (tagged slow, so only the CI profile catches them), which is what prompted the allow_other_host exemption.
CodeRabbit review of the full #1534-#1537 stack diff: 0 findings.

  • Ran the automated tests
  • Ran /code-review (or equivalent): fixed any concerns (and rechecked), linked follow-up issues above, or noted why they are not important above
  • Documentation updated, or not needed

Deploy runbook (after merge, as part of the Phase 2 deploy)

  1. Deploy in a low-usage window: cap production deploy.
  2. Verify GitHub sign-in explicitly (cookie serializer and key-digest change both touch session cookies).
  3. Watch Sentry; cap production deploy:rollback is the fallback (rollback stays safe because cache_format_version is held at 6.1).
  4. After the week-long Phase 2 soak: remove config/initializers/cookie_rotator.rb and flip cache_format_version to 7.0, or fold both into the Rails 7.1 hop (Update morph to Rails 7.1.x in staging then production #1358).

Assisted-by: OpenCode:anthropic.claude-fable-5

@benrfairless
benrfairless added this pull request to stack #1531 September 11, 2026 09:25
@benrfairless benrfairless mentioned this pull request Sep 11, 2026
4 tasks done
@benrfairless benrfairless self-assigned this Sep 11, 2026
@benrfairless benrfairless changed the title chore/1355 rails 7 0 defaults Adopt the Rails 7.0 framework defaults Sep 11, 2026
@benrfairless
benrfairless marked this pull request as ready for review September 11, 2026 10:10
@benrfairless
benrfairless requested a review from a team as a code owner September 11, 2026 10:10
@Br3nda
Br3nda force-pushed the chore/1355-rails-7-0-defaults branch from 41b273b to 40debba Compare September 15, 2026 06:32
@Br3nda
Br3nda force-pushed the chore/1355-rails-7-0-defaults branch from 40debba to 82577a0 Compare September 15, 2026 07:12
@Br3nda
Br3nda force-pushed the chore/1355-rails-7-0-defaults branch from 82577a0 to b465d4a Compare September 15, 2026 07:35

@ianheggie-oaf ianheggie-oaf left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved - makes sense to me

video_preview_arguments, variant_processor and
multiple_file_field_include_hidden configure Active Storage, which
#1497 removed; config/application.rb does not load its engine.

Part of #1355

Assisted-by: OpenCode:anthropic.claude-fable-5
Signed-off-by: Ben Fairless <[email protected]>
button_to with a string caption previously rendered
<input type="submit">; with this flag it renders <button> like the
block form always did. Our button_to calls carry Bootstrap .btn
classes, which style both elements the same way, and the specs
assert on captions rather than element type.

Part of #1355

Assisted-by: OpenCode:anthropic.claude-fable-5
Signed-off-by: Ben Fairless <[email protected]>
The single stylesheet_link_tag in the layout passes media: "all"
explicitly, so removing the implicit media="screen" default changes
no rendered output here.

Part of #1355

Assisted-by: OpenCode:anthropic.claude-fable-5
Signed-off-by: Ben Fairless <[email protected]>
key_generator_hash_digest_class changes how session and cookie
encryption/signing keys are derived, so on its own it would
invalidate every cookie issued before the deploy and log all users
out. The cookie_rotator initializer from the 7.0 upgrade guide keeps
old cookies readable: they verify against the SHA1-derived secrets
and are rewritten with the SHA256-derived ones on first request.
The rotator can be removed after a soak, once live sessions have
been rewritten.

Part of #1355

Assisted-by: OpenCode:anthropic.claude-fable-5
Signed-off-by: Ben Fairless <[email protected]>
hash_digest_class covers ETags and cache key digests. Existing
ETags stop matching (clients simply refetch once) and fragment
cache keys change (memcached entries regenerate); both are
self-healing after deploy.

Part of #1355

Assisted-by: OpenCode:anthropic.claude-fable-5
Signed-off-by: Ben Fairless <[email protected]>
Nothing in the app references TimeWithZone by class name, so
removing the deprecated .name override ("Time") changes nothing
here.

Part of #1355

Assisted-by: OpenCode:anthropic.claude-fable-5
Signed-off-by: Ben Fairless <[email protected]>
Makes test cases behave closer to real requests and jobs (query
cache on, executor callbacks run). The suite is the proof: it
passes with the flag on.

Part of #1355

Assisted-by: OpenCode:anthropic.claude-fable-5
Signed-off-by: Ben Fairless <[email protected]>
Previously Net::SMTP defaults applied (30s open, 60s read). Mail
goes out through postal.oaf.org.au on the local network, and
alert/invitation mail is sent from Sidekiq jobs where a hung
connection blocks a worker; failing fast and letting the job retry
is the better trade.

Part of #1355

Assisted-by: OpenCode:anthropic.claude-fable-5
Signed-off-by: Ben Fairless <[email protected]>
Part of #1355

Assisted-by: OpenCode:anthropic.claude-fable-5
Signed-off-by: Ben Fairless <[email protected]>
The suite uses factory_bot rather than fixtures, so this is inert
here; enabled for completeness of the 7.0 defaults.

Part of #1355

Assisted-by: OpenCode:anthropic.claude-fable-5
Signed-off-by: Ben Fairless <[email protected]>
INSERTs now reference every column rather than only the changed
ones, so database defaults and Rails attribute defaults can no
longer drift apart silently.

Part of #1355

Assisted-by: OpenCode:anthropic.claude-fable-5
Signed-off-by: Ben Fairless <[email protected]>
Every redirect_to in the app targets an internal record or path
(the sign-out redirect was hardened separately in #1438 with an
allowlisted path regexp), and the GitHub OAuth redirect happens at
the Rack level in OmniAuth, outside this protection. The one
intended cross-host redirect is the Discourse SSO endpoint, whose
job is to send the user to help.morph.io: it gets
allow_other_host: true.

Part of #1355

Assisted-by: OpenCode:anthropic.claude-fable-5
Signed-off-by: Ben Fairless <[email protected]>
config/initializers/wrap_parameters.rb has set exactly this
behaviour since Rails 4, so the flag is a no-op here; the
initializer stays for clarity until a future cleanup.

Part of #1355

Assisted-by: OpenCode:anthropic.claude-fable-5
Signed-off-by: Ben Fairless <[email protected]>
Nothing in the app calls Digest::UUID, so the change is inert here.

Part of #1355

Assisted-by: OpenCode:anthropic.claude-fable-5
Signed-off-by: Ben Fairless <[email protected]>
X-XSS-Protection: 0 turns off browsers' flawed legacy XSS auditor,
per current guidance; the other headers keep their previous values.

Part of #1355

Assisted-by: OpenCode:anthropic.claude-fable-5
Signed-off-by: Ben Fairless <[email protected]>
Resolves the FIXME left when the serializer moved to :hybrid, which
has been reading Marshal cookies and rewriting them as JSON in
production since November 2025 (commit 16d7983, deployed
2025-11-07). Ten months exceeds the lifetime of every cookie we
care about; any straggler fails to parse, comes back nil, and the
user signs in again. Verify GitHub sign-in explicitly after deploy,
per the PR runbook.

Part of #1355

Assisted-by: OpenCode:anthropic.claude-fable-5
Signed-off-by: Ben Fairless <[email protected]>
Nothing in the app reads request.content_type, so returning the
unmodified header (including any charset parameter) changes nothing
here.

Part of #1355

Assisted-by: OpenCode:anthropic.claude-fable-5
Signed-off-by: Ben Fairless <[email protected]>
Every flag in new_framework_defaults_7_0.rb is enabled in the
preceding commits, one per flag, so config.load_defaults 7.0
changes one thing beyond replacing the file: cache_format_version,
which is explicitly held at 6.1. Rails 6.1 cannot read 7.0-format
cache entries and production memcached outlives a Capistrano
rollback, so adopting the new format would break rollback to the
previous release during the post-deploy soak. It flips once this
hop has soaked, or in the Rails 7.1 upgrade where the 7.0 format
becomes the floor. This completes the defaults adoption for the
Phase 2 hop.

Part of #1355

Assisted-by: OpenCode:anthropic.claude-fable-5
Signed-off-by: Ben Fairless <[email protected]>
@ianheggie-oaf
ianheggie-oaf force-pushed the chore/1355-rails-7-0-defaults branch from b465d4a to 544dc30 Compare September 22, 2026 01:57
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.

Update morph to Rails 7.0.x with ruby 3.0.x

2 participants