build: make the lint cache-busting self-validating in script/lint
`--no-cache-filter=lint` is silently ignored by BuildKit when no stage matches the name, so the entire anti-false-green mechanism hung on one unvalidated magic string: renaming or mistyping the `lint` stage would have left the lint layer served from cache and `script/lint` reporting green having linted nothing. Reproduced here — with the filter pointed at a nonexistent stage and no `--target`, an unchanged tree built with `RUN golangci-lint run ... CACHED` and exited 0. `--target lint` closes it: a stage name that does not exist now fails loudly (`target stage "nosuchstage" could not be found`, exit 1) instead of passing. The two flags name the same stage from the same string and validate each other; both the script and the stage definition in Dockerfile.lint carry a comment saying they must be kept in sync. `--output=type=cacheonly` drops the image export. Nothing consumes the image — the deliverable of this build is an exit code — and the export cost seconds per run and left one dangling image behind every time, on a host where pruning is prohibited. The lint stage still executes and a lint failure still exits non-zero, both verified rather than assumed. TODO.md: the 2026-08-07 entry's claim that the repo has no linter pin and lints on the host is marked superseded in place rather than rewritten; the narrowed scaffold exemption now names `.dockerignore` alongside `Dockerfile.lint` and `script/lint`; and the specific wall-clock timings are replaced by the durable property they were evidence for, since they vary per host and per run.
This commit is contained in:
20
script/lint
20
script/lint
@@ -8,13 +8,31 @@
|
||||
# --no-cache-filter=lint forces the lint stage to re-execute every run, so
|
||||
# an unchanged tree is still actually linted; the deps stage keeps its
|
||||
# cache, so the module download is not repeated.
|
||||
#
|
||||
# --target lint and --no-cache-filter=lint must BOTH be present, and both
|
||||
# must keep naming the stage that Dockerfile.lint calls `lint`. Do not
|
||||
# "simplify" either one away. BuildKit silently ignores --no-cache-filter
|
||||
# when no stage matches the name: rename or typo the stage and the filter
|
||||
# becomes a no-op, the lint layer is served from cache, and script/lint
|
||||
# reports green having linted nothing — the exact false green this whole
|
||||
# setup exists to prevent. --target fails loudly on a name that does not
|
||||
# exist, so the two flags validate each other's magic string.
|
||||
#
|
||||
# --output=type=cacheonly skips the image export. Nothing consumes the
|
||||
# image — the deliverable of this build is an exit code — and exporting it
|
||||
# costs seconds per run and leaves a dangling image behind every time. The
|
||||
# lint stage still executes and a lint failure still exits non-zero.
|
||||
set -eu
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
||||
|
||||
main() {
|
||||
cd "$ROOT"
|
||||
docker build --no-cache-filter=lint -f Dockerfile.lint .
|
||||
docker build \
|
||||
--target lint \
|
||||
--no-cache-filter=lint \
|
||||
--output=type=cacheonly \
|
||||
-f Dockerfile.lint .
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
Reference in New Issue
Block a user