#!/usr/bin/env bash # verificar-repro.sh — ¿reproducen los artefactos bit a bit? Reconstruye y COMPARA. Etapa 1 del SDD 23. # # ── POR QUÉ ESTE SCRIPT Y NO UN GREP ─────────────────────────────────────────────────────────── # El no-determinismo se mide RECONSTRUYENDO Y COMPARANDO, no buscando cadenas sospechosas en los # binarios. El plan heredado daba 92 paquetes por «no-deterministas probables» porque tenían rutas # `/src` en `.debug_*`; al mirarlo resultó que `/src` es un bind CONSTANTE del lab, y que las únicas # rutas realmente ajenas venían de terceros (la ruta de compilación de Alpine horneada en el musl # prebuilt, cadenas literales del fuente, rutas de autores en assets SVG). Un grep sobre binarios da # CANDIDATOS, no veredictos. Ver SDD 23 §1. # # ── EL MÉTODO, Y POR QUÉ ES BARATO ───────────────────────────────────────────────────────────── # Se APARTA el artefacto (no se borra) y se reconstruye. Como las DEPS siguen en el store, el rebuild # es sólo el paquete en cuestión, no su cadena — que es lo que hace viable verificar decenas. Después # `hammer why-differs` compara los dos árboles y nombra la causa si divergen. # # ── LA RED DE SEGURIDAD ──────────────────────────────────────────────────────────────────────── # Si el rebuild FALLA (y fallará en alguna: hay recetas que sólo construyen en ciertas máquinas), el # artefacto original se RESTAURA. Un verificador que destruye lo que verifica es peor que no tenerlo. # Por eso se aparta a un directorio temporal en vez de borrar, y hay `trap` para restaurar todo lo # pendiente si se interrumpe. # # Uso: scripts/verificar-repro.sh [receta.toml ...] (sin args: muestra por clase) # Env: STORE (def ./store) N (def 30, tamaño de la muestra) set -uo pipefail ROOT="$(cd "$(dirname "$0")/.." && pwd)"; cd "$ROOT" STORE="${STORE:-./store}"; HAMMER="${HAMMER:-./target/release/hammer}"; N="${N:-30}" GUARDA="$(mktemp -d)"; PENDIENTE="" restaurar_todo() { for h in $PENDIENTE; do [ -d "$GUARDA/$h" ] && [ ! -d "$STORE/$h" ] && mv "$GUARDA/$h" "$STORE/$h" && echo " ↩ restaurado $h" done rm -rf "$GUARDA" } trap restaurar_todo EXIT INT TERM if [ $# -gt 0 ]; then RECETAS="$*" else # Muestra por CLASE de build: si sólo se verifican paquetes C con meson, el resultado no dice nada # de Rust ni de Go, que son justamente donde el no-determinismo suele vivir (codegen paralelo, # orden de símbolos, timestamps de módulos). RECETAS=$(ls recipes/*.toml recipes/incoming-wlr/*.toml 2>/dev/null | shuf -n "$N" --random-source=/dev/zero | tr '\n' ' ') fi ok=0; dif=0; fallo=0; saltada=0; DIVERGEN="" for f in $RECETAS; do [ -f "$f" ] || continue n=$(awk -F'"' '/^name[[:space:]]*=/{print $2; exit}' "$f"); [ -n "$n" ] || n=$(basename "$f" .toml) H=$("$HAMMER" --store "$STORE" hash "$f" 2>/dev/null | tail -1 | sed 's/^b3://') [ -n "$H" ] || { saltada=$((saltada+1)); continue; } D="$H-$n" [ -d "$STORE/$D" ] || { saltada=$((saltada+1)); continue; } mv "$STORE/$D" "$GUARDA/$D"; PENDIENTE="$PENDIENTE $D" if "$HAMMER" --store "$STORE" build "$f" >/dev/null 2>&1 && [ -d "$STORE/$D" ]; then if "$HAMMER" why-differs "$GUARDA/$D" "$STORE/$D" >/dev/null 2>&1; then printf " ✓ %-26s REPRODUCE\n" "$n"; ok=$((ok+1)) rm -rf "$GUARDA/$D" else printf " ✗ %-26s DIVERGE\n" "$n"; dif=$((dif+1)); DIVERGEN="$DIVERGEN $n" "$HAMMER" why-differs "$GUARDA/$D" "$STORE/$D" 2>&1 | tail -4 | sed 's/^/ /' rm -rf "$GUARDA/$D" fi else # El rebuild falló: restaurar y NO contarlo como divergencia (no es lo mismo «no reproduce» que # «no construye acá»). Distinguirlos importa: mezclarlos inventaría un problema de determinismo. printf " ⚠ %-26s no construyó (artefacto restaurado)\n" "$n"; fallo=$((fallo+1)) rm -rf "$STORE/$D"; mv "$GUARDA/$D" "$STORE/$D" fi PENDIENTE=$(echo "$PENDIENTE" | sed "s| $D||") done echo "════════════════════════════════════════════════════════════" echo " REPRODUCEN: $ok · DIVERGEN: $dif · no construyeron: $fallo · sin artefacto: $saltada" [ "$dif" -gt 0 ] && echo " ⚠ divergen:$DIVERGEN" [ "$dif" -eq 0 ] && [ "$ok" -gt 0 ] && echo " ✅ PUERTA SUPERADA: 0 divergencias sobre $ok verificadas" exit $(( dif > 0 ? 1 : 0 ))