Make script/cibuild unable to report an unearned green (closes #85)
All checks were successful
check / check (push) Successful in 3m13s
All checks were successful
check / check (push) Successful in 3m13s
script/cibuild was a bare `docker build .`. On an unchanged tree Docker served the check RUN layers from cache, so make fmt-check, make lint and make test never executed - and the build still exited 0. Measured at 221ms with zero ok lines and every check layer CACHED, against 162s for a real run. CI showed the same signature: 6 second "successes" on main. An ARG CHECK_EPOCH now sits immediately above the check RUNs in both stages - each stage declares its own, since ARG scope is per-stage - and script/cibuild passes a fresh value per invocation. Dependency and module layers sit above the ARG and still cache, so this does not make every build cold. The epoch is assigned before the build rather than inlined into the --build-arg. Under `set -eu` a command substitution that fails inside an argument does not abort the script: CHECK_EPOCH would become an empty string, an empty string is a constant, a constant CHECK_EPOCH restores the cached false green, and the guard would silently disarm itself while still exiting 0. As a bare assignment, set -e catches a failing date and no build starts. The README and Dockerfile state the guarantee conditionally. It holds per build context and CHECK_EPOCH value, and depends on script/cibuild passing a fresh one - a bare `docker build .` with no --build-arg still replays the check layers from the second consecutive run onward. That residual gap is tracked in #91 along with the remaining upstream hardening. Verification is recorded once, in the PR's verification comment, rather than restated with differing numbers in three places.
This commit was merged in pull request #89.
This commit is contained in:
22
README.md
22
README.md
@@ -617,10 +617,24 @@ them. We provide:
|
||||
lint findings.
|
||||
* `script/docker` — build the Docker image tagged via
|
||||
`script/projectname`
|
||||
* `script/cibuild` — CI entrypoint: `docker build .` (the Dockerfile
|
||||
runs the checks). This is the full CI-equivalent gate — it runs the
|
||||
checks in the same containers CI does, from a clean copy of the tree,
|
||||
so it also catches anything that depends on host state.
|
||||
* `script/cibuild` — CI entrypoint: `docker build` (the `Dockerfile`
|
||||
runs `make fmt-check` and `make lint` in its lint stage and `make
|
||||
test` in its builder stage). This is the full CI-equivalent gate — it
|
||||
runs the checks in the same containers CI does, from a clean copy of
|
||||
the tree, so it also catches anything that depends on host state. It
|
||||
passes a fresh `--build-arg CHECK_EPOCH`, which the `Dockerfile`
|
||||
declares immediately above the check `RUN`s in both stages. Those
|
||||
layers are keyed on that value, so a new value re-runs them even on a
|
||||
byte-identical tree, and a green from this script means the checks
|
||||
executed. Dependency and module layers sit above the `ARG` and still
|
||||
cache, so a build is not cold.
|
||||
|
||||
That guarantee is conditional on the fresh value, so **run the gate
|
||||
through `script/cibuild`, not by invoking `docker build` yourself**. A
|
||||
bare `docker build .` supplies no `CHECK_EPOCH`; the empty default is
|
||||
a constant, so the second and every later build on an unchanged tree
|
||||
serves all three check layers from cache, executes nothing, and still
|
||||
exits 0. Issue #91 tracks making that case fail loudly instead.
|
||||
* `script/precommit` — pre-commit gate: `go mod tidy` + `go fmt` (must
|
||||
not change files), then `script/check`
|
||||
* `script/install-precommit` — install the git pre-commit hook that
|
||||
|
||||
Reference in New Issue
Block a user