All checks were successful
check / check (pull_request) Successful in 4m0s
Every lint run now happens inside its own container, invoked through script/lint, and linting is a build step rather than a container command: a successful build of the new root Dockerfile.lint IS a clean lint. That shape also works where the docker daemon is remote and bind mounts are impossible. Its FROM line -- golangci/golangci-lint:v2.12.2, pinned by digest -- is now the only pin of the linter version in this repo. A container per run has its own lint cache and its own golangci-lint lock, both discarded with it, so neither cross-worktree contamination nor lock contention exists any more. The machinery that defended against them is therefore gone: the per-worktree cache directories, the lock-retry loop, and script/lint-audit, which existed to catch findings replayed from a cache that no longer exists. So is the host lint path in its entirety -- the native escape hatch, its version detection, and VAULTIK_LINT_IN_CONTAINER in both script/lint and the Dockerfile. Nothing lints on the host, at any version. A cached build lints nothing, so the CHECK_EPOCH mechanism the product Dockerfile already used is what makes a green mean something: ARG CHECK_EPOCH with no default, placed below the module layers so dependency caching survives, a `RUN [ -n "$CHECK_EPOCH" ] || exit 1` guard so a build that withholds the arg fails instead of replaying, and the value expanded into the lint command itself. script/lint computes `epoch="$(date +%s%N)$$"` as a bare assignment on its own line, because inline in the argument a failing substitution does not abort under `set -eu` and yields a constant empty epoch -- which is exactly the false green being prevented. The product Dockerfile loses its lint stage rather than gaining a second linter pin. That stage ran `make lint`, which is now `docker build`: docker-in-docker inside a BuildKit step with no daemon. Calling golangci-lint directly there instead would have meant two independently bumpable digests for one tool. `make fmt-check` moves beside `make test` in the builder stage, and script/cibuild now builds Dockerfile.lint and then Dockerfile, each with its own fresh epoch, failing on either. Consequence, stated in comments rather than left to be discovered: script/docker builds the product image only and no longer lints; script/check and script/cibuild are the gates. Two decisions taken deliberately and documented where they apply. `golangci-lint config verify` is omitted: it fetches its JSON schema over an unpinned live HTTPS call, which would make the gate depend on a remote resource outside this repo's hash-pinning discipline and turn an upstream outage or an egress-less runner into a red that is not a lint verdict. script/lint-fix is kept, reimplemented as a bind-mounted docker run against the image parsed out of Dockerfile.lint -- a build step cannot write fixes back to the worktree -- and its header states outright that it is a developer convenience, never a gate, and needs a local daemon. cmd/vaultik/lintdocker_test.go parses both Dockerfiles and both scripts and fails if any part of the mechanism is dropped: the digest pin, the defaultless ARG below `go mod download`, the emptiness guard, the expansion of the epoch into each check command, the bare per-invocation epoch assignment in both scripts, cibuild building both files, and the absence of any host-lint escape hatch. Every one of those losses is silent -- the build still exits 0 and nothing is checked -- which is why they are asserted rather than trusted. script/lint takes no arguments now, and says so instead of dropping them: a build step has no command line to pass linter flags to.
55 lines
2.0 KiB
Bash
Executable File
55 lines
2.0 KiB
Bash
Executable File
#!/bin/sh
|
|
# script/lint-fix: run the linter's autofixer. Rewrites files in place
|
|
# for every finding the enabled linters know how to fix; findings
|
|
# without an autofix are reported but left alone.
|
|
#
|
|
# THIS IS A DEVELOPER CONVENIENCE AND NEVER A GATE. Nothing in
|
|
# script/check, script/precommit or script/cibuild calls it, and no gate
|
|
# reads its exit status. The gate is script/lint, which builds
|
|
# Dockerfile.lint; run that afterwards to find out whether the tree is
|
|
# actually clean.
|
|
#
|
|
# Unlike script/lint this cannot be a build step: a build step writes
|
|
# into an image, and fixes have to land in the worktree. So it runs the
|
|
# same pinned image as a container with the tree bind-mounted, which
|
|
# means it needs a LOCAL docker daemon -- a remote daemon has no access
|
|
# to these files, and this script will appear to do nothing there. The
|
|
# image reference is parsed out of Dockerfile.lint's FROM line, so the
|
|
# autofixer is always the same version as the linter that gates; fixes
|
|
# written by a different version are not necessarily fixes for the
|
|
# version that decides.
|
|
set -eu
|
|
|
|
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
|
DOCKERFILE="$ROOT/Dockerfile.lint"
|
|
|
|
# The image reference from Dockerfile.lint, tag and digest included.
|
|
lint_image() {
|
|
awk '$1 == "FROM" { print $2; exit }' "$DOCKERFILE"
|
|
}
|
|
|
|
main() {
|
|
cd "$ROOT"
|
|
|
|
image="$(lint_image)"
|
|
if [ -z "$image" ]; then
|
|
echo "lint-fix: no FROM line found in $DOCKERFILE" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Run as the invoking user so the rewritten files stay owned by
|
|
# them. HOME is set because the Go and golangci-lint caches default
|
|
# under it and that user has no home inside the container; those
|
|
# caches are per-container and discarded with it.
|
|
docker run --rm \
|
|
--user "$(id -u):$(id -g)" \
|
|
--env HOME=/tmp \
|
|
--env GOFLAGS=-buildvcs=false \
|
|
--volume "$ROOT:/src" \
|
|
--workdir /src \
|
|
"$image" \
|
|
golangci-lint run --config .golangci.yml --fix "$@" ./...
|
|
}
|
|
|
|
main "$@"
|