check / check (push) Successful in 3m50s
`REPO_POLICIES.md` binds the repo to a JavaScript styleguide, but nothing checked `static/js/`. ESLint, pinned by `package.json` and `yarn.lock`, now lints it with the styleguide's two checkable rules, `no-var` and `prefer-const`. It runs only in Docker on a digest-pinned node image: a `js-deps` stage installs ESLint and stays cached until the manifests change, and a `js-lint` stage runs it. `script/lint` builds `js-lint`, so `make lint` and `make check` fail on a violation, and the image build depends on it as on the repo's other checks. ESLint, node and yarn are not prerequisites, and `make lint` never uses a host copy. Model: opus-5-5
72 lines
2.9 KiB
Bash
Executable File
72 lines
2.9 KiB
Bash
Executable File
#!/bin/sh
|
|
# script/lint: run the linters, golangci-lint over the Go code and then
|
|
# ESLint over static/js/. Neither is ever installed locally.
|
|
#
|
|
# golangci-lint 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, and it removes the host linter's shared
|
|
# cache, which has attributed other checkouts' findings to this one.
|
|
#
|
|
# --no-cache-filter=lint forces the lint stage to re-execute on every run; a
|
|
# cached lint stage exits 0 in under a second having linted nothing. The deps
|
|
# stage keeps its cache, so module downloads are not repeated.
|
|
# --progress=plain keeps the linter's own output visible on success, so a
|
|
# passing run shows the issue count rather than nothing.
|
|
# --output=type=cacheonly leaves no image behind to clean up.
|
|
#
|
|
# docker silently ignores --no-cache-filter for a stage name that does not
|
|
# match, so a rename or a typo would restore the cached false green with no
|
|
# warning and a fast exit 0. The flag is therefore not trusted: the build
|
|
# output is teed to a log and a run is only a pass if golangci-lint's own
|
|
# summary line ("N issues." / "N issues:") is in it. No summary, no lint,
|
|
# whatever the exit code says.
|
|
set -eu
|
|
|
|
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
|
|
|
main() {
|
|
cd "$ROOT"
|
|
|
|
log="$(mktemp -t webhooker-lint.XXXXXXXX)"
|
|
rcfile="$(mktemp -t webhooker-lint-rc.XXXXXXXX)"
|
|
trap 'rm -f "$log" "$rcfile"' EXIT INT TERM
|
|
|
|
# The pipeline's status is tee's, and POSIX sh has no pipefail, so the
|
|
# build's status travels via a file. Output still streams live.
|
|
{
|
|
docker build \
|
|
-f Dockerfile.lint \
|
|
--no-cache-filter=lint \
|
|
--progress=plain \
|
|
--output=type=cacheonly \
|
|
. 2>&1 && echo 0 >"$rcfile" || echo $? >"$rcfile"
|
|
} | tee "$log" >&2
|
|
|
|
rc="$(cat "$rcfile")"
|
|
[ "$rc" -eq 0 ] || exit "$rc"
|
|
|
|
if ! grep -qE '[0-9]+ issues[.:]' "$log"; then
|
|
echo "script/lint: golangci-lint printed no summary line; the linter" >&2
|
|
echo " did not run. Check that the stage named in --no-cache-filter" >&2
|
|
echo " still matches a stage in Dockerfile.lint." >&2
|
|
exit 1
|
|
fi
|
|
|
|
# ESLint runs in the Dockerfile's js-lint stage, which the image build
|
|
# runs too. It prints nothing on a pass, so there is no summary to look
|
|
# for. Instead the stage is named once, for both flags: --target fails
|
|
# on a name that matches no stage, so a rename cannot leave
|
|
# --no-cache-filter silently ignored. The js-deps stage, which installs
|
|
# ESLint, keeps its cache, so ESLint is not downloaded again.
|
|
js_stage=js-lint
|
|
docker build \
|
|
--target "$js_stage" \
|
|
--no-cache-filter="$js_stage" \
|
|
--progress=plain \
|
|
--output=type=cacheonly \
|
|
.
|
|
}
|
|
|
|
main "$@"
|