Files
felhom.eu/scripts/iso/test/test_rootpw_guard.py
T

77 lines
3.6 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""R-587: a RELEASE ISO build refuses to start while a *.rootpw.txt sits in its out directory.
The out directory is the public bucket's publish source; a root-password file there was kept off the
bucket only by the publish command's --include pattern, by an accident of naming.
HOW IT RUNS ANYWHERE. The build script is bash and needs docker; the CI runner has neither (Alpine +
BusyBox + python3 + git). So this test lifts the `rootpw_guard` function out of build-felhom-iso.sh
VERBATIM and runs it under `sh` with a stub `die` — the function is written in POSIX sh for exactly
this. It also checks, statically, that the script CALLS the guard before the build does anything else
of consequence (the clean-tree gate and the docker preflight), because a guard that is defined and not
called is the seam-built-never-wired shape.
Run: python3 scripts/iso/test/test_rootpw_guard.py"""
import os
import re
import shutil
import subprocess
import sys
import tempfile
HERE = os.path.dirname(os.path.abspath(__file__))
BUILD = os.path.join(os.path.dirname(HERE), "build-felhom-iso.sh")
def guard_source():
text = open(BUILD, encoding="utf-8").read()
m = re.search(r"^rootpw_guard\(\) \{\n.*?^\}\n", text, re.S | re.M)
if not m:
raise SystemExit("FAIL: rootpw_guard() not found in build-felhom-iso.sh")
return text, m.group(0)
def run_guard(func, release, out_dir):
script = ('die() { echo "DIE: $1"; exit 1; }\nRELEASE=%s\nOUT_DIR=%s\n%s\nrootpw_guard\necho PROCEEDED\n'
% ("true" if release else "false", out_dir, func))
p = subprocess.run(["sh", "-c", script], capture_output=True, text=True)
return p.returncode, p.stdout + p.stderr
def main():
fails = []
text, func = guard_source()
tmp = tempfile.mkdtemp(prefix="rootpw-guard-")
try:
open(os.path.join(tmp, "felhom-installer-9.9.9-pve9.2-1.iso"), "w").close()
# 1 — the genuine article: a release with a clean out dir proceeds.
rc, out = run_guard(func, True, tmp)
if rc != 0 or "PROCEEDED" not in out:
fails.append("clean out dir: a release build was refused (rc=%d): %s" % (rc, out))
# 2 — the decoy: a stray rootpw file (named so the publish --include would MISS it) refuses.
stray = os.path.join(tmp, "felhom-pve-9.2-1-v1.24.0-nested.iso.rootpw.txt")
open(stray, "w").close()
rc, out = run_guard(func, True, tmp)
if rc == 0 or "PROCEEDED" in out or "rootpw.txt" not in out:
fails.append("stray rootpw file: the release build was NOT refused (rc=%d): %s" % (rc, out))
# 3 — a non-release build in the same dir proceeds (it is the producer of these files, not a publish).
rc, out = run_guard(func, False, tmp)
if rc != 0 or "PROCEEDED" not in out:
fails.append("non-release build was refused (rc=%d): %s" % (rc, out))
finally:
shutil.rmtree(tmp, ignore_errors=True)
# 4 — wired: the guard is CALLED, and before the clean-tree gate and the docker preflight.
call = re.search(r"^rootpw_guard\s*$", text, re.M)
later = [re.search(r"^clean_tree_gate\s*$", text, re.M), re.search(r"^command -v docker", text, re.M)]
if not call or any(m is None or m.start() < call.start() for m in later):
fails.append("rootpw_guard is not called before clean_tree_gate and the docker preflight")
if fails:
print("FAIL (R-587):\n " + "\n ".join(fails))
return 1
print("OK: a release build refuses a *.rootpw.txt in its out dir; a clean dir and a non-release build proceed")
return 0
if __name__ == "__main__":
sys.exit(main())