build: define the lint stage name once so the two flags cannot diverge
The previous commit claimed --target and --no-cache-filter "validate each
other's magic string". They do not. --target validates only its own
argument; a typo confined to --no-cache-filter left the build green and
linting nothing:
docker build --target lint --no-cache-filter=lnit ...
#10 [lint 2/2] RUN golangci-lint run ... CACHED exit 0
Three of the four edit paths were caught and one was not, so the original
false green survived in the narrow case.
The duplication was the defect: the stage name appeared twice on one
command line and nothing tied the copies together. Correcting only the
prose would have left the hazard live and merely warned about, so the name
is now written once, as `stage=lint`, and passed to both flags. Divergence
is unrepresentable rather than documented — there is a single name to get
wrong, and --target rejects it loudly when it is not a stage in
Dockerfile.lint, which now covers the filter too because it is the same
string.
The comments in script/lint and Dockerfile.lint and the TODO.md entry drop
the false "validate each other" claim and state the real property, along
with the residual hazard that is genuinely unguarded: --target checks that
the name exists, not that it names the stage which actually runs
golangci-lint, and it stops the build there, so relocating the lint step
or appending a stage after it would go unnoticed.
This commit is contained in:
39
script/lint
39
script/lint
@@ -5,18 +5,28 @@
|
||||
# image and lints as a build step. This works even when the docker daemon
|
||||
# is remote and bind mounts are impossible.
|
||||
#
|
||||
# --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.
|
||||
# --no-cache-filter 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. Both flags below must stay: do
|
||||
# not "simplify" either one away.
|
||||
#
|
||||
# --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.
|
||||
# The stage name is written ONCE, in $stage, and passed to both --target
|
||||
# and --no-cache-filter, so the two flags cannot come to name different
|
||||
# stages. That is the whole point of the variable. BuildKit silently
|
||||
# ignores --no-cache-filter when no stage matches its argument: a filter
|
||||
# naming a stage that does not exist is a no-op, the lint layer is served
|
||||
# from cache, and script/lint reports green having linted nothing — the
|
||||
# false green this setup exists to prevent. --target, by contrast, fails
|
||||
# loudly on a name that is not in the file. With a single shared name, a
|
||||
# typo or a stale rename therefore becomes a hard error instead of a
|
||||
# silent skip, because the one name reaches both flags.
|
||||
#
|
||||
# What the tooling does NOT check, and is left to whoever edits this:
|
||||
# $stage must name the stage in Dockerfile.lint that actually runs
|
||||
# golangci-lint. --target verifies that the name exists, not that it is
|
||||
# the right stage, and it stops the build at that stage — so moving the
|
||||
# lint step into a different stage, or adding a stage after this one,
|
||||
# would not be caught here. Keep this file and Dockerfile.lint in sync.
|
||||
#
|
||||
# --output=type=cacheonly skips the image export. Nothing consumes the
|
||||
# image — the deliverable of this build is an exit code — and exporting it
|
||||
@@ -26,11 +36,14 @@ set -eu
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
||||
|
||||
# Must match the stage name in Dockerfile.lint.
|
||||
stage=lint
|
||||
|
||||
main() {
|
||||
cd "$ROOT"
|
||||
docker build \
|
||||
--target lint \
|
||||
--no-cache-filter=lint \
|
||||
--target "$stage" \
|
||||
--no-cache-filter="$stage" \
|
||||
--output=type=cacheonly \
|
||||
-f Dockerfile.lint .
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user