[E00-S02-T01] docker compose up -d starts DB + app #382

Merged
kpcto merged 2 commits from feature/168 into main 2026-08-28 23:55:20 +00:00
Member

What changed

Establishes the [E00-S02-T01] Docker Compose baseline (#59) so docker compose up -d starts both the database and the application from a clean clone, with no .env required (all values have defaults).

Review round (addresses the two request-changes findings on the previous pass):

  • apps/server/Dockerfile — both build and runtime stages moved from node:24-alpine to node:24.19.0-bookworm-slim: the architecture/Technology-Stack doc §5.4 requires a glibc Debian base because argon2 is a native dependency (musl/Alpine causes native-module build surprises).
  • compose.yaml — db image moved from postgres:16-alpine to postgres:18-bookworm (the PostgreSQL 18 line on Debian bookworm), matching the documented PostgreSQL 18 target (§5.2/§5.4/§6.2 and the §7 golden tuple pin PostgreSQL 18.6).
  • tests/compose-config.test.mjs — assertions updated so the committed state they lock in matches the corrected image bases.

Baseline content:

  • compose.yaml (repo root) — the stack:
    • db service: postgres:18-bookworm, POSTGRES_DB/POSTGRES_USER/POSTGRES_PASSWORD with :- defaults, host port 5432 published (overridable via POSTGRES_PORT);
    • app service: built from the committed apps/server/Dockerfile, DATABASE_URL pointed at the db service (postgres://…@db:5432/…), host port 3000 published (overridable via APP_PORT), depends_on: [db] so the application starts after the database.
  • apps/server/Dockerfile — multi-stage Node 24.19.0 image:
    • build stage: node:24.19.0-bookworm-slim + Corepack-enabled pnpm 11.23.0, pnpm install --frozen-lockfile (every workspace manifest copied so the in-image workspace matches the lockfile importers), then pnpm --filter @personal-blog/server build (tsc → dist/);
    • runtime stage: node:24.19.0-bookworm-slim, copies node_modules, compiled dist/ and the manifest, EXPOSE 3000, CMD ["node", "apps/server/dist/index.js"] (the bootstrap placeholder loads and exits 0 — the Fastify shell that turns it into a serving process is a later story).
  • .dockerignore (repo root) — excludes .git, node_modules, dist, .env*, logs from the build context.
  • tests/compose-config.test.mjs — zero-dependency node:test suite that locks in both acceptance criteria against the committed state (see table), plus non-vacuous mutation probes and, when a Docker CLI/daemon is present, real-stack probes: docker compose config validation and an actual docker compose up -d → ps → down run asserting the db container is up and the app container starts cleanly (skips cleanly where Docker is unavailable).

Explicitly out of scope per the brief, not touched: PostgreSQL health gate (E00-S02-T02), app health endpoint (E00-S02-T03), DB volume persistence (E00-S02-T04). No manifests, no lockfile, no CI workflow changes — the frozen-install CI job is unaffected.

Criterion → test table

Acceptance criterion Test (fails without the committed state)
docker compose up -d starts the database tests/compose-config.test.mjs — "the database service is defined so docker compose up -d starts the database": the committed compose.yaml declares a db service with the committed PostgreSQL 18 image (postgres:18-bookworm), credentials with defaults, and a published :5432 port. Non-vacuous: "removing the db service makes the database criterion fail (mutation probe)" proves the assertion catches a missing db. When Docker is available, "docker compose up -d starts the database and application containers" runs the real stack and asserts the db container is running
docker compose up -d starts the application tests/compose-config.test.mjs — "the application service is defined so docker compose up -d starts the application" (app service: build context ., apps/server/Dockerfile, DATABASE_URL → db, published :3000, depends_on: [db]) and "the application image is defined by a committed multi-stage Dockerfile" (Node 24.19.0 bookworm-slim build+runtime stages, frozen install, pnpm --filter @personal-blog/server build, runtime entrypoint node apps/server/dist/index.js). Non-vacuous: app-removal mutation probe + Dockerfile-without-CMD probe. When Docker is available, the real-stack probe asserts the app container is created and starts cleanly (exit 0)

Test plan executed

  • node --test tests/compose-config.test.mjs → 9/9 pass, 2 skipped (Docker probes skip cleanly where no Docker CLI/daemon exists — this sandbox has none) ✓
  • Full suite node --test tests/*.test.mjs → 48 pass / 12 fail / 2 skip; the 12 failures are the pre-existing Node-22-environment ones on pristine main (no node_modules, engines.node: 24.x restriction) — identical to the baseline the previous tester recorded, none caused by this change ✓
  • compose.yaml re-parsed (minimal block-YAML parser + independent structure check): db.image = postgres:18-bookworm, app.build unchanged ✓
  • Regression check: architecture-import suite and all compose-config assertions green on the changed files; lockfile/CI workflow untouched ✓
  • Manual acceptance (issue test plan): docker compose up -d then confirm DB + app containers start; rollback docker compose down — the Docker-gated probe automates exactly this when a daemon is available

Risks / notes

  • The app container starts and exits 0 at T01 because apps/server is still the bootstrap placeholder; the health-gate/health-endpoint stories (T02/T03) turn it into a staying-up process. The integration probe therefore asserts a clean exit (0) for app and running for db.
  • postgres:18-bookworm is the floating 18.x tag per the review direction; the doc §5.4 exact-minor pin (postgres:18.6-bookworm) is the provenance-verified target for later provenance re-runs (§5.2.1), and 18-bookworm is the same 18.x line on Debian bookworm.
  • The Docker-gated tests skip when docker/the Compose plugin/daemon are unavailable, so the suite stays green on Docker-less machines while giving real end-to-end validation where Docker exists.
  • .dockerignore keeps local artifacts out of the build context; a committed .env.example template remains out of scope (E00-S04).

Refs #168

## What changed Establishes the [E00-S02-T01] Docker Compose baseline (#59) so `docker compose up -d` starts both the database and the application from a clean clone, with no `.env` required (all values have defaults). **Review round (addresses the two `request-changes` findings on the previous pass):** - **`apps/server/Dockerfile`** — both build and runtime stages moved from `node:24-alpine` to **`node:24.19.0-bookworm-slim`**: the architecture/Technology-Stack doc §5.4 requires a glibc Debian base because argon2 is a native dependency (musl/Alpine causes native-module build surprises). - **`compose.yaml`** — `db` image moved from `postgres:16-alpine` to **`postgres:18-bookworm`** (the PostgreSQL 18 line on Debian bookworm), matching the documented PostgreSQL 18 target (§5.2/§5.4/§6.2 and the §7 golden tuple pin PostgreSQL 18.6). - **`tests/compose-config.test.mjs`** — assertions updated so the committed state they lock in matches the corrected image bases. Baseline content: - **`compose.yaml`** (repo root) — the stack: - `db` service: `postgres:18-bookworm`, `POSTGRES_DB`/`POSTGRES_USER`/`POSTGRES_PASSWORD` with `:-` defaults, host port `5432` published (overridable via `POSTGRES_PORT`); - `app` service: built from the committed `apps/server/Dockerfile`, `DATABASE_URL` pointed at the `db` service (`postgres://…@db:5432/…`), host port `3000` published (overridable via `APP_PORT`), `depends_on: [db]` so the application starts after the database. - **`apps/server/Dockerfile`** — multi-stage Node 24.19.0 image: - `build` stage: `node:24.19.0-bookworm-slim` + Corepack-enabled pnpm 11.23.0, `pnpm install --frozen-lockfile` (every workspace manifest copied so the in-image workspace matches the lockfile importers), then `pnpm --filter @personal-blog/server build` (`tsc` → `dist/`); - `runtime` stage: `node:24.19.0-bookworm-slim`, copies `node_modules`, compiled `dist/` and the manifest, `EXPOSE 3000`, `CMD ["node", "apps/server/dist/index.js"]` (the bootstrap placeholder loads and exits 0 — the Fastify shell that turns it into a serving process is a later story). - **`.dockerignore`** (repo root) — excludes `.git`, `node_modules`, `dist`, `.env*`, logs from the build context. - **`tests/compose-config.test.mjs`** — zero-dependency `node:test` suite that locks in both acceptance criteria against the committed state (see table), plus non-vacuous mutation probes and, when a Docker CLI/daemon is present, real-stack probes: `docker compose config` validation and an actual `docker compose up -d` → `ps` → `down` run asserting the `db` container is up and the `app` container starts cleanly (skips cleanly where Docker is unavailable). Explicitly out of scope per the brief, **not touched**: PostgreSQL health gate (E00-S02-T02), app health endpoint (E00-S02-T03), DB volume persistence (E00-S02-T04). No manifests, no lockfile, no CI workflow changes — the frozen-install CI job is unaffected. ## Criterion → test table | Acceptance criterion | Test (fails without the committed state) | | --- | --- | | `docker compose up -d` starts the database | `tests/compose-config.test.mjs` — "the database service is defined so `docker compose up -d` starts the database": the committed `compose.yaml` declares a `db` service with the committed PostgreSQL 18 image (`postgres:18-bookworm`), credentials with defaults, and a published `:5432` port. Non-vacuous: "removing the db service makes the database criterion fail (mutation probe)" proves the assertion catches a missing `db`. When Docker is available, "docker compose up -d starts the database and application containers" runs the real stack and asserts the `db` container is **running** | | `docker compose up -d` starts the application | `tests/compose-config.test.mjs` — "the application service is defined so `docker compose up -d` starts the application" (`app` service: build context `.`, `apps/server/Dockerfile`, `DATABASE_URL` → `db`, published `:3000`, `depends_on: [db]`) **and** "the application image is defined by a committed multi-stage Dockerfile" (Node 24.19.0 bookworm-slim build+runtime stages, frozen install, `pnpm --filter @personal-blog/server build`, runtime entrypoint `node apps/server/dist/index.js`). Non-vacuous: app-removal mutation probe + Dockerfile-without-`CMD` probe. When Docker is available, the real-stack probe asserts the `app` container is created and starts cleanly (exit 0) | ## Test plan executed - `node --test tests/compose-config.test.mjs` → **9/9 pass, 2 skipped** (Docker probes skip cleanly where no Docker CLI/daemon exists — this sandbox has none) ✓ - Full suite `node --test tests/*.test.mjs` → 48 pass / 12 fail / 2 skip; the 12 failures are the pre-existing Node-22-environment ones on pristine `main` (no `node_modules`, `engines.node: 24.x` restriction) — identical to the baseline the previous tester recorded, none caused by this change ✓ - `compose.yaml` re-parsed (minimal block-YAML parser + independent structure check): `db.image = postgres:18-bookworm`, `app.build` unchanged ✓ - Regression check: architecture-import suite and all compose-config assertions green on the changed files; lockfile/CI workflow untouched ✓ - Manual acceptance (issue test plan): `docker compose up -d` then confirm DB + app containers start; rollback `docker compose down` — the Docker-gated probe automates exactly this when a daemon is available ## Risks / notes - The `app` container starts and exits 0 at T01 because `apps/server` is still the bootstrap placeholder; the health-gate/health-endpoint stories (T02/T03) turn it into a staying-up process. The integration probe therefore asserts a clean exit (0) for `app` and **running** for `db`. - `postgres:18-bookworm` is the floating 18.x tag per the review direction; the doc §5.4 exact-minor pin (`postgres:18.6-bookworm`) is the provenance-verified target for later provenance re-runs (§5.2.1), and `18-bookworm` is the same 18.x line on Debian bookworm. - The Docker-gated tests `skip` when `docker`/the Compose plugin/daemon are unavailable, so the suite stays green on Docker-less machines while giving real end-to-end validation where Docker exists. - `.dockerignore` keeps local artifacts out of the build context; a committed `.env.example` template remains out of scope (E00-S04). Refs #168
bot-implementer added 1 commit 2026-08-28 23:35:22 +00:00
feat: docker compose up -d starts DB + app (E00-S02-T01)
CI / Frozen lockfile install (pull_request) Successful in 43s
3be575818d
bot-implementer added 1 commit 2026-08-28 23:45:24 +00:00
fix: align app/db image bases with documented stack (E00-S02-T01)
CI / Frozen lockfile install (pull_request) Successful in 48s
3bbea68e6c
Switch the app image from node:24-alpine to node:24.19.0-bookworm-slim in
both build and runtime stages (Technology-Stack 5.4: glibc Debian base
required because argon2 is a native dependency; musl/Alpine causes
native-module build surprises), and the db image from postgres:16-alpine
to postgres:18-bookworm (Technology-Stack 5.2/5.4/6.2 + golden tuple 7
pin PostgreSQL 18.6; 18-bookworm is the 18.x line on Debian bookworm).

Update tests/compose-config.test.mjs so the committed assertions lock in
the corrected image bases (db image, Dockerfile build/runtime stages,
parser probe).
kpcto merged commit 9e2ac55400 into main 2026-08-28 23:55:20 +00:00
kpcto deleted branch feature/168 2026-08-28 23:55:20 +00:00
Sign in to join this conversation.