Author SHA1 Message Date
sneak 03f78f0af3 Fall back to dev when git describe prints nothing (closes #74)
check / check (push) Waiting to run
The Makefile example in the Go styleguide and in the HTTP server conventions
set VERSION from `git describe --tags --always` alone. Where that prints
nothing (outside a git checkout, or where git is missing or refuses the
checkout), the binary was stamped with an empty version and nothing said so.
Both now read
`VERSION ?= $(or $(shell git describe --tags --always 2>/dev/null),dev)`, and
the comment above each names that condition. A `VERSION` from the environment
or the make command line still takes precedence.

Model: opus-5-5
2026-10-04 03:15:25 +00:00
clawbot dcc0ba0b66 Fix git ownership and -race in the canonical Go Dockerfile (closes #73)
check / check (push) Waiting to run
The canonical Go `Dockerfile` example in `prompts/REPO_POLICIES.md` had two defects.

The test phase ran `go test -race` on the alpine Go image, which has no C compiler, so `-race` failed before any test ran. The test phase now uses the Debian Go image, pinned by digest like the others.

A build context sent as a tar stream keeps the sender's file owners, so git refused the checkout and the version step failed the build. The builder stage now runs `git config --system --add safe.directory /src`; the policy and both checklists say why in the same words.

The builder's `apk add --no-cache git` line is unchanged: whether it must be pinned is the open owner question on #72.

Model: opus-5-5
2026-10-04 04:31:51 +02:00
clawbot 343628fb3a Pin golangci-lint v2.14.0; disable exhaustruct_v5 (closes #65)
check / check (push) Waiting to run
The canonical golangci-lint moves from v2.12.2 to v2.14.0, built with go1.27: v2.12.2 refuses a module whose `go` directive names 1.27 or later. The policy now states the rule: the `go` directive must not name a newer Go minor version than the one golangci-lint was built with.

From v2.13.0, `default: all` turns on `exhaustruct_v5`, the successor of the deprecated `exhaustruct`. `.golangci.yml` disables it beside the old name, which stays listed or its deprecation warning returns.

v2.12.2 rejects the new `.golangci.yml`, so a repo changes the lint phase digest and re-vendors `.golangci.yml` in one commit; both checklists point to that rule.

Model: opus-5-5
2026-10-04 03:32:08 +02:00
clawbot 7ea5cdcdcd Cover more secret shapes in the canonical .gitignore (closes #38)
check / check (push) Successful in 23s
The canonical .gitignore matched only .env, .env.*, *.pem and *.key, so prod.env, .envrc, *.p12 and *.pfx bundles, and the SSH private keys id_rsa, id_dsa, id_ecdsa and id_ed25519 could be committed. The secrets section now covers the same shapes as the canonical .dockerignore, written to gitignore's own rules: unanchored, no **/ prefix, character ranges for case. example.env and sample.env stay trackable through negations, and the comment tells a repository to add its own negation for any other committed template.

Judgement call: the existing entries were rewritten with character ranges, which only widens them, and the bare .env line is dropped because *.env covers it.

Model: opus-5-5
2026-10-03 17:39:40 +02:00
8 changed files with 108 additions and 37 deletions
+23 -5
View File
@@ -20,8 +20,26 @@ Thumbs.db
# Node
node_modules/
# Environment / secrets
.env
.env.*
*.pem
*.key
# Secrets. Unanchored like every entry above, so each matches at every
# depth. Matching is case-sensitive on Linux, so names use character
# ranges rather than a lowercase form that misses `Server.Key`.
# Environment files. `*.env` covers bare `.env` and the `prod.env`
# convention. Only the templates `example.env` and `sample.env` are
# re-included below. A repository that commits any other template adds
# its own negation after these lines, for example `!.env.example`.
*.[eE][nN][vV]
.[eE][nN][vV].*
.[eE][nN][vV][rR][cC]
!example.env
!sample.env
# Private keys and the bundles carrying them.
*.[pP][eE][mM]
*.[kK][eE][yY]
*.[pP]12
*.[pP][fF][xX]
[iI][dD]_[rR][sS][aA]
[iI][dD]_[dD][sS][aA]
[iI][dD]_[eE][cC][dD][sS][aA]
[iI][dD]_[eE][dD]25519
+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
+24
View File
@@ -21,6 +21,30 @@ fmt-check, and commit.
# Completed Steps
- 2026-10-04: The Makefile examples in the Go styleguide and the HTTP server
conventions now fall back to `dev` when `git describe` prints nothing (outside
a git checkout, or where git is missing or refuses the checkout), instead of
stamping an empty version (issue 74). The canonical `Dockerfile` already fails
on a `dev` version when `.git` is in the build context.
- 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
`.gitignore`'s own rules (no `**/` prefix) and case-folded with character
ranges. `example.env` and `sample.env` stay trackable through negations.
- 2026-10-02: The image version now comes from git inside the build (issues 69
and 71), superseding the 2026-09-08 entry that excluded `.git`. The canonical
`.dockerignore` sends `.git` but keeps out `.git/config`, which can hold a
+5 -3
View File
@@ -1,6 +1,6 @@
---
title: Code Styleguide — Go
last_modified: 2026-10-02
last_modified: 2026-10-04
---
1. Try to hard wrap long lines at 77 characters or less.
@@ -51,8 +51,10 @@ last_modified: 2026-10-02
# ?= rather than := so that a `VERSION` build argument takes precedence:
# where a build stage invokes make, `ARG VERSION` puts it in the
# environment and `?=` defers to it. Otherwise `git describe` runs, in a
# build stage on the `.git` the build context carries.
VERSION ?= $(shell git describe --tags --always)
# build stage on the `.git` the build context carries. When it prints
# nothing (outside a git checkout, or where git is missing or refuses the
# checkout), the version falls back to `dev`.
VERSION ?= $(or $(shell git describe --tags --always 2>/dev/null),dev)
GOLDFLAGS += -X main.Version=$(VERSION)
+12 -6
View File
@@ -1,6 +1,6 @@
---
title: Existing Repo Checklist
last_modified: 2026-10-02
last_modified: 2026-10-03
---
Use this checklist when beginning work in a repo that may not yet conform to our
@@ -67,10 +67,14 @@ 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. `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. 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. `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
@@ -80,7 +84,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
+5 -3
View File
@@ -1,6 +1,6 @@
---
title: Go HTTP Server Conventions
last_modified: 2026-10-02
last_modified: 2026-10-04
---
This document defines the architectural patterns, design decisions, and
@@ -987,8 +987,10 @@ Use ldflags to inject version information at build time:
# ?= rather than := so that a `VERSION` build argument takes precedence:
# where a build stage invokes make, `ARG VERSION` puts it in the
# environment and `?=` defers to it. Otherwise `git describe` runs, in a
# build stage on the `.git` the build context carries.
VERSION ?= $(shell git describe --tags --always)
# build stage on the `.git` the build context carries. When it prints
# nothing (outside a git checkout, or where git is missing or refuses the
# checkout), the version falls back to `dev`.
VERSION ?= $(or $(shell git describe --tags --always 2>/dev/null),dev)
build:
go build -ldflags "-X main.Version=$(VERSION)" ./cmd/httpd
+12 -6
View File
@@ -1,6 +1,6 @@
---
title: New Repo Checklist
last_modified: 2026-10-02
last_modified: 2026-10-03
---
Use this checklist when creating a new repository from scratch. Follow the steps
@@ -76,10 +76,14 @@ 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. `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. 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.
- The Dockerfile carries a `lint` phase and a `test` phase, each invoking
its tool directly rather than through `make` or `script/`, and the final
@@ -94,7 +98,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`
+26 -14
View File
@@ -1,6 +1,6 @@
---
title: Repository Policies
last_modified: 2026-10-02
last_modified: 2026-10-03
---
This document covers repository structure, tooling, and workflow standards. Code
@@ -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 Go image. The canonical Go repo
and the test phase is based on the Debian Go image. The canonical Go repo
`Dockerfile`:
```dockerfile
@@ -173,8 +173,9 @@ style conventions are in separate documents:
COPY . .
RUN golangci-lint run --config .golangci.yml ./...
# Test phase
# golang:1.x-alpine, YYYY-MM-DD
# 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
FROM golang@sha256:... AS test
WORKDIR /src
COPY go.mod go.sum ./
@@ -192,6 +193,8 @@ 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
@@ -244,11 +247,14 @@ 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. `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. 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.
- 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.
@@ -451,12 +457,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