CI keeps checking when you stop remembering
You have been reviewing carefully for four courses. CI exists because that will not last, and it is not a criticism — attention is finite and yours will move on.
The starter has a seatbelt, not a lock
The starter repository ships scripts/precommit-key-guard.sh, a hook that refuses a commit staging something shaped like an API key. Its own comments are honest about the limits:
"This is a seatbelt, not a lock. It cannot see a key you never staged, and it cannot see one already in your history."
Two more limits it does not mention. A hook lives in .git/hooks, which is not committed, so it protects only the machine where somebody remembered to install it. And git commit --no-verify skips it entirely.
So the same check has to exist somewhere nobody can skip and nobody has to install. That is the whole argument for CI, in one file.
The four checks worth having
In order of what they save you from:
The key scan. The same pattern as the pre-commit hook, run on every push and pull request — but over the whole tree, not the diff. The hook already covers the diff at the moment the line is written; CI runs after the hook can be skipped with one flag, after a push from a machine that never installed it, and after a key somebody committed months ago. Scanning only the diff here would reproduce exactly the blind spot CI exists to cover. Check out the full history too, or a shallow clone scans the tip and misses the commit worth catching.
This is the one that prevents the unrecoverable mistake, so it goes first. The starter ships it in .github/workflows/key-scan.yml; read it rather than trusting this paragraph.
Type check and lint. tsc --noEmit and your linter. Cheap, and they catch the class of error that a generated diff introduces most often.
The build. If it does not build, nothing else matters. It also catches the import that only worked because of your local cache.
The tests. The suite you started in course 2, plus course 3's honesty checks and course 4's cross-user store test. These are the ones that encode judgement, and they are the reason the suite is worth having at all.
Fail the build, do not warn
A warning is a check that has already stopped working. Nobody reads a green build's warnings, and after the third one you stop reading the output at all.
If a rule is not worth failing on, remove it. A small suite that must pass beats a large one that mostly does.
The check you write yourself
Ask CI something specific to your project that no off-the-shelf tool knows. For this repository the natural one is: does any file outside the proxy reference the API key?
- name: Key stays in the proxy
run: |
! grep -rn "EODHD_API_KEY" src --include="*.ts*" \
| grep -v "src/app/api/proxy/"
Four lines, and it enforces the central architectural rule of the whole track — permanently, on every push, without anyone remembering it. That is what a good project-specific check looks like: an invariant you decided once, made mechanical.
The pre-commit hook still earns its place
CI catches it after you push; the hook catches it before it exists. Keep both, because the difference between "I fixed it before committing" and "I have to rewrite history and rotate a key" is the hook.
Layers, not alternatives — the same argument as cooldown plus hysteresis in course 4.
Try it now
Add the key scan and the project-specific check first, then push a branch that deliberately violates each one and confirm the build goes red. A check you have never seen fail is not evidence — the same rule as course 3's tests.