Case index CI/CD pipeline with gates that block and a recorded rollback / 02 of 04

Claim

The release lane gates on five live smoke assertions: a build that misses one is rolled back to the previous release, and deploy.log records the order.

Eight PR gates feed one required ci-ok check; the local release lane builds, swaps, waits for health, proves five smoke assertions, and records a rollback.

(GitHub Actions, Docker Buildx, Trivy, cosign, Fastify, PostgreSQL)

01 / What it is

What it is

A Fastify invoice API on PostgreSQL 16 with a GitHub Actions workflow, a Dockerfile and a local release script.

The workflow defines eight gates and a ci-ok aggregator; the release script swaps a current pointer, waits on /health and /ready, runs five smoke assertions and rolls back to the previous release when an assertion misses.

Rollback timeline read from deploy.log: deploy.start, health.ok, smoke.fail, rollback.start, rollback.health.ok and rollback.smoke.ok, with elapsed seconds for the broken release.
Rollback timeline read from deploy.log, deploy.start through rollback.smoke.ok.
Terminal transcript of bash scripts/verify.sh: ESLint and tsc --noEmit pass, nine integration tests pass against PostgreSQL 16, and the final line is verify: ok.
Verification transcript — ESLint, tsc --noEmit and nine integration tests; verify: ok.
Trivy report for the locally built invoice-api:slim image at CRITICAL and HIGH severity with ignore-unfixed: zero findings and exit code 0, beside the trivy job settings read from .github/workflows/pr.yml.
Local Trivy report of invoice-api:slim at CRITICAL and HIGH with ignore-unfixed — zero findings, exit 0; flags read from pr.yml.

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 healthy /health and /ready responses as success; a process can answer both while its invoice route returns errors.

Cost Every release pays for five live 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

Pathological case

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 against the live service; any miss triggers rollback.start, restores the previous release, and re-runs the same smoke gate. deploy.log records every step in order.

05 / Measured

Measured

The local lane is measured: nine integration tests passed, the smoke gate passed five of five assertions with /ready p95 7.7 ms, and deploy.log records the rollback order. The workflow structure check found eight gates feeding ci-ok and zero unpinned actions.

The workflow is the source of the gate topology; the local lane is the source of the runtime evidence.

Measured results and the method behind each value.
Metric Value Method Artifact
Rollback ordering deploy.start → health.ok → smoke.fail → rollback.start → rollback.health.ok → rollback.smoke.ok read deploy.log in file order; elapsed seconds computed from the recorded timestamps deploy.log
Image scan 0 CRITICAL/HIGH findings across 68 targets 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 including ci-ok; 0 unpinned uses node structural check over .github/workflows/pr.yml docs/evidence/workflow-structure.txt
Docker daemon Docker 28.1.1, linux/x86_64; the image was built and scanned locally docker info docs/evidence/docker-info-latest.txt

Gate checks (test counts, not metrics)

  • Integration tests: 9/9 integration tests passed; verify: ok; exit 0. bash scripts/verify.sh (eslint, tsc --noEmit, vitest run) docs/evidence/verify-latest.txt
  • Smoke gate: 2 runs passed 5/5 smoke assertions (healthy release and rollback); /ready p95 23.1 ms. bash scripts/demo.sh against the local release on 127.0.0.1:8787 docs/evidence/demo.txt

Invariants and their tests

Back to the case index