Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions docs/operations/deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,20 +59,29 @@ Keys are minted by the operator CLI and never over HTTP. Run it as a one-off
container against the same database:

```bash
docker compose run --rm api python -m hymical_forms.cli create-key --name admin
docker compose run --rm --no-deps api python -m hymical_forms.cli create-key --name admin
```

The key is printed once. Store it; it cannot be shown again. See
[Authentication](../api/authentication.md).

!!! warning "Keep `--no-deps` on every one-off command"

The `api` service depends on `migrate`, so `docker compose run` without
`--no-deps` runs `alembic upgrade head` before the command. After a
`docker compose build`, that would migrate the database under the old `api`
and `worker`, against the [deploy order](#deploy-order). With `--no-deps` the
command only checks the schema revision, like the API and the worker do, and
refuses to run against one it does not expect.

### Retention cleanup

Nothing is deleted automatically. Run the sweep the same way, from whatever
scheduler you already use:

```bash
docker compose run --rm api python -m hymical_forms.cli cleanup-submissions --dry-run
docker compose run --rm api python -m hymical_forms.cli cleanup-submissions
docker compose run --rm --no-deps api python -m hymical_forms.cli cleanup-submissions --dry-run
docker compose run --rm --no-deps api python -m hymical_forms.cli cleanup-submissions
```

See [Submission retention](retention.md).
Expand Down
9 changes: 9 additions & 0 deletions src/hymical_forms/migrations/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,22 @@

from __future__ import annotations

from logging.config import fileConfig

from alembic import context
from sqlalchemy import Connection

from hymical_forms.config import Settings
from hymical_forms.db import create_engine_from_url
from hymical_forms.models import Base

# Only the alembic command reads an ini file. Without this its logging section is
# never applied and a migration run prints nothing, so an operator cannot tell
# what it did. A Config built in code (the tests, schema.py) has no file and
# leaves the caller's logging alone.
if context.config.config_file_name is not None:
fileConfig(context.config.config_file_name, disable_existing_loggers=False)

target_metadata = Base.metadata


Expand Down
37 changes: 37 additions & 0 deletions tests/test_alembic_command.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""
the alembic command an operator runs to migrate, as the container image runs it
"""

from __future__ import annotations

import subprocess
import sys
from pathlib import Path

REPOSITORY_ROOT = Path(__file__).resolve().parent.parent


def test_the_alembic_command_reports_each_revision_it_applies(tmp_path: Path) -> None:
# The one-shot migrate step is otherwise silent, so an operator cannot tell
# from its output whether it applied six revisions or none. Revision 0001 is
# used because SQLite cannot replay the chain to head (see AGENTS.md).
database = tmp_path / "forms.db"
completed = subprocess.run(
[
sys.executable,
"-m",
"alembic",
"-x",
f"database_url=sqlite:///{database.as_posix()}",
"upgrade",
"0001",
],
cwd=REPOSITORY_ROOT,
capture_output=True,
text=True,
timeout=120,
check=False,
)

assert completed.returncode == 0, completed.stderr
assert "Running upgrade -> 0001" in completed.stderr
Loading