Add plan: bake git commit into the operator binary and log it at startup

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-08-11 18:02:41 +02:00
parent 837e374228
commit e4d2a191d0

View File

@@ -0,0 +1,75 @@
# 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` — 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<Function>_<scenario>` 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 <module>/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"`.