Skip to content

LT-53534: support Rails 8 and Devise 5 - #1

Closed
relsett wants to merge 16 commits into
masterfrom
feature/LT-53534-rails-8-devise-5-compatibility
Closed

LT-53534: support Rails 8 and Devise 5#1
relsett wants to merge 16 commits into
masterfrom
feature/LT-53534-rails-8-devise-5-compatibility

Conversation

@relsett

@relsett relsett commented Aug 24, 2026

Copy link
Copy Markdown

Что меняется

  • переиспользованы runtime fixes из upstream PR Migrate CI to GitHub Actions and expand the Ruby/Rails test matrix Houdini/two_factor_authentication#219 с сохранением авторства;
  • update_attributes заменён на update;
  • TOTP timestamp сохраняется как UTC Time, поэтому Rails 8 корректно пишет его в timestamp column;
  • resend_code остаётся collection route и больше не попадает в resource actions;
  • добавлен CI на Ruby 3.4.10, Rails 8.0.5.1/8.1.3.1, Devise 4.9.4/5.0.4 и ROTP 6.2.0;
  • добавлены regression tests на resend route и persist/reload/replay TOTP.

Upstream: Houdini#219

Проверка

Локально в Docker: Rails 8.1.3.1 + Devise 5.0.4 + ROTP 6.2.0 — 81 examples, 0 failures.

После merge leveltravel PR #26241 будет закреплён на exact commit SHA этого fork; application monkey patch будет удалён.

willnet and others added 16 commits August 24, 2026 17:20
Rails 7 removes Active Record's deprecated update_attributes method. Direct OTP issuance and cleanup would therefore fail before an authentication flow could complete.

Switch these calls to update and rename the non-persisted GuestUser test double's matching method so the test harness continues to mirror the production interface.
With the newer ROTP versions required by the Rails 8 matrix, verification returns a Unix timestamp while the persisted column expects a datetime. Convert it explicitly to UTC Time before assignment.

Rails 8 also validates the resource action list strictly: resend_code is declared as a separate collection route and must not be listed as a resource action. Keep that endpoint while limiting the resource actions to show and update.
Current concurrent-ruby releases no longer load Ruby's logger standard library as a side effect. Older Active Support versions still reference Logger constants during initialization, so the dummy application can fail before the test suite starts when those dependencies are resolved together.

Require logger explicitly before loading the Rails boot files. This keeps the historical framework targets bootable without constraining concurrent-ruby or changing application behavior.
The historical dummy application predates the manifest required by current sprockets-rails releases. When the expanded matrix boots a modern Rails stack with the asset railtie enabled, initialization stops because no application asset manifest is present.

Add the standard manifest and link the existing JavaScript and stylesheet directories. Older Rails targets retain their existing asset behavior while newer targets can complete application boot.
Zeitwerk infers SmsProvider from sms_provider.rb by default, while the dummy application intentionally defines SMSProvider. Register SMS as an acronym so the file name and constant agree when modern Rails validates or autoloads the test application.

Require the standalone provider explicitly from spec_helper as well. This makes it available before examples and support hooks run under both the classic and Zeitwerk autoloaders instead of relying on generation-specific load timing.
The generator spec wrote its output into a repository-relative tmp directory, tying test artifacts to the checkout layout. Use Ruby's system temporary directory so Rails' generator test helpers own an isolated destination consistently across local and hosted environments.

The existence assertion also relied on Ammeter's deprecated override of RSpec's exist matcher. That compatibility matcher warns through the legacy ActiveSupport::Deprecation class delegator, which newer Rails releases no longer expose. Check File.exist? directly through satisfy to preserve the assertion without depending on Ammeter or RSpec internals.
Newer ROTP releases percent-encode provisioning labels, so matching the raw URI rejects output that is semantically equivalent and valid. Parse the URI and verify its scheme, host, decoded path, and secret independently to preserve the authentication contract across ROTP generations.

Use URI.decode_www_form for query parameters in both examples and decode the issuer path before comparison. This removes the separate CGI parsing shape and keeps the assertions focused on the values an authenticator receives rather than their wire encoding.
The pessimistic Capybara 2.5 constraint keeps the test bundle on the 2.x line, which uses a Proc API removed by Ruby 4 and prevents the feature suite from booting on the newest matrix targets.

Retain 2.5 as the lower bound while allowing any release below Capybara 4. This lets Bundler select the maintained 3.x line for current Rubies without dropping the older Capybara versions needed by the historical Ruby and Rails combinations.
A single unconstrained dependency set no longer resolves to versions that can boot every supported runtime. Older Rubies need the last compatible Nokogiri releases, and Loofah must stay below the release that assumes the Nokogiri HTML4 namespace. Rails 4.2 and Rails 5.2 through 7.0 also require different SQLite adapter generations.

Add ostruct only for Ruby 4, where it is no longer bundled as a default library, and declare sprockets-rails for the dummy application instead of depending on historical Rails defaults.

Interpret a two-component RAILS_VERSION as an exact minor series by expanding it before applying the pessimistic constraint. This prevents a Rails 8.0 job from silently resolving Rails 8.1. Rename the edge selector from master to main and pin the Rails repository branch explicitly.
The Travis configuration only covered Ruby 2.2 through 2.5 against Rails 4.2, 5.2, and the former master branch. Move the project to GitHub Actions and exercise 22 explicit combinations spanning Ruby 2.2 through 4.0 and Rails 4.2 through 8.1 plus Rails main.

Every versioned Rails entry is required because the compatibility fixes now allow those releases to complete database setup and all 79 examples. Rails main follows unreleased framework changes, so allow only that job to fail while still reporting upstream compatibility regressions. Keep fail-fast disabled so one failure does not hide results from the rest of the matrix.

Run the entire matrix on ubuntu-latest from the job definition and keep each matrix record focused on its dependency combination. Derive the Rails main exception directly from matrix.rails instead of repeating operating-system and experimental flags in every record.

Use actions/checkout v7 without persisted credentials, ruby/setup-ruby with Bundler caching, read-only contents permission, and a per-job timeout. Remove Travis only after the replacement workflow and the expanded matrix have been prepared.
The first hosted GitHub Actions run showed that ruby/setup-ruby selected Bundler 2.3 for Ruby 2.3 through 2.5. Rails 4.2.11.3 declares bundler >= 1.3 and < 2.0, so dependency resolution stopped during the setup step before the database or specs could run.

Pin Bundler 1.17.3 on each Rails 4.2 matrix entry and retain the setup action default for every newer Rails target. Ruby 2.2 had already selected a compatible Bundler automatically, but an explicit pin keeps all Rails 4.2 jobs deterministic.
The hosted Ruby 2.7 / Rails 7.1 job resolved Psych 5.4 and sqlite3 1.7.3. Bundler installed their native extensions in parallel, allowing sqlite3 extconf to load Ruby 2.7's bundled Psych Ruby code with the Psych 5 extension. The incompatible parser APIs caused sqlite3 compilation to fail before tests started.

Constrain this one historical dependency set to Psych 4, whose extension remains compatible with Ruby 2.7's loader. Newer Ruby and Rails combinations continue using the latest available Psych. The CI-equivalent Bundler 2.4 parallel install now reaches database setup and passes all 79 examples.
@relsett

relsett commented Aug 24, 2026

Copy link
Copy Markdown
Author

Заменён на #2: новый PR построен от точного v2.2.0 и не включает unrelated post-release изменения upstream master.

@relsett relsett closed this Aug 24, 2026
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.

2 participants