Run all linting in Docker via Dockerfile.lint (closes #30) #31
3 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| 2bfa11c10c |
Walk the path CI runs in lint-once, and pin the branch the container installs (closes #33)
All checks were successful
check / check (push) Successful in 35s
The header of test/packaging/lint-once.test.ts claimed a duplicate prettier pass is caught wherever it is added. It was not: the walk started at `make check`, which never reads `Dockerfile`, so appending `RUN yarn run prettier --check .` to the image that `script/cibuild` builds left the suite green — two prettier passes on the one path where it matters most. The walk now also starts at `.gitea/workflows/check.yml` and follows its `run:` steps into `script/cibuild` and from there into both images, so the graph under test is the one CI executes rather than the one it was assumed to execute. Reaching `script/cibuild` and `Dockerfile` is asserted, and the test and build image is asserted to invoke prettier zero times. The lockfile assertion was a substring check against the whole of `script/bootstrap`. That script has two install sites, and the containers take the second, because the pinned node image ships yarn; changing that site to a bare `yarn install` kept the suite green while the container's install stopped being pinned. `install_js_deps` is now resolved out of the script and split at its `missing yarn` guard, and every `yarn install` occurrence in each branch is required to carry `--frozen-lockfile`. That the container runs `script/bootstrap` at all is asserted too, so the lockfile assertions cannot end up describing a script the image never executes. Prettier is counted per occurrence instead of per line: `prettier --check . && prettier --check src` was one invocation by the old count. The `continue` that followed a counted line also dropped every script, make, yarn and docker edge sharing that line, so a subtree could be hidden behind a single `&&`; edges are now extracted from every line. Undercounting is what would make this file worthless, so every way of reaching nothing is a thrown error rather than a quiet zero: an unknown Makefile target, an unknown package.json script, a missing script file, a node that resolves to no commands, and an unknown node kind. All five are tested, as is a walk that legitimately counts zero, and the cycle guard. Every assertion in the file was mutation-tested: changed to assert something else, run, and confirmed to fail for its own named reason. The two mutations above were reproduced and both now turn the suite red. test/packaging/entrypoints.test.ts said `make check` runs test, lint and fmt-check. Formatting has been part of the lint container since the duplicate host pass was removed, so the comment now says what it does. |
|||
| a73f0abbe8 |
Check formatting once per make check, in the container (closes #29)
All checks were successful
check / check (push) Successful in 59s
script/check ran script/test, script/lint and script/fmt-check. Since linting moved into Docker, script/lint is a build of Dockerfile.lint, which runs `prettier --check .` as a build step — so make check checked formatting twice over the same tree: once in the container and once on the host. script/precommit had the same pair. Drop the script/fmt-check call from both. The container keeps the check, because a successful Dockerfile.lint build is what CI treats as proof of a clean tree, and it is the stronger of the two verdicts: its prettier is digest-pinned and installed under --frozen-lockfile, while the host's is whatever the working tree happens to have. The pre-commit hook is unchanged in what it catches — script/lint still fails a badly formatted tree, and therefore the commit. script/fmt-check survives as a standalone entrypoint, as REPO_POLICIES.md requires, for asking the formatting question by itself without docker. Its verdict cannot drift from the container's: prettier is pinned to an exact version, installed from yarn.lock in both places, and reads .gitignore as its default ignore file, which is why .dockerignore keeps .gitignore in the build context. The count is asserted rather than promised. test/packaging/lint-once.test.ts walks the invocation graph from each entrypoint — through the Makefile shims, the script/ calls, the package.json scripts and the docker build into Dockerfile.lint's RUN steps — and counts prettier invocations: one per make check, one per script/precommit, and one each for make lint and make fmt-check alone, so neither can become a no-op that satisfies the count trivially. The walk also asserts which nodes it reached, so a restructure that defeats the resolver fails the test instead of quietly counting zero. Observed: 2 prettier invocations per make check before, 1 after. |
|||
| fed39d19cf |
Run all linting in Docker via Dockerfile.lint (closes #30)
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. |