The publishing pipeline

This page is part of the site it describes. The docs deploy to Cloudflare Pages, and this page documents the pipeline that ships them: edit Knap and JSON, rebuild with the tool, prove the committed output is current, merge, and Pages takes it from there. The tool documents its own deployment.

The loop

The whole loop: edit docs/src/*.knap or docs/data/*.json, rebuild the site, prove the committed output is current, ship it through a PR. On merge, the deploy workflow pushes docs/site/ to Cloudflare Pages:

$ sh docs/build.sh
rendered: index shell templates contract dogfood deploy
$ git diff --exit-code docs/site/ && echo clean
clean

The reproducibility gate

The gate is the docs-repro job in .github/workflows/ci.yml: it builds the dogbed binary, runs sh docs/build.sh, and fails with git diff --exit-code docs/site/ if anything differs. A red build means exactly one thing — you forgot to rebuild the site before committing. Watch it fail on a stale tree:

$ echo stale >> docs/site/index.html
$ git diff --exit-code docs/site/
diff --git a/docs/site/index.html b/docs/site/index.html
index 3de7d22..65b7de6 100644
--- a/docs/site/index.html
+++ b/docs/site/index.html
@@ -43,3 +43,4 @@
 <p><a href="index.html">index</a> · <a href="shell.html">shell</a> · <a href="templates.html">templates</a> · <a href="contract.html">contract</a> · <a href="dogfood.html">dogfood</a> · <a href="deploy.html">deploy</a></p>
 </body>
 </html>
+stale
$ echo "exit: $?"
exit: 1

The Cloudflare setup

The Cloudflare setup, as inspected: the project is dogbed — live at dogbed.filed.fyi and on the default dogbed.pages.dev domain. The output directory is docs/site/, committed to the repo — the deploy ships the exact bytes in the tree, no build step on the Pages side. The deploy path is wrangler direct upload from GitHub Actions: the repo carries the two secrets below, and no Cloudflare GitHub App checks appear on commits, so the Pages integration path is not in play.

$ gh secret list --repo drawmeanelephant/dogbed
CLOUDFLARE_ACCOUNT_ID	2026-09-30T00:10:25Z
CLOUDFLARE_API_TOKEN	2026-09-30T00:10:33Z

The deploy workflow is checkout and deploy, nothing else:

$ cat .github/workflows/deploy.yml
name: deploy
on:
  push:
    branches: [main]
jobs:
  pages-deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Deploy docs/site to Cloudflare Pages
        uses: cloudflare/wrangler-action@v3
        with:
          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
          command: pages deploy docs/site --project-name=dogbed

Rollback

Cloudflare keeps every deployment. To roll back:

  1. open the Cloudflare dashboard, Workers & Pages, project dogbed
  2. open Deployments — every push to main is listed with its commit
  3. pick the last good deployment and choose Rollback to this deployment
$ npx wrangler pages deployment rollback <deployment-id> --project-name=dogbed

The first deploy

The deploy workflow first fired on the merge of the very PR that added it — push to main, 28 seconds, deployed. No build step, no ceremony: checkout and wrangler. The workflow log, trimmed to the wrangler action (timestamps are the runner’s):

2026-09-30T00:28:04.4001917Z   command: pages deploy docs/site --project-name=dogbed
2026-09-30T00:28:04.4906387Z [command]/usr/local/bin/npx --no-install wrangler --version
2026-09-30T00:28:11.8067446Z [command]/usr/local/bin/npm i [email protected]
2026-09-30T00:28:19.0876014Z ✅ Wrangler installed
2026-09-30T00:28:19.0896103Z [command]/usr/local/bin/npx --no-install wrangler --version
2026-09-30T00:28:19.8311712Z 3.90.0
2026-09-30T00:28:19.8842932Z [command]/usr/local/bin/npx wrangler pages deploy docs/site --project-name=dogbed
2026-09-30T00:28:22.2939592Z Uploading... (0/7)
2026-09-30T00:28:22.7886426Z Uploading... (2/7)
2026-09-30T00:28:22.8702344Z Uploading... (5/7)
2026-09-30T00:28:22.9625881Z Uploading... (7/7)
2026-09-30T00:28:22.9628024Z ✨ Success! Uploaded 7 files (1.71 sec)
2026-09-30T00:28:24.2006374Z 🌎 Deploying...
2026-09-30T00:28:25.4739626Z ✨ Deployment complete! Take a peek over at https://a505925f.dogbed.pages.dev

And both domains answered the moment it landed:

$ curl -s -o /dev/null -w "%{http_code}" https://dogbed.pages.dev/
200
$ curl -s -o /dev/null -w "%{http_code}" https://dogbed.filed.fyi/
200

index · shell · templates · contract · dogfood · deploy
rendered by oliver