Files
takana/scripts/atuq-nested.sh
T
Sergio 5d06a3b83d atuq ABRE: el app_id estaba compilado en el binario, y el árbol copiado llegaba de sólo lectura
Con la cadena GTK3 arreglada, atuq arrancó de verdad: parent vivo, CERO procesos de contenido
muertos, y pintando una página local. Quedaban dos cosas.

1. SE ANUNCIABA COMO `firefox-default`. El application.ini decía RemotingName=atuq y aun así el
   proceso se presentaba así — porque esa cadena es un MOZ_APP_REMOTINGNAME horneado en el ELF, y es
   la que Gecko pasa a g_set_prgname(), o sea el **app_id de Wayland**. Consecuencia visible: el
   escritorio no casaba la ventana con nuestro .desktop (StartupWMClass=atuq) ⇒ icono genérico y sin
   agrupar.

   Se parchea EN SITIO, no recompilando: cambiarlo en recipes/firefox.toml brandearía la BASE como
   atuq, y firefox tiene que seguir siendo firefox para los otros forks. Es seguro porque es una
   cadena C terminada en NUL en el pool de .rodata —`firefox-default\0` son 16 bytes exactos y se
   escriben 16—, y se EXIGE una única ocurrencia: si upstream la duplica o la renombra, falla en vez
   de parchear el sitio equivocado. Verificado después: 0 ocurrencias de la vieja, y el log dice
   `(atuq:26423)`.

2. UN BUG QUE SÓLO SE VE FUERA DE ROOT. `cp -a` preserva los modos y los ficheros del store están
   sellados sin permiso de escritura; todo lo que viene después los modifica. En el worker pasaba
   inadvertido porque corre como root, que ignora los bits. El primer build local murió con
   `PermissionError: /out/usr/lib/atuq/application.ini`. Se arregla con `chmod -R u+w` en la receta y
   no en el entorno: un artefacto no debe depender de con qué uid lo construiste.

Y el runner pasa a hidratar las variantes `-shared`: en runtime una `.a` no sirve de nada, y son las
que gtk3 declara NEEDED desde el arreglo del cuadro de las dos Pango.

Este atuq se construyó EN LOCAL en segundos, que era la promesa entera del diseño derivado del
SDD 26: iterar el envoltorio sin volver a pagar un build de Gecko.
2026-09-05 14:31:35 +00:00

111 lines
6.4 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/hammer"
# ── 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 `hammer/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 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
xkeyboard-config"
# `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
h="$("$H" --store "$STORE" hash "recipes/$r.toml" 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"
if [ "${REHIDRATAR:-0}" = "1" ] || [ ! -f "$SELLO" ] || [ "$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"
echo "$VIGENTES" | while read -r d; do
[ -n "$d" ] && "$H" --store "$STORE" hydrate "${d%%-*}" --into "$RFS" >/dev/null
done
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
[ -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 \
--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
wait $PID 2>/dev/null || true
'