myxce/.kiro/specs/build-time-versioning/tasks.md
Rongsong Shen 4b7cc7867f
feat(build-time-versioning): add build_time meson option and C-wide define
Add a build_time meson option (string, default '') sibling to the
existing commit_id option, threaded project-wide via the same
add_project_arguments() call that already defines VERSION_COMMIT_ID --
one call, one mechanism, no new plumbing. No consumer wired yet (later
tasks); a plain meson setup with neither option set still configures
cleanly with both defines resolving to empty string.

Task 1/13 of build-time-versioning (spec: .kiro/specs/build-time-versioning/).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-20 08:29:32 +00:00

11 KiB

Implementation Plan

  • 1. Foundation: meson build-time option and C project-wide define

    • Add a build_time meson option (string, default '') alongside the existing commit_id option in src/meson.options
    • Extend src/meson.build's existing add_project_arguments() call to also define VERSION_BUILD_TIME for every C component, mirroring how VERSION_COMMIT_ID is already threaded through
    • 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

  • 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
    • Fall back to the literal string unknown for commit_id when git itself fails (e.g. no .git present)
    • Append -Dcommit_id="$commit_id" -Dbuild_time="$build_time" to MESON_OPTS for every build profile, before the existing meson setup/meson setup --reconfigure calls
    • After computing both values, verify each is non-empty (build_time always is; commit_id falls back to unknown only on a genuine git failure) and print a loud warning to stderr if either resolves empty anyway -- the build tooling must not silently proceed with a value it cannot confirm
    • Observable: running /xce-build scheduler debug followed by the resulting binary's --version shows a commit id matching the current git rev-parse --short HEAD at build time, not empty; deliberately breaking the computation (e.g. temporarily renaming .git) prints the warning instead of silently succeeding
    • Requirements: 1.1, 1.2, 1.3, 1.4, 4.1, 4.2, 5.1, 5.2
    • Boundary: xce-build.sh
  • 2.2 (P) Nix packages compute and pass build time

    • Add a builtins.currentTime-derived build-time value in src/flake.nix, passed via a new -Dbuild_time= entry alongside the existing -Dcommit_id= wiring in xce.nix/xcecli.nix
    • Since builtins.currentTime cannot itself be empty, the confirmation this requirement asks for is structural here: assert (in the derivation or a post-build check) that the meson option actually reached the binary, rather than a runtime empty-value check
    • Observable: nix build .#xcecore_debug produces a binary whose --version shows a non-empty build time, without any git invocation inside the sandboxed derivation
    • Requirements: 1.1, 1.2, 1.4, 4.1, 4.2, 5.1, 5.2
    • Boundary: Nix packaging
  • 3. Core: C daemon version display

  • 3.1 Extend the shared version macro with build time and unknown fallback

    • Extend MK_VERSION/STR in src/include/utils.h so the composed string carries both commit id and build time
    • Both fields render as unknown when their underlying define is empty, never a blank/garbled string
    • Observable: a build with both meson options unset produces a --version string containing unknown for both fields, not empty gaps
    • Requirements: 1.4, 2.2
    • Boundary: utils.h
  • 3.2 (P) Controller and scheduler: version in --version and startup log

    • Update each daemon's argp_program_version construction to use the extended macro from 3.1
    • Add the version string to each daemon's existing first startup INFO log line (controller's "My name is %s" line, scheduler's "HA cluster config..." line) rather than a second, independently-formatted line
    • Observable: xce_controller --version and xce_scheduler --version each print commit id + build time and exit without starting the daemon; each daemon's startup log's first lines include the identical two values
    • Requirements: 1.1, 1.2, 2.1, 2.2, 3.1, 3.2, 4.3
    • Boundary: controller/main.c, scheduler/main.c
    • Depends: 3.1
  • 3.3 (P) Worker: add --version and startup log line

    • Add a long-only --version option to worker's getopt_long() option table and a handling case that prints the same macro output and exits before any other startup work
    • Add the version string to worker's existing first startup INFO log line (its own "HA cluster config..." line)
    • Observable: xcecd --version prints commit id + build time and exits without starting the daemon; the startup log's first lines include the identical two values
    • Requirements: 1.1, 1.2, 2.1, 2.2, 3.1, 3.2, 4.3
    • Boundary: worker/main.c
    • Depends: 3.1
  • 4. Core: Go binary version display

  • 4.1 go_build script injects ldflags from environment

    • src/tools/go_build reads XCE_GIT_COMMIT/XCE_BUILD_TIME from its environment (both optional, empty-safe) and appends -ldflags "-X main.GitCommit=... -X main.BuildTime=..." to its go build invocation, falling back to unknown for any unset value
    • Observable: invoking go_build with neither env var set still produces a working binary whose injected vars read unknown
    • Requirements: 1.1, 1.2, 1.4, 4.1, 4.2
    • Boundary: go_build script
  • 4.2 apiserver and xcecli meson builds pass resolved values through

    • src/frontend/apiserver/meson.build and src/frontend/xcecli/meson.build read the project's commit_id/build_time meson options and pass them as XCE_GIT_COMMIT/XCE_BUILD_TIME env vars into their existing custom_target() calls
    • Observable: a meson-driven /xce-build apiserver (with 2.1's values set) produces an xce_apiserver binary whose linked-in vars are non-unknown
    • Requirements: 1.1, 1.2, 4.1, 4.2
    • Boundary: apiserver/meson.build, xcecli/meson.build
    • Depends: 4.1
  • 4.3 (P) Makefile dev-loop path computes and passes the same values

    • src/frontend/Makefile's build-server/build-cli targets compute commit id (git, dirty-suffixed) and build time (date) directly and pass the identical -ldflags shape 4.1 establishes, independent of meson
    • Fall back to unknown for commit id when git itself fails (e.g. no .git present), matching 2.1's/4.1's fallback discipline
    • Observable: make build-server and make build-cli each produce a binary whose --version/-version output matches the same commit id git rev-parse --short HEAD reports at build time
    • Requirements: 1.1, 1.2, 1.3, 1.4, 4.1, 4.2, 5.1, 5.2
    • Boundary: Makefile
    • Depends: 4.1
  • 4.4 (P) apiserver: version flag, vars, and startup log

    • Declare GitCommit/BuildTime package vars (default unknown) in src/frontend/apiserver/cmd/main.go
    • Add a -version flag, checked before utils.InitLogger(), that prints both values and exits with no logging side effects
    • Extend the existing logger.Info("********** Initialize Logger done!") call site to also log both values
    • Observable: xce_apiserver -version prints both values and exits without starting the server; the startup log's first lines include the identical two values
    • Requirements: 1.1, 1.2, 2.1, 2.2, 3.1, 3.2
    • 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
    • Requirements: 1.1, 1.2, 2.1
    • Boundary: xcecli/cmd/main.go
    • Depends: 4.1
  • 5. Core: xceadmin version display

  • 5.1 (P) meson-build.sh generates a build-info module before packaging

    • src/xceadmin/meson.build's custom_target() passes the resolved commit id/build time to meson-build.sh (new positional args or env vars)
    • meson-build.sh writes a small _build_info.py (containing GIT_COMMIT/BUILD_TIME string constants, unknown fallback) into the mirrored source tree's xceadmin package directory, before the existing pip install step, matching the schema-sync step's own before-install ordering
    • After writing the module, verify it actually landed in the mirrored tree before proceeding to pip install (e.g. a file-exists check) and fail the build loudly if it did not, rather than silently packaging the fallback-shaped-but-wrong wheel
    • Observable: a meson-driven /xce-build xceadmin produces a wheel whose installed _build_info.py contains non-unknown values when 2.1's values are set
    • Requirements: 1.1, 1.2, 1.4, 4.1, 4.2
    • Boundary: xceadmin/meson.build, xceadmin/meson-build.sh
    • Depends: 1
  • 5.2 (P) Committed fallback stub and CLI version wiring

    • Commit a fallback src/xceadmin/src/xceadmin/_build_info.py with both fields set to unknown (used whenever 5.1's generated version isn't present, e.g. pip install -e .)
    • Wire click.version_option() on cli.py's top-level group, importing _build_info defensively and composing its version string from pyproject.toml's static version plus the two build-info fields
    • Observable: both pip install -e . (no meson) and the meson-built wheel produce a working xceadmin --version that prints and exits; the editable-install case shows unknown for both build fields, never an import error
    • Requirements: 1.1, 1.2, 2.1
    • Boundary: xceadmin/src/xceadmin
  • 6. Integration: cross-toolchain consistency and regression verification

  • 6.1 Motivating-incident regression test

    • Build a C daemon via /xce-build (any profile) and confirm its --version commit id is non-unknown and matches git rev-parse --short HEAD at build time -- the exact scenario that was previously silent/empty
    • Repeat against at least the asan and debug profiles specifically (the motivating incident was a debug-profile binary), confirming both carry accurate, matching values
    • Observable: an automated check fails loudly if xce-build.sh's computed commit id ever regresses to empty/missing, in either profile
    • Requirements: 4.1, 4.2, 4.3, 5.1, 5.2
    • Depends: 2.1, 3.2, 3.3
  • 6.2 Version/startup-log consistency test

    • For one C daemon (controller or scheduler) and one Go binary (apiserver), capture the startup log's version line and separately run --version/-version, asserting the two report byte-identical commit id and build time
    • Observable: a passing test proves --version and the startup log can never disagree for the same artifact
    • Requirements: 2.2
    • Depends: 3.2, 4.4
  • 6.3 xceadmin end-to-end build/install/version cycle

    • Run xceadmin's full meson-driven build (schema sync, venv install, pytest, wheel build), install the resulting wheel, and confirm xceadmin --version reports the generated (non-unknown) build-info fields
    • Observable: the installed wheel's --version output differs from an editable (pip install -e .) install's unknown-only output, proving the generated module actually replaces the fallback
    • Requirements: 1.1, 1.2, 2.1, 4.1, 4.2
    • Depends: 5.1, 5.2