# 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 ./...