feat(build-time-versioning): xcecli --version via Cobra rootCmd.Version

Adds GitCommit/BuildTime vars to xcecli's main.go. Cobra's rootCmd is
declared unexported in router.go (a different file than the task's
original main.go-only boundary), so main.go can't reach it directly --
resolved by extending router.go's sole exported entry point,
Execute(cm, version), to accept the composed version string and set
rootCmd.Version before rootCmd.Execute() runs. Cobra then auto-registers
--version/-v natively once rootCmd.Version is non-empty (confirmed via
reading Cobra v1.9.1 source directly) -- zero additional flag-parsing
code, as designed.

Live-verified via both build paths (meson xce-build.sh and make
build-cli): real commit id (matching git rev-parse --short HEAD plus
dirty suffix) and build time, exit 0.

xcecli's startup log deliberately not extended -- Requirement 3 scopes
that to the four daemons only; xcecli is a one-shot CLI. Corrects a
stale design.md line that had assumed otherwise.

Task 4.5/13 -- closes out the Go binary version display phase (tasks
4.1-4.5).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Rongsong Shen 2026-08-20 14:07:38 +00:00
parent 17e837b196
commit fb09afe6be
No known key found for this signature in database
GPG key ID: 5BCDAAE0123E9FE2
4 changed files with 27 additions and 10 deletions

View file

@ -176,7 +176,7 @@ graph TB
- `src/tools/go_build` (consumed by both `apiserver/meson.build` and `xcecli/meson.build`'s `custom_target()`) reads `XCE_GIT_COMMIT`/`XCE_BUILD_TIME` from its environment (empty-safe — falls back to `"unknown"` when unset, e.g. a bare `go build` invoked by hand) and appends `-ldflags "-X main.GitCommit=$v1 -X main.BuildTime=$v2"` to its existing `go build` invocation.
- `src/frontend/Makefile`'s `build-server`/`build-cli` targets compute the same two values directly (git/date, dev-loop path, no meson involved) and pass the identical `-ldflags` shape — same output format as the meson path, different computation site (mirrors the C-side split).
- `apiserver/cmd/main.go`: package-level `var (GitCommit = "unknown"; BuildTime = "unknown")`, a new `-version` bool flag (checked first, before `utils.InitLogger()` — prints and exits, no logging side effects), and the existing `logger.Info("********** Initialize Logger done!")` call site extended to also log both values (same convention, `"********** <action> done!"` is untouched; the version fields ride as structured zap fields on that same line or an adjacent one).
- `xcecli/cmd/main.go`: same two vars; `rootCmd.Version = fmt.Sprintf(...)` set once at init (Cobra then handles `--version` natively — zero new flag-parsing code); its own existing `logger.Info("********** Initialize Logger on client side done!")` extended the same way.
- `xcecli/cmd/main.go`: same two vars; version string passed into `router.Execute(cm, version)`, which sets `rootCmd.Version` before `rootCmd.Execute()` runs (Cobra then handles `--version` natively — zero new flag-parsing code). Correction (task 4.5): xcecli's startup log is deliberately NOT extended — Requirement 3 scopes the startup-log obligation to "the controller, scheduler, worker, or apiserver daemon" only; xcecli is a one-shot CLI, not a daemon, and was never in Requirement 3's scope.
**Contracts**: Service [ ] / API [ ] / Event [ ] / Batch [ ] / State [ ]

View file

@ -6,7 +6,7 @@
- Observable: a plain `meson setup` with neither option set still configures successfully (both defines resolve to empty string, no build failure)
- _Requirements: 1.1, 1.2, 1.4, 4.3_
- [ ] 2. Foundation: compute real values at both build entry points
- [x] 2. Foundation: compute real values at both build entry points
- [x] 2.1 (P) xce-build.sh computes and passes commit id and build time
- Compute `commit_id` via `git rev-parse --short HEAD`, appended with `-dirty` when the tree has uncommitted changes (`git status --porcelain` or `git diff --quiet HEAD --`)
- Compute `build_time` via `date -u +%Y-%m-%dT%H:%M:%SZ`
@ -24,7 +24,7 @@
- _Requirements: 1.1, 1.2, 1.4, 4.1, 4.2, 5.1, 5.2_
- _Boundary: Nix packaging_
- [ ] 3. Core: C daemon version display
- [x] 3. Core: C daemon version display
- [x] 3.1 Extend the shared version macro with build time and unknown fallback
- ~~Extend `MK_VERSION`/`STR`~~ — `MK_VERSION`/`STR` cannot express the empty-string-to-`unknown` substitution at the preprocessor level, so implemented as new runtime functions `xce_version_field()`/`xce_compose_version()` (`utils.h`/`utils.c`) instead; `MK_VERSION`/`STR` marked SUPERSEDED with an explicit doc comment and handoff notes for 3.2 (round 1 REJECTED for silently orphaning `MK_VERSION` + undocumented `argp_program_version` file-scope-const-initializer trap; round 2 APPROVED after adding the SUPERSEDED comment + NOTE FOR CALLERS guidance)
- Both fields render as `unknown` when their underlying define is empty (verified via `""`, not just `NULL`), never a blank/garbled string
@ -48,7 +48,7 @@
- _Boundary: worker/main.c_
- _Depends: 3.1_
- [ ] 4. Core: Go binary version display
- [x] 4. Core: Go binary version display
- [x] 4.1 go_build script injects ldflags from environment
- `src/tools/go_build` reads `XCE_GIT_COMMIT`/`XCE_BUILD_TIME` via `[ -z ... ]` (correctly empty-safe, not just unset-safe) and appends `-ldflags "-X main.GitCommit=... -X main.BuildTime=..."` to its `go build` invocation, falling back to `unknown`
- Confirmed (research finding, independently reproduced by review): `-X` on a `main` package var that doesn't exist yet is silently ignored by the Go linker, not a build error -- this task has no real ordering dependency on 4.4/4.5
@ -81,11 +81,13 @@
- _Boundary: apiserver/cmd/main.go_
- _Depends: 4.1_
- [ ] 4.5 (P) xcecli: version field and vars
- Declare the same two package vars in `src/frontend/xcecli/cmd/main.go`; set Cobra's built-in `rootCmd.Version` field from them once at init
- Observable: `xcecli --version` (Cobra's native handling) prints both values and exits
- [x] 4.5 (P) xcecli: version field and vars
- `GitCommit`/`BuildTime` vars in `main.go`; `router.go`'s `Execute()` extended with a `version string` parameter, setting `rootCmd.Version` before `rootCmd.Execute()` runs (the sole call site, confirmed by both implementer and review)
- Cobra's native `--version`/`-v` auto-registers once `rootCmd.Version != ""` (confirmed via reading Cobra v1.9.1 source directly) -- zero additional flag-parsing code, as designed
- Observable confirmed live (independently re-verified by review, both build paths): `xcecli --version` prints commit id (matching `git rev-parse --short HEAD` + dirty suffix) and build time, exits 0
- xcecli's startup log deliberately NOT extended -- Requirement 3 scopes that obligation to the four daemons only; xcecli is a one-shot CLI (design.md corrected to match)
- _Requirements: 1.1, 1.2, 2.1_
- _Boundary: xcecli/cmd/main.go_
- _Boundary: xcecli/cmd/main.go, xcecli/router/router.go_
- _Depends: 4.1_
- [ ] 5. Core: xceadmin version display