Etapa G: orquestador de tandas import-batch.sh (escalera nix→Alpine, yield por tier)

Faltaba el orquestador de "importar tandas a escala": las piezas per-paquete ya
existían (nix-import.sh, alpine-import.sh) pero sin un driver que aplicara la ESCALERA
DE FALLBACK en lote.

- import-batch.sh <pkg...> | -f <listfile>: por paquete intenta nix (Rust/Go/CLI,
  ~100% musl) y cae a Alpine main→community si el import falla (Alpine trae los parches
  de musl). Mapeo opcional nixattr=alpinepkg. Escribe OUTDIR/<pkg>.toml.
- Mide el YIELD POR TIER (nix / alpine / fallaron) — el dato real al escalar, sin
  especular. stdout limpio = nombres importados (encadenable con xargs/pin-recipes).
- Encadena con las piezas existentes: pin-recipes.sh (Fase 2 tag→SHA) + build-repo.sh
  (Etapa F pack --build + repo firmado).
- tandas/cli-rust.txt: primera tanda curada (tier 1 Rust) para el modo -f.

VALIDADO REAL en vivo (nix 2.34.7): tanda {fd, hyperfine, jq} → yield 3/3, nix=2
(fd/hyperfine), alpine=1 (jq cae a main + baja test-portable.patch). pin en lote sobre
la tanda: fd v10.4.2 → SHA 7027d453, jq (tarball) salteado.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-21 09:01:24 -04:00
co-authored by Claude Opus 4.8
parent 41aec17048
commit 66dbc3a561
2 changed files with 90 additions and 0 deletions
+74
View File
@@ -0,0 +1,74 @@
#!/bin/sh
# import-batch.sh <pkg...> | import-batch.sh -f <listfile> — Etapa G: importa una TANDA a
# escala aplicando la ESCALERA DE FALLBACK (memoria etapa-g): por cada paquete intenta primero nix
# (Rust/Go/CLI moderno, ~100% en musl) y, si el import falla (src no-clasificable, eval error), cae
# a Alpine (que TRAE los parches de musl) probando `main` y luego `community`. Escribe la receta a
# OUTDIR/<pkg>.toml y, al final, MIDE EL YIELD POR TIER (nix / alpine / fallaron) — el dato real que
# decide dónde está la fricción al escalar, sin especular.
#
# Es sólo el IMPORT (barato). El trabajo real es construir: encadená con
# scripts/pin-recipes.sh "$OUTDIR"/*.toml (Fase 2: tag→SHA en lote)
# scripts/build-repo.sh (Etapa F: pack --build + repo firmado)
#
# Ej: scripts/import-batch.sh ripgrep fd bat hyperfine
# scripts/import-batch.sh -f tandas/cli-rust.txt
# OUTDIR=recipes/incoming scripts/import-batch.sh sd dust
# Env: OUTDIR (default recipes), NIX_STORE (p.ej. $HOME/.nixstore si /nix/store no es escribible),
# ALPINE_BRANCH (default master), HAMMER (cmd del cli). Un nombre puede mapear nix→alpine
# distintos con `nixattr=alpinepkg` (p.ej. `ripgrep=ripgrep`, `the_silver_searcher=the-silver-searcher`).
set -eu
here=$(dirname "$0")
OUTDIR="${OUTDIR:-recipes}"
export OUTDIR # alpine-import.sh baja sus .patch acá
# Resolver la lista de paquetes: -f <fichero> (una entrada por línea, '#' comenta) o argumentos.
if [ "${1:-}" = "-f" ]; then
[ -n "${2:-}" ] || { echo "uso: import-batch.sh -f <listfile>" >&2; exit 1; }
pkgs=$(sed -e 's/#.*//' -e '/^[[:space:]]*$/d' "$2")
else
[ "$#" -gt 0 ] || { echo "uso: import-batch.sh <pkg...> | -f <listfile>" >&2; exit 1; }
pkgs=$(printf '%s\n' "$@")
fi
mkdir -p "$OUTDIR"
nix_n=0; alpine_n=0; failed=""; got=""
# IFS=newline para tolerar una entrada por línea; restaurar después no hace falta (sub-shell del for).
OLDIFS=$IFS; IFS='
'
for entry in $pkgs; do
IFS=$OLDIFS
# Mapeo opcional nixattr=alpinepkg; si no hay '=', el mismo nombre sirve para ambas fuentes.
case $entry in
*=*) nixattr=${entry%%=*}; alpinepkg=${entry#*=} ;;
*) nixattr=$entry; alpinepkg=$entry ;;
esac
out="$OUTDIR/${nixattr}.toml"
printf '%-24s ' "$nixattr" >&2
if "$here/nix-import.sh" "$nixattr" > "$out.tmp" 2>/dev/null && [ -s "$out.tmp" ]; then
mv "$out.tmp" "$out"
nix_n=$((nix_n + 1)); got="$got $nixattr"
echo "✓ nix" >&2
elif "$here/alpine-import.sh" "$alpinepkg" main > "$out.tmp" 2>/dev/null && [ -s "$out.tmp" ]; then
mv "$out.tmp" "$out"
alpine_n=$((alpine_n + 1)); got="$got $nixattr"
echo "✓ alpine/main (parches musl)" >&2
elif "$here/alpine-import.sh" "$alpinepkg" community > "$out.tmp" 2>/dev/null && [ -s "$out.tmp" ]; then
mv "$out.tmp" "$out"
alpine_n=$((alpine_n + 1)); got="$got $nixattr"
echo "✓ alpine/community (parches musl)" >&2
else
rm -f "$out.tmp"
failed="$failed $nixattr"
echo "✗ ni nix ni Alpine" >&2
fi
done
total=$((nix_n + alpine_n))
echo >&2
echo "yield import: $total/$((total + $(printf '%s' "$failed" | wc -w))) — nix=$nix_n, alpine=$alpine_n.${failed:+ FALLARON:$failed}" >&2
echo "recetas en $OUTDIR/.${got:+ Siguiente: pin + build —} ${got:+scripts/pin-recipes.sh}${got:+ $OUTDIR/*.toml}" >&2
# Salida limpia en stdout: los nombres importados (para encadenar con xargs si se quiere).
printf '%s\n' $got
+16
View File
@@ -0,0 +1,16 @@
# Tanda CLI Rust moderno — Etapa G. Tier 1 de la escalera (Rust/musl-friendly → import-nix ~100%).
# Uso: NIX_STORE=$HOME/.nixstore OUTDIR=recipes/incoming scripts/import-batch.sh -f tandas/cli-rust.txt
# Luego: scripts/pin-recipes.sh recipes/incoming/*.toml && (pack --build para medir build-yield).
# Sintaxis: un paquete por línea; '#' comenta; `nixattr=alpinepkg` si los nombres difieren.
fd
bat
hyperfine
ripgrep
sd
dust
procs
tokei
hexyl
xh
zoxide
bottom