From c9bf22d483e4603f89d300291197dc480e580357 Mon Sep 17 00:00:00 2001 From: clawbot <35+clawbot@noreply.example.org> Date: Fri, 2 Oct 2026 08:49:03 +0200 Subject: [PATCH] Stamp the git tag or short commit in a plain docker build (closes #67) .dockerignore now sends .git, without .git/config, which can hold a credential. The build stage takes the VERSION build argument when one is given, otherwise git describe --tags --always of that .git, and fails if the context carries .git and still yields no version. A plain docker build . used to stamp dev. The CI checkout fetches full history so CI sees the tag and stamps the same value as make build. Model: opus-5-5 --- .dockerignore | 7 ++++++- .gitea/workflows/check.yml | 2 ++ Dockerfile | 14 +++++++++++++- TODO.md | 11 +++++++++++ script/cibuild | 8 ++++---- 5 files changed, 36 insertions(+), 6 deletions(-) diff --git a/.dockerignore b/.dockerignore index fd9fbce..e1d1d88 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,4 +1,9 @@ -.git +# .git is sent without its config. Without a VERSION build argument the +# stage that compiles runs `git describe --tags --always` on .git, which +# does not need .git/config; that file can hold a credential, such as a +# password in a remote URL or the token the CI checkout step stores there. +.git/config + .claude .DS_Store sfdupes diff --git a/.gitea/workflows/check.yml b/.gitea/workflows/check.yml index ee73864..1be9221 100644 --- a/.gitea/workflows/check.yml +++ b/.gitea/workflows/check.yml @@ -6,4 +6,6 @@ jobs: steps: # actions/checkout v4.2.2, 2026-02-22 - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + with: + fetch-depth: 0 - run: script/cibuild diff --git a/Dockerfile b/Dockerfile index a6009f2..2885086 100644 --- a/Dockerfile +++ b/Dockerfile @@ -108,7 +108,19 @@ ARG CHECK_EPOCH RUN echo "gate test, epoch ${CHECK_EPOCH}" && make test RUN echo "gate fmt-check, epoch ${CHECK_EPOCH}" && make fmt-check -RUN make build +# The version stamped into the binary: the VERSION build argument when +# one is given, otherwise `git describe --tags --always` of the .git in +# the build context (git is installed by script/bootstrap above). A +# context that carries .git and still yields no version fails the build; +# with neither, as from a source tarball, it is "dev". +ARG VERSION +RUN version="${VERSION:-$(git describe --tags --always || echo dev)}"; \ + if [ -e .git ] && { [ -z "$version" ] || [ "$version" = dev ] || \ + [ "$version" = unknown ]; }; then \ + echo "no version could be derived although the build context carries .git" >&2; \ + exit 1; \ + fi; \ + make build VERSION="$version" # Runtime stage # alpine:3.22, 2026-07-23 diff --git a/TODO.md b/TODO.md index 7fd3083..b540d3a 100644 --- a/TODO.md +++ b/TODO.md @@ -29,6 +29,17 @@ # Completed Steps +- stamp the git tag or short commit in a plain `docker build .` + instead of `dev` (2026-10-02, branch `next`, closes + https://git.eeqj.de/sneak/sfdupes/issues/67): `.dockerignore` now + sends `.git`, without `.git/config`, and the `Dockerfile` build + stage takes the `VERSION` build argument when one is given, + otherwise `git describe --tags --always` of that `.git`. The build + fails if the context carries `.git` and the version still comes out + empty, `dev` or `unknown`. The CI checkout step fetches the full + history (`fetch-depth: 0`) so CI sees the tag and stamps the same + value as `make build`. + - replace the 1 KiB end-window sampling with the head/tail plus content-hash ladder (2026-09-22, branch `next`, closes https://git.eeqj.de/sneak/sfdupes/issues/61): a file under 10 MiB is diff --git a/script/cibuild b/script/cibuild index 91fd8b2..55a90ba 100755 --- a/script/cibuild +++ b/script/cibuild @@ -16,10 +16,10 @@ # implies the repo is green. # # That implication holds only because of CHECK_EPOCH. A COPY layer is -# invalidated by changed content, and a merge commit's tree is -# byte-identical to the branch head it merges, so without a fresh value -# here Docker serves the gate layers from cache and the build reports a -# green it never earned. Passing the current epoch invalidates the gate +# invalidated only by changed content, and a rebuild of an unchanged +# checkout sends the same content, so without a fresh value here Docker +# serves the gate layers from cache and the build reports a green it +# never earned. Passing the current epoch invalidates the gate # layers on every run while leaving the pinned base images and # go mod download cached; see the Dockerfile for the placement. set -eu -- 2.54.0