#!/usr/bin/env bash # farm-lab-sync.sh — pone el LAB ANCLADO en un worker y PRUEBA que sella igual que el hub. # # ── POR QUÉ NO BASTA CON farm-sync ────────────────────────────────────────────────────────────── # `farm-sync.sh` sube el código con `--exclude /.dev-fs`, a propósito: el lab es del entorno, no del # repo. Eso estaba bien cuando el toolchain NO entraba en el hash. Desde el 2026-08-10 sí entra # (`takana-core/src/lab.rs`), así que un worker con su lab horneado en la golden —rustc 1.96— sella # en direcciones DISTINTAS a las del hub —1.97— y su trabajo no le sirve a nadie: no es que compile # distinto, es que lo guarda en otra dirección y el hub nunca lo encuentra. # # Tampoco basta con correr `bootstrap-devfs.sh` en el worker: su paso 0 sólo trae la imagen si NO # hay rootfs, y la golden trae uno. Se reemplaza a la fuerza. # # ── EL WORKER SIGUE SIN SECRETOS ──────────────────────────────────────────────────────────────── # La imagen se EMPUJA desde el hub por scp, no se baja del Storage Box. Así el worker no necesita la # llave del box y se mantiene el invariante del modelo hub-and-spoke: compute puro y descartable. # # ── EVIDENCIA, NO FE ──────────────────────────────────────────────────────────────────────────── # El paso final NO es "extraje la imagen": es comparar el `takana hash` de una receta testigo entre # worker y hub. Si no coinciden, este script FALLA y el worker no debe construir — un worker que # sella en otra dirección quema dinero produciendo artefactos que nadie va a encontrar. # # Uso: scripts/farm/farm-lab-sync.sh set -euo pipefail ROOT="$(cd "$(dirname "$0")/../.." && pwd)"; cd "$ROOT" IP="${1:?falta la IP del worker}" SSH_KEY="${SSH_KEY:-$HOME/.ssh/github5}" REMOTE="${REMOTE:-/opt/takana}" SSH="ssh -i $SSH_KEY -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" IMG="$ROOT/.dev-fs/lab-image.tar.zst" TESTIGO="${TESTIGO:-recipes/zlib.toml}" log() { printf '\033[1;34m==>\033[0m %s\n' "$*"; } ok() { printf '\033[1;32m✓\033[0m %s\n' "$*"; } [ -f "$IMG" ] || { echo "no existe $IMG — corré scripts/lab-image.sh --crear" >&2; exit 1; } log "1/4 empujando la imagen del lab ($(du -h "$IMG" | cut -f1)) a $IP" rsync -a --partial-dir=.rsync-partial -e "$SSH" "$IMG" "root@$IP:$REMOTE/.dev-fs/lab-image.tar.zst" log "2/4 reemplazando el rootfs del worker (rotación, no extracción encima)" $SSH "root@$IP" "set -e cd $REMOTE/.dev-fs rm -rf alpine.nuevo && mkdir -p alpine.nuevo zstd -d -q -c lab-image.tar.zst | tar -x -C alpine.nuevo rm -rf alpine.viejo [ -d alpine ] && mv alpine alpine.viejo mv alpine.nuevo/alpine alpine rmdir alpine.nuevo rm -rf alpine.viejo" log "3/4 recompilando hammer en el worker (el binario debe traer el lab en hash_inputs)" # `touch` ANTES de compilar, y no es paranoia: `rsync -a` PRESERVA EL MTIME DEL HUB, así que un # fuente recién sincronizado puede quedar más VIEJO que el binario que el worker compiló hace un # rato ⇒ cargo dice «Finished in 0.09s» y no recompila nada. El worker se queda con el takana # anterior, calcula la huella vieja y el paso 4 falla sin que se vea por qué (2026-08-12). $SSH "root@$IP" "cd $REMOTE && . \$HOME/.cargo/env 2>/dev/null find crates -name '*.rs' -exec touch {} + cargo build --release --bin takana --bin hammer 2>&1 | tail -2" log "4/4 EVIDENCIA: ¿el worker sella en la misma dirección que el hub?" h_hub=$("$ROOT/target/release/takana" --store "$ROOT/store" hash "$TESTIGO" | tail -1) h_w=$($SSH "root@$IP" "cd $REMOTE && ./target/release/takana --store ./store hash $TESTIGO 2>&1 | tail -1") echo " hub : $h_hub" echo " worker : $h_w" if [ "$h_hub" = "$h_w" ]; then ok "el worker sella igual que el hub — puede construir" else echo "⛔ DIVERGEN. Este worker NO debe construir: sus artefactos irían a direcciones que el hub" echo " nunca va a buscar. Revisá que la imagen del lab llegó entera y que recompiló hammer." >&2 exit 1 fi