internal/store connects via pgx and runs golang-migrate migrations embedded in the binary (go:embed), so Deklarix stays a single binary despite the move to Postgres. Schema covers the five MVP tables (submission, asset, extraction, finding, evidence_package, participant). extraction, finding and evidence_package are append-only by design: a Postgres trigger rejects UPDATE/DELETE outright, since a corrigible evidence archive isn't an evidence archive. Corrections to a finding are new rows whose supersedes column points at the row they replace (set at INSERT time on the new row, since the trigger blocks UPDATE on the old one) — "currently valid" findings are the ones no other row supersedes. scripts/test.sh now spins up a disposable Postgres container so the store's integration tests (including the append-only guarantee) actually run on every test.sh/release.sh invocation instead of silently skipping for lack of DATABASE_URL. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
49 lines
1.2 KiB
Go
49 lines
1.2 KiB
Go
package store
|
|
|
|
import (
|
|
"database/sql"
|
|
"embed"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"github.com/golang-migrate/migrate/v4"
|
|
pgxmigrate "github.com/golang-migrate/migrate/v4/database/pgx/v5"
|
|
"github.com/golang-migrate/migrate/v4/source/iofs"
|
|
_ "github.com/jackc/pgx/v5/stdlib"
|
|
)
|
|
|
|
//go:embed migrations/*.sql
|
|
var migrationsFS embed.FS
|
|
|
|
// Migrate wendet alle ausstehenden Migrationen aus migrations/ an.
|
|
// Die Migrationen sind im Binary eingebettet (go:embed), damit Deklarix
|
|
// weiterhin als einzelnes Binary lauffähig bleibt.
|
|
func Migrate(databaseURL string) error {
|
|
db, err := sql.Open("pgx", databaseURL)
|
|
if err != nil {
|
|
return fmt.Errorf("store: open db for migration: %w", err)
|
|
}
|
|
defer db.Close()
|
|
|
|
driver, err := pgxmigrate.WithInstance(db, &pgxmigrate.Config{})
|
|
if err != nil {
|
|
return fmt.Errorf("store: migration driver: %w", err)
|
|
}
|
|
|
|
source, err := iofs.New(migrationsFS, "migrations")
|
|
if err != nil {
|
|
return fmt.Errorf("store: migration source: %w", err)
|
|
}
|
|
|
|
m, err := migrate.NewWithInstance("iofs", source, "pgx5", driver)
|
|
if err != nil {
|
|
return fmt.Errorf("store: migrate init: %w", err)
|
|
}
|
|
defer m.Close()
|
|
|
|
if err := m.Up(); err != nil && !errors.Is(err, migrate.ErrNoChange) {
|
|
return fmt.Errorf("store: migrate up: %w", err)
|
|
}
|
|
return nil
|
|
}
|