🔒 Repository is read-only – file editing is disabled.

PaganLinux/tmp-verify-recipe-semantics.py main

101 linii Raw ← Powrót
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
#!/usr/bin/env python3
"""Porownuje SEMANTYKE (yaml.safe_load) wersji HEAD i roboczej kazdej zmienionej receptury.

Wypisuje dla kazdego pliku:
  - klucze, ktore realnie zmienily wartosc
  - informacje, czy roznica jest wylacznie formatowaniem (te same dane, inny zapis)
"""
import subprocess
import sys
import yaml

R = "/var/lib/pagan-sync/recipes"


def sh(*args):
    return subprocess.run(args, cwd=R, capture_output=True, text=True)


def load(text):
    return yaml.safe_load(text)


def main():
    st = sh("git", "status", "--porcelain")
    files = []
    for line in st.stdout.splitlines():
        if not line.strip():
            continue
        code, path = line[:2], line[3:].strip()
        if code.strip() == "??":
            continue
        if not path.endswith(".yaml"):
            continue
        files.append(path)

    print(f"plikow do sprawdzenia: {len(files)}\n")
    bad = []
    only_fmt = []
    real = []
    for path in files:
        try:
            head_text = sh("git", "show", f"HEAD:{path}").stdout
        except Exception as e:
            print(f"!! {path}: nie moge czytac HEAD ({e})")
            continue
        try:
            work_text = open(f"{R}/{path}", encoding="utf-8").read()
        except Exception as e:
            print(f"!! {path}: nie moge czytac pliku ({e})")
            continue

        try:
            a = load(head_text)
            b = load(work_text)
        except Exception as e:
            print(f"!! {path}: YAML nie parsuje sie: {e}")
            bad.append(path)
            continue

        if a == b:
            only_fmt.append(path)
            continue

        if not isinstance(a, dict) or not isinstance(b, dict):
            print(f"%% {path}: zmiana typu danych ({type(a).__name__} -> {type(b).__name__})")
            real.append((path, ["<typ>"]))
            continue

        keys = sorted(set(a) | set(b))
        diff_keys = []
        for k in keys:
            va, vb = a.get(k, "<brak>"), b.get(k, "<brak>")
            if va != vb:
                diff_keys.append(k)
        real.append((path, diff_keys))
        print(f"## {path}")
        for k in diff_keys:
            va, vb = a.get(k, "<brak>"), b.get(k, "<brak>")
            ra, rb = repr(va), repr(vb)
            if len(ra) > 160:
                ra = ra[:160] + f"...[len={len(str(va))}]"
            if len(rb) > 160:
                rb = rb[:160] + f"...[len={len(str(vb))}]"
            print(f"     {k}:\n       HEAD: {ra}\n       WORK: {rb}")
        print()

    print("=" * 70)
    print(f"semantycznie identyczne (tylko formatowanie): {len(only_fmt)}")
    for p in only_fmt:
        print(f"    ~ {p}")
    print(f"z realna zmiana tresci: {len(real)}")
    for p, ks in real:
        print(f"    * {p}: {', '.join(ks)}")
    if bad:
        print(f"NIEPARSOWALNE: {len(bad)}")
        for p in bad:
            print(f"    ! {p}")


if __name__ == "__main__":
    sys.exit(main())