Valcraven Docs
Deployment & Operations

CI with Workers Builds

How the web Worker auto-deploys on push via Cloudflare Workers Builds, and why the realtime Worker deploys manually.

Valcraven uses Cloudflare Workers Builds — Cloudflare's built-in, Git-connected CI/CD — to deploy the web Worker automatically. Once your repository is connected to the Worker, every push to the production branch (main) triggers a build-and-deploy on Cloudflare's infrastructure. There's no GitHub Actions deploy workflow to maintain and no wrangler API token to store as a repo secret for routine deploys.

How it works

Workers Builds is configured in the Cloudflare dashboard, not by a file in this repo — you connect your GitHub (or GitLab) repository to the Worker and set the branch, build command, and deploy command there. For the Valcraven web Worker that means:

  • Trigger — a push to main.
  • Build — the OpenNext build (npm run build:cf), which produces apps/web/.open-next.
  • Deploywrangler deploy of that bundle (what npm run deploy:cf runs under the hood).

Because the exact commands are configured in the dashboard, treat the above as the shape of the pipeline rather than a checked-in config. The important properties for downstream apps:

  • Only the web Worker auto-deploys. Workers Builds is scoped to a single Worker + repo connection.
  • Secrets set with wrangler secret put persist across deploys — Workers Builds does not re-apply them, and you should never put secret values in the build config. See Secrets & env.
  • D1 migrations are not run by the build. Apply them separately (see Running migrations in prod) — a deploy that ships code expecting a new column will fail at runtime until the migration is applied to the remote D1.

The realtime Worker deploys manually

The realtime Worker (apps/realtime) is a separate Worker and is not part of the Workers Builds pipeline. Deploy it by hand whenever apps/realtime changes:

npm run deploy:realtime      # wrangler deploy --env production, in apps/realtime

Or ship both Workers at once (app first, so the WEB service binding target exists):

npm run deploy:cf:all        # build:cf → deploy:cf (web) → deploy:realtime

This split is intentional: the realtime Worker owns Durable Objects and long-lived WebSocket state, and its production config lives under a named [env.production] env in apps/realtime/wrangler.toml. Keeping it a manual, deliberate deploy avoids surprising the live agent DOs on every push. See Deploying to Cloudflare Workers → The realtime Worker.

The repo's GitHub Actions are on-demand only

Separately from deploys, the repo ships GitHub Actions workflows for verification — ci.yml (lint, typecheck, unit + integration tests, build), e2e.yml (Playwright), and mobile.yml (Expo bundle). All of them are workflow_dispatch only — they do not run automatically on push or pull request. This keeps GitHub Actions cost down during heavy active development. Consequences:

  • PRs show no automatic checks. Don't wait for them — verify locally (npm run build, npm test) instead.
  • To run a workflow, dispatch it manually from the Actions Run workflow button or the CLI:
gh workflow run ci.yml            # optionally: --ref <branch>
gh workflow run e2e.yml
gh workflow run mobile.yml

These workflows are not the deploy path — they build and test but do not push to Cloudflare. Deploys happen through Workers Builds (web) and deploy:realtime (realtime).

On this page