mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-06-22 16:01:14 +00:00
[AZ-402] Replay — gps-denied-replay console-script + shared main(config)
Implements the replay-mode CLI dispatcher per ADR-011 (replay-as- configuration): - src/gps_denied_onboard/cli/replay.py: argparse with all 6 required args (--video, --tlog, --output, --camera-calibration, --config, --mavlink-signing-key) plus --pace and --time-offset-ms; path validation, calibration JSON schema-validation, config mutation (mode='replay' + replay sub-block + signing-key hex on dev_static field), dispatch into runtime_root.main(config). - runtime_root.main() now accepts an optional Config (additive, backward-compat). Adds dedicated catch for ReplayInputAdapterError mapping to EXIT_FDR_OPEN_FAILURE (2) so the CLI's exit-code matrix holds end-to-end (AC-9 + epic AZ-265 AC-8). - Signing-key contents stored as hex; redacted in startup banner. - Top-level except logs full traceback via logger.exception + stderr print and exits 1. The CLI does NOT call compose_root directly — it builds a Config and hands it to the shared airborne main, which calls compose_root, which branches on config.mode (AZ-401 / replay protocol Invariant 11). Tests: 22 unit tests covering AC-1..AC-10 + extras (signing-key redaction, file-not-dir validation, dev_static propagation, unhandled exception traceback). Full regression: 2085 passed (+22) green; no new flaky tests. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -618,10 +618,39 @@ def _read_flight_root(config: Config) -> str:
|
||||
return str(path) if path is not None else "<unknown>"
|
||||
|
||||
|
||||
def main() -> int: # pragma: no cover — guarded entrypoint
|
||||
def main(config: Config | None = None) -> int:
|
||||
"""Shared airborne-binary entrypoint.
|
||||
|
||||
Both the live ``gps-denied-onboard`` console-script and the replay
|
||||
``gps-denied-replay`` console-script (AZ-402) dispatch here. When
|
||||
``config`` is ``None`` the live binary's behaviour is preserved: load
|
||||
from environment + default paths and compose. When a pre-built
|
||||
``Config`` is supplied (replay CLI), it is used directly so the CLI
|
||||
can mutate ``config.mode = "replay"`` + populate the replay sub-block
|
||||
before the airborne main runs.
|
||||
|
||||
Per ADR-011 there is one composition root, ``compose_root``, which
|
||||
branches on ``config.mode``. The CLI MUST NOT call ``compose_root``
|
||||
directly (replay protocol Invariant 11).
|
||||
|
||||
Exit codes:
|
||||
|
||||
* ``0`` — success.
|
||||
* ``EXIT_FDR_OPEN_FAILURE`` (``2``) — operator-visible startup hard-fail:
|
||||
FDR cannot open OR replay auto-sync impossible (AZ-405 AC-8 / epic
|
||||
AZ-265 AC-8). Both share the code because both demand operator
|
||||
action before the binary can run.
|
||||
* ``EXIT_GENERIC_FAILURE`` (``1``) — any other error.
|
||||
"""
|
||||
from gps_denied_onboard.replay_input import ReplayInputAdapterError
|
||||
|
||||
try:
|
||||
config = load_config(env=os.environ, paths=())
|
||||
if config is None:
|
||||
config = load_config(env=os.environ, paths=())
|
||||
compose_root(config)
|
||||
except ReplayInputAdapterError as exc:
|
||||
print(f"runtime_root: replay sync impossible: {exc}", file=sys.stderr)
|
||||
return EXIT_FDR_OPEN_FAILURE
|
||||
except (ConfigurationError, StrategyNotLinkedError, RuntimeError) as exc:
|
||||
print(f"runtime_root: {exc}", file=sys.stderr)
|
||||
return EXIT_GENERIC_FAILURE
|
||||
|
||||
Reference in New Issue
Block a user