From 02e93a02eab6da18479af3b30bcbdd0911d7c73f Mon Sep 17 00:00:00 2001 From: Sergio Date: Fri, 4 Sep 2026 02:27:22 +0000 Subject: [PATCH] =?UTF-8?q?ra=C3=ADz=20sucia:=20945=20ficheros=20de=20perl?= =?UTF-8?q?=20en=20`/`,=20con=20guardi=C3=A1n=20que=20nombra=20al=20culpab?= =?UTF-8?q?le=20y=20el=20n=C3=BAmero=20que=20difiere=20el=20arreglo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hidratando el rootfs de sway aparecieron 945 ficheros sueltos en la raíz — `AnyDBM_File.0`, `App::Cpan.0`, … Son las páginas nroff de perl: su `Configure -des` no encuentra nroff, elige `man1ext='0'` y `man1dir=' '` (la convención de perl para «no instales man»), pero `installman` las GENERA igual y con el directorio vacío `make install DESTDIR=/out` las deja en `/out/`. Ninguna métrica sobre recetas puede ver esto: hay que hidratar un rootfs de verdad y mirarlo. El guardián va en `hydrate-profile.py` y mira **por artefacto**, no sobre el árbol fundido: en el árbol fundido el nombre del culpable ya se perdió y 945 ficheros en `/` no se parecen en nada a «una fase install con el destino vacío», que es lo que son. Barrido el store entero: **perl es el único** — `.times`/`.dmerge` son internos del store y `product-rootfs`/`seed-zig` son especiales. El arreglo es UNA línea (`-Dman1dir=… -Dman3dir=… -Dman1ext=1 -Dman3ext=3`) y NO se aplica hoy: yupana radio perl → transitivos 347 · sellados que CAEN a deuda 305 · TODAS las imágenes 305 rebuilds para mover páginas de man de sitio no se paga solo. Queda escrito en la receta para ir con el próximo bump de perl, cuando el re-hash ya esté pagado. Comprobado que el comentario NO entra en `hash_inputs`: el ArtifactHash es idéntico antes y después (b3:1af26f6b). Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01CGyBj4QyeCZiwLMkP9GJp6 --- recipes/perl.toml | 19 +++++++++++++++++++ scripts/hydrate-profile.py | 25 +++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/recipes/perl.toml b/recipes/perl.toml index 8fc13a1e..903de001 100644 --- a/recipes/perl.toml +++ b/recipes/perl.toml @@ -34,5 +34,24 @@ sh Configure -des -Dprefix=/usr -Dcc="$ZW/cc" -Dld="$ZW/cc" -Duseshrplib -Dusenm compile = 'make -j"$(nproc)"' install = 'make install DESTDIR=/out' +# ⚠ DEUDA MEDIDA Y NO PAGADA (2026-09-04): esta receta instala 945 ficheros en la RAÍZ del rootfs. +# +# Su `Configure -des` no encuentra nroff, así que elige `man1ext='0'` y —lo importante— +# `man1dir=' '` / `man3dir=' '`, la convención de perl para «no instales man». Pero `installman` +# igual GENERA las páginas preformateadas y, con el directorio vacío, `make install DESTDIR=/out` +# las deja en `/out/`: `AnyDBM_File.0`, `App::Cpan.0`, … 945 ficheros sueltos en `/`. +# Se ve hidratando un rootfs de verdad; ninguna métrica sobre recetas puede verlo. El guardián que +# lo caza y NOMBRA al culpable está en `scripts/hydrate-profile.py` (§quién ensucia la raíz). +# +# EL ARREGLO ES UNA LÍNEA —`-Dman1dir=/usr/share/man/man1 -Dman3dir=/usr/share/man/man3 +# -Dman1ext=1 -Dman3ext=3`— Y NO SE APLICA HOY. `yupana radio perl` dice por qué: +# +# dependientes transitivos 347 · sellados que CAEN a deuda 305 +# imágenes afectadas: base, cli, escritorio-{kde,gnome,cosmic,sway,mirada} (TODAS) +# +# 305 rebuilds para mover unas páginas de man de sitio no se paga solo. **Va con el próximo bump de +# perl**, cuando el re-hash ya esté pagado: ahí la línea cuesta cero. Esto no es un olvido, es una +# decisión con el número delante. + [deps] build = ["pkgconf"] diff --git a/scripts/hydrate-profile.py b/scripts/hydrate-profile.py index b74514b7..c344af16 100755 --- a/scripts/hydrate-profile.py +++ b/scripts/hydrate-profile.py @@ -146,6 +146,23 @@ def main(): for nombre, msg in fallidos: print(" %-28s %s" % (nombre, msg[0] if msg else "")) + # ── quién ensucia la RAÍZ del rootfs ──────────────────────────────────────────────────────── + # Se mira por ARTEFACTO y no sobre el árbol fundido, porque en el árbol fundido el nombre del + # culpable ya se perdió: 945 ficheros sueltos en `/` no se parecen a «una fase install con el + # directorio de destino vacío», que es lo que son. Cazado el 2026-09-04 mirando un rootfs de + # verdad, no una métrica: `perl` instala sus 945 páginas nroff en `/` porque su Configure eligió + # `man1dir=' '`. Ver el comentario en `recipes/perl.toml`. + sucios = [] + for nombre, _h, d in artefactos: + extra = sorted(e for e in os.listdir(d) if e not in FHS_RAIZ) + if extra: + sucios.append((nombre, extra)) + if sucios: + print("\n ⚠ entradas FUERA del FHS en la raíz del rootfs:") + for nombre, extra in sorted(sucios, key=lambda x: -len(x[1])): + muestra = ", ".join(extra[:3]) + (", …" if len(extra) > 3 else "") + print(" %-28s %4d (%s)" % (nombre, len(extra), muestra)) + n_fich = sum(len(f) for _, _, f in os.walk(into)) du = subprocess.run(["du", "-sh", into], capture_output=True, text=True).stdout.split() print("\n== %s: %d ficheros, %s" % (into, n_fich, du[0] if du else "?")) @@ -154,5 +171,13 @@ def main(): FLAGS_CON_VALOR = {"--into", "--store", "--link"} +# Lo que un artefacto PUEDE poner en la raíz. Todo lo demás es una fase `install` que se equivocó de +# destino, y en un rootfs fundido eso se lee como si fuera parte del sistema. +FHS_RAIZ = {"bin", "sbin", "lib", "lib64", "usr", "etc", "var", "run", "opt", "srv", + "home", "root", "boot", "dev", "proc", "sys", "tmp", "mnt", "media", + "ente", # el manifiesto de atestación del producto (hammer bootstrap) + "linuxrc", # el enlace de busybox, que va en la raíz por contrato del kernel + ".hammer"} # metadatos del store: los pone hammer al sellar, no la receta + if __name__ == "__main__": main()