Adds .gitea/workflows/test.yaml that gates every push on `go build`,
`bun run build`, `go vet`, the migration coordination check, and the
role-split end-to-end migration smoke. On push to main + green, calls
Dokploy's compose.deploy API and polls /health/ready until 200.
t-paliad-282 / m/paliad#114. Design: docs/design-cicd-pre-deploy-gate-2026-05-25.md
(inventor shift on mai/cronus/inventor-ci-cd-pre).
Catches all three of today's outage classes:
brunel (~13:20) slot collision -> TestMigrations_NoDuplicateSlot
hermes (~16:05) dropped-col refs -> TestBootSmoke
mig 129 (~14:56) 42501 ownership -> TestMigrations_EndToEndAsAppRole
Snapshot approach. internal/db/testdata/prod-snapshot.sql is a pg_dump
of youpc-supabase paliad schema + applied_migrations rows. CI restores
this into a fresh `supabase/postgres:15.8.1.060` (same image, same role
topology as prod) and runs ApplyMigrations as the `postgres` role
(which is NOT a superuser on supabase/postgres, matching prod). Existing
migrations are skipped (already in applied_migrations); only NEW migs
from the PR run end-to-end. This sidesteps the fresh-DB idempotence
debt in some historical migrations (mig 037 missing pg_trgm, mig 051
inner COMMIT) — those are tracked separately and don't block the gate.
Sub-changes:
- internal/handlers/handlers.go — new /health/ready endpoint distinct
from /healthz. /healthz stays liveness (process alive, no DB); /ready
is readiness (DB pool pings within 2 s). Returns 503 when svc or pool
is nil (DB-less deploys are intentionally not-ready). svc.Pool added
to handlers.Services, wired in cmd/server/main.go.
- internal/db/migrate_test.go — TestMigrations_NoDuplicateSlot (pure
unit, catches brunel) and TestMigrations_EndToEndAsAppRole (snapshot-
gated, catches the 42501 class).
- cmd/server/main_smoke_test.go — TestBootSmoke now also asserts
/health/ready returns 503 with a nil svc. New TestHealthReady_Live
asserts 200 against a live pool.
- internal/db/migrations/024_rename_department_columns.up.sql and
027_rename_to_partner_units.up.sql — ALTER INDEX / ALTER POLICY
exception handlers now catch undefined_object OR undefined_table OR
duplicate_object. Old handler only caught undefined_object; Postgres
raises undefined_table when source object never existed, and
duplicate_object when destination already exists. The expanded
handlers make these migrations truly idempotent across all plausible
starting states.
- Makefile — verify-mig-app, test-frontend, refresh-snapshot targets.
refresh-snapshot pg_dumps youpc-supabase prod (needs PALIAD_PROD_DATABASE_URL),
strips pg16 \restrict commands for pg15 restore compat, and filters
applied_migrations rows to this branch's max on-disk version.
- internal/db/testdata/README.md — explains the snapshot's purpose,
refresh procedure, and how to verify locally.
- docs/cicd-runner-setup-2026-05-25.md — one-time admin steps for
registering a Gitea Actions runner on mriver and wiring DOKPLOY_TOKEN
as a repo secret. Documents soft-launch plan per m's Q11.4 (keep
Dokploy's autoDeploy=true webhook alive for one week, disable after
the workflow has gated 5 successful deploys).
Build clean. Full go test ./internal/... ./cmd/... green without
TEST_DATABASE_URL. With TEST_DATABASE_URL + TEST_APP_DATABASE_URL set
to a supabase/postgres scratch + snapshot restored:
TestMigrations_NoDuplicateSlot, TestMigrations_EndToEndAsAppRole,
TestBootSmoke, TestHealthReady_Live all pass. Live-DB service tests in
internal/services/* fail under supabase/postgres 15.8 with a 42P08
parameter-binding error (unrelated to Slice A — tracked as a follow-up).