All checks were successful
check / check (push) Successful in 9s
script/lint runs the linter directly when it is already inside a container and otherwise builds Dockerfile.lint, so the linter never runs on a developer host. That closes three host-only mechanisms: the result cache golangci-lint keys on file content rather than location, which produced a confirmed false green and findings reported against other checkouts; the host-global $TMPDIR/golangci-lint.lock, which fails a run in a way no caller can distinguish from findings; and host/container version skew, which hid thirteen findings on one repo. Detection is on LINT_IN_CONTAINER=1, set by every Dockerfile, and on nothing else. The two directions are not symmetric: a false negative inside a container attempts a nested docker build, finds no daemon and fails loudly, while a false positive on a host silently lints there, which is the defect this issue exists to kill. /.dockerenv is therefore rejected even as a fallback -- measured absent inside BuildKit RUN steps and present on any host that is itself a container, so it fails in both directions and one of them is the dangerous one. Nothing else changes shape. The Dockerfile still runs make check, script/check still runs test, lint and fmt-check, script/cibuild is still a single docker build with CHECK_EPOCH and VERSION, and the Go multistage lint stage and its COPY --from=lint ordering dependency survive with ENV LINT_IN_CONTAINER=1 added. Dockerfile.lint is the standalone developer-host path and carries the same CHECK_EPOCH guard, with the ARG below the dependency layer so only the lint re-runs. The script/bootstrap golangci-lint install and the per-checkout GOLANGCI_LINT_CACHE/TMPDIR wrapper are deleted as superseded. Neither has a caller left. A JS repo's yarn install stays: the rule is that no lint verdict may come from a host invocation, not that no linter binary may exist there, and in a repo whose formatter is its linter the formatter necessarily runs on the host. golangci-lint config verify is kept, on measurement. Under the pinned v2.12.2 a bogus top-level key and a bogus key under linters.settings.lll both pass `golangci-lint run` with exit 0 and `0 issues` while config verify exits 3 and names them; an unknown linter name fails run and passes config verify. It needs no network: every case reproduced byte-identically under `docker run --network none`, in a container where `getent hosts golangci-lint.run` exits 2. Comment blocks were cut hard across every file this unit touches. .dockerignore drops from 67 comment lines to 28, script/cibuild from 17 to 12, script/docker from 18 to 12, and prompts/REPO_POLICIES.md from 1182 lines to 907. What remains says why a line is load-bearing; the discovery narratives are gone. config verify lives in script/lint's native branch rather than in a Dockerfile, so every path that lints inherits it: the lint stage of the main image, which is what CI runs, as well as Dockerfile.lint. Putting it in one Dockerfile is how the other path silently loses it.
28 lines
930 B
Docker
28 lines
930 B
Docker
# Lint-only image, built by script/lint when it is not already inside a
|
|
# container. Linting is a build step, so a successful build is a clean
|
|
# lint, and nothing is bind-mounted, which matters when the daemon is
|
|
# remote.
|
|
#
|
|
# node 22-alpine, 2026-02-22
|
|
FROM node@sha256:e4bf2a82ad0a4037d28035ae71529873c069b13eb0455466ae0bc13363826e34
|
|
|
|
WORKDIR /app
|
|
|
|
# Makes script/lint run the linter directly instead of recursing into
|
|
# another docker build, which has no daemon here.
|
|
ENV LINT_IN_CONTAINER=1
|
|
|
|
COPY script/ script/
|
|
COPY package.json yarn.lock ./
|
|
RUN script/bootstrap
|
|
|
|
COPY . .
|
|
|
|
# ARG sits after the dependency layer so that layer stays cached and
|
|
# only the lint re-runs. The guard fails a bare `docker build
|
|
# -f Dockerfile.lint .`, which would otherwise reuse the empty (stable)
|
|
# cache key and report a lint it never ran.
|
|
ARG CHECK_EPOCH
|
|
RUN [ -n "$CHECK_EPOCH" ] || exit 1
|
|
RUN echo "lint epoch: ${CHECK_EPOCH}" && make lint
|