CI/CD Setup
The sync workflow, the offline PR check, and the deploy doorbell that turns backend changes into reviewable frontend PRs.
Two workflows on the frontend, one step on the backend. Both workflow files ship in the package — copy them from node_modules/chowbea-axios/templates/ into .github/workflows/, or let init --pinned place them for you.
Frontend: the PR check (chowbea-pinned-ci.yml)
Runs on every PR: npm ci → chowbea-axios generate --quiet → your typecheck. Fully offline — no endpoint, no secrets, no staleness fetch. If the committed pins and your app code disagree, the PR goes red; nothing else can move the ground under it.
Replace the template's npx tsc --noEmit step with your project's real typecheck/build command.
Frontend: the sync workflow (chowbea-sync.yml)
Listens on three triggers:
repository_dispatch(eventchowbea-sync) — the backend's doorbellworkflow_dispatch— a manual button- a daily cron — safety net for a missed dispatch
It runs sync and opens (or updates) a PR on the fixed branch chowbea/sync via peter-evans/create-pull-request. Unchanged contract → clean tree → no PR. The PR's file diff is the contract diff, and its own CI run typechecks your entire app against the new types before merge.
The one token you must set: PRs created with the default GITHUB_TOKEN cannot trigger other workflows (GitHub's anti-recursion rule) — the sync PR would show no checks. Set the token: input on the create-pull-request step to a fine-grained PAT or GitHub App token with contents + pull-requests write (the template calls it CHOWBEA_PR_TOKEN). The fallback — closing and reopening the PR by hand — works because the pinned check listens on reopened, but you don't want that as your routine.
If your endpoints need auth, uncomment the template's env block and add the [fetch.auth] env vars as repository secrets. Both templates check out with persist-credentials: false — the workflow token never sits in .git/config while npm ci runs postinstall scripts (create-pull-request pushes with its own token, so nothing breaks).
Backend: the doorbell
Append to the deploy workflow, after deploy succeeds — never on bare push, when the endpoint still serves the old contract:
- name: Notify client repos of new API contract
env:
GH_TOKEN: ${{ secrets.CHOWBEA_SYNC_TOKEN }} # fine-grained PAT, contents: write on the client repos
run: |
for repo in ${{ vars.CHOWBEA_CLIENT_REPOS }}; do # e.g. "org/web org/mobile"
gh api "repos/${repo}/dispatches" -f event_type=chowbea-sync
doneMultiple frontends? Space-separate them in the CHOWBEA_CLIENT_REPOS variable — one loop, one doorbell each.
The backend keeps its existing Type Bus gates (extract --check, --fail-on-removed); pinned inputs changes nothing on the producer side.
Why the race goes away
Frontend PR merges first? main stays pinned to the old contract — still green. Backend merges and deploys later? Doorbell → sync PR → review → merge. A frontend PR that uses not-yet-deployed backend types can't go green until the sync PR lands — which is the system correctly refusing to merge code against an API that doesn't exist yet. Ship order: backend merges and deploys, sync PR lands, frontend rebases.
Secrets & tokens at a glance
| Where | Name | Scope | Purpose |
|---|---|---|---|
| Frontend repo secret | CHOWBEA_PR_TOKEN | contents + pull-requests write (this repo) | Sync PRs get CI checks |
| Backend repo secret | CHOWBEA_SYNC_TOKEN | contents: write (each client repo) | Fire repository_dispatch |
| Backend repo variable | CHOWBEA_CLIENT_REPOS | — | Which frontends to ring |
| Frontend repo secrets | your [fetch.auth] env vars | — | Only if the endpoints are auth-gated |
One fine-grained PAT scoped to all client repos can serve both token roles if everything lives in one org.