Files
app-catalog-felhom.eu/scripts/check-probe-measured.py
T

83 lines
3.3 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""check-probe-measured.py — a `setup_done_probe:` must say what it MEASURED, before and after (R-715).
WHY. The setup gate (`09` §3 decision 46) opens an app when its own status says the first admin exists. A probe
that never flips keeps the app closed to everyone but the household — and it also makes the household's "Done"
press refuse (the press asks the probe first). Measured 2026-09-29 on gramps-web: its status answered 405 after the
setup, the box read only 200s, and the press was refused until the catalog was fixed. And zipline's upstream status
route answered 403 after the setup. A probe copied from upstream docs is a guess; only a before/after measurement on
a real install is evidence.
THE RULE. The comment block directly above `setup_done_probe:` (the contiguous `#` lines) must carry:
* the word "measured",
* a date (YYYY-MM-DD), and
* both answers: "before … after …", or "<value> -> <value>".
A comment elsewhere in the file (the tagline, another block) does not count.
Run from the repo root: python3 scripts/check-probe-measured.py [--root=DIR] [app ...]
Exit 0 all probes carry a measurement · 1 a probe without one.
"""
import os
import re
import sys
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
DATE = re.compile(r"\b20\d\d-\d\d-\d\d\b")
def comment_above(lines, i):
out = []
j = i - 1
while j >= 0 and lines[j].lstrip().startswith("#"):
out.append(lines[j].lstrip()[1:].strip())
j -= 1
return " ".join(reversed(out))
def check(path):
lines = open(path, encoding="utf-8").read().split("\n")
for i, l in enumerate(lines):
if l.startswith("setup_done_probe:"):
c = comment_above(lines, i)
low = c.lower()
has_pair = "->" in c or ("before" in low and "after" in low)
if "measured" not in low or not DATE.search(c) or not has_pair:
return "the comment above setup_done_probe: lacks %s (found: %r)" % (
", ".join(x for x, ok in (("the word 'measured'", "measured" in low), ("a date", bool(DATE.search(c))),
("the before/after answers", has_pair)) if not ok), c[:160])
return None
def main():
root = ROOT
apps = []
for a in sys.argv[1:]:
if a.startswith("--root="):
root = a.split("=", 1)[1]
else:
apps.append(a)
tdir = os.path.join(root, "templates")
names = apps or sorted(d for d in os.listdir(tdir) if os.path.isdir(os.path.join(tdir, d)))
bad, n = [], 0
for app in names:
for dp, _, files in os.walk(os.path.join(tdir, app)):
if ".felhom.yml" in files and os.path.basename(dp) == app:
p = os.path.join(dp, ".felhom.yml")
if "\nsetup_done_probe:" in open(p, encoding="utf-8").read():
n += 1
why = check(p)
if why:
bad.append("%s: %s" % (app, why))
if bad:
print("probe-measured: FAIL — %d of %d probes carry no measurement:" % (len(bad), n))
for b in bad:
print(" - " + b)
return 1
print("probe-measured: OK — %d probes, each with a measured before/after" % n)
return 0
if __name__ == "__main__":
sys.exit(main())