docs(01-05): add SUMMARY for server /health + Flyway + HOCON plan
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
---
|
||||
phase: 01-project-infrastructure-module-wiring
|
||||
plan: 05
|
||||
subsystem: infra
|
||||
tags: [ktor, flyway, hocon, postgres, slf4j, kotlinx-serialization]
|
||||
|
||||
requires:
|
||||
- phase: 01-project-infrastructure-module-wiring
|
||||
provides: "recipe.jvm.server precompiled plugin (Plan 02) wires ktor-server-netty, ktor-server-content-negotiation, ktor-serialization-kotlinx-json, flyway-core, flyway-database-postgresql, postgresql JDBC, ktor-server-test-host, logback-classic. Plan 03 applied recipe.jvm.server + recipe.quality to server module and added implementation(projects.shared) so SERVER_PORT is reachable."
|
||||
provides:
|
||||
- "Running-but-empty server: GET /health returns {\"status\":\"ok\"} with Content-Type application/json"
|
||||
- "HOCON application.conf with localhost defaults + ${?ENV} overrides for PORT/DATABASE_URL/DATABASE_USER/DATABASE_PASSWORD"
|
||||
- "Database.migrate() Flyway boot sequence with fail-loud IllegalStateException contract on unreachable Postgres"
|
||||
- "server/src/main/resources/db/migration/ resource directory anchored by .gitkeep so classpath:db/migration resolves before Phase 3 adds V1__init.sql"
|
||||
- "configureRouting() extension extracted from Application.module() so tests compose routing without invoking Database.migrate (no Postgres in CI)"
|
||||
affects: [phase-02-auth, phase-03-households, phase-05-recipe-catalog, phase-11-deployment]
|
||||
|
||||
tech-stack:
|
||||
added: [Flyway runtime API (flyway-core 12.x), HOCON env-var override pattern, SLF4J server-side logging]
|
||||
patterns:
|
||||
- "HOCON ${?ENV} two-line override pattern (PITFALL #5 mitigation)"
|
||||
- "Fail-loud server boot: Database.migrate throws IllegalStateException on Flyway/JDBC failure"
|
||||
- "Routing extracted to Application.configureRouting() extension so testApplication composes routing without DB dependency"
|
||||
- "Server uses SLF4J/Logback (NOT Kermit — Kermit is client-only)"
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- server/src/main/kotlin/dev/ulfrx/recipe/Database.kt
|
||||
- server/src/main/resources/application.conf
|
||||
- server/src/main/resources/db/migration/.gitkeep
|
||||
modified:
|
||||
- server/src/main/kotlin/dev/ulfrx/recipe/Application.kt
|
||||
- server/src/test/kotlin/dev/ulfrx/recipe/ApplicationTest.kt
|
||||
|
||||
key-decisions:
|
||||
- "Use HOCON ${?ENV} optional substitution (two-line default + override) rather than ${ENV:default} (invalid HOCON) or ${ENV} (required, crashes on unset)"
|
||||
- "Server logs via SLF4J/Logback, not Kermit — Kermit reserved for the multiplatform client"
|
||||
- "Database.migrate is fail-loud: IllegalStateException on any Flyway error; no silent degraded mode"
|
||||
- "cleanDisabled(true) is double-enforced (precompiled plugin CLI guard + programmatic Database.migrate guard)"
|
||||
- "Extract Application.configureRouting() so /health test runs without Postgres — preserves D-11 invariant that ./gradlew :server:test passes in fresh clones / CI"
|
||||
- "Default credentials in application.conf (recipe/recipe/recipe @ localhost:5432/recipe) match Plan 06 docker-compose for zero-config dev boot"
|
||||
|
||||
patterns-established:
|
||||
- "HOCON ${?ENV} override: every secret/per-env value gets a default line followed by ${?ENV_VAR} optional substitution"
|
||||
- "Fail-loud infrastructure: critical boot operations (DB migration, future JWKS load) throw IllegalStateException rather than returning a status"
|
||||
- "Routing extraction for testability: features expose Application.configureXxx() extensions; module() is the production composition root"
|
||||
|
||||
requirements-completed: [INFRA-02]
|
||||
|
||||
duration: ~1 min (executor work — implementation commits authored ahead of executor invocation)
|
||||
completed: 2026-04-24
|
||||
---
|
||||
|
||||
# Phase 01 Plan 05: Server /health + Flyway + HOCON Boot Summary
|
||||
|
||||
**Running-but-empty Ktor server: HOCON-configured Flyway boot with fail-loud Postgres contract, GET /health returning `{"status":"ok"}`, and a routing extraction that lets tests verify the route without a running database.**
|
||||
|
||||
## Performance
|
||||
|
||||
- **Duration:** Implementation commits span 2026-04-24 18:22:08 → 18:23:14 (~66s of authoring); executor verification + SUMMARY ~1 min
|
||||
- **Started:** 2026-04-24T18:22:08Z (commit 24018ef)
|
||||
- **Completed:** 2026-04-24T18:23:14Z (commit 59d0695)
|
||||
- **Tasks:** 3
|
||||
- **Files modified:** 5 (3 created, 2 modified)
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- HOCON `application.conf` reads PORT + DATABASE_URL/USER/PASSWORD via the `${?ENV}` two-line override pattern; defaults match the Plan 06 docker-compose stack so `docker compose up -d postgres && ./gradlew :server:run` works with zero env config.
|
||||
- `Database.migrate(app: Application)` runs `Flyway.configure().dataSource(...).locations("classpath:db/migration").baselineOnMigrate(true).validateOnMigrate(true).cleanDisabled(true).load().migrate()` and throws `IllegalStateException` on any failure — D-16 fail-loud contract satisfied.
|
||||
- `db/migration/.gitkeep` keeps the resource directory in the repo so Flyway's classpath resolution succeeds before Phase 3 introduces the first SQL migration.
|
||||
- `Application.kt` rewritten with explicit Ktor imports (D-11 allWarningsAsErrors clean), installs `ContentNegotiation { json() }`, calls `Database.migrate(this)`, then delegates to `Application.configureRouting()` which exposes `GET /health → Health(status="ok")`.
|
||||
- `ApplicationTest.kt` rewritten to compose `configureRouting()` directly (skipping `Database.migrate`) so `./gradlew :server:test --tests "*health*"` passes without a running Postgres — required for fresh-clone / CI runs.
|
||||
|
||||
## Task Commits
|
||||
|
||||
Each task was committed atomically prior to executor invocation (commits already in branch history):
|
||||
|
||||
1. **Task 1: HOCON config + db/migration/.gitkeep + Database.kt** — `24018ef` (feat)
|
||||
2. **Task 2: Application.kt rewrite (ContentNegotiation, Flyway boot, /health)** — `daefe6c` (refactor)
|
||||
3. **Task 3: ApplicationTest.kt rewrite (no-Postgres /health assertion)** — `59d0695` (test)
|
||||
|
||||
**Plan metadata:** appended in this commit (docs).
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
- `server/src/main/resources/application.conf` (created) — HOCON config: ktor.deployment.port + database.{url,user,password} with `${?ENV}` overrides
|
||||
- `server/src/main/resources/db/migration/.gitkeep` (created) — anchors the Flyway classpath resource directory in git
|
||||
- `server/src/main/kotlin/dev/ulfrx/recipe/Database.kt` (created) — `object Database { fun migrate(app) }` with fail-loud Flyway invocation, SLF4J logging
|
||||
- `server/src/main/kotlin/dev/ulfrx/recipe/Application.kt` (modified) — explicit imports; installs ContentNegotiation; runs Database.migrate; delegates to configureRouting(); exposes GET /health returning serializable `Health(status)`
|
||||
- `server/src/test/kotlin/dev/ulfrx/recipe/ApplicationTest.kt` (modified) — replaces template `testRoot()` with health-endpoint test that composes routing without DB
|
||||
|
||||
## Decisions Made
|
||||
|
||||
See `key-decisions` in frontmatter. Highlights:
|
||||
|
||||
- HOCON `${?ENV}` optional substitution chosen over `${ENV}` (required) and `${ENV:default}` (invalid HOCON) per PITFALL #5.
|
||||
- Server logging via SLF4J/Logback (not Kermit) because Logback is already wired in `recipe.jvm.server` and Kermit is reserved for the multiplatform client.
|
||||
- `Application.configureRouting()` extension extracted to satisfy the no-Postgres-required invariant for `./gradlew :server:test`.
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
None — plan executed exactly as written. All artifacts match the plan's `must_haves` (truths, artifacts, key_links) verified against the filesystem; explicit imports satisfy D-11; `${?ENV}` lines all present; fail-loud contract intact; `Database.migrate` not referenced from the test.
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
None.
|
||||
|
||||
## User Setup Required
|
||||
|
||||
None — no external service configuration required. Postgres for end-to-end boot is provided by the Plan 06 docker-compose stack; Plan 05's own success criteria (test passing without a running DB) require nothing from the operator.
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
- Phase 2 (Auth) inherits a Ktor server with ContentNegotiation pre-installed, so JWT validation routes can return `@Serializable` DTOs immediately.
|
||||
- Phase 3 (Households) drops `V1__init.sql` into `server/src/main/resources/db/migration/`; the Flyway boot pathway is already validated.
|
||||
- Phase 11 (Deployment) inherits the HOCON `${?ENV}` pattern; homelab deploy configures `DATABASE_URL/USER/PASSWORD` via env vars without touching `application.conf`.
|
||||
- Manual end-to-end verification (`docker compose up -d postgres && ./gradlew :server:run && curl http://localhost:8080/health`) deferred to Plan 07 / manual smoke per the plan's verification section.
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
- File `server/src/main/resources/application.conf` — FOUND
|
||||
- File `server/src/main/resources/db/migration/.gitkeep` — FOUND
|
||||
- File `server/src/main/kotlin/dev/ulfrx/recipe/Database.kt` — FOUND
|
||||
- File `server/src/main/kotlin/dev/ulfrx/recipe/Application.kt` — FOUND
|
||||
- File `server/src/test/kotlin/dev/ulfrx/recipe/ApplicationTest.kt` — FOUND
|
||||
- Commit `24018ef` (feat 01-05 Task 1) — FOUND in git log
|
||||
- Commit `daefe6c` (refactor 01-05 Task 2) — FOUND in git log
|
||||
- Commit `59d0695` (test 01-05 Task 3) — FOUND in git log
|
||||
|
||||
---
|
||||
*Phase: 01-project-infrastructure-module-wiring*
|
||||
*Completed: 2026-04-24*
|
||||
Reference in New Issue
Block a user