Adopt golib/pg for migrations and pool construction
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.
This commit is contained in:
@@ -26,6 +26,27 @@ The Terraform provider lives in a sibling repo:
|
||||
Foreign keys guarantee a node can only reference a role/status that exists, and
|
||||
a role/status in use cannot be deleted.
|
||||
|
||||
## Schema migrations
|
||||
|
||||
The SQL lives in [`migrations/`](migrations), is embedded in the `encapi`
|
||||
binary, and is applied at startup before the server listens — there is no
|
||||
mirrored copy in the deployment to drift out of sync.
|
||||
|
||||
The runner is [`golib/pg`](https://git.unkin.net/unkin/golib)'s
|
||||
`pg.NewMigrated`: encapi owns the SQL, the shared library owns the mechanics.
|
||||
Each start takes `pg_advisory_lock` on a key derived from the lock name
|
||||
`encapi-migrations`, creates `schema_migrations` (`version`, `applied_at`) if
|
||||
missing, and applies every embedded file whose filename is not yet recorded — in
|
||||
lexical (version) order, each file's SQL and its tracking row in one transaction
|
||||
— then unlocks. Replicas starting together queue on the lock and then find
|
||||
nothing to do.
|
||||
|
||||
A file the live database already satisfies but `schema_migrations` does not
|
||||
record is re-run, which is how the pre-migration schema is adopted: migrations
|
||||
are `IF NOT EXISTS`-guarded, so the re-run is a no-op that only lands the
|
||||
tracking row. Add schema changes as a new numbered file; never edit an applied
|
||||
one.
|
||||
|
||||
## ENC output
|
||||
|
||||
`encapi` renders two shapes from the same data:
|
||||
@@ -90,6 +111,22 @@ make test # full suite (Postgres via testcontainers)
|
||||
make test-short # skip container-backed DB tests
|
||||
```
|
||||
|
||||
### `GOPRIVATE`
|
||||
|
||||
encapi depends on `git.unkin.net/unkin/golib`, which is served by Gitea and is
|
||||
unknown to `proxy.golang.org` / `sum.golang.org`. Module resolution therefore
|
||||
needs:
|
||||
|
||||
```
|
||||
export GOPRIVATE=git.unkin.net
|
||||
```
|
||||
|
||||
The `Makefile` exports it for every target, and the `Dockerfile` and the
|
||||
woodpecker Go steps set it themselves, so `make build|test|lint` and CI work on
|
||||
a clean checkout. Only bare `go` commands run outside `make` need it in your
|
||||
shell — set it there (or in your shell profile) rather than with `go env -w`,
|
||||
which is machine state this repo cannot carry.
|
||||
|
||||
## Releases
|
||||
|
||||
- **`encapi` server image** — tagging `vX.Y.Z` builds and pushes
|
||||
|
||||
Reference in New Issue
Block a user