Author SHA1 Message Date
sneak b910a1e808 Move the canonical golangci-lint to v2.15.0 and bring back canonicalheader
check / check (push) Canceled after 0s
v2.15.0 (released 2026-10-16, built with go1.27.0 like v2.14.0) bundles
canonicalheader with its upstream fix for non-deterministic results, the
defect behind issue 105. Linted 20 times each on a scratch package with
two planted findings beside a ResponseWriter.Header() call, v2.14.0
reported them on 8 runs and nothing on 12; v2.15.0 reported them on all
20. REPO_POLICIES.md names the new digest; .golangci.yml no longer
disables canonicalheader and is otherwise unchanged, since it verifies
against v2.15.0 as it was and default: all switches on no new linter.
2026-11-01 16:40:35 -08:00
5 changed files with 34 additions and 79 deletions

No files matched your search

-2
View File
@@ -25,8 +25,6 @@ linters:
# silenced by disabling that name, not by enabling the successor. # silenced by disabling that name, not by enabling the successor.
- wsl # Deprecated, replaced by wsl_v5 - wsl # Deprecated, replaced by wsl_v5
- gomodguard # Deprecated, replaced by gomodguard_v2 - gomodguard # Deprecated, replaced by gomodguard_v2
# Misses findings at random in v2.14.0; back once a pinned release fixes it
- canonicalheader
settings: settings:
lll: lll:
line-length: 88 line-length: 88
+9 -10
View File
@@ -21,16 +21,15 @@ fmt-check, and commit.
# Completed Steps # Completed Steps
- 2026-11-03: A first test run that timed out now gets no verbose rerun - 2026-11-01: Moved the canonical golangci-lint to v2.15.0 (released
(https://git.eeqj.de/sneak/prompts/issues/61), on sneak's approval at 2026-10-16), still built with go1.27.0, so the `go` directive rule is
https://git.eeqj.de/sneak/prompts/issues/61#issuecomment-165195. The Go recipe unchanged. The canonical `.golangci.yml` passes `golangci-lint config verify`
in `REPO_POLICIES.md` looks in the first run's output for on v2.15.0, and `default: all` switches on no new linter. `canonicalheader` is
`panic: test timed out` or `*** Test killed`, and exits 1 when either is enabled again (issue 105): v2.15.0 carries the upstream commit "fix:
there. The Python `make test` example takes the same `timeout 90 sh -c` non-deterministic results", and on a scratch package with two planted findings
wrapper as its `Dockerfile` example. The 60 and 90 second rule and the beside a `ResponseWriter.Header()` call, linted 20 times with each pinned
5-minute rule now say a hung package uses the backstop once. The Go item in image, v2.14.0 reported them on 8 runs and nothing on 12, while v2.15.0
both repo checklists now asks for this, so a repository with the old recipe reported them on all 20.
fails it.
- 2026-10-27: Added `prompts/API_POLICIES.md` - 2026-10-27: Added `prompts/API_POLICIES.md`
(https://git.eeqj.de/sneak/prompts/issues/137). It holds sneak's rule from (https://git.eeqj.de/sneak/prompts/issues/137). It holds sneak's rule from
https://git.eeqj.de/sneak/routewatch/issues/199 that a value in an API https://git.eeqj.de/sneak/routewatch/issues/199 that a value in an API
+3 -4
View File
@@ -1,6 +1,6 @@
--- ---
title: Existing Repo Checklist title: Existing Repo Checklist
last_modified: 2026-11-03 last_modified: 2026-10-26
--- ---
Use this checklist when beginning work in a repo that may not yet conform to our Use this checklist when beginning work in a repo that may not yet conform to our
@@ -70,9 +70,8 @@ with your task.
(`--mount=type=cache,target=/root/.cache/go-build`), `golangci-lint run` (`--mount=type=cache,target=/root/.cache/go-build`), `golangci-lint run`
also mounts golangci-lint's also mounts golangci-lint's
(`--mount=type=cache,target=/root/.cache/golangci-lint`), and no step (`--mount=type=cache,target=/root/.cache/golangci-lint`), and no step
mounts anything else; both `go test` runs carry `-count=1`; a first run mounts anything else; both `go test` runs carry `-count=1`, as in the
that timed out gets no rerun, as in the canonical Go `Dockerfile` in canonical Go `Dockerfile` in `REPO_POLICIES.md`
`REPO_POLICIES.md`
- [ ] Python: a `test` phase that installs its packages from `Pipfile.lock` does - [ ] Python: a `test` phase that installs its packages from `Pipfile.lock` does
it above `ARG CACHEBUST`, with `COPY script/ script/`, it above `ARG CACHEBUST`, with `COPY script/ script/`,
`COPY Pipfile Pipfile.lock ./` and `RUN script/bootstrap`. When `COPY Pipfile Pipfile.lock ./` and `RUN script/bootstrap`. When
+3 -4
View File
@@ -1,6 +1,6 @@
--- ---
title: New Repo Checklist title: New Repo Checklist
last_modified: 2026-11-03 last_modified: 2026-10-26
--- ---
Use this checklist when creating a new repository from scratch. Follow the steps Use this checklist when creating a new repository from scratch. Follow the steps
@@ -129,9 +129,8 @@ Template files can be fetched from:
(`--mount=type=cache,target=/root/.cache/go-build`), `golangci-lint run` (`--mount=type=cache,target=/root/.cache/go-build`), `golangci-lint run`
also mounts golangci-lint's also mounts golangci-lint's
(`--mount=type=cache,target=/root/.cache/golangci-lint`), and no step (`--mount=type=cache,target=/root/.cache/golangci-lint`), and no step
mounts anything else; both `go test` runs carry `-count=1`; a first run mounts anything else; both `go test` runs carry `-count=1`, as in the
that timed out gets no rerun, as in the canonical Go `Dockerfile` in canonical Go `Dockerfile` in `REPO_POLICIES.md`
`REPO_POLICIES.md`
- Python: a `test` phase that installs its packages from `Pipfile.lock` does - Python: a `test` phase that installs its packages from `Pipfile.lock` does
it above `ARG CACHEBUST`, with `COPY script/ script/`, it above `ARG CACHEBUST`, with `COPY script/ script/`,
`COPY Pipfile Pipfile.lock ./` and `RUN script/bootstrap`. When `COPY Pipfile Pipfile.lock ./` and `RUN script/bootstrap`. When
+19 -59
View File
@@ -1,6 +1,6 @@
--- ---
title: Repository Policies title: Repository Policies
last_modified: 2026-11-03 last_modified: 2026-11-01
--- ---
This document covers repository structure, tooling, and workflow standards. Code This document covers repository structure, tooling, and workflow standards. Code
@@ -233,8 +233,6 @@ style conventions are in separate documents:
# Test phase. -race needs cgo and so a C compiler, which the Debian Go # Test phase. -race needs cgo and so a C compiler, which the Debian Go
# image ships and the alpine one does not. -count=1 because the mounted # image ships and the alpine one does not. -count=1 because the mounted
# cache also holds test results, which go test would otherwise replay. # cache also holds test results, which go test would otherwise replay.
# A first run that timed out gets no rerun, because a rerun would only
# wait for the same hang.
# golang:1.x, YYYY-MM-DD # golang:1.x, YYYY-MM-DD
FROM golang@sha256:... AS test FROM golang@sha256:... AS test
WORKDIR /src WORKDIR /src
@@ -243,16 +241,9 @@ style conventions are in separate documents:
ARG CACHEBUST ARG CACHEBUST
COPY . . COPY . .
RUN --mount=type=cache,target=/root/.cache/go-build \ RUN --mount=type=cache,target=/root/.cache/go-build \
status=0; \ go test -count=1 -timeout 90s -race -cover ./... || \
output=$(go test -count=1 -timeout 90s -race -cover ./... 2>&1) || status=$?; \ { echo "--- Rerunning with -v for details ---"; \
printf '%s\n' "$output"; \ go test -count=1 -timeout 90s -race -v ./...; exit 1; }
test "$status" -eq 0 && exit 0; \
case "$output" in \
*"panic: test timed out"*|*"*** Test killed"*) exit 1 ;; \
esac; \
echo "--- Rerunning with -v for details ---"; \
go test -count=1 -timeout 90s -race -v ./...; \
exit 1
# Build stage. Nothing is wanted from either phase above; the copies # Build stage. Nothing is wanted from either phase above; the copies
# are what make BuildKit build them first, so nothing after them can # are what make BuildKit build them first, so nothing after them can
@@ -422,8 +413,7 @@ style conventions are in separate documents:
`pipenv install --dev --deploy`: `pipenv install --dev --deploy`:
```dockerfile ```dockerfile
# Test phase. Both pytest runs share one 90-second timeout, so a hang # Test phase. The 90-second timeout covers both pytest runs.
# uses it once and gets no rerun.
# python:3.x-slim, YYYY-MM-DD # python:3.x-slim, YYYY-MM-DD
FROM python@sha256:... AS test FROM python@sha256:... AS test
# make and git are base tools that script/bootstrap installs when they # make and git are base tools that script/bootstrap installs when they
@@ -494,19 +484,15 @@ style conventions are in separate documents:
improvement bug against that repo. Add a 90-second timeout to the test improvement bug against that repo. Add a 90-second timeout to the test
invocation (`go test -timeout 90s`). The backstop deliberately sits above the invocation (`go test -timeout 90s`). The backstop deliberately sits above the
hard cap so that it catches a genuinely hung test rather than a merely slow hard cap so that it catches a genuinely hung test rather than a merely slow
one. A hung package uses the backstop once, because a first run that timed out one.
gets no rerun. `-timeout` applies to each package, so several hung packages
can each use it.
- **The test command should use the conditional verbose rerun pattern.** Run - **The test command should use the conditional verbose rerun pattern.** Run
tests without `-v` (verbose) first. If tests fail, automatically rerun with tests without `-v` (verbose) first. If tests fail, automatically rerun with
`-v` to show full output. This keeps CI logs and `docker build` output clean `-v` to show full output. This keeps CI logs and `docker build` output clean
on success (just package/suite summaries) while providing full diagnostic on success (just package/suite summaries) while providing full diagnostic
detail on failure (every test case, every assertion). A first run that timed detail on failure (every test case, every assertion). The command lives in the
out gets no rerun, because the rerun would only wait for the same hang. The `test` phase of the `Dockerfile`, since `script/test` builds that phase; the
command lives in the `test` phase of the `Dockerfile`, since `script/test` Makefile form below is the same pattern for any repo-local invocation:
builds that phase; the Makefile form below is the same pattern for any
repo-local invocation:
```makefile ```makefile
test: test:
@@ -519,29 +505,11 @@ style conventions are in separate documents:
```makefile ```makefile
test: test:
@status=0; \ @go test -count=1 -timeout 90s -race -cover ./... || \
output=$$(go test -count=1 -timeout 90s -race -cover ./... 2>&1) || status=$$?; \ { echo "--- Rerunning with -v for details ---"; \
printf '%s\n' "$$output"; \ go test -count=1 -timeout 90s -race -v ./...; exit 1; }
test "$$status" -eq 0 && exit 0; \
case "$$output" in \
*"panic: test timed out"*|*"*** Test killed"*) exit 1 ;; \
esac; \
echo "--- Rerunning with -v for details ---"; \
go test -count=1 -timeout 90s -race -v ./...; \
exit 1
``` ```
The first run's output is kept so that the recipe can look in it for what Go
prints for a hang:
- `panic: test timed out` means the test's own timer fired.
- `*** Test killed` means `go test` stopped a test that ignored that timer,
about a minute later.
Go prints the goroutine stacks with either line, as long as the test can
still respond. A rerun would only wait for the same hang, so the recipe
exits 1. A failure with neither line in its output reruns with `-v`. The
kept output prints when the first run ends, not while it runs.
`-count=1` is required on both invocations: it defeats Go's test _result_ `-count=1` is required on both invocations: it defeats Go's test _result_
cache, so neither run can report a stored pass in place of running the cache, so neither run can report a stored pass in place of running the
tests. It leaves the build cache alone, so it costs the runtime of the suite tests. It leaves the build cache alone, so it costs the runtime of the suite
@@ -560,25 +528,17 @@ style conventions are in separate documents:
```makefile ```makefile
test: test:
@timeout 90 sh -c 'python -m pytest || { \ @python -m pytest || \
echo "--- Rerunning with -v for details ---"; \ { echo "--- Rerunning with -v for details ---"; \
python -m pytest -v; exit 1; }' python -m pytest -v; exit 1; }
``` ```
Both pytest runs share that one 90-second timeout, as in the `Dockerfile`
example above, so a hang uses it once and gets no rerun. Ctrl-C does not
stop a running `make test`, which goes on until the tests end or the 90
seconds run out. On a Mac without `timeout`, the target fails until it is
installed.
The `exit 1` ensures the target always fails after a rerun — the first run The `exit 1` ensures the target always fails after a rerun — the first run
already proved the tests are broken, so the build must not pass even if a already proved the tests are broken, so the build must not pass even if a
flaky test happens to succeed on the second attempt. The rerun exists solely flaky test happens to succeed on the second attempt. The rerun exists solely
for diagnostic output. for diagnostic output.
- Docker builds must complete in under 5 minutes. A hung package uses the - Docker builds must complete in under 5 minutes.
90-second test backstop once, because a first run that timed out gets no
rerun.
- `make check` must not modify any files in the repo. Tests may use temporary - `make check` must not modify any files in the repo. Tests may use temporary
directories. directories.
@@ -698,10 +658,10 @@ style conventions are in separate documents:
`test-support` depguard rule, where a repo names its own test-support packages `test-support` depguard rule, where a repo names its own test-support packages
by full import path. A repo adds entries there and changes nothing else, and a by full import path. A repo adds entries there and changes nothing else, and a
re-vendor carries its entries forward. The canonical golangci-lint version is re-vendor carries its entries forward. The canonical golangci-lint version is
v2.14.0 (released 2026-09-24), pinned as the digest of the lint phase's base v2.15.0 (released 2026-10-16), pinned as the digest of the lint phase's base
image image
(`golangci/golangci-lint@sha256:ad862ba6b3798cbe0fd9fd7408d498fd74fbd2623a92406b2fd3898faf0bf98f`, (`golangci/golangci-lint@sha256:fbfcfd12a6f8d7f1b97b3362ca80fc2a244d46863148c73cb88e7793af96c481`,
which reports `2.14.0 built with go1.27.0 from 114493f9`). A module's `go` which reports `2.15.0 built with go1.27.0 from 25c5369a`). A module's `go`
directive must not name a newer Go minor version than the one golangci-lint directive must not name a newer Go minor version than the one golangci-lint
was built with, or golangci-lint refuses to lint it: this release lints was built with, or golangci-lint refuses to lint it: this release lints
`go 1.27.1` but not `go 1.28`. That digest is the only pin, since no repo `go 1.27.1` but not `go 1.28`. That digest is the only pin, since no repo