mirror of
https://github.com/torlando-tech/pyxis.git
synced 2026-08-24 03:29:58 +00:00
136 lines
4.5 KiB
Python
136 lines
4.5 KiB
Python
#!/usr/bin/env python3
|
|
"""Fail closed when a tdeck-release build is not release-qualified."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import shutil
|
|
import subprocess
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
ENVIRONMENT = "tdeck-release"
|
|
BUILD = ROOT / ".pio" / "build" / ENVIRONMENT
|
|
EXCLUDED_DEFINITIONS = (
|
|
"PYXIS_TEST_HOOKS",
|
|
"PYXIS_TEST_TCP_HOST",
|
|
"PYXIS_TEST_TCP_PORT",
|
|
"MEMORY_INSTRUMENTATION_ENABLED",
|
|
"BOOT_PROFILING_ENABLED",
|
|
)
|
|
EXCLUDED_STRINGS = (
|
|
b"T:CALL_PROFILE",
|
|
b"T:CALL_INJECT",
|
|
b"T:DEST",
|
|
b"PYXIS_TEST_HOOKS",
|
|
b"Boot Profile Summary",
|
|
b"Memory monitor started",
|
|
b"Firmware: v1.0.0",
|
|
b"SPIFFS FileSystem mount failed",
|
|
b"tile.openstreetmap.org",
|
|
b"Download map tiles:",
|
|
)
|
|
REQUIRED_STRINGS = (
|
|
b"FileSystem mount failed; preserving persistent data",
|
|
b"Blank LittleFS partition; formatting once",
|
|
)
|
|
EXCLUDED_SYMBOLS = (
|
|
"test_call_",
|
|
"process_test_serial_command",
|
|
"MemoryMonitor::",
|
|
"BootProfiler::",
|
|
"MapTileDownloader::",
|
|
"MapTileHttpArduino::",
|
|
)
|
|
PINNED_DEPENDENCIES = {
|
|
"microReticulum": "cd0338e7fc07d3a7785a450656ba766491cbf6e8",
|
|
"microLXMF": "60fb7d6951bd17275da83fdd9581900d55b0a2ab",
|
|
"microStore": "2762f7606800ffb23f4a593947d4f58e259cda7a",
|
|
}
|
|
|
|
|
|
def run(*args: str, cwd: Path = ROOT) -> str:
|
|
return subprocess.run(
|
|
args,
|
|
cwd=cwd,
|
|
check=True,
|
|
text=True,
|
|
capture_output=True,
|
|
).stdout.strip()
|
|
|
|
|
|
def require(condition: bool, message: str) -> None:
|
|
if not condition:
|
|
raise SystemExit(f"release audit failed: {message}")
|
|
|
|
|
|
def package_commit(name: str) -> str:
|
|
package = ROOT / ".pio" / "libdeps" / ENVIRONMENT / name
|
|
require(package.is_dir(), f"missing dependency package {name}")
|
|
top = Path(run("git", "rev-parse", "--show-toplevel", cwd=package)).resolve()
|
|
require(top == package.resolve(), f"{name} is not an independently verifiable VCS package")
|
|
return run("git", "rev-parse", "HEAD", cwd=package)
|
|
|
|
|
|
def locate_nm() -> str:
|
|
direct = shutil.which("xtensa-esp32s3-elf-nm")
|
|
if direct:
|
|
return direct
|
|
candidate = (
|
|
Path.home()
|
|
/ ".platformio/packages/toolchain-xtensa-esp32s3/bin/xtensa-esp32s3-elf-nm"
|
|
)
|
|
require(candidate.is_file(), "xtensa-esp32s3-elf-nm not found")
|
|
return str(candidate)
|
|
|
|
|
|
def main() -> None:
|
|
run("pio", "run", "-e", ENVIRONMENT, "-t", "compiledb")
|
|
|
|
compile_commands = ROOT / "compile_commands.json"
|
|
commands = json.loads(compile_commands.read_text())
|
|
main_compile = next(
|
|
entry for entry in commands if Path(entry["file"]).as_posix().endswith("src/main.cpp")
|
|
)
|
|
command = main_compile.get("command") or " ".join(main_compile["arguments"])
|
|
present = [flag for flag in EXCLUDED_DEFINITIONS if flag in command]
|
|
require(not present, f"excluded compiler definitions present: {', '.join(present)}")
|
|
|
|
firmware = BUILD / "firmware.bin"
|
|
elf = BUILD / "firmware.elf"
|
|
require(firmware.is_file(), "firmware.bin missing")
|
|
require(elf.is_file(), "firmware.elf missing")
|
|
|
|
firmware_bytes = firmware.read_bytes()
|
|
strings_present = [value.decode() for value in EXCLUDED_STRINGS if value in firmware_bytes]
|
|
require(not strings_present, f"excluded firmware strings present: {', '.join(strings_present)}")
|
|
strings_missing = [value.decode() for value in REQUIRED_STRINGS if value not in firmware_bytes]
|
|
require(not strings_missing, f"required firmware strings missing: {', '.join(strings_missing)}")
|
|
|
|
expected_version = os.environ.get("PYXIS_VERSION_OVERRIDE") or run(
|
|
"git", "describe", "--tags", "--always", "--dirty"
|
|
).removeprefix("v")
|
|
expected_version_label = f"Firmware: {expected_version}".encode()
|
|
require(expected_version_label in firmware_bytes, f"embedded version label missing: {expected_version}")
|
|
|
|
symbols = run(locate_nm(), "-C", str(elf))
|
|
symbols_present = [value for value in EXCLUDED_SYMBOLS if value in symbols]
|
|
require(not symbols_present, f"excluded ELF symbols present: {', '.join(symbols_present)}")
|
|
|
|
for name, expected in PINNED_DEPENDENCIES.items():
|
|
actual = package_commit(name)
|
|
require(actual == expected, f"{name} resolved to {actual}, expected {expected}")
|
|
|
|
compile_commands.unlink()
|
|
print("release audit passed")
|
|
print(f"firmware_size={len(firmware_bytes)}")
|
|
print(f"firmware_version={expected_version}")
|
|
for name, expected in PINNED_DEPENDENCIES.items():
|
|
print(f"{name}={expected}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|