Claim
The release lane gates on five live smoke assertions: a build that misses one is rolled back to the previous release, and the hosted demo page records the order.
Eight PR gates feed one required ci-ok check; the demo page starts the gate run, ships a bad image, and redeploys the previous release.
Live
Click Run the gates and watch each job turn green.
pipeline-demo-57mh.onrender.com invoice-api-26b4.onrender.com View the code
What to expect on the free instance
- The free instance sleeps after 15 minutes idle and takes about a minute to wake.
- The demo page starts one action at a time; the buttons stay disabled while a run is in flight.
- A full gate run takes several minutes and spends the repository's Actions minutes.
01 / What it is
What it is
A Fastify invoice API on PostgreSQL 16 with a GitHub Actions workflow, a Dockerfile and a release script. The workflow defines eight gates and a ci-ok aggregator; the hosted demo page dispatches the gate run, the bad-image run and the rollback from a server-side token.
The release lane swaps a current pointer, waits on /health and /ready, runs five smoke assertions and redeploys the previous release when an assertion misses.
02 / The hard part
The hard part
A process can answer /health and /ready while its invoice route returns errors, and a release with no return path leaves that process in front of traffic. The hard part is deciding the deploy on what the service does, not on what its health endpoint reports.
03 / The decision
The decision
Smoke decides the release; health starts the wait
Decision After the health wait, run five smoke assertions and roll back when any assertion misses, then re-smoke the restored release.
Rejected alternative Treat /health and /ready as success; a process can answer both while its invoice route returns errors.
Cost Every release pays for five assertions and a rollback restart, and rollback returns to the previous release.
Evidence deploy.log records deploy.start, health.ok, smoke.fail, rollback.start, rollback.health.ok, rollback.smoke.ok.
04 / The failure it refuses
The failure it refuses
The process answers /health and /ready while the invoice route returns errors, or a release is promoted with no way back to the previous build.
Mechanism The release lane swaps the current pointer, waits on /health and /ready, then runs five smoke assertions; any miss triggers rollback.start, restores the previous release, and re-runs the same smoke gate. deploy.log records every step.
05 / Measured
Measured
Local run: nine integration tests passed, the smoke gate passed 5/5 assertions with /ready p95 7.7 ms, and demo.txt records the rollback order. The workflow structure check found 20 pinned actions and zero unpinned.
Green demo run 40929946794; red run 40930412365 stopped at bad-image-trivy with 10 HIGH. A rollback moved Render from dep-dbhonnk9v7es73bg70f0 to dep-dbhoo3rbc2fs73f1b85g.
| Metric | Value | Method | Artifact |
|---|---|---|---|
| Release, broken release and rollback | healthy 5/5 · broken smoke FAIL · rollback 5/5 | bash scripts/demo.sh against the local release on 127.0.0.1:8787; summary in docs/evidence/demo.txt | docs/evidence/demo.txt |
| Image scan | 0 CRITICAL/HIGH findings with ignore-unfixed · exit 0 | trivy image --severity CRITICAL,HIGH --ignore-unfixed --exit-code 1 invoice-api:slim | docs/evidence/trivy-local.txt |
| Workflow structure | 9 jobs incl. ci-ok · 20 pinned uses · 0 unpinned | node structural check over .github/workflows/pr.yml | docs/evidence/workflow-structure.txt |
Gate checks (test counts, not metrics)
- Smoke gate: 5/5 assertions · /ready p95 7.7 ms. bash scripts/smoke.sh against the local release; five named behaviours
docs/evidence/smoke.txt - Verification gate: 9/9 integration tests · verify: ok · exit 0. bash scripts/verify.sh (eslint, tsc --noEmit, vitest run)
docs/evidence/verify-latest.txt
Invariants and their tests
- Unauthenticated POST /invoices is rejected with 401; an authenticated request returns 201 with a UUID id
tests/integration/api.test.ts - GET /ready reports 503 pending_migrations when a migration is unapplied, and 200 when database and migrations are okay
tests/integration/api.test.ts - An unknown invoice id returns 404 not_found with no stack trace in the body
tests/integration/api.test.ts