Files
rgoue/script/lint
sneak 599286a88e 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.
2026-08-10 12:33:41 +00:00

21 lines
690 B
Bash
Executable File

#!/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 "$@"