Files
takana/scripts/farm/farm-worker-loop.sh
T
sergio ad7ec4aea4 Etapa G: watchdog protege árboles con 'hammer build' en vuelo (fase host vendor/resolve)
go-13 (dnscontrol/lazysql, árboles de deps Go enormes) destapó otra race del watchdog: durante el
fetch/'go mod vendor'/resolve_phases (host-side, antes del bwrap) el árbol no está bind-montado ⇒ el
watchdog lo borraba a mitad del vendor → go.mod desaparecía → 'receta sin compile, heurística no
encontró build system'. Ahora además protege work/sources/<name>-* si <name> tiene un proceso
'hammer build' activo (cubre toda la vida del build, no solo la fase sandbox).
2026-06-27 01:07:09 -04:00

74 lines
4.2 KiB
Bash
Executable File

#!/usr/bin/env bash
# farm-worker-loop.sh — loop AUTÓNOMO del worker. Construye la cola recipes/incoming/ en
# bucle (PROMOTE=0: sólo sella al store local, NO promueve, NO firma, NO toca gitea), con
# watchdog de disco integrado. El hub (laptop) le mete recetas nuevas vía farm-sync.sh y le
# baja el store sellado; este loop sólo muele lo que haya, 24/7, sin esperar a nadie.
#
# Idempotente: cada ciclo re-escanea incoming/; lo ya-sellado sale por cache-hit (instantáneo),
# sólo construye lo nuevo. Cuando la cola está toda construida, duerme IDLE_SLEEP y reintenta.
#
# Lo lanza systemd (hammer-farm.service). Manual: JOBS=2 ./scripts/farm/farm-worker-loop.sh
set -uo pipefail
HAMMER_DIR="${HAMMER_DIR:-/opt/hammer}"
cd "$HAMMER_DIR"
. "$HOME/.cargo/env" 2>/dev/null || true
JOBS="${JOBS:-$(nproc)}"
IDLE_SLEEP="${IDLE_SLEEP:-300}"
# --- watchdog de disco: cada 3 min borra work/sources/* que ningún bwrap activo bind-monta
# (seguro: sellada=store CAS, en-cola=se re-extrae sola). Evita que el vendoring llene el disco.
# Además, si el disco supera DISK_HIGH%, purga los CACHES Go (GOMODCACHE ~/go/pkg/mod + gocache):
# el `go mod vendor` de las recetas Go acumula ahí decenas de GB sin tope (el vendor/ local de cada
# receta ya tiene lo que el build necesita; el modcache sólo se usa DURANTE el vendor). Si la purga
# pisa un vendor en curso, esa receta reintenta el próximo ciclo (cache-hit el resto). Sin esto, una
# tanda Go grande llena el disco de 80G y el I/O-wait dispara el load (cuello real, no CPU/RAM).
DISK_HIGH="${DISK_HIGH:-82}"
(
while :; do
sleep 180
# Protección 1: árboles que un bwrap activo bind-monta (fase compile dentro del sandbox).
active=$(pgrep -af bwrap 2>/dev/null | grep -oE 'work/sources/[^ ]+' | sort -u)
# Protección 2: árboles cuya receta tiene un `hammer build` EN VUELO. Durante el fetch/`go mod
# vendor`/resolve_phases (host-side, ANTES de que arranque el bwrap) el árbol no está bind-montado
# ⇒ la protección 1 no lo cubre. Una tanda Go con árbol de deps enorme (dnscontrol/lazysql) tarda
# minutos vendoreando; sin esto, el watchdog le borra el go.mod a mitad → "no build system".
building=$(pgrep -af 'release/hammer' 2>/dev/null | grep -oE '[^ ]+\.toml' | sed 's#.*/##;s#\.toml$##' | sort -u)
for d in work/sources/*/; do
[ -d "$d" ] || continue; dd=${d%/}
printf '%s\n' "$active" | grep -qxF "$dd" && continue
nm=$(basename "$dd"); nm=${nm%-*} # work/sources/<name>-<sha40> → <name> (el sha no trae '-')
printf '%s\n' "$building" | grep -qxF "$nm" && continue
rm -rf "$dd"
done
use=$(df --output=pcent "$HAMMER_DIR" 2>/dev/null | tr -dc '0-9')
if [ -n "$use" ] && [ "$use" -ge "$DISK_HIGH" ]; then
# OJO: `go clean -modcache` borra ~/go/pkg/mod ENTERO. Si lo corre mientras un `go mod vendor`
# del fetch está bajando módulos, le arranca el cache bajo los pies (`.partial: no such file`)
# y ESE vendor falla — y con tandas pesadas (k8s/azure SDK, varios GB en paralelo) son MUCHOS a
# la vez ⇒ el lote no converge. La purga de work/sources de arriba ya liberó lo grueso (segura:
# saltea bind-montadas). Sólo purgo el modcache si NO hay vendor de host activo; si lo hay,
# difiero al próximo tick (el sandbox `go install` usa GOPATH=/tmp + -mod=vendor ⇒ no toca el
# modcache del host, así que purgar durante un build sandbox sigue siendo seguro).
if pgrep -f 'go mod vendor' >/dev/null 2>&1; then
echo "$(date -u +%FT%TZ) watchdog: disco ${use}% ≥ ${DISK_HIGH}% pero hay vendor activo ⇒ difiero purga modcache"
else
echo "$(date -u +%FT%TZ) watchdog: disco ${use}% ≥ ${DISK_HIGH}% ⇒ purgo caches Go"
go clean -modcache 2>/dev/null; go clean -cache 2>/dev/null
fi
fi
done
) &
echo "$(date -u +%FT%TZ) worker-loop arrancado: JOBS=$JOBS IDLE_SLEEP=$IDLE_SLEEP"
while :; do
n=$(ls recipes/incoming/*.toml 2>/dev/null | wc -l)
if [ "$n" -gt 0 ]; then
echo "$(date -u +%FT%TZ) ciclo: $n recetas en cola"
JOBS="$JOBS" PROMOTE=0 scripts/build-farm.sh 2>&1 | tail -50
echo "$(date -u +%FT%TZ) ciclo terminado; store sellado disponible para el hub"
else
echo "$(date -u +%FT%TZ) cola vacía; durmiendo ${IDLE_SLEEP}s"
fi
sleep "$IDLE_SLEEP"
done