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.
36 lines
1.7 KiB
Docker
36 lines
1.7 KiB
Docker
# Lint image: every lint run happens here, and nowhere else. The repo is
|
|
# COPYed into a digest-pinned image and the linters run as build steps, so a
|
|
# successful build IS a clean lint. `script/lint` does nothing but build this
|
|
# file, which also works where the docker daemon is remote and bind mounts are
|
|
# impossible. Nothing that runs inside a container may call `script/lint`:
|
|
# that is why Dockerfile no longer runs `make check`.
|
|
# node 22.22.0 on Alpine 3.23.3 (node:22-alpine), 2026-08-09
|
|
FROM node@sha256:e4bf2a82ad0a4037d28035ae71529873c069b13eb0455466ae0bc13363826e34 AS lint
|
|
WORKDIR /app
|
|
|
|
# Manifests before sources, so the dependency install layer stays cached
|
|
# until package.json or yarn.lock changes. script/bootstrap ends in
|
|
# `yarn install --frozen-lockfile`; the lint steps below are deliberately
|
|
# not cached.
|
|
COPY script/ script/
|
|
COPY package.json yarn.lock ./
|
|
RUN script/bootstrap
|
|
|
|
COPY . .
|
|
|
|
# LINT_EPOCH is a cache buster, with the same fail-closed contract as
|
|
# CHECK_EPOCH in Dockerfile. No lint cache is wanted: on an unchanged tree
|
|
# Docker serves the linter layers in well under a second, having linted
|
|
# nothing, and the build still exits 0. The guard makes an absent argument a
|
|
# hard failure — an unset ARG is the empty string, which is a perfectly
|
|
# stable cache key, so a plain `docker build -f Dockerfile.lint .` would
|
|
# otherwise get exactly that false green. Every layer below this one is a
|
|
# child of the guard, so a fresh epoch forces all of them to execute.
|
|
ARG LINT_EPOCH
|
|
RUN [ -n "$LINT_EPOCH" ] || exit 1
|
|
|
|
# The linters are invoked directly rather than through `make lint`, because
|
|
# `make lint` is the build of this file.
|
|
RUN yarn run eslint .
|
|
RUN yarn run prettier --check .
|