1 Commits
Author SHA1 Message Date
sneak b9cbda4ff0 Fall back to dev when git describe prints nothing (closes #74)
check / check (push) Successful in 35s
The Makefile example in the Go styleguide and in the HTTP server conventions
set VERSION from `git describe --tags --always` alone. Outside a git checkout,
such as an unpacked source tarball, that prints nothing, so 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 says so. A `VERSION` from the environment or the make
command line still takes precedence.

Model: opus-5-5
2026-10-04 02:16:59 +00:00
6 changed files with 24 additions and 47 deletions
+4 -11
View File
@@ -22,17 +22,10 @@ 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.
conventions now fall back to `dev` when `git describe` prints nothing, as it
does outside a git 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: 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
+2 -3
View File
@@ -51,9 +51,8 @@ last_modified: 2026-10-04
# ?= 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. When it prints
# nothing (outside a git checkout, or where git is missing or refuses the
# checkout), the version falls back to `dev`.
# build stage on the `.git` the build context carries. Outside a git
# checkout it prints nothing, and the version falls back to `dev`.
VERSION ?= $(or $(shell git describe --tags --always 2>/dev/null),dev)
GOLDFLAGS += -X main.Version=$(VERSION)
+4 -8
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
+2 -3
View File
@@ -987,9 +987,8 @@ 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. When it prints
# nothing (outside a git checkout, or where git is missing or refuses the
# checkout), the version falls back to `dev`.
# build stage on the `.git` the build context carries. Outside a git
# checkout it prints nothing, and the version falls back to `dev`.
VERSION ?= $(or $(shell git describe --tags --always 2>/dev/null),dev)
build:
+4 -8
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
+8 -14
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.