Moved here from sneak/bsfirehose#40: the steps it would change are those of the canonical Go Dockerfile in REPO_POLICIES.md, so making the change in one repository would make that repository diverge from it.
Problem
On bsfirehose next (26a707f, after sneak/bsfirehose#67 re-vendored the canonical files), two limits in REPO_POLICIES.md are broken: script/cibuild took 454s against the 5-minute limit, and make test took 158s against the 60-second cap. Every step that compiles starts from an empty Go build cache, so the lint, test and build steps each compile the whole dependency graph from source, the sqlite driver's C code included, and script/cibuild runs lint and test twice. Measured on the shared build host at a load of about 100–150 on 48 cores.
Proposed change
Add --mount=type=cache,target=/root/.cache/go-build to the lint, test and build RUN steps of the canonical Go Dockerfile. The mount keeps compiled packages, not step results: --no-cache still re-runs every step. Go keys each cache entry on the source, the flags and the toolchain, so the mount changes how long a step takes, not what it builds. Keep the default sharing=shared: Go's cache is safe for concurrent use, and sharing=locked would make every build on a host wait for the others. Mount Go's build cache only, never golangci-lint's own cache (#30).
Add -count=1 to both go test invocations of the test phase, and rewrite the paragraph that says the test phase needs none: with the mount, the phase does hold stored results. With a warm mount and today's flags, the test step took 3.8s and printed every tested package as (cached); no test ran.
Measurements
A throwaway build ran each step twice in a row on one mount:
step
empty cache
warm mount
test (-count=1)
127s
8s
lint
131s
15s
build
109s
not measured
Question for the owner
The mount saves time only where the builder keeps it between builds, and on the shared build host it does not. Three back-to-back make test builds each got a newly created, empty mount, and inside one script/cibuild the second lint and test steps were as slow as the first. With the change applied locally, make test took 99–174s and script/cibuild 503s, no better than without it. The builder runs with default settings (there is no daemon.json) and its build cache holds 327 GB, so its own cleanup most likely removes a mount that has been used once. Meeting either limit on this host therefore also needs its builder set to keep cache mounts, which is host configuration and not a repository file. Should the build host be configured that way? The reading taken here: propose the policy change regardless, since it costs nothing where the mount is dropped.
Definition of done
The canonical Go Dockerfile carries the three mounts and -count=1 on both test invocations, and the policy text says why -count=1 is there.
A planted failing test and a planted lint finding still fail script/cibuild with a warm mount.
sneak/bsfirehose#40 closes once bsfirehose has re-vendored this and both limits hold there.
Model: opus-5-5
Moved here from https://git.eeqj.de/sneak/bsfirehose/issues/40: the steps it would change are those of the canonical Go `Dockerfile` in `REPO_POLICIES.md`, so making the change in one repository would make that repository diverge from it.
## Problem
On bsfirehose `next` (`26a707f`, after https://git.eeqj.de/sneak/bsfirehose/issues/67 re-vendored the canonical files), two limits in `REPO_POLICIES.md` are broken: `script/cibuild` took 454s against the 5-minute limit, and `make test` took 158s against the 60-second cap. Every step that compiles starts from an empty Go build cache, so the lint, test and build steps each compile the whole dependency graph from source, the sqlite driver's C code included, and `script/cibuild` runs lint and test twice. Measured on the shared build host at a load of about 100–150 on 48 cores.
## Proposed change
- Add `--mount=type=cache,target=/root/.cache/go-build` to the lint, test and build `RUN` steps of the canonical Go `Dockerfile`. The mount keeps compiled packages, not step results: `--no-cache` still re-runs every step. Go keys each cache entry on the source, the flags and the toolchain, so the mount changes how long a step takes, not what it builds. Keep the default `sharing=shared`: Go's cache is safe for concurrent use, and `sharing=locked` would make every build on a host wait for the others. Mount Go's build cache only, never golangci-lint's own cache (https://git.eeqj.de/sneak/prompts/issues/30).
- Add `-count=1` to both `go test` invocations of the test phase, and rewrite the paragraph that says the test phase needs none: with the mount, the phase does hold stored results. With a warm mount and today's flags, the test step took 3.8s and printed every tested package as `(cached)`; no test ran.
## Measurements
A throwaway build ran each step twice in a row on one mount:
| step | empty cache | warm mount |
| ----------------- | ----------- | ------------ |
| test (`-count=1`) | 127s | 8s |
| lint | 131s | 15s |
| build | 109s | not measured |
## Question for the owner
The mount saves time only where the builder keeps it between builds, and on the shared build host it does not. Three back-to-back `make test` builds each got a newly created, empty mount, and inside one `script/cibuild` the second lint and test steps were as slow as the first. With the change applied locally, `make test` took 99–174s and `script/cibuild` 503s, no better than without it. The builder runs with default settings (there is no `daemon.json`) and its build cache holds 327 GB, so its own cleanup most likely removes a mount that has been used once. Meeting either limit on this host therefore also needs its builder set to keep cache mounts, which is host configuration and not a repository file. Should the build host be configured that way? The reading taken here: propose the policy change regardless, since it costs nothing where the mount is dropped.
## Definition of done
- The canonical Go `Dockerfile` carries the three mounts and `-count=1` on both test invocations, and the policy text says why `-count=1` is there.
- A planted failing test and a planted lint finding still fail `script/cibuild` with a warm mount.
- https://git.eeqj.de/sneak/bsfirehose/issues/40 closes once bsfirehose has re-vendored this and both limits hold there.
Model: opus-5-5
sneak ruled on #40 (#40 (comment)) that linting and testing run in Docker "and you can simply disable all caching". A Go build cache mount is caching kept between builds.
It also cannot work under the canonical scripts: on this host's Docker, --no-cache gives every BuildKit cache mount a fresh, empty directory (found for #87). That is why each of the measured builds above got a new, empty mount, so the builder's cleanup is not the cause and configuring the host would not change it.
What remains is that uncached builds break the time limits. That is now the owner question on #113, with bsfirehose's figures from this issue added there. sneak/bsfirehose#40 waits on that answer.
Model: opus-5-5
Closing: the record already decides this.
- sneak ruled on https://git.eeqj.de/sneak/prompts/issues/40 (https://git.eeqj.de/sneak/prompts/issues/40#issuecomment-54891) that linting and testing run in Docker "and you can simply disable all caching". A Go build cache mount is caching kept between builds.
- It also cannot work under the canonical scripts: on this host's Docker, `--no-cache` gives every BuildKit cache mount a fresh, empty directory (found for https://git.eeqj.de/sneak/prompts/pulls/87). That is why each of the measured builds above got a new, empty mount, so the builder's cleanup is not the cause and configuring the host would not change it.
What remains is that uncached builds break the time limits. That is now the owner question on https://git.eeqj.de/sneak/prompts/issues/113, with bsfirehose's figures from this issue added there. https://git.eeqj.de/sneak/bsfirehose/issues/40 waits on that answer.
Model: opus-5-5
clawbot
self-assigned this 2026-10-06 04:33:48 +02:00
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.
Moved here from sneak/bsfirehose#40: the steps it would change are those of the canonical Go
DockerfileinREPO_POLICIES.md, so making the change in one repository would make that repository diverge from it.Problem
On bsfirehose
next(26a707f, after sneak/bsfirehose#67 re-vendored the canonical files), two limits inREPO_POLICIES.mdare broken:script/cibuildtook 454s against the 5-minute limit, andmake testtook 158s against the 60-second cap. Every step that compiles starts from an empty Go build cache, so the lint, test and build steps each compile the whole dependency graph from source, the sqlite driver's C code included, andscript/cibuildruns lint and test twice. Measured on the shared build host at a load of about 100–150 on 48 cores.Proposed change
--mount=type=cache,target=/root/.cache/go-buildto the lint, test and buildRUNsteps of the canonical GoDockerfile. The mount keeps compiled packages, not step results:--no-cachestill re-runs every step. Go keys each cache entry on the source, the flags and the toolchain, so the mount changes how long a step takes, not what it builds. Keep the defaultsharing=shared: Go's cache is safe for concurrent use, andsharing=lockedwould make every build on a host wait for the others. Mount Go's build cache only, never golangci-lint's own cache (#30).-count=1to bothgo testinvocations of the test phase, and rewrite the paragraph that says the test phase needs none: with the mount, the phase does hold stored results. With a warm mount and today's flags, the test step took 3.8s and printed every tested package as(cached); no test ran.Measurements
A throwaway build ran each step twice in a row on one mount:
-count=1)Question for the owner
The mount saves time only where the builder keeps it between builds, and on the shared build host it does not. Three back-to-back
make testbuilds each got a newly created, empty mount, and inside onescript/cibuildthe second lint and test steps were as slow as the first. With the change applied locally,make testtook 99–174s andscript/cibuild503s, no better than without it. The builder runs with default settings (there is nodaemon.json) and its build cache holds 327 GB, so its own cleanup most likely removes a mount that has been used once. Meeting either limit on this host therefore also needs its builder set to keep cache mounts, which is host configuration and not a repository file. Should the build host be configured that way? The reading taken here: propose the policy change regardless, since it costs nothing where the mount is dropped.Definition of done
Dockerfilecarries the three mounts and-count=1on both test invocations, and the policy text says why-count=1is there.script/cibuildwith a warm mount.Model: opus-5-5
Closing: the record already decides this.
--no-cachegives every BuildKit cache mount a fresh, empty directory (found for #87). That is why each of the measured builds above got a new, empty mount, so the builder's cleanup is not the cause and configuring the host would not change it.What remains is that uncached builds break the time limits. That is now the owner question on #113, with bsfirehose's figures from this issue added there. sneak/bsfirehose#40 waits on that answer.
Model: opus-5-5