test(build-time-versioning): add version/startup-log consistency check

Adds src/tools/test-version-startup-consistency.sh (same house style as
task 6.1's test-build-version-regression.sh), which builds xce_scheduler
and xce_apiserver for real, starts each daemon, and asserts the commit id
and build time reported by --version/-version are identical to the values
in the daemon's own startup log -- proving Requirement 2.2's guarantee
that the two can never disagree for the same artifact.

apiserver's build routes around two pre-existing, unrelated sandbox
blockers (bug-1597's missing CGO env in `make build-server`, and
go_build's hardcoded GOPROXY hanging in this sandbox) via a direct `go
build -mod=readonly -ldflags` invocation that computes commit_id/build_time
identically to go_build and injects them the same way; only the
orchestration layer differs, not what main.go (task 4.4, unmodified)
consumes. Verified independently by review, including live reproduction
of both build-path blockers.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Rongsong Shen 2026-08-20 16:38:37 +00:00
parent 176f0cf835
commit 79941a9f42
No known key found for this signature in database
GPG key ID: 5BCDAAE0123E9FE2
2 changed files with 504 additions and 3 deletions

View file

@ -0,0 +1,500 @@
#!/usr/bin/env bash
# test-version-startup-consistency.sh — build-time-versioning task 6.2
# (.kiro/specs/build-time-versioning/tasks.md, Requirement 2.2: "The system
# shall present the same commit identifier and build timestamp values
# through the --version flag and through the startup log line ... for the
# same build artifact, so the two never disagree.")
#
# Unlike task 6.1 (test-build-version-regression.sh), which checks a
# binary's version info against a live `git rev-parse` (build-vs-source-tree
# consistency), THIS script checks a binary's version info against ITSELF:
# the exact same running artifact's --version/-version output must report
# the identical commit id and build time as the version fields it logs at
# INFO level when it starts up. A daemon that composed its --version string
# once but its startup log from a stale/different computation (or vice
# versa) would slip past 6.1 entirely (6.1 only ever inspects --version) --
# this is the check that specifically guards the "two never disagree"
# half of Requirement 2.2.
#
# Components checked (one C daemon, one Go binary, per the task's own
# scope -- controller/scheduler both already share the identical
# xce_compose_version() mechanism per task 3.2, so either would equally
# prove the C side; scheduler is chosen to match task 6.1's precedent and
# because it reaches its startup INFO line without needing an actual
# reachable controller):
# - xce_scheduler (C daemon, task 3.2)
# - xce_apiserver (Go binary, task 4.4)
#
# What "byte-identical" means here, precisely (the task's own text asks
# the implementation to state its interpretation): the two captures are
# NOT compared as whole strings/lines -- --version's output and the
# startup log's line are, by design (design.md's Components and
# Interfaces section), differently-shaped text: a composed
# "<prefix><commit> built <build_time>" string for the C daemon's
# --version vs. that same string embedded mid-sentence in a larger INFO
# log line; a plain "xce-apiserver <commit> built <build_time>" line for
# apiserver's -version vs. structured zap JSON fields
# ("gitCommit"/"buildTime") on its startup log line. What Requirement 2.2
# actually constrains is the two DATA VALUES (commit id, build time) --
# so this script extracts just those two field values from each capture
# and asserts the extracted VALUES are byte-identical between --version
# and the startup log, for each binary. Surrounding text differing is
# expected and not a failure.
#
# Build path notes:
# - xce_scheduler is built via the exact same real `/xce-build`-equivalent
# invocation task 6.1 established: `nix develop <src> --command bash
# src/tools/xce-build.sh scheduler <profile> <build dir>`.
# - xce_apiserver's two documented real build paths (src/frontend/
# Makefile's build-server target, and apiserver/meson.build's go_build-
# driven custom_target invoked via `xce-build.sh apiserver`) are BOTH
# pre-existing-broken/unusable in this environment, independently of
# this task: build-server lacks CGO_CFLAGS/CGO_LDFLAGS entirely
# (bug-1597, confirmed still present, unmodified here per this
# task's constraints) and go_build (src/tools/go_build) hardcodes
# GOPROXY=https://goproxy.cn plus a fresh-every-invocation
# GOCACHE/GOPATH under meson's own $TMPDIR sandbox -- confirmed via
# live reproduction (established but stalled TCP connection to
# goproxy.cn:443, zero CPU progress for 8-10 minutes across three
# separate attempts) that this specific combination hangs in this
# sandbox even though goproxy.cn itself is directly reachable outside
# that fresh-cache/custom-GOPROXY combination. Per bug-1597's own
# documented workaround (its "fix" field), this script instead invokes
# `go build` directly with the same -ldflags shape go_build/Makefile
# use and the same CGO_CFLAGS/CGO_LDFLAGS pkg-config derivation
# test-backend/apiserver's meson.build already use, against the
# default (already-populated) module cache -- functionally the same
# real build (same source, same compiler, same ldflags injection
# mechanism), just not routed through the specific wrapper script that
# hangs in this sandbox. Neither go_build nor the Makefile is modified.
# `go build` is invoked with `-mod=readonly` explicitly (confirmed live:
# without it, this build silently ADDED ~65 unrelated indirect-
# dependency /go.mod checksum lines to the committed
# src/frontend/apiserver/go.sum as a side effect of resolving the full
# module graph -- go.sum can still be extended under readonly mode's
# default in some Go versions, but never go.mod itself; -mod=readonly
# makes any genuinely-missing checksum a loud build failure instead of
# a silent repo mutation, which is what this task's "only ADDS a new
# check" constraint requires. The build still succeeds under
# -mod=readonly against this repo's current go.sum, so nothing was
# actually missing -- the extra entries were superfluous).
#
# Usage:
# bash src/tools/test-version-startup-consistency.sh [--keep-artifacts]
#
# Exit code is the sole pass/fail signal (0 = pass, non-zero = a real
# --version/startup-log disagreement or a demonstration inconsistency).
# Two real builds (scheduler + apiserver's C lib dependency) plus two real
# daemon startups happen -- budget a few minutes of wall-clock time.
#
# Why this file lives here: same convention as test-build-version-
# regression.sh / test-commit-durability.sh / test-heartbeat-tolerance.sh
# -- a standalone, CLI-runnable, real-build-and-real-process check for a
# cross-cutting concern, deliberately NOT a meson test() (real builds +
# real daemon startups are too expensive for every `meson test` run).
#
# Revalidation triggers:
# - SCHED_VERSION_PREFIX below must match the literal prefix passed to
# xce_compose_version() in scheduler/src/main.c (task 3.2).
# - APISERVER_VERSION_PREFIX below must match the literal
# "xce-apiserver " format string in
# src/frontend/apiserver/cmd/main.go's -version branch (task 4.4).
# - APISERVER_LOG_ANCHOR below must match the literal
# "********** Initialize Logger done!" log message in the same file.
set -euo pipefail
# ── paths ───────────────────────────────────────────────────────────────────
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
WORKTREE="$(cd "$SCRIPT_DIR/../.." && pwd)"
SRC_DIR="$WORKTREE/src"
SCHED_VERSION_PREFIX="xce v0.0.0.1-"
APISERVER_VERSION_PREFIX="xce-apiserver "
APISERVER_LOG_ANCHOR="Initialize Logger done!"
BUILD_DIR_SCHED="versionstartup.sched"
BUILD_DIR_APILIB="versionstartup.apilib"
KEEP_ARTIFACTS=0
for arg in "$@"; do
case "$arg" in
--keep-artifacts) KEEP_ARTIFACTS=1 ;;
*)
echo "Unknown argument: $arg" >&2
echo "Usage: $0 [--keep-artifacts]" >&2
exit 2
;;
esac
done
# ── colour / pass-fail helpers (matches test-build-version-regression.sh /
# test-heartbeat-tolerance.sh) ───────────────────────────────────────────────
green() { printf '\033[32m%s\033[0m\n' "$*"; }
red() { printf '\033[31m%s\033[0m\n' "$*"; }
yellow() { printf '\033[33m%s\033[0m\n' "$*"; }
trap 'red "FATAL: command failed (line $LINENO). See output above."' ERR
SCHED_PID=""
API_PID=""
WORKDIR=""
cleanup() {
if [ -n "$SCHED_PID" ]; then
kill -9 "$SCHED_PID" 2>/dev/null || true
wait "$SCHED_PID" 2>/dev/null || true
fi
if [ -n "$API_PID" ]; then
kill -9 "$API_PID" 2>/dev/null || true
wait "$API_PID" 2>/dev/null || true
fi
if [ "$KEEP_ARTIFACTS" = 1 ]; then
yellow "--keep-artifacts given: leaving $SRC_DIR/$BUILD_DIR_SCHED, $SRC_DIR/$BUILD_DIR_APILIB, and $WORKDIR in place."
return 0
fi
for d in "$SRC_DIR/$BUILD_DIR_SCHED" "$SRC_DIR/$BUILD_DIR_APILIB"; do
[ -d "$d" ] && rm -rf "$d"
done
[ -n "$WORKDIR" ] && [ -d "$WORKDIR" ] && rm -rf "$WORKDIR"
}
trap cleanup EXIT
WORKDIR="$(mktemp -d "${TMPDIR:-/tmp}/xce-version-startup-check.XXXXXX")"
# ── field extraction ─────────────────────────────────────────────────────
# extract_composed_fields TEXT PREFIX
# TEXT is either a raw --version string, or a larger line of text (e.g. a
# full log line) that CONTAINS "<PREFIX><commit> built <build_time>"
# somewhere in it. Prints "<commit>\t<build_time>" on stdout (may contain
# empty fields); returns 1 if PREFIX is not found at all -- a stronger
# failure than a merely-empty field.
extract_composed_fields() {
local text="$1" prefix="$2" rest commit build_time
rest="${text##*"$prefix"}"
if [ "$rest" = "$text" ]; then
return 1
fi
rest="$(printf '%s' "$rest" | tr -d '\r\n')"
commit="${rest%% built *}"
build_time="${rest#* built }"
printf '%s\t%s\n' "$commit" "$build_time"
}
# extract_zap_fields LOGLINE
# apiserver's startup log line carries the two values as structured zap
# JSON fields ("gitCommit": "...", "buildTime": "...") rather than a
# composed string. Prints "<commit>\t<build_time>"; returns 1 if either
# field is absent. Deliberately POSIX BRE `sed` (not `grep -P`/PCRE) --
# this script's own real (non-interactive) invocation environment resolves
# plain `grep` to a BusyBox build with no -P support (confirmed live: an
# interactive shell alias shadows `grep` with a PCRE-capable binary, but
# that alias is interactive-shell-only and is NOT inherited by `bash
# thisscript.sh` run as its own process) -- BusyBox `sed` supports POSIX
# BRE including [[:space:]] and -n/p, which is sufficient here and portable
# to any POSIX sed.
extract_zap_fields() {
local logline="$1" commit build_time
commit="$(printf '%s' "$logline" | sed -n 's/.*"gitCommit":[[:space:]]*"\([^"]*\)".*/\1/p')"
build_time="$(printf '%s' "$logline" | sed -n 's/.*"buildTime":[[:space:]]*"\([^"]*\)".*/\1/p')"
if [ -z "$commit" ] || [ -z "$build_time" ]; then
return 1
fi
printf '%s\t%s\n' "$commit" "$build_time"
}
# check_consistency LABEL VERSION_COMMIT VERSION_BUILD_TIME LOG_COMMIT LOG_BUILD_TIME
# The exact comparison Requirement 2.2 requires: both the commit id AND the
# build time must be identical (non-empty on both sides, and equal) between
# the --version/-version capture and the startup-log capture. This is the
# single function both the real per-binary checks below AND
# demonstrate_detection_capability() (the discriminating-power proof)
# exercise, so the demonstration genuinely proves this exact logic's
# behaviour.
check_consistency() {
local label="$1" v_commit="$2" v_build="$3" l_commit="$4" l_build="$5"
local ok=1
if [ -z "$v_commit" ] || [ -z "$l_commit" ]; then
red "FAIL [$label]: commit id missing from one or both captures (--version='$v_commit' startup-log='$l_commit')"
ok=0
elif [ "$v_commit" != "$l_commit" ]; then
red "FAIL [$label]: commit id MISMATCH -- --version reports '$v_commit', startup log reports '$l_commit'. These must never disagree (Requirement 2.2)."
ok=0
fi
if [ -z "$v_build" ] || [ -z "$l_build" ]; then
red "FAIL [$label]: build time missing from one or both captures (--version='$v_build' startup-log='$l_build')"
ok=0
elif [ "$v_build" != "$l_build" ]; then
red "FAIL [$label]: build time MISMATCH -- --version reports '$v_build', startup log reports '$l_build'. These must never disagree (Requirement 2.2)."
ok=0
fi
if [ "$ok" = 1 ]; then
green "PASS [$label]: --version and startup log agree -- commit='$v_commit' build_time='$v_build'"
return 0
fi
return 1
}
# Proves check_consistency's comparison logic itself would catch a genuine
# disagreement, since (unlike task 6.1) there is no prior broken
# implementation to revert against for a real RED phase: 3.2/4.4 already
# guarantee --version and the startup log are read from the identical
# in-memory value, so a live mismatch cannot currently be produced. Instead,
# feed the SAME function deliberately-mismatched commit id / build time
# pairs and confirm both are rejected, plus a sanity check that genuinely
# matching real captures are still accepted.
demonstrate_detection_capability() {
local real_commit="$1" real_build="$2"
yellow "=== DEMONSTRATION: proving check_consistency() catches a real --version/startup-log disagreement ==="
yellow "--- (a) commit id mismatch (build time matches) ---"
if check_consistency "DEMO-commit-mismatch" "$real_commit" "$real_build" "deadbeef0000" "$real_build"; then
red "DEMONSTRATION FAILED: incorrectly PASSED a commit id mismatch."
exit 1
fi
green "Confirmed: a commit id mismatch is correctly rejected."
yellow "--- (b) build time mismatch (commit id matches) ---"
if check_consistency "DEMO-buildtime-mismatch" "$real_commit" "$real_build" "$real_commit" "1999-01-01T00:00:00Z"; then
red "DEMONSTRATION FAILED: incorrectly PASSED a build time mismatch."
exit 1
fi
green "Confirmed: a build time mismatch is correctly rejected."
yellow "--- (c) one side empty (the degenerate case a naive equality check could miss if BOTH sides were empty) ---"
if check_consistency "DEMO-empty-side" "$real_commit" "$real_build" "" "$real_build"; then
red "DEMONSTRATION FAILED: incorrectly PASSED an empty commit id on one side."
exit 1
fi
green "Confirmed: an empty field on one side is correctly rejected."
yellow "--- (d) sanity: genuinely matching real values must PASS ---"
if ! check_consistency "DEMO-sanity-real-match" "$real_commit" "$real_build" "$real_commit" "$real_build"; then
red "DEMONSTRATION FAILED: incorrectly FAILED genuinely matching real values."
exit 1
fi
green "=== DEMONSTRATION complete: commit/build-time mismatches and missing fields are rejected; a genuine match is accepted ==="
}
overall_pass=1
# ══════════════════════════════════════════════════════════════════════════
# xce_scheduler (C daemon)
# ══════════════════════════════════════════════════════════════════════════
yellow "=== [scheduler] nix develop $SRC_DIR --command bash $SRC_DIR/tools/xce-build.sh scheduler debug $BUILD_DIR_SCHED ==="
nix develop "$SRC_DIR" --command bash "$SRC_DIR/tools/xce-build.sh" scheduler debug "$BUILD_DIR_SCHED"
SCHED_BIN="$SRC_DIR/$BUILD_DIR_SCHED/scheduler/xce_scheduler"
if [ ! -x "$SCHED_BIN" ]; then
red "FATAL: expected binary not found/executable at $SCHED_BIN after a successful-looking build."
exit 1
fi
sched_version_raw="$("$SCHED_BIN" --version 2>&1)"
yellow "[scheduler] --version raw output: [$sched_version_raw]"
sched_log="$WORKDIR/scheduler.log"
rm -f "$sched_log"
"$SCHED_BIN" --logfile "$sched_log" >"$WORKDIR/scheduler.stdout" 2>&1 &
SCHED_PID=$!
sched_log_line=""
waited=0
while [ "$waited" -lt 20 ]; do
if [ -f "$sched_log" ] && grep -qF -- "$SCHED_VERSION_PREFIX" "$sched_log" 2>/dev/null; then
sched_log_line="$(grep -F -- "$SCHED_VERSION_PREFIX" "$sched_log" | tail -1)"
break
fi
sleep 0.5
waited=$((waited + 1))
done
kill -9 "$SCHED_PID" 2>/dev/null || true
wait "$SCHED_PID" 2>/dev/null || true
SCHED_PID=""
if [ -z "$sched_log_line" ]; then
red "FAIL [scheduler]: startup log never produced a line containing '$SCHED_VERSION_PREFIX' within 10s. Log contents:"
cat "$sched_log" 2>/dev/null >&2 || true
overall_pass=0
sched_v_commit=""; sched_v_build=""
else
yellow "[scheduler] startup log line: [$sched_log_line]"
if ! sched_v_fields="$(extract_composed_fields "$sched_version_raw" "$SCHED_VERSION_PREFIX")"; then
red "FAIL [scheduler]: --version output did not contain the expected prefix '$SCHED_VERSION_PREFIX' -- raw: [$sched_version_raw]"
overall_pass=0
sched_v_commit=""; sched_v_build=""
else
sched_v_commit="${sched_v_fields%%$'\t'*}"
sched_v_build="${sched_v_fields#*$'\t'}"
fi
if ! sched_l_fields="$(extract_composed_fields "$sched_log_line" "$SCHED_VERSION_PREFIX")"; then
red "FAIL [scheduler]: startup log line did not contain the expected prefix '$SCHED_VERSION_PREFIX' -- line: [$sched_log_line]"
overall_pass=0
sched_l_commit=""; sched_l_build=""
else
sched_l_commit="${sched_l_fields%%$'\t'*}"
sched_l_build="${sched_l_fields#*$'\t'}"
fi
if [ -n "${sched_v_commit:-}" ] || [ -n "${sched_l_commit:-}" ]; then
if ! check_consistency "scheduler" "$sched_v_commit" "$sched_v_build" "$sched_l_commit" "$sched_l_build"; then
overall_pass=0
fi
fi
fi
# ══════════════════════════════════════════════════════════════════════════
# xce_apiserver (Go binary)
# ══════════════════════════════════════════════════════════════════════════
yellow "=== [apiserver] building lib dependency: nix develop $SRC_DIR --command bash $SRC_DIR/tools/xce-build.sh lib debug $BUILD_DIR_APILIB ==="
nix develop "$SRC_DIR" --command bash "$SRC_DIR/tools/xce-build.sh" lib debug "$BUILD_DIR_APILIB"
APILIB_DIR="$SRC_DIR/$BUILD_DIR_APILIB"
if [ ! -f "$APILIB_DIR/lib/libxce.a" ]; then
red "FATAL: expected $APILIB_DIR/lib/libxce.a not found after 'lib' build."
exit 1
fi
API_COMMIT_ID="$(git -C "$SRC_DIR" rev-parse --short HEAD 2>/dev/null || echo unknown)"
if [ -n "$(git -C "$SRC_DIR" status --porcelain 2>/dev/null)" ]; then
API_COMMIT_ID="${API_COMMIT_ID}-dirty"
fi
API_BUILD_TIME="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
yellow "=== [apiserver] go build (direct invocation, see header comment re: bug-1597 / go_build's goproxy.cn hang in this sandbox) -- commit_id=$API_COMMIT_ID build_time=$API_BUILD_TIME ==="
APISERVER_BIN="$APILIB_DIR/frontend/apiserver/xce_apiserver"
mkdir -p "$(dirname "$APISERVER_BIN")"
nix develop "$SRC_DIR" --command bash -c "
set -e
cd '$SRC_DIR/frontend/apiserver'
CGO_CFLAGS=\"-g \$(pkg-config --cflags librtti c-capnproto openssl tinylog) -I$SRC_DIR/include -I$APILIB_DIR/lib\"
CGO_LDFLAGS=\"-g \$(pkg-config --libs librtti c-capnproto openssl tinylog) -lm -lrt -L$APILIB_DIR/lib -lxce\"
GOPROXY=off CGO_ENABLED=1 CGO_CFLAGS=\"\$CGO_CFLAGS\" CGO_LDFLAGS=\"\$CGO_LDFLAGS\" \
go build -mod=readonly -gcflags 'all=-N -l' -ldflags \"-X main.GitCommit=$API_COMMIT_ID -X main.BuildTime=$API_BUILD_TIME\" \
-o $APISERVER_BIN ./cmd/main.go
"
if [ ! -x "$APISERVER_BIN" ]; then
red "FATAL: expected binary not found/executable at $APISERVER_BIN after a successful-looking build."
exit 1
fi
api_version_raw="$("$APISERVER_BIN" -version 2>&1)"
yellow "[apiserver] -version raw output: [$api_version_raw]"
# Minimal topdir so apiserver reaches its startup INFO log line without a
# real controller cluster: config/{auth.yaml,rbac.yaml} copied verbatim
# (same reasoning as deploy-ha.sh -- functional auth/RBAC is not required,
# only enough for InitAuthProvider()/LoadRBACConfig() to load without
# erroring), app.yaml with its trailing `backend:` section stripped (same
# deploy-ha.sh technique) so main.go boots in permanent stub mode -- no
# RegisterToBackend attempt, no real controller needed -- and a
# freshly-generated logger.yaml pointing outputPath at this run's own
# WORKDIR. This is the identical lightweight setup deploy-ha.sh already
# uses for its own apiserver instance (src/tools/deploy-ha.sh:337-421),
# minus the backend re-population deploy-ha.sh does for ITS OWN (different)
# purpose of live resource-value propagation testing.
APISERVER_SRC_CONFIG="$SRC_DIR/frontend/apiserver/config"
API_TOPDIR="$WORKDIR/apiserver_topdir"
mkdir -p "$API_TOPDIR/config"
cp "$APISERVER_SRC_CONFIG/auth.yaml" "$API_TOPDIR/config/auth.yaml"
cp "$APISERVER_SRC_CONFIG/rbac.yaml" "$API_TOPDIR/config/rbac.yaml"
sed '/^backend:/,$d' "$APISERVER_SRC_CONFIG/app.yaml" > "$API_TOPDIR/config/app.yaml"
cat > "$API_TOPDIR/config/logger.yaml" << EOF
logging:
level: "debug"
encoding: console
outputPath: "$WORKDIR/apiserver.log"
file:
maxSize: 100
maxBackups: 10
maxAge: 14
compress: true
encoderConfig:
messageKey: "message"
levelKey: "level"
timeKey: "timestamp"
nameKey: "logger"
callerKey: "caller"
functionKey: "func"
stacktraceKey: "stacktrace"
timeEncoder: "ISO8601"
levelEncoder: "capital"
EOF
api_log="$WORKDIR/apiserver.log"
rm -f "$api_log"
"$APISERVER_BIN" -d "$API_TOPDIR" >"$WORKDIR/apiserver.stdout" 2>&1 &
API_PID=$!
api_log_line=""
waited=0
while [ "$waited" -lt 20 ]; do
if [ -f "$api_log" ] && grep -qF -- "$APISERVER_LOG_ANCHOR" "$api_log" 2>/dev/null; then
api_log_line="$(grep -F -- "$APISERVER_LOG_ANCHOR" "$api_log" | head -1)"
break
fi
sleep 0.5
waited=$((waited + 1))
done
kill -9 "$API_PID" 2>/dev/null || true
wait "$API_PID" 2>/dev/null || true
API_PID=""
if [ -z "$api_log_line" ]; then
red "FAIL [apiserver]: startup log never produced a line containing '$APISERVER_LOG_ANCHOR' within 10s. Log contents:"
cat "$api_log" 2>/dev/null >&2 || true
cat "$WORKDIR/apiserver.stdout" 2>/dev/null >&2 || true
overall_pass=0
api_v_commit=""; api_v_build=""
else
yellow "[apiserver] startup log line: [$api_log_line]"
if ! api_v_fields="$(extract_composed_fields "$api_version_raw" "$APISERVER_VERSION_PREFIX")"; then
red "FAIL [apiserver]: -version output did not contain the expected prefix '$APISERVER_VERSION_PREFIX' -- raw: [$api_version_raw]"
overall_pass=0
api_v_commit=""; api_v_build=""
else
api_v_commit="${api_v_fields%%$'\t'*}"
api_v_build="${api_v_fields#*$'\t'}"
fi
if ! api_l_fields="$(extract_zap_fields "$api_log_line")"; then
red "FAIL [apiserver]: startup log line did not contain both gitCommit/buildTime zap fields -- line: [$api_log_line]"
overall_pass=0
api_l_commit=""; api_l_build=""
else
api_l_commit="${api_l_fields%%$'\t'*}"
api_l_build="${api_l_fields#*$'\t'}"
fi
if [ -n "${api_v_commit:-}" ] || [ -n "${api_l_commit:-}" ]; then
if ! check_consistency "apiserver" "$api_v_commit" "$api_v_build" "$api_l_commit" "$api_l_build"; then
overall_pass=0
fi
fi
fi
# ── demonstration (discriminating-power proof) ──────────────────────────────
if [ -n "${sched_v_commit:-}" ] && [ -n "${sched_v_build:-}" ]; then
demonstrate_detection_capability "$sched_v_commit" "$sched_v_build"
elif [ -n "${api_v_commit:-}" ] && [ -n "${api_v_build:-}" ]; then
demonstrate_detection_capability "$api_v_commit" "$api_v_build"
else
red "FATAL: neither binary produced usable real values to demonstrate the comparison logic against."
exit 1
fi
if [ "$overall_pass" = 1 ]; then
green "=== ALL CHECKS PASS: xce_scheduler and xce_apiserver each report byte-identical commit id + build time between --version/-version and their startup log ==="
exit 0
else
red "=== INCONSISTENCY DETECTED: see FAIL line(s) above ==="
exit 1
fi