Three related fixes from PR #493 deploy incident (deploy/1156 failed at
bootstrap; tradein-backend USER MAPPING never created):
1. deploy-tradein.yml — restart tradein-backend AFTER SQL migrations apply.
Root cause: `compose up -d` runs at line 157 before migrations at line 163,
so tradein-backend lifespan hook (ensure_fdw_user_mapping) tries to
CREATE USER MAPPING against FOREIGN SERVER gendesign_remote that doesn't
exist yet (created by 060_postgres_fdw_extension.sql later). Hook fails
permanently with `psycopg.errors.UndefinedObject: server "gendesign_remote"
does not exist`; never retried. Restart after migrations triggers retry.
2. deploy.yml — extract inline psql DO block into ops/db-bootstrap/
set_tradein_fdw_password.sql, run via `psql -v pw=$ENV -f <file>`.
Original block used multi-line backslash continuations inside double-quoted
bash string with $$-dollar-quoting + psql var substitution — fragile
escaping likely caused deploy/1156 failure between migration apply (09:01:03)
and compose up -d. Clean .sql file with `-v pw=` substitution is safe and
psql-native.
3. deploy.yml — verify postgres in gendesign_shared after `compose up -d`,
force-recreate if not. Compose sometimes skips recreate on network membership
change (PR #493 introduced `networks: shared (alias gendesign-postgres)` to
postgres service). Without postgres in shared, tradein-postgres can't resolve
DNS `gendesign-postgres` -> FDW queries fail with "could not translate host
name". Manual fix on prod: `docker network connect --alias gendesign-postgres
gendesign_shared gendesign-postgres-1`.
No code changes — only deploy workflow and one new SQL file.
- backend/alembic/* — alembic infra (env.py, script.py.mako). versions/ empty
for now; first migration goes in Stage 2a when models are finalized.
- backend/Dockerfile: bake alembic.ini + alembic/ into the image so
`docker compose exec backend alembic upgrade head` works in prod.
- backend/db/init/99_drop_unused_extensions.sql + bind-mount in both compose
files: drops postgis-image's TIGER/topology/fuzzystrmatch on fresh volumes.
- .pre-commit-config.yaml + pre-commit in dev deps: ruff/prettier on commit
to stop CI failures like UP035 from leaking out.
- ops/backup.sh, ops/restore.sh: pg_dump cron script with optional Selectel S3
upload. 7-day local retention. Restore guard: requires RESTORE_CONFIRM=yes.
- Makefile: new targets `make migration NAME=...`, `make pre-commit-install`.
- backend/.env.example: SENTRY_DSN comment with sentry.io reference.