From 9b3d7326ce30a4984db67dfe2709801fd007457a Mon Sep 17 00:00:00 2001 From: clawbot <35+clawbot@noreply.example.org> Date: Tue, 6 Oct 2026 13:48:23 +0200 Subject: [PATCH] Run the tests under the race detector, in Docker (closes #23) script/test is now the standard script: it builds only the test stage of the Dockerfile, without the build cache, so tests no longer run on the host. That stage calls go test -race -cover directly, with a verbose rerun on failure, on the Debian-based golang image, which has the C toolchain -race needs. The test stage no longer copies from the lint stage, so script/test builds the tests alone. A new final stage copies one file from each of the two stages; that is what makes a plain docker build, and so script/cibuild, run both. The README entry for script/test and TODO.md say what now runs. Model: opus-5-5 --- Dockerfile | 17 ++++++++++++----- README.md | 4 +++- TODO.md | 4 ++++ script/test | 13 ++++++++++--- 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/Dockerfile b/Dockerfile index d7d21fe..61f5e60 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,13 +9,20 @@ RUN make fmt-check # Called directly: make lint is itself a docker build of this stage. RUN golangci-lint run --config .golangci.yml ./... -# Test stage: run full test suite -# golang 1.22.12 (2025-02-04) +# Test stage: run full test suite under the race detector +# golang 1.22.12 (Debian-based; -race needs its C toolchain), 2025-02-04 FROM golang@sha256:1cf6c45ba39db9fd6db16922041d074a63c935556a05c5ccb62d181034df7f02 AS test -# Depend on lint stage so both stages always run -COPY --from=lint /src/go.sum /dev/null WORKDIR /src COPY go.mod go.sum ./ RUN go mod download COPY . . -RUN make test +# Called directly: make test is itself a docker build of this stage. +RUN go test -timeout 90s -race -cover ./... || \ + { echo "--- Rerunning with -v for details ---"; \ + go test -timeout 90s -race -v ./...; exit 1; } + +# Final stage: the copies make a plain docker build run both stages above. +# golang 1.22.12 (2025-02-04) +FROM golang@sha256:1cf6c45ba39db9fd6db16922041d074a63c935556a05c5ccb62d181034df7f02 +COPY --from=lint /src/go.sum /dev/null +COPY --from=test /src/go.sum /dev/null diff --git a/README.md b/README.md index 5d0d7f5..d9aa4b8 100644 --- a/README.md +++ b/README.md @@ -145,7 +145,9 @@ alpine. We provide: `script/bootstrap`, then `script/install-precommit` - `script/projectname` — output the project name (our own extension); used by `script/docker` for the image tag -- `script/test` — run the test suite (`go test -v ./...`) +- `script/test` — run the test suite under the race detector in Docker by + building only the `test` stage of the `Dockerfile`, without the build cache, + so every run tests; the image is tagged `simplelog-test` - `script/lint` — run golangci-lint in Docker by building only the `lint` stage of the `Dockerfile` (which also runs the format check), without the build cache, so every run lints; the image is tagged `simplelog-lint` diff --git a/TODO.md b/TODO.md index 4a3b4ec..b16ca6a 100644 --- a/TODO.md +++ b/TODO.md @@ -24,6 +24,10 @@ files it depends on: .golangci.yml, REPO_POLICIES.md, .editorconfig, # Completed Steps +* 2026-10-06: the tests run under the race detector, in Docker: + `script/test` builds the `test` stage of the `Dockerfile`, which runs + `go test -race`, and a new final stage makes a plain `docker build` + run both the `lint` and `test` stages * 2026-10-06: the linter runs only in Docker: `script/lint` builds the `lint` stage of the `Dockerfile`, every `docker build` in `script/` runs without the build cache, and `script/bootstrap` no longer diff --git a/script/test b/script/test index 98bd019..cd239f2 100755 --- a/script/test +++ b/script/test @@ -1,12 +1,19 @@ #!/bin/sh -# script/test: run the test suite. +# script/test: run the test suite. Testing is a phase of the Dockerfile +# and this builds that phase alone, on the same terms as script/lint: +# --target because a phase that is not the last stage is built only when +# named, --no-cache because a cached test layer is a test that did not +# run, and a tag so each build replaces the previous image. set -eu -ROOT="$(cd "$(dirname "$0")/.." && pwd -P)" +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)" +ROOT="$(cd "$SCRIPT_DIR/.." && pwd -P)" main() { cd "$ROOT" - go test -v ./... + docker build --no-cache \ + --target test \ + -t "$("$SCRIPT_DIR/projectname")-test" . } main "$@"