#!/bin/sh # script/lint: run golangci-lint over the whole tree. # # The linter is never installed on the host: it runs only inside the # Dockerfile.lint build, one way, everywhere. A clean build is a clean # lint. See Dockerfile.lint for why the lint step cannot be cached. set -eu ROOT="$(cd "$(dirname "$0")/.." && pwd -P)" main() { cd "$ROOT" # A value no other run repeats. Dockerfile.lint folds it into the # lint step's cache key, so the linter re-executes every run instead # of an unchanged tree returning a cached success having linted # nothing. cachebust="$(date +%s)-$$" tmp="$(mktemp -d "${TMPDIR:-/tmp}/pixa-lint.XXXXXX")" trap 'rm -rf "$tmp"' EXIT INT TERM # --progress=plain so the lint step's own output reaches the log we # check below; --output=type=cacheonly because we want the linter's # verdict, not an image left in the local store. The build status # travels through a file: a pipeline's exit status is tee's, not the # build's. ( set +e docker build \ --progress=plain \ --build-arg CACHEBUST="$cachebust" \ --output=type=cacheonly \ -f Dockerfile.lint . 2>&1 echo "$?" >"$tmp/status" ) | tee "$tmp/build.log" status="$(cat "$tmp/status" 2>/dev/null || echo 1)" [ "${status:-1}" -eq 0 ] || exit "${status:-1}" # The linter's start line must appear as build output, not only in # the build's echo of the RUN instruction. A step served from cache # prints the instruction and none of its output; a step that runs # prints a "# ..." output line. Requiring that output # line means a future edit dropping the CACHEBUST reference from # Dockerfile.lint fails here rather than passing having linted # nothing. if ! grep -Eq '^#[0-9]+ +[0-9]+\.[0-9]+ +pixa-lint: running golangci-lint' \ "$tmp/build.log"; then echo "script/lint: golangci-lint did not execute (cached step?)." >&2 exit 1 fi } main "$@"