Upgrade to Rails 7.1 - #11195
Conversation
| :if => ->(host) { host.managed } | ||
| end | ||
|
|
||
| # Defined explicitly (rather than via alias_attribute) because operatingsystem |
There was a problem hiding this comment.
I'm thinking whether we could utilize a small helper in ApplicationRecord that would be something like:
def self.alias_association(*aliases, original)
aliases.each do |alias_name|
alias_method alias_name, original
alias_method "#{alias_name}=", "#{original}="
end
endThat would enable you to just write alias_association :os, :operatingsystem (and so on) in the models. Thoughts?
There was a problem hiding this comment.
Thanks for the suggestion! I've been going back and forth on this.
On one hand, the explicit methods are verbose but very transparent - each one is self-contained, easy to grep for, and immediately obvious what it does without needing to know about a helper. They also handle the cases that don't fit the simple getter+setter pattern (e.g. MailNotification#mailer_method reads via self[:method] to avoid shadowing Object#method, LookupKey#value_before_type_cast is a single reader delegating to a manually-overridden method, and ApplicationRecord#to_label/to_s aren't association aliases at all). So the helper would only cover a subset of the replacements - the rest would still need explicit methods.
On the other hand, for the straightforward association aliases (os/operatingsystem, arch/architecture, hostname/name), a helper like yours would definitely cut the boilerplate.
One thing I'd suggest if we do go with a helper: use define_method instead of alias_method:
def self.alias_association(*aliases, original)
aliases.each do |alias_name|
define_method(alias_name) { send(original) }
define_method("#{alias_name}=") { |value| send("#{original}=", value) }
end
endalias_method copies the method body at the time it's called - it's a snapshot. If a plugin later prepends a module that redefines operatingsystem (which plugins do via config.to_prepare, i.e. after the model class body has already been evaluated), the os alias would still point to the pre-override version. define_method creates a live delegation that always dispatches to the current definition of the target method, so plugin overrides are picked up correctly. I couldn't find an existing plugin that actually overrides any of these specific methods today, but given Foreman's plugin ecosystem, define_method feels like the safer default.
I can either leave it as it is or add the helper either as part of this PR or as a followup - what do you think?
|
Minor docs bug this PR will cause: https://redhat.atlassian.net/browse/SAT-49436 |
Bumps the rails gem to ~> 7.1.0 (config.load_defaults stays at '7.0' deliberately, see comment in config/application.rb) and fixes the resulting compatibility breaks: - alias_attribute: replace aliases that target non-attribute methods or manually-overridden methods with explicit reader/writer methods, since Rails 7.2 will stop supporting those (arch, hostname, os, hostgroup_parameters, mailer_method, override_values/_ids/_order, select_title, to_label/to_s). - serialize :attr, Type -> serialize :attr, type: Type (positional arg deprecated). - database_cleaner (unmaintained, references ActiveRecord::Base directly) -> database_cleaner-active_record. - ActiveRecord::MigrationContext no longer accepts the old ActiveRecord::SchemaMigration class as its schema_migration arg. - assert_deprecated/assert_not_deprecated now require an explicit deprecator argument. - as_deprecation_tracker temporarily points at a fork/branch with Rails 7.1 deprecators-registry support, pending upstream release (github.com/domcleal/as_deprecation_tracker/pull/5). - basic_rest_response_test's pagination test no longer matches redirect_to's response body, which Rails 7.1 stopped populating by default (rails/rails@c2e756a9); assert on @response.redirect? instead. - SubnetTest: FactoryBot.build_stubbed fakes persisted? without ever inserting a row, but Rails 7.1's uniqueness validator now skips its DB check for a persisted, unchanged attribute already covered by a unique index (rails/rails#45149), trusting the DB already enforced it on insert. That trust is misplaced for build_stubbed objects. Same issue/fix as thoughtbot/factory_bot#1634; use build instead. - Api::V2::RegistrationControllerTest: RecordNotFound now calls #inspect on the id (rails/rails@dd6fcc43), a security fix for ANSI escape injection into logs via a crafted id (CVE-2025-55193 / GHSA-76r7-hhxj-r776, backported to activerecord 7.1.5.2+). String ids (as opposed to Integer) are now quoted in the message. - EncryptValue#decrypt_field: MessageEncryptor's refactor (rails/rails#47326) made decrypt_and_verify always raise MessageEncryptor::InvalidMessage on a corrupt message, regardless of cipher; non-AEAD ciphers (aes-256-cbc, used here) previously raised MessageVerifier::InvalidSignature instead. Rescue both so decryption failures keep degrading gracefully instead of raising.
eda50e2 to
08bfd99
Compare
|
To test plugins against this PR you can use https://github.com/theforeman/actions#foreman-plugin-ruby-tests. So update the plugin's workflow to use |
| # search for a metric - e.g.: | ||
| # Host::Managed.with("failed") --> all reports which have a failed counter > 0 | ||
| # Host::Managed.with("failed",20) --> all reports which have a failed counter > 20 | ||
| scope :with, lambda { |*arg| |
There was a problem hiding this comment.
I might be blind or missing something, but why do we drop with scopes?
There was a problem hiding this comment.
Rails 7.1 added ActiveRecord::QueryMethods#with (native CTE support). Both ConfigReport and Host::Managed had their own unrelated scope :with, which now collides and raises ArgumentError at boot under 7.1.
I initially considered renaming to with_metric, just as we have with_status, but I've found no usage of this and it doesn't seem to be a public, documented interface either. So unless I am wrong and it actually is used, I would prefer deleting it to just renaming.
|
|
||
| module Foreman | ||
| class Application < Rails::Application | ||
| # Intentionally not bumped to '7.1' yet even though the rails gem itself is. |
There was a problem hiding this comment.
We should load_defaults for 7.1 here. All of the new settings that break the app should be overridable if we're not ready to use them, see the current pattern below. Ideally with follow ups or at least comments for the future.
There was a problem hiding this comment.
I agree we should load 7.1, however I've chosen the path of least resistance here (loading 7.0) to support Rails 7.1 ASAP and intended to do the bump, with required fixes, in a separate PR. Do we prefer doing it all in this PR?
There was a problem hiding this comment.
I think it would be interesting to know what breaks, especially in plugins but we certainly can break it up. Usually we use Refs #redmine - ... instead of Fixes #redmine - for that.
There was a problem hiding this comment.
Does it mean you would not consider the (future) redmine ticket created from "Upgrade foreman to Rails 7.1" JIRA ticket a complete fix? Because it uses defaults from 7.0, as a kind of compatibility layer so we don't have to change everything immediately?
My opinion is that redmine will be fixed by this PR including the 7.0 defaults because it, indeed, upgrades foreman to Rails 7.1. But if you disagree, then obviously I will have to include changes for 7.1 config to ship it completely.
Summary
Bumps the
railsgem to~> 7.1.0.config.load_defaultsis deliberately kept at'7.0'(see comment inconfig/application.rb) — loading 7.1 defaults activates ~20 behavior changes at once (includingActiveRecord::Encryptionswitching to SHA-256, which affects already-encrypted data) across core and every plugin, and should be its own separate, incremental effort.Depends on domcleal/as_deprecation_tracker#5 (Rails 7.1 support for the deprecators registry) being merged and released.
bundler.d/test.rbcurrently points at a fork/branch of that gem as a temporary safety net so CI doesn't silently lose deprecation-tracking coverage in the meantime; this must be reverted to the plain~> 1.6constraint once a release containing that fix is out, before this PR can be merged.Also blocked on auditing the ~30 bundled plugins for their own Rails 7.1 compatibility.
Compatibility fixes
arch,hostname,os,hostgroup_parameters,mailer_method,override_values/_ids/_order,select_title,to_label/to_s).serialize :attr, Type→serialize :attr, type: Type(positional type arg deprecated).ActiveRecord::Basedirectly →database_cleaner-active_record.ActiveRecord::SchemaMigrationclass as itsschema_migrationarg (app/registries/foreman/plugin.rb).redirect_tono longer sets a default HTML body (rails/rails@c2e756a9); assert on@response.redirect?instead.persisted?, unchanged attribute already covered by a unique index (rails/rails#45149), trusting the DB already enforced it on insert — an assumptionFactoryBot.build_stubbedviolates (same issue as thoughtbot/factory_bot#1634). Switched tobuild.RecordNotFoundnow calls#inspecton the id, a security fix for ANSI escape injection into logs via a crafted id (CVE-2025-55193 / GHSA-76r7-hhxj-r776). String ids are now quoted in the message.MessageEncryptor's refactor (rails/rails#47326) makesdecrypt_and_verifyalways raiseMessageEncryptor::InvalidMessageon a corrupt message regardless of cipher; non-AEAD ciphers (aes-256-cbc, used here) previously raisedMessageVerifier::InvalidSignature. Now rescuing both so decryption failures keep degrading gracefully.Test plan
bin/rails testsuite passes (verified locally; CI will confirm)as_deprecation_trackerfork PR merged/released, then revertbundler.d/test.rbto~> 1.6