From e09a48a69e96846b50406145316a349247faf9bb Mon Sep 17 00:00:00 2001 From: sergio Date: Tue, 21 Jul 2026 22:51:11 -0400 Subject: [PATCH] =?UTF-8?q?farm:=20tandas.sh=20=E2=80=94=20encadenar=20cam?= =?UTF-8?q?pa=C3=B1as=20en=20orden,=20sin=20solaparse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit En serie y no en paralelo: dos `hammer build` simultáneos compiten por el mismo work/sources y por el watchdog de disco del worker-loop. El paralelismo vive DENTRO de cada build (-j$(nproc)). En tandas y no en una lista plana: una tanda es una unidad topológica (la cadena GUI, el stack wayland, los kernels). Cuando una se cae por su raíz —como pango arrastró a 7 recetas— se ve de un vistazo en el resumen y se re-lanza sola; una lista de 40 nombres no dice nada al fallar. Espera a que termine la campaña en vuelo, así se encola mientras otra muele. Co-Authored-By: Claude Opus 4.8 (1M context) --- scripts/farm/tandas.sh | 53 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100755 scripts/farm/tandas.sh diff --git a/scripts/farm/tandas.sh b/scripts/farm/tandas.sh new file mode 100755 index 00000000..e0fa0c68 --- /dev/null +++ b/scripts/farm/tandas.sh @@ -0,0 +1,53 @@ +#!/bin/sh +# tandas.sh — encadena varias campañas de deuda en el worker, en ORDEN y sin solaparse. +# +# POR QUÉ EN SERIE Y NO EN PARALELO: dos `hammer build` simultáneos sobre el mismo store compiten +# por el mismo `work/sources` y por el watchdog de disco del worker-loop (que borra árboles que +# ningún bwrap tiene montado). El paralelismo del worker está DENTRO de cada build (`-j$(nproc)`), +# no entre builds. +# +# POR QUÉ TANDAS Y NO UNA LISTA LARGA: una tanda es una unidad con sentido topológico — la cadena +# GUI, el stack wayland, los kernels. Si una tanda se cae entera por su raíz (como pango arrastró a +# 7 recetas el 2026-07-21), se ve de un vistazo en el resumen y se re-lanza sola. Una lista de 40 +# nombres planos no dice nada cuando falla. +# +# Espera a que termine cualquier campaña en vuelo antes de arrancar, así se puede encolar mientras +# otra muele. +# +# Uso (en el worker): +# scripts/farm/tandas.sh "gui:pixman,cairo,pango" "kernels:linux,linux-generic" +# Desde el hub: +# ssh root@ 'systemd-run --unit=tandas --working-directory=/opt/hammer \ +# /opt/hammer/scripts/farm/tandas.sh "gui:pixman,cairo" …' +# ssh root@ 'tail -f /var/log/tandas.log' +set -u +HAMMER_DIR="${HAMMER_DIR:-/opt/hammer}" +cd "$HAMMER_DIR" +LOG="${LOG:-/var/log/tandas.log}" +ESPERA="${ESPERA:-30}" + +ts() { date -u +%FT%TZ; } +[ $# -gt 0 ] || { echo "uso: tandas.sh […]" >&2; exit 2; } + +# No solaparse con una campaña previa (ni con otra corrida de este mismo script). +while pgrep -f 'campana-deuda\.s[h]' >/dev/null 2>&1; do + echo "$(ts) esperando a que termine la campaña en vuelo…" | tee -a "$LOG" + sleep "$ESPERA" +done + +echo "$(ts) ═══ tandas arranca: $# tanda(s)" | tee -a "$LOG" +resumen="" +for spec in "$@"; do + nombre="${spec%%:*}" + lista="$(echo "${spec#*:}" | tr ',' ' ')" + n=$(echo "$lista" | wc -w) + echo "$(ts) ── TANDA '$nombre' ($n recetas)" | tee -a "$LOG" + # campana-deuda.sh ya trae el PATH del go del store, el heartbeat del dead-man y el log por receta. + LOG="/var/log/tanda-$nombre.log" scripts/farm/campana-deuda.sh $lista 2>&1 | tee -a "$LOG" + ok=$(grep -c '✓ ' "/var/log/tanda-$nombre.log" 2>/dev/null || echo 0) + ko=$(grep -c '✗ ' "/var/log/tanda-$nombre.log" 2>/dev/null || echo 0) + resumen="$resumen\n $nombre: $ok selladas, $ko fallidas (de $n)" +done + +echo "$(ts) ═══ tandas fin" | tee -a "$LOG" +printf "%b\n" "$resumen" | tee -a "$LOG"