From 0b5546bcef1e7afc5269d5dfab6c09c127af0e0a Mon Sep 17 00:00:00 2001 From: "benjamin.baker" Date: Tue, 8 Sep 2026 16:47:56 -0400 Subject: [PATCH] Cap plugin revision ids at the 32 chars MySQL stores Rolling the containers failed on strict MySQL: the endpoints chain could not stamp its new head with "Data too long for column version_num". Plugin version tables declare version_num VARCHAR(32) and the new revision id was 33 chars. Nothing in the suite caught it. The tests run on SQLite, which does not enforce declared column widths, so an over-long id passes locally and fails only on a real deploy - after the migration body has run and only the stamp fails. The rollback was clean here, but the failure lands at the worst possible point. Shorten the id and assert the limit in the test that already parses every plugin revision, so the next over-long id fails on a laptop instead of during a roll. Core is deliberately not covered: its alembic_version is VARCHAR(128), and it already carries ids longer than 32 that are correct. --- .../versions/0019_split_shipped_monitor_policies.py | 6 ++++-- tests/test_plugin_migrations.py | 13 ++++++++++++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/plugins/endpoints/migrations/versions/0019_split_shipped_monitor_policies.py b/plugins/endpoints/migrations/versions/0019_split_shipped_monitor_policies.py index 69d1028b..92ea28dd 100644 --- a/plugins/endpoints/migrations/versions/0019_split_shipped_monitor_policies.py +++ b/plugins/endpoints/migrations/versions/0019_split_shipped_monitor_policies.py @@ -39,7 +39,7 @@ Idempotent on the policy names, so a re-run neither duplicates the split rows nor resurrects the bundle. Guards on the policy tables being present and no-ops if they are not, matching 0007. -Revision ID: endpoints0019splitmonitorpolicies +Revision ID: endpoints0019splitpolicies Revises: endpoints0018endpointlocks """ from alembic import op @@ -49,7 +49,9 @@ import sqlalchemy as sa from datetime import datetime -revision = 'endpoints0019splitmonitorpolicies' +# Alembic stores this in alembic_version.version_num, a VARCHAR(32). A longer +# id passes on SQLite and fails on strict MySQL with "Data too long". +revision = 'endpoints0019splitpolicies' down_revision = 'endpoints0018endpointlocks' branch_labels = None depends_on = None diff --git a/tests/test_plugin_migrations.py b/tests/test_plugin_migrations.py index 146c748a..36930f35 100644 --- a/tests/test_plugin_migrations.py +++ b/tests/test_plugin_migrations.py @@ -115,7 +115,7 @@ EXPECTED_HEAD_REVISION['warranty'] = 'warranty0002proof' # on one endpoint, a caller-authority axis separate from the policy hold. # 0019 splits the 0007 bundle into one shipped Configuration Policy per monitor # and drops the shipped Business Policy, so sites compose their own. -EXPECTED_HEAD_REVISION['endpoints'] = 'endpoints0019splitmonitorpolicies' +EXPECTED_HEAD_REVISION['endpoints'] = 'endpoints0019splitpolicies' # Plugins built after the cutover: their 0001 baseline really creates tables the # core chain never owned. @@ -208,6 +208,17 @@ def test_plugin_has_migration_chain(plugin): if isinstance(node.value, ast.Constant): down = node.value.value assert rev, f"{plugin}: {script.name} has no revision id" + # alembic_version_.version_num is VARCHAR(32). A longer id + # passes every SQLite test and then fails the real deploy on strict + # MySQL with "Data too long for column 'version_num'", after the + # migration body has already run. Caught + # endpoints0019splitmonitorpolicies (33 chars) during a container roll. + # The CORE chain's alembic_version is VARCHAR(128), so core ids are not + # bound by this and are not checked here. + assert len(rev) <= 32, ( + f"{plugin}: {script.name} revision id {rev!r} is {len(rev)} chars; " + 'alembic_version.version_num holds 32' + ) assert found_down, f"{plugin}: {script.name} has no down_revision" revisions.add(rev) if down is None: