db: integrate golang-migrate for schema migrations

- internal/db/migrations/: five embedded SQL files containing
  the migration SQL previously held as Go string literals.
  Files follow the NNN_description.up.sql naming convention
  required by golang-migrate's iofs source.
- internal/db/migrate.go: rewritten to use
  github.com/golang-migrate/migrate/v4 with the
  database/sqlite driver (modernc.org/sqlite, pure Go) and
  source/iofs for compile-time embedded SQL.
  - newMigrate() opens a dedicated *sql.DB so m.Close() does
    not affect the caller's shared connection.
  - Migrate() includes a compatibility shim: reads the legacy
    schema_version table and calls m.Force(v) before m.Up()
    so existing databases are not re-migrated.
  - LatestSchemaVersion promoted from var to const.
- internal/db/db.go: added path field to DB struct; Open()
  translates ':memory:' to a named shared-cache URI
  (file:mcias_N?mode=memory&cache=shared) so the migration
  runner can open a second connection to the same in-memory
  database without sharing the handle that golang-migrate
  will close on teardown.
- go.mod: added golang-migrate/migrate/v4 v4.19.1 (direct).
All callers unchanged. All tests pass; golangci-lint clean.
This commit is contained in:
2026-03-12 11:52:39 -07:00
parent b2f2f04646
commit d7b69ed983
12 changed files with 399 additions and 264 deletions

View File

@@ -4,6 +4,52 @@ Source of truth for current development state.
---
All phases complete. **v1.0.0 tagged.** All packages pass `go test ./...`; `golangci-lint run ./...` clean.
### 2026-03-12 — Integrate golang-migrate for database migrations
**internal/db/migrations/** (new directory — 5 embedded SQL files)
- `000001_initial_schema.up.sql` — full initial schema (verbatim from migration 1)
- `000002_master_key_salt.up.sql` — adds `master_key_salt` to server_config
- `000003_failed_logins.up.sql``failed_logins` table for brute-force lockout
- `000004_tags_and_policy.up.sql``account_tags` and `policy_rules` tables
- `000005_pgcred_access.up.sql``owner_id` column + `pg_credential_access` table
- Files are embedded at compile time via `//go:embed migrations/*.sql`; no
runtime filesystem access is needed
**internal/db/migrate.go** (rewritten)
- Removed hand-rolled `migration` struct and `migrations []migration` slice
- Uses `github.com/golang-migrate/migrate/v4` with the `database/sqlite`
driver (modernc.org/sqlite, pure Go, no CGO) and `source/iofs` for embedded
SQL files
- `LatestSchemaVersion` changed from `var` to `const = 5`
- `Migrate(db *DB) error`: compatibility shim reads legacy `schema_version`
table; if version > 0, calls `m.Force(legacyVersion)` before `m.Up()` so
existing databases are not re-migrated. Returns nil on ErrNoChange.
- `SchemaVersion(db *DB) (int, error)`: delegates to `m.Version()`; returns 0
on ErrNilVersion
- `newMigrate(*DB)`: opens a **dedicated** `*sql.DB` for the migrator so that
`m.Close()` (which closes the underlying connection) does not affect the
caller's shared connection
- `legacySchemaVersion(*DB)`: reads old schema_version table; returns 0 if
absent (fresh DB or already on golang-migrate only)
**internal/db/db.go**
- Added `path string` field to `DB` struct for the migrator's dedicated
connection
- `Open(":memory:")` now translates to a named shared-cache URI
`file:mcias_N?mode=memory&cache=shared` (N is atomic counter) so the
migration runner can open a second connection to the same in-memory database
without sharing the `*sql.DB` handle that golang-migrate will close
**go.mod / go.sum**
- Added `github.com/golang-migrate/migrate/v4 v4.19.1` (direct)
- Transitive: `hashicorp/errwrap`, `hashicorp/go-multierror`,
`go.uber.org/atomic`
All callers (`cmd/mciassrv`, `cmd/mciasdb`, all test helpers) continue to call
`db.Open(path)` and `db.Migrate(database)` unchanged.
All tests pass (`go test ./...`); `golangci-lint run ./...` reports 0 issues.
### 2026-03-12 — UI: pgcreds create button; show logged-in user
**web/templates/pgcreds.html**