Offline-installable bundle (wheelhouse + model + source) built on a machine with internet and installed on the air-gapped Pi with --no-index (zero fetches), plus a systemd unit (auto-restart, device access, hardening) and env template. Runs the package from source via python -m edge (no offline build backend). Drift-guard test keeps the env template in sync with what the daemon reads and rejects inline comments that systemd EnvironmentFile= would fold into values.
57 lines
2.2 KiB
Python
57 lines
2.2 KiB
Python
"""Guard: the systemd unit + env template must stay in sync with what the daemon
|
|
actually reads. If someone renames an env var in edge/config.py or edge/daemon.py
|
|
(or edits the unit), this fails instead of shipping a broken bundle.
|
|
"""
|
|
import os
|
|
|
|
DEPLOY = os.path.join(os.path.dirname(__file__), "..", "deploy", "systemd")
|
|
ENV_EXAMPLE = os.path.join(DEPLOY, "move-detect-edge.env.example")
|
|
UNIT = os.path.join(DEPLOY, "move-detect-edge.service")
|
|
|
|
# Keys the daemon consumes (edge/config.py:load_config + edge/daemon.py:main).
|
|
REQUIRED_ENV = {
|
|
"DEVICE_ID", "DEVICE_SECRET", "CAFE24_URL", "CAMERA_ID",
|
|
"MOVENET_ONNX", "UVC_DEVICE", "TARGET_FPS", "QUEUE_DB",
|
|
}
|
|
|
|
|
|
def _env_keys(text):
|
|
keys = set()
|
|
for line in text.splitlines():
|
|
line = line.strip()
|
|
if not line or line.startswith("#") or "=" not in line:
|
|
continue
|
|
keys.add(line.split("=", 1)[0].strip())
|
|
return keys
|
|
|
|
|
|
def test_env_example_lists_every_required_key():
|
|
with open(ENV_EXAMPLE, encoding="utf-8") as f:
|
|
keys = _env_keys(f.read())
|
|
missing = REQUIRED_ENV - keys
|
|
assert not missing, f"env template missing required keys: {sorted(missing)}"
|
|
|
|
|
|
def test_env_example_values_have_no_inline_comments():
|
|
# systemd EnvironmentFile= does NOT strip inline comments — a trailing "# ..." on
|
|
# a KEY=value line becomes part of the value and breaks int()/float()/path/secret.
|
|
with open(ENV_EXAMPLE, encoding="utf-8") as f:
|
|
offenders = []
|
|
for line in f.read().splitlines():
|
|
s = line.strip()
|
|
if not s or s.startswith("#") or "=" not in s:
|
|
continue
|
|
value = s.split("=", 1)[1]
|
|
if "#" in value:
|
|
offenders.append(s)
|
|
assert not offenders, f"inline comments in env values break systemd: {offenders}"
|
|
|
|
|
|
def test_systemd_unit_wiring():
|
|
with open(UNIT, encoding="utf-8") as f:
|
|
unit = f.read()
|
|
assert "EnvironmentFile=/etc/move-detect/edge.env" in unit
|
|
assert "PYTHONPATH=/opt/move-detect/src" in unit
|
|
assert "python -m edge" in unit # runs the package entrypoint
|
|
assert "Restart=always" in unit # unattended resilience
|
|
assert "WantedBy=multi-user.target" in unit
|