All checks were successful
check / check (push) Successful in 3m9s
golangci-lint no longer runs on the host. script/lint builds Dockerfile.lint, which copies the repo into the digest-pinned golangci-lint image and lints as a build step, so a successful build is a clean lint. The host binary shared one cache and one lock with every other checkout on the machine, which produced findings attributed to unrelated worktrees as well as unearned passes. Two properties the wrapper has to get right: - --no-cache-filter=lint forces the lint stage to re-execute. Without it an unchanged tree replays the layer and the build exits 0 in under a second having linted nothing. The deps stage stays cacheable. - Both lint steps use RUN --network=none. golangci-lint config verify is documented as fetching its JSON schema over HTTPS; the pinned image resolves it with no network, and --network=none enforces that rather than trusting it. Verify is kept because golangci-lint run silently ignores config keys it does not recognize. The main Dockerfile's lint stage now invokes golangci-lint directly instead of `make lint`, which would otherwise need a docker daemon inside the build. golangci-lint installation is removed from script/bootstrap.
38 lines
1.8 KiB
Docker
38 lines
1.8 KiB
Docker
# Lint-only image, built by script/lint. golangci-lint is never installed on
|
|
# the host: the repo is COPYed into the pinned image and linted as a build
|
|
# step, so a successful build IS a clean lint. This works even when the docker
|
|
# daemon is remote and bind mounts are impossible.
|
|
#
|
|
# script/lint passes --no-cache-filter=lint. Without it an unchanged tree
|
|
# replays the lint stage from cache and the build succeeds in under a second
|
|
# having run no linter at all. Do not drop that flag.
|
|
#
|
|
# The lint steps run with --network=none. `golangci-lint config verify` is
|
|
# documented as fetching its JSON schema over HTTPS, which would make linting
|
|
# depend on an unpinned remote artifact; this pinned image resolves the schema
|
|
# without any network, and --network=none enforces that rather than trusting
|
|
# it. It also proves no linter reaches out at analysis time. If a future image
|
|
# bump makes either step need the network, this build fails loudly instead of
|
|
# quietly acquiring an unpinned dependency.
|
|
|
|
# golangci/golangci-lint:v2.12.2 (Debian-based), 2026-08-07
|
|
# Using Debian-based image because mattn/go-sqlite3 (CGO) does not
|
|
# compile on Alpine musl (off64_t is a glibc type).
|
|
FROM golangci/golangci-lint:v2.12.2@sha256:5cceeef04e53efe1470638d4b4b4f5ceefd574955ab3941b2d9a68a8c9ad5240 AS deps
|
|
|
|
WORKDIR /src
|
|
|
|
# Copy go mod files first for better layer caching. This stage is cacheable;
|
|
# only the lint stage below is forced to re-execute.
|
|
COPY go.mod go.sum ./
|
|
RUN go mod download
|
|
|
|
FROM deps AS lint
|
|
|
|
COPY . .
|
|
|
|
# `run` silently ignores config keys it does not recognize, so a typo would
|
|
# disable a setting without a word. `config verify` is what catches that.
|
|
RUN --network=none golangci-lint config verify --config .golangci.yml
|
|
RUN --network=none golangci-lint run --config .golangci.yml ./...
|