250 líneas de comentario en 151 scripts. Control verificado: el diff no toca NI UNA línea que no empiece por #, y la sintaxis de los 151 pasa. El barrido saltea heredocs y cadenas triples, y el guardián DISPARÓ 3 veces: las tres eran el MOTD que el script escribe DENTRO de la imagen construida — texto del producto, no comentario del script. Se cambiaron aparte y a propósito, que es rebranding, no limpieza. Y el hallazgo caro: casaba contra , que es el TARGET de tracing — o sea el module_path!, o sea el nombre del crate. La etapa 4 lo movió a y el script quedó casando NADA. No fallaba: imprimía cero atribuciones, indistinguible de un log sin problemas. Comprobado con el binario (RUST_LOG=info sobre zlib), no deducido. Ahora acepta las dos, y tiene que seguir aceptándolas porque los logs viejos en disco dicen la vieja. Además 14 rutas de módulo en docs, que el barrido anterior no tocó porque no es frontera de palabra.
181 lines
12 KiB
Bash
Executable File
181 lines
12 KiB
Bash
Executable File
#!/bin/sh
|
|
# atuq-nested.sh — hidrata `atuq` con su cierre de runtime y lo abre como VENTANA ANIDADA en el
|
|
# compositor que ya tenés delante (waypipe, mirada, sway…). No toca la GPU como master: atuq es un
|
|
# cliente wayland más.
|
|
#
|
|
# scripts/atuq-nested.sh # abre atuq y lo deja corriendo SECS segundos
|
|
# SECS=60 scripts/atuq-nested.sh
|
|
#
|
|
# ── POR QUÉ HIDRATAR Y NO EJECUTAR DESDE EL STORE ──────────────────────────────────────────────
|
|
# El binario es musl y el host es glibc: no hay `/lib/ld-musl-x86_64.so.1` afuera. Un artefacto que
|
|
# no arranca desde la ruta del store NO está roto — corre HIDRATADO, y ésa es la prueba que vale.
|
|
#
|
|
# ── LAS FUENTES NO SON OPCIONALES ──────────────────────────────────────────────────────────────
|
|
# Un navegador sin una sola fuente arranca, pinta y muestra cuadraditos. Es el modo de fallo que ya
|
|
# nos costó una tarde en GNOME: la clausura decía 100% y la pantalla decía otra cosa. `dejavu-fonts`
|
|
# va en la lista de raíces por eso, no por completismo.
|
|
set -eu
|
|
ROOT="$(cd "$(dirname "$0")/.." && pwd)"; cd "$ROOT"
|
|
H="$ROOT/target/release/takana"
|
|
|
|
# ── EL ROOTFS VA EN EL MISMO MOUNT QUE EL STORE, Y NO ES UN CAPRICHO ───────────────────────────
|
|
# `hydrate` proyecta con HARDLINKS, y `linkat()` rechaza cruzar un punto de montaje aunque los dos
|
|
# lados sean el mismo filesystem. Acá el store es `/dev/sdb` bind-monteado en `takana/store`
|
|
# mientras `work/` vive en `/dev/sdc`: hidratar a `work/…` muere con
|
|
# «Invalid cross-device link (os error 18)» — medido, no supuesto.
|
|
# El volumen entero está montado en /mnt/cosecha, así que store y destino comparten mount ahí.
|
|
# Se comprueba con `findmnt -T`, NUNCA con `stat -c %d` (que dice el device, no el mount).
|
|
STORE="${STORE:-/mnt/cosecha/store}"
|
|
RFS="${RFS:-/mnt/cosecha/escritorios/atuq-rootfs}"
|
|
ALPINE="$ROOT/.dev-fs/alpine"
|
|
SECS="${SECS:-25}"
|
|
URL="${URL:-about:support}"
|
|
|
|
# Las raíces: atuq y lo que su motor abre en RUNTIME. `wayland-protocols` y compañía no están —son
|
|
# de build— y meterlas engordaría el rootfs sin cambiar nada de lo que se ve.
|
|
# Las variantes `-shared` y NO las estáticas: en runtime una `.a` no sirve para nada, y son las que
|
|
# gtk3 declara NEEDED desde que se arregló el cuadro de las dos Pango.
|
|
RAICES="atuq gcc-libs gtk3 atk gdk-pixbuf-shared pango-shared cairo-shared libepoxy glib-shared pcre2-shared
|
|
libffi-shared zlib-shared harfbuzz-shared fribidi freetype-shared fontconfig-shared pixman libpng-shared
|
|
libjpeg-turbo-shared wayland libxkbcommon mesa libdrm dbus pipewire pulseaudio alsa-lib
|
|
libxml2-shared dejavu-fonts
|
|
bzip2-shared expat-shared ncurses-shared libsndfile libudev-zero zstd dbus-shared
|
|
libnotify
|
|
ffmpeg libva
|
|
xkeyboard-config grim"
|
|
# ⚠ `gcc-libs` FALTABA, y su ausencia hacía MENTIR a esta prueba entera (2026-09-07). `atuq` declara
|
|
# `NEEDED libstdc++.so.6` y `libgcc_s.so.1` —lo hereda de firefox, que va con clang++ y la libstdc++
|
|
# COMPARTIDA de Alpine—, y esas dos sólo existían en el lab. Como la jaula monta `.dev-fs/alpine`
|
|
# como capa de abajo, el navegador arrancaba resolviéndolas contra el LAB y todos decíamos «atuq
|
|
# corre». Corría, sí: en una máquina con Alpine debajo, que es exactamente lo que la distro no es.
|
|
# La clausura ya estaba bien —`recipes/atuq.toml` declara `runtime = ["gcc-libs"]` y el vigía de
|
|
# sonames da CERO—, lo que estaba mal era ESTA lista, o sea el instrumento y no el artefacto.
|
|
# Se nota a simple vista porque los dos ficheros no se parecen: el del corpus pesa 20.850.968 bytes
|
|
# y el del lab 2.804.104. Si algún día hay duda de cuál se cargó, se mira el tamaño.
|
|
#
|
|
# La segunda tanda salió del guardián que nació de ese fallo, `scripts/test-atuq-rootfs.py`, que
|
|
# mira el rootfs HIDRATADO en vez del grafo: `bzip2-shared expat-shared ncurses-shared libsndfile
|
|
# libudev-zero zstd dbus-shared` — siete sonames más que también se resolvían contra el lab.
|
|
# `libexpat` y `libzstd` los pide mesa (iris_dri, libEGL, libgbm), o sea el camino de pintado;
|
|
# `libdbus-1.so.3` lo pide pipewire y NO lo trae `dbus` sino `dbus-shared`, el mismo cuadro de las
|
|
# variantes `-shared` de gtk3. Mantener esta lista a mano es lo que falla: por eso ahora hay un
|
|
# guardián que la audita en vez de confiar en que alguien se acuerde.
|
|
# `ffmpeg` y `libva` entran el 2026-09-07 y NO son recetas nuevas: las dos estaban selladas en el
|
|
# corpus y ya viven en la CLAUSURA de los cuatro escritorios, arrastradas por `mpv`. Lo que faltaba
|
|
# era esta lista, o sea otra vez el instrumento. Firefox abre `libavcodec.so.<53..63>` por `dlopen`
|
|
# literal —está en las cadenas de `libxul.so`— y nuestro ffmpeg 7.1 publica `libavcodec.so.61`, o
|
|
# sea H.264/AAC por software. Sin estas dos raíces el runner mide un rootfs MÁS POBRE que la imagen
|
|
# y reporta un hueco que la imagen no tiene. Ver SDD 26 §6.10.
|
|
# ⚠ `libva` va por simetría con la imagen, no porque encienda VA-API: las tres mesa construyen con
|
|
# `-Dgallium-va=disabled -Dvideo-codecs=`, así que el cargador está y no hay UN SOLO driver
|
|
# (`*_drv_video.so`) que cargar. Es media función, como libnotify sin daemon.
|
|
# `grim` no lo necesita atuq: lo necesita QUIEN LO MIRA. Con `CAPTURA=<fichero.png>` este script
|
|
# saca la pantalla real desde dentro de la jaula, contra el mismo socket wayland. Es lo que separó
|
|
# «el proceso vive» de «la ventana muestra lo que queremos»: durante horas dije «mirá tu pantalla»
|
|
# y la ventana estaba ahí desde el principio — lo que fallaba era el branding DENTRO del zip.
|
|
# La receta vive en `recipes/incoming-wlr/grim.toml`; se resuelve más abajo con el resto.
|
|
# `xkeyboard-config` son DATOS, no una librería, y sin ellos el navegador ni pinta:
|
|
# xkbcommon: ERROR: failed to add default include path /usr/share/X11/xkb
|
|
# Es el mismo modo de fallo silencioso que las fuentes: nada en la clausura lo ve venir.
|
|
|
|
# ── EL ROOTFS SE REVALIDA POR HASH, NO POR EXISTENCIA ──────────────────────────────────────────
|
|
# «Ya está hidratado» es la pregunta equivocada: tras cualquier re-hash del corpus el directorio
|
|
# sigue ahí, con los artefactos VIEJOS, y el script abriría contento la versión que acabás de
|
|
# arreglar. Es la misma forma del cache-hit que congela regresiones. La pregunta correcta es si los
|
|
# hashes VIGENTES son los que están puestos, así que se anota la lista resuelta y se compara.
|
|
VIGENTES=""
|
|
faltan=""
|
|
for r in $RAICES; do
|
|
# La receta puede vivir en el corpus o en una cola: `grim` es de `incoming-wlr`. Se prueban las
|
|
# dos, en ese orden, que es el mismo que usa la resolución de deps (sibling-first, luego padre).
|
|
rec="recipes/$r.toml"; [ -f "$rec" ] || rec="recipes/incoming-wlr/$r.toml"
|
|
h="$("$H" --store "$STORE" hash "$rec" 2>/dev/null | tail -1 | sed 's/^b3://')"
|
|
if [ -z "$h" ] || [ ! -d "$STORE/$h-$r" ]; then faltan="$faltan $r"; continue; fi
|
|
VIGENTES="$VIGENTES$h-$r
|
|
"
|
|
done
|
|
# Un nodo sin artefacto NO se saltea en silencio: un rootfs al que le faltan tres paquetes arranca
|
|
# y falla tres capas más abajo.
|
|
[ -z "$faltan" ] || { echo "✗ faltan artefactos:$faltan" >&2; exit 1; }
|
|
|
|
SELLO="$RFS/.raices"
|
|
# ⚠ LOS DOS LADOS DE LA COMPARACIÓN TIENEN QUE PASAR POR LA MISMA TRITURADORA. `$VIGENTES` termina
|
|
# en `\n` y `$(cat …)` RECORTA los `\n` finales ⇒ comparados en crudo NUNCA son iguales y el rootfs
|
|
# se rehidrataba entero en CADA corrida (3013 ficheros), diciendo «DESACTUALIZADO» aunque estuviera
|
|
# perfecto. La caché existía y no cacheaba nada; medido 2026-09-07. Metiendo el de la izquierda en
|
|
# otra sustitución de comandos, los dos pierden el mismo `\n` y la comparación vuelve a significar
|
|
# lo que dice.
|
|
if [ "${REHIDRATAR:-0}" = "1" ] || [ ! -f "$SELLO" ] || [ "$(printf '%s' "$VIGENTES")" != "$(cat "$SELLO")" ]; then
|
|
# Nada de `diff <(...)`: esto es /bin/sh, y la sustitución de proceso es de bash. Un script que
|
|
# sólo funciona cuando /bin/sh resulta ser bash es una trampa que salta en otra máquina.
|
|
[ -f "$SELLO" ] && echo "==> el rootfs está DESACTUALIZADO — se rehidrata"
|
|
echo "==> hidratando $(echo $RAICES | wc -w) raíces en $RFS"
|
|
rm -rf "$RFS"; mkdir -p "$RFS"
|
|
# ⚠ ESTE BUCLE SALÍA 1 SIEMPRE, Y CON `set -e` MATABA EL SCRIPT SIN IMPRIMIR NADA (2026-09-07).
|
|
# `$VIGENTES` termina en `\n` y `echo` agrega otro ⇒ la última vuelta lee la línea VACÍA, el
|
|
# `[ -n "$d" ]` da 1, ése es el estado de la última vuelta, ése es el estado del `while`, ése es
|
|
# el del pipeline, y `set -e` sale. Justo DESPUÉS de hidratar bien las 41 raíces: la única señal
|
|
# era `exit=1` tras «==> hidratando». No se veía porque esta rama sólo corre cuando cambian los
|
|
# hashes —con el sello al día el script ni entra—, así que el bug vivía escondido detrás de la
|
|
# caché. Lo destapó agregar `ffmpeg` a las raíces. Ahora: `continue` en la vacía, y un `hydrate`
|
|
# que falla GRITA en vez de dejar un rootfs a medias con el sello sin escribir.
|
|
echo "$VIGENTES" | while read -r d; do
|
|
[ -n "$d" ] || continue
|
|
"$H" --store "$STORE" hydrate "${d%%-*}" --into "$RFS" >/dev/null || {
|
|
echo "✗ hydrate falló en $d" >&2; exit 1; }
|
|
done || exit 1
|
|
printf '%s' "$VIGENTES" > "$SELLO"
|
|
echo "==> hidratado: $(find "$RFS" -type f | wc -l) ficheros"
|
|
else
|
|
echo "==> rootfs al día ($(echo "$VIGENTES" | grep -c . ) raíces, hashes vigentes)"
|
|
fi
|
|
|
|
# `ROOTFS_ONLY=1` corta acá: deja el rootfs hidratado y al día, y no abre ventana ni pide pantalla.
|
|
# Lo usa `scripts/test-atuq-codecs.sh`, que corre headless y necesita el MISMO árbol que esta
|
|
# ventana — duplicar la lista de raíces en dos scripts es exactamente cómo se desincronizan.
|
|
[ "${ROOTFS_ONLY:-0}" = "1" ] && exit 0
|
|
|
|
[ -n "${WAYLAND_DISPLAY:-}" ] || { echo "✗ no hay WAYLAND_DISPLAY — esto necesita una pantalla" >&2; exit 1; }
|
|
SOCK="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/$WAYLAND_DISPLAY"
|
|
[ -S "$SOCK" ] || { echo "✗ no existe el socket $SOCK" >&2; exit 1; }
|
|
echo "==> pantalla: $SOCK"
|
|
|
|
exec bwrap --overlay-src "$ALPINE" --overlay-src "$RFS" --tmp-overlay / \
|
|
--dev-bind /dev /dev --proc /proc --bind /sys /sys --tmpfs /tmp --tmpfs /run \
|
|
--ro-bind "$SOCK" /run/wayland-0 \
|
|
--bind "$(dirname "${CAPTURA:-/tmp/atuq-captura.png}")" /salida \
|
|
--uid 0 --gid 0 \
|
|
--setenv PATH /usr/bin:/bin:/usr/sbin:/sbin \
|
|
--setenv LD_LIBRARY_PATH /usr/lib:/lib \
|
|
--setenv XDG_RUNTIME_DIR /run --setenv WAYLAND_DISPLAY wayland-0 \
|
|
--setenv XDG_DATA_DIRS /usr/share --setenv HOME /root \
|
|
--setenv GDK_BACKEND wayland --setenv MOZ_ENABLE_WAYLAND 1 \
|
|
--setenv LIBGL_DRIVERS_PATH /usr/lib/dri \
|
|
--setenv MOZ_LOG_FILE /tmp/atuq-moz.log \
|
|
sh -c '
|
|
mkdir -p /root /run/cache /etc
|
|
[ -s /etc/machine-id ] || tr -d - < /proc/sys/kernel/random/uuid > /etc/machine-id
|
|
echo "=== identidad del artefacto ==="
|
|
sed -n "/^\[App\]/,/^$/p" /usr/lib/atuq/application.ini
|
|
echo "=== fuentes visibles ==="
|
|
fc-list 2>/dev/null | wc -l
|
|
timeout '"$SECS"' /usr/bin/atuq --no-remote '"$URL"' >/tmp/atuq.log 2>&1 &
|
|
PID=$!
|
|
sleep 8
|
|
echo "=== ¿vive? ==="
|
|
if kill -0 $PID 2>/dev/null; then echo " ✓ atuq corriendo (PID $PID) — mirá tu pantalla"; else echo " ✗ murió"; fi
|
|
echo "=== log ==="; tail -25 /tmp/atuq.log
|
|
# ── LA PRUEBA EN EL PÍXEL ─────────────────────────────────────────────────────────────────
|
|
# Un proceso vivo NO es una ventana correcta. Se captura la pantalla entera (grim sin `-o`
|
|
# compone todas las salidas) y se deja el PNG afuera, para mirarlo de verdad.
|
|
if [ -n "'"${CAPTURA:-}"'" ]; then
|
|
sleep 4
|
|
if grim "/salida/'"$(basename "${CAPTURA:-captura.png}")"'" 2>/dev/null; then
|
|
echo "captura: $(wc -c < "/salida/'"$(basename "${CAPTURA:-captura.png}")"'") bytes → '"${CAPTURA:-}"'"
|
|
else
|
|
echo "captura: grim NO pudo — ¿el compositor expone wlr-screencopy?"
|
|
fi
|
|
fi
|
|
wait $PID 2>/dev/null || true
|
|
'
|