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.docker build -f Dockerfile.lint . 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, making lint network-dependent and breaking hash-pinning. Decide deliberately whether to include it.
Also remove golangci-lint installation from script/bootstrap — nothing runs on the host any more.
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.** `docker build -f Dockerfile.lint .` 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**, making lint network-dependent and breaking hash-pinning. Decide deliberately whether to include it.
Also remove golangci-lint installation from `script/bootstrap` — nothing runs on the host any more.
## 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
Plan, per the owner ruling above (every lint run in Docker, through script/lint, no cache):
A root Dockerfile.lintFROM the pinned golangci/golangci-lint:v2.12.2@sha256:… image copies the repo in and runs golangci-lint run --config .golangci.yml ./... as a build step. script/lint only builds it, with --no-cache (the same reason as script/cibuild: a cached build lints nothing), and removes the image it built.
The main Dockerfile lint stage runs golangci-lint directly instead of make lint, since script/lint now needs Docker.
No host golangci-lint remains: the install lines in script/bootstrap and the Makefile go, and script/fmt drops golangci-lint run --fix (a fix cannot be written back from a build); gofumpt and prettier stay.
golangci-lint config verify is not run: it fetches its schema over an unpinned network call.
Out of scope, owned elsewhere: hash-pinning other tool installs (#68), gofumpt in fmt-check (#70).
Model: opus-5-5
Plan, per the owner ruling above (every lint run in Docker, through `script/lint`, no cache):
- A root `Dockerfile.lint` `FROM` the pinned `golangci/golangci-lint:v2.12.2@sha256:…` image copies the repo in and runs `golangci-lint run --config .golangci.yml ./...` as a build step. `script/lint` only builds it, with `--no-cache` (the same reason as `script/cibuild`: a cached build lints nothing), and removes the image it built.
- The main `Dockerfile` lint stage runs `golangci-lint` directly instead of `make lint`, since `script/lint` now needs Docker.
- No host golangci-lint remains: the install lines in `script/bootstrap` and the `Makefile` go, and `script/fmt` drops `golangci-lint run --fix` (a fix cannot be written back from a build); `gofumpt` and prettier stay.
- `golangci-lint config verify` is not run: it fetches its schema over an unpinned network call.
- Out of scope, owned elsewhere: hash-pinning other tool installs (https://git.eeqj.de/sneak/mfer/issues/68), `gofumpt` in `fmt-check` (https://git.eeqj.de/sneak/mfer/issues/70).
Model: opus-5-5
clawbot
self-assigned this 2026-10-04 04:49:21 +02:00
Built as planned in #127. script/lint builds Dockerfile.lint uncached and removes the image it built afterwards. The main Dockerfile calls golangci-lint directly. Nothing installs or runs golangci-lint on the host any more. script/lint drops the gofmt check it repeated, which script/fmt-check still runs.
Model: opus-5-5
Built as planned in https://git.eeqj.de/sneak/mfer/pulls/127. `script/lint` builds `Dockerfile.lint` uncached and removes the image it built afterwards. The main `Dockerfile` calls `golangci-lint` directly. Nothing installs or runs golangci-lint on the host any more. `script/lint` drops the `gofmt` check it repeated, which `script/fmt-check` still runs.
Model: opus-5-5
Correction to the plan above: sneak's ruling on the shared tracking issue (sneak/prompts#40 (comment)) settles it differently. There is no separate Dockerfile.lint; linting is the lint stage of the main Dockerfile, and script/lint builds just that stage with caching off (docker build --no-cache --target lint). The config check is left out because he ruled it unnecessary. #127 is being reworked to that.
Model: opus-5-5
Correction to the plan above: sneak's ruling on the shared tracking issue (https://git.eeqj.de/sneak/prompts/issues/40#issuecomment-54891) settles it differently. There is no separate `Dockerfile.lint`; linting is the `lint` stage of the main `Dockerfile`, and `script/lint` builds just that stage with caching off (`docker build --no-cache --target lint`). The config check is left out because he ruled it unnecessary. https://git.eeqj.de/sneak/mfer/pulls/127 is being reworked to that.
Model: opus-5-5
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:
docker build -f Dockerfile.lint .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 verifyfetches its JSON schema over an unpinned live HTTPS call, making lint network-dependent and breaking hash-pinning. Decide deliberately whether to include it.Also remove golangci-lint installation from
script/bootstrap— nothing runs on the host any more.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
Plan, per the owner ruling above (every lint run in Docker, through
script/lint, no cache):Dockerfile.lintFROMthe pinnedgolangci/golangci-lint:v2.12.2@sha256:…image copies the repo in and runsgolangci-lint run --config .golangci.yml ./...as a build step.script/lintonly builds it, with--no-cache(the same reason asscript/cibuild: a cached build lints nothing), and removes the image it built.Dockerfilelint stage runsgolangci-lintdirectly instead ofmake lint, sincescript/lintnow needs Docker.script/bootstrapand theMakefilego, andscript/fmtdropsgolangci-lint run --fix(a fix cannot be written back from a build);gofumptand prettier stay.golangci-lint config verifyis not run: it fetches its schema over an unpinned network call.gofumptinfmt-check(#70).Model: opus-5-5
Built as planned in #127.
script/lintbuildsDockerfile.lintuncached and removes the image it built afterwards. The mainDockerfilecallsgolangci-lintdirectly. Nothing installs or runs golangci-lint on the host any more.script/lintdrops thegofmtcheck it repeated, whichscript/fmt-checkstill runs.Model: opus-5-5
Correction to the plan above: sneak's ruling on the shared tracking issue (sneak/prompts#40 (comment)) settles it differently. There is no separate
Dockerfile.lint; linting is thelintstage of the mainDockerfile, andscript/lintbuilds just that stage with caching off (docker build --no-cache --target lint). The config check is left out because he ruled it unnecessary. #127 is being reworked to that.Model: opus-5-5
clawbot referenced this issue2026-10-04 09:43:57 +02:00