La verificación de reproducibilidad de Stage 2 (dos builds + diff de bytes) probó que musl reconstruye bit-idéntico pero busybox NO, y cazó dos no-determinismos: 1. Config interactiva: tras editar .config, silentoldconfig prompea por opciones NEW leyendo stdin → set de applets variable (build no-determinista y flaky). Fix en busybox.toml: `yes '' | make oldconfig` (defaults, determinista; 1.36.1 no tiene olddefconfig). 2. Headers UAPI del kernel: el config determinista habilita console-tools, que exige linux/kd.h (zig no la bundlea). Fix: linux-headers en bootstrap-devfs.sh (zig cc nativo la halla en /usr/include). Con ambos, musl y busybox reconstruyen bit-idéntico. Runbook §8b documenta la verificación y deja pendiente: reproducibilidad de los componentes Rust y el rebuild dentro de un builder rootfs (auto-alojamiento pleno). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
157 lines
6.0 KiB
Bash
Executable File
157 lines
6.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# bootstrap-devfs.sh — Prepara .dev-fs/ con todo lo que hammer-build necesita:
|
|
# 1. Rootfs Alpine minirootfs como base hermética del sandbox.
|
|
# 2. Compilador zig oficial bajo .dev-fs/tools/zig (symlink versionado).
|
|
# 3. Build tools (make, autoconf, automake, m4, patch, coreutils, libtool,
|
|
# pkgconf, bash) instaladas dentro del rootfs con apk.
|
|
# 4. Directorios de caché y work para builds posteriores.
|
|
#
|
|
# Idempotente: cada paso se salta si ya hizo. Reinvocar tras un cambio de
|
|
# versión actualiza in-place.
|
|
#
|
|
# Variables (override por entorno):
|
|
# ALPINE_BRANCH rama de Alpine (default v3.23)
|
|
# ALPINE_VER versión exacta del tarball (default 3.23.4)
|
|
# ALPINE_SHA256 sha256 del tarball (verificado obligatorio)
|
|
# ZIG_VER versión de zig (default 0.16.0)
|
|
# ZIG_SHA256 sha256 del tarball zig
|
|
#
|
|
# Uso:
|
|
# ./scripts/bootstrap-devfs.sh # bootstrap completo
|
|
# ./scripts/bootstrap-devfs.sh --skip-apk # sólo rootfs + zig
|
|
# ./scripts/bootstrap-devfs.sh --force-zig # re-descarga zig
|
|
|
|
set -euo pipefail
|
|
|
|
ALPINE_BRANCH="${ALPINE_BRANCH:-v3.23}"
|
|
ALPINE_VER="${ALPINE_VER:-3.23.4}"
|
|
ALPINE_SHA256="${ALPINE_SHA256:-85498865362aa7ebececa0d725a2f2e4db7ac4e4b2850b8df21645afa0d03ee3}"
|
|
ZIG_VER="${ZIG_VER:-0.16.0}"
|
|
ZIG_SHA256="${ZIG_SHA256:-70e49664a74374b48b51e6f3fdfbf437f6395d42509050588bd49abe52ba3d00}"
|
|
|
|
SKIP_APK=0
|
|
FORCE_ZIG=0
|
|
FORCE_ROOTFS=0
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
--skip-apk) SKIP_APK=1 ;;
|
|
--force-zig) FORCE_ZIG=1 ;;
|
|
--force-rootfs) FORCE_ROOTFS=1 ;;
|
|
-h|--help)
|
|
sed -n '2,/^$/p' "$0" | sed 's/^# \?//'
|
|
exit 0 ;;
|
|
*)
|
|
echo "argumento desconocido: $arg" >&2
|
|
exit 1 ;;
|
|
esac
|
|
done
|
|
|
|
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
DEVFS="$REPO_ROOT/.dev-fs"
|
|
ROOTFS="$DEVFS/alpine"
|
|
TOOLS="$DEVFS/tools"
|
|
ZIG_DIR="$TOOLS/zig-x86_64-linux-$ZIG_VER"
|
|
ZIG_LINK="$TOOLS/zig"
|
|
CACHE="$DEVFS/cache"
|
|
WORK="$REPO_ROOT/work"
|
|
|
|
log() { printf '\033[1;34m==>\033[0m %s\n' "$*"; }
|
|
ok() { printf '\033[1;32m✓\033[0m %s\n' "$*"; }
|
|
|
|
mkdir -p "$DEVFS" "$TOOLS" "$CACHE" "$WORK"
|
|
|
|
# --- 1. Rootfs Alpine ---------------------------------------------------------
|
|
if [[ $FORCE_ROOTFS -eq 1 ]] && [[ -d "$ROOTFS" ]]; then
|
|
log "rootfs: --force-rootfs ⇒ borro $ROOTFS"
|
|
rm -rf "$ROOTFS"
|
|
fi
|
|
|
|
if [[ -f "$ROOTFS/bin/busybox" ]]; then
|
|
ok "rootfs Alpine ya presente en $ROOTFS"
|
|
else
|
|
log "rootfs: descargando alpine-minirootfs $ALPINE_VER"
|
|
tar="$DEVFS/alpine-minirootfs-$ALPINE_VER.tar.gz"
|
|
url="https://dl-cdn.alpinelinux.org/alpine/$ALPINE_BRANCH/releases/x86_64/alpine-minirootfs-$ALPINE_VER-x86_64.tar.gz"
|
|
curl -fsSL "$url" -o "$tar"
|
|
echo "$ALPINE_SHA256 $tar" | sha256sum -c -
|
|
mkdir -p "$ROOTFS"
|
|
tar -xzf "$tar" -C "$ROOTFS"
|
|
rm -f "$tar"
|
|
ok "rootfs Alpine $ALPINE_VER extraído"
|
|
fi
|
|
|
|
# --- 2. Compilador zig --------------------------------------------------------
|
|
if [[ $FORCE_ZIG -eq 1 ]] && [[ -e "$ZIG_DIR" ]]; then
|
|
log "zig: --force-zig ⇒ borro $ZIG_DIR"
|
|
rm -rf "$ZIG_DIR"
|
|
fi
|
|
|
|
if [[ -x "$ZIG_LINK/zig" ]]; then
|
|
ok "zig $ZIG_VER ya presente en $ZIG_LINK"
|
|
else
|
|
log "zig: descargando $ZIG_VER"
|
|
tar="$TOOLS/zig.tar.xz"
|
|
url="https://ziglang.org/download/$ZIG_VER/zig-x86_64-linux-$ZIG_VER.tar.xz"
|
|
curl -fsSL "$url" -o "$tar"
|
|
echo "$ZIG_SHA256 $tar" | sha256sum -c -
|
|
tar -xJf "$tar" -C "$TOOLS"
|
|
rm -f "$tar"
|
|
ln -sfn "zig-x86_64-linux-$ZIG_VER" "$ZIG_LINK"
|
|
ok "zig $ZIG_VER instalado, symlink $ZIG_LINK"
|
|
fi
|
|
|
|
# --- 3. Build tools en el rootfs (apk add) ------------------------------------
|
|
if [[ $SKIP_APK -eq 1 ]]; then
|
|
log "apk: --skip-apk ⇒ saltando build tools"
|
|
else
|
|
# `binutils` aporta ld/ar/ranlib/nm/strip: configure de autotools los inspecciona
|
|
# incluso cuando el compilador real es zig cc.
|
|
#
|
|
# `rust cargo`: las recetas Cargo (hammerd, arje-zero) corren `cargo build` DENTRO del
|
|
# sandbox. CAVEAT (primera corrida 2026-06-11): el rust de Alpine tiene host triple
|
|
# `x86_64-alpine-linux-musl`, pero `BuildSys::Cargo` pide `--target x86_64-unknown-linux-musl`
|
|
# y Alpine no trae ese std (no hay rustup). Falta resolver el toolchain Rust del lab (plan C.2)
|
|
# — ver docs/runbooks/stage1-vm-boot.md §8. Hasta entonces, los componentes C (musl, busybox)
|
|
# sí construyen end-to-end.
|
|
# `linux-headers`: UAPI del kernel (linux/kd.h, etc.) que zig cc NO bundlea; busybox y otros
|
|
# paquetes la necesitan. zig cc nativo la encuentra en /usr/include. (Lo destapó la
|
|
# verificación de reproducibilidad de Stage 2: con el config determinista, los applets de
|
|
# console-tools la exigen.)
|
|
NEEDED="make autoconf automake m4 patch coreutils libtool pkgconf bash binutils rust cargo linux-headers"
|
|
missing=""
|
|
for pkg in $NEEDED; do
|
|
# apk info -e devuelve el paquete si está instalado, vacío si no.
|
|
if ! bwrap --bind "$ROOTFS" / --proc /proc --dev /dev --unshare-all \
|
|
--setenv PATH /usr/bin:/usr/sbin:/bin:/sbin \
|
|
/sbin/apk info -e "$pkg" >/dev/null 2>&1; then
|
|
missing="$missing $pkg"
|
|
fi
|
|
done
|
|
if [[ -n "$missing" ]]; then
|
|
log "apk: instalando paquetes faltantes:$missing"
|
|
bwrap --bind "$ROOTFS" / --proc /proc --dev /dev \
|
|
--ro-bind /etc/resolv.conf /etc/resolv.conf --share-net \
|
|
/sbin/apk update >/dev/null
|
|
bwrap --bind "$ROOTFS" / --proc /proc --dev /dev \
|
|
--ro-bind /etc/resolv.conf /etc/resolv.conf --share-net \
|
|
/sbin/apk add $missing
|
|
fi
|
|
ok "build tools presentes en el rootfs ($NEEDED)"
|
|
fi
|
|
|
|
# --- 4. Resumen ---------------------------------------------------------------
|
|
cat <<EOF
|
|
|
|
bootstrap-devfs.sh — OK
|
|
rootfs: $ROOTFS
|
|
zig: $ZIG_LINK ($("$ZIG_LINK/zig" version))
|
|
cache: $CACHE
|
|
work: $WORK
|
|
|
|
Variables que la CLI ya recoge por defecto (override con env vars):
|
|
HAMMER_ROOTFS = $ROOTFS
|
|
HAMMER_ZIG = $ZIG_LINK
|
|
HAMMER_CACHE = $CACHE # vacío ("") desactiva la caché
|
|
HAMMER_WORK = $WORK
|
|
EOF
|