Skip to content

test: a migration declared above the app version can never run - #867

Merged
blaipr merged 1 commit into
mainfrom
test/a-migration-declared-above-the-app-version-can-never-run
Aug 23, 2026
Merged

test: a migration declared above the app version can never run#867
blaipr merged 1 commit into
mainfrom
test/a-migration-declared-above-the-app-version-can-never-run

Conversation

@blaipr

@blaipr blaipr commented Aug 23, 2026

Copy link
Copy Markdown
Member

ModuleBase::checkUpgradeNeeded() asks whether the stored version is behind the version
the code reports, and Version::getVersionStringNormalized() builds that from
AppInfoInterface::APP_VERSION and APP_BUILD alone. Those two constants have not moved
since the rewrite was imported, so they still say 400.21031301 — while both shipped
migrations declare 400.24210101 and 400.24240101, above it.

Installer stamps the same value it reports, so every installation this codebase performs
records 400.21031301 and is never behind. No upgrade is triggered, and neither migration
can run. It fails silently and looks exactly like an installation that had nothing to do.

This asserts the invariant over every declared version: a handler above the application's
own can never be the reason an upgrade happens. The two that are currently in that state
are listed by name, in the same shape RoutesAreDispatchableTest uses for the action ids
that resolve to no controller — the test asserts those two are still unreachable and
that nothing else is, so a migration added tomorrow cannot quietly join them.

Resolving the two is a deployment decision rather than a test change, and the reasoning is
recorded on the constant: bumping APP_BUILD makes both reachable, but 40024210101.sql
drops a column that dbstructure.sql already ships without, so it would then run against
schemas that already have it applied and fail. The migration has to be made conditional
first, or those two accepted as applying only to installations arriving from 3.2.

Checked by removing one of the two from the list: the test fails, naming the handler, the
version it declares and the version the application reports.

`ModuleBase::checkUpgradeNeeded()` asks whether the stored version is behind the version
the code reports, and `Version::getVersionStringNormalized()` builds that from
`AppInfoInterface::APP_VERSION` and `APP_BUILD` alone. Those two constants have not moved
since the rewrite was imported, so they still say `400.21031301` — while both shipped
migrations declare `400.24210101` and `400.24240101`, above it.

`Installer` stamps the same value it reports, so every installation this codebase performs
records `400.21031301` and is never behind. No upgrade is triggered, and neither migration
can run. It fails silently and looks exactly like an installation that had nothing to do.

This asserts the invariant over every declared version: a handler above the application's
own can never be the reason an upgrade happens. The two that are currently in that state
are listed by name, in the same shape RoutesAreDispatchableTest uses for the action ids
that resolve to no controller — the test asserts those two are *still* unreachable and
that nothing else is, so a migration added tomorrow cannot quietly join them.

Resolving the two is a deployment decision rather than a test change, and the reasoning is
recorded on the constant: bumping APP_BUILD makes both reachable, but `40024210101.sql`
drops a column that `dbstructure.sql` already ships without, so it would then run against
schemas that already have it applied and fail. The migration has to be made conditional
first, or those two accepted as applying only to installations arriving from 3.2.

Checked by removing one of the two from the list: the test fails, naming the handler, the
version it declares and the version the application reports.
@blaipr
blaipr merged commit cf611cc into main Aug 23, 2026
8 checks passed
@blaipr
blaipr deleted the test/a-migration-declared-above-the-app-version-can-never-run branch August 23, 2026 23:05
blaipr added a commit that referenced this pull request Aug 23, 2026
…873)

#867 recorded the two shipped migrations as declaring versions the application can never
reach, and left resolving that as a deployment decision. The first half is true and the
second was the wrong conclusion, so this corrects the record before somebody acts on it.

`AppInfoInterface::APP_BUILD` has not moved since the rewrite was imported, so the code
reports `400.21031301` while the migrations declare `400.24210101` and `400.24240101`.
But `checkUpgradeNeeded()` compares the *stored* version against that, and
`getTargetUpgradeHandlers()` then selects every handler above the stored one. An
installation arriving from 3.2 stores something like `320.19012701`, which is behind
both — so it triggers the upgrade and runs both migrations. Measured against the real
Version class rather than reasoned about.

What cannot reach them is an installation this rewrite performed, which is stamped with
the version the code reports. Neither migration applies to one: `dbstructure.sql` already
ships CustomFieldData with the composite primary key and no `id` column, and text written
after the escaping change is stored as typed.

Bumping APP_BUILD would make it worse. `40024210101.sql` would run against schemas that
already have it applied, dropping a column that is not there; and `40024240101.sql`
decodes stored text, saying in its own header that it is not idempotent and that no decode
of stored text could be — so on an installation whose text is already correct it turns a
name typed as `Q&A` into something else. The population that would benefit is the narrow
one installed by this rewrite before the escaping change; the population that would be
damaged is every installation since.

The two stay on the list, and the list keeps doing the job it was added for: a third
migration cannot join them unnoticed. No behaviour changes here — only the reasoning
recorded on the constant, which is what the next person will read before touching
APP_BUILD.
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