- Add 03-02-SUMMARY.md with execution results - Update STATE.md: advance plan, record metrics, add decisions, update session - Update ROADMAP.md: phase 03 complete (2/2 plans, all summaries present) - Update REQUIREMENTS.md: mark DB-02 complete
89 lines
4.6 KiB
Markdown
89 lines
4.6 KiB
Markdown
---
|
|
phase: 03-postgresql-support
|
|
plan: "02"
|
|
subsystem: wiring
|
|
tags: [postgresql, sqlite, database, docker-compose, branching]
|
|
dependency_graph:
|
|
requires: [03-01]
|
|
provides: [DATABASE_URL branching, postgres docker profile, NewTestPostgresServer]
|
|
affects: [cmd/diunwebhook/main.go, compose.yml, compose.dev.yml, pkg/diunwebhook/diunwebhook.go]
|
|
tech_stack:
|
|
added: []
|
|
patterns: [DATABASE_URL env var branching, Docker Compose profiles, build-tagged test helpers]
|
|
key_files:
|
|
created:
|
|
- pkg/diunwebhook/postgres_test.go
|
|
modified:
|
|
- cmd/diunwebhook/main.go
|
|
- pkg/diunwebhook/diunwebhook.go
|
|
- compose.yml
|
|
- compose.dev.yml
|
|
decisions:
|
|
- "DATABASE_URL present activates PostgreSQL path; absent falls back to SQLite with DB_PATH"
|
|
- "postgres Docker service uses profiles: [postgres] so default compose up remains SQLite-only"
|
|
- "UNIQUE detection uses strings.ToLower for case-insensitive matching across SQLite and PostgreSQL"
|
|
- "Build tag //go:build postgres gates postgres_test.go so standard test runs have no pgx dependency"
|
|
metrics:
|
|
duration: "~2 minutes"
|
|
completed: "2026-03-24T08:13:21Z"
|
|
tasks_completed: 2
|
|
files_changed: 5
|
|
---
|
|
|
|
# Phase 03 Plan 02: Wire PostgreSQL Support and Deployment Infrastructure Summary
|
|
|
|
DATABASE_URL branching in main.go routes to PostgresStore or SQLiteStore at startup; Docker Compose postgres profile enables optional PostgreSQL; build-tagged test helper and cross-dialect UNIQUE detection complete the integration.
|
|
|
|
## What Was Built
|
|
|
|
### Updated main.go (cmd/diunwebhook/main.go)
|
|
- `DATABASE_URL` env var check: when set, opens pgx connection, runs `RunPostgresMigrations`, creates `NewPostgresStore`, logs `"Using PostgreSQL database"`
|
|
- When absent: existing SQLite path using `RunSQLiteMigrations` (renamed in Plan 01), `NewSQLiteStore`, logs `"Using SQLite database at {path}"`
|
|
- Blank import `_ "github.com/jackc/pgx/v5/stdlib"` registers the `"pgx"` driver name
|
|
- All route wiring and graceful shutdown logic unchanged
|
|
|
|
### Cross-dialect UNIQUE detection (pkg/diunwebhook/diunwebhook.go)
|
|
- `TagsHandler` now uses `strings.Contains(strings.ToLower(err.Error()), "unique")` for 409 Conflict detection
|
|
- SQLite errors: `UNIQUE constraint failed: tags.name` (uppercase UNIQUE)
|
|
- PostgreSQL errors: `duplicate key value violates unique constraint "tags_name_key"` (lowercase unique)
|
|
- Both backends now return 409 correctly
|
|
|
|
### Docker Compose postgres profiles
|
|
- `compose.yml`: postgres service added with `profiles: [postgres]`, healthcheck via `pg_isready`, `DATABASE_URL` env var in app service, conditional `depends_on` with `required: false`, `postgres-data` volume
|
|
- `compose.dev.yml`: same postgres service with port 5432 exposed on host for direct psql access during development
|
|
- Default `docker compose up` (no profile) unchanged — SQLite only, no new services start
|
|
|
|
### Build-tagged test helper (pkg/diunwebhook/postgres_test.go)
|
|
- `//go:build postgres` tag — only compiled with `go test -tags postgres`
|
|
- `NewTestPostgresServer()` constructs a `*Server` backed by PostgreSQL using `TEST_DATABASE_URL` env var (defaults to `postgres://diun:diun@localhost:5432/diundashboard_test?sslmode=disable`)
|
|
- Calls `RunPostgresMigrations` and `NewPostgresStore` — mirrors the production startup path
|
|
|
|
## Decisions Made
|
|
|
|
| Decision | Rationale |
|
|
|----------|-----------|
|
|
| DATABASE_URL presence-check (not a separate DB_DRIVER var) | Simpler UX; empty string = SQLite, any value = PostgreSQL |
|
|
| profiles: [postgres] in compose files | Standard Docker Compose pattern for optional services; default deploy unchanged |
|
|
| required: false in depends_on | App can start without postgres service (SQLite fallback); Docker Compose v2.20+ required |
|
|
| //go:build postgres tag on test helper | Prevents pgx import at test time for standard `go test ./...` runs; explicit opt-in |
|
|
| strings.ToLower for UNIQUE check | SQLite and PostgreSQL use different cases in constraint error messages |
|
|
|
|
## Deviations from Plan
|
|
|
|
None — plan executed exactly as written. The `export_test.go` rename (RunMigrations -> RunSQLiteMigrations) was already completed as a deviation in Plan 01, as noted in the objective.
|
|
|
|
## Verification Results
|
|
|
|
- `go build ./...` exits 0
|
|
- `go test -count=1 ./pkg/diunwebhook/` passes (all 20+ SQLite tests, postgres_test.go skipped)
|
|
- `docker compose config` validates without errors
|
|
- `docker compose --profile postgres config` shows postgres service
|
|
- `grep -c "DATABASE_URL" cmd/diunwebhook/main.go` returns 1
|
|
- `grep "strings.ToLower" pkg/diunwebhook/diunwebhook.go` shows case-insensitive UNIQUE check
|
|
|
|
## Known Stubs
|
|
|
|
None — this plan wires implementation code, no UI stubs.
|
|
|
|
## Self-Check: PASSED
|