Run all linting in Docker via Dockerfile.lint (closes #30)
All checks were successful
check / check (push) Successful in 1m2s
All checks were successful
check / check (push) Successful in 1m2s
Linting now happens in one place only: a new root Dockerfile.lint copies the repo into the digest-pinned node image already used by Dockerfile and runs eslint and prettier as build steps, so a successful build is a clean lint. script/lint is reduced to building it, which also works where the docker daemon is remote and bind mounts are impossible. No host lint path survives: the "lint" script is gone from package.json, so there is no second, unpinned way to get a lint verdict. Caching is waived for lint, because a lint build over an unchanged tree returns success in well under a second having linted nothing. LINT_EPOCH is the cache buster and it fails closed exactly as CHECK_EPOCH does: an unset ARG is the empty string, which is a perfectly stable cache key, so the guard rejects it and a bare `docker build -f Dockerfile.lint .` errors out instead of serving a green it did not earn. Both linters sit below the guard, so a fresh epoch forces them to execute while the bootstrap and dependency layers above stay cached. That makes script/lint a docker build, which nothing inside a container may call. script/check calls script/lint, so the Dockerfile image can no longer run make check: the lint stage and its COPY --from=lint ordering hack are deleted, and the remaining stage runs make test and make build under the existing CHECK_EPOCH guard. script/cibuild is now the composite gate and builds the lint image first, so a lint failure is reported before the slower suite runs. The .dockerignore exclusions are unchanged and still apply to the lint build, including the .claude/ exclusion (eslint's flat config does not ignore dot-directories, so a nested worktree in the context would be linted) and the deliberate exception that keeps .gitignore in the context for prettier. A new test asserts no per-Dockerfile ignore file shadows the root one for either image, and test/packaging/lint-docker.test.ts asserts the whole shape: the docker-only lint path, the digest pin, manifests copied before sources, the fail-closed guard with both linters below it, the absence of a lint stage or make check in Dockerfile, and the build order in script/cibuild.
This commit is contained in:
17
script/lint
17
script/lint
@@ -1,13 +1,24 @@
|
||||
#!/bin/sh
|
||||
# script/lint: run the linter (eslint plus a prettier check).
|
||||
# script/lint: run the linters. eslint and prettier are never run against
|
||||
# the working tree from here: linting runs via docker only, one way,
|
||||
# everywhere — script/lint builds Dockerfile.lint, which COPYs the repo into
|
||||
# the pinned node image and runs the linters as build steps. That works even
|
||||
# when the docker daemon is remote and bind mounts are impossible.
|
||||
#
|
||||
# LINT_EPOCH is passed on every invocation because no lint cache is wanted:
|
||||
# on an unchanged tree Docker would otherwise serve the linter layers, having
|
||||
# linted nothing, and still exit 0. Dockerfile.lint refuses to build without
|
||||
# the argument, so no path to a lint result can quietly come from cache.
|
||||
#
|
||||
# Nothing that runs inside a container may call this script; see the header
|
||||
# of Dockerfile.
|
||||
set -eu
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
||||
|
||||
main() {
|
||||
cd "$ROOT"
|
||||
yarn run eslint .
|
||||
yarn run prettier --check .
|
||||
docker build --build-arg LINT_EPOCH="$(date +%s)" -f Dockerfile.lint .
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
Reference in New Issue
Block a user