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).
144 lines
6.9 KiB
Makefile
144 lines
6.9 KiB
Makefile
# Paliad — developer entrypoints.
|
|
#
|
|
# Targets here are the gate tier from the test-strategy design
|
|
# (docs/design-paliad-test-strategy-2026-05-19.md). Slice 1 lands:
|
|
#
|
|
# make verify-migrations — dry-run every pending migration (BEGIN..ROLLBACK)
|
|
# plus the full boot smoke (apply + tracker
|
|
# advances + /healthz returns 200).
|
|
# make verify-mig — alias for verify-migrations.
|
|
# make test — short test pass: go test ./internal/... -short
|
|
# plus the cmd/server package. Includes the
|
|
# live-DB tests when TEST_DATABASE_URL is set,
|
|
# skips them otherwise.
|
|
# make test-go — go test ./... -race (full Go suite).
|
|
#
|
|
# Future slices will extend this with:
|
|
# make test-frontend — bun test (Slice 3 / Slice 6)
|
|
# make e2e — Playwright golden-path suite (Slice 4)
|
|
#
|
|
# All targets are idempotent. None of them write to the filesystem outside
|
|
# the test runner's working dirs. None of them touch internal/db/migrations/
|
|
# files.
|
|
|
|
.PHONY: help verify-migrations verify-mig verify-mig-app test test-go test-frontend refresh-snapshot
|
|
|
|
help:
|
|
@echo "Paliad — developer targets"
|
|
@echo ""
|
|
@echo " verify-migrations Dry-run pending migrations + boot smoke (needs TEST_DATABASE_URL)"
|
|
@echo " verify-mig Alias for verify-migrations"
|
|
@echo " verify-mig-app End-to-end migration smoke as non-superuser role"
|
|
@echo " (needs TEST_APP_DATABASE_URL — t-paliad-282 / m/paliad#114)"
|
|
@echo " test Short test pass — covers gate tier"
|
|
@echo " test-go Full Go suite with race detector"
|
|
@echo " test-frontend Frontend bun:test suite"
|
|
@echo ""
|
|
@echo "Set TEST_DATABASE_URL to enable live-DB tests. Example:"
|
|
@echo " export TEST_DATABASE_URL=postgres://paliad:...@localhost:11833/paliad_test"
|
|
@echo ""
|
|
@echo "Set TEST_APP_DATABASE_URL to enable the role-split smoke. Example:"
|
|
@echo " export TEST_APP_DATABASE_URL=postgres://paliad_app:...@localhost:5432/paliad_scratch"
|
|
|
|
# Gate target — the test that would have caught mig 098 / mig 099 before
|
|
# deploy. Combines:
|
|
# - TestMigrations_DryRun (internal/db): per-migration BEGIN..ROLLBACK
|
|
# - TestBootSmoke (cmd/server): apply-end-to-end + tracker advances
|
|
# + /healthz 200
|
|
#
|
|
# Requires TEST_DATABASE_URL. Without it, both tests skip and the target
|
|
# is effectively a no-op — guard against that explicitly so CI doesn't
|
|
# silently green a missing env var.
|
|
verify-migrations:
|
|
@if [ -z "$$TEST_DATABASE_URL" ]; then \
|
|
echo "ERROR: TEST_DATABASE_URL is not set."; \
|
|
echo " The migration gate cannot run without a scratch DB."; \
|
|
echo " Set TEST_DATABASE_URL to a Postgres URL the test can"; \
|
|
echo " open transactions against, e.g."; \
|
|
echo " export TEST_DATABASE_URL=postgres://paliad:PW@localhost:11833/paliad_test"; \
|
|
exit 2; \
|
|
fi
|
|
@echo "==> migration dry-run (per-mig BEGIN..ROLLBACK)"
|
|
go test -count=1 -run TestMigrations_DryRun ./internal/db/
|
|
@echo "==> boot smoke (apply + tracker + /healthz)"
|
|
go test -count=1 -run TestBootSmoke ./cmd/server/
|
|
|
|
verify-mig: verify-migrations
|
|
|
|
# Gate-tier test pass. -short skips the slow live-DB tests when the
|
|
# author opts out via `if testing.Short() { t.Skip(...) }`; today most of
|
|
# paliad's live-DB tests gate on TEST_DATABASE_URL instead, so -short is
|
|
# forward-compatible rather than load-bearing.
|
|
test:
|
|
go test -short ./internal/... ./cmd/...
|
|
|
|
# Full Go suite with race detection. Slower but catches concurrent-map
|
|
# regressions that -short would skip; intended for the merge-to-main gate
|
|
# (full suite, not per-PR).
|
|
test-go:
|
|
go test -race ./...
|
|
|
|
# Frontend bun:test suite. Runs the 4 existing pure-TS tests today; will
|
|
# grow as mendel's Slice 3 (frontend test infill) lands.
|
|
test-frontend:
|
|
cd frontend && bun test
|
|
|
|
# Role-split end-to-end migration smoke — the catch for the mig 129 42501
|
|
# ownership class (m/paliad#114). Runs ApplyMigrations as a non-superuser
|
|
# role against TEST_APP_DATABASE_URL. Fails the build if any migration
|
|
# assumes more privilege than the deploy role has.
|
|
#
|
|
# Developer setup (local):
|
|
# psql -c "CREATE ROLE paliad_app LOGIN PASSWORD 'ci' NOSUPERUSER;"
|
|
# psql -c "CREATE DATABASE paliad_scratch OWNER paliad_app;"
|
|
# export TEST_APP_DATABASE_URL=postgres://paliad_app:ci@localhost:5432/paliad_scratch
|
|
verify-mig-app:
|
|
@if [ -z "$$TEST_APP_DATABASE_URL" ]; then \
|
|
echo "ERROR: TEST_APP_DATABASE_URL is not set."; \
|
|
echo " The role-split migration smoke cannot run without a non-superuser scratch DB."; \
|
|
echo " See Makefile comments above this target for setup."; \
|
|
exit 2; \
|
|
fi
|
|
go test -count=1 -run TestMigrations_EndToEndAsAppRole ./internal/db/
|
|
|
|
# Refresh the prod schema snapshot used by CI's migration smoke
|
|
# (t-paliad-282 / m/paliad#114). Connects to youpc-supabase prod, dumps
|
|
# the paliad schema + applied_migrations rows, strips rows beyond the
|
|
# current branch's max on-disk version, and writes
|
|
# internal/db/testdata/prod-snapshot.sql.
|
|
#
|
|
# When to refresh:
|
|
# - After merging a PR that added a new migration to main.
|
|
# - When CI's migration smoke starts spuriously failing because the
|
|
# snapshot's applied set diverges from on-disk by more than this
|
|
# branch's worth of new migs.
|
|
#
|
|
# Requires PALIAD_PROD_DATABASE_URL env var (a Postgres URL with
|
|
# pg_dump rights on youpc-supabase). Example:
|
|
# export PALIAD_PROD_DATABASE_URL='postgres://postgres:PW@100.99.98.201:11833/postgres'
|
|
refresh-snapshot:
|
|
@if [ -z "$$PALIAD_PROD_DATABASE_URL" ]; then \
|
|
echo "ERROR: PALIAD_PROD_DATABASE_URL is not set."; \
|
|
echo " Refresh requires read access to youpc-supabase prod."; \
|
|
exit 2; \
|
|
fi
|
|
@echo "==> dumping paliad schema (no owner, no privs)..."
|
|
@pg_dump --schema-only --schema=paliad --no-owner --no-privileges \
|
|
--no-publications --no-subscriptions \
|
|
"$$PALIAD_PROD_DATABASE_URL" > internal/db/testdata/prod-snapshot.sql.tmp
|
|
@echo "==> appending applied_migrations rows..."
|
|
@pg_dump --data-only --table=paliad.applied_migrations \
|
|
--no-owner --no-privileges \
|
|
"$$PALIAD_PROD_DATABASE_URL" >> internal/db/testdata/prod-snapshot.sql.tmp
|
|
@echo "==> stripping pg16 \\restrict / \\unrestrict commands for pg15 compat..."
|
|
@sed -i.bak '/^\\restrict /d; /^\\unrestrict /d' internal/db/testdata/prod-snapshot.sql.tmp
|
|
@rm -f internal/db/testdata/prod-snapshot.sql.tmp.bak
|
|
@echo "==> stripping applied_migrations rows beyond branch's max on-disk version..."
|
|
@MAX_VER=$$(ls internal/db/migrations/*.up.sql | xargs -I{} basename {} | sed 's/_.*//' | sort -n | tail -1); \
|
|
awk -v max=$$MAX_VER ' \
|
|
/^[0-9]+\t/ { split($$0, a, "\t"); if (a[1]+0 > max) next; } \
|
|
{ print } \
|
|
' internal/db/testdata/prod-snapshot.sql.tmp > internal/db/testdata/prod-snapshot.sql
|
|
@rm internal/db/testdata/prod-snapshot.sql.tmp
|
|
@wc -l internal/db/testdata/prod-snapshot.sql
|