3.6 KiB
Plan: Bake the git commit into the operator binary and log it at startup
Created: 2026-08-11 18:02
Context
There is no versioning yet, and the image tag (egress-proxies-operator:dev,
imagePullPolicy: IfNotPresent) says nothing about what code is actually
running. The user wants the commit hash baked into the image at build time,
and — the key requirement — the binary itself must know it and print it into
the log stream during initialization, so kubectl logs | head answers "which
version is running". Docker builds cannot use Go's automatic VCS stamp because
.dockerignore excludes .git (correctly — re-including it would bust layer
caching), so the hash must travel git → Makefile → --build-arg → -ldflags -X.
Implementation
1. New package internal/version (version.go + version_test.go):
var Commit string— stamped at link time via-ldflags "-X gitea.home.hrajfrisbee.cz/kacerr/egress-proxies-operator/internal/version.Commit=<hash>".func Resolve() string— returnsCommitif non-empty; otherwise falls back todebug.ReadBuildInfo()VCS settings (vcs.revisiontruncated to 12 chars,-dirtysuffix whenvcs.modified=true), so plain host builds (make build,make run,go run) are stamped for free since.gitis present there;"unknown"when neither source is available (e.g.go test).- Internal
resolve(ldflagsCommit string, readBuildInfo func() (*debug.BuildInfo, bool)) stringso the fallback logic is table-testable with a fake build-info func (house style: stdlib testing,t.Parallel(), subtests,Test<Function>_<scenario>names).
2. cmd/main.go:
- Add
--versionbool flag; immediately after the existingflag.Parse()(main.go:132), if set: printversion.Resolve()to stdout and exit 0 (before logger/manager setup). - Right after
ctrl.SetLogger(...)(main.go:134):setupLog.Info("Starting egress-proxies-operator", "commit", version.Resolve(), "goVersion", runtime.Version())— first line of every run, plain V(0) so it appears at any log level.
3. Makefile:
- Near the other variables:
GIT_COMMIT ?= $(shell git rev-parse --short=12 HEAD 2>/dev/null || echo unknown)$(shell test -z "$$(git status --porcelain 2>/dev/null)" || echo -dirty)(git status --porcelaincatches staged and untracked changes, whichgit diff --quietmisses). docker-build: add--build-arg GIT_COMMIT=$(GIT_COMMIT).docker-buildx: add the same--build-argto thebuildx buildline.build/run/run-devstay untouched — the ReadBuildInfo fallback covers them.
4. Dockerfile:
ARG GIT_COMMIT=unknownin the builder stage; extend the existinggo buildwith-ldflags "-X <module>/internal/version.Commit=${GIT_COMMIT}".- Re-declare
ARG GIT_COMMITin the distroless stage and addLABEL org.opencontainers.image.revision="${GIT_COMMIT}"so the hash is also visible viadocker inspectwithout running the binary.
No changes to deploy manifests, providers, or the reconciler. CHANGELOG entry after the user confirms it works (house convention).
Verification
go test ./internal/version/ # resolve() table tests
go build ./... && go test ./... # nothing else broke
go run ./cmd/main.go --version # host build: VCS-stamped hash (+ -dirty), exit 0
make docker-build IMG=egress-proxies-operator:dev
docker run --rm egress-proxies-operator:dev --version # prints the baked commit
docker inspect egress-proxies-operator:dev \
--format '{{index .Config.Labels "org.opencontainers.image.revision"}}'
Live check: redeploy on the cluster and confirm the first log line carries
"commit".