Owner ruling, sneak 2026-08-09: every lint run happens inside a Docker container, invoked through the script/ entrypoint. Docker is always available. Linting runs independently and does not need a cache. He has directed a PR for every repo not already set up this way.
Reference implementation is sneak/homoicon — copy its shape: a root Dockerfile.lint built FROM golangci/golangci-lint:v2.12.2@sha256:5cceeef04e53efe1470638d4b4b4f5ceefd574955ab3941b2d9a68a8c9ad5240, which COPYs the repo in and runs golangci-lint run --config .golangci.yml ./... as a build step, with script/lint reduced to building it. Linting as a build step means a successful build IS a clean lint, and it works even where the docker daemon is remote and bind mounts are impossible.
Two things to get right, both of which would otherwise ship a false green:
A cached build lints nothing. A lint build on an unchanged tree returns success in well under a second having run no linter. Caching is explicitly waived here, so force the lint layers to execute.
golangci-lint config verify fetches its JSON schema over an unpinned live HTTPS call. Decide deliberately whether to include it.
Also remove golangci-lint installation from script/bootstrap — nothing runs on the host any more.
Note this repo has a substantial open lint backlog under its 1.1.0 milestone; containerising the runner does not change what those findings are, but it does mean everyone measures them the same way.
Definition of done
script/lint runs the linter only in Docker; no host golangci-lint path remains.
Two consecutive script/lint runs on an unchanged tree both demonstrably execute the linter.
Negative control: introduce a deliberate lint violation, confirm it fails with that specific finding, revert, confirm clean.
Owner ruling, sneak 2026-08-09: every lint run happens inside a Docker container, invoked through the `script/` entrypoint. Docker is always available. Linting runs independently and does not need a cache. He has directed a PR for every repo not already set up this way.
Reference implementation is `sneak/homoicon` — copy its shape: a root `Dockerfile.lint` built `FROM golangci/golangci-lint:v2.12.2@sha256:5cceeef04e53efe1470638d4b4b4f5ceefd574955ab3941b2d9a68a8c9ad5240`, which COPYs the repo in and runs `golangci-lint run --config .golangci.yml ./...` as a build step, with `script/lint` reduced to building it. Linting as a build step means a successful build IS a clean lint, and it works even where the docker daemon is remote and bind mounts are impossible.
Two things to get right, both of which would otherwise ship a false green:
1. **A cached build lints nothing.** A lint build on an unchanged tree returns success in well under a second having run no linter. Caching is explicitly waived here, so force the lint layers to execute.
2. **`golangci-lint config verify` fetches its JSON schema over an unpinned live HTTPS call.** Decide deliberately whether to include it.
Also remove golangci-lint installation from `script/bootstrap` — nothing runs on the host any more.
Note this repo has a substantial open lint backlog under its `1.1.0` milestone; containerising the runner does not change what those findings are, but it does mean everyone measures them the same way.
## Definition of done
- `script/lint` runs the linter only in Docker; no host golangci-lint path remains.
- Two consecutive `script/lint` runs on an unchanged tree both demonstrably execute the linter.
- Negative control: introduce a deliberate lint violation, confirm it fails with that specific finding, revert, confirm clean.
- `make check` still green.
Canonical tracking issue: https://git.eeqj.de/sneak/prompts/issues/40
Linting now runs only in Docker. New Dockerfile.lint (pinned golangci-lint v2.12.2) runs the linter as a build step; script/lint is reduced to building it, passing a fresh GATE_RUN build arg each run so the lint layer always executes. script/bootstrap no longer installs golangci-lint. The main Dockerfile lint stage now calls golangci-lint directly instead of make lint, so building it is not docker-in-docker.
config verify is deliberately not run: it fetches its JSON schema over an unpinned live HTTPS call, which REPO_POLICIES.md's hash-pinning rule forbids.
Verified: two consecutive runs on an unchanged tree both executed the linter; a deliberate unused-function violation failed the lint with that exact finding and reverting returned it to clean; make check and the full docker build . are both green.
Model: opus-4-8
Done in PR https://git.eeqj.de/sneak/upaas/pulls/200.
Linting now runs only in Docker. New `Dockerfile.lint` (pinned golangci-lint v2.12.2) runs the linter as a build step; `script/lint` is reduced to building it, passing a fresh `GATE_RUN` build arg each run so the lint layer always executes. `script/bootstrap` no longer installs golangci-lint. The main `Dockerfile` lint stage now calls `golangci-lint` directly instead of `make lint`, so building it is not docker-in-docker.
`config verify` is deliberately not run: it fetches its JSON schema over an unpinned live HTTPS call, which REPO_POLICIES.md's hash-pinning rule forbids.
Verified: two consecutive runs on an unchanged tree both executed the linter; a deliberate unused-function violation failed the lint with that exact finding and reverting returned it to clean; `make check` and the full `docker build .` are both green.
Model: opus-4-8
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Owner ruling, sneak 2026-08-09: every lint run happens inside a Docker container, invoked through the
script/entrypoint. Docker is always available. Linting runs independently and does not need a cache. He has directed a PR for every repo not already set up this way.Reference implementation is
sneak/homoicon— copy its shape: a rootDockerfile.lintbuiltFROM golangci/golangci-lint:v2.12.2@sha256:5cceeef04e53efe1470638d4b4b4f5ceefd574955ab3941b2d9a68a8c9ad5240, which COPYs the repo in and runsgolangci-lint run --config .golangci.yml ./...as a build step, withscript/lintreduced to building it. Linting as a build step means a successful build IS a clean lint, and it works even where the docker daemon is remote and bind mounts are impossible.Two things to get right, both of which would otherwise ship a false green:
golangci-lint config verifyfetches its JSON schema over an unpinned live HTTPS call. Decide deliberately whether to include it.Also remove golangci-lint installation from
script/bootstrap— nothing runs on the host any more.Note this repo has a substantial open lint backlog under its
1.1.0milestone; containerising the runner does not change what those findings are, but it does mean everyone measures them the same way.Definition of done
script/lintruns the linter only in Docker; no host golangci-lint path remains.script/lintruns on an unchanged tree both demonstrably execute the linter.make checkstill green.Canonical tracking issue: sneak/prompts#40
clawbot referenced this issue2026-09-03 18:29:57 +02:00
Done in PR #200.
Linting now runs only in Docker. New
Dockerfile.lint(pinned golangci-lint v2.12.2) runs the linter as a build step;script/lintis reduced to building it, passing a freshGATE_RUNbuild arg each run so the lint layer always executes.script/bootstrapno longer installs golangci-lint. The mainDockerfilelint stage now callsgolangci-lintdirectly instead ofmake lint, so building it is not docker-in-docker.config verifyis deliberately not run: it fetches its JSON schema over an unpinned live HTTPS call, which REPO_POLICIES.md's hash-pinning rule forbids.Verified: two consecutive runs on an unchanged tree both executed the linter; a deliberate unused-function violation failed the lint with that exact finding and reverting returned it to clean;
make checkand the fulldocker build .are both green.Model: opus-4-8