From 775f438d78bd45358a38f5b8d2bfca86e005e2e4 Mon Sep 17 00:00:00 2001 From: "Kai (Tam Nhu) Tran" <61256810+kaitranntt@users.noreply.github.com> Date: Sat, 16 May 2026 12:33:45 -0400 Subject: [PATCH] refactor(docker): drop Bun from runtime stage; generate npm lockfile in build stage (#1256) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Build stage keeps Bun for fast installs; appends `npm install --package-lock-only` to generate an ephemeral package-lock.json immediately after `bun run build:all`. - Runtime stage removes BUN_VERSION ARG/ENV, the bun.sh curl install, and `bun install --frozen-lockfile --production`. Replaces with: COPY --from=build /app/package.json /app/package-lock.json ./ RUN npm ci --omit=dev --ignore-scripts - --ignore-scripts rationale: postinstall (scripts/postinstall.js) writes ~/.ccs/ config — not needed in Docker context. bcrypt v6+ is pure-JS, no native compile. - package-lock.json already in .gitignore (line 33); never committed. - docker/Dockerfile.integrated unchanged — no Bun present there (alpine + npm). - Targets: image size reduction >= 300 MB by eliminating ~130 MB Bun binary + installer. --- docker/Dockerfile | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 09b0be39..a8ba1239 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -2,6 +2,7 @@ # ============================================================================= # Build stage: compile TypeScript and build UI +# Bun is used here for speed; npm lockfile is generated for the runtime stage. # ============================================================================= FROM node:20-bookworm-slim AS build @@ -35,29 +36,37 @@ RUN bun run build:all # Validate build artifacts exist RUN test -d dist && test -d lib && echo "[OK] Build artifacts validated" +# Generate a fresh npm lockfile from package.json immediately after build. +# This lockfile is ephemeral — never committed; bun.lock remains the dev lockfile. +# --ignore-scripts: only lockfile generation, no install side-effects needed here. +# postinstall (scripts/postinstall.js) is a user-facing setup script; it must NOT +# run during the Docker build (it writes to ~/.ccs/ which does not exist here). +RUN --mount=type=cache,target=/root/.npm \ + npm install --package-lock-only --ignore-scripts + # ============================================================================= -# Runtime stage: minimal production image +# Runtime stage: Node-only, no Bun # ============================================================================= FROM node:20-bookworm-slim AS runtime SHELL ["/bin/bash", "-lc"] -# Pin bun version for reproducible builds -ARG BUN_VERSION=1.2.21 -ENV BUN_INSTALL=/usr/local/bun -ENV PATH="$BUN_INSTALL/bin:/home/node/.opencode/bin:$PATH" +# opencode installs its binary to /home/node/.opencode/bin — keep on PATH +ENV PATH="/home/node/.opencode/bin:$PATH" RUN apt-get update \ && apt-get install -y --no-install-recommends curl ca-certificates unzip \ && rm -rf /var/lib/apt/lists/* -# Install specific bun version -RUN curl -fsSL https://bun.sh/install | bash -s "bun-v${BUN_VERSION}" - WORKDIR /app -COPY package.json bun.lock ./ -RUN bun install --frozen-lockfile --production --ignore-scripts +# Copy the ephemeral lockfile generated in the build stage (never committed). +# --ignore-scripts: postinstall writes ~/.ccs/ config which is not needed inside +# the Docker image; runtime deps (bcrypt v6+, express, etc.) have no native +# compilation steps requiring postinstall. +COPY --from=build /app/package.json /app/package-lock.json ./ +RUN --mount=type=cache,target=/root/.npm \ + npm ci --omit=dev --ignore-scripts COPY docker/entrypoint.sh /usr/local/bin/ccs-entrypoint RUN chmod +x /usr/local/bin/ccs-entrypoint