feat: app reports ready only after the startup migration run (E00-S03-T06)

This commit is contained in:
implementer
2026-08-30 01:56:19 +00:00
parent e8cafa090b
commit ebdee5daa5
5 changed files with 118 additions and 16 deletions
+27 -5
View File
@@ -5,8 +5,12 @@
# [E00-S02-T01/T02/T03] baseline: builds the workspace server package with the
# pinned toolchain (Node 24.19.0 + pnpm 11.23.0, frozen lockfile) and runs the
# compiled entrypoint. Since T03 the entrypoint is a minimal Node `node:http`
# server answering `GET /health` with `{"status":"ok"}` (HTTP 200) on port
# 3000, so the app container stays up and the health endpoint succeeds. The
# server answering `GET /health` on port 3000. Since T06 (E00-S03) the health
# endpoint is the readiness probe: the app runs the startup migrations (the
# `MigrationRunner` from `@personal-blog/database-postgres`) before reporting
# ready — `GET /health` answers 503 `{"status":"not ready"}` while the run is
# in flight and flips to 200 `{"status":"ok"}` only after it completes — so
# the app container does not report ready before migrations complete. The
# Fastify 5 application shell (and the real HTTP API) lands in a later story;
# DB volume persistence (T04), read-only root filesystem (T06) and multi-arch
# build targets (T07) are Compose-level concerns (see compose.yaml — the
@@ -17,6 +21,15 @@
# T05 the runtime stage drops root privileges (runs as the image's non-root
# `node` user).
#
# The app now depends on the `database-postgres` workspace package (the single
# owner of the pg/Kysely driver, E00-S03-T02). The build stage therefore also
# installs/builds that package — the server's `build`/`typecheck` scripts
# build their workspace dependency first (`pnpm --filter
# @personal-blog/database-postgres build`), and the runtime stage ships the
# compiled `packages/database-postgres/dist` next to the copied workspace
# node_modules links so the server's `@personal-blog/database-postgres` import
# resolves at run time.
#
# T08: the image embeds no secrets. The Dockerfile declares no secret-bearing
# ARG/ENV instruction (the only ENV is `NODE_ENV=production`) and every COPY
# copies a fixed, non-secret path (manifests, source, compiled dist) — never
@@ -44,15 +57,21 @@ RUN corepack enable
# invalidate the dependency layer, then install against the committed lockfile
# (the same `--frozen-lockfile` path CI and developers use). Every workspace
# package manifest is copied so the in-image workspace matches the lockfile
# importers exactly (apps/server, packages/core, extensions/example).
# importers exactly (apps/server, packages/core, packages/database-postgres,
# extensions/example).
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml tsconfig.base.json ./
COPY apps/server/package.json apps/server/package.json
COPY packages/core/package.json packages/core/package.json
COPY packages/database-postgres/package.json packages/database-postgres/package.json
COPY extensions/example/package.json extensions/example/package.json
RUN pnpm install --frozen-lockfile
# Compile the server package (tsc -p apps/server/tsconfig.json -> dist/).
# Compile the server package (tsc -p apps/server/tsconfig.json -> dist/). The
# server's build script builds its workspace dependency first (the
# `database-postgres` package, whose compiled dist the server imports), so a
# single command produces both dists in the right order.
COPY apps/server apps/server
COPY packages/database-postgres packages/database-postgres
RUN pnpm --filter @personal-blog/server build
# --- runtime stage: Node 24.19.0 (bookworm-slim) + compiled output only ------
@@ -61,10 +80,13 @@ WORKDIR /app
ENV NODE_ENV=production
# The workspace install (devDependencies included — image-size pruning is a
# later E00-S02 concern) plus the compiled server output and manifest.
# later E00-S02 concern) plus the compiled server output, the compiled
# database-postgres output the server imports, and the package manifests.
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/apps/server/dist ./apps/server/dist
COPY --from=build /app/apps/server/package.json ./apps/server/package.json
COPY --from=build /app/packages/database-postgres/dist ./packages/database-postgres/dist
COPY --from=build /app/packages/database-postgres/package.json ./packages/database-postgres/package.json
# T05: run as the image's non-root `node` user (uid/gid 1000, shipped with the
# official Node image) so the app container does not run with root privileges.