raíz sucia: 945 ficheros de perl en /, con guardián que nombra al culpable y el número que difiere el arreglo

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) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CGyBj4QyeCZiwLMkP9GJp6
This commit is contained in:
Sergio
2026-09-04 02:27:22 +00:00
co-authored by Claude Opus 5
parent 937169da28
commit 02e93a02ea
2 changed files with 44 additions and 0 deletions
+19
View File
@@ -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"]
+25
View File
@@ -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()