feat(build-time-versioning): xceadmin committed fallback stub and CLI version wiring
Commits a fallback src/xceadmin/src/xceadmin/_build_info.py (GIT_COMMIT/
BUILD_TIME both "unknown") for install paths that never run meson-build.sh
(e.g. `pip install -e .`), and wires click.version_option() on cli.py's
top-level group. cli.py imports _build_info defensively (try/except
ImportError with an inline fallback) and composes the version string from
importlib.metadata.version("xceadmin") plus both build-info fields.
Verified independently by review: editable install shows "unknown" for
both fields with no import error; the meson-built wheel shows real,
matching commit id + build time; full xceadmin suite 261 passed/3 skipped
(258 baseline + 3 new tests); ruff clean.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
e1cd9d257c
commit
7f5c87ce9d
4 changed files with 114 additions and 5 deletions
|
|
@ -90,7 +90,7 @@
|
|||
- _Boundary: xcecli/cmd/main.go, xcecli/router/router.go_
|
||||
- _Depends: 4.1_
|
||||
|
||||
- [ ] 5. Core: xceadmin version display
|
||||
- [x] 5. Core: xceadmin version display
|
||||
- [x] 5.1 (P) meson-build.sh generates a build-info module before packaging
|
||||
- `commit_id`/`build_time` passed as 2 new positional args (`$6`/`$7`, matching this custom_target's existing all-positional style); write block sits between schema-sync and `pip install`, with an independent-per-field `unknown` fallback and a file-existence guard checking the exact path it just wrote (the mirrored tree, not the real source tree)
|
||||
- Observable confirmed live (full real build+test+wheel cycle, independently re-run by review): mirrored tree's `_build_info.py` and the packaged wheel both contain real, matching, non-`unknown` values; 258 tests pass, 3 skips confirmed as documented live-HA e2e tests
|
||||
|
|
@ -98,12 +98,13 @@
|
|||
- _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
|
||||
- [x] 5.2 (P) Committed fallback stub and CLI version wiring
|
||||
- Committed `src/xceadmin/src/xceadmin/_build_info.py` with `GIT_COMMIT`/`BUILD_TIME` both `"unknown"` -- exact variable names verified identical to 5.1's `meson-build.sh`-generated module; `cli.py`'s import is a genuine `try/except ImportError` (inline fallback class), not a bare import
|
||||
- `click.version_option()` wired directly on the top-level `@click.group() def cli()`; version string composed via `importlib.metadata.version("xceadmin")` plus both `_build_info` fields, no hand-parsed `pyproject.toml`
|
||||
- Observable confirmed live (independently re-verified by review, both install paths): fresh scratch-venv editable install (`pip install --no-deps -e .`, no meson) -> `xceadmin --version` prints `0.1.0 (unknown, unknown)`, exit 0, no import error; the meson-built wheel installed into a separate scratch venv -> `--version` shows real, non-`unknown` commit id + build time. Full meson build/test: 261 passed, 3 skipped (258 baseline + 3 new tests)
|
||||
- _Requirements: 1.1, 1.2, 2.1_
|
||||
- _Boundary: xceadmin/src/xceadmin_
|
||||
- _Depends: 5.1_
|
||||
|
||||
- [ ] 6. Integration: cross-toolchain consistency and regression verification
|
||||
- [ ] 6.1 Motivating-incident regression test
|
||||
|
|
|
|||
19
src/xceadmin/src/xceadmin/_build_info.py
Normal file
19
src/xceadmin/src/xceadmin/_build_info.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
"""Committed fallback build-info stub (build-time-versioning task 5.2).
|
||||
|
||||
Both fields are always "unknown" here. This file ships checked into git so
|
||||
that a non-meson `pip install -e .` (the documented fast-iteration path,
|
||||
`build-deployment.md`) still imports `xceadmin._build_info` cleanly without
|
||||
ever having run the meson build -- Requirement 1.4's "record as unknown
|
||||
rather than fail" applies here too.
|
||||
|
||||
When built via the meson custom_target (`src/xceadmin/meson-build.sh`, task
|
||||
5.1), this exact filename is regenerated with real `GIT_COMMIT`/`BUILD_TIME`
|
||||
values -- but only in meson's own out-of-tree MIRRORED copy of this package
|
||||
(`$BUILD_DIR/repo-mirror/src/xceadmin/src/xceadmin/_build_info.py`). This
|
||||
checked-in copy, under the live source tree, is never overwritten.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
GIT_COMMIT = "unknown"
|
||||
BUILD_TIME = "unknown"
|
||||
|
|
@ -29,6 +29,8 @@ from __future__ import annotations
|
|||
|
||||
from collections.abc import Callable
|
||||
from datetime import datetime, timezone
|
||||
from importlib.metadata import PackageNotFoundError
|
||||
from importlib.metadata import version as _package_version
|
||||
|
||||
import click
|
||||
|
||||
|
|
@ -37,6 +39,41 @@ from xceadmin.build import build_cluster_config, dump_cluster_config, write_outp
|
|||
from xceadmin.push import probe_config, push_config
|
||||
from xceadmin.scheduler import RecordResult, dump, record_start, record_status, record_stop
|
||||
|
||||
try:
|
||||
from xceadmin import _build_info
|
||||
except ImportError: # pragma: no cover - defensive only: a committed
|
||||
# "unknown"-only fallback stub (task 5.2, src/xceadmin/src/xceadmin/
|
||||
# _build_info.py) always ships in-tree, and the meson build (task 5.1)
|
||||
# overwrites it with real values -- this module should therefore always
|
||||
# be importable in practice. Guarded anyway per the task's own "import
|
||||
# defensively" wording, matching this feature's established empty ->
|
||||
# "unknown" fail-soft discipline (go_build, meson-build.sh,
|
||||
# xce_version_field()) rather than letting a genuinely broken/partial
|
||||
# install turn into an import error at CLI startup (Requirement 1.4).
|
||||
class _build_info: # type: ignore[no-redef]
|
||||
GIT_COMMIT = "unknown"
|
||||
BUILD_TIME = "unknown"
|
||||
|
||||
|
||||
def _compose_version() -> str:
|
||||
"""Composes the string `xceadmin --version` prints: pyproject.toml's
|
||||
static semantic version -- read via ``importlib.metadata`` (the standard
|
||||
way to read an *installed* package's own version, rather than
|
||||
hand-parsing pyproject.toml) -- plus the two build-info fields, which are
|
||||
either task 5.1's meson-generated real values or task 5.2's committed
|
||||
"unknown" fallback, whichever this install currently carries
|
||||
(Requirements 1.1, 1.2, 2.1).
|
||||
"""
|
||||
try:
|
||||
pkg_version = _package_version("xceadmin")
|
||||
except PackageNotFoundError: # pragma: no cover - only if xceadmin
|
||||
# itself isn't installed/registered, which shouldn't happen for an
|
||||
# entry point already running as `xceadmin`; fails soft to
|
||||
# "unknown" rather than raising, matching Requirement 1.4.
|
||||
pkg_version = "unknown"
|
||||
return f"{pkg_version} ({_build_info.GIT_COMMIT}, {_build_info.BUILD_TIME})"
|
||||
|
||||
|
||||
_DEFAULT_OUTPUT = "cluster.bin"
|
||||
_DEFAULT_CONTROLLER = "localhost:9991"
|
||||
# Every --controller option below also reads this env var (click precedence:
|
||||
|
|
@ -51,6 +88,7 @@ _STATUS_LABEL_WIDTH = 13 # len("started at:") + 2 -- requirements.md's "Status
|
|||
|
||||
|
||||
@click.group()
|
||||
@click.version_option(version=_compose_version(), prog_name="xceadmin")
|
||||
def cli() -> None:
|
||||
"""xceadmin: build and deploy XCE cluster configuration."""
|
||||
|
||||
|
|
|
|||
|
|
@ -70,6 +70,57 @@ def test_xcecommon_is_importable_independent_of_xceadmin() -> None:
|
|||
import xcecommon # noqa: F401 - import success is the assertion
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# --version (build-time-versioning task 5.2): click.version_option() wired on
|
||||
# the top-level group, composing pyproject.toml's static semantic version
|
||||
# with the two _build_info fields (task 5.1's meson-generated real values, or
|
||||
# task 5.2's committed "unknown"-only fallback stub for a non-meson `pip
|
||||
# install -e .`). Observable: `xceadmin --version` prints and exits 0, never
|
||||
# an import error, regardless of which _build_info variant is installed.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_cli_version_flag_exits_zero_and_shows_package_version() -> None:
|
||||
"""`xceadmin --version` must exit 0 and print pyproject.toml's static
|
||||
semantic version (0.1.0) plus build-info fields -- Requirements 1.1,
|
||||
1.2, 2.1."""
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(cli, ["--version"])
|
||||
|
||||
assert result.exit_code == 0, result.output
|
||||
assert "0.1.0" in result.output # pyproject.toml's static [project].version
|
||||
assert "version" in result.output.lower()
|
||||
|
||||
|
||||
def test_cli_version_flag_includes_build_info_fields() -> None:
|
||||
"""The printed --version string must include BOTH _build_info fields
|
||||
(whichever variant -- "unknown" fallback or meson-generated real values
|
||||
-- is currently installed), not just the bare package version, so
|
||||
--version and the eventual startup log line (out of this task's scope)
|
||||
can never disagree (Requirement 2.2)."""
|
||||
from xceadmin import _build_info
|
||||
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(cli, ["--version"])
|
||||
|
||||
assert result.exit_code == 0, result.output
|
||||
assert _build_info.GIT_COMMIT in result.output
|
||||
assert _build_info.BUILD_TIME in result.output
|
||||
|
||||
|
||||
def test_build_info_module_is_importable_with_string_fields() -> None:
|
||||
"""xceadmin._build_info must always be importable with both fields
|
||||
present as non-empty strings -- whether it's the committed "unknown"-only
|
||||
fallback stub (task 5.2, a non-meson `pip install -e .`) or the
|
||||
meson-generated real-value module (task 5.1) this test run happens to be
|
||||
installed against. Never an import error (tasks.md 5.2's Observable
|
||||
bullet, Requirement 1.4)."""
|
||||
from xceadmin import _build_info
|
||||
|
||||
assert isinstance(_build_info.GIT_COMMIT, str) and _build_info.GIT_COMMIT
|
||||
assert isinstance(_build_info.BUILD_TIME, str) and _build_info.BUILD_TIME
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# config build
|
||||
# ---------------------------------------------------------------------------
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue