109 lines
6.4 KiB
Bash
Executable File
109 lines
6.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# estado-granja.sh — foto RÁPIDA de la granja, on-demand (no depende del cron). Corre cuando quieras
|
|
# ver dónde va sin esperar al latido: salud del worker, qué muele, avance KDE, y si el cron corrió.
|
|
# scripts/farm/estado-granja.sh
|
|
set -uo pipefail
|
|
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"; cd "$ROOT"
|
|
SSH_KEY="${SSH_KEY:-$HOME/.ssh/github5}"
|
|
FLEET="$ROOT/scripts/farm/.fleet"
|
|
SSH="ssh -i $SSH_KEY -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o BatchMode=yes -o LogLevel=ERROR -o ConnectTimeout=12"
|
|
|
|
echo "══ WORKERS ($(date '+%H:%M:%S')) ══"
|
|
if [ -s "$FLEET" ]; then
|
|
while read -r name ip; do
|
|
[ -n "${name:-}" ] || continue
|
|
echo "▶ $name ($ip)"
|
|
$SSH "root@$ip" '
|
|
# ⚠ EL `load average` DE UN LXC ES EL DEL ANFITRIÓN, no el nuestro. /proc/loadavg no está
|
|
# virtualizado acá (sin lxcfs), así que ese número cuenta lo que muelen los OTROS inquilinos
|
|
# del Proxmox de gioser. Medido 2026-09-21: «load 5.31» con el 4º campo en `1/1546` —UN hilo
|
|
# ejecutable de 1546— y presión de CPU en 0.00, o sea el worker IDLE leído como «compilando»,
|
|
# que es justo la conclusión cara (se espera a que «termine» algo que no corre). Los dos
|
|
# números de abajo SÍ son del contenedor.
|
|
echo " $(uptime | sed "s/.*load average/load del ANFITRIÓN/")"
|
|
run=$(cut -d" " -f4 /proc/loadavg)
|
|
psi=$(sed -n "s/^some avg10=\([0-9.]*\).*/\1/p" /proc/pressure/cpu 2>/dev/null)
|
|
echo " nuestro: $run ejecutables/hilos · presión CPU avg10=${psi:-(sin PSI)}"
|
|
n=$(ls /opt/takana/store 2>/dev/null | wc -l); echo " store: $n artefactos"
|
|
# ⚠ LOS DOS NOMBRES. El renombre hammer→takana (ADR 0016) dejó esta sonda ciega: el worker
|
|
# invoca `./target/release/takana` y acá se buscaba `release/hammer`, así que «moliendo»
|
|
# decía «(nada — idle)» CON EL WORKER CONSTRUYENDO. Comprobado el 2026-09-14: el informe
|
|
# decía idle mientras allá corría `takana --store ./store build recipes/centrifugo.toml`.
|
|
# Un informe que miente sobre si hay trabajo es peor que no tenerlo — leyendo ese «idle» se
|
|
# concluye que la granja está seca. El binario `hammer` sigue existiendo (cargo lo compila
|
|
# como alias), así que se aceptan los dos.
|
|
b=$(pgrep -af "release/(takana|hammer)" | grep -E "release/(takana|hammer) (build|--store|hash)" | grep -oE "[^ /]+\.toml" | sort -u | tr "\n" " ")
|
|
echo " moliendo: ${b:-(nada — idle o entre colas)}"
|
|
' 2>/dev/null || echo " ⚠ sin respuesta (¿dead-man lo mató? ¿aún arrancando?)"
|
|
done < "$FLEET"
|
|
else
|
|
echo " flota vacía (scripts/farm/.fleet) — no hay worker vivo"
|
|
fi
|
|
|
|
echo; echo "══ AVANCE KDE (grafo local) ══"
|
|
if [ -f docs/state/build-state-kde.json ]; then
|
|
python3 - <<'PY'
|
|
import json,os,time
|
|
f="docs/state/build-state-kde.json"
|
|
d=json.load(open(f)); q=d["by_queue"].get("incoming-kde",{})
|
|
# ⚠ EL DENOMINADOR NO INCLUYE A LOS AJENOS. Un `ajeno` (qorpa-ajenos.toml) no tiene receta y NO SE
|
|
# SELLA NUNCA: está declarado fuera de alcance a propósito. Sumándolo, la barra se clavaba en
|
|
# «197/198 sellado» con el árbol TERMINADO, y se lee «falta una receta» — durante días (medido
|
|
# 2026-09-21: el 198 era `xwayland`, y X11 está fuera de alcance para toda la distro). Un informe
|
|
# que dice que falta trabajo cuando no falta manda a buscar un build que no existe. Se descuentan
|
|
# del total, pero se NOMBRAN: descontar en silencio es la otra forma de mentir.
|
|
aj=sorted(k for k,v in d["nodes"].items()
|
|
if v.get("queue")=="incoming-kde" and v.get("state")=="ajeno")
|
|
tot=sum(v for k,v in q.items() if k!="ajeno"); s=q.get("sealed",0)
|
|
age=int(time.time()-os.path.getmtime(f))
|
|
bar=int(30*s/tot) if tot else 0
|
|
cola=f" · {len(aj)} ajeno ({', '.join(aj)} — fuera de alcance, sin receta)" if aj else ""
|
|
print(f" [{'#'*bar}{'.'*(30-bar)}] {s}/{tot} sellado "
|
|
f"(deuda {q.get('debt',0)}, nunca {q.get('never',0)}){cola}")
|
|
print(f" grafo regenerado hace {age//60}m {age%60}s (lo actualiza el cron o `scripts/build-state.py --kde`)")
|
|
PY
|
|
else
|
|
echo " (sin grafo — corré scripts/build-state.py --kde)"
|
|
fi
|
|
|
|
echo; echo "══ LATIDO DE COSECHA ══"
|
|
# Se comprueba el EFECTO, no el mecanismo. Antes esto miraba `crontab -l` y decía «⚠ NO instalado»
|
|
# — cierto y RUIDOSO a la vez: desde el 2026-09-18 el latido lo sostiene el cron de root del
|
|
# ANFITRIÓN, y un agente enjaulado no ve ese crontab ni lo verá nunca. Un aviso que salta siempre es
|
|
# un aviso que nadie lee. Lo que sí prueba que el latido vive son sus commits, que están acá.
|
|
# ⚠ Y SE MIDE SOBRE origin/main, NO SOBRE EL LOCAL. El cron del anfitrión commitea en SU clon y
|
|
# empuja a gitea; este árbol no se entera hasta un `fetch`. Sin él, `git log` local da un latido
|
|
# viejo y el aviso de abajo salta con el latido SANO — falso positivo medido dos veces el
|
|
# 2026-09-21 (decía «hace 40m» con el ciclo de las 01:01 ya en gitea). El fetch es barato y va con
|
|
# techo: si la red falla, se degrada al local en vez de romper la foto.
|
|
timeout 20 git fetch -q origin main 2>/dev/null || true
|
|
# Se elige la ref con el latido MÁS NUEVO y se usa ESA para la fecha y para el asunto: sacarlos de
|
|
# refs distintas daba un «hace 3m» con el asunto de un ciclo viejo.
|
|
REF=HEAD
|
|
if git rev-parse -q --verify FETCH_HEAD >/dev/null 2>&1; then
|
|
a=$(git log -1 --format=%ct --grep="cosecha granja" HEAD 2>/dev/null || echo 0)
|
|
b=$(git log -1 --format=%ct --grep="cosecha granja" FETCH_HEAD 2>/dev/null || echo 0)
|
|
[ "${b:-0}" -gt "${a:-0}" ] && REF=FETCH_HEAD
|
|
fi
|
|
ult=$(git log -1 --format=%ct --grep="cosecha granja" "$REF" 2>/dev/null)
|
|
if [ -n "$ult" ]; then
|
|
edad=$(( $(date +%s) - ult ))
|
|
printf ' último ciclo: hace %dm %ds — %s\n' $((edad/60)) $((edad%60)) \
|
|
"$(git log -1 --format=%s --grep='cosecha granja' "$REF" 2>/dev/null)"
|
|
# dispara en :00 y :30 ⇒ más de 40 min sin commit es un latido perdido, no una pausa
|
|
if [ "$edad" -gt 2400 ]; then
|
|
echo " ⚠ hace más de 40m que no commitea — el latido dispara en :00 y :30"
|
|
else
|
|
echo " ✓ al día"
|
|
fi
|
|
else
|
|
echo " ⚠ no hay ningún commit de cosecha en el historial"
|
|
fi
|
|
if crontab -l 2>/dev/null | grep -q '[c]osecha-cron'; then
|
|
echo " (y además está en el crontab de este usuario)"
|
|
fi
|
|
if [ -f work/cosecha-cron.log ]; then
|
|
echo " último ciclo (tail):"
|
|
grep -E 'cosecha-cron (arranca|fin)|siembra|cosecha ✓|estado commit' work/cosecha-cron.log | tail -6 | sed 's/^/ /'
|
|
fi
|