Provision and apply the schema from Compose (task 205) #145

Merged
rob merged 3 commits from task-205-compose-auto-schema into main 2026-08-13 09:59:05 +00:00
Owner

Two new full-profile services, provision then schema-upgrade, run ahead of api and gate it with service_completed_successfully — a failed step stops api starting at all. PlaceMark.Database gets a Dockerfile (none existed), following ADR-0104's shape: pinned SDK to build, runtime:10.0-alpine to run (no ASP.NET dependency, unlike api/webui), non-root.

The decision the ticket left open: provisioning runs automatically too, not schema-only. provision connects as the superuser Compose already creates, and grants nothing wider than the provision command already has. This is what makes a fresh clone's docker compose --profile full up --wait work unattended — verified directly on a throwaway volume, not assumed. No seed role by default (matches CI's own provisioning step); overridable via .env.

Verified directly, not just built: fresh volume brings the whole stack up and reaches api healthy with schema applied; torn down and brought up again is a no-op (Password reset on: ..., No pending scripts); a deliberately broken script leaves api in Compose's own created state, never started, naming the exact failure. docker compose up with no profile still starts postgres alone (docker compose config --services).

ADR-0123, README's "Full local stack" section rewritten to match, .env.example updated with the two new overridable passwords.

Two new `full`-profile services, `provision` then `schema-upgrade`, run ahead of `api` and gate it with `service_completed_successfully` — a failed step stops `api` starting at all. `PlaceMark.Database` gets a Dockerfile (none existed), following ADR-0104's shape: pinned SDK to build, `runtime:10.0-alpine` to run (no ASP.NET dependency, unlike `api`/`webui`), non-root. The decision the ticket left open: **provisioning runs automatically too**, not schema-only. `provision` connects as the superuser Compose already creates, and grants nothing wider than the `provision` command already has. This is what makes a fresh clone's `docker compose --profile full up --wait` work unattended — verified directly on a throwaway volume, not assumed. No seed role by default (matches CI's own provisioning step); overridable via `.env`. Verified directly, not just built: fresh volume brings the whole stack up and reaches `api` healthy with schema applied; torn down and brought up again is a no-op (`Password reset on: ...`, `No pending scripts`); a deliberately broken script leaves `api` in Compose's own `created` state, never started, naming the exact failure. `docker compose up` with no profile still starts postgres alone (`docker compose config --services`). ADR-0123, README's "Full local stack" section rewritten to match, `.env.example` updated with the two new overridable passwords.
Regenerate the ADR index for ADR-0123
Some checks failed
CI / build (pull_request) Failing after 22s
CI / e2e (pull_request) Has been skipped
CI / container-images (pull_request) Has been skipped
3035aa616a
Merge remote-tracking branch 'origin/main' into task-205-compose-auto-schema
All checks were successful
CI / build (pull_request) Successful in 3m13s
CI / container-images (pull_request) Has been skipped
CI / e2e (pull_request) Successful in 2m1s
137149dc46
rob left a comment

Verdict: mergeable

Reviewed at 137149d (commit_id matches head). CI green (build + e2e, run #523).

Reproduced the load-bearing claims directly, on an isolated project name/ports/volume (placemark-review145) so Rob's own placemark-postgres-1 and postgres-data were never touched — confirmed both before and after:

  1. Ordering and failure gating, real not hopeful. Fresh volume, docker compose --profile full up -d --build --wait: provisionschema-upgradeapi/webui, all healthy, unattended. Then a genuinely malformed script (my first attempt used a valid -- SQL comment and proved nothing — redid it with actual garbage): schema-upgrade exited 1 naming the exact script and Postgres's own syntax error, and api-1/webui-1 both stayed in Compose's Created state, StartedAt the zero value, never started. Matches the ADR's own account exactly.
  2. Fresh-volume and re-run behaviour, both reproduced: first run creates the roles and applies all four scripts; torn down (keeping the volume) and brought up again, provision reports Password reset on: ... and schema-upgrade reports No pending scripts, api reaching healthy again with no failures.
  3. No-profile docker compose up still starts postgres alonedocker compose config --services returns only postgres with no profile flag, --profile full config --services returns all five.
  4. ADR-0123 argues the provisioning-in-Compose decision rather than asserting it — the "obvious shape collides with ADR-0031" section and the explicit rejection of provisioning from inside the API container are both real arguments, not restated conclusions. Seed role: confirmed unset by default in docker-compose.yml, .env.example and the ADR consistently, matching CI's own provisioning step, with the drop-on-rerun behaviour called out plainly in both .env.example and the README.
  5. Dockerfile matches ADR-0104's shape — pinned SDK build stage, non-root USER $APP_UID, runtime (not aspnet) base since it has no ASP.NET dependency, no EXPOSE/healthcheck since it runs to completion. Builds clean from this head, which already has #146's SSH.NET pin merged in — and structurally can't have been at risk from that change anyway: this Dockerfile restores only PlaceMark.Database.csproj directly, which carries no reference to any of the three test projects #146 touched.
  6. README's "Full local stack" section matches current behaviour — the manual provision/schema-upgrade commands are gone from that section, replaced with the automatic account; the plain Postgres-only workflow above it is untouched, still manual, as it should be.

One non-issue worth recording rather than raising: both provision and schema-upgrade log a Cannot load library libgssapi_krb5.so.2 line on every run. Confirmed this is pre-existing and not introduced here — PlaceMark.Api's own already-shipped aspnet:10.0-alpine image logs the identical line and has for as long as that Dockerfile has existed; it's Npgsql's own harmless GSSAPI probe against an Alpine base with no krb5-libs, and every command still exits 0 and does what it says. Not this PR's to fix.

Nothing outside .dockerignore, .env.example, README.md, docker-compose.yml, the new ADR/index entry and the new Dockerfile changed.

Verdict: mergeable Reviewed at 137149d (commit_id matches head). CI green (build + e2e, run #523). Reproduced the load-bearing claims directly, on an isolated project name/ports/volume (`placemark-review145`) so Rob's own `placemark-postgres-1` and `postgres-data` were never touched — confirmed both before and after: 1. **Ordering and failure gating, real not hopeful.** Fresh volume, `docker compose --profile full up -d --build --wait`: `provision` → `schema-upgrade` → `api`/`webui`, all healthy, unattended. Then a genuinely malformed script (my first attempt used a valid `--` SQL comment and proved nothing — redid it with actual garbage): `schema-upgrade` exited 1 naming the exact script and Postgres's own syntax error, and `api-1`/`webui-1` both stayed in Compose's `Created` state, `StartedAt` the zero value, never started. Matches the ADR's own account exactly. 2. **Fresh-volume and re-run behaviour**, both reproduced: first run creates the roles and applies all four scripts; torn down (keeping the volume) and brought up again, `provision` reports `Password reset on: ...` and `schema-upgrade` reports `No pending scripts`, `api` reaching healthy again with no failures. 3. **No-profile `docker compose up` still starts postgres alone** — `docker compose config --services` returns only `postgres` with no profile flag, `--profile full config --services` returns all five. 4. **ADR-0123 argues the provisioning-in-Compose decision rather than asserting it** — the "obvious shape collides with ADR-0031" section and the explicit rejection of provisioning from inside the API container are both real arguments, not restated conclusions. Seed role: confirmed unset by default in `docker-compose.yml`, `.env.example` and the ADR consistently, matching CI's own provisioning step, with the drop-on-rerun behaviour called out plainly in both `.env.example` and the README. 5. **Dockerfile matches ADR-0104's shape** — pinned SDK build stage, non-root `USER $APP_UID`, `runtime` (not `aspnet`) base since it has no ASP.NET dependency, no `EXPOSE`/healthcheck since it runs to completion. Builds clean from this head, which already has #146's SSH.NET pin merged in — and structurally can't have been at risk from that change anyway: this Dockerfile restores only `PlaceMark.Database.csproj` directly, which carries no reference to any of the three test projects #146 touched. 6. **README's "Full local stack" section matches current behaviour** — the manual `provision`/schema-upgrade commands are gone from that section, replaced with the automatic account; the plain Postgres-only workflow above it is untouched, still manual, as it should be. One non-issue worth recording rather than raising: both `provision` and `schema-upgrade` log a `Cannot load library libgssapi_krb5.so.2` line on every run. Confirmed this is pre-existing and not introduced here — `PlaceMark.Api`'s own already-shipped `aspnet:10.0-alpine` image logs the identical line and has for as long as that Dockerfile has existed; it's Npgsql's own harmless GSSAPI probe against an Alpine base with no `krb5-libs`, and every command still exits 0 and does what it says. Not this PR's to fix. Nothing outside `.dockerignore`, `.env.example`, `README.md`, `docker-compose.yml`, the new ADR/index entry and the new Dockerfile changed.
rob merged commit 71d6548b50 into main 2026-08-13 09:59:05 +00:00
rob deleted branch task-205-compose-auto-schema 2026-08-13 09:59:05 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
rob/PlaceMark!145
No description provided.