Skip to content

ci: check the Worker configs against the code before deploying - #124

Open
nplusonedev wants to merge 2 commits into
mainfrom
ci/verify-worker-configs
Open

ci: check the Worker configs against the code before deploying#124
nplusonedev wants to merge 2 commits into
mainfrom
ci/verify-worker-configs

Conversation

@nplusonedev

Copy link
Copy Markdown
Contributor

wrangler.jsonc names code that no TypeScript gate reads: the entrypoint, and
every Durable Object and Workflow class the entrypoint has to export. Rename a
class without renaming its binding and pnpm lint, pnpm typecheck and
pnpm test all stay green. The first failure is the deploy.

The ci job now bundles both Workers and stops there.

What it catches, measured

Renamed one class_name in wrangler.jsonc and ran both gates:

result
pnpm typecheck 0 errors
wrangler deploy --dry-run exit 1

The dry run's message: Your Worker depends on the following Durable Objects, which are not exported in your entrypoint file: AgentBudgetTypo. Wrangler runs
the same validation for Workflow class exports, so the runs-workflow binding
is covered too.

Left alone, that first fails in a job that has already mutated something. The
substrate job applies D1 migrations before it deploys, and the deploy job
applies the dispatcher's migrations and runs only after the substrate and canary
have shipped. ci gates every later job, so catching it here means nothing
moved. Same argument the file already makes about job ordering: half a topology
is worse than none of it.

What it does not catch

Stated because the obvious guess is wrong. A misspelled cloudflare:* import is
not caught. Changed the import to cloudflare:workflowz: the dry run exited
0 and the bad specifier appeared verbatim in the emitted bundle. Wrangler's
esbuild plugin externalizes on filter: /^cloudflare:.*/, so any spelling after
the colon passes.

The vitest suite is what catches that case, because an alias key matches the
exact specifier only and a typo falls through to Node resolution.

Also not caught, and this one is a deliberate trade: Dockerfile build failures.
See below.

Why --containers-rollout none

Without it a dry run still builds every container image. Wrangler passes
!dryRun as the push flag, not the build flag, so --dry-run skips only the
upload. That meant the check rebuilt all 7 images on every push to main, images
the deploy jobs then build again on a runner with no layer cache.

With the flag: zero image builds, ~2s per check, and the ci job needs no
Docker.

The cost is honest. Wrangler skips the container build entirely when rollout is
none, so a broken Dockerfile, a bad base image, or a COPY of a missing file
is not caught here and still surfaces in the deploy jobs. That is unchanged from
today — main has no pre-deploy Dockerfile gate either — and paying 7 image
builds on every push to cover occasional infra/** edits is the worse trade. A
conditional plain dry-run gated on infra/** changes would close it later.

Not a PR gate

deploy.yml triggers on push to main and workflow_dispatch only, so the
ci job never runs on a pull request. This is a pre-deploy gate. PR-time
coverage would need its own pull_request workflow, the way docs.yml does it.
Out of scope here.

Verification

Both commands run in a clean checkout against the pinned wrangler, no Cloudflare
credentials in the environment: exit 0, 2.1s and 1.6s, zero Building image
lines, Total Upload still reported.

Boundary: "needs no Docker" was verified by observing that no build is invoked,
not by running on a host without Docker installed.

Split out of #123, where it was originally filed on a rationale that a negative
test disproved.

@flaredispatch-fractalboxdev flaredispatch-fractalboxdev Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ pr-review could not complete: { "step": "review", "cause": Error: ModelCallFailed: openai returned 429: {"success":false,"result":[],"messages":[],"error":[{"code":2003,"message":"Rate limited"}],"name":"AiGatewayError","httpCode":429,"internalCode":2003,"message":"Rate limited","description":"Rate limited"}, "summaryMd": undefined }

📋 View full logs & reviewed diff ↗

`wrangler.jsonc` names code that no TypeScript gate reads: the entrypoint, and
every Durable Object class the entrypoint has to export. Rename a class without
renaming its binding and `pnpm lint`, `pnpm typecheck` and `pnpm test` all stay
green. Verified by renaming one `class_name`: typecheck reported 0 errors, the
dry run exited 1 with "Your Worker depends on the following Durable Objects,
which are not exported in your entrypoint file".

Left alone, that first fails in the deploy jobs — after D1 migrations have been
applied, and for the dispatcher after the substrate and canary have already
shipped. `ci` gates every later job, so catching it here means nothing mutates.

`--containers-rollout none` is load-bearing. A plain `--dry-run` still builds
every container image (wrangler skips the push, not the build), which the deploy
jobs then build again; with the flag each check is a ~2s bundle and the `ci` job
needs no Docker.

Scoped honestly: this does NOT catch a misspelled `cloudflare:*` import.
`cloudflare:*` is an esbuild external, so any spelling bundles and exits 0. The
vitest suite is what catches that, because its alias matches only the exact
specifier.
@nplusonedev
nplusonedev force-pushed the ci/verify-worker-configs branch from 8ba47ac to 9300096 Compare August 15, 2026 21:27
@nplusonedev

Copy link
Copy Markdown
Contributor Author

Rebased onto current main (d543589) with no content change — the fresh SHA also retriggers flare-dispatch/pr-review, whose failure on the previous head was the review infrastructure itself rate-limiting (openai returned 429 through the gateway), not a finding against this diff.

Re-validated the PR's claims on the rebased head:

  • Both dry runs execute unauthenticated (requireAuth is skipped under --dry-run in wrangler's own source) and exit 0 in seconds with no Docker and no image builds — the containers flag is what keeps a 7-image build out of ci, while the DO-export validation lives in the bundling path and runs regardless.
  • The failure mode it gates still reproduces on current main: renaming one class_name fails the dry run with Your Worker depends on the following Durable Objects, which are not exported in your entrypoint file, while typecheck/lint/test all stay green.
  • Nothing landed since this PR opened covers config-vs-code validation — the nearest gates (container-config.test.ts, cron-parity.test.ts) check pool caps, image pins and cron parity, none tie class_name to entrypoint exports.

Still valid, still worth merging as-is.

@flaredispatch-fractalboxdev flaredispatch-fractalboxdev Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI code review — 💬 Comment

Risk tier: full · 0 critical · 1 warnings · 0 suggestions

Reviewers: security ⚠️ · performance 1 · code-quality ⚠️ · documentation ⚠️ · release-management 0 · compliance ⚠️ · agents-md ⚠️

1. ⚠️ Warning — Dry-run duplicates worker bundling before deployment

📍 .github/workflows/deploy.yml:84-85

Both dry-run commands build each Worker bundle, and the subsequent deploy jobs will bundle them again. This adds a full duplicate build to every deployment CI run; consider validating the generated configuration or using a deploy flow that reuses the dry-run artifact if Wrangler supports it.

📋 View full logs & reviewed diff ↗

@nplusonedev

Copy link
Copy Markdown
Contributor Author

Validated the retriggered review's finding.

Duplicate bundling — accurate observation, accepted as the cost of the gate. The dry runs do bundle both Workers, and the deploy jobs bundle them again. Measured on this branch: each dry run completes in a few seconds, unauthenticated, with no image builds (--containers-rollout none skips the Docker path while the DO-export validation lives in bundling and runs regardless). Reusing the bundle is not practical: the deploy jobs run on separate runners, so handing the artifact across costs upload/download round-trips exceeding the seconds saved, and wrangler deploy takes no prebuilt-bundle input — it always bundles. A few duplicated seconds in ci buys failing before either deploy job has applied D1 migrations, which is the whole point of the PR.

The two dry runs cost seconds and pnpm test costs minutes, so ordered last a
config that names missing code waited out the whole suite before saying so.
After typecheck deliberately, so a real code error still fails with the
typechecker's message rather than esbuild's.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant