Files
forgefirm/scripts/check-ui-vendor.py
T
ScottW514 e99888753f forgetest's page moves onto Bootstrap with forgectrl's theme; forgectrl pinned at the panel overhaul
The acceptance page is assembled by page.py from forgetest/forgetest/ui/
(index.html, page.css, help.js, app.js) plus theme.css and the vendored
Bootstrap files, which are byte for byte the ones forgectrl's panel
carries, so the two pages look like one product and share the light and
dark themes (same localStorage key). A plain file is read in a checkout;
on the dev image the recipe installs ui/ gzipped and page.py reads the
.gz sibling, inflating once at first request: the rootfs is raw ext4, so
bytes in the package are bytes on the image. The explanatory prose
(campaign rules, the queues, the campaign actions, the prerequisites
switch, the bench intro) is a "?" popover with a link into the
documentation site; operator steps, prompts, notices and the live-laser
acknowledgment stay in the page, and confirmLive() stays a blocking
dialog. The page's own rules hold: rows, prompt buttons and tool entries
are built once and updated in place, and the popovers sit on static
markup only, so no rebuild orphans one. On a phone the Run pane goes to
the top for the duration of a run.

scripts/check-ui-vendor.py compares the shared files against forgectrl
at its pinned revision (or a local checkout with --forgectrl); it runs
in forgetest-ci.yml, so the copies cannot drift.

Tests: test_page.py (the gzipped install assembles to the same bytes as
a checkout, one self-contained response, the token placeholder once, a
missing marker refused); test_server asserts the served page's
invariants; test_responsiveness keeps its rules with needles pointed at
the new files, its ASCII rule applied to our own sources (Bootstrap's
CSS carries an em dash of its own), and its self-contained rule testing
asset tags rather than the presence of https:// (the documentation links
are meant to be there). forgectrl.panel-serves gains two needles for the
panel's theme attribute and save bar. Proof: the unit suite, and the
page in Chrome against a fake catalog (both themes, popovers, the bench
tab, a full operator run with its prompt, abort).

forgectrl pinned at 9d1f6f2 (the panel on Bootstrap, one save bar, help
popovers, themes, the gzipped page); PV unchanged. The pin moves only
forgectrl's fingerprint. The forgetest changes are the harness's own and
have no catalog consequence.
2026-08-24 12:13:53 -04:00

90 lines
3.1 KiB
Python

#!/usr/bin/env python3
"""The files forgetest's page shares with forgectrl's panel, byte for byte.
theme.css (the OpenGlow theme on Bootstrap) and the vendored Bootstrap
files live in both repos so each page is self-contained; they are meant
to be identical, and this is the check. forgetest's copies are compared
against forgectrl's at the revision the recipe pins (fetched from GitHub
by default) or in a local checkout (--forgectrl PATH). Exit status 1 on
any difference or missing file.
python3 scripts/check-ui-vendor.py
python3 scripts/check-ui-vendor.py --forgectrl ../forgectrl
"""
import argparse
import hashlib
import os
import re
import sys
import urllib.request
HERE = os.path.dirname(os.path.abspath(__file__))
REPO = os.path.normpath(os.path.join(HERE, ".."))
SHARED = ("theme.css", "vendor/bootstrap.min.css", "vendor/bootstrap.bundle.min.js",
"vendor/LICENSE")
FORGETEST_UI = os.path.join(REPO, "forgetest", "forgetest", "ui")
PIN = os.path.join(REPO, "meta-forgefirm", "recipes-forgefirm", "forgectrl", "forgectrl-pin.inc")
RAW = "https://raw.githubusercontent.com/ScottW514/forgectrl/%s/src/ui/%s"
def pinned_rev():
with open(PIN, encoding="utf-8") as f:
m = re.search(r'^SRCREV\s*=\s*"([0-9a-f]{7,40})"', f.read(), re.M)
if not m:
sys.exit("check-ui-vendor: no SRCREV in %s" % PIN)
return m.group(1)
def sha(data):
return hashlib.sha256(data).hexdigest()
def theirs(name, local, rev):
if local:
path = os.path.join(local, "src", "ui", *name.split("/"))
with open(path, "rb") as f:
return f.read(), path
url = RAW % (rev, name)
with urllib.request.urlopen(url, timeout=30) as r:
return r.read(), url
def main():
ap = argparse.ArgumentParser(description=__doc__.split("\n")[0])
ap.add_argument("--forgectrl", help="a local forgectrl checkout instead of the pinned revision")
args = ap.parse_args()
rev = None if args.forgectrl else pinned_rev()
print("forgectrl: %s" % (args.forgectrl or ("pinned %s" % rev)))
bad = 0
for name in SHARED:
ours_path = os.path.join(FORGETEST_UI, *name.split("/"))
try:
with open(ours_path, "rb") as f:
ours = f.read()
except OSError as e:
print(" MISSING forgetest copy of %s (%s)" % (name, e))
bad += 1
continue
try:
other, where = theirs(name, args.forgectrl, rev)
except Exception as e: # noqa: BLE001
print(" MISSING forgectrl copy of %s (%s)" % (name, e))
bad += 1
continue
if sha(ours) == sha(other):
print(" ok %s (%d bytes, %s)" % (name, len(ours), sha(ours)[:12]))
else:
print(" DIFFERS %s: forgetest %s, forgectrl %s (%s)"
% (name, sha(ours)[:12], sha(other)[:12], where))
bad += 1
if bad:
print("check-ui-vendor: %d shared file(s) out of step; copy from forgectrl/src/ui/ "
"(or push forgectrl and bump its pin)" % bad)
return 1
print("check-ui-vendor: the shared UI files are identical")
return 0
if __name__ == "__main__":
sys.exit(main())