This repository publishes a static documentation site. The root landing page is web/public/index.html; the interactive document graph is web/public/graph.html (served at /graph.html). Vite is rooted at web/; npm run build writes web/dist/ with shared chunks in web/dist/assets/, the admin SPA under web/dist/admin/ (see web/admin/README.md), and the docs browser under web/dist/docs/ (see web/docs/README.md). CI copies assets/, admin/, and docs/ into _bundle/public/ so the Worker serves /admin/ and /docs/ from the same static asset tree. OAuth/CORS hardening for that Worker is summarized in docs/admin-oauth-worker.md (path-specific CORS for /api/github/user, no separate “OAuth enabled” env flag).
Build Site runs npm ci and npm run build at the repository root, then packs public/ (static files, including those three trees from web/dist/) and worker/ (TypeScript Worker from the same checkout—PR head on PR builds) under _bundle/ in one artifact. Deploy Site checks out only the default branch (trusted cloudflare_site/wrangler.toml; never PR-controlled config on the secret-bearing runner), downloads the artifact to _bundle/, then copies only _bundle/public/ and _bundle/worker/ into cloudflare_site/ (so a malicious artifact cannot overwrite wrangler.toml or other repo files), then runs Wrangler. Deployment uses Cloudflare Workers with static assets (not the legacy Pages direct-upload / wrangler pages deploy flow).
Two GitHub Actions workflows:
- Build Site — on
pull_requestandpushtomain, checks out the PR head when relevant, installs Node dependencies, builds the site (admin and docs SPAs plus shared assets), assembles_bundle/public/and_bundle/worker/, uploads artifactsite(_bundle/contents). - Deploy Site — on successful Build Site via
workflow_run, checks out the repo default ref (trusted Wrangler project files), downloads artifactsiteinto_bundle/, copiespublic/andworker/intocloudflare_site/, then:- push to
main:wrangler deploy→ production Worker traffic. - pull_request:
wrangler versions upload --preview-alias pr-<pr-number>→ preview URL on*.workers.devwithout changing production (alias falls back topr-<workflow_run.id>only when the same fork branch matches more than one open PR).
- push to
GitHub Deployments use environments site-preview and site-production; PRs also get a single upserted comment with the preview link.
For architecture and naming, see 2026-04-09-site-cloudflare-pages-design.md (document filename still says “pages” for history; content describes Workers). Repository layout for web/ vs cloudflare_site/ is decided in ADR 0019.
- In the Cloudflare dashboard, use Workers & Pages → Create → Create Worker (or let the first
wrangler deploycreate it). The Worker name must match the GitHub variable below. - Configure preview URLs (default on when
workers_devis enabled). PR builds rely onwrangler versions uploadwith--preview-alias. - Optional: set a workers.dev subdomain for your account.
Create an API token that can deploy Workers for your account, for example:
- Account → Cloudflare Workers → Edit (or the “Edit Cloudflare Workers” template), and
- Account → Account Settings → Read if Wrangler requires it.
Store it as GitHub secret CLOUDFLARE_API_TOKEN. A token scoped only to “Cloudflare Pages — Edit” is not enough for wrangler deploy / versions upload on a Worker.
- Copy Account ID → secret
CLOUDFLARE_ACCOUNT_ID. - Set
CLOUDFLARE_PROJECT_NAMEas a GitHub Actions variable (same name as before for compatibility): value = Worker name in the dashboard. The deploy workflow passes it aswrangler deploy --name=…/versions upload --name=….
Attach routes or custom domains to the Worker (Workers → your Worker → Domains & Routes), not to a Pages project. Production URLs in GitHub Deployments will follow the hostname Wrangler reports (often *.workers.dev until a custom domain is primary).
If you previously used Cloudflare Pages with wrangler pages deploy, create the Worker as above, point DNS/custom hostnames to the Worker, then disable or delete the old Pages project to avoid confusion.
On a fork, open Settings → Secrets and variables → Actions. Add secrets CLOUDFLARE_API_TOKEN, CLOUDFLARE_ACCOUNT_ID, and variable CLOUDFLARE_PROJECT_NAME (Worker name).
Under Settings → Actions → General, allow Fork pull request workflows from contributors so fork PRs can run Build Site without Cloudflare credentials in the fork.
Deploy Site runs in the base repository with secrets; fork workflow logs should not show those values.
Configure the same secrets/variables at org or repo scope. Confirm pull-requests: write on the deploy workflow matches org policy for fork PR comments.
Disable GitHub Pages under Settings → Pages if it was only used for this site.
Full stack (recommended for admin OAuth): from the repository root, run npm run dev so Vite serves the SPA and Wrangler runs the site Worker with shared process env — see web/admin/README.md.
Static tree + Worker (closer to production asset layout): install dependencies, run the root build, copy the same layout CI uses under cloudflare_site/public/, then run Wrangler:
npm ci
npm run build
mkdir -p cloudflare_site/public/assets
mkdir -p cloudflare_site/public/admin
mkdir -p cloudflare_site/public/docs
cp web/public/index.html cloudflare_site/public/index.html
cp web/public/graph.html cloudflare_site/public/graph.html
cp -a web/dist/assets/. cloudflare_site/public/assets/
cp -a web/dist/admin/. cloudflare_site/public/admin/
cp -a web/dist/docs/. cloudflare_site/public/docs/
cd cloudflare_site && npx wrangler@4 devRequires a Cloudflare login or API token in the environment per Wrangler docs.
Deploy job skipped. The triggering workflow display name must be Build Site exactly, and workflow_run.repository must match the current repo.
Could not determine Workers deployment URL. The workflow reads deployment-url from cloudflare/wrangler-action, then falls back to parsing Wrangler stdout/stderr for a workers.dev URL. Upgrade wranglerVersion in the workflow if Wrangler output format changed.
Preview upload fails (PR builds). Requires Wrangler ≥ 4.21.0 for --preview-alias. The deploy workflow pins 4.36.0.
Artifact download 404. Build Site must upload artifact site; Deploy Site needs actions: read.
No PR comment. Same as before: ambiguous head when resolving the PR number; see the design spec.
Secrets missing on the deployed Worker / missing_turnstile_keys after deploy. cloudflare/wrangler-action runs wrangler secret bulk using the name in wrangler.toml, not the --name flag on wrangler deploy. The deploy workflow patches name to match CLOUDFLARE_PROJECT_NAME before Wrangler runs so secrets and deploy target the same Worker. If you deploy manually with only --name=…, run wrangler secret bulk (or wrangler secret put) with the same --name.
Dashboard: Worker Variables and Secrets lists plain vars and encrypted secrets. Secret values are never shown after save; only names appear.