🔒 Repository is read-only – file editing is disabled.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
#!/usr/bin/env python3
"""
================================================================================
strip-recipes-footprints.py — usuwa pola `footprint*` z receptur PAGBUILD.yaml
================================================================================
PaganOS odchodzi od modelu CRUX/cards/NuTyX, w którym lista plików pakietu była
zadeklarowana w recepturze (pliki `*.footprint` -> pola `footprint`,
`footprint_devel`, `footprint_man`, `footprint_<lang>` w YAML). To był balast:
* `pagbuild` NIGDY nie czytał tych pól — pakiet powstaje z katalogu DESTDIR
(faza `package:`), a więc z faktycznie zainstalowanych plików;
* `pag` sam wylicza SHA256 każdego pliku przy instalacji i zapisuje je
w /var/lib/pag/files.db (model jak dpkg/pacman — lista plików jest
pochodną pakietu, nie deklaracją w recepturze).
Zostaje model „jak Arch/Debian”: jedna receptura -> jeden pakiet -> wszystkie
pliki, a menedżer pakietów sam śledzi ich listę i sumy.
USUNIĘCIE JEST IDEMPOTENTNE. Skrypt nie rusza żadnych innych pól ani formatowania
poza wycięciem logicznych kluczy `footprint*` (wraz z ich wieloliniowymi
wartościami). Obsługiwane typy skalarów YAML:
* blokowe (`footprint: |`, `...: >-`) — wcięte linie treści,
* w cudzysłowie (`footprint: "....\\n...."`) — z zawijaniem przez `\\` + newline,
* w apostrofach (`footprint: '...'`),
* proste jednolinijkowe.
Użycie:
python3 strip-recipes-footprints.py [--root recipes] [--apply]
Bez `--apply` działa w trybie dry-run (nic nie zapisuje).
"""
from __future__ import annotations
import argparse
import glob
import os
import re
import sys
# Klucz na początku linii (kolumna 0): dokładnie `footprint` albo
# `footprint_<subpakiet>`, np. `footprint_devel`, `footprint_xfwm4_pl`.
# Nazwa podpakietu może zawierać myślniki (`footprint_autoconf-archive_man`),
# plusy (`footprint_crypto++_devel`), kropki itd. – dopuszczamy dowolny znak
# poza dwukropkiem (separatorem) i białym znakiem.
KEY_RE = re.compile(r"^footprint(?:_[^:\s]+)?:")
def _is_indented(line: str) -> bool:
return line[:1] in (" ", "\t")
def _value_span(lines: list[str], i: int) -> int:
"""Zwraca liczbę linii (>=1) zajmowanych przez wartość klucza w linii `i`.
Linia `i` jest linią klucza `footprint*:` w kolumnie 0.
"""
after = lines[i].split(":", 1)[1]
stripped = after.lstrip()
# ── Skalar blokowy (| lub >, opcjonalnie z chomping/indent) ──
if stripped[:1] in ("|", ">"):
j = 1
while i + j < len(lines):
nxt = lines[i + j]
if nxt.strip() == "" or _is_indented(nxt):
j += 1
else:
break
return j
# ── Skalar w cudzysłowie ──
if stripped[:1] == '"':
k = after.index('"')
buf = after[k + 1:]
j = 1
pos = 0
while True:
closed = False
while pos < len(buf):
c = buf[pos]
if c == "\\":
# Znak po backslashu jest escape'owany; końcowy `\` przed
# newline to kontynuacja linii – wtedy `pos += 2` przeskakuje
# poza koniec `buf` i płynnie przechodzimy do następnej linii.
pos += 2
continue
if c == '"':
closed = True
break
pos += 1
if closed or i + j >= len(lines):
return j
buf = lines[i + j]
pos = 0
j += 1
# ── Skalar w apostrofach ('' = escaped apostrof) ──
if stripped[:1] == "'":
k = after.index("'")
buf = after[k + 1:]
j = 1
pos = 0
while True:
closed = False
while pos < len(buf):
c = buf[pos]
if c == "'":
if pos + 1 < len(buf) and buf[pos + 1] == "'":
pos += 2
continue
closed = True
break
pos += 1
if closed or i + j >= len(lines):
return j
buf = lines[i + j]
pos = 0
j += 1
# ── Skalar prosty (jedna linia) ──
return 1
def strip_text(text: str) -> tuple[str, list[str]]:
"""Usuwa logiczne klucze `footprint*`. Zwraca (nowy_tekst, [usunięte_klucze])."""
lines = text.split("\n")
out: list[str] = []
removed: list[str] = []
i = 0
n = len(lines)
while i < n:
line = lines[i]
if not _is_indented(line) and KEY_RE.match(line):
removed.append(line.split(":", 1)[0])
i += _value_span(lines, i)
continue
out.append(line)
i += 1
return "\n".join(out), removed
def main() -> int:
ap = argparse.ArgumentParser(description="Usuwa pola footprint* z PAGBUILD.yaml")
ap.add_argument("--root", default="recipes", help="katalog z recepturami (domyślnie: recipes)")
ap.add_argument("--apply", action="store_true", help="zapisz zmiany (domyślnie dry-run)")
args = ap.parse_args()
pattern = os.path.join(args.root, "**", "PAGBUILD.yaml")
files = sorted(glob.glob(pattern, recursive=True))
if not files:
sys.exit(f"❌ Nie znaleziono receptur pod: {pattern}")
changed_files = 0
removed_total = 0
for path in files:
try:
with open(path, encoding="utf-8") as f:
orig = f.read()
except OSError as e:
print(f"⚠️ Pominięto {path}: {e}")
continue
new, removed = strip_text(orig)
if not removed:
continue
changed_files += 1
removed_total += len(removed)
if args.apply:
with open(path, "w", encoding="utf-8") as f:
f.write(new)
if changed_files <= 5:
shown = ", ".join(removed[:6]) + (" ..." if len(removed) > 6 else "")
print(f" {path}: -{len(removed)} ({shown})")
verb = "Zmieniono" if args.apply else "Do zmiany"
print(f"\n{verb}: {changed_files} plików, usunięto {removed_total} pól footprint* "
f"(z {len(files)} receptur).")
if not args.apply and changed_files:
print("Dry-run — uruchom z --apply, aby zapisać.")
return 0
if __name__ == "__main__":
raise SystemExit(main())