tawasuyu

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 1572Shell 203Rust 109Markdown 993728 Definiciones

⎇ 4517a985c1a9d0a586556f3393c32da0a4d662d2 scripts/build-farm.sh Shell · 88 líneas · 4.9 KB Historia crudo

Etapa G (granja): promote más seguro — patches viajan con la receta + guarda de colisión 4517a985c · sergio ·

1#!/bin/sh
2# build-farm.sh — Etapa G: GRANJA de build paralela sobre la COLA DE STAGING (recipes/incoming/).
3# El import escala (import-batch ~95% yield, cientos en minutos); el COMPILE es el cuello de botella
4# (1-7 min c/u) pero es embarrassingly-parallel + cacheable por contenido (el store dedup global). Esta
5# es la pieza que convierte "decenas por sesión serial" en "drenar la cola en paralelo": construye N a
6# la vez, y lo que CONSTRUYE+sella lo PROMUEVE incoming/->recipes/ y lo publica al repo firmado; lo que
7# falla queda staged con su motivo (MSRV, dep faltante, …) para revisión.
8#
9# Dos fases: (1) BUILD en paralelo (lo caro, worker pool de $JOBS) — escribe status por paquete; (2)
10# PROMOTE+PUBLISH en SERIE (el index.json del repo es mutable compartido ⇒ no se paraleliza) + firma 1 vez.
11# Idempotente: una receta ya sellada sale por cache-hit; re-correr sólo reintenta lo que falta.
12#
13# Uso:   JOBS=4 scripts/build-farm.sh                 # drena recipes/incoming/*.toml
14#        JOBS=6 QUEUE=recipes/incoming scripts/build-farm.sh
15# Env:   JOBS (def: nproc-2), QUEUE (def recipes/incoming), STORE (def ./store), REPO (def dist/repo),
16#        KEY (clave de firma; def dist/keys/release.ed25519), DISTRO (def dev), HAMMER (cmd del cli),
17#        PROMOTE (def 1; =0 sólo construye+mide, no toca recipes/ ni el repo).
18set -eu
19
20ROOT="$(cd "$(dirname "$0")/.." && pwd)"; cd "$ROOT"
21QUEUE="${QUEUE:-recipes/incoming}"
22STORE="${STORE:-./store}"
23REPO="${REPO:-$ROOT/dist/repo}"
24KEY="${KEY:-$ROOT/dist/keys/release.ed25519}"
25DISTRO="${DISTRO:-dev}"
26HAMMER="${HAMMER:-$ROOT/target/release/hammer}"
27PROMOTE="${PROMOTE:-1}"
28JOBS="${JOBS:-$(( $(nproc) - 2 ))}"; [ "$JOBS" -ge 1 ] || JOBS=1
29
30FARM="$ROOT/work/farm"
31rm -rf "$FARM"; mkdir -p "$FARM/log" "$FARM/status"
32
33queue=$(ls "$QUEUE"/*.toml 2>/dev/null || true)
34[ -n "$queue" ] || { echo "cola vacía: $QUEUE/*.toml"; exit 0; }
35n_total=$(printf '%s\n' "$queue" | wc -l | tr -d ' ')
36echo "==> GRANJA: $n_total recetas en $QUEUE, $JOBS workers en paralelo"
37
38# ── Fase 1: BUILD en paralelo. Cada worker construye una receta y deja su status (.ok con hash | .fail
39# con motivo). El store sella atómico y cada build usa sources/<name>-<sha> + out/<hash>-<name> propios
40# ⇒ sin colisión entre recetas distintas. `xargs -P` es el pool.
41printf '%s\n' "$queue" | xargs -P"$JOBS" -I '{}' sh -c '
42  f="$1"; n=$(basename "$f" .toml); log="'"$FARM"'/log/$n.log"
43  H=$("'"$HAMMER"'" --store "'"$STORE"'" build "$f" 2>"$log" | tail -1 || true)
44  if [ "${H#b3:}" != "$H" ] && [ -n "$H" ]; then
45    echo "$H" > "'"$FARM"'/status/$n.ok"; printf "  ✓ %-18s %s\n" "$n" "$H"
46  else
47    r=$(grep -oiE "requires rustc [0-9.]+|cannot find -l[a-z0-9]+|could not find .[a-z0-9_-]+.|error\[E[0-9]+\]|not found|unsupported|no such file" "$log" | head -1)
48    echo "${r:-fallo (ver log)}" > "'"$FARM"'/status/$n.fail"; printf "  ✗ %-18s %s\n" "$n" "${r:-fallo}"
49  fi
50' _ '{}'
51
52# ── Fase 2: PROMOTE + PUBLISH en serie (índice del repo = recurso mutable compartido).
53ok=$(ls "$FARM"/status/*.ok 2>/dev/null | wc -l | tr -d ' ')
54fail=$(ls "$FARM"/status/*.fail 2>/dev/null | wc -l | tr -d ' ')
55echo "==> BUILD-YIELD: $ok/$n_total construyen ($fail fallan)"
56
57if [ "$PROMOTE" = 1 ] && [ "$ok" -gt 0 ]; then
58  echo "==> promoviendo + publicando $ok al repo firmado…"
59  for s in "$FARM"/status/*.ok; do
60    n=$(basename "$s" .ok)
61    # GUARDA DE COLISIÓN: si ya existe una receta canónica en recipes/ y la cola trae
62    # OTRA variante con el mismo nombre, NO la promovemos (mv sobrescribiría lo canónico:
63    # p.ej. un `curl` mínimo-para-appstream pisando el curl 8.20 full de la que dependen tools).
64    # Queda staged en la cola para triage manual; tampoco re-publicamos sobre lo canónico.
65    if [ -f "$QUEUE/$n.toml" ] && [ -f "recipes/$n.toml" ]; then
66      printf "  = %s (ya existe recipes/%s.toml canónica — NO promovido, queda en %s)\n" "$n" "$n" "$QUEUE"
67      continue
68    fi
69    if [ -f "$QUEUE/$n.toml" ]; then
70      # mover la receta Y sus patches referenciados (viven junto al .toml en la cola);
71      # si no, `pack` no los encuentra en recipes/ y falla toda receta parcheada.
72      for p in $(grep -oE '"[^"]+\.patch"' "$QUEUE/$n.toml" | tr -d '"'); do
73        [ -f "$QUEUE/$p" ] && mv -f "$QUEUE/$p" recipes/
74      done
75      mv "$QUEUE/$n.toml" recipes/
76    fi
77    "$HAMMER" --store "$STORE" pack "recipes/$n.toml" --repo "$REPO" --distro-version "$DISTRO" --build >/dev/null 2>&1 \
78      && printf "  + %s\n" "$n" || printf "  ! %s (pack falló)\n" "$n"
79  done
80  "$HAMMER" repo sign --repo "$REPO" --key "$KEY" >/dev/null 2>&1 && echo "==> release re-firmado"
81fi
82
83# ── Reporte de fallos (motivo por paquete) para triage.
84if [ "$fail" -gt 0 ]; then
85  echo "==> FALLARON ($fail) — quedan staged en $QUEUE:"
86  for s in "$FARM"/status/*.fail; do printf "  ✗ %-18s %s\n" "$(basename "$s" .fail)" "$(cat "$s")"; done
87fi
88echo "==> granja: $ok promovidos, $fail staged. logs en $FARM/log/"