Skip to content

Support Rails 6.1 through 8.1 on Ruby 3.0 through 3.4 - #12

Draft
IgorFroehner wants to merge 3 commits into
chore/release-hygienefrom
chore/dependency-envelope
Draft

Support Rails 6.1 through 8.1 on Ruby 3.0 through 3.4#12
IgorFroehner wants to merge 3 commits into
chore/release-hygienefrom
chore/dependency-envelope

Conversation

@IgorFroehner

@IgorFroehner IgorFroehner commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

Note

Stacked on #11 — base is chore/release-hygiene, so this diff shows only the dependency work. Merge #11 first and this retargets to master automatically.

The problem

The gem was installable on nothing current:

spec.required_ruby_version = ['>= 3.0', '< 3.4']
spec.add_dependency 'activerecord', '~> 6.1'

Ruby 3.4 and 3.5 are out, and Rails 6.1 has been EOL since late 2024. No supported Rails and no current Ruby satisfied both constraints at once. Every bug fix queued behind this would have shipped into a gem no modern app could add.

What it took

The library code needed no changes. That was the open question going in, and the answer is that lib/ is untouched in this diff.

One line in the spec harness was not compatible — MigrationContext.new took a required schema-migration class in 6.1, gained a default in 7.0, and dropped the argument entirely in 7.2. It is now selected on Active Record version.

Proving it rather than asserting it

Widening a constraint without testing the range is just a bigger claim. Added an Appraisal matrix — one gemfile and committed lockfile per Rails version — and all six run the full suite:

Appraisal Result
rails-6.1 40 examples, 0 failures
rails-7.0 40 examples, 0 failures
rails-7.1 40 examples, 0 failures
rails-7.2 40 examples, 0 failures
rails-8.0 40 examples, 0 failures
rails-8.1 40 examples, 0 failures

CI expands to a Ruby × Rails matrix. The exclusions are measured, not guessed:

  • Rails 6.1 and 7.0 on Ruby 3.4 fail with LoadError: cannot load such file -- mutex_m, which left the default gems in 3.4. Verified locally on 3.4.8; 7.1 and 7.2 pass on the same Ruby.
  • Each Rails version is excluded below its own minimum Ruby.

On the two hand-pins

Only one of them was actually removable, which is not what I predicted:

  • concurrent-ruby < 1.3.5 moves out of the root Gemfile into the 6.1 and 7.0 appraisals — the only versions it applies to. It no longer constrains everyone.
  • rubocop-factory_bot ~> 2.23.0 stays. I removed it to check and got Property AutoCorrect of cop FactoryBot/CreateList is supposed to be a boolean and contextual is not — it constrains the linter toolchain against jetrockets-standard 1.29, not Rails, so the Rails upgrade does not release it. The comment in Gemfile is updated to say so.

Lockfiles

gemfiles/*.gemfile.lock are not committed, and neither is the root Gemfile.lock.

I tried committing them first, for reproducible matrix cells. It cannot work here: a lockfile records one Ruby's resolution, and each Rails version runs across several Rubies. The locks were generated on Ruby 3.3, so the 3.0 and 3.1 cells failed to install with i18n-1.15.2 requires ruby version >= 3.1. Serving every cell would need one lockfile per Ruby/Rails pair — 23 of them.

Each cell resolves on its own instead, which is what Appraisal expects. The trade-off is real and worth naming: an upstream release can turn CI red without a commit here. For a library that is arguably the point, since it is also how a user's install would break.

Also

  • pg drops from runtime to development dependency. Revisions need PostgreSQL column types, but the adapter is the host app's to declare — this was blocking installation for anyone not already on pg.
  • README gains the compatibility table, the PostgreSQL env vars the specs need, and how to run a single appraisal.
  • spec/dummy/tmp/ added to .gitignore — generated by the generator specs.

Verification

The gem was installable on nothing current. `required_ruby_version` capped at
`< 3.4` and `activerecord` was pinned to `~> 6.1`, so no supported Rails and no
current Ruby satisfied it.

The library itself turned out to be compatible already. One line in the spec
harness was not: `MigrationContext.new` took a required schema migration class
in 6.1, gained a default in 7.0, and dropped the argument in 7.2. It is now
selected by Active Record version.

Widen the requirements to `>= 6.1, < 9` and `>= 3.0`, and add an Appraisal
matrix that proves the claim rather than asserting it. All six Rails versions
run the full suite. CI covers the Ruby/Rails combinations that exist, excluding
Rails < 7.1 on Ruby 3.4 — those need `mutex_m`, which left the default gems —
and each Rails version below its own minimum Ruby.

The concurrent-ruby pin moves out of the root Gemfile and into the 6.1 and 7.0
appraisals, which are the only places it applies. The rubocop-factory_bot pin
stays: it constrains the linter, not Rails, and removing it breaks cop config
loading against jetrockets-standard 1.29.

Drop `pg` as a runtime dependency. Revisions need PostgreSQL column types, but
the adapter belongs to the host application.
@IgorFroehner
IgorFroehner force-pushed the chore/dependency-envelope branch from 3c50729 to 63c811f Compare August 14, 2026 19:19
The committed lockfiles were generated on arm64 macOS, so PLATFORMS held only
`arm64-darwin` and every specs job failed at `bundle install` on x86_64 Linux.

Add the Linux platforms the runners use and x86_64 macOS for contributors.
A lockfile records one Ruby's resolution. The matrix runs each Rails version
across several Rubies, and the locks were generated on 3.3, so the 3.0 and 3.1
cells failed to install: i18n 1.15.2 requires Ruby >= 3.1.

Serving every cell from committed locks would need one per Ruby/Rails pair.
Let each cell resolve instead, which is what Appraisal expects.
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