Deployment
Deploy zudo-doc sites to Cloudflare Workers with GitHub Actions CI/CD.
Overview
zudo-doc is a fully static site generator — the output of pnpm build is a directory of HTML, CSS, and JavaScript files that can be deployed anywhere. The default deployment target is Cloudflare Workers (static assets), using GitHub Actions for CI/CD automation.
The CI pipeline is split into parallel jobs to keep total build time low. Site generation and document history extraction run as separate jobs and are merged before deployment.
Cloudflare Workers Static Assets
The built site is deployed using wrangler deploy. The deploy artifact includes:
The zfb build output (
dist/)Pre-generated doc history JSON files (if
docHistoryis enabled)dist/— the explicit main entry produced by the CF adapter_ worker. js
The site is served at root (base = "/") via the Workers static asset layer. Dynamic routes (e.g. /) are handled by dist/; all other GET requests are served as static assets from dist/.
Info
The site URL is configured via [[routes]] in wrangler.toml (custom domain binding). Bindings such as ANTHROPIC_API_KEY, DOCS_SITE_URL, and RATE_LIMIT KV are also declared in wrangler.toml.
CI Pipeline Structure
The project uses three GitHub Actions workflows: production deploys (main-deploy.yml), PR checks and previews (pr-checks.yml), and branch previews (preview-deploy.yml).
Production (main-deploy.yml)
Triggered on push to main. Runs five jobs:
build-site — full clone (
fetch-depth: 0), builds the zfb site withpnpm build(noSKIP_DOC_HISTORY— the preBuild step needs full git history to populate the SSG meta block)html-validate — validates the built
dist/for HTML element errorsbuild-history — full clone (
fetch-depth: 0), runs@takazudo/zudo-doc-history-server generateto pre-extract git history for all content filesdeploy — merges the site and history artifacts, then deploys via
wrangler deploy(production)notify — sends an IFTTT webhook notification with the deploy status
Concurrency: group: production-deploy, cancel-in-progress: false — earlier deploys are allowed to finish rather than being cancelled.
PR Checks (pr-checks.yml)
Triggered on pull requests targeting main. Runs fifteen jobs:
check-template-drift — verifies no drift between source files and generator templates
check-pin-parity — verifies
@takazudo/zfbpin is consistent across all package sourcescheck-fixture-settings-drift — verifies every key in
src/is present in all e2e fixture settings files (or allowlisted)config/ settings. ts check-package-safelist — verifies the generated
dist/covers all responsive/arbitrary utility classes in thesafelist. css @takazudo/zudo-docpackagecheck-b4push-ci-parity — verifies every lightweight guard gate in
scripts/has a matching CI jobrun- b4push. sh check-e2e-spec-naming — verifies all e2e spec filenames use known fixture prefixes so Playwright picks them up
lint-gates — runs
pnpm tags:audit --ciandpnpm lint:tokens(design token lint)typecheck — runs
pnpm check(TypeScript type checking)package-tests — runs all package-scoped vitest suites
root-tests — runs the root unit test suite
build-site — full clone (
fetch-depth: 0),pnpm build, thencheck:links(noSKIP_DOC_HISTORY— same rationale as production: preBuild needs full history for SSG meta dates)html-validate — validates the built
dist/for HTML element errorsbuild-history — full clone, doc history generation
e2e — runs Playwright end-to-end tests
preview — merges artifacts and uploads a preview via
wrangler versions upload --preview-alias, then posts the URL as a PR comment
Concurrency: group: pr-checks-{pr-number}, cancel-in-progress: true — outdated runs are cancelled to save CI minutes.
Preview Deployments
Each PR gets a stable preview-alias URL via wrangler versions upload --preview-alias <alias> (enabled by preview_urls = true in wrangler.toml). The alias is pr-<number> for PR checks and the branch slug for branch previews. The URL is on *.workers.dev (not the production custom domain) and its subdomain starts with the alias:
https: / / <alias>- zudo- doc. <your- subdomain>. workers. devCI parses the exact host from wrangler's output and posts it automatically as a comment on the PR by the preview job.
SKIP_DOC_HISTORY
When SKIP_DOC_HISTORY=1 is set, the doc-history zfb plugin skips all git history calls and writes an empty manifest, causing the visible Created/Updated/Author block to be absent from every SSG page. In standard CI this flag is not set on the build-site job — the preBuild step needs full git access to populate real dates.
| Context | Value | Why |
|---|---|---|
CI build-site job | unset | preBuild needs full git history to populate the SSG meta block (Created/Updated/Author) |
CI e2e job | unset | E2E tests verify history features inline |
Local pnpm build | unset | The Created/Updated/Author meta block is embedded (preBuild). The per-page history dropdown JSON is not generated locally by default — see GEN_DOC_HISTORY below |
Tip
SKIP_DOC_HISTORY=1 is an escape hatch for shallow-clone or custom CI variants where git history is unavailable. Setting it causes the Created/Updated/Author block to be absent from every page.
GEN_DOC_HISTORY
GEN_DOC_HISTORY=1 (introduced in #1986) gates the doc-history plugin's postBuild hook — the step that writes the per-page revision/diff JSON into dist/doc-history/ feeding the DocHistory dropdown island. The plugin's preBuild hook (the visible Created/Updated/Author meta block) is unaffected and is governed by SKIP_DOC_HISTORY alone. postBuild now follows this decision table:
| Context | postBuild JSON | Why |
|---|---|---|
SKIP_DOC_HISTORY=1 | skipped | overrides GEN_DOC_HISTORY and CI — wins over everything |
GEN_DOC_HISTORY=1 | generated | explicit local opt-in |
CI / GITHUB_ACTIONS set | generated | keeps the build-site artifact byte-identical to before |
plain local pnpm build | skipped | the #1986 default — see Tip below |
Tip
postBuild is off by default locally because its per-content-file git log --follow chain exceeds zfb's 120s postBuild lifecycle budget on a large corpus and fails a plain pnpm build. It is redundant locally anyway: dev reads history live from the standalone :4322 server, and CI generates the JSON in the dedicated parallel build-history job. To make the dropdown work when previewing a locally-built dist/ (or building the Tauri offline reader), run GEN_DOC_HISTORY=1 pnpm build. Note this gates only the zfb plugin's postBuild — the standalone @takazudo/zudo-doc-history-server generate CLI used by the build-history job is never gated and always generates when invoked.
Inter-Job Data Sharing
Build artifacts are shared between jobs using actions/cache rather than actions/upload-artifact. Cache keys are scoped to github.run_id to prevent cross-run contamination:
key: dist-${{ github.run_id }}
key: doc-history-${{ github.run_id }}The deploy (or preview) job restores both caches and merges the dist/ directories before calling wrangler deploy (production) or wrangler versions upload (preview).
Note
Using actions/cache for job-to-job data transfer (rather than artifacts) avoids the overhead of uploading and downloading large ZIP archives. Cache entries are cheaper for temporary intra-run data.
Required Secrets
Configure these in your repository's Settings → Secrets and variables → Actions:
| Secret | Description |
|---|---|
CLOUDFLARE_API_TOKEN | Cloudflare API token with Workers deployment permissions |
CLOUDFLARE_ACCOUNT_ID | Your Cloudflare account ID |
IFTTT_PROD_NOTIFY | IFTTT webhook URL for deploy notifications (optional) |
Warning
The CLOUDFLARE_API_TOKEN needs the Workers: Edit permission (and KV Storage: Edit if you use KV namespaces). Scoping it to the specific Worker is recommended over granting account-wide access.