CI and Deployment
The continuous-integration and deployment workflows that verify and publish repository surfaces.
CI jobs and artifacts
. runs on pull requests and pushes to main. Its current job names are validate, packed-host-matrix, packed-host-install, demo-sites-build, demo-editors-build, demo-editors-batch, doc-site-build, and four styleguide artifact jobs.
| Job | Shape | Main responsibility and artifact |
|---|---|---|
validate | One Ubuntu job, 45-minute budget | Frozen install, repository gates, contract gates, unit/build checks, host/dev/SiteProject/browser lanes, static-site/editor artifact verification, and demo browser proof. On failure it may upload playwright-results. |
packed-host-matrix | One Ubuntu discovery job | Runs packed-host-matrix --github-output and exposes the disk-discovered host matrix. It does not run the host proof itself. |
packed-host-install | Fail-fast-disabled matrix from the previous job | Gives every discovered host its own isolated packed-install job and runs smoke:host-install -- --host <name>. |
demo-sites-build | Fail-fast-disabled sample, shop, landing, blog matrix | Builds and verifies each static dist-site, then uploads demo-site-<site>-<sha>. |
demo-editors-build | Fail-fast-disabled sample, shop, landing, blog matrix | Builds each editor host in its own job/process with demo:build-editor <name>, verifies its dist-editor, uploads demo-editor-<name>-<sha>, then runs that host's test:browser:demo-editor lane. |
demo-editors-batch | One Ubuntu job, no matrix, no needs | Runs pnpm demo:build-editors — the same serial, single-parent-process batch a developer runs locally to build all four editors at once — timing the step, then verifies all four editor targets and greps the JS, CSS and HTML under packages/ for leaked / paths or columnNumber dev sourcemap markers. demo-editors-build's one-process-per-host matrix cannot exercise a leak that only appears from the second host onward in one parent process (Epic #827); this job exists to make that bug class visible to CI. |
doc-site-build | One Ubuntu documentation artifact job | Runs pnpm doc:build-site from the default shallow checkout, verifies doc/dist with the doc-site contract, and uploads doc-site-<sha> from doc/dist. |
sample-sg-site-build | One standalone styleguide artifact job | Frozen installs, runs Sample checks and computed styles, verifies the exact two pins, then uploads sample-sg-site-<sha>. The validate job retains the twelve-component pack proof. |
shop-sg-site-build, landing-sg-site-build, blog-sg-site-build | One isolated Ubuntu job per standalone demo styleguide | Frozen installs root and host, checks the host dependency contract and computed styles, verifies the target manifest, and uploads its <name>-sg-site-<sha> artifact. |
The validation job's browser steps consume prepared artifacts and never rebuild them. demo:build-sites runs before the SiteProject and demos lanes, while demo:build-editors and the test:browser:demo-editor lane cover the four per-host editor artifacts. The separate doc-site-build job builds and verifies doc/dist before it uploads the documentation artifact; the production matrix consumes that exact SHA-named artifact. The validation-gates and browser-lanes pages explain the local equivalents and port ownership.
Trusted-run deployment
. is an independent production workflow. Its automatic trigger is only a successful CI workflow run for a push to main in this repository. A manual dispatch provides target (all or one registry key), the CI run_id and full sha; the same guard still verifies the run through the GitHub API and checks that origin/main has not moved.
The production matrix contains thirteen current hosted targets. Dispatching with target: all (the default) selects every target once; selecting a registry key runs only that target.
| Target key | Worker | Wrangler config | Domain | Artifact directory | Manifest | CI artifact |
|---|---|---|---|---|---|---|
doc | zudo-composer | wrangler.doc.jsonc | zudo-composer.zudolab.dev | doc/dist | doc-site-manifest.json | doc-site-<sha> |
sample-sg | zc-sg-sample | wrangler.sample-sg.jsonc | zc-sg-sample.zudolab.dev | styleguide/ | doc-site-manifest.json | sample-sg-site-<sha> |
shop-sg | zc-sg-shop | wrangler.shop-sg.jsonc | zc-sg-shop.zudolab.dev | styleguide/ | doc-site-manifest.json | shop-sg-site-<sha> |
landing-sg | zc-sg-landing | wrangler.landing-sg.jsonc | zc-sg-landing.zudolab.dev | styleguide/ | doc-site-manifest.json | landing-sg-site-<sha> |
blog-sg | zc-sg-blog | wrangler.blog-sg.jsonc | zc-sg-blog.zudolab.dev | styleguide/ | doc-site-manifest.json | blog-sg-site-<sha> |
sample | zc-demo-sample | wrangler.demo-sample.jsonc | zc-demo-sample.zudolab.dev | packages/ | site-manifest.json | demo-site-sample-<sha> |
shop | zc-demo-shop | wrangler.demo-shop.jsonc | zc-demo-shop.zudolab.dev | packages/ | site-manifest.json | demo-site-shop-<sha> |
landing | zc-demo-landing | wrangler.demo-landing.jsonc | zc-demo-landing.zudolab.dev | packages/ | site-manifest.json | demo-site-landing-<sha> |
blog | zc-demo-blog | wrangler.demo-blog.jsonc | zc-demo-blog.zudolab.dev | packages/ | site-manifest.json | demo-site-blog-<sha> |
sample-editor | zc-demo-sample-editor | wrangler.demo-sample-editor.jsonc | zc-demo-sample-editor.zudolab.dev | packages/ | demo-editor-manifest.json | demo-editor-sample-<sha> |
shop-editor | zc-demo-shop-editor | wrangler.demo-shop-editor.jsonc | zc-demo-shop-editor.zudolab.dev | packages/ | demo-editor-manifest.json | demo-editor-shop-<sha> |
landing-editor | zc-demo-landing-editor | wrangler.demo-landing-editor.jsonc | zc-demo-landing-editor.zudolab.dev | packages/ | demo-editor-manifest.json | demo-editor-landing-<sha> |
blog-editor | zc-demo-blog-editor | wrangler.demo-blog-editor.jsonc | zc-demo-blog-editor.zudolab.dev | packages/ | demo-editor-manifest.json | demo-editor-blog-<sha> |
The doc and all four *-sg targets are multi-page static sites. Keep each target key, Worker, config, domain, artifact directory, manifest, and CI artifact name aligned with the registry. Both verifyArtifact hooks directly use verifyDocSiteArtifact, and liveRoutes reads each manifest's routes. The routeFile and assetUrl hooks select each page's own HTML and canonical asset URL for extensionless and trailing-slash pages; see the live delivery contract. Static demo builds and disposable editor builds retain their own manifest contracts and artifact directories; the editor build is per-host and uses the demo-editor kind rather than a shared sample. Local doc builds may omit sourceRevision, but production preflight requires the full expected SHA and the same recorded artifact revision before Wrangler.
Sample's doc-site artifact keeps the root pnpm sg:build-site command. Shop, Landing and Blog use pnpm sg:build-styleguide <shop-sg|landing-sg|blog-sg>; pnpm sg:build-styleguides builds all four catalogs serially. Each standalone host has its own frozen lockfile and target-specific artifact directory. Their new Workers use the existing missing-Worker path to bind their configured domains after main CI.
Rollout sequence
Each matrix leg follows the same guarded sequence. The deployment script's preflight captures remote state before it runs the non-mutating Wrangler dry run, so the rollback precondition is known before any upload:
Guard. Load the guard from a fresh trusted
maincheckout. Require a successful same-repositorymainCI push (or validate the equivalent manual inputs), the exact full SHA, and a currentorigin/main.Download. Download only the target's
<prefix>-<sha>artifact from the selected CI run.Verify. Verify every artifact byte, manifest, source identity, route list, and target-specific contract before Cloudflare access.
Preflight. Require deployment credentials, run
wrangler whoami, and resolve the target config before any mutation.Capture. Inspect the target's current state. Require the current deployment to be one 100% version and save that exact active version as the rollback target. Split traffic, missing rollback state, or partial Cloudflare state fails closed.
Dry run. Run
wrangler deploy --dry-run --no-bundlewith the verified directory and target config.Upload. Use
wrangler versions uploadfor the verified directory and retain the version ID returned by that upload.Activate. Activate only that returned version ID at 100%; never infer an ID from an older versions list entry.
Live check. Fetch the manifest, every emitted asset, and every target route over bounded HTTPS requests, checking bytes, checksums, MIME types, and the active version.
Rollback. If live verification fails, roll back only while the exact uploaded version remains active. A successful rollback still leaves the job failed so the incident remains visible.
There is one first-Worker exception. When both deployments list and versions
list report that a target Worker does not exist, a plain wrangler deploy creates the Worker, uploads its verified artifact, and binds its custom domain. It has no prior rollback target. Any other missing-state combination fails before mutation; a first-deploy live failure is reported red because there is no prior Worker to restore.
Secrets and environment boundaries
Pull-request validation has no Cloudflare secrets. The production step receives only the existing deployment credentials after the trusted-run guard and artifact checks: a Cloudflare API token (CLOUDFLARE_API_TOKEN or CLOUDFLARE_TOKEN) and CLOUDFLARE_ACCOUNT_ID. Missing or partial credentials fail visibly. Local Wrangler OAuth sessions may be useful for read-only checks, but must never be copied into repository or workflow secrets.
For the target contracts, live-check details, and manual recovery rules, use the hosted-demo operator guide.