STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
which_firmware.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2"""Answer "which firmware is this board running?" from a digest it reported.
3
4 which_firmware.py 5f2885e2 # a prefix is enough
5 which_firmware.py 5f2885e2 --registry DIR # a registry tree other than the default
6
7The digest is what a board puts in its heartbeat and what the DAQ shows in the
8config panel. CI records one file per distinct image on the `firmware-registry`
9branch, so get that branch first:
10
11 git fetch origin firmware-registry:firmware-registry
12 git worktree add /tmp/fwreg firmware-registry
13
14and point --registry at it, or set STAR_FIRMWARE_REGISTRY.
15"""
16
17from __future__ import annotations
18
19import argparse
20import json
21import os
22import sys
23from pathlib import Path
24
25
26def find(registry: Path, prefix: str) -> list[dict]:
27 prefix = prefix.lower().strip()
28 hits = []
29 for path in sorted(registry.rglob("*.json")):
30 try:
31 rec = json.loads(path.read_text())
32 except (json.JSONDecodeError, OSError):
33 continue
34 if str(rec.get("image_digest", "")).lower().startswith(prefix):
35 rec["_path"] = str(path)
36 hits.append(rec)
37 return hits
38
39
40def main() -> int:
41 ap = argparse.ArgumentParser(description=__doc__.splitlines()[0])
42 ap.add_argument("digest", help="full digest or a unique prefix (case-insensitive)")
43 ap.add_argument(
44 "--registry",
45 default=os.environ.get("STAR_FIRMWARE_REGISTRY", ""),
46 help="registry tree (default: $STAR_FIRMWARE_REGISTRY)",
47 )
48 args = ap.parse_args()
49
50 if len(args.digest.strip()) < 4:
51 print("error: give at least 4 hex characters", file=sys.stderr)
52 return 2
53 if not args.registry:
54 print(
55 "error: no registry tree given. Pass --registry or set "
56 "STAR_FIRMWARE_REGISTRY; see this file's docstring for how to get "
57 "the firmware-registry branch.",
58 file=sys.stderr,
59 )
60 return 2
61
62 registry = Path(args.registry)
63 if not registry.is_dir():
64 print(f"error: {registry} is not a directory", file=sys.stderr)
65 return 2
66
67 hits = find(registry, args.digest)
68 if not hits:
69 if args.digest.strip("0") == "":
70 print(
71 "All-zero digest: the board did not report one. Either it has "
72 "not computed it yet, or esp_partition_get_sha256() failed, "
73 "which means the image on it does not verify."
74 )
75 return 1
76 print(
77 f"No image in the registry starts with {args.digest!r}.\n"
78 "That means this firmware was never built by CI on main -- a local "
79 "build, a branch, or an image from before the registry existed."
80 )
81 return 1
82 if len(hits) > 1:
83 print(f"{args.digest!r} matches {len(hits)} images; give more characters:")
84 for rec in hits:
85 print(f" {rec['image_digest']} {rec.get('board')}")
86 return 1
87
88 rec = hits[0]
89 commits = rec.get("commits") or []
90 print(f"board {rec.get('board')}")
91 print(f"digest {rec['image_digest']}")
92 print(f"image_len {rec.get('image_len')}")
93 if len(commits) == 1:
94 print(f"commit {commits[0]}")
95 else:
96 print(f"commits {len(commits)} commits build these exact bytes:")
97 for c in commits:
98 print(f" {c}")
99 for key in (
100 "subject",
101 "first_seen_commit_at",
102 "platform",
103 "framework",
104 "toolchains",
105 "esptool",
106 "gcc",
107 "built_by",
108 "run_url",
109 ):
110 if rec.get(key):
111 print(f"{key:14} {rec[key]}")
112 print(f"record {rec['_path']}")
113 return 0
114
115
116if __name__ == "__main__":
117 sys.exit(main())
list[dict] find(Path registry, str prefix)
int main()