Unveränderter Stand von certvia/dev (a48c5fb) plus Craftvia-Spezifikation und Brandbook unter docs/craftvia/. ISMS-Module werden im Folgecommit entfernt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
80 lines
2.9 KiB
Python
80 lines
2.9 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
Regressionsschutz: Rendert alle Richtlinien in einer Framework-Sicht und vergleicht das
|
|
Ergebnis mit einem Git-Stand. Erwartung fuer TISAX: 0 Abweichungen.
|
|
|
|
python3 _render_diff.py <git-revision> [--framework TISAX|ISO|BEIDE]
|
|
|
|
Beispiele:
|
|
python3 _render_diff.py a9649b3 # TISAX-Sicht gegen den Stand vor dem ISO-Umbau
|
|
python3 _render_diff.py dev --framework ISO # ISO-Sicht gegen dev
|
|
|
|
Verglichen wird der gerenderte Text ohne Leerzeilen und ohne Kommentar-Anker — also das,
|
|
was ein Mandant tatsaechlich zu sehen bekommt.
|
|
"""
|
|
import difflib, glob, json, os, re, subprocess, sys
|
|
|
|
BASE = os.path.dirname(os.path.abspath(__file__))
|
|
REPO = subprocess.run(["git", "rev-parse", "--show-toplevel"], cwd=BASE,
|
|
capture_output=True, text=True).stdout.strip()
|
|
|
|
if len(sys.argv) < 2:
|
|
print(__doc__); sys.exit(2)
|
|
REV = sys.argv[1]
|
|
FW = "TISAX"
|
|
if "--framework" in sys.argv:
|
|
FW = sys.argv[sys.argv.index("--framework") + 1].upper()
|
|
|
|
schema = json.load(open(os.path.join(BASE, "variables.schema.json"), encoding="utf-8"))
|
|
|
|
|
|
def ctx():
|
|
c = {k: (True if k.startswith("FLAG_") else "{%s}" % k) for k in schema["properties"]}
|
|
for k in ["FLAG_OT_USED", "FLAG_DEV_INHOUSE", "FLAG_CRYPTO_PKI", "FLAG_CUSTOMER_SYSTEMS"]:
|
|
c[k] = False
|
|
c["FLAG_FW_TISAX"] = FW in ("TISAX", "BEIDE")
|
|
c["FLAG_FW_ISO27001"] = FW in ("ISO", "BEIDE")
|
|
return c
|
|
|
|
|
|
def render(txt, c):
|
|
pat = re.compile(r"\{\{#if (\w+)\}\}((?:(?!\{\{#if )(?!\{\{/if\}\}).)*?)\{\{/if\}\}", re.S)
|
|
prev = None
|
|
while prev != txt:
|
|
prev = txt
|
|
txt = pat.sub(lambda m: (m.group(2) if c.get(m.group(1)) else ""), txt)
|
|
txt = re.sub(r"<!--.*?-->", "", txt, flags=re.S)
|
|
txt = re.sub(r"\{\{LINK:([^}]+)\}\}", r"[\1]", txt)
|
|
txt = re.sub(r"\{\{(\w+)\}\}", lambda m: str(c.get(m.group(1), "?")), txt)
|
|
return [l.rstrip() for l in txt.split("\n") if l.strip()]
|
|
|
|
|
|
def at_rev(relpath):
|
|
r = subprocess.run(["git", "show", "%s:%s" % (REV, relpath)], cwd=REPO,
|
|
capture_output=True, text=True)
|
|
return r.stdout if r.returncode == 0 else None
|
|
|
|
|
|
c = ctx()
|
|
drift, missing, checked = 0, 0, 0
|
|
for path in sorted(glob.glob(os.path.join(BASE, "richtlinien", "*.md"))):
|
|
rel = os.path.relpath(path, REPO)
|
|
old_raw = at_rev(rel)
|
|
if old_raw is None:
|
|
missing += 1
|
|
print(" neu in dieser Fassung:", os.path.basename(path))
|
|
continue
|
|
checked += 1
|
|
old, new = render(old_raw, c), render(open(path, encoding="utf-8").read(), c)
|
|
if old != new:
|
|
drift += 1
|
|
print("ABWEICHUNG:", os.path.basename(path))
|
|
for line in list(difflib.unified_diff(old, new, lineterm="", n=0))[:10]:
|
|
print(" ", line[:140])
|
|
|
|
print()
|
|
print("Sicht: %s · Vergleichsstand: %s" % (FW, REV))
|
|
print("geprueft: %d Richtlinien, neu: %d, abweichend: %d" % (checked, missing, drift))
|
|
print("OK" if drift == 0 else "PRUEFEN")
|
|
sys.exit(0 if drift == 0 else 1)
|