build: run golangci-lint in a pinned container via script/lint (closes #41)
golangci-lint is no longer invoked on the host anywhere in the repo.
Dockerfile.lint pins golangci/golangci-lint:v2.12.2 by digest and runs
the linter as a build step, so a successful build IS a clean lint, and
`make lint` becomes a thin shim over script/lint. This removes the host
linter install that produced a false green here, where a branch that was
genuinely red with a goconst finding reported "0 issues" off the shared
host cache; a container per run has its own cache and lock.
Two deliberate divergences from the sneak/homoicon reference shape:
- Two stages rather than one. A cached `deps` stage holds
`go mod download`, then `FROM deps AS lint` carries the source copy
and the lint run, and script/lint builds with
`--no-cache-filter=lint`. Caching of the lint result is explicitly
waived (a cached build lints nothing), and splitting the stages means
busting the lint layer does not re-fetch the module cache over the
network on every run.
- No `golangci-lint config verify` step. It resolves its JSON schema
over a live, unpinned HTTPS call: an unpinned network input inside
the one step whose purpose is a pinned, reproducible gate, and a
schema-host outage would surface as a red build. `golangci-lint run`
already fails on a malformed config. The reason is recorded in a
comment in Dockerfile.lint.
.dockerignore excludes .git only; the lint reads the Go sources,
go.mod/go.sum and .golangci.yml, none of which come from there.
The TODO.md scaffold-exemption note is narrowed rather than dropped:
Dockerfile.lint and script/lint are now permitted and required, while CI
config, REPO_POLICIES.md, an application Dockerfile and any other
script/ entrypoint still are not.
Verified, since a green docker build is the classic false green: two
consecutive script/lint runs on an unchanged tree each showed the
`golangci-lint run` layer executing (9.8s and 7.9s, both "0 issues.")
while the deps layers reported CACHED; a deliberate indent-error-flow
violation failed the build naming that finding and the unused one, and a
revert went clean again. `make check` green.
This commit is contained in:
20
script/lint
Executable file
20
script/lint
Executable file
@@ -0,0 +1,20 @@
|
||||
#!/bin/sh
|
||||
# script/lint: run the linter. golangci-lint is never installed locally:
|
||||
# it runs via docker only, one way, everywhere — script/lint builds
|
||||
# Dockerfile.lint, which COPYs the repo into the pinned golangci-lint
|
||||
# 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.
|
||||
set -eu
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
||||
|
||||
main() {
|
||||
cd "$ROOT"
|
||||
docker build --no-cache-filter=lint -f Dockerfile.lint .
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Reference in New Issue
Block a user