tawasuyu

sergio/takana

> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…

TOML 1569Shell 203Rust 109Markdown 993728 Definiciones

⎇ 52d3bbfeb35aca2e7a87728c2506fc5a87d9997d scripts/atuq-nested.sh Shell · 130 líneas · 7.9 KB Historia crudo

atuq-nested: `CAPTURA=<png>` — la prueba en el píxel, en una variable 52d3bbfeb · Sergio ·

1#!/bin/sh
2# atuq-nested.sh — hidrata `atuq` con su cierre de runtime y lo abre como VENTANA ANIDADA en el
3# compositor que ya tenés delante (waypipe, mirada, sway…). No toca la GPU como master: atuq es un
4# cliente wayland más.
5#
6#   scripts/atuq-nested.sh            # abre atuq y lo deja corriendo SECS segundos
7#   SECS=60 scripts/atuq-nested.sh
8#
9# ── POR QUÉ HIDRATAR Y NO EJECUTAR DESDE EL STORE ──────────────────────────────────────────────
10# El binario es musl y el host es glibc: no hay `/lib/ld-musl-x86_64.so.1` afuera. Un artefacto que
11# no arranca desde la ruta del store NO está roto — corre HIDRATADO, y ésa es la prueba que vale.
12#
13# ── LAS FUENTES NO SON OPCIONALES ──────────────────────────────────────────────────────────────
14# Un navegador sin una sola fuente arranca, pinta y muestra cuadraditos. Es el modo de fallo que ya
15# nos costó una tarde en GNOME: la clausura decía 100% y la pantalla decía otra cosa. `dejavu-fonts`
16# va en la lista de raíces por eso, no por completismo.
17set -eu
18ROOT="$(cd "$(dirname "$0")/.." && pwd)"; cd "$ROOT"
19H="$ROOT/target/release/hammer"
20
21# ── EL ROOTFS VA EN EL MISMO MOUNT QUE EL STORE, Y NO ES UN CAPRICHO ───────────────────────────
22# `hydrate` proyecta con HARDLINKS, y `linkat()` rechaza cruzar un punto de montaje aunque los dos
23# lados sean el mismo filesystem. Acá el store es `/dev/sdb` bind-monteado en `hammer/store`
24# mientras `work/` vive en `/dev/sdc`: hidratar a `work/…` muere con
25# «Invalid cross-device link (os error 18)» — medido, no supuesto.
26# El volumen entero está montado en /mnt/cosecha, así que store y destino comparten mount ahí.
27# Se comprueba con `findmnt -T`, NUNCA con `stat -c %d` (que dice el device, no el mount).
28STORE="${STORE:-/mnt/cosecha/store}"
29RFS="${RFS:-/mnt/cosecha/escritorios/atuq-rootfs}"
30ALPINE="$ROOT/.dev-fs/alpine"
31SECS="${SECS:-25}"
32URL="${URL:-about:support}"
33
34# Las raíces: atuq y lo que su motor abre en RUNTIME. `wayland-protocols` y compañía no están —son
35# de build— y meterlas engordaría el rootfs sin cambiar nada de lo que se ve.
36# Las variantes `-shared` y NO las estáticas: en runtime una `.a` no sirve para nada, y son las que
37# gtk3 declara NEEDED desde que se arregló el cuadro de las dos Pango.
38RAICES="atuq gtk3 atk gdk-pixbuf-shared pango-shared cairo-shared libepoxy glib-shared pcre2-shared
39        libffi-shared zlib-shared harfbuzz-shared fribidi freetype-shared fontconfig-shared pixman libpng-shared
40        libjpeg-turbo-shared wayland libxkbcommon mesa libdrm dbus pipewire pulseaudio alsa-lib
41        libxml2-shared dejavu-fonts
42        xkeyboard-config grim"
43# `grim` no lo necesita atuq: lo necesita QUIEN LO MIRA. Con `CAPTURA=<fichero.png>` este script
44# saca la pantalla real desde dentro de la jaula, contra el mismo socket wayland. Es lo que separó
45# «el proceso vive» de «la ventana muestra lo que queremos»: durante horas dije «mirá tu pantalla»
46# y la ventana estaba ahí desde el principio — lo que fallaba era el branding DENTRO del zip.
47# La receta vive en `recipes/incoming-wlr/grim.toml`; se resuelve más abajo con el resto.
48# `xkeyboard-config` son DATOS, no una librería, y sin ellos el navegador ni pinta:
49#     xkbcommon: ERROR: failed to add default include path /usr/share/X11/xkb
50# Es el mismo modo de fallo silencioso que las fuentes: nada en la clausura lo ve venir.
51
52# ── EL ROOTFS SE REVALIDA POR HASH, NO POR EXISTENCIA ──────────────────────────────────────────
53# «Ya está hidratado» es la pregunta equivocada: tras cualquier re-hash del corpus el directorio
54# sigue ahí, con los artefactos VIEJOS, y el script abriría contento la versión que acabás de
55# arreglar. Es la misma forma del cache-hit que congela regresiones. La pregunta correcta es si los
56# hashes VIGENTES son los que están puestos, así que se anota la lista resuelta y se compara.
57VIGENTES=""
58faltan=""
59for r in $RAICES; do
60  # La receta puede vivir en el corpus o en una cola: `grim` es de `incoming-wlr`. Se prueban las
61  # dos, en ese orden, que es el mismo que usa la resolución de deps (sibling-first, luego padre).
62  rec="recipes/$r.toml"; [ -f "$rec" ] || rec="recipes/incoming-wlr/$r.toml"
63  h="$("$H" --store "$STORE" hash "$rec" 2>/dev/null | tail -1 | sed 's/^b3://')"
64  if [ -z "$h" ] || [ ! -d "$STORE/$h-$r" ]; then faltan="$faltan $r"; continue; fi
65  VIGENTES="$VIGENTES$h-$r
66"
67done
68# Un nodo sin artefacto NO se saltea en silencio: un rootfs al que le faltan tres paquetes arranca
69# y falla tres capas más abajo.
70[ -z "$faltan" ] || { echo "✗ faltan artefactos:$faltan" >&2; exit 1; }
71
72SELLO="$RFS/.raices"
73if [ "${REHIDRATAR:-0}" = "1" ] || [ ! -f "$SELLO" ] || [ "$VIGENTES" != "$(cat "$SELLO")" ]; then
74  # Nada de `diff <(...)`: esto es /bin/sh, y la sustitución de proceso es de bash. Un script que
75  # sólo funciona cuando /bin/sh resulta ser bash es una trampa que salta en otra máquina.
76  [ -f "$SELLO" ] && echo "==> el rootfs está DESACTUALIZADO — se rehidrata"
77  echo "==> hidratando $(echo $RAICES | wc -w) raíces en $RFS"
78  rm -rf "$RFS"; mkdir -p "$RFS"
79  echo "$VIGENTES" | while read -r d; do
80    [ -n "$d" ] && "$H" --store "$STORE" hydrate "${d%%-*}" --into "$RFS" >/dev/null
81  done
82  printf '%s' "$VIGENTES" > "$SELLO"
83  echo "==> hidratado: $(find "$RFS" -type f | wc -l) ficheros"
84else
85  echo "==> rootfs al día ($(echo "$VIGENTES" | grep -c . ) raíces, hashes vigentes)"
86fi
87
88[ -n "${WAYLAND_DISPLAY:-}" ] || { echo "✗ no hay WAYLAND_DISPLAY — esto necesita una pantalla" >&2; exit 1; }
89SOCK="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/$WAYLAND_DISPLAY"
90[ -S "$SOCK" ] || { echo "✗ no existe el socket $SOCK" >&2; exit 1; }
91echo "==> pantalla: $SOCK"
92
93exec bwrap --overlay-src "$ALPINE" --overlay-src "$RFS" --tmp-overlay / \
94  --dev-bind /dev /dev --proc /proc --bind /sys /sys --tmpfs /tmp --tmpfs /run \
95  --ro-bind "$SOCK" /run/wayland-0 \
96  --bind "$(dirname "${CAPTURA:-/tmp/atuq-captura.png}")" /salida \
97  --uid 0 --gid 0 \
98  --setenv PATH /usr/bin:/bin:/usr/sbin:/sbin \
99  --setenv LD_LIBRARY_PATH /usr/lib:/lib \
100  --setenv XDG_RUNTIME_DIR /run --setenv WAYLAND_DISPLAY wayland-0 \
101  --setenv XDG_DATA_DIRS /usr/share --setenv HOME /root \
102  --setenv GDK_BACKEND wayland --setenv MOZ_ENABLE_WAYLAND 1 \
103  --setenv LIBGL_DRIVERS_PATH /usr/lib/dri \
104  --setenv MOZ_LOG_FILE /tmp/atuq-moz.log \
105  sh -c '
106    mkdir -p /root /run/cache /etc
107    [ -s /etc/machine-id ] || tr -d - < /proc/sys/kernel/random/uuid > /etc/machine-id
108    echo "=== identidad del artefacto ==="
109    sed -n "/^\[App\]/,/^$/p" /usr/lib/atuq/application.ini
110    echo "=== fuentes visibles ==="
111    fc-list 2>/dev/null | wc -l
112    timeout '"$SECS"' /usr/bin/atuq --no-remote '"$URL"' >/tmp/atuq.log 2>&1 &
113    PID=$!
114    sleep 8
115    echo "=== ¿vive? ==="
116    if kill -0 $PID 2>/dev/null; then echo "  ✓ atuq corriendo (PID $PID) — mirá tu pantalla"; else echo "  ✗ murió"; fi
117    echo "=== log ==="; tail -25 /tmp/atuq.log
118    # ── LA PRUEBA EN EL PÍXEL ─────────────────────────────────────────────────────────────────
119    # Un proceso vivo NO es una ventana correcta. Se captura la pantalla entera (grim sin `-o`
120    # compone todas las salidas) y se deja el PNG afuera, para mirarlo de verdad.
121    if [ -n "'"${CAPTURA:-}"'" ]; then
122      sleep 4
123      if grim "/salida/'"$(basename "${CAPTURA:-captura.png}")"'" 2>/dev/null; then
124        echo "captura: $(wc -c < "/salida/'"$(basename "${CAPTURA:-captura.png}")"'") bytes → '"${CAPTURA:-}"'"
125      else
126        echo "captura: grim NO pudo — ¿el compositor expone wlr-screencopy?"
127      fi
128    fi
129    wait $PID 2>/dev/null || true
130  '