Files
hammer/scripts/bootstrap-devfs.sh
T
SergioandClaude Opus 4.7 1fc8c97617 Fase 0+1 cerradas: GNU grep 3.12 real construido e hidratado
Cierra el primer entregable del roadmap. Cambios:

- `Source`: ahora admite dos modos mutuamente excluyentes — `git` (repo+commit) o
  `tarball` (url+sha256). El hash de entrada del artefacto usa el commit en modo
  git y el sha256 en modo tarball; ambos son identificadores inmutables del
  contenido fuente. Validación en `Source::kind()` con error claro.
- `fetch`: dispatch por modo. Tarball cacheado en `work/tarballs/<sha>.tar`,
  descarga vía `curl -fL`, verificación sha256 con `sha2`, extracción con
  `--strip-components` (default 1, GNU-style).
- `recipes/grep.toml`: GNU grep 3.12 desde el tarball release de gnu.org
  (sha256 fijado, sin patches, `--disable-perl-regexp --disable-nls`).
- Bootstrap: añade `binutils` al rootfs Alpine — configure de autotools mira
  ld/ar/ranlib incluso cuando el compilador real es zig cc.
- `.gitignore`: `/work/` (artefactos transitorios del build).
- `docs/10-roadmap.md`: Fase 0 y Fase 1 marcadas ; entregable cerrado.

Nota sobre v3.11 vs v3.12: probé primero v3.11 y el binario producido daba
"memory exhausted" en cualquier regex (bug conocido de grep+musl static al
inicializar DFA). v3.12 corrige y funciona limpio dentro del rootfs Alpine.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-09 14:11:46 +00:00

146 lines
5.1 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.
NEEDED="make autoconf automake m4 patch coreutils libtool pkgconf bash binutils"
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