Skip to content

CI: the checks on every push

2 min read · main part only; folded sections are optional

After this you can answer: What must pass before code can ship, and why in that order?

flowchart TB
P["push or pull request"] --> Q["quality<br/>types, lint, tests, audit"]
Q --> E["e2e<br/>browser tests, real app"]
Q --> D["docker<br/>build + scan 3 images"]
E --> G{"all green<br/>on main?"}
D --> G
G -->|"yes"| CD["CD starts"]
  1. quality (10 min limit): install with a frozen lockfile, then pnpm typecheck, pnpm lint, pnpm test, pnpm audit (fails on critical advisories) and pnpm docs:check. No database needed, so it is fast and runs first.
  2. e2e (after quality): Postgres 17 and Redis 7 run as service containers inside the job. It migrates, seeds, starts the api and web, and runs Playwright browser tests.
  3. docker (after quality, next to e2e): builds the api, web and worker images and scans each with Trivy. A fixable critical vulnerability fails the job. Nothing is pushed.
  4. One run per branch. A new push cancels the older run of the same branch.
  5. CD waits for it. CD starts on workflow_run when CI completes on main, and its jobs check that CI succeeded.

The workflow is .github/workflows/ci.yml. Its GitHub token may only read the repository (contents: read): no CI job can write anything or reach AWS. The database values in the e2e job are for a throwaway database, not real secrets.

The order is about fast feedback and cost: a type error fails the cheap job first, before anyone waits for browsers or image builds.

From → toWhat happensWhat allows it
CI workflow → CD publish jobCD starts after CI finished on main
  • trigger workflow_run: workflows [CI], types [completed], branches [main]; the job checks CI's conclusionpublish
Play the deploy flow on the map: it starts with CI →

Not in AWS: GitHub → the repository → Actions → CI.

SymptomCauseWhere to look
A merge to main never reaches AWSCI failed; CD’s jobs check CI’s result and do nothingActions → CI run
CI fails at pnpm audit with no code changeA new critical advisory for a dependencyThe audit output
docker fails at “Scan web image”A fixable critical CVE in the imageTrivy’s table in the log
Why does CD use workflow_run instead of push?

A push trigger would start CD at the same time as CI, before anyone knows whether the code works. completed fires on failure too, so CD checks the conclusion.

Why not push the images CI builds?

CI runs for pull requests and forks too, with a read-only token. Only CD, after a green CI on main, builds the ARM64 images that ship. CI builds x86 only to prove the Dockerfiles work.

What is the trade-off of failing on critical findings only?

The build stays green for high and medium findings, so they can pile up unseen. Failing on every finding would block work on issues with no fix yet.

Advanced: Details: caches

pnpm’s store is cached by actions/setup-node. Image layers use the GitHub Actions cache with one scope per image. Playwright’s browsers are cached by the lockfile hash.

Answer these without scrolling up. If one feels hard, reread that section.

  1. Which checks run in the quality job?
  2. Why does quality run before e2e and docker?
  3. What does the docker job do with the images after the scan?
  4. What can the CI token do?
  5. Which trigger starts CD, and what does CD check first?

Redraw it from memory: push → quality → (e2e, docker) → green on main → CD

Try this: on the map, select the CI workflow and read what allows its arrow to the CD publish job

Read this book online: https://book.theversehub.in/