# 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="`. - `func Resolve() string` — returns `Commit` if non-empty; otherwise falls back to `debug.ReadBuildInfo()` VCS settings (`vcs.revision` truncated to 12 chars, `-dirty` suffix when `vcs.modified=true`), so plain host builds (`make build`, `make run`, `go run`) are stamped for free since `.git` is present there; `"unknown"` when neither source is available (e.g. `go test`). - Internal `resolve(ldflagsCommit string, readBuildInfo func() (*debug.BuildInfo, bool)) string` so the fallback logic is table-testable with a fake build-info func (house style: stdlib testing, `t.Parallel()`, subtests, `Test_` names). **2. `cmd/main.go`**: - Add `--version` bool flag; immediately after the existing `flag.Parse()` (main.go:132), if set: print `version.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 --porcelain` catches staged and untracked changes, which `git diff --quiet` misses). - `docker-build`: add `--build-arg GIT_COMMIT=$(GIT_COMMIT)`. - `docker-buildx`: add the same `--build-arg` to the `buildx build` line. - `build`/`run`/`run-dev` stay untouched — the ReadBuildInfo fallback covers them. **4. `Dockerfile`**: - `ARG GIT_COMMIT=unknown` in the builder stage; extend the existing `go build` with `-ldflags "-X /internal/version.Commit=${GIT_COMMIT}"`. - Re-declare `ARG GIT_COMMIT` in the distroless stage and add `LABEL org.opencontainers.image.revision="${GIT_COMMIT}"` so the hash is also visible via `docker inspect` without running the binary. No changes to deploy manifests, providers, or the reconciler. CHANGELOG entry after the user confirms it works (house convention). ## Verification ```bash 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"`.