#!/usr/bin/env python3 """ apply-pagan-hooks.py – nakłada hooki pre/post-install na zmigrowane receptury. Obsługuje pakiety, które w cards generowały skrypty post-install/post-remove do $PKGMK_ROOT (kernele, moduły nvidia/vboxhost, pakiet bazowy nutyx). W nowym systemie pag uruchamia hooki z katalogu hooks/ w pakiecie (.pag), a pagbuild osadza je na podstawie pola `hooks:` w PAGBUILD.yaml. Kroki: 1. usuwa z faz build/package bloki `echo "..." > $PKGMK_ROOT/$name.post-*`, 2. dodaje pole `hooks:` z gotowymi skryptami (ścieżki absolutne, odporne na runtime – wersja jądra znajdowana globem /usr/lib/modules), 3. branding nutyx -> pagan w recepturze pakietu bazowego nutyx. Użycie: python3 apply-pagan-hooks.py [--recipes ./recipes] [--dry-run] """ import argparse import os import re import sys try: import yaml except ImportError: print("Brak PyYAML", file=sys.stderr) sys.exit(2) # ── Specyfikacja hooków ── def _kernel_hook(glob_pattern: str, initrd_name: str) -> str: return ( f'KREL="$(basename /usr/lib/modules/{glob_pattern} 2>/dev/null | head -n1)"\n' f'[ -n "$KREL" ] || exit 0\n' f'mkinitramfs "$KREL"\n' f'rm -f /boot/{initrd_name}\n' f'mv "/boot/initrd-$KREL" /boot/{initrd_name}' ) HOOKS = { "kernel-612": { "post-install": _kernel_hook("6.12.*", "initrd-612"), "post-remove": "rm -f /boot/initrd-612", }, "kernel-618": { "post-install": _kernel_hook("6.18.*", "initrd-618"), "post-remove": "rm -f /boot/initrd-618", }, "kernel-66": { "post-install": _kernel_hook("6.6.*", "initrd-66"), "post-remove": "rm -f /boot/initrd-66", }, "kernel": { "post-install": _kernel_hook("7*", "initrd-stable"), "post-remove": "rm -f /boot/initrd-stable", }, "kernel-mainline": { "post-install": _kernel_hook("7*", "initrd-mainline"), "post-remove": "rm -f /boot/initrd-mainline", }, "nvidia-kernel-612": { "post-install": 'depmod -a "$(basename /usr/lib/modules/6.12.* 2>/dev/null)"', }, "nvidia-kernel-618": { "post-install": 'depmod -a "$(basename /usr/lib/modules/6.18.* 2>/dev/null)"', }, "nvidia-kernel-66": { "post-install": 'depmod -a "$(basename /usr/lib/modules/6.6.* 2>/dev/null)"', }, "nvidia-580-kernel-618": { "post-install": 'depmod -a "$(basename /usr/lib/modules/* 2>/dev/null)"', }, "nvidia-580": { "post-install": ( 'ln -sf "libEGL.so.${PKG_VERSION}" /usr/lib/libEGL.so.1\n' "ln -sf libEGL.so.1 /usr/lib/libEGL.so" ), }, "vboxhost-kernel-612": { "post-install": 'depmod -a "$(basename /usr/lib/modules/6.12.* 2>/dev/null)"', }, "vboxhost-kernel-618": { "post-install": 'depmod -a "$(basename /usr/lib/modules/6.18.* 2>/dev/null)"', }, "vboxhost-kernel-66": { "post-install": 'depmod -a "$(basename /usr/lib/modules/6.6.* 2>/dev/null)"', }, "vboxhost-kernel": { "post-install": 'depmod -a "$(basename /usr/lib/modules/7* 2>/dev/null)"', }, "nutyx": { "post-install": ( 'if [ -f /usr/lib/os-release ]; then\n' ' sed -i "/VERSION/d" /usr/lib/os-release\n' ' sed -i "/URL/d" /usr/lib/os-release\n' ' sed -i "/LOGO/d" /usr/lib/os-release\n' ' printf \'VERSION=systemd\\nVERSION_ID=%s\\nLOGO=pagan.png\\n' 'HOME_URL="https://paganlinux.eu"\\n\' "$PKG_VERSION" >> /usr/lib/os-release\n' "fi" ), }, } # Bloki echo piszące skrypty do $PKGMK_ROOT – usuwane z faz build/package AUTOGEN_RE = re.compile( r'[ \t]*(?:echo|cat)\s+(?P["\'])(?P.*?)(?P=q)\s*>>?\s*' r'\$PKGMK_ROOT/\$name\.(?:post-install|post-remove|pre-install|pre-remove)\s*', re.S, ) NUTYX_RE = re.compile(r"NutyX|Nutyx|nutyx", re.IGNORECASE) def find_recipe(recipes_root, pkgname): for dirpath, _dirs, files in os.walk(recipes_root): if "PAGBUILD.yaml" not in files: continue fp = os.path.join(dirpath, "PAGBUILD.yaml") try: with open(fp, encoding="utf-8") as f: d = yaml.safe_load(f) or {} if d.get("pkgname") == pkgname: return fp except Exception: pass return None def dump_recipe(data): class D(yaml.Dumper): pass def str_rep(dumper, s): if "\n" in s: return dumper.represent_scalar("tag:yaml.org,2002:str", s, style="|") if re.match(r"^\d+\.\d+", s) or ":" in s or s.startswith("https://"): return dumper.represent_scalar("tag:yaml.org,2002:str", s, style="'") return dumper.represent_scalar("tag:yaml.org,2002:str", s) D.add_representer(str, str_rep) return yaml.dump(data, Dumper=D, default_flow_style=False, allow_unicode=True, sort_keys=False, width=120) def patch_recipe(fp, hooks, dry_run): with open(fp, encoding="utf-8") as f: data = yaml.safe_load(f) or {} # 1. usuń bloki autogen post-install/post-remove z faz for phase in ("build", "package"): body = data.get(phase) if isinstance(body, str) and "$PKGMK_ROOT" in body: data[phase] = AUTOGEN_RE.sub("", body).strip("\n") # 2. branding nutyx -> pagan (pakiet bazowy) if data.get("pkgname") == "nutyx": for phase in ("build", "package"): body = data.get(phase) if isinstance(body, str): data[phase] = body.replace("logo_nutyx_120.png", "logo_nutyx_120.png") data[phase] = data[phase].replace("nutyx.png", "pagan.png") # 3. hooki data["hooks"] = hooks if dry_run: print(f"[dry-run] {fp}: hooks={list(hooks.keys())}") return with open(fp, "w", encoding="utf-8") as f: f.write(dump_recipe(data)) print(f"✅ {os.path.relpath(fp)}: hooks={list(hooks.keys())}") def main(): ap = argparse.ArgumentParser() ap.add_argument("--recipes", default="recipes") ap.add_argument("--dry-run", action="store_true") args = ap.parse_args() for pkgname, hooks in HOOKS.items(): fp = find_recipe(args.recipes, pkgname) if not fp: print(f"⚠ {pkgname}: nie znaleziono PAGBUILD.yaml (pomiń)") continue patch_recipe(fp, hooks, args.dry_run) if __name__ == "__main__": main()