catálogo objetivo P1: manifiesto de perfiles — el set de la distro deja de ser un string de shell
El destino de la distro vivía en 2 strings de product-userland-from-repo.sh, 1 de mirada-usb.sh y 55 tandas planas. Ahora en docs/state/targets.toml: un perfil = una imagen enviable, listando sólo las RAÍCES (lo que se pide por nombre); la clausura la calcula el grafo, no un humano. 4 perfiles: base (26), cli (46, hereda base), escritorio-mirada (14), escritorio-kde (7 raíces, cola incoming-kde). scripts/targets.py expande hereda (transitivo, con detección de ciclo) preservando el orden — mirada lo necesita. Lift-and-shift verificado: las 3 listas expandidas son byte-idénticas a los strings que reemplazan. Ninguna imagen cambia de contenido. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -34,8 +34,9 @@ declare -A PIN=(
|
||||
[mesa-swrast]=410e4fb9806598921048f492508725f55ee8475a1e9ef8ea2dc64f357154a0d9
|
||||
[libxkbcommon]=cef7db4a82882ee942fef5e758344da67c9f3fce6b221610a7db9cb05bfb3b53
|
||||
)
|
||||
# Orden: libs primero, binarios mirada al final. Todos aportan .so o /usr/bin.
|
||||
PKGS="mesa-swrast wayland libdrm seatd libinput libudev-zero libxkbcommon pixman linux-pam mtdev libevdev mirada-compositor mirada-greeter mirada-ctl"
|
||||
# Orden: libs primero, binarios mirada al final. Todos aportan .so o /usr/bin. El set sale del
|
||||
# MANIFIESTO DE OBJETIVO (docs/state/targets.toml, perfil `escritorio-mirada`), que preserva el orden.
|
||||
PKGS="$("$(dirname "$0")/targets.py" escritorio-mirada)"
|
||||
|
||||
resolve() { # nombre -> hash del store (pin si existe, si no el más reciente por mtime)
|
||||
local n="$1"
|
||||
|
||||
@@ -18,11 +18,12 @@ DB="$RFS/var/lib/hammer/installed.json"
|
||||
|
||||
# Set curado por defecto: el userland "real" que hace usable a la distro. Todos deben estar en el
|
||||
# índice del repo firmado. Editá/pasá args para cambiarlo.
|
||||
# base-system: userland foundational C (shell/VCS/privilegios/red/disco/TLS/proceso/editor/docs),
|
||||
# de-Alpinizado y firmado (tandas base-system-1..3). CLI Rust: la capa "moderna" curada encima.
|
||||
BASE_SYSTEM_PKGS="bash git sudo doas util-linux shadow kbd procps-ng iproute2 iputils dhcpcd e2fsprogs dosfstools parted rsync sed tzdata vim dbus ca-certificates gnupg lsof strace pciutils usbutils mandoc"
|
||||
CLI_RUST_PKGS="bat fd ripgrep jq zoxide dust procs htop less tree hyperfine delta eza sd tokei fzf skim git-cliff gitui starship"
|
||||
DEFAULT_PKGS="$BASE_SYSTEM_PKGS $CLI_RUST_PKGS"
|
||||
# El set ya NO vive acá: sale del MANIFIESTO DE OBJETIVO (docs/state/targets.toml), perfil `cli` =
|
||||
# `base` (userland foundational C, de-Alpinizado, tandas base-system-1..3) + la capa CLI Rust encima.
|
||||
# Cambiar el userland del producto = editar el manifiesto, que es también lo que mide el grafo de
|
||||
# estado. Env PERFIL para armar otra imagen (`scripts/targets.py --lista` los muestra).
|
||||
PERFIL="${PERFIL:-cli}"
|
||||
DEFAULT_PKGS="$("$ROOT/scripts/targets.py" "$PERFIL")"
|
||||
PKGS_LIST="${PKGS:-${*:-$DEFAULT_PKGS}}"
|
||||
|
||||
[ -n "${OVER:-}" ] || { rm -rf "$RFS"; mkdir -p "$RFS"; }
|
||||
|
||||
Executable
+83
@@ -0,0 +1,83 @@
|
||||
#!/usr/bin/env python3
|
||||
# targets.py — lee `docs/state/targets.toml` (el manifiesto de objetivo) y expande un perfil a su
|
||||
# lista de RAÍCES. Un solo parser para dos consumidores: los scripts de imagen (shell) y el grafo
|
||||
# de estado (`build-state.py`), para que el set de paquetes de la distro no vuelva a vivir en tres
|
||||
# strings de shell distintos.
|
||||
#
|
||||
# Uso: scripts/targets.py cli # raíces expandidas, una línea separada por espacios
|
||||
# scripts/targets.py --lista # perfiles disponibles, con su descripción
|
||||
# scripts/targets.py --json # todo el manifiesto expandido
|
||||
# Como módulo: from targets import load → {perfil: {descripcion, cola, hereda, paquetes, raices}}
|
||||
#
|
||||
# `hereda` es transitivo y el orden se PRESERVA (primero las raíces de lo heredado, en el orden
|
||||
# declarado, después las propias), porque hay imágenes donde el orden de hidratación importa
|
||||
# (mirada: libs antes que binarios). Duplicados: se queda la primera aparición.
|
||||
import json, sys, tomllib
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
DEFAULT = ROOT / "docs/state/targets.toml"
|
||||
|
||||
|
||||
def load(path=DEFAULT):
|
||||
"""Devuelve {perfil: {descripcion, cola, hereda, paquetes, raices}} con `raices` ya expandido."""
|
||||
doc = tomllib.load(open(path, "rb"))
|
||||
crudos = doc.get("perfil", {})
|
||||
perfiles = {}
|
||||
for n, p in crudos.items():
|
||||
perfiles[n] = dict(
|
||||
descripcion=p.get("descripcion", ""),
|
||||
cola=p.get("cola", "corpus"),
|
||||
hereda=list(p.get("hereda", [])),
|
||||
paquetes=list(p.get("paquetes", [])),
|
||||
)
|
||||
|
||||
# Expansión de `hereda` con detección de ciclo (un manifiesto con un ciclo es un error del
|
||||
# humano, no un caso a tolerar: sale ruidoso).
|
||||
def expandir(n, camino):
|
||||
if n in camino:
|
||||
raise ValueError(f"ciclo en `hereda`: {' → '.join(camino + [n])}")
|
||||
if n not in perfiles:
|
||||
raise KeyError(f"perfil desconocido en `hereda`: {n}")
|
||||
p = perfiles[n]
|
||||
if "raices" in p:
|
||||
return p["raices"]
|
||||
vistos, raices = set(), []
|
||||
for h in p["hereda"] + [None]:
|
||||
for pkg in (expandir(h, camino + [n]) if h else p["paquetes"]):
|
||||
if pkg not in vistos:
|
||||
vistos.add(pkg); raices.append(pkg)
|
||||
p["raices"] = raices
|
||||
return raices
|
||||
|
||||
for n in perfiles:
|
||||
expandir(n, [])
|
||||
return perfiles
|
||||
|
||||
|
||||
def main():
|
||||
args = sys.argv[1:]
|
||||
try:
|
||||
perfiles = load()
|
||||
except FileNotFoundError:
|
||||
print(f"no existe {DEFAULT.relative_to(ROOT)}", file=sys.stderr); return 1
|
||||
except (ValueError, KeyError) as e:
|
||||
print(f"manifiesto inválido: {e}", file=sys.stderr); return 1
|
||||
|
||||
if not args or args[0] in ("--lista", "-l"):
|
||||
for n, p in sorted(perfiles.items()):
|
||||
print(f"{n:20} {len(p['raices']):3} raíces [{p['cola']}] {p['descripcion']}")
|
||||
return 0
|
||||
if args[0] == "--json":
|
||||
print(json.dumps(perfiles, indent=1, ensure_ascii=False)); return 0
|
||||
|
||||
n = args[0]
|
||||
if n not in perfiles:
|
||||
print(f"perfil desconocido: {n} (hay: {', '.join(sorted(perfiles))})", file=sys.stderr)
|
||||
return 1
|
||||
print(" ".join(perfiles[n]["raices"]))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user