From 7f62ca6253892b6efb3e8d029e206d6489a71a7d Mon Sep 17 00:00:00 2001 From: abrichr Date: Sun, 2 Aug 2026 12:28:22 +0200 Subject: [PATCH] fix(worker): fail the image build on a stale or missing lockfile The worker image was built with: COPY package.json pnpm-workspace.yaml pnpm-lock.yaml* turbo.json ./ RUN pnpm install --frozen-lockfile || pnpm install Both halves defeat the lockfile. The `pnpm-lock.yaml*` glob makes the COPY succeed when the lockfile is absent, and the `|| pnpm install` fallback re-resolves every dependency from scratch whenever `--frozen-lockfile` fails. Between them, a build with a stale or missing lockfile still went green, and shipped a dependency set nobody had reviewed. The deployed image was therefore not reproducible from the commit it claimed to come from, and lockfile drift was invisible rather than loud. Copy the lockfile without the glob and drop the fallback. A stale lockfile is now a change to make in the repository, which is where it belongs. Verified before committing: `pnpm install --frozen-lockfile` succeeds against the committed lockfile (494 packages, 0 downloaded, exit 0), and the full image builds on Fly's remote builder with the exact command `Deploy Worker` runs: flyctl deploy --config apps/worker/fly.toml \ --dockerfile apps/worker/Dockerfile --remote-only --build-only That produced registry.fly.io/wright-worker:deployment-01KZ103D4RXNBE1NQKK9Q1N9JS (815 MB) and created no release, so wright-worker.fly.dev stayed on v18 throughout. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01NyCHrzA1psrKMFfroYbzaM --- apps/worker/Dockerfile | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/apps/worker/Dockerfile b/apps/worker/Dockerfile index a2380d6..e3b534f 100644 --- a/apps/worker/Dockerfile +++ b/apps/worker/Dockerfile @@ -18,13 +18,25 @@ RUN corepack enable && corepack prepare pnpm@9.15.0 --activate WORKDIR /app -# Copy only what pnpm needs for dependency resolution (layer cache) -COPY package.json pnpm-workspace.yaml pnpm-lock.yaml* turbo.json ./ +# Copy only what pnpm needs for dependency resolution (layer cache). +# +# `pnpm-lock.yaml` is copied WITHOUT a glob on purpose. The previous +# `pnpm-lock.yaml*` tolerated the file being absent, so a build with no +# lockfile at all still succeeded and resolved whatever was newest that day. +COPY package.json pnpm-workspace.yaml pnpm-lock.yaml turbo.json ./ COPY packages/shared/package.json packages/shared/ COPY apps/worker/package.json apps/worker/ -# Install all deps (including devDependencies needed for build) -RUN pnpm install --frozen-lockfile || pnpm install +# Install all deps (including devDependencies needed for build). +# +# No `|| pnpm install` fallback. That fallback silently defeated the lockfile: +# when the lockfile was stale or unreadable, `--frozen-lockfile` failed, the +# fallback re-resolved every dependency from scratch, and the build went green +# with a dependency set nobody had reviewed. The deployed image was therefore +# not reproducible from the commit it claimed to come from, and lockfile drift +# was invisible. Fail loudly instead: a stale lockfile is a change to make in +# the repository, not a thing for the release build to paper over. +RUN pnpm install --frozen-lockfile # --------------------------------------------------------------------------- # Stage 2: Build TypeScript