#!/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. # # --target lint and --no-cache-filter=lint must BOTH be present, and both # must keep naming the stage that Dockerfile.lint calls `lint`. Do not # "simplify" either one away. BuildKit silently ignores --no-cache-filter # when no stage matches the name: rename or typo the stage and the filter # becomes a no-op, the lint layer is served from cache, and script/lint # reports green having linted nothing — the exact false green this whole # setup exists to prevent. --target fails loudly on a name that does not # exist, so the two flags validate each other's magic string. # # --output=type=cacheonly skips the image export. Nothing consumes the # image — the deliverable of this build is an exit code — and exporting it # costs seconds per run and leaves a dangling image behind every time. The # lint stage still executes and a lint failure still exits non-zero. set -eu ROOT="$(cd "$(dirname "$0")/.." && pwd -P)" main() { cd "$ROOT" docker build \ --target lint \ --no-cache-filter=lint \ --output=type=cacheonly \ -f Dockerfile.lint . } main "$@"