# syntax=docker/dockerfile:1.6 # `# syntax=...` enables BuildKit's --mount feature on the builder so we can # share the pnpm content-addressed store across image builds. Without it the # next two RUN steps fall back to plain mode and the cache mount is ignored # (build still works, just slower). # Stage 1: Build TypeScript FROM node:20-alpine AS builder RUN corepack enable && corepack prepare pnpm@9.15.0 --activate WORKDIR /app # Copy workspace config and package manifests COPY pnpm-workspace.yaml pnpm-lock.yaml package.json tsconfig.base.json ./ COPY src/mcpd/package.json src/mcpd/tsconfig.json src/mcpd/ COPY src/db/package.json src/db/tsconfig.json src/db/ COPY src/shared/package.json src/shared/tsconfig.json src/shared/ COPY src/web/package.json src/web/tsconfig.json src/web/ # Install all dependencies. The cache mount keeps pnpm's CAS store warm # across builds: only newly-changed packages get downloaded; everything # else hardlinks from the cache. Drops install from ~60s to <5s on a # warm cache. `--frozen-lockfile` still guarantees lockfile fidelity. RUN --mount=type=cache,id=pnpm-store-mcpd-builder,target=/root/.local/share/pnpm/store \ pnpm install --frozen-lockfile # Copy source code COPY src/mcpd/src/ src/mcpd/src/ COPY src/db/src/ src/db/src/ COPY src/db/prisma/ src/db/prisma/ COPY src/shared/src/ src/shared/src/ COPY src/web/src/ src/web/src/ COPY src/web/index.html src/web/vite.config.ts src/web/ # Generate Prisma client and build TypeScript + web SPA RUN pnpm -F @mcpctl/db db:generate RUN pnpm -F @mcpctl/shared build && pnpm -F @mcpctl/db build && pnpm -F @mcpctl/mcpd build RUN pnpm -F @mcpctl/web build # Stage 2: Production runtime FROM node:20-alpine RUN apk add --no-cache git openssh-client \ && corepack enable && corepack prepare pnpm@9.15.0 --activate WORKDIR /app # Copy workspace config, manifests, and lockfile COPY pnpm-workspace.yaml pnpm-lock.yaml package.json ./ COPY src/mcpd/package.json src/mcpd/ COPY src/db/package.json src/db/ COPY src/shared/package.json src/shared/ # Install all deps (prisma CLI needed at runtime for db push). Same # cache-mount trick as the builder stage; separate cache id so the two # stages don't compete for the same lock. RUN --mount=type=cache,id=pnpm-store-mcpd-runtime,target=/root/.local/share/pnpm/store \ pnpm install --frozen-lockfile # Copy prisma schema and generate client COPY src/db/prisma/ src/db/prisma/ RUN pnpm -F @mcpctl/db db:generate # Copy built output from builder COPY --from=builder /app/src/shared/dist/ src/shared/dist/ COPY --from=builder /app/src/db/dist/ src/db/dist/ COPY --from=builder /app/src/mcpd/dist/ src/mcpd/dist/ # Copy the web SPA bundle. registerWebUi() looks for it at this path # (or wherever MCPD_WEB_ROOT is set). COPY --from=builder /app/src/web/dist/ /usr/share/mcpd/web/ # Copy templates for seeding COPY templates/ templates/ # Copy entrypoint COPY deploy/entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh EXPOSE 3100 HEALTHCHECK --interval=10s --timeout=5s --retries=3 --start-period=10s \ CMD wget -q --spider http://localhost:3100/healthz || exit 1 ENTRYPOINT ["/entrypoint.sh"]