Author SHA1 Message Date
sneak bab59b474a Set the lint digest and .golangci.yml together; state the Go version rule
check / check (push) Successful in 29s
The canonical .golangci.yml now names exhaustruct_v5, which v2.12.2
rejects, so a repo that re-vendors the file on the old digest gets a
lint phase that fails before checking any code. REPO_POLICIES now says
a repo sets the lint phase digest and re-vendors .golangci.yml in one
commit, whichever prompted the change, and both repo checklists point
to that rule. It also replaces "a new Go version needs a golangci-lint
built with it" with the rule golangci-lint applies: the go directive
must not name a newer Go minor version than the one it was built with.

Model: opus-5-5
2026-10-03 15:42:32 +00:00
sneak 10729365de Pin golangci-lint v2.14.0; disable exhaustruct_v5 (closes #65)
v2.12.2 is built with go1.26 and refuses to lint a module whose go
directive is 1.27 or later. v2.14.0 is built with go1.27. Releases
from v2.13.0 deprecate exhaustruct in favour of exhaustruct_v5, which
default: all switches on and which reports every partial struct
literal, so the canonical config disables it beside exhaustruct for
the same reason. REPO_POLICIES now names the new image digest and says
that a repo moving to a new version re-vendors .golangci.yml with it.

Model: opus-5-5
2026-10-03 15:42:23 +00:00
5 changed files with 41 additions and 44 deletions
+1
View File
@@ -17,6 +17,7 @@ linters:
disable:
# Genuinely incompatible with project patterns
- exhaustruct # Requires all struct fields
- exhaustruct_v5 # Requires all struct fields (successor to exhaustruct)
- godot # Requires comments to end with periods
- wrapcheck # Too verbose for internal packages
- varnamelen # Short names like db, id are idiomatic Go
+7 -7
View File
@@ -21,13 +21,13 @@ fmt-check, and commit.
# Completed Steps
- 2026-10-03: Fixed two defects in the canonical Go `Dockerfile` example (issue
73). The test phase now uses the Debian Go image, since `-race` needs cgo and
the alpine image has no C compiler, so the phase failed before running a test.
The stage that compiles runs `git config --system --add safe.directory /src`,
because a context sent as a tar stream keeps the sender's file owners and git
refuses that checkout, leaving the version empty. Both checklists state that
step in the same words.
- 2026-10-03: Moved the canonical golangci-lint to v2.14.0, built with go1.27,
because v2.12.2 refuses to lint a module whose `go` directive is 1.27 (issue
65). Releases from v2.13.0 deprecate `exhaustruct` in favour of
`exhaustruct_v5`, which `default: all` switches on, so the canonical
`.golangci.yml` now disables `exhaustruct_v5` beside `exhaustruct`. v2.12.2
rejects that file, so `REPO_POLICIES.md` and both repo checklists now say a
repo sets the lint phase digest and re-vendors `.golangci.yml` in one commit.
- 2026-10-03: Brought the canonical `.gitignore` level with `.dockerignore` on
secrets (issue 38): it now also ignores `prod.env`-style `*.env` files,
`.envrc`, `*.p12`, `*.pfx` and the extensionless SSH private keys, written to
+7 -9
View File
@@ -67,14 +67,10 @@ with your task.
argument when one is given, otherwise from `git describe --tags --always`.
That gives the tag on a tagged commit; on a later commit, the tag, the
number of commits since it and the short commit (`v1.2.3-4-gabc1234`); and
the short commit when no tag is reachable. The stage that compiles also
marks its working directory safe for git
(`git config --system --add safe.directory /src`): a context sent as a tar
stream keeps the sender's file owners, and git refuses a checkout owned by
another user, so the version would come out empty. `ARG VERSION` has no
default, and the build fails if the context carries `.git` and the version
still comes out empty, `dev` or `unknown`. A plain `docker build .` with
no build arguments must succeed; a Dockerfile that refuses an empty build
the short commit when no tag is reachable. `ARG VERSION` has no default,
and the build fails if the context carries `.git` and the version still
comes out empty, `dev` or `unknown`. A plain `docker build .` with no
build arguments must succeed; a Dockerfile that refuses an empty build
argument drops that refusal and keeps the argument. `script/docker` and
`script/cibuild` pass the version they compute on the host; it takes
precedence. A tag-derived version additionally needs `fetch-depth: 0` on
@@ -84,7 +80,9 @@ with your task.
`https://git.eeqj.de/sneak/prompts/raw/branch/main/.gitea/workflows/check.yml`
- [ ] Language-specific config:
- [ ] Go: `go.mod`, `go.sum`, `.golangci.yml` (fetch from
`https://git.eeqj.de/sneak/prompts/raw/branch/main/.golangci.yml`)
`https://git.eeqj.de/sneak/prompts/raw/branch/main/.golangci.yml` and,
in the same commit, set the lint phase digest to the one named in the
`.golangci.yml` paragraph of `REPO_POLICIES.md`)
- [ ] JS: `package.json`, `yarn.lock`, `.prettierrc`, `.prettierignore`
(fetch from
`https://git.eeqj.de/sneak/prompts/raw/branch/main/.prettierrc` and
+7 -9
View File
@@ -76,14 +76,10 @@ Template files can be fetched from:
argument when one is given, otherwise from `git describe --tags --always`.
That gives the tag on a tagged commit; on a later commit, the tag, the
number of commits since it and the short commit (`v1.2.3-4-gabc1234`); and
the short commit when no tag is reachable. The stage that compiles also
marks its working directory safe for git
(`git config --system --add safe.directory /src`): a context sent as a tar
stream keeps the sender's file owners, and git refuses a checkout owned by
another user, so the version would come out empty. `ARG VERSION` has no
default, and the build fails if the context carries `.git` and the version
still comes out empty, `dev` or `unknown`. A plain `docker build .` with
no build arguments must succeed; a Dockerfile that refuses an empty build
the short commit when no tag is reachable. `ARG VERSION` has no default,
and the build fails if the context carries `.git` and the version still
comes out empty, `dev` or `unknown`. A plain `docker build .` with no
build arguments must succeed; a Dockerfile that refuses an empty build
argument drops that refusal and keeps the argument.
- The Dockerfile carries a `lint` phase and a `test` phase, each invoking
its tool directly rather than through `make` or `script/`, and the final
@@ -98,7 +94,9 @@ Template files can be fetched from:
`https://git.eeqj.de/sneak/prompts/raw/branch/main/.gitea/workflows/check.yml`
- [ ] Language-specific:
- [ ] Go: `go mod init sneak.berlin/go/<name>`, `.golangci.yml` (fetch from
`https://git.eeqj.de/sneak/prompts/raw/branch/main/.golangci.yml`)
`https://git.eeqj.de/sneak/prompts/raw/branch/main/.golangci.yml` and,
in the same commit, set the lint phase digest to the one named in the
`.golangci.yml` paragraph of `REPO_POLICIES.md`)
- [ ] JS: `yarn init`, `yarn add --dev prettier`
- [ ] Python: `pyproject.toml`
+19 -19
View File
@@ -160,7 +160,7 @@ style conventions are in separate documents:
- **The gate phases are separate stages, and the build stage depends on both.**
The lint phase is based on the `golangci/golangci-lint` image (pinned by
hash), so lint failures surface in seconds rather than after a full compile,
and the test phase is based on the Debian Go image. The canonical Go repo
and the test phase is based on the Go image. The canonical Go repo
`Dockerfile`:
```dockerfile
@@ -173,9 +173,8 @@ style conventions are in separate documents:
COPY . .
RUN golangci-lint run --config .golangci.yml ./...
# Test phase. -race needs cgo and so a C compiler, which the Debian Go
# image ships and the alpine one does not.
# golang:1.x, YYYY-MM-DD
# Test phase
# golang:1.x-alpine, YYYY-MM-DD
FROM golang@sha256:... AS test
WORKDIR /src
COPY go.mod go.sum ./
@@ -193,8 +192,6 @@ style conventions are in separate documents:
COPY --from=lint /src/go.sum /dev/null
COPY --from=test /src/go.sum /dev/null
RUN apk add --no-cache git
# A tar-stream context keeps the sender's file owners, which git refuses.
RUN git config --system --add safe.directory /src
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
@@ -247,14 +244,11 @@ style conventions are in separate documents:
`git describe --tags --always`. That gives the tag on a tagged commit; on
a later commit, the tag, the number of commits since it and the short
commit (`v1.2.3-4-gabc1234`); and the short commit when no tag is
reachable. The stage that compiles also marks its working directory safe
for git (`git config --system --add safe.directory /src`): a context sent
as a tar stream keeps the sender's file owners, and git refuses a checkout
owned by another user, so the version would come out empty. `ARG VERSION`
has no default, and the build fails if the context carries `.git` and the
version still comes out empty, `dev` or `unknown`. A plain
`docker build .` with no build arguments must succeed; a Dockerfile that
refuses an empty build argument drops that refusal and keeps the argument.
reachable. `ARG VERSION` has no default, and the build fails if the
context carries `.git` and the version still comes out empty, `dev` or
`unknown`. A plain `docker build .` with no build arguments must succeed;
a Dockerfile that refuses an empty build argument drops that refusal and
keeps the argument.
- Every repo should have a Gitea Actions workflow (`.gitea/workflows/`) that
runs `script/cibuild` on push, and checks out the repo as its only other step.
@@ -457,12 +451,18 @@ style conventions are in separate documents:
`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
re-vendor carries its entries forward. The canonical golangci-lint version is
v2.12.2 (released 2026-05-06), pinned as the digest of the lint phase's base
v2.14.0 (released 2026-09-24), pinned as the digest of the lint phase's base
image
(`golangci/golangci-lint@sha256:5cceeef04e53efe1470638d4b4b4f5ceefd574955ab3941b2d9a68a8c9ad5240`,
which reports `2.12.2 built with go1.26.2 from c0d3ddc9`). That digest is the
only pin, since no repo installs golangci-lint on the host: bumping the
version means changing it and nothing else.
(`golangci/golangci-lint@sha256:ad862ba6b3798cbe0fd9fd7408d498fd74fbd2623a92406b2fd3898faf0bf98f`,
which reports `2.14.0 built with go1.27.0 from 114493f9`). A module's `go`
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
`go 1.27.1` but not `go 1.28`. That digest is the only pin, since no repo
installs golangci-lint on the host. A repo sets the lint phase digest to the
one named here and re-vendors `.golangci.yml` in the same commit, whichever of
the two prompted the change: the canonical copy can name linters that an older
golangci-lint rejects, and a newer golangci-lint can add linters that
`default: all` switches on until the canonical copy disables them.
- **`script/bootstrap` installs a pinned tool by comparing versions, never by
testing presence.** An `if ! command -v <tool>; then install; fi` guard tests