Adopt golib/pg for migrations and pool construction #6

Merged
benvin merged 1 commits from benvin/adopt-golib-pg into main 2026-09-05 11:15:13 +10:00
Member

Why

encapi applied its schema as one cumulative IF NOT EXISTS blob re-executed
inline on every start. It grows forever, records nothing about what has run, and
cannot express a change that is not a fresh CREATE. git.unkin.net/unkin/golib
now owns that mechanism (versioned files, schema_migrations, advisory lock), so
encapi should keep the SQL and drop the runner.

How

  • Move the DDL verbatim into migrations/0001_init.sql, embedded via
    migrations.FS. It stays IF NOT EXISTS-guarded, so the first start against
    the live database re-runs it as a no-op and only lands the tracking row.
  • Build the pool with pg.NewMigrated under the lock name encapi-migrations,
    and delete the inline migrate(). database.New now takes a context and a
    logger; main.go passes the signal context, so a start queued on the
    migration lock still dies on SIGTERM.
  • Render the DSN with pg.DSN. The env var contract is unchanged
    DBHOST/DBPORT/DBUSER/DBPASS/DBNAME/DBSSL are still resolved by
    internal/config, not pg.DSNFromEnv, because encapi defaults DBUSER and
    DBNAME to encapi where DSNFromEnv treats both as required.
  • Guards: embedded set must match migrations/, every CREATE must be
    idempotent, the derived advisory key is pinned to its computed value, and a
    container test proves the adoption path over a database that predates
    schema_migrations. make test-short now covers ./migrations/....
  • Plumb GOPRIVATE=git.unkin.net for the first cross-repo Go dependency:
    Makefile export, Dockerfile ENV, woodpecker Go steps, README note.

Full suite green with Postgres via testcontainers; gofmt/vet/pre-commit clean.

## Why encapi applied its schema as one cumulative `IF NOT EXISTS` blob re-executed inline on every start. It grows forever, records nothing about what has run, and cannot express a change that is not a fresh `CREATE`. `git.unkin.net/unkin/golib` now owns that mechanism (versioned files, `schema_migrations`, advisory lock), so encapi should keep the SQL and drop the runner. ## How - Move the DDL verbatim into `migrations/0001_init.sql`, embedded via `migrations.FS`. It stays `IF NOT EXISTS`-guarded, so the first start against the live database re-runs it as a no-op and only lands the tracking row. - Build the pool with `pg.NewMigrated` under the lock name `encapi-migrations`, and delete the inline `migrate()`. `database.New` now takes a context and a logger; `main.go` passes the signal context, so a start queued on the migration lock still dies on SIGTERM. - Render the DSN with `pg.DSN`. **The env var contract is unchanged** — `DBHOST/DBPORT/DBUSER/DBPASS/DBNAME/DBSSL` are still resolved by `internal/config`, not `pg.DSNFromEnv`, because encapi defaults `DBUSER` and `DBNAME` to `encapi` where `DSNFromEnv` treats both as required. - Guards: embedded set must match `migrations/`, every `CREATE` must be idempotent, the derived advisory key is pinned to its computed value, and a container test proves the adoption path over a database that predates `schema_migrations`. `make test-short` now covers `./migrations/...`. - Plumb `GOPRIVATE=git.unkin.net` for the first cross-repo Go dependency: Makefile export, Dockerfile `ENV`, woodpecker Go steps, README note. Full suite green with Postgres via testcontainers; gofmt/vet/pre-commit clean.
unkin-agent added 1 commit 2026-09-02 00:17:31 +10:00
Adopt golib/pg for migrations and pool construction
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/build Pipeline was successful
42307f285f
encapi's schema was a cumulative IF NOT EXISTS blob re-executed inline on every
start: it grows forever, records nothing, and cannot express a change that is
not a fresh CREATE. golib owns that mechanism now, so encapi keeps the SQL and
drops the runner.

- Move the DDL verbatim into migrations/0001_init.sql, embedded via
  migrations.FS. It stays IF NOT EXISTS-guarded, so the first start against the
  live database re-runs it as a no-op and only lands the schema_migrations row.
- Build the pool with pg.NewMigrated under the advisory lock named
  encapi-migrations, and delete the inline migrate(). database.New now takes a
  context and a logger; main.go hands it the signal context so a start blocked
  on the migration lock still dies on SIGTERM.
- Render the DSN with pg.DSN. The env var contract is untouched — the fields are
  still resolved by internal/config, because encapi defaults DBUSER and DBNAME
  to "encapi" where pg.DSNFromEnv treats both as required.
- Guard the set: embedded files must match migrations/, every CREATE must be
  idempotent, the derived lock key is pinned, and a container test proves the
  adoption path over a database that predates schema_migrations.
- Plumb GOPRIVATE=git.unkin.net for the first cross-repo Go dependency:
  exported by the Makefile, set in the Dockerfile and the woodpecker Go steps,
  documented in the README.
benvin merged commit 757799300f into main 2026-09-05 11:15:13 +10:00
benvin deleted branch benvin/adopt-golib-pg 2026-09-05 11:15:13 +10:00
Sign in to join this conversation.