Stamp the build version into the binary (closes #253)
All checks were successful
check / check (push) Successful in 3m12s

This commit was merged in pull request #260.
This commit is contained in:
2026-08-24 03:15:20 +02:00
parent 032f265d69
commit ee2276a912
9 changed files with 573 additions and 14 deletions

View File

@@ -1,7 +1,10 @@
#!/bin/sh
# script/docker: build the Docker image tagged with the project name.
# Identical in all repos; the tag comes from script/projectname.
# Generic: needs no adaptation.
# The tag comes from script/projectname.
#
# .dockerignore excludes .git/, so the builder stage cannot derive the
# version itself. It is resolved here, where the checkout is, and passed
# in as a build arg; without it the image would stamp itself "unknown".
set -eu
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
@@ -9,7 +12,9 @@ ROOT="$(cd "$SCRIPT_DIR/.." && pwd -P)"
main() {
cd "$ROOT"
docker build -t "$("$SCRIPT_DIR/projectname")" .
docker build \
--build-arg VERSION="$("$SCRIPT_DIR/version")" \
-t "$("$SCRIPT_DIR/projectname")" .
}
main "$@"

65
script/version Executable file
View File

@@ -0,0 +1,65 @@
#!/bin/sh
# script/version: output the version string the binary is stamped with.
# Our own extension to scripts-to-rule-them-all. The Makefile's build
# target and script/docker both take the value from here, so a `make
# build` binary and a `make docker` image built from the same checkout
# report the same thing.
#
# Order of precedence:
#
# 1. $VERSION, if set and non-empty. This is how the value reaches a
# build that cannot derive it: .dockerignore excludes .git/, so the
# builder stage has no git metadata and the Dockerfile takes the
# value as a build arg instead.
# 2. `git describe --tags --always --dirty` against this checkout. At
# a clean tagged commit that is exactly the tag; otherwise it
# carries the short SHA, the commit distance when a tag is
# reachable, and a -dirty suffix for uncommitted changes.
# 3. "unknown", for a tree with no git metadata and no $VERSION -- a
# source tarball, or `docker build .` with no --build-arg. That
# case must not fail the build and must not name a tag the tree may
# not be at, so it names nothing.
#
# The git step insists the enclosing repository is this checkout, not
# merely some repository above it: an unpacked tarball sitting inside an
# unrelated working copy would otherwise be stamped with that copy's
# version.
#
# Nothing here may vary between two builds of the same commit: the
# release gate asserts the binary is byte-identical across builds. That
# rules out a build timestamp, a hostname, and a builder identity.
set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
# in_this_checkout succeeds when git can read metadata for a repository
# whose work tree root is $ROOT.
in_this_checkout() {
command -v git >/dev/null 2>&1 || return 1
top="$(git rev-parse --show-toplevel 2>/dev/null)" || return 1
[ -n "$top" ] || return 1
top="$(cd "$top" 2>/dev/null && pwd -P)" || return 1
[ "$top" = "$ROOT" ]
}
main() {
if [ -n "${VERSION:-}" ]; then
echo "$VERSION"
return 0
fi
cd "$ROOT"
if in_this_checkout; then
# --always keeps an untagged history from failing the build: it
# falls back to the bare short SHA.
git describe --tags --always --dirty 2>/dev/null && return 0
fi
echo "unknown"
}
main "$@"