feat(deploy): inline bootstrap into db-init — compose is the single source; DB_PORT everywhere

z_rls_role.sh is gone. The db-init service carries the bootstrap SQL
inline (the script's docker-exec/local-socket mode was its only reason
to exist as a file, and the no-clone deployment path could not use it:
docker bind-mounts of missing host paths auto-create empty DIRECTORIES,
so a compose-only install got '/z_rls_role.sh: no such file').

The inline form surfaced two silent bugs the script had masked:
- psql defaulted its DATABASE to the username (POSTGRES_DB is not a
  libpq variable) — the ownership loop ran against 'postgres' and
  no-opped on existing volumes. Fixed with PGDATABASE: bookhoard.
- PGPORT was never propagated: with a custom DB_PORT, db-init dialed
  5432 while Postgres listened on the custom port. Fixed with
  PGPORT: ${DB_PORT:-5432} — DB_PORT is now a variable in every
  connection path (db listener, host mappings, app, db-init).

Also removed the db service's initdb.d mount: the app executes the full
schema as the runtime-role owner on every boot incl. first (pg_trgm is
a trusted extension; the db owner can create it), so the bootstrap SQL
was its only content.

Verified: dev two-boot against the existing 40-table volume (owners
converged, 0 off-owner tables/functions), custom-port throwaway
(DB_PORT=15432: db-init + app both on the custom port, healthy),
default-port throwaway (fresh volume, healthy), full Go suite green.
Manual repair path: docker compose run --rm db-init.
This commit is contained in:
John O'Keefe committed 2026-10-29 13:06:16 -04:00
1 parent cf73eec2e4
commit 304ea9599f
4 files changed
+73 -90

No files matched your search

+34 -6
View File
@@ -69,14 +69,42 @@ services:
image: postgres:15-alpine
container_name: bookhoard_db_init
environment:
POSTGRES_USER: postgres
POSTGRES_DB: bookhoard
PGHOST: db
DBPASS: ${DBPASS}
PGUSER: postgres
PGDATABASE: bookhoard
PGPASSWORD: ${DBPASS}
DBSRVPASS: ${DBSRVPASS}
volumes:
- ./database/schema/z_rls_role.sh:/z_rls_role.sh:ro
command: bash /z_rls_role.sh
entrypoint: ["bash", "-c"]
command:
- |
set -e
: "${DBSRVPASS:?DBSRVPASS required in .env}"
psql -v ON_ERROR_STOP=1 -v role_pass="$DBSRVPASS" <<'EOSQL'
DO $$$$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'bookhoard_srv') THEN
CREATE ROLE bookhoard_srv LOGIN;
END IF;
END $$$$;
ALTER ROLE bookhoard_srv LOGIN PASSWORD :'role_pass';
DO $$$$
DECLARE r RECORD;
BEGIN
FOR r IN SELECT tablename FROM pg_tables WHERE schemaname = 'public' LOOP
EXECUTE format('ALTER TABLE public.%I OWNER TO bookhoard_srv', r.tablename);
END LOOP;
FOR r IN SELECT sequencename FROM pg_sequences WHERE schemaname = 'public' LOOP
EXECUTE format('ALTER SEQUENCE public.%I OWNER TO bookhoard_srv', r.sequencename);
END LOOP;
FOR r IN SELECT p.oid::regprocedure AS fn FROM pg_proc p
JOIN pg_namespace n ON p.pronamespace = n.oid
WHERE n.nspname = 'public' AND p.prokind = 'f' LOOP
EXECUTE format('ALTER FUNCTION %s OWNER TO bookhoard_srv', r.fn);
END LOOP;
END $$$$;
ALTER SCHEMA public OWNER TO bookhoard_srv;
ALTER DATABASE bookhoard OWNER TO bookhoard_srv;
EOSQL
depends_on:
db:
condition: service_healthy