Deploy & Release — How Code/Content Ships

2026-06-03 · Ben · architecture

Deploy & Release — How Code/Content Ships

Ground-truth map of the release pipeline (verified from the actual files, not docs). Mapped in LayerView under flow release-deploy.

Repos (single source of truth = GitHub)

RepoOwnerRole
vpn-astrospp-benSite + content. What the editor and CI build from.
vpn-editorspp-benEditor worker (editor.vpn.com) — dispatches deploys
vpn-workerstheofficialvpn5 edge workers serving www.vpn.com

(Older docs said theofficialvpn/vpn-astro — wrong; corrected.)

Two build sources — keep them in sync

Builds fromUsed by
CI (.github/workflows/deploy-i18n.yml)GitHub main (fresh checkout)editor buttons, translation jobs
Local scripts (deploy.sh / promote.sh)your local working treeoperator at a terminal

Both run the same gates via scripts/build.sh (api-contract, internal-link, content-quality, seo-audit, key-leak, translation-qa, stale-translations, a11y). Never inline a subset — that's a gate-bypass.

⚠️ The rule (now ENFORCED, not just warned): staging may only be deployed from pushed git state. deploy.sh auto-pushes main (or hard-fails) before deploying to staging — there is no yes | bypass anymore. This guarantees origin/main always equals what's on staging, so the editor's Sync to Production (which rebuilds origin/main via CI) can never ship a stale main.

Why this is enforced — incident 2026-06-17. Staging was deployed repeatedly from a local working tree via deploy.sh while the commits were never pushed (origin/main fell 8 behind). A Sync to Production from the editor then rebuilt the stale origin/main and shipped the old, superseded pricing design to www.vpn.com while staging showed the correct one. Root cause: staging was fed from a laptop, not git; the soft warning was bypassed. Fix: deploy.sh now pushes-or-fails (commit on main).

Residual (accepted): the guard is a script convention. A manual npx wrangler pages deploy dist --project-name=vpn-staging (bypassing deploy.sh) would skip it, and promote.sh still only warns. Plan #89 (CF Pages branch deploys + ff-merge, infra-level parity) is PARKED — the single main branch is sufficient for the sequential edit→staging→sync workflow, and the push guard closes the actual hole. Revisit only if concurrent in-flight changes ever need staging isolation.

Official process (the only rule you need)

staging.vpn.com is the source of truth. If it looks right, Sync. You never look at git.

Everything (editor content edits + operator code pushes) lands on main and is deployed to staging.vpn.com as part of the same action — so the staging site always equals what "Sync to Production" will ship. Sync rebuilds main → prod becomes an exact copy of staging. Keeping the staging site honest (a deploy on every main change) is the tooling's job, not the reviewer's.

Triggers → target

editor /api/deploy          ─┐
editor /api/deploy/i18n      ├─ repository_dispatch → CI → build.sh → staging.vpn.com (vpn-staging)
translation DO finalize      │
workflow_run (after Translate)┘
editor /api/deploy/sync-prod ── dispatch {target: production} → CI → www.vpn.com (vpn-astro)
operator: bash scripts/deploy.sh  ── local build → staging.vpn.com
operator: bash scripts/promote.sh ── reuse verified dist → www.vpn.com
git push ───────────────────────────────────────────────→ triggers NOTHING (no push trigger)

Runbook

TaskCommand / action
Deploy to staging (local)bash scripts/deploy.sh (push first)
Deploy to staging (editor)editor.vpn.com → Deploy
Promote staging → prod (local)bash scripts/promote.sh (reuses staging-verified dist)
Promote → prod (editor)editor.vpn.com → Sync to Production (rebuilds GitHub main via CI)
Roll back prodbash scripts/rollback.sh
Emergency prod (bypass staging)bash scripts/deploy.sh --emergency (rare)

Canonical prod path: the editor's Sync to Production (always builds the reviewed, pushed GitHub source via CI). Because deploy.sh now pushes-or-fails, origin/main is always current → this path is safe. promote.sh is the operator fallback — it still only warns on out-of-sync (not auto-push), so prefer the editor for prod.

Concepts hub (separate, isolated)

concepts/ deploys independently to concepts.vpn.com (CF Pages project vpn-concepts) via cd concepts && npm run deploy. Off the staging→prod pipeline entirely; not part of this flow.