Make the CI gate execute the checks it reports on (closes #119)
All checks were successful
check / check (push) Successful in 3m0s
All checks were successful
check / check (push) Successful in 3m0s
The check workflow ran `script/cibuild`, a plain `docker build .`. With a warm layer cache the lint and builder stages replayed instead of running, so a commit could report "Successful in 4s" without being formatted, linted, tested, or built. Squash-merging an already-built branch onto `next` hits exactly that path, so no merge was actually validated. The workflow now writes `.ci-fingerprint` into the build context: the hash of the last commit that touched the context. Any commit that changes code gets a new value, invalidates the `COPY . .` layer of both check stages, and really runs `make fmt-check`, `make lint`, `make test` and `make build`. A docs-only commit leaves it unchanged and still replays from cache in seconds, as `.dockerignore` already intends. The module download layer sits above `COPY . .` and stays cached either way, so this is cheaper than a scoped `--no-cache-filter`. `script/cibuild` is a model script shared across repos and is left untouched. The second half of the problem was that Gitea cancels the in-flight run when a newer commit lands on the same branch and records the cancellation as `failure`, marking commits red that were never tested. That cancellation is unconditional server-side for push events, so the superseding run now rewrites the exact `Has been cancelled` status to `skipped`. Genuine failures are never touched.
This commit is contained in:
28
README.md
28
README.md
@@ -1104,6 +1104,34 @@ binary is statically linked and runs on Alpine.
|
||||
`docker build .` is the CI gate — if it passes, the code is formatted,
|
||||
linted, tested, and compiled.
|
||||
|
||||
#### CI gate honesty
|
||||
|
||||
A layer cache lets `docker build .` exit 0 in seconds with the lint and
|
||||
test stages replayed rather than executed, which would make a green
|
||||
check meaningless. The `check` workflow therefore writes
|
||||
`.ci-fingerprint` into the build context before building. Its value is
|
||||
the hash of the last commit that touched the build context, so:
|
||||
|
||||
- Any commit that changes code (including a squash merge whose tree
|
||||
matches an already-built branch) gets a new fingerprint, invalidates
|
||||
the `COPY . .` layer of both check stages, and really runs
|
||||
`make fmt-check`, `make lint`, `make test`, and `make build`. A run
|
||||
that reports success ran them.
|
||||
- A docs-only commit leaves the fingerprint unchanged — `.dockerignore`
|
||||
excludes `*.md` and `LICENSE` from the context anyway — so the image
|
||||
replays from cache and costs seconds.
|
||||
|
||||
The module download layer sits above `COPY . .` and stays cached either
|
||||
way.
|
||||
|
||||
The workflow's first step covers a second way the gate lied: Gitea
|
||||
cancels an in-flight run when a newer commit lands on the same branch
|
||||
and records that cancellation as a `failure` status, marking a commit
|
||||
red that was never tested. Cancellation is unconditional server-side for
|
||||
push events, so the superseding run rewrites the exact
|
||||
`Has been cancelled` status to `skipped`. Genuine failures are never
|
||||
touched.
|
||||
|
||||
## TODO
|
||||
|
||||
See [TODO.md](TODO.md).
|
||||
|
||||
Reference in New Issue
Block a user