Files
vaultik/script/cibuild
clawbot c3bb3b5580
All checks were successful
check / check (push) Successful in 3m13s
Make script/cibuild unable to report an unearned green (closes #85)
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.
2026-08-09 09:37:55 +02:00

33 lines
1.4 KiB
Bash
Executable File

#!/bin/sh
# script/cibuild: run the CI build. The Dockerfile does not run
# script/check; it runs `make fmt-check` and `make lint` in its lint
# stage and `make test` in its builder stage. A successful build
# implies those three passed, provided they actually ran -- which is
# what the CHECK_EPOCH below is for.
# Generic: needs no adaptation. The Gitea workflow runs this on push.
set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
main() {
cd "$ROOT"
# The Dockerfile's check layers are keyed on CHECK_EPOCH, so a
# fresh value here is what forces them to re-run: without it an
# unchanged tree replays them from cache, the checks never execute,
# and the build still exits 0. The ARG sits immediately above the
# check RUNs, so dependency and module layers still cache.
#
# Assign the epoch on its own line rather than inline in the
# argument. 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, and a
# constant CHECK_EPOCH is exactly the cached-check false green this
# script exists to prevent -- so the guard would disarm itself and
# still exit 0. As a bare assignment, `set -e` catches a failing
# `date` and no build starts.
epoch="$(date +%s)"
docker build --build-arg CHECK_EPOCH="$epoch" .
}
main "$@"