Continuous integration
.github/workflows/ci.yml runs one job per lane, so a failed check names what
broke. It runs on every pull request, on every push to main, on demand, and
from field-release-checks.yml for the exact commit of a release.
Required checks
The main ruleset (.github/rulesets/main.json) requires test,
client-build, docker-build, gitleaks (Secret Scan) and context
(PR context). The Replit publish (scripts/deploy-checks.js) builds only a
commit whose test, client-build, docker-build and gitleaks passed.
test is the gate. It passes only when every lane below passed. Because of
that gate, a new lane needs no ruleset change: add it to the needs list of
test. Do not rename test, client-build or docker-build. The ruleset and
the Replit publish check look for those names.
Lanes
| Check | What it proves | Run it locally |
|---|---|---|
unit-server |
Data-layer types, generated migrations match the schema, and the server, scripts and shared tests. Tests that need a real PostgreSQL skip here. | npm run db:typecheck && npm run db:check:ci && npm test |
unit-client |
Client typecheck, lint (client/eslint.config.js, React Hooks and React Refresh rules included) and tests |
npm --prefix client run typecheck && npm --prefix client run lint && npm --prefix client test |
postgres |
Every test file that opens a real PostgreSQL, against a postgres:16-alpine service |
FIELD_DB_TEST_DOCKER=1 npm test (runs every test, with a Docker PostgreSQL) |
quote-invoice-proof |
The quote and invoice proof (README.md, "Development") gives the same 25 PDFs, emails and API responses on two PGlite runs and on a run with PostgreSQL storage |
See below |
client-build |
The client bundle builds | npm --prefix client run build |
docker-build |
The image builds | docker build . |
smoke |
Playwright signs a synthetic operator in to the built image, on desktop and phone viewports, and opens the jobs board and the catalogue. /health must name the commit the image was built from. |
e2e/run-image-smoke.sh <image> [commit] |
audit |
No high or critical advisory outside the accepted list in SECURITY.md ("Dependency Advisories"), and none past its recheck date. It also writes CycloneDX SBOMs of the server and client production dependencies (artifact sbom). |
node scripts/dependency-audit.mjs |
test |
The gate: every lane above passed |
To run the quote and invoice proof the way CI does:
npm run qa:quote-invoice-proof -- . /tmp/proof-a
npm run qa:quote-invoice-proof -- . /tmp/proof-b
PROOF_STORAGE=postgres npm run qa:quote-invoice-proof -- . /tmp/proof-postgres
node scripts/quote-invoice-proof/compare.mjs /tmp/proof-a /tmp/proof-b
node scripts/quote-invoice-proof/compare.mjs /tmp/proof-a /tmp/proof-postgres
The PostgreSQL run starts a throwaway container when
FIELD_DB_TEST_DATABASE_URL is not set. The smoke needs Docker and
Playwright's Chromium:
npm --prefix e2e ci
npx --prefix e2e playwright install chromium
docker build -t field:local .
e2e/run-image-smoke.sh field:local
All lane data is synthetic: the verification catalogue
(scripts/verification/fixtures.js) and fixture operators with
example.invalid addresses.
Documentation-only pull requests
A pull request that changes only files under docs/ and Markdown files skips
postgres, quote-invoice-proof and smoke, and docker-build skips its
build steps but still reports success, because the ruleset requires it by
name. Nothing in the
server, the client bundle or the image reads those files. The unit lanes, the
client build and the audit still run. A push to main, a manual run and a
release check always run every lane, because the Replit publish needs a
passing test on each commit it deploys.
Heavy lanes on Crabbox
The heavy lanes (postgres, quote-invoice-proof, smoke) need Docker and a
few minutes of CPU. They run on GitHub-hosted runners, which cost nothing for a
public repository. To run them away from a workstation, for example to prove a
large change before you push, use Crabbox. Field has no crabbox.yaml yet, so
each run names its command:
crabbox run --preflight --preflight-tools node,docker --shell 'npm ci && FIELD_DB_TEST_DOCKER=1 npm test'
crabbox run --preflight --shell 'npm ci && npm run db:build && PROOF_STORAGE=postgres npm run qa:quote-invoice-proof -- . /tmp/proof-postgres'
crabbox run --fresh-pr arcforgelabs/field#<number> --shell 'docker build -t field:local . && npm --prefix e2e ci && npx --prefix e2e playwright install --with-deps chromium && e2e/run-image-smoke.sh field:local'
crabbox actions hydrate can also prepare a box from ci.yml. Add a
crabbox.yaml with these lanes as jobs when they are run this way often.