Releasing to prod
Cut a develop→main release, deploy both Workers, and verify the web version actually activated (not just uploaded).
AppSeed follows Gitflow: feature branches integrate into develop, and a release promotes develop to main. Production deploys off main. A full release is four steps — cut, deploy, verify, and check — and the "verify" step is the one that bites, so don't skip it.
1. Cut the release (develop → main)
Make sure develop is green and every change you intend to ship has merged. Then open and merge a release PR:
gh pr create --base main --head develop --title "Release vX.Y.Z — <summary>" --body "..."
gh pr merge <pr> --merge # a MERGE commit — never --squashMerge with --merge, not --squash. A release must keep main and develop sharing history. Squashing a release rewrites the lineage and causes every-file conflicts on the next release. (Feature PRs into develop are squash-merged as usual; only the develop → main release is a merge commit.)
Then tag the merge commit and push the tag — the tag is the version marker (the package.json version stays static):
git checkout main && git pull
git tag -a vX.Y.Z <merge-sha> -m "Release vX.Y.Z — <summary>"
git push origin vX.Y.Z2. Deploy the Workers
- Web Worker — the push to
maintriggers Workers Builds, which builds the OpenNext bundle and uploads a new version. - Realtime Worker — deploy it by hand (it is not in the Workers Builds pipeline):
npm run deploy:realtime # wrangler deploy --env production, in apps/realtime3. Verify the web version actually activated
A version upload is not a deployment. Workers Builds reliably uploads a new version, but the active deployment does not always advance to it on its own — so confirm the release is genuinely live. The fastest check is to hit a URL that only exists in this release (a new page or route). If it 404s, the new code is not serving yet.
# a release-only page should now resolve; an existing one confirms the site is up
curl -s -o /dev/null -w "%{http_code}\n" https://<your-domain>/docs/<new-page-in-this-release>If the active deployment did not advance (versions uploaded but the old one is still serving), activate the newest CI-built version manually:
# Authenticate wrangler with YOUR Cloudflare account. EITHER a scoped API token:
export CLOUDFLARE_API_TOKEN="<token with Workers Scripts:Edit on this account>"
# OR a Global API Key — then the token variable MUST be unset (it takes precedence):
# unset CLOUDFLARE_API_TOKEN
# export CLOUDFLARE_API_KEY="<your global api key>"
# export CLOUDFLARE_EMAIL="<your cloudflare account email>"
npx wrangler versions list --name <web-worker-name> # newest version is last
npx wrangler versions view <version-id> --name <web-worker-name> # confirm it lists env.ASSETS
npx wrangler versions deploy <version-id>@100 --name <web-worker-name> --yesTwo rules for this step:
- Activate the CI-built version — do not deploy the web Worker from a local
build:cf. In this monorepo, a local OpenNext build hits hoisted-node_modulescopy errors and can produce an unsound bundle. The version Workers Builds already uploaded is the trustworthy artifact; just point traffic at it. (mainand the release-merge ondevelophave identical web trees, so the version uploaded right after the push is the correct release bundle.) - Confirm assets, not just HTML. Fetch a
/_next/static/*.cssURL — it must return200 (text/css). A404means an assetless version went live; see the assets-binding gotcha in Secrets & env.
4. Post-release checks
- Homepage returns
200, and a release-only route returns200(step 3). - A
/_next/static/*.cssasset returns200 (text/css)— the site is styled. - The realtime Worker is healthy (its
/agents/*and/realtime/*routes respond).
Why the manual activation step exists
On the AppSeed reference deployment, Workers Builds was observed to upload versions on each main push without activating them — leaving prod on a stale build until a manual wrangler versions deploy. Until a given deployment's Workers Builds connection is confirmed to auto-activate at 100%, treat step 3's manual activation as a standard part of every release. See the CI with Workers Builds page for how the pipeline is wired.