gioser

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 604Rust 56Shell 36Markdown 281227 Definiciones

⎇ 03e908fa20939db7003e6aeecc78d6d930971d21 scripts/farm/exp-ec.sh Shell · 81 líneas · 5.1 KB Historia crudo

exp-ec: tres fallos del propio arnés — sellaba basura y el veredicto no miraba el estado de salida 1a97da983 · Sergio ·

1#!/bin/bash
2# exp-ec.sh — el experimento del ADR 0020: ¿cuántas recetas dejan de construir si las fases
3# corriesen con `sh -ec` en vez de `sh -c`?
4#
5# ── POR QUÉ NO SELLA NADA, Y POR QUÉ ESO IMPORTA ───────────────────────────────────────────────
6# Modificar una receta le cambia el `ArtifactHash`, así que medir esto a lo bruto dejaría un
7# duplicado basura en el store POR CADA receta que pase — en un store que se respalda. En vez de eso
8# se inyecta `set -e` al principio de cada fase Y un `exit 77` al final de la última, de modo que el
9# build SIEMPRE falla y nunca sella. El veredicto se lee en el código que reporta takana:
10#
11#   · falló con 77   ⇒ las fases corrieron ENTERAS bajo set -e   ⇒ SOBREVIVE a `-ec`
12#   · falló con ≠77  ⇒ una orden intermedia falla y hoy se traga ⇒ SE ROMPE con `-ec`
13#
14# Las copias se escriben en `recipes/exp-ec-*.toml` —tienen que vivir ahí porque takana resuelve las
15# deps RELATIVAS al directorio de la receta— y se barren al terminar.
16#
17# ⚠ El lock se toma POR RECETA, no por tanda. La regla 1 de CLAUDE.md dice lo contrario para las
18# tandas normales, y con razón; acá es al revés a propósito: una tanda de éstas puede durar horas y
19# retener `work/.farm-build.lock` todo ese tiempo pararía la cosecha del latido.
20#
21# Uso:  MUESTRA=/ruta/lista.txt scripts/farm/exp-ec.sh     (una receta por línea, p.ej. recipes/gzip.toml)
22cd "$(dirname "$0")/../.."
23TMP=${TMPDIR:-/tmp}/exp-ec
24# REC se puede apuntar a una COPIA de recipes/: en el worker, la siembra de la cosecha corre con
25# `rsync --delete` cada 30 min y barrería las copias del experimento a mitad de una tanda larga.
26REC=${REC:-$(cd "$(dirname "$0")/../.." && pwd)/recipes}
27mkdir -p "$TMP"; OUT="$TMP/resultados.tsv"; : > "$OUT"
28
29# El lock va RELATIVO a la raíz del repo, que es donde CLAUDE.md lo define (`work/.farm-build.lock`).
30# En la caja `work` es un symlink a /work y en el worker es un directorio de verdad: cablear la ruta
31# absoluta de una máquina rompe en la otra con `flock: cannot open lock file`.
32LOCK=${LOCK:-work/.farm-build.lock}
33# Y `takana` no está en el PATH del worker — allá se invoca por su ruta dentro del árbol.
34TAKANA=${TAKANA:-$(command -v takana 2>/dev/null || echo ./target/release/takana)}
35# Techo por receta. 420 s alcanza para las ligeras; la cola pesada (llvm, firefox, kernels) pide
36# mucho más y se le pasa TLIMITE por entorno. Un timeout NO es un veredicto: se cuenta aparte.
37TLIMITE=${TLIMITE:-420}
38
39while read -r r; do
40  [ -f "$r" ] || continue
41  n=$(basename "$r" .toml)
42  awk '
43    /^(compile|install|configure|check|prepare) = .{3}$/ { print; print "set -e"; inb=1; next }
44    { print }
45  ' "$r" > "$REC/exp-ec-$n.toml"
46  # El `exit 77` va al cierre de la ÚLTIMA FASE, que NO es la última `'''` del fichero: una receta
47  # puede traer bloques `'''` después de las fases (un `[[service]]`, una nota larga). MEDIDO el
48  # 2026-09-18: en `dwarves`, `elfutils` y `elfutils-libdw` el `exit 77` cayó fuera de toda fase, el
49  # build corrió entero y SELLÓ — con el hash movido por el `set -e`, o sea basura en el store. Por
50  # eso acá se rastrea la anidación en vez de buscar la última comilla triple.
51  last=$(awk '
52    /^(compile|install|configure|check|prepare) = .{3}$/ { dentro=1; next }
53    dentro && /^.{3}$/ { fin=NR; dentro=0 }
54    END { print fin }' "$REC/exp-ec-$n.toml")
55  [ -n "$last" ] || { echo "[SIN-FASES] $n"; printf '%s\t%s\t%s\n' "$n" "SIN-FASES" "" >> "$OUT"; rm -f "$REC/exp-ec-$n.toml"; continue; }
56  [ -n "$last" ] && sed -i "${last}i exit 77" "$REC/exp-ec-$n.toml"
57
58  log="$TMP/$n.log"
59  timeout "$TLIMITE" flock -o "$LOCK" "$TAKANA" ${STORE:+--store "$STORE"} build "$REC/exp-ec-$n.toml" > "$log" 2>&1
60  rc=$?
61  # ⚠ El veredicto se lee en el ESTADO DE SALIDA que reporta takana, NO buscando `exit 77` en el
62  # log: takana IMPRIME el script de la fase, así que esa cadena aparece SIEMPRE — la inyectamos
63  # nosotros. Buscarla a secas daría SOBREVIVE hasta a un build que selló.
64  if grep -q "build phase falló (exit 77)" "$log"; then v="SOBREVIVE"
65  elif grep -qE "sealed path=\S*-$n\$" "$log"; then
66    # No debería pasar nunca: si selló, el `exit 77` no llegó a correr y el artefacto lleva el hash
67    # MOVIDO por el `set -e` ⇒ es basura en el store. Se grita con el hash para poder borrarlo.
68    v="SELLO-INDEBIDO"
69    echo "   ⚠⚠ $n SELLÓ — basura: $(grep -oE "sealed path=\S+" "$log" | tail -1 | sed 's|.*/||')"
70  elif [ $rc -eq 124 ]; then v="TIMEOUT"
71  elif grep -qE "build phase falló \(exit [0-9]+\)" "$log"; then v="SE-ROMPE"
72  else v="OTRO"; fi
73  det=$(grep -oE "build phase falló \(exit [0-9]+\): .*" "$log" | head -1 | cut -c1-90)
74  printf '%s\t%s\t%s\n' "$n" "$v" "$det" >> "$OUT"
75  echo "[$v] $n"
76done < ${MUESTRA:?hace falta MUESTRA=<fichero con una receta por línea>}
77
78echo "════ RESUMEN ════"
79for v in SOBREVIVE SE-ROMPE TIMEOUT OTRO; do echo "$v: $(grep -c "	$v	" "$OUT")"; done
80rm -f "$REC"/exp-ec-*.toml
81echo "copias de experimento barridas de recipes/: $(ls "$REC"/exp-ec-*.toml 2>/dev/null | wc -l) quedan"