From 41ad87b3b9337d3618336fe19f9790deb2222a7e Mon Sep 17 00:00:00 2001 From: sneak Date: Tue, 6 Oct 2026 05:25:30 +0000 Subject: [PATCH] Keep Go's build cache between builds (closes #124) The Dockerfile runs make test and make build with Go's build cache in a BuildKit cache mount, so a build compiles only what changed since the last one instead of the standard library and every dependency from nothing. The mount has an id of its own, so builds of other repositories, compiled against other C headers, do not share it. script/test passes -count=1, so no test result is taken from the cache. Model: opus-5-5 --- Dockerfile | 10 ++++++++-- README.md | 6 ++++-- TODO.md | 10 ++++++++++ script/cibuild | 3 ++- script/test | 4 +++- 5 files changed, 27 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index d0afa31..16f0162 100644 --- a/Dockerfile +++ b/Dockerfile @@ -50,7 +50,12 @@ ARG CHECK_EPOCH COPY . . -RUN make test +# This cache mount keeps Go's build cache between builds for make test and +# make build; -count=1 in script/test keeps test results out of it. Go's cache +# does not notice C header changes, so the mount has its own id: change the id +# when the C packages installed above change. +RUN --mount=type=cache,id=sneak/secret/go-build,target=/root/.cache/go-build \ + make test # The version stamped into the binary: the VERSION build argument when one # is given, otherwise `git describe --tags --always` of the .git the build @@ -58,7 +63,8 @@ RUN make test # one, the short commit when no tag is reachable. A context that carries .git # and still yields no version fails the build. ARG VERSION -RUN version="${VERSION:-$(git describe --tags --always)}"; \ +RUN --mount=type=cache,id=sneak/secret/go-build,target=/root/.cache/go-build \ + version="${VERSION:-$(git describe --tags --always)}"; \ if [ -e .git ] && { [ -z "$version" ] || [ "$version" = dev ] || \ [ "$version" = unknown ]; }; then \ echo "no version could be derived although the build context carries .git" >&2; \ diff --git a/README.md b/README.md index 29d38f7..0f44d03 100644 --- a/README.md +++ b/README.md @@ -604,7 +604,8 @@ provide: - `script/build` — build the `secret` binary into the repo root, stamping the version (`VERSION` from the environment, else `git describe`) and the git commit -- `script/test` — run `go vet` and the test suite (verbose rerun on failure) +- `script/test` — run `go vet` and the test suite (verbose rerun on failure), + every test on every run, never a result from Go's test cache - `script/lint` — run `golangci-lint` in docker only: builds `Dockerfile.lint`, where the linter is a build step that runs on every call, also on an unchanged tree @@ -624,7 +625,8 @@ provide: - `script/cibuild` — CI entrypoint: `docker build --ulimit memlock=-1:-1 .` (memguard needs mlock; the Dockerfile runs the checks), with a new `CHECK_EPOCH` build argument on every run so the checks run again on an - unchanged tree + unchanged tree; the `Dockerfile` keeps Go's build cache between builds, so + `make test` and `make build` compile only what changed - `script/precommit` — pre-commit checks: `go mod tidy` verification, then `script/check` - `script/install-precommit` — install the git pre-commit hook that runs diff --git a/TODO.md b/TODO.md index 37ea5a0..644b927 100644 --- a/TODO.md +++ b/TODO.md @@ -18,6 +18,16 @@ https://git.eeqj.de/sneak/secret/milestone/12 # Completed Steps +- 2026-10-06: `make test` in `script/cibuild` no longer compiles the standard + library and every dependency from nothing on every build + (https://git.eeqj.de/sneak/secret/issues/124). The `Dockerfile` runs it and + `make build` with Go's build cache in a BuildKit cache mount, which docker + keeps between builds, so each compiles only what changed since the last build. + The mount has an id of its own, so other repositories' builds do not share it. + `script/test` passes `-count=1`, so every test runs on every build and no + result comes from Go's test cache. A build with an empty cache, such as the + first after docker's build cache is cleared, compiles everything in + `make test` as before. - 2026-10-06: The tests run quickly with the race detector on (https://git.eeqj.de/sneak/secret/issues/120). Most of their time went to deriving keys from passphrases with scrypt, which is slow on purpose. The new diff --git a/script/cibuild b/script/cibuild index bb21515..feaf540 100755 --- a/script/cibuild +++ b/script/cibuild @@ -6,7 +6,8 @@ # the lower limit of a plain `docker build .`. # A cached build checks nothing: a new CHECK_EPOCH on every run makes the # Dockerfile's check steps run again on an unchanged tree, while its base -# images and module downloads stay cached. +# images, module downloads and the Go build cache that make test and make +# build use stay cached. set -eu ROOT="$(cd "$(dirname "$0")/.." && pwd -P)" diff --git a/script/test b/script/test index 3735b43..7a48c8b 100755 --- a/script/test +++ b/script/test @@ -9,7 +9,9 @@ main() { # CGO is required (Makefile exports this too) export CGO_ENABLED=1 go vet ./... - go test ./... || go test -v ./... + # -count=1: run every test, never take a result from Go's test cache, + # which the Dockerfile keeps between builds + go test -count=1 ./... || go test -count=1 -v ./... } main "$@"