fix: copy every workspace manifest into the image deps layers - #370
fix: copy every workspace manifest into the image deps layers#370Deodat-Lawson wants to merge 1 commit into
Conversation
The deps stage lists workspace manifests one COPY at a time so source
edits don't bust the install cache. That list is hand-maintained and
nothing checked it against the workspace, so the two packages added
recently — google-drive and document-conversion-engine — were never
added to it.
pnpm install --frozen-lockfile then resolves a workspace missing those
members, their dependencies are never installed, and the failure only
surfaces in the builder stage as a type error naming a dependency the
package does declare:
packages/google-drive/src/wire.ts:7
Type error: Cannot find module 'zod'
Both image builds were affected, so the worker would have failed the
same way once the web build was fixed.
Adds scripts/ci/check-dockerfile-manifests.mjs, run early in CI: it
compares the workspace against each Dockerfile's deps layer and prints
the exact COPY line to add. Without it the next package repeats this,
and the symptom points at the wrong file.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e79278591c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // apps/* manifests are only required in the Dockerfile that builds | ||
| // that app; a worker image has no reason to carry apps/web's. | ||
| if (manifest.startsWith("apps/") && !dockerfile.startsWith(manifest.slice(0, manifest.lastIndexOf("/")))) { | ||
| continue; |
There was a problem hiding this comment.
Keep the web manifest covered by the worker check
When apps/web/package.json is omitted from the worker dependency layer, this exception skips it and CI still reports success. The worker Dockerfile explicitly copies and runs source from apps/web, whose third-party dependencies are installed from that manifest, so removing its existing COPY recreates the unresolved-dependency failure this guard is intended to catch.
Useful? React with 👍 / 👎.
| if (!contents.includes(manifest)) { | ||
| failures.push({ dockerfile, manifest }); | ||
| } |
There was a problem hiding this comment.
Verify the manifest is copied before dependency installation
When a manifest path appears only in a comment or in a COPY after RUN pnpm install, this substring test passes even though the dependency layer does not contain the manifest at installation time. That leaves this new CI guard green while the subsequent image build fails with the same missing-dependency symptom; restrict the check to active COPY instructions before the install command.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit e792785. Configure here.
| import { join, resolve as resolvePath } from "node:path"; | ||
|
|
||
| const ROOT = resolvePath(import.meta.dirname, "../.."); | ||
| const DOCKERFILES = ["apps/web/Dockerfile", "apps/worker/Dockerfile"]; |
There was a problem hiding this comment.
Manifest check skips prebuilt Dockerfile
Medium Severity
The new guard only inspects apps/web/Dockerfile and apps/worker/Dockerfile, so it never sees apps/web/Dockerfile.prebuilt. That file uses the same hand-maintained deps-layer COPY list and still omits packages/google-drive and packages/document-conversion-engine. docker.yml smoke-builds its migrate target from that incomplete workspace, and the check will report success the next time a package is added there too.
Reviewed by Cursor Bugbot for commit e792785. Configure here.


Problem
mainhas not produced a deployable image since 2026-08-29 — four consecutive failed runs of the Docker workflow. Becausedocker.ymlonly pushes:lateston a successful build, GHCR's:latestis still commit5452aedd, and any productiondocker compose pulltoday is a no-op. Three merges have landed on a branch that cannot build.The most recent failure:
packages/google-drive/package.jsondeclares"zod": "^3.23.8", so the error points at the wrong thing.Root cause
The deps stage copies workspace manifests one explicit
COPYline at a time, so that source edits don't bust the install cache:That list is maintained by hand and nothing has ever compared it to the actual workspace. The two packages added recently —
packages/google-driveandpackages/document-conversion-engine— were never added, in either Dockerfile.So
pnpm install --frozen-lockfileresolves a workspace that is missing those members. The install succeeds (the lockfile is consistent; the packages simply aren't part of the graph it sees), their dependencies are never installed, and the failure surfaces two minutes later in the builder stage as an unresolvable import — naming a dependency the package does declare, in a file that is not at fault.apps/worker/Dockerfilehad the identical omission, so the worker image would have failed the same way as soon as the web build was fixed.Changes
apps/web/Dockerfile,apps/worker/Dockerfile— add the two missing manifests to the deps layer.scripts/ci/check-dockerfile-manifests.mjs— compares every workspace manifest against each Dockerfile's deps layer and fails with the exactCOPYline to add..github/workflows/CI.yml— runs that check early in thecheckjob, beforepnpm install. It needs no dependencies, so it fails in seconds rather than after a multi-minute image build.Why the guard
This failure mode is silent at the point of the mistake and misleading at the point of discovery: adding a package is normal work, nothing warns you, and the error that eventually appears blames a dependency that is correctly declared. Without a check, the next package added repeats it.
Verified both directions:
Validation
The
pull_requesttrigger runs a smoke build of both images, which is the real test of this change — a green build here is proof the deps layer now resolveszodforpackages/google-drive.Note for whoever deploys after this merges
This unblocks the image, but production is still on
5452aeddand the intervening commits replace Clerk with better-auth. Deploying will needBETTER_AUTH_SECRETset (openssl rand -base64 32) before the app can boot — it isrequiredString()— plusBETTER_AUTH_URLbehind the proxy, and three new migrations will apply. That is a planned cutover, not a routine redeploy.🤖 Generated with Claude Code
Note
Low Risk
Build/CI guardrails and Dockerfile manifest COPY lines only; no runtime auth, data, or application logic changes.
Overview
Fixes broken Docker image builds caused by hand-maintained deps-stage
COPYlists omitting newer workspace packages (document-conversion-engine,google-drive). Without those manifests,pnpm install --frozen-lockfilesees an incomplete workspace, skips those packages’ dependencies, and the builder later fails with misleading errors (e.g. missingzod).apps/web/Dockerfileandapps/worker/Dockerfilenow copy both missingpackage.jsonfiles in the deps layer.Adds
scripts/ci/check-dockerfile-manifests.mjs, which enumerates workspace manifests underpackages/,pipelines/, andapps/and asserts each required path appears in the web and worker Dockerfiles (with app-specific rules so the worker image isn’t forced to list unrelatedapps/*manifests)..github/workflows/CI.ymlruns that script early in thecheckjob—beforepnpm install—so omissions fail in seconds instead of deep in a multi-minute image build.Reviewed by Cursor Bugbot for commit e792785. Bugbot is set up for automated code reviews on this repo. Configure here.