From 00efbab1078982a05ecb61c670778c0be4682964 Mon Sep 17 00:00:00 2001 From: Lucas Berger Date: Thu, 4 Jun 2026 10:17:06 -0400 Subject: [PATCH] fix(01-01): build Docker image from repo-root pnpm workspace context The walking-skeleton Dockerfile built from a ./apps/api context and could not work in a pnpm workspace: the lockfile lives at the repo root, pnpm 11 refused esbuild's build script without the root pnpm-workspace.yaml, the dev stage never compiled src->dist, and the production stage had invalid COPY syntax referencing a path outside its context. Switch to the correct monorepo pattern: build from the repo-root context, copy the workspace manifest + lockfile + both package.jsons, and install with --frozen-lockfile --filter @familysync/api... Reorder stages so production is default; dev reuses builder output. Fix the dev volume mount path. Surfaced while clearing the Task 3 checkpoint (stack bring-up): drizzle-kit push applied the 4 tables and /health returned {ok:true,db:up} end-to-end. --- apps/api/Dockerfile | 48 +++++++++++++++++++++++------------------- docker-compose.dev.yml | 5 +++-- docker-compose.yml | 5 ++++- 3 files changed, 33 insertions(+), 25 deletions(-) diff --git a/apps/api/Dockerfile b/apps/api/Dockerfile index 60c1461..cfa80f4 100644 --- a/apps/api/Dockerfile +++ b/apps/api/Dockerfile @@ -1,29 +1,33 @@ +# Built from the REPO ROOT context (see docker-compose.yml: build.context: .) +# so the pnpm workspace manifest + lockfile are available for a deterministic, +# workspace-aware install. apps/api is one package in the pnpm workspace. FROM node:22-alpine AS base WORKDIR /app RUN corepack enable pnpm -FROM base AS deps -COPY package.json pnpm-lock.yaml* ./ -RUN pnpm install --frozen-lockfile --prod - +# Install layer: copy only manifests + lockfile first for cache efficiency. +# Both workspace package.json files are needed so --frozen-lockfile can validate +# every importer in pnpm-lock.yaml. pnpm-workspace.yaml carries allowBuilds.esbuild. FROM base AS builder -COPY package.json pnpm-lock.yaml* ./ -RUN pnpm install --frozen-lockfile -COPY tsconfig.json ./ -COPY src/ ./src/ -RUN pnpm build - -FROM base AS production -WORKDIR /app -COPY --from=deps /app/node_modules ./node_modules -COPY --from=builder /app/dist ./dist -# PWA static assets served from ./public (built separately) -COPY apps/pwa/dist/ ./public/ 2>/dev/null || true -CMD ["node", "dist/index.js"] +COPY pnpm-workspace.yaml pnpm-lock.yaml package.json ./ +COPY apps/api/package.json ./apps/api/ +COPY apps/pwa/package.json ./apps/pwa/ +RUN pnpm install --frozen-lockfile --filter @familysync/api... +COPY apps/api ./apps/api +RUN pnpm --filter @familysync/api build FROM base AS dev -WORKDIR /app -COPY package.json pnpm-lock.yaml* ./ -RUN pnpm install --frozen-lockfile -COPY tsconfig.json ./ -CMD ["pnpm", "dev"] +WORKDIR /app/apps/api +COPY --from=builder /app /app +CMD ["node", "--watch", "dist/index.js"] + +FROM base AS production +COPY pnpm-workspace.yaml pnpm-lock.yaml package.json ./ +COPY apps/api/package.json ./apps/api/ +COPY apps/pwa/package.json ./apps/pwa/ +RUN pnpm install --frozen-lockfile --prod --filter @familysync/api... +COPY --from=builder /app/apps/api/dist ./apps/api/dist +WORKDIR /app/apps/api +# PWA static assets (apps/pwa) are built and served separately; /health works +# without them and the catch-all static route 404s gracefully. +CMD ["node", "dist/index.js"] diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 53be185..1e0beab 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -2,10 +2,11 @@ services: api: build: - context: ./apps/api + context: . + dockerfile: apps/api/Dockerfile target: dev volumes: - - ./apps/api/src:/app/src + - ./apps/api/src:/app/apps/api/src environment: NODE_ENV: development diff --git a/docker-compose.yml b/docker-compose.yml index 12b4cba..46ab567 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,9 @@ services: api: - build: ./apps/api + build: + context: . + dockerfile: apps/api/Dockerfile + target: production environment: DB_HOST: mariadb DB_PORT: 3306