Appearance
Runbook: Local Delivery Fallback
Purpose: Deliver without GitHub Actions — run the CI gate, cut the release, and publish the docs site from a workstation, using the same entry points the workflows use When to use: Actions is unavailable (billing cap hit, spending limit, outage — e.g. #1648) and delivery must not stall Prerequisites: A clean checkout of the repo on its main branch;
node>= 22,npm,jq,make, andghauthenticated (gh auth status); Cloudflare credentials for the docs deploy only Estimated time: 10–20 minutes for all three
The three Actions workflows and their local equivalents. Each local path runs the same underlying entry point as the workflow — there is no parallel logic to drift:
| Workflow | What it does | Local equivalent |
|---|---|---|
ci.yml | checks + tests + doc-drift gate | make ci (step 1) |
auto-release.yml | version bump, CHANGELOG, tag, GitHub Release | bob-release (step 2) |
docs-deploy.yml | build + verify + publish the docs site | npm run docs:deploy (step 3) |
Run them in this order: gate first, then release, then docs.
Steps
1. Run the CI gate locally
make ci is exactly what Actions runs (checks + full test suite + docs gates):
bash
BOB_ALLOW_NONPROJECT=1 make ciBOB_ALLOW_NONPROJECT=1is required:test-prunebuilds fixtures in temp dirs that the #707 project guard otherwise refuses (CI sets the same flag).- The full suite takes several minutes;
test-state-machinealone runs 2–3 min. - Do not trust a piped exit code (
make ci | tailreports the pipe's status, not make's) — run it bare, or capture to a file and check$?directly.
Verify: the run ends with All tests passed. and exit code 0. A red suite blocks the release cut — fix first, exactly as a red CI check would block a merge.
Known flaky tests (pass on isolated re-run — re-run before treating as real): test-state-machine.js (execSync timeout under load), test-checks.sh check-fleet grep (#1621), test-dev-up-docs (port race under suite load).
2. Cut the release
bash
bob-release # or: make release-localThis is auto-release.yml verbatim: npm run release (commit-and-tag-version) bumps from Conventional Commits, rewrites CHANGELOG.md, commits chore(release): X.Y.Z, tags vX.Y.Z, pushes commit + tag atomically (3-attempt rebase retry), and publishes a GitHub Release from the new changelog section.
- Preview first with
bob-release --dry-run; force a bump type with--release-as patch|minor|major(parity with the workflow's manual dispatch). - It refuses on: tracked working-tree changes (stash first — untracked files only warn), a branch other than main, or
HEAD != origin/<main>. - It is idempotent: re-running when up to date is a no-op, and re-running after a partial failure (bump landed, push or Release didn't) resumes the unfinished half instead of double-bumping.
Verify:
bash
git describe --tags # the new vX.Y.Z, on HEAD
gh release view "$(git describe --tags --abbrev=0)" --json tagName,urlBoth must show the new version. If the Release is missing, re-run bob-release (the resume path publishes it).
3. Publish the docs site
bash
export CLOUDFLARE_API_TOKEN=... # token with Pages:Edit
export CLOUDFLARE_ACCOUNT_ID=... # account UUID
npm run docs:deploy # primary engine (vitepress) -> bigbrain-docsSame engine the workflow drives: builds via scripts/docs-site/build.sh, then wrangler pages deploy, then fetches the published site back (#1446) — a green upload alone is not evidence of publication. A comparison engine deploys with npm run docs:deploy -- --engine starlight (lands at <project>-starlight). An interactive wrangler login session works in place of the env vars.
Verify: the deploy script's own post-deploy fetch must pass (it fails the run if the site doesn't answer). Spot-check a page that this delivery changed — "done" for docs means live and verified, not uploaded.
4. Close the loop
- If Actions is down for billing (#1648-class), leave the tracking todo open — this runbook is the bridge, not the fix.
- Nothing else to reconcile: the release commit is loop-guarded (
chore(release)prefix), so when Actions comes back the next push resumes auto-release cleanly on top of whatever was cut locally.
See also
- Versioning how-to — how auto-release works and how it's provisioned
.github/workflows/auto-release.yml,ci.yml,docs-deploy.yml— the workflows this substitutes for- Incident response runbook — when something is broken rather than unavailable
- Self-hosted Actions runner — the structural fix for minute caps: CI on fleet hardware bills zero minutes, so this fallback is needed less often