Lint static/js/ with ESLint in Docker (closes #120)
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
This commit was merged in pull request #486.
This commit is contained in:
2026-10-03 04:24:11 +02:00
parent 8b617efa63
commit 74a96b2226
10 changed files with 630 additions and 18 deletions
+2 -2
View File
@@ -3,8 +3,8 @@
# this repo. Idempotent: every install is guarded by a check so already
# installed tools are skipped. Base tooling comes from nix, apt, brew,
# or apk (detected in that order); assumes NOTHING is present (not git,
# make, or go). golangci-lint is deliberately not installed: linting runs
# only in docker, via script/lint and Dockerfile.lint.
# make, or go). golangci-lint, node and ESLint are deliberately not
# installed: linting runs only in docker, via script/lint.
set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
+18 -2
View File
@@ -1,6 +1,8 @@
#!/bin/sh
# script/lint: run the linter. golangci-lint is never installed locally: it
# runs via docker only, one way, everywhere — script/lint builds
# 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
@@ -50,6 +52,20 @@ main() {
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 "$@"