• Adopt golib/pg for migrations and pool construction (#125)
    ci/woodpecker/tag/docker Pipeline was successful

    unkin-agent released this 2026-09-05 12:10:59 +10:00 | 52 commits to master since this release

    Why

    The Postgres schema was a ~180-line DDL blob inlined in internal/database/postgres.go and re-executed on every start. It could only grow — each change appended another ALTER TABLE ... ADD COLUMN IF NOT EXISTS — and nothing recorded what had ever been applied. golib/pg already owns that mechanic for the estate (arrproxy#10), so artifactapi keeps the SQL and hands over the machinery.

    How

    • Depend on git.unkin.net/unkin/golib v0.1.0.
    • Move the DDL verbatim into migrations/0001_init.sql, embedded by the new migrations package, and build the pool with pg.NewMigrated (LockName: "artifactapi-migrations"). The runner takes a cluster-wide advisory lock the old blob never took, so replicas starting together queue instead of racing each other through the DDL.
    • No-op on the live database. Production has the schema but no schema_migrations, so its first start on this build re-runs 0001. Every statement is IF NOT EXISTS-guarded, so that run changes nothing and only lands the tracking row. TestMigratingAnAlreadyPopulatedSchemaIsANoOp drops the row from a migrated database and asserts exactly that; TestMigrationsAreIdempotent keeps future migrations additive and idempotent.
    • Env var contract unchanged. config.DatabaseDSN stays the DSN source rather than pg.DSNFromEnv: the variable names match, but golib deliberately has no default user or database name, and artifactapi documents and ships DBUSER/DBNAME defaults of artifacts.
    • Drift guards: the embedded set is asserted equal to migrations/, and the derived advisory key is pinned so a rename cannot silently let two builds migrate at once.
    • Plumb GOPRIVATE=git.unkin.net for the first cross-repo Go dependency — exported by the Makefile, set in the Dockerfile and both woodpecker Go steps, documented in the README.

    database.New's signature and every caller are untouched.

    Transitive bumps come with golib's go.mod (testcontainers-go 0.42 → 0.44, otel, x/crypto, x/net); go build, go vet, golangci-lint, go test -race ./pkg/... ./internal/... and pre-commit run --all-files are clean.

    Reviewed-on: #125
    Co-authored-by: unkin-agent unkin-agent@unkin.net
    Co-committed-by: unkin-agent unkin-agent@unkin.net

    Downloads