|
| 1 | +# Upgrading a scaffolded host |
| 2 | + |
| 3 | +A host generated by `smpy new` or `smpy create-host` owns its Alembic history. |
| 4 | +Its initial revision reflects the models installed at generation time; it does |
| 5 | +not import the framework repository's historical revisions. Updating packages |
| 6 | +and running schema autogenerate cannot reproduce historical data transformations. |
| 7 | + |
| 8 | +## Check and prepare |
| 9 | + |
| 10 | +Back up the database and review the release notes before upgrading packages. |
| 11 | +From the project root, with the host's Python environment active: |
| 12 | + |
| 13 | +```bash |
| 14 | +uv run smpy doctor |
| 15 | +uv run alembic -c host/alembic.ini heads |
| 16 | +uv run smpy migrations prepare mail.settings_namespace_v1 --head <your-host-head> |
| 17 | +uv run smpy migrations prepare users.hashed_bearer_tokens_v1 --head users@head |
| 18 | +``` |
| 19 | + |
| 20 | +Replace `<your-host-head>` with an actual head in **your** graph. Use `users@head` |
| 21 | +only if your graph already declares that label; otherwise pass the relevant host |
| 22 | +head id. Preparing the mail revision may advance the same branch, so inspect |
| 23 | +`heads` again before preparing the next revision. For flat hosts use |
| 24 | +`alembic.ini` instead of `host/alembic.ini`. The preparation command detects both |
| 25 | +layouts, or accepts `--config path/to/alembic.ini`. |
| 26 | + |
| 27 | +Preparation writes one self-contained revision into that host's |
| 28 | +`migrations/versions`. It never connects to the database or applies the revision. |
| 29 | +The frozen recipes ship in the CLI package, and generated revisions import only |
| 30 | +Alembic and SQLAlchemy: future module/package upgrades cannot change their code. |
| 31 | +The first prepared recipe for an unlabelled module declares `mail` or `users`; |
| 32 | +existing labels are reused without redeclaration. `--head` is required and must |
| 33 | +resolve to one current host head. No upstream roots are grafted onto your graph. |
| 34 | + |
| 35 | +## Review and apply |
| 36 | + |
| 37 | +Review and keep the generated revision in your application's version control. |
| 38 | +Do not generate a token invalidation revision for a baseline that is already |
| 39 | +compatible: applying it deliberately signs clients out. |
| 40 | + |
| 41 | +| Upgrade id | Effect | |
| 42 | +|---|---| |
| 43 | +| `mail.settings_namespace_v1` | Move system-scope `users.mailer` to `mail.backend` and the six `users.smtp_*` transport keys to `mail.*`. Existing target values win; stale source rows are removed. Tenant/user settings are untouched. Downgrade reverses the mapping with the same target-wins rule. | |
| 44 | +| `users.hashed_bearer_tokens_v1` | Delete access and refresh credentials **before** changing both token columns to `String(64)`. Downgrade deletes credentials again and restores the legacy column types; it cannot recover secrets. | |
| 45 | + |
| 46 | +Stop every old application worker, including background workers, before applying |
| 47 | +the token upgrade. Mixed old/new workers must not mint or use plaintext tokens |
| 48 | +during this upgrade. Clients using bearer tokens or `sm_auth` must sign in again. |
| 49 | +Other session cookies are unaffected. After reviewing and scheduling the upgrade: |
| 50 | + |
| 51 | +```bash |
| 52 | +uv run alembic -c host/alembic.ini upgrade heads |
| 53 | +uv run smpy doctor |
| 54 | +``` |
| 55 | + |
| 56 | +Restart the application with the upgraded packages. Role provisioning remains an |
| 57 | +idempotent startup step; it needs no prepared data revision. |
| 58 | + |
| 59 | +## Compatibility checks |
| 60 | + |
| 61 | +Modules expose read-only `check_data_compatibility(db)` hooks with semantic upgrade |
| 62 | +ids. Boot runs them before module settings hydration and module startup. Doctor |
| 63 | +(`make doctor` or `smpy doctor`) reports them as `SM033` and exits nonzero on errors. |
| 64 | + |
| 65 | +Unmoved system mail keys warn. Incompatible token columns or legacy token |
| 66 | +representations refuse boot outside development; development logs warnings. |
| 67 | +Checks inspect state, so equivalent manual ports are accepted without requiring |
| 68 | +upstream revision ids. A current schema-only baseline with empty token tables |
| 69 | +passes and does not trigger credential deletion. |
| 70 | + |
| 71 | +A never-migrated database skips the initial check so the first-run wizard stays |
| 72 | +reachable. Checks replay after wizard/out-of-band migrations before deferred |
| 73 | +settings hydration/startup; an incompatibility keeps the setup gate closed. |
| 74 | +This is detection and explicit preparation for the two bundled upgrades, not an |
| 75 | +automatic migration system for arbitrary third-party module data. |
0 commit comments