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:
75
docs/plans/2026-08-11-1802-bake-commit-version.md
Normal file
75
docs/plans/2026-08-11-1802-bake-commit-version.md
Normal 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"`.
|
||||||
Reference in New Issue
Block a user