Files
takana/scripts/farm/farm-sync.sh
T
sergioandClaude Opus 5 129aff9dd9 granja: el worker rehacía 87 de 90 recetas por ciclo — el sync del store es de UN SOLO SENTIDO
Fui a mirar los fallos de la granja y el número no cuadraba: de 20 «fallos» de incoming-gnome,
17 correspondían a recetas CON ARTEFACTO VIGENTE en el hub. Primero pensé que eran marcadores
`.fail` rancios, y me equivoqué: `build-farm.sh` hace `rm -rf "$FARM"` al empezar cada ciclo,
así que el status es siempre del ciclo actual. Después pensé que el worker corría recetas
viejas, y también me equivoqué: comparados los md5, hub y worker tienen copias IDÉNTICAS.

LA CAUSA REAL está en `farm-sync.sh`: sube el código EXCLUYENDO /store y baja el store del
worker. El store viaja worker→hub y nunca hub→worker. O sea que **el worker no se entera de
nada de lo que se sella en el hub**, y cada ciclo reintenta —y vuelve a fallar— trabajo ya
hecho. Medido: de las 90 recetas de incoming-gnome, **87 ya están selladas en el hub**. El
worker estaba quemando el 97% de esa cola en repetir lo hecho.

EL ARREGLO no es mandarle los artefactos (gigabytes por un enlace de 8 Mbps) sino la LISTA:
`work/farm-sellados.txt` son los nombres `<hash>-<paquete>` del store del hub, unos kilobytes.
`farm-sync.sh` la regenera en cada sync (para que no envejezca sola) y `build-farm.sh` salta
la receta cuyo hash vigente ya esté ahí. Ojo al detalle de rsync: la subida excluye /work, así
que el manifiesto necesita un `--include` ANTES del `--exclude` — rsync aplica la primera regla
que casa, y sin ese orden el fichero no viajaba y el arreglo no habría hecho nada en silencio.

Y LAS TRES DEUDAS REALES DE LA GRANJA SON UNA. gnome-session, gnome-settings-daemon y gdm
mueren todas en GTK3, que el frente GNOME aparcó a propósito. Verificado contra el meson.build
de cada tag: gnome-session 48.0 lo pide incondicional; gnome-settings-daemon 48.1 pide gtk+-3.0
Y gtk+-x11-3.0 (dos de las tres deudas aparcadas, no una); gdm 48.0 lo tiene condicionado a
`if have_xdmcp` pero da igual, porque muere construyendo gnome-session.

De paso, gnome-session pasaba `-Dsystemd=false -Dsystemd_journal=false`, dos opciones que NO
EXISTEN en 48.0 (sus opciones reales son seis: deprecation_flags, session_selector,
systemduserunitdir, docbook, man, x11). Meson aborta en la primera opción desconocida, así que
la receta ni llegaba a configurar y el fallo real quedaba tapado. Corregido, más -Dx11=false.
El diagnóstico de fondo YA ESTABA en la receta desde el 2026-07-27 y era más completo que el
mío (dice GTK3 **y** libsystemd); quité la nota duplicada que había añadido.

Y en el respaldo: la comprobación inicial de SSH era de un solo intento y tiró la corrida al
relanzarlo llegando a casa, con el wifi aún sin levantar. Es el peor momento para rendirse —
quien relanza un respaldo interrumpido acaba de cambiar de red. Ahora reintenta.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-07 18:27:29 -04:00

74 lines
3.8 KiB
Bash
Executable File

#!/usr/bin/env bash
# farm-sync.sh <user@host> — orquesta el worker VPS desde el HUB (laptop). Una sola pasada:
# 1. sube código + recipes/incoming/ al worker (rsync, el worker no toca gitea)
# 2. baja el store/ sellado que el worker construyó
# 3. promueve+firma lo cosechado (cache-hit instantáneo en los artefactos bajados) + commit/push
#
# El acceso es UNO solo: laptop->VPS por SSH (la clave de firma y gitea nunca salen del laptop).
# Idempotente y seguro de re-correr. Pensado para correr cada vez que el laptop está online.
#
# Uso: scripts/farm/farm-sync.sh root@1.2.3.4
# scripts/farm/farm-sync.sh root@1.2.3.4 --no-promote # sólo sincroniza, no firma
# Env: SSH_KEY (def ~/.ssh/github5), REMOTE (def /opt/hammer)
set -euo pipefail
VPS="${1:?uso: farm-sync.sh user@host [--no-promote]}"
SSH_KEY="${SSH_KEY:-$HOME/.ssh/github5}"
REMOTE="${REMOTE:-/opt/hammer}"
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
cd "$ROOT"
SSH="ssh -i $SSH_KEY -o StrictHostKeyChecking=accept-new"
# El MANIFIESTO de lo ya sellado en el hub, para que el worker no rehaga trabajo hecho. Es la otra
# mitad del arreglo del sync unidireccional (ver la nota en `scripts/build-farm.sh`): el store no
# viaja hub→worker porque son gigabytes, pero la lista de nombres son kilobytes y evita que el worker
# reintente y falle cada ciclo lo que el hub ya tiene. Se regenera acá, en cada sync, para que no se
# quede vieja sola.
mkdir -p work
ls ./store 2>/dev/null | grep -E '^[0-9a-f]{64}-' > work/farm-sellados.txt || true
echo "==> 0. manifiesto de sellados del hub: $(wc -l < work/farm-sellados.txt) artefactos"
echo "==> 1. subiendo código + cola al worker ($VPS:$REMOTE)"
rsync -az --delete -e "$SSH" \
--include '/work/' --include '/work/farm-sellados.txt' \
--exclude /work --exclude /store --exclude '/store-*' --exclude /target \
--exclude /dist --exclude /.dev-fs --exclude /.git --exclude /.scratch \
--exclude '*.png' --exclude '/content*' \
./ "$VPS:$REMOTE/"
echo "==> 2. bajando artefactos sellados del worker"
# ── ⚠ NO BAJAR LO QUE YA PODAMOS: era un BUCLE DE CHURN DE 24 G ────────────────────────────────
# Medido el 2026-08-07: `store-gc.sh` borró 362 artefactos superados (24 G) por la mañana y por la
# tarde volvió a borrar **los mismos 362** — solapamiento del 100%. No era casualidad: este rsync
# bajaba el store del worker ENTERO, y el worker nunca se poda, así que conservaba los superados y
# nos los devolvía en cada cosecha. Podar → cosechar → vuelven → podar. El disco pagaba 24 G por
# vuelta y la métrica de espacio libre mentía sobre el progreso.
#
# El registro `work/store-gc-superados.txt` es la unión de todos los manifiestos de poda: nombres
# `<hash>-<paquete>` de artefactos que YA decidimos que sobran. Como el store es CAS —los nombres
# son inmutables y un artefacto superado no vuelve a ser vigente salvo que una receta retroceda—
# excluirlos es seguro; y si una receta retrocediera, su artefacto se reconstruye, que es barato.
LEDGER="work/store-gc-superados.txt"
if [ -s "$LEDGER" ]; then
echo " (excluyendo $(wc -l < "$LEDGER") artefactos ya podados — ver la nota del bucle de churn)"
rsync -az -e "$SSH" --exclude-from="$LEDGER" "$VPS:$REMOTE/store/" ./store/
else
rsync -az -e "$SSH" "$VPS:$REMOTE/store/" ./store/
fi
if [ "${2:-}" = "--no-promote" ]; then
echo "✓ store sincronizado (sin promote)"; exit 0
fi
echo "==> 3. promote + firma (cache-hit en lo que el worker selló)"
scripts/build-farm.sh
echo "==> 4. commit + push de la cosecha"
git add recipes/ tandas/ 2>/dev/null || true
if ! git diff --cached --quiet; then
git commit -q -m "Etapa G: cosecha del worker VPS — promote + firma"
git push -q origin main && echo "✓ pusheado"
else
echo "(nada nuevo que commitear)"
fi