takana etapa 5c: comentarios de scripts, MOTD, y un BUG que introdujo la etapa 4
250 líneas de comentario en 151 scripts. Control verificado: el diff no toca NI UNA línea que no empiece por #, y la sintaxis de los 151 pasa. El barrido saltea heredocs y cadenas triples, y el guardián DISPARÓ 3 veces: las tres eran el MOTD que el script escribe DENTRO de la imagen construida — texto del producto, no comentario del script. Se cambiaron aparte y a propósito, que es rebranding, no limpieza. Y el hallazgo caro: casaba contra , que es el TARGET de tracing — o sea el module_path!, o sea el nombre del crate. La etapa 4 lo movió a y el script quedó casando NADA. No fallaba: imprimía cero atribuciones, indistinguible de un log sin problemas. Comprobado con el binario (RUST_LOG=info sobre zlib), no deducido. Ahora acepta las dos, y tiene que seguir aceptándolas porque los logs viejos en disco dicen la vieja. Además 14 rutas de módulo en docs, que el barrido anterior no tocó porque no es frontera de palabra.
This commit is contained in:
@@ -112,7 +112,7 @@ esté probado.
|
||||
El crate `takana-agent` (Fase 6) expone:
|
||||
|
||||
```rust
|
||||
// hammer_agent::client
|
||||
// takana_agent::client
|
||||
pub struct AgentClient { pub welcome: Welcome, /* … */ }
|
||||
impl AgentClient {
|
||||
pub fn connect(sock: &Path) -> Result<Self>;
|
||||
@@ -124,12 +124,12 @@ impl AgentClient {
|
||||
pub fn next_async(&self, timeout: Duration) -> Result<Event>;
|
||||
}
|
||||
|
||||
// hammer_agent::translator
|
||||
// takana_agent::translator
|
||||
pub trait IntentTranslator { fn translate(&self, intent: &str, ctx: &SystemContext) -> Result<Swm, TranslateError>; }
|
||||
pub struct MockTranslator { /* HashMap<intent, Swm> */ }
|
||||
pub struct IntentCatalog { pub intents: Vec<CatalogEntry> } // YAML loader
|
||||
|
||||
// hammer_agent::orchestrator
|
||||
// takana_agent::orchestrator
|
||||
pub struct Orchestrator<T: IntentTranslator> { /* … */ }
|
||||
impl<T> Orchestrator<T> {
|
||||
pub fn new(t: T, base: BaseRef, apply: ApplyTarget, compile: CompileMode) -> Self;
|
||||
|
||||
+5
-5
@@ -71,7 +71,7 @@ pre-requisito de validación.
|
||||
`open_by_handle_at`+`CAP_DAC_READ_SEARCH`) queda para el track posterior: `nix` 0.30 no
|
||||
parsea los info-records de FID y el modo FID perdería el fd que hoy hashea el contenido.
|
||||
- [x] Hash del contenido tras la mutación (`content_hash`) y de-dup idempotente.
|
||||
`hammer_journal::content_hash_of`/`hash_file` (blake3 plano, estilo `b3sum`, distinto del
|
||||
`takana_journal::content_hash_of`/`hash_file` (blake3 plano, estilo `b3sum`, distinto del
|
||||
`of_inputs` de artefactos). `Journal::record_dedup` omite el evento si deja el archivo
|
||||
idéntico al último de ese path (o un `Delete` sobre algo ya borrado); `last_for_path` lo
|
||||
resuelve. El watcher hashea cada `CLOSE_WRITE` y usa `record_dedup`: una reescritura sin
|
||||
@@ -86,7 +86,7 @@ pre-requisito de validación.
|
||||
- [x] Bridge `Mutation::SourcePatch` → `Recipe` + `build` + hidratación.
|
||||
- [x] CLI: `takana apply [--prefix DIR] [--base-ref base.json]`,
|
||||
`takana swm-verify`, `takana export --journal DIR > out.swm`.
|
||||
- [x] `patch_url` / `content_url` remotos. `hammer_build::download` (curl, sólo fuera del
|
||||
- [x] `patch_url` / `content_url` remotos. `takana_build::download` (curl, sólo fuera del
|
||||
sandbox; acepta `file://` para tests offline). `build_source_patch` descarga `patch_url`
|
||||
al `swm-recipes/<commit>.patch` antes de compilar; `takana apply` descarga `content_url`
|
||||
y **verifica BLAKE3 antes de escribir** (hash erróneo ⇒ nada tocado). Su integridad la
|
||||
@@ -98,7 +98,7 @@ pre-requisito de validación.
|
||||
modelados: `Mutation::SourcePatch` (y `RecipeInline`) llevan campos `repo`/`commit`
|
||||
**xor** `tarball`/`sha256` (resueltos por `swm::swm_source_kind`, misma regla que
|
||||
`recipe::Source::kind`); `takana export` reconstruye ambos sin caer a `file_drop`.
|
||||
- [x] Firma `signature` (ed25519) y `TrustStore` local. `hammer_core::sign`: `KeyPair`
|
||||
- [x] Firma `signature` (ed25519) y `TrustStore` local. `takana_core::sign`: `KeyPair`
|
||||
(genera/carga/escribe claves), `TrustStore::load(dir)` (lee `*.ed25519.pub`),
|
||||
`Swm::verify_signature(&trust) → SigStatus` (`trusted`/`unknown-key`/`bad-sig`/
|
||||
`unsigned`) sobre bytes canónicos JSON del manifiesto sin la firma. CLI:
|
||||
@@ -121,7 +121,7 @@ pre-requisito de validación.
|
||||
- [x] Subsistemas independientes en `hammerd::main` (FIFO/watcher/bus en threads); si uno
|
||||
falla en init, los demás siguen.
|
||||
- [x] Política expresiva leída de `/etc/hammer/agent-caps.toml` (`hammerd --agent-caps`).
|
||||
`hammer_core::AgentCapsConfig`: `default` + `[[rule]]` por `uid`/`gid` (primera que casa
|
||||
`takana_core::AgentCapsConfig`: `default` + `[[rule]]` por `uid`/`gid` (primera que casa
|
||||
gana), `caps_for(uid,gid)`. `bus::policy_from_config` la enchufa; sin fichero o con
|
||||
fichero inválido, cae a la política por-UID por defecto (avisando). Ejemplo en
|
||||
`examples/agent-caps.toml`. El `default_policy` hardcoded queda como fallback.
|
||||
@@ -328,7 +328,7 @@ Diseño completo en [SDD 11 — Bootstrap from-scratch](11-bootstrap.md). Resume
|
||||
de-Alpinizados:** `recipes/{flex,bison}.toml` (kconfig), `recipes/openssl.toml` (3.5.4, certs/extract-cert),
|
||||
`recipes/elfutils.toml` (sólo libelf 0.194 para objtool, con shims musl mínimos). Capa de arranque
|
||||
restante: m4, libz/libzstd, gcc (status libc).
|
||||
- ✅ **Etapa A — orquestador `bootstrap all` + log de transparencia exportable:** `hammer_bootstrap::all`
|
||||
- ✅ **Etapa A — orquestador `bootstrap all` + log de transparencia exportable:** `takana_bootstrap::all`
|
||||
encadena stage0→stage1→stage2 en una corrida del host (`takana bootstrap all --url … --sha256 … --version …`)
|
||||
y deja el `bootstrap.json` poblado; `takana bootstrap manifest` lo imprime (la superficie de publicación
|
||||
del log, SDD 11 §4). El veredicto `✓ REPRODUCIBLE` lo sigue sellando el rebuild in-VM (`selfhost-verify.sh`),
|
||||
|
||||
@@ -54,7 +54,7 @@ confianza de Alpine y la deriva de su propio [log de transparencia](09-trust-mod
|
||||
|
||||
Modelamos el toolchain semilla como una **fuente fijada**, no como un build: un `Source` de
|
||||
tipo tarball con `sha256` pinned (`zig-<ver>-linux-x86_64.tar.xz`, o el tarball de
|
||||
`musl-cross-make`). `takana-bootstrap` lo descarga (fuera del sandbox, como `hammer_build::download`
|
||||
`musl-cross-make`). `takana-bootstrap` lo descarga (fuera del sandbox, como `takana_build::download`
|
||||
ya hace para `patch_url`/`content_url`), verifica el sha256 **antes** de sellarlo, y lo deposita
|
||||
en el store con un `artifact_hash` derivado de ese sha256. A partir de ahí el resto del
|
||||
bootstrap depende del *hash del store*, no de la ruta del host.
|
||||
@@ -262,7 +262,7 @@ el toolchain) y **correr el rebuild anidado** en la VM.
|
||||
|
||||
### 7.4 Estado: builder ensamblado + rebuild in-VM ejecutado ✅ ✓ REPRODUCIBLE
|
||||
|
||||
El **ensamblado** del builder está implementado (`hammer_bootstrap::builder_rootfs`, variante a):
|
||||
El **ensamblado** del builder está implementado (`takana_bootstrap::builder_rootfs`, variante a):
|
||||
|
||||
```
|
||||
takana bootstrap builder --stage1 <H> --seed-hash <H> \
|
||||
|
||||
@@ -80,11 +80,11 @@ kernel en `/boot`, los módulos GRUB en `/boot/grub/i386-pc` y los pasos `grub-m
|
||||
anuncia el `of_tree` esperado (de un índice de **mirror E3** o release firmada), el apply lo exige antes
|
||||
de tocar el root. Maneja ficheros regulares + symlinks; valida E2E en host con `scripts/upgrade-e2e-test.sh`
|
||||
(apply v1→v2→rollback→rollback contra el binario real). **GC de generaciones ✅** (`takana upgrade
|
||||
prune [--keep N]` / `hammer_upgrade::prune`): borra las generaciones que el rollback ya no alcanza
|
||||
prune [--keep N]` / `takana_upgrade::prune`): borra las generaciones que el rollback ya no alcanza
|
||||
(huérfanas tras un rollback — las de la [cadena viva](#) siempre se conservan); `--keep N` además
|
||||
recorta la cadena a sus N más nuevas (limita la profundidad de rollback, como `delete-generations` en
|
||||
NixOS). **Journal de intención + replay ✅** (`takana upgrade recover [--rollback]` /
|
||||
`hammer_upgrade::{pending,recover}`): el apply escribe el **plan completo** a `pending.json` ANTES de
|
||||
`takana_upgrade::{pending,recover}`): el apply escribe el **plan completo** a `pending.json` ANTES de
|
||||
proyectar y lo limpia al commitear; si un corte/reinicio lo interrumpe, `pending.json` sobrevive y el
|
||||
FHS quedó a medias. La proyección es **re-entrante** (`project_plan`, backups *idempotentes* —
|
||||
`backup_existing_once` nunca pisa el original capturado), así `recover` **completa** (roll-forward:
|
||||
|
||||
@@ -110,7 +110,7 @@ muerto antes del takeover).
|
||||
hace `switch_root` a la ext4 real. Ese initrd chico es lo que destraba EFI-stub directo (el cuelgue
|
||||
del metal era por un initrd de 100MB). Validado en OVMF por `scripts/efi-disk-boot-test.sh`. (La vía
|
||||
squashfs+overlay del [post-etapa-e] queda como optimización futura; el pivote a ext4 ya cumple.)
|
||||
3. **Contrato de generaciones-grafo** ✅ (módulo `hammer_upgrade::boot_graph` + subcomando `takana
|
||||
3. **Contrato de generaciones-grafo** ✅ (módulo `takana_upgrade::boot_graph` + subcomando `takana
|
||||
boot`): el modelo in-place de generaciones se lee como un **grafo de estados** navegable (id =
|
||||
`of_tree`, DAG por `parent`), `takana boot graph` emite `/run/hammer/boot-graph.json` con el
|
||||
formato del contrato, y `takana boot activate <id>` (o `--from-select`) deja el sistema en un nodo
|
||||
|
||||
@@ -113,7 +113,7 @@ Es el mismo fichero de lock que toman `scripts/farm/farm-worker-loop.sh` y `camp
|
||||
que con esto quedan serializados los tres. Deliberadamente **no** está dentro de `takana build`: los
|
||||
scripts de la granja ya lo toman por fuera y takana se bloquearía contra ellos.
|
||||
|
||||
Nada más del armador toca el store: `plan` calcula el `ArtifactHash` con `hammer_build::artifact_hash`,
|
||||
Nada más del armador toca el store: `plan` calcula el `ArtifactHash` con `takana_build::artifact_hash`,
|
||||
que es cómputo puro sobre las recetas, y `probe`/`gate`/`bundles`/`closure` sólo leen.
|
||||
|
||||
**Si el build muere con «no encuentro el ejecutable zig en …», el zig está bien: falta ESA versión.**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
# alpine-import.sh <pkg> [main|community] — Etapa G, fuente #2: baja el APKBUILD de Alpine aports
|
||||
# (y SUS PARCHES de musl), y emite la receta hammer vía `hammer import-alpine`. Alpine es la mejor
|
||||
# (y SUS PARCHES de musl), y emite la receta takana vía `takana import-alpine`. Alpine es la mejor
|
||||
# semilla cuando importar de nix falla en musl: su receta YA incluye el trabajo de portabilidad.
|
||||
#
|
||||
# Ej: scripts/alpine-import.sh coreutils main > recipes/coreutils-alpine.toml
|
||||
@@ -18,7 +18,7 @@ BASE="https://gitlab.alpinelinux.org/alpine/aports/-/raw/${BRANCH}/${REPO}/${PKG
|
||||
apk=$(curl -fsSL "${BASE}/APKBUILD") || { echo "no encontré ${REPO}/${PKG} en aports@${BRANCH}" >&2; exit 1; }
|
||||
toml=$(printf '%s' "$apk" | $HAMMER import-alpine -)
|
||||
|
||||
# sha256 automático: Alpine publica sha512, pero hammer pide sha256. Bajamos el tarball UNA vez y
|
||||
# sha256 automático: Alpine publica sha512, pero takana pide sha256. Bajamos el tarball UNA vez y
|
||||
# lo calculamos, reemplazando el FIXME ⇒ receta lista para `pack --build` sin tocar el hash a mano.
|
||||
url=$(printf '%s\n' "$toml" | sed -n 's/^tarball = "\(.*\)"$/\1/p' | head -1)
|
||||
if [ -n "$url" ]; then
|
||||
|
||||
@@ -38,7 +38,12 @@ if desde:
|
||||
lineas = lineas[ini:]
|
||||
|
||||
marcas = [(i, m.group(1)) for i, l in enumerate(lineas)
|
||||
if (m := re.search(r"hammer_build: fetch hash=\S+ name=(\S+)", l))]
|
||||
# Las DOS formas del target de tracing (ADR 0016): el target es el module_path!, o sea
|
||||
# el nombre del crate, que pasó de `hammer_build` a `takana_build` en la etapa 4. Hay que
|
||||
# aceptar las dos porque los logs VIEJOS que ya están en disco dicen la forma vieja — y
|
||||
# porque este script no falla si no casa: atribuye cero fallos y se ve igual que un log
|
||||
# sin problemas.
|
||||
if (m := re.search(r"(?:hammer|takana)_build: fetch hash=\S+ name=(\S+)", l))]
|
||||
if not marcas:
|
||||
print("sin marcadores `fetch` en la ventana — ¿log equivocado o --desde muy tarde?")
|
||||
sys.exit(1)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
# attest-boot-test.sh — valida END-TO-END el gate de atestación (I4): hammer firma el seed (arje-packager)
|
||||
# attest-boot-test.sh — valida END-TO-END el gate de atestación (I4): takana firma el seed (arje-packager)
|
||||
# y el arje-zero CON gate (arje-zero-attest) lo verifica al arranque. Demuestra ambos caminos:
|
||||
# - íntegro: el gate atesta los binarios críticos ✓ y arranca los servicios (SSH responde);
|
||||
# - TAMPER=1: se altera 1 byte de un binario crítico DESPUÉS de firmar ⇒ el gate (política Halt)
|
||||
# aborta y cae a la shell de rescate (sin SSH) — la integridad comprometida NO levanta el entorno.
|
||||
#
|
||||
# DOS MODOS:
|
||||
# (A) REAL (preferido) — bootea el `product-attested-rootfs` que produce `hammer bootstrap product
|
||||
# (A) REAL (preferido) — bootea el `product-attested-rootfs` que produce `takana bootstrap product
|
||||
# --attest` (init con gate + seed firmada ya hidratados). Es el camino de PRODUCCIÓN, no un spike.
|
||||
# Uso: PRODUCT_ATTESTED=<hash> ./scripts/attest-boot-test.sh
|
||||
# (B) SPIKE (fallback) — overlaya a mano el arje-zero con gate + firma el seed sobre una copia del
|
||||
|
||||
@@ -20,7 +20,7 @@ H="$ROOT/target/release/takana"
|
||||
|
||||
# ── EL ROOTFS VA EN EL MISMO MOUNT QUE EL STORE, Y NO ES UN CAPRICHO ───────────────────────────
|
||||
# `hydrate` proyecta con HARDLINKS, y `linkat()` rechaza cruzar un punto de montaje aunque los dos
|
||||
# lados sean el mismo filesystem. Acá el store es `/dev/sdb` bind-monteado en `hammer/store`
|
||||
# lados sean el mismo filesystem. Acá el store es `/dev/sdb` bind-monteado en `takana/store`
|
||||
# mientras `work/` vive en `/dev/sdc`: hidratar a `work/…` muere con
|
||||
# «Invalid cross-device link (os error 18)» — medido, no supuesto.
|
||||
# El volumen entero está montado en /mnt/cosecha, así que store y destino comparten mount ahí.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# boot-builder-vm.sh — Bootea el builder rootfs (Stage 1 + toolchain) en QEMU para correr el
|
||||
# rebuild in-rootfs (auto-alojamiento, SDD 11 §7; runbook §8c).
|
||||
#
|
||||
# El builder se arma con `hammer bootstrap builder … --out work/builder-rootfs` y se empaqueta como
|
||||
# El builder se arma con `takana bootstrap builder … --out work/builder-rootfs` y se empaqueta como
|
||||
# initramfs (cpio newc + gzip). Aquí lo booteamos con arje-zero como PID 1: levanta hammerd + una
|
||||
# getty en consola y deja una shell. Desde ahí se corre el driver embebido:
|
||||
#
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
# bootstrap-devfs.sh — Prepara .dev-fs/ con todo lo que hammer-build necesita:
|
||||
# bootstrap-devfs.sh — Prepara .dev-fs/ con todo lo que takana-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,
|
||||
@@ -174,7 +174,7 @@ else
|
||||
# rust 1.91.1 ⇒ techo MSRV que bloquea recetas modernas (ouch/uv 1.93, oxlint/ruff 1.94,
|
||||
# zellij 1.92). edge trae rust 1.96.0. Apuntamos los repos del rootfs a EDGE para que `apk add`
|
||||
# resuelva rust/cargo/clang 1.96 (+ arrastra gcc 15.2/musl 1.2.6/llvm22 como deps). El of_tree
|
||||
# del 4/4 NO depende de esto (usa SWAP_RUST con el rust hammer-built); esto es el toolchain del
|
||||
# del 4/4 NO depende de esto (usa SWAP_RUST con el rust takana-built); esto es el toolchain del
|
||||
# CATÁLOGO. OJO: edge es RODANTE y no se puede pinear —sólo publica la última versión y Alpine no
|
||||
# da snapshots datados—, así que el paso 3a-quater registra lo resuelto en un lock y AVISA de la
|
||||
# deriva. Anclar de verdad exigiría espejar APKINDEX + los .apk, que es un frente aparte.
|
||||
@@ -189,7 +189,7 @@ else
|
||||
# verificación de reproducibilidad de Stage 2: con el config determinista, los applets de
|
||||
# console-tools la exigen.)
|
||||
# `bwrap git curl`: el rebuild *dentro* del builder rootfs (Stage 2 pleno, SDD 11 §7) corre
|
||||
# hammer-build con su propio toolchain. En el host estas tres las aporta el sistema, pero en la
|
||||
# takana-build con su propio toolchain. En el host estas tres las aporta el sistema, pero en la
|
||||
# VM el toolchain ES el único userland capaz, así que deben vivir aquí: `bwrap` anida el sandbox
|
||||
# de build, `git` rematerializa las fuentes git (git archive del mirror), `curl` baja tarballs.
|
||||
# `kmod` aporta insmod/modprobe: en la VM el sandbox de build usa overlay (módulo del kernel,
|
||||
@@ -206,10 +206,10 @@ else
|
||||
# runtime, sigue en el base como tool autotools general.)
|
||||
# NOTA: `elfutils-dev` YA NO va aquí — de-Alpinizado a recipes/elfutils.toml (sólo libelf, con shims
|
||||
# musl argp/error/libintl/fts/obstack/rawmemchr), deps.build del kernel. objtool enlaza el libelf
|
||||
# hammer desde la capa overlay. (El runtime libelf.so.1 de Alpine, si algún otro tool lo necesita,
|
||||
# takana desde la capa overlay. (El runtime libelf.so.1 de Alpine, si algún otro tool lo necesita,
|
||||
# es la misma versión 0.194 ABI-compatible.)
|
||||
# NOTA: `openssl-dev` YA NO va aquí — de-Alpinizado a recipes/openssl.toml (libcrypto estática,
|
||||
# deps.build del kernel). El host-tool certs/extract-cert lo enlaza desde la capa overlay hammer.
|
||||
# deps.build del kernel). El host-tool certs/extract-cert lo enlaza desde la capa overlay takana.
|
||||
# El runtime libcrypto3/libssl3 (que curl/git necesitan) lo trae Alpine aparte, no es openssl-dev.
|
||||
# `clang-dev clang-libs`: aportan libclang.so — `bindgen` (que usan varios *-sys: libbzip3-sys,
|
||||
# etc.) lo carga en build para generar bindings de los headers C. No lo trae el base.
|
||||
@@ -344,7 +344,7 @@ fi
|
||||
# linkea dinámicamente como intérprete (/lib/ld-musl-x86_64.so.1) — NO el musl target del 4/4
|
||||
# (recipes/musl.toml, estático, intacto). Reconstruimos musl 1.2.5 con 256 keys y reemplazamos el
|
||||
# loader. ABI-compat (mismo musl 1.2.5): gcc/make/etc. siguen corriendo. Seguro para of_tree: el
|
||||
# loader runtime de las tools no afecta los bytes que emiten; el 4/4 es estático contra hammer-musl.
|
||||
# loader runtime de las tools no afecta los bytes que emiten; el 4/4 es estático contra takana-musl.
|
||||
# Idempotente: si ya existe el backup .orig128, no rehace nada.
|
||||
# CRÍTICO: el loader reconstruido DEBE ser de la MISMA versión de musl que el rootfs (Alpine edge
|
||||
# es rodante: pasó de 1.2.5 a 1.2.6). Si difieren, los coreutils del rootfs (linkeados contra la
|
||||
|
||||
@@ -99,7 +99,7 @@ printf '%s\n' "$queue" | xargs -P"$JOBS" -I '{}' sh -c '
|
||||
# a la vez en sources/<dep>-<sha>/output ⇒ meson/tar chocan ("Some other Meson process is already using
|
||||
# this build directory", "Directory not empty", "No such file"). Tras la Fase 1 la dep ya la selló el
|
||||
# ganador de la carrera ⇒ reconstruir el fallo EN SERIE ahora cache-hitea la dep y compila sin colisión.
|
||||
# Un solo pase basta: cada `hammer build` arrastra sus deps, así que aunque el orden no sea topológico,
|
||||
# Un solo pase basta: cada `takana build` arrastra sus deps, así que aunque el orden no sea topológico,
|
||||
# la dep se construye/sella dentro del propio reintento. Sólo se reintenta lo que tiene FIRMA de colisión
|
||||
# (no MSRV/dep-faltante reales, que re-fallarían y sólo gastarían tiempo).
|
||||
for s in "$FARM"/status/*.fail; do
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#!/bin/sh
|
||||
# build-repo.sh — Etapa F (dogfood): puebla un REPO DE PAQUETES hammer desde el corpus
|
||||
# build-repo.sh — Etapa F (dogfood): puebla un REPO DE PAQUETES takana desde el corpus
|
||||
# `recipes/*.toml` y firma el release. Convierte la maquinaria de paquetería en la cadena de
|
||||
# suministro real de la distro: cada receta se vuelve un `.swm` (mutación sobre fuente), el repo
|
||||
# es el catálogo firmado, y `hammer install <nombre> --repo <dir|url>` lo reproduce localmente.
|
||||
# es el catálogo firmado, y `takana install <nombre> --repo <dir|url>` lo reproduce localmente.
|
||||
#
|
||||
# Para cada receta intenta ANCLAR el `expected_hash` con `pack --build` (instantáneo si el
|
||||
# artefacto ya está sellado en el store — cache-hit; ver hammer-build::build). Si construir desde
|
||||
# artefacto ya está sellado en el store — cache-hit; ver takana-build::build). Si construir desde
|
||||
# cero excede el timeout (o falla), cae a `pack` sin ancla: la entrada del catálogo igual existe y
|
||||
# el `source_patch` sigue siendo reproducible, sólo sin hash del autor contra el que comparar.
|
||||
#
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
# depende de qué) vivía disperso: en la cabeza del que construía, en docs que envejecen (el de
|
||||
# matar-gcc decía 47 y eran 16), en el store (que guarda TODOS los sellados históricos, no sólo el
|
||||
# vigente). Cada medición a mano mentía distinto. Este script deriva el estado de la ÚNICA fuente de
|
||||
# verdad —las recetas + `hammer hash` + el store— y lo escribe a `docs/state/build-state.json`.
|
||||
# verdad —las recetas + `takana hash` + el store— y lo escribe a `docs/state/build-state.json`.
|
||||
# Versionado: el `git diff` de ese fichero ES el avance entre dos corridas (qué se saldó, qué se
|
||||
# rompió). La vista humana la arma `build-state-view.py` a partir del mismo JSON.
|
||||
#
|
||||
@@ -14,7 +14,7 @@
|
||||
# sealed — el artefacto del hash VIGENTE (el de la receta de hoy) está en el store. Al día.
|
||||
# debt — hay sellados históricos pero NINGUNO es el vigente ⇒ la receta cambió, falta rebuild.
|
||||
# never — no hay ningún sellado de esta receta en el store.
|
||||
# unhashable — `hammer hash` falló (receta ilegible / dep rota). Un HUECO real del grafo.
|
||||
# unhashable — `takana hash` falló (receta ilegible / dep rota). Un HUECO real del grafo.
|
||||
# wanted — NO HAY RECETA: una imagen lo pide (docs/state/targets.toml) y no existe todavía.
|
||||
# ARISTA = dep de build (name → dep). El grafo debe CERRAR: toda dep apunta a una receta del corpus.
|
||||
#
|
||||
@@ -128,7 +128,7 @@ def load_recipes():
|
||||
# EJECUCIÓN declarada en la receta no entraba en la clausura ni en el rootfs
|
||||
# hidratado: `firefox` declaraba `runtime = ["gcc-libs"]` y el grafo seguía sin
|
||||
# conocerla, así que la imagen se armaba sin `libstdc++.so.6` y el navegador no
|
||||
# arrancaba. `deps.runtime` existe en el esquema de hammer desde siempre; lo que
|
||||
# arrancaba. `deps.runtime` existe en el esquema de takana desde siempre; lo que
|
||||
# faltaba era que ESTE contador lo mirara. Un campo que nadie lee es un campo que
|
||||
# miente. La unión es la definición de clausura: para CORRER hacen falta las dos.
|
||||
deps=sorted(set(d.get("deps", {}).get("build", []))
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@
|
||||
# distinta con su propio OS. Si coinciden ⇒ CONSENSO. Si divergen ⇒ candidato a `why-differs`
|
||||
# (SDD 17 §2) — y eso también es información: una divergencia es un canal impuro encontrado.
|
||||
#
|
||||
# Por qué esto no lo puede ofrecer nix/debian: su repro es incompleta. hammer ya paga el
|
||||
# Por qué esto no lo puede ofrecer nix/debian: su repro es incompleta. takana ya paga el
|
||||
# invariante (bit-repro verificada: selfhost, kernel), así que esto cae casi solo del invariante
|
||||
# — que es la tesis del SDD 17: buscar "qué cae del invariante", no "qué frontera agregar".
|
||||
#
|
||||
|
||||
@@ -50,7 +50,7 @@ BASE="${BASE:-$ROOT/.dev-fs/alpine}"
|
||||
MODE="${MODE:-bare}"; OUT="${OUT:-/mnt/cosecha/escritorios}"; SECS="${SECS:-0}"
|
||||
mkdir -p "$OUT"
|
||||
|
||||
# GL por software. Se resuelve por HASH VIGENTE (`hammer hash`), no por el más reciente del store:
|
||||
# GL por software. Se resuelve por HASH VIGENTE (`takana hash`), no por el más reciente del store:
|
||||
# con dos artefactos del mismo paquete conviviendo, la fecha miente.
|
||||
if [ -z "${MESA+x}" ]; then
|
||||
MH=$(./target/release/takana --store ./store hash recipes/mesa-llvmpipe.toml 2>/dev/null | tr -d "\n" | sed "s/b3://")
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
# hydrate-cosmic.sh — proyecta al FHS el cierre de runtime de COSMIC desde artefactos SELLADOS del
|
||||
# store, sin rebuild ni reproduce (`hammer hydrate <hash> --into`).
|
||||
# store, sin rebuild ni reproduce (`takana hydrate <hash> --into`).
|
||||
#
|
||||
# Es el hermano de scripts/gnome/hydrate-gnome.sh y comparte con él lo que importa: el cierre sale
|
||||
# del GRAFO REAL de recetas (`[deps].build`, resolución hermano→padre, la misma que usa hammer) y el
|
||||
# artefacto se elige por `hammer hash` —el ArtifactHash de la receta de HOY, sin construir—, no por
|
||||
# del GRAFO REAL de recetas (`[deps].build`, resolución hermano→padre, la misma que usa takana) y el
|
||||
# artefacto se elige por `takana hash` —el ArtifactHash de la receta de HOY, sin construir—, no por
|
||||
# el más reciente del store. Con dos artefactos del mismo paquete conviviendo, la fecha miente.
|
||||
#
|
||||
# ── POR QUÉ LAS RAÍCES SE NOMBRAN A MANO ────────────────────────────────────────────────────────
|
||||
|
||||
@@ -397,7 +397,7 @@ PY
|
||||
rm -f "$MERGED/etc/motd"
|
||||
cat > "$MERGED/etc/motd" <<'MOTD'
|
||||
|
||||
#-- hammer :: escritorio COSMIC (metal, Intel i915 + iris HW) ---------------
|
||||
#-- takana :: escritorio COSMIC (metal, Intel i915 + iris HW) ---------------
|
||||
GL por HARDWARE (iris). Es la diferencia con la imagen de KDE, y el motivo
|
||||
de este viaje: en QEMU mesa era software y ScreenCast no podía exportar
|
||||
dmabuf.
|
||||
|
||||
@@ -71,7 +71,7 @@ echo " ✓ libc.so + libc.musl-x86_64.so.1"
|
||||
|
||||
echo "==> inyectando arje-logind-compat (el login1 del fractal) + su política"
|
||||
# cosmic-comp pregunta por su sesión igual que mutter y kwin. El daemon no entra por el cierre porque
|
||||
# nadie lo declara como dep de build. Se elige por `hammer hash` —la receta de HOY—, no por el más
|
||||
# nadie lo declara como dep de build. Se elige por `takana hash` —la receta de HOY—, no por el más
|
||||
# reciente del store. Su política D-Bus viaja DENTRO del artefacto desde 2026-07-30.
|
||||
if ALC="${ALC:-store/$(./target/release/takana --store store hash recipes/arje-logind-compat.toml 2>/dev/null | tail -1 | sed 's/^b3://')-arje-logind-compat}"; [ -d "$ALC" ]; then
|
||||
install -Dm755 "$ALC"/usr/bin/arje-logind-compat "$MERGED"/usr/bin/arje-logind-compat
|
||||
@@ -158,7 +158,7 @@ chmod 1777 "$MERGED/tmp"
|
||||
# `<components version=… origin=…>`. Sin ese fichero la tienda abre perfecta y no lista NADA
|
||||
# —«No results for …»— porque su catálogo está vacío, no porque le falte nada del sistema.
|
||||
#
|
||||
# hammer no tiene appstreamcli (sería AppStream + glib, o sea la torre de C que esta campaña evita),
|
||||
# takana no tiene appstreamcli (sería AppStream + glib, o sea la torre de C que esta campaña evita),
|
||||
# pero el formato es XML plano y la agregación es exactamente eso: envolver los `<component>` de cada
|
||||
# metainfo en una raíz. Doce líneas de python valen la librería entera.
|
||||
echo "==> catálogo AppStream para cosmic-store (agrega los metainfo del rootfs)"
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
# junto (B2). Es el paso hacia la imagen instalable real: el store y el estado mutable dejan de ser meros
|
||||
# directorios del rootfs y pasan a ser particiones propias, como en un sistema instalado de verdad.
|
||||
#
|
||||
# /dev/vda1 → / rootfs (runtime + toolchain + /work del rebuild) [hammer-root]
|
||||
# /dev/vda2 → /store artefactos CAS BLAKE3 (inmutables) [hammer-store]
|
||||
# /dev/vda3 → /var/lib/hammer estado mutable: journal + overlays (upper/work) [hammer-state]
|
||||
# /dev/vda1 → / rootfs (runtime + toolchain + /work del rebuild) [takana-root]
|
||||
# /dev/vda2 → /store artefactos CAS BLAKE3 (inmutables) [takana-store]
|
||||
# /dev/vda3 → /var/lib/hammer estado mutable: journal + overlays (upper/work) [takana-state]
|
||||
#
|
||||
# El kernel (recipes/linux.toml) trae VIRTIO_BLK+EXT4+EFI_PARTITION(=y por defconfig) ⇒ lee la GPT y
|
||||
# monta /dev/vda1 como root sin initramfs. Las otras dos particiones las monta un wrapper `/sbin/init`
|
||||
|
||||
@@ -61,7 +61,7 @@ else:
|
||||
net = ["-netdev", "user,id=n0", "-device", "e1000,netdev=n0"] if WANT_NET else []
|
||||
|
||||
# APPEND override: por defecto consola serie + initramfs. Útil para experimentar con params del kernel
|
||||
# (p.ej. el frente kernel-from-source: el bzImage hammer bootea pero bwrap falla en pivot_root porque /
|
||||
# (p.ej. el frente kernel-from-source: el bzImage takana bootea pero bwrap falla en pivot_root porque /
|
||||
# es el rootfs absoluto del namespace; `rootfstype=tmpfs` NO lo arregla — la causa es que / no tiene
|
||||
# mount padre, no el tipo de fs). Override con APPEND="…".
|
||||
# Boot de disco (Etapa B3) vs initramfs. En disco / ya es un mount ext4 real ⇒ rdinit=/sbin/init directo,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
# efi-boot-test.sh — valida la rama UEFI del medio live (refinamiento #3): arma un ISO con
|
||||
# iso-image.sh EFI=1 y lo arranca por las DOS firmwares, probando que es un HÍBRIDO BIOS+UEFI:
|
||||
# (A) UEFI — QEMU con OVMF (pflash) → grubx64.efi (de la ESP FAT poblada con mtools de hammer) →
|
||||
# (A) UEFI — QEMU con OVMF (pflash) → grubx64.efi (de la ESP FAT poblada con mtools de takana) →
|
||||
# kernel EFI-stub + initrd vía EFI → producto + sshd.
|
||||
# (B) BIOS — el MISMO ISO con SeaBIOS (El Torito i386-pc) → producto + sshd.
|
||||
# La ESP FAT la puebla el mtools construido por hammer (recipes/mtools.toml); el grubx64.efi lo arma
|
||||
# La ESP FAT la puebla el mtools construido por takana (recipes/mtools.toml); el grubx64.efi lo arma
|
||||
# grub-mkimage -O x86_64-efi. El kernel ya trae EFI_STUB (sin rebuild).
|
||||
#
|
||||
# Uso: PRODUCT=<hash-o-prefijo> ./scripts/efi-boot-test.sh
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
# Arma la imagen con install-image-efi.sh y la arranca por UEFI (OVMF pflash, SIN -kernel: la firmware
|
||||
# lanza \EFI\BOOT\BOOTX64.EFI). Asevera la cadena soberana completa por marcadores serie deterministas:
|
||||
# 1. EFI-stub cargó el kernel+initrd horneados (sin GRUB ni systemd-boot)
|
||||
# 2. el initramfs de pivote resolvió hammer-root por LABEL y montó las ext4
|
||||
# 2. el initramfs de pivote resolvió takana-root por LABEL y montó las ext4
|
||||
# 3. switch_root: arje-zero re-montó / (re-mounted) = tomó PID1
|
||||
#
|
||||
# Nota: el cmdline horneado termina en console=tty0 ⇒ el stdout de userspace va a la pantalla. Los
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
# efi-install-test.sh — valida el lazo COMPLETO de instalación EFI desde el medio live en OVMF (ADR 0010
|
||||
# paso 5, hermano UEFI de scripts/iso-install-test.sh que es BIOS):
|
||||
# (1) arma un ISO instalador EFI (iso-image.sh EFI=1 INSTALLER=1 AUTO_INSTALL=/dev/vda) — GRUB-EFI carga
|
||||
# el live, cuyo /init desatendido corre hammer-install; el payload trae el kernel metal EFI-stub;
|
||||
# (2) lo bootea EN OVMF con un disco EN BLANCO ⇒ el live corre bajo UEFI ⇒ hammer-install detecta
|
||||
# el live, cuyo /init desatendido corre takana-install; el payload trae el kernel metal EFI-stub;
|
||||
# (2) lo bootea EN OVMF con un disco EN BLANCO ⇒ el live corre bajo UEFI ⇒ takana-install detecta
|
||||
# /sys/firmware/efi y toma la RAMA EFI (MBR+ESP-0xEF, EFI-stub soberano), instala y apaga;
|
||||
# (3) bootea ESE disco SOLO en OVMF (sin ISO, sin -kernel) ⇒ la firmware lanza \EFI\BOOT\BOOTX64.EFI →
|
||||
# initramfs de pivote → arje-zero. Cierra "ISO live UEFI → disco EFI instalado → bootea solo".
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/sh
|
||||
# build-timed.sh <receta.toml> — corre `hammer build` MIDIENDO la duración de pared y la registra
|
||||
# build-timed.sh <receta.toml> — corre `takana build` MIDIENDO la duración de pared y la registra
|
||||
# como metadata, para alimentar el camino crítico PESADO de yupana (el peso que le faltaba a las
|
||||
# ondas: sin él, critical-path = nº de pasos; con él, ETA real en segundos).
|
||||
#
|
||||
@@ -12,12 +12,12 @@
|
||||
#
|
||||
# SÓLO BUILDS REALES. Un cache-hit devuelve al instante; un build C/KDE tarda minutos. Se registra
|
||||
# sólo si la pared supera UMBRAL_TIEMPO (def 3s), así los cache-hits no envenenan la mediana con ~0.
|
||||
# Limitación honesta: `hammer build` arrastra deps ⇒ la pared incluye deps NO selladas. Construyendo
|
||||
# Limitación honesta: `takana build` arrastra deps ⇒ la pared incluye deps NO selladas. Construyendo
|
||||
# en orden topológico (drenar) las deps ya están selladas (cache-hit) y la pared mide sobre todo ESTA
|
||||
# receta. No es perfecto; es una cota superior buena para pesar. La precisión exacta pediría instrumentar
|
||||
# el sellado dentro de hammer-build (Rust), deuda futura.
|
||||
# el sellado dentro de takana-build (Rust), deuda futura.
|
||||
#
|
||||
# Sale con el MISMO código que `hammer build` (transparente para el llamador).
|
||||
# Sale con el MISMO código que `takana build` (transparente para el llamador).
|
||||
set -u
|
||||
HAMMER="${TAKANA:-${HAMMER:-./target/release/takana}}"
|
||||
STORE="${STORE:-./store}"
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
# campana-deuda.sh — muele en el WORKER una lista EXPLÍCITA de recetas en deuda, medida en el HUB.
|
||||
#
|
||||
# POR QUÉ NO `saldar-deuda-static.sh` DIRECTO EN EL WORKER (2026-07-21): ese script calcula la deuda
|
||||
# en vivo con `hammer hash --check` contra el store LOCAL. En el worker el store es PARCIAL (el del
|
||||
# en vivo con `takana hash --check` contra el store LOCAL. En el worker el store es PARCIAL (el del
|
||||
# volumen: ~290 artefactos vs 720 del hub) ⇒ mide 716 recetas de deuda en vez de 37 y se pone a
|
||||
# reconstruir media distro. Es la regla de [[frente-harkaq-jaula]] con otra cara: **el worker MIDE,
|
||||
# el hub CLASIFICA**. Acá el hub decide la lista (`DRY=1 saldar-deuda-static.sh`) y el worker sólo
|
||||
# ejecuta. La lista se pasa por argumentos o por DEUDA=, no se recalcula.
|
||||
#
|
||||
# ORDEN: las RAÍCES del stack GUI primero (glib unblocks=14, luego harfbuzz/cairo/pango → gtk4 →
|
||||
# libadwaita). `hammer build` arrastra deps recursivamente, así que el orden no es obligatorio —
|
||||
# libadwaita). `takana build` arrastra deps recursivamente, así que el orden no es obligatorio —
|
||||
# pero empezar por la raíz hace que la cascada caiga cuanto antes y que un corte temprano deje el
|
||||
# máximo destrabado. El stack GUI es lo que el laptop NO puede construir (zig-skew, ver
|
||||
# [[etapa-g-gui-chain-boundary]]) ⇒ es exactamente lo que justifica la granja.
|
||||
@@ -133,11 +133,11 @@ for r in $DEUDA; do
|
||||
echo "$(ts) sin intentar: $(echo "$DEUDA" | tr ' ' '\n' | tail -n +"$i" | tr '\n' ' ')" | tee -a "$LOG"
|
||||
exit 3
|
||||
fi
|
||||
# heartbeat: el dead-man ya cuenta `hammer build` como trabajo, pero entre receta y receta hay
|
||||
# heartbeat: el dead-man ya cuenta `takana build` como trabajo, pero entre receta y receta hay
|
||||
# huecos (fetch/vendor) donde no hay proceso con ese nombre. Tocarlo evita una muerte espuria.
|
||||
touch /run/hammer-heartbeat 2>/dev/null || true
|
||||
# build-timed.sh mide la pared y registra la duración en $STORE/.times (sólo builds reales), para
|
||||
# el camino crítico pesado de yupana. Transparente: sale con el mismo código que `hammer build`.
|
||||
# el camino crítico pesado de yupana. Transparente: sale con el mismo código que `takana build`.
|
||||
if HAMMER="$HAMMER" STORE="$STORE" "$HAMMER_DIR/scripts/farm/build-timed.sh" "$f" >>"$LOG" 2>&1; then
|
||||
ultimo_rc=0; ok=$((ok + 1)); echo "$(ts) [$i/$total] ✓ $r" | tee -a "$LOG"
|
||||
else
|
||||
|
||||
@@ -102,7 +102,7 @@ else
|
||||
# b) Un PNG NUEVO de una receta tampoco viaja. Y hay recetas cuyo `[source] dir` tiene PNG
|
||||
# dentro: los 9 iconos de `recipes/atuq/branding/icons/`.
|
||||
#
|
||||
# Como hammer SÍ hashea el árbol de un `[source] dir` (`recipe.rs`, `dir:<hash>`), el efecto no es
|
||||
# Como takana SÍ hashea el árbol de un `[source] dir` (`recipe.rs`, `dir:<hash>`), el efecto no es
|
||||
# silencioso —bendito sea—: el worker calcula OTRO ArtifactHash. Medido el mismo día,
|
||||
# `atuq` daba `b3:f2960991` en el hub y `b3:b33e81a5` en el worker por ESE único fichero de más.
|
||||
# O sea que el worker no podía coincidir con el hub ni construyendo bien.
|
||||
@@ -162,7 +162,7 @@ fi
|
||||
# tiene un token que funciona, y este latido corre cada 30 min aunque no haya sesión (setsid) ⇒ el
|
||||
# hub es la AUTORIDAD de vida del worker. Es defensa en profundidad: el dead-man del worker cubre
|
||||
# "hub caído"; esto cubre "dead-man del worker roto". Un worker sin trabajo activo REAPER_MAX ciclos
|
||||
# seguidos se BORRA desde acá, con el MISMO blindaje que el dead-man (label role=hammer-worker;
|
||||
# seguidos se BORRA desde acá, con el MISMO blindaje que el dead-man (label role=takana-worker;
|
||||
# gioser no tiene label ⇒ jamás pasa, ni por accidente).
|
||||
REAPER_MAX="${REAPER_MAX:-2}" # 2 ciclos × 30 min ≈ 1 h idle, igual que el dead-man del worker
|
||||
if [ -s "$FLEET" ] && command -v hcloud >/dev/null 2>&1; then
|
||||
@@ -205,7 +205,7 @@ if [ -s "$FLEET" ] && command -v hcloud >/dev/null 2>&1; then
|
||||
echo "==> reaper: $name SIN trabajo activo — strike $strikes/$REAPER_MAX"
|
||||
if [ "$strikes" -lt "$REAPER_MAX" ]; then sobreviven="${sobreviven}${name} ${ip}
|
||||
"; continue; fi
|
||||
# BLINDAJE (igual que el dead-man): sólo se borra un server con label role=hammer-worker.
|
||||
# BLINDAJE (igual que el dead-man): sólo se borra un server con label role=takana-worker.
|
||||
if hcloud server describe "$name" -o format='{{.Labels}}' 2>/dev/null | grep -q hammer-worker; then
|
||||
echo " ☠ IDLE $strikes ciclos ⇒ el HUB borra $name (label verificado; gioser protegido)"
|
||||
if hcloud server delete "$name" >/dev/null 2>&1; then rm -f "$strike_f"
|
||||
@@ -244,7 +244,7 @@ if [ -x "$ROOT/scripts/poda-fuentes.sh" ]; then
|
||||
"$ROOT/scripts/poda-fuentes.sh" --aplicar || echo " ⚠ poda de fuentes falló (rc=$?) — sigo"
|
||||
fi
|
||||
|
||||
# 3. Regenerar el grafo de estado desde el store ya cosechado (usa el hammer del laptop, que tiene
|
||||
# 3. Regenerar el grafo de estado desde el store ya cosechado (usa el takana del laptop, que tiene
|
||||
# el subcomando `hash`). Barato (~14s cada uno). El corpus canónico y el frente KDE, por separado.
|
||||
echo "==> regenerando grafo de estado"
|
||||
if [ -x "$HAMMER" ]; then
|
||||
@@ -289,7 +289,7 @@ if [ -x "$HAMMER" ]; then
|
||||
echo "$drenaje_out" | grep '⚠' | sed 's/^/ /'
|
||||
fi
|
||||
# VIGÍA DE FUENTES (ADR 0013 §3). Desde que el mirror propio se consulta ANTES que upstream,
|
||||
# `hammer build` deja de avisar cuando una URL de terceros muere: sirve los bytes del mirror y
|
||||
# `takana build` deja de avisar cuando una URL de terceros muere: sirve los bytes del mirror y
|
||||
# sigue. Eso es lo correcto para construir y sería CIEGO sin este contrapeso — las URLs se irían
|
||||
# muriendo una a una y el corpus resultaría irreconstruible el día que falte el mirror, con todo
|
||||
# en verde hasta ese momento. Igual que el grafo de wlr, que pasó 17 días anunciando un 121/121
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
# Antes de morir sólo queda una cosa honesta: sync(1) + umount, para que el ext4 del volumen quede
|
||||
# consistente. No es "guardar" (ya está guardado), es cerrar la puerta al salir.
|
||||
#
|
||||
# QUÉ CUENTA COMO TRABAJO: un `hammer build` en vuelo, el loop del worker, o un heartbeat fresco
|
||||
# QUÉ CUENTA COMO TRABAJO: un `takana build` en vuelo, el loop del worker, o un heartbeat fresco
|
||||
# (`/run/hammer-heartbeat`, que un job largo puede tocar). Si nada de eso aparece durante
|
||||
# IDLE_MAX_TICKS ticks seguidos, se borra.
|
||||
#
|
||||
@@ -101,15 +101,15 @@ if [ "${1:-}" = "--verificar" ]; then
|
||||
fi
|
||||
|
||||
hay_trabajo() {
|
||||
# `hammer build` cubre TODO el build (fetch/vendor/compile) porque el proceso vive toda la
|
||||
# `takana build` cubre TODO el build (fetch/vendor/compile) porque el proceso vive toda la
|
||||
# invocación. `campana-deuda` = campaña deliberada. NO se cuenta `farm-worker-loop`: ese loop
|
||||
# está SIEMPRE vivo (idle-loopea con la cola vacía) ⇒ contarlo como trabajo hacía que el worker
|
||||
# NUNCA acumulara ticks y NUNCA se matara — un worker con cola seca quedaba idle para siempre
|
||||
# (incidente 2026-07-23, 2ª vez). El loop construyendo YA se ve por su `hammer build` hijo.
|
||||
# (incidente 2026-07-23, 2ª vez). El loop construyendo YA se ve por su `takana build` hijo.
|
||||
pgrep -f 'release/hammer.* build ' >/dev/null 2>&1 && { echo "hammer build en vuelo"; return 0; }
|
||||
pgrep -f 'campana-deuda' >/dev/null 2>&1 && { echo "campaña deliberada en vuelo"; return 0; }
|
||||
# UNA TRANSFERENCIA TAMBIÉN ES TRABAJO (2026-08-09). Poblar el volumen con el store del hub son
|
||||
# horas de rsync durante las cuales no corre ningún `hammer build` ⇒ el worker acumulaba ticks y
|
||||
# horas de rsync durante las cuales no corre ningún `takana build` ⇒ el worker acumulaba ticks y
|
||||
# se borraba A SÍ MISMO a mitad de la copia. El volumen sobrevive, pero la transferencia muere y
|
||||
# hay que reanudarla a mano; con un enlace lento eso puede no converger nunca.
|
||||
# `pgrep -x` (nombre EXACTO del proceso), no `pgrep -f`: `-f` mira la línea de órdenes completa y
|
||||
@@ -145,7 +145,7 @@ self="$(hostname)"
|
||||
# una sola no basta cuando el fallo es irreversible:
|
||||
#
|
||||
# 1. LISTA NEGRA por nombre — explícita, legible, imposible de malinterpretar.
|
||||
# 2. LABEL role=hammer-worker — el switch sólo se borra si ÉL MISMO es un worker de la granja.
|
||||
# 2. LABEL role=takana-worker — el switch sólo se borra si ÉL MISMO es un worker de la granja.
|
||||
# gioser no tiene labels (map[]), así que jamás pasa este filtro ni por accidente.
|
||||
#
|
||||
# La capa 2 es la fuerte: no depende de acordarse de añadir nombres a una lista. Un server que no
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
# llave del box y se mantiene el invariante del modelo hub-and-spoke: compute puro y descartable.
|
||||
#
|
||||
# ── EVIDENCIA, NO FE ────────────────────────────────────────────────────────────────────────────
|
||||
# El paso final NO es "extraje la imagen": es comparar el `hammer hash` de una receta testigo entre
|
||||
# El paso final NO es "extraje la imagen": es comparar el `takana hash` de una receta testigo entre
|
||||
# worker y hub. Si no coinciden, este script FALLA y el worker no debe construir — un worker que
|
||||
# sella en otra dirección quema dinero produciendo artefactos que nadie va a encontrar.
|
||||
#
|
||||
@@ -53,7 +53,7 @@ $SSH "root@$IP" "set -e
|
||||
log "3/4 recompilando hammer en el worker (el binario debe traer el lab en hash_inputs)"
|
||||
# `touch` ANTES de compilar, y no es paranoia: `rsync -a` PRESERVA EL MTIME DEL HUB, así que un
|
||||
# fuente recién sincronizado puede quedar más VIEJO que el binario que el worker compiló hace un
|
||||
# rato ⇒ cargo dice «Finished in 0.09s» y no recompila nada. El worker se queda con el hammer
|
||||
# rato ⇒ cargo dice «Finished in 0.09s» y no recompila nada. El worker se queda con el takana
|
||||
# anterior, calcula la huella vieja y el paso 4 falla sin que se vea por qué (2026-08-12).
|
||||
$SSH "root@$IP" "cd $REMOTE && . \$HOME/.cargo/env 2>/dev/null
|
||||
find crates -name '*.rs' -exec touch {} +
|
||||
|
||||
@@ -84,7 +84,7 @@ LEDGER="work/store-gc-superados.txt"
|
||||
# CAS y `.dmerge` hardlinkea los artefactos: en el worker, 25 G medidos con `du` —que deduplica
|
||||
# por inodo— son MUCHOS más en el cable, porque cada enlace se transfiere y se escribe como una
|
||||
# copia entera. El tamaño que informa `du` del origen NO es el que hace falta en el destino.
|
||||
# 2. **`.dmerge` es CACHÉ PURA y no tiene por qué viajar.** Es la capa de merge que hammer
|
||||
# 2. **`.dmerge` es CACHÉ PURA y no tiene por qué viajar.** Es la capa de merge que takana
|
||||
# reconstruye sola; copiarla no aporta un solo artefacto y es la parte más pesada del árbol
|
||||
# (en el hub llegó a 132 G con 111,7 GiB exclusivos). Cosechar es traerse ARTEFACTOS.
|
||||
#
|
||||
@@ -100,7 +100,7 @@ else
|
||||
fi
|
||||
|
||||
# ── GUARDIÁN: ningún artefacto cosechado puede llegar VACÍO ────────────────────────────────────
|
||||
# Un directorio sin ficheros no es un artefacto, es un nombre — y `hammer build` lo lee como
|
||||
# Un directorio sin ficheros no es un artefacto, es un nombre — y `takana build` lo lee como
|
||||
# presencia y sella sin construir. Barrerlos acá es barato; detectarlos tres eslabones más abajo
|
||||
# (manifiesto → grafo → build) cuesta una campaña.
|
||||
VACIOS=0
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
# farm-up.sh [N] — levanta N workers EFÍMEROS desde el snapshot golden y los alinea con la cola
|
||||
# actual del laptop. Modelo hub-and-spoke: el worker NO tiene secretos (ni firma ni gitea); es
|
||||
# compute puro y descartable. La imagen 'hammer-golden' trae el toolchain + el store sellado
|
||||
# compute puro y descartable. La imagen 'takana-golden' trae el toolchain + el store sellado
|
||||
# horneados ⇒ el worker arranca con TODO el catálogo cacheado (cache-hit instantáneo), sólo
|
||||
# construye lo NUEVO que el hub le empuje.
|
||||
#
|
||||
@@ -12,7 +12,7 @@
|
||||
# La flota viva se registra en scripts/farm/.fleet ("<name> <ip>" por línea; gitignored).
|
||||
# Idempotente al escalar: re-correr con más N agrega workers sin pisar los vivos.
|
||||
#
|
||||
# Env: IMAGE (snapshot, def 417847948=hammer-golden-rust-go-2026-08-08), TYPE (def ccx23),
|
||||
# Env: IMAGE (snapshot, def 417847948=takana-golden-rust-go-2026-08-08), TYPE (def ccx23),
|
||||
# LOCATION
|
||||
# (def hel1), SSHKEY (llave del proyecto Hetzner, def desarrollo@jlsoltech.com), SSH_KEY
|
||||
# (privada local p/ SSH, def ~/.ssh/github5), REMOTE (def /opt/hammer).
|
||||
@@ -20,7 +20,7 @@ set -euo pipefail
|
||||
|
||||
N="${1:-1}"
|
||||
# ── GOLDEN 417847948 (2026-08-08): la anterior NO TENÍA RUST NI GO ─────────────────────────────
|
||||
# `hammer` invoca `cargo vendor` (y el toolchain Go) en el HOST, no dentro del sandbox: el vendoreo
|
||||
# `takana` invoca `cargo vendor` (y el toolchain Go) en el HOST, no dentro del sandbox: el vendoreo
|
||||
# pasa ANTES de entrar a la caja. La golden vieja (408909310) no los traía, así que la granja no
|
||||
# podía construir NINGUNA receta Rust ni Go — o sea COSMIC entero, las 228 Rust y las 362 Go del
|
||||
# corpus: **el 76% del catálogo sólo se podía construir en el hub**, una sola máquina.
|
||||
@@ -84,7 +84,7 @@ sin_volumen() {
|
||||
fi
|
||||
# El server acaba de nacer y todavía no tiene nada dentro. Dejarlo vivo sería un idle facturando
|
||||
# (es el caso hworker-4, 5 días), así que se borra — con el MISMO blindaje que el dead-man y
|
||||
# farm-down: sólo si lleva el label role=hammer-worker. gioser no lo lleva ⇒ jamás cae por acá.
|
||||
# farm-down: sólo si lleva el label role=takana-worker. gioser no lo lleva ⇒ jamás cae por acá.
|
||||
if hcloud server describe "$name" -o format='{{.Labels}}' 2>/dev/null | grep -q hammer-worker; then
|
||||
if hcloud server delete "$name" >/dev/null 2>&1; then
|
||||
echo " $name borrado (recién creado, vacío): sin factura colgando." >&2
|
||||
@@ -205,13 +205,13 @@ for k in $(seq 1 "$N"); do
|
||||
fi
|
||||
# BIND-MOUNT, NO SYMLINK (2026-07-22). Anclar el store con \`ln -s\` rompía TODA receta con
|
||||
# \`zig_version\` en el worker, y el síntoma se leía como deuda de la cascada GUI. Por qué:
|
||||
# hammer deriva el lab del PADRE DEL STORE (BuildConfig::defaults_for_store ⇒
|
||||
# takana deriva el lab del PADRE DEL STORE (BuildConfig::defaults_for_store ⇒
|
||||
# project_root = store_root.parent(), de ahí .dev-fs/{alpine,tools/zig,cache}). Con el symlink
|
||||
# el store resuelve a /mnt/cosecha/store ⇒ busca /mnt/cosecha/.dev-fs, que no existe, y muere
|
||||
# con 'no encuentro el ejecutable zig en …' apuntando al lugar equivocado. Un bind-mount deja
|
||||
# los datos en el volumen igual, pero \$REMOTE/store sigue siendo una ruta REAL bajo
|
||||
# \$REMOTE ⇒ el padre vuelve a ser /opt/hammer y .dev-fs se encuentra. Mantiene las dos
|
||||
# invariantes a la vez: sellar = persistir, y el lab donde hammer lo espera.
|
||||
# invariantes a la vez: sellar = persistir, y el lab donde takana lo espera.
|
||||
if [ -L $REMOTE/store ]; then rm -f $REMOTE/store; fi
|
||||
mkdir -p $REMOTE/store
|
||||
mountpoint -q $REMOTE/store || mount --bind /mnt/cosecha/store $REMOTE/store
|
||||
|
||||
@@ -44,7 +44,7 @@ SUELO_FRIO_MIN="${SUELO_FRIO_MIN:-120}"
|
||||
sleep 180
|
||||
# Protección 1: árboles que un bwrap activo bind-monta (fase compile dentro del sandbox).
|
||||
active=$(pgrep -af bwrap 2>/dev/null | grep -oE 'work/sources/[^ ]+' | sort -u)
|
||||
# Protección 2: árboles cuya receta tiene un `hammer build` EN VUELO. Durante el fetch/`go mod
|
||||
# Protección 2: árboles cuya receta tiene un `takana build` EN VUELO. Durante el fetch/`go mod
|
||||
# vendor`/resolve_phases (host-side, ANTES de que arranque el bwrap) el árbol no está bind-montado
|
||||
# ⇒ la protección 1 no lo cubre. Una tanda Go con árbol de deps enorme (dnscontrol/lazysql) tarda
|
||||
# minutos vendoreando; sin esto, el watchdog le borra el go.mod a mitad → "no build system".
|
||||
@@ -85,7 +85,7 @@ SUELO_FRIO_MIN="${SUELO_FRIO_MIN:-120}"
|
||||
# mientras un vendor está activo —o en la VENTANA justo antes de que arranque (otra receta de la
|
||||
# cola en serie)— le arranca el cache bajo los pies (`.partial`/`go-build/...: no such file`) y
|
||||
# ESE vendor falla. La purga de work/sources de arriba ya liberó lo grueso (segura: saltea las
|
||||
# protegidas). Por eso NO purgo las caches Go si hay CUALQUIER `hammer build` en vuelo (no solo
|
||||
# protegidas). Por eso NO purgo las caches Go si hay CUALQUIER `takana build` en vuelo (no solo
|
||||
# un `go mod vendor` visible en este instante): difiero al próximo tick / al gap idle entre
|
||||
# ciclos. El sandbox `go install` usa GOCACHE=/src + -mod=vendor ⇒ no toca estas caches del host.
|
||||
if pgrep -f 'go mod vendor' >/dev/null 2>&1 || pgrep -f 'release/hammer.* build ' >/dev/null 2>&1; then
|
||||
@@ -156,13 +156,13 @@ SUELO_FRIO_MIN="${SUELO_FRIO_MIN:-120}"
|
||||
# con el MISMO ArtifactHash, o sea la misma dirección del store. Sin divergencias, sin colisión de
|
||||
# nombre y sin un solo artefacto que podar al retirarla. El andamio ya no sostenía nada: el trabajo
|
||||
# de la isla dinámica vive en `incoming-gnome`, que es la cola que el perfil usa (119/119).
|
||||
# ⇒ La comprobación que decide NO es `diff` de las recetas sino `hammer hash`: la resolución de deps
|
||||
# ⇒ La comprobación que decide NO es `diff` de las recetas sino `takana hash`: la resolución de deps
|
||||
# es hermano→padre, así que dos ficheros idénticos en colas distintas PUEDEN sellar distinto.
|
||||
# Orden: las colas con deuda van ANTES de las cerradas (kde 163/163, wlr 129/129 salen por cache-hit).
|
||||
QUEUES="${QUEUES:-recipes/incoming recipes/incoming-go recipes/incoming-gnome recipes/incoming-cosmic recipes/incoming-kde recipes/incoming-wlr}"
|
||||
|
||||
# REBUILD DEL BINARIO AL ARRANCAR (2026-07-18): el hammer horneado en la imagen golden ENVEJECE.
|
||||
# Caso real que costó una noche de KDE: la golden del 29-jun traía un hammer sin el fix del overlay
|
||||
# REBUILD DEL BINARIO AL ARRANCAR (2026-07-18): el takana horneado en la imagen golden ENVEJECE.
|
||||
# Caso real que costó una noche de KDE: la golden del 29-jun traía un takana sin el fix del overlay
|
||||
# lowerdir (commit e69eaaa, 12-jul) ⇒ toda receta de muchas deps (kio=52, kwin, plasma-*) desbordaba
|
||||
# el límite de 4KB del mount options y el sandbox NI ARRANCABA — 43 recetas atascadas jurando "en
|
||||
# cola". El source SÍ llega fresco (farm-sync rsync-ea el repo), pero nadie recompilaba. Reconstruir
|
||||
@@ -177,10 +177,10 @@ fi
|
||||
# UNA vez, y este loop vive días: `uptime` de 8 días con el binario congelado en el arranque. El
|
||||
# source SÍ sigue llegando fresco cada 30 min (la siembra rsync-ea el repo), así que el worker
|
||||
# termina con FUENTE NUEVO y BINARIO VIEJO — que es peor que el fósil de la golden, porque nada lo
|
||||
# delata: el ArtifactHash NO se mueve por la versión de hammer, así que el build se comporta
|
||||
# delata: el ArtifactHash NO se mueve por la versión de takana, así que el build se comporta
|
||||
# distinto y `build-state.json` sigue en verde.
|
||||
#
|
||||
# Caso real que lo motiva: el 2026-09-06 el worker corría un hammer de las 04:54 y el arreglo que
|
||||
# Caso real que lo motiva: el 2026-09-06 el worker corría un takana de las 04:54 y el arreglo que
|
||||
# materializa los submódulos git había entrado a las 15:11 (`114aeba`). `waterfox` moría en 4 s por
|
||||
# `waterfox/browser/locales/moz.build` inexistente y el diagnóstico apuntaba a la receta, a los
|
||||
# parches y al `--filter=blob:none` — a todo menos al binario. `strings` sobre los dos binarios lo
|
||||
|
||||
@@ -59,7 +59,7 @@ while :; do
|
||||
[ -f "recipes/$n.toml" ] || continue
|
||||
if [ -s "$OUT/$n.verdicts" ]; then hechas=$((hechas + 1)); continue; fi
|
||||
|
||||
# Forzar build real: `hammer build` pega en la caché si el artefacto ya está sellado y no
|
||||
# Forzar build real: `takana build` pega en la caché si el artefacto ya está sellado y no
|
||||
# correría nada (veredicto vacío que NO es un fallo). La copia va AL LADO del original o se
|
||||
# rompe la resolución de deps.build, que son relativas al dir de la receta.
|
||||
tmp="recipes/.hk-$n.toml"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
# Ciclo (idempotente, seguro de re-correr):
|
||||
# 1. git pull --rebase (sincroniza con el otro agente que pushea al mismo main)
|
||||
# 2. baja el store sellado del worker (rsync)
|
||||
# 3. por cada receta en recipes/incoming-go/: ¿está sellada? (hammer build con `timeout` ⇒ cache-hit
|
||||
# 3. por cada receta en recipes/incoming-go/: ¿está sellada? (takana build con `timeout` ⇒ cache-hit
|
||||
# instantáneo = sí; si se pasa del timeout sería un build local ⇒ se SALTA, no compilamos en el hub)
|
||||
# 4. SMOKE-TEST del binario instalado: existe + ELF estático + corre version/--help sin panic/segfault.
|
||||
# - PASA ⇒ promueve (mv recipes/, pack --build) — se firma+commitea al final.
|
||||
@@ -91,7 +91,7 @@ for f in "$QUEUE"/*.toml; do
|
||||
rn=$(sed -n 's/^name *= *"\(.*\)".*/\1/p' "$f" | head -1); [ -n "$rn" ] || rn="$n"
|
||||
|
||||
# Pre-check BARATO: ¿el worker selló algo para este nombre? (tras el rsync el store local lo tiene).
|
||||
# Sin esto, `hammer build` de una receta NO sellada ARRANCARÍA un build local (vendor+compile) y lo
|
||||
# Sin esto, `takana build` de una receta NO sellada ARRANCARÍA un build local (vendor+compile) y lo
|
||||
# mataría el timeout ⇒ CPU del hub desperdiciada cada cosecha. Si no hay seal `*-<rn>`, salto ya.
|
||||
ls "$STORE"/*-"$rn" >/dev/null 2>&1 || { skipped="$skipped $n"; continue; }
|
||||
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
# respaldo-promover.sh — mueve lo que el worker depositó en el BUZÓN al respaldo consolidado.
|
||||
#
|
||||
# ── POR QUÉ HAY UN BUZÓN Y NO ESCRITURA DIRECTA ────────────────────────────────────────────────
|
||||
# El worker es EFÍMERO y se borra solo; darle escritura sobre `hammer/store` sería darle permiso de
|
||||
# El worker es EFÍMERO y se borra solo; darle escritura sobre `takana/store` sería darle permiso de
|
||||
# borrado sobre el único respaldo que existe. Y los permisos de una subcuenta del Storage Box son
|
||||
# BINARIOS: `--readonly` sí o no. No hay append-only, no hay write-sin-delete (verificado en la API
|
||||
# el 2026-08-09, no de memoria).
|
||||
#
|
||||
# Así que la contención no se hace con un permiso sino con el ALCANCE: la subcuenta de escritura
|
||||
# tiene su home en `incoming/` y `hammer/store` sencillamente NO EXISTE para ella. Lo máximo que
|
||||
# tiene su home en `incoming/` y `takana/store` sencillamente NO EXISTE para ella. Lo máximo que
|
||||
# puede destruir es lo que ella misma depositó y todavía no se promovió — que sigue estando en el
|
||||
# volumen de la granja. Un permiso puede estar mal puesto; un directorio que no está en tu home no.
|
||||
#
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
# protegerse no es un respaldo. La subcuenta acota el daño posible a cero por construcción:
|
||||
# --readonly el sistema de ficheros se monta de sólo lectura del lado del box
|
||||
# --reachable-externally=false sólo desde la red de Hetzner: la credencial no sirve fuera
|
||||
# --home-directory hammer no ve el resto del box
|
||||
# --home-directory takana no ve el resto del box
|
||||
#
|
||||
# ── LA VERIFICACIÓN ES NEGATIVA, Y ESO NO ES OPCIONAL ──────────────────────────────────────────
|
||||
# «Sólo lectura» es una afirmación sobre lo que el sistema **impide**, así que no se comprueba
|
||||
@@ -24,21 +24,21 @@
|
||||
#
|
||||
# ── LA LLAVE VIVE EN EL WORKER Y MUERE CON ÉL ──────────────────────────────────────────────────
|
||||
# Se genera en el worker (nunca viaja una privada desde el laptop) y su pública se autoriza en
|
||||
# `hammer/.ssh/authorized_keys` del box, escrita CON LA CUENTA PRINCIPAL — la subcuenta no puede
|
||||
# `takana/.ssh/authorized_keys` del box, escrita CON LA CUENTA PRINCIPAL — la subcuenta no puede
|
||||
# escribir ni su propio authorized_keys, que es justo lo que se quería.
|
||||
# El home es `hammer` y no `hammer/store` a propósito: el authorized_keys tiene que estar en el home,
|
||||
# El home es `takana` y no `takana/store` a propósito: el authorized_keys tiene que estar en el home,
|
||||
# y no queremos un `.ssh` dentro del store, que es CAS y se cuenta por entradas.
|
||||
#
|
||||
# ⚠ La shell del Storage Box es RESTRINGIDA: acepta `ls`/`mkdir`/`rm` pero **no redirección**.
|
||||
# `echo k > authorized_keys` falla EN SILENCIO (crea el directorio y no el fichero). Va con `scp`.
|
||||
#
|
||||
# ── DOS SUBCUENTAS, DOS VERBOS (2026-08-09) ────────────────────────────────────────────────────
|
||||
# `sub1` (home `hammer`, --readonly) es para MIRAR qué hay ya respaldado y no volver a subirlo.
|
||||
# `sub1` (home `takana`, --readonly) es para MIRAR qué hay ya respaldado y no volver a subirlo.
|
||||
# `sub2` (home `incoming`, escritura) es el BUZÓN donde el worker DEPOSITA lo nuevo.
|
||||
#
|
||||
# No hay una sola subcuenta que escriba sin poder borrar: los permisos de la API son binarios
|
||||
# (`--readonly` sí o no; verificado, no recordado). La contención se hace por ALCANCE — el buzón no
|
||||
# tiene a `hammer/store` dentro de su home, así que el respaldo consolidado no existe para él. Lo que
|
||||
# tiene a `takana/store` dentro de su home, así que el respaldo consolidado no existe para él. Lo que
|
||||
# el worker puede destruir se reduce a lo que él mismo depositó y aún no se promovió, que sigue
|
||||
# estando en el volumen. Promoción: `scripts/farm/respaldo-promover.sh` (un `mv`, cero bytes de red).
|
||||
#
|
||||
@@ -78,7 +78,7 @@ CRED="$HOME/.config/hammer/storagebox-worker.env"
|
||||
# worker», que apunta al worker cuando el problema estaba en el known_hosts del laptop. Un mensaje
|
||||
# que culpa al sitio equivocado cuesta más que no tener mensaje.
|
||||
# Purgar la entrada vieja es correcto ACÁ y sólo acá: la identidad del worker no es su llave, es el
|
||||
# label `role=hammer-worker` del proyecto hcloud; el secreto no viaja por este canal.
|
||||
# label `role=takana-worker` del proyecto hcloud; el secreto no viaja por este canal.
|
||||
ssh_worker() { # $1 = ip, resto = orden remota
|
||||
ip="$1"; shift
|
||||
ssh-keygen -R "$ip" >/dev/null 2>&1
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
# saldar-deuda-static.sh — reconstruye las recetas `link = "static"` cuyo hash VIGENTE no está
|
||||
# sellado en el store (la "deuda de rebuild" que destapó `hammer hash`, 2026-07-17).
|
||||
# sellado en el store (la "deuda de rebuild" que destapó `takana hash`, 2026-07-17).
|
||||
#
|
||||
# POR QUÉ EXISTE. El trabajo reciente (matar-gcc migró 27 recetas C a zig, el frente static, harkaq
|
||||
# declarando deps) cambió muchas recetas base sin re-sellarlas. Cada cambio re-hashea la receta Y
|
||||
@@ -8,7 +8,7 @@
|
||||
# a la receta de hoy. `scripts/static-audit.sh` las cuenta como "sin artefacto" (deuda real, ya no
|
||||
# enmascarada). Este script las salda.
|
||||
#
|
||||
# NO HACE FALTA ORDEN TOPOLÓGICO: `hammer build` arrastra sus deps de build recursivamente
|
||||
# NO HACE FALTA ORDEN TOPOLÓGICO: `takana build` arrastra sus deps de build recursivamente
|
||||
# (resolve_build_dep_hashes), así que construir `curl` construye `openssl` y `perl` primero. Se
|
||||
# puede correr en cualquier orden; las cache-hit son instantáneas.
|
||||
#
|
||||
@@ -17,7 +17,7 @@
|
||||
# `farm-down`. Corre igual en el laptop, pero el stack GUI (cairo/pango/gtk…) fallará por zig-skew:
|
||||
# usá `SKIP_GUI=1` para saltarlo (default en laptop).
|
||||
#
|
||||
# LA DEUDA SE CALCULA EN VIVO, no de una lista que envejece: `hammer hash --check` por receta. Eso
|
||||
# LA DEUDA SE CALCULA EN VIVO, no de una lista que envejece: `takana hash --check` por receta. Eso
|
||||
# es exactamente lo que el audit usa para no auditar sellados rancios.
|
||||
#
|
||||
# Uso: scripts/farm/saldar-deuda-static.sh # construye toda la deuda
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
# tandas.sh — encadena varias campañas de deuda en el worker, en ORDEN y sin solaparse.
|
||||
#
|
||||
# POR QUÉ EN SERIE Y NO EN PARALELO: dos `hammer build` simultáneos sobre el mismo store compiten
|
||||
# POR QUÉ EN SERIE Y NO EN PARALELO: dos `takana build` simultáneos sobre el mismo store compiten
|
||||
# por el mismo `work/sources` y por el watchdog de disco del worker-loop (que borra árboles que
|
||||
# ningún bwrap tiene montado). El paralelismo del worker está DENTRO de cada build (`-j$(nproc)`),
|
||||
# no entre builds.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
# vps-setup.sh — provisiona un WORKER de granja hammer en un VPS o LXC fresco.
|
||||
# vps-setup.sh — provisiona un WORKER de granja takana en un VPS o LXC fresco.
|
||||
#
|
||||
# DOS FAMILIAS SOPORTADAS, detectadas por gestor de paquetes:
|
||||
# · apt → Debian/Ubuntu: los workers efímeros de Hetzner (`farm-up.sh`, imagen golden).
|
||||
@@ -64,11 +64,11 @@ else
|
||||
#
|
||||
# ⚠ `patch` VA EXPLÍCITO Y NO ES OBVIO (2026-08-29). En Debian entra de regalo dentro de
|
||||
# `build-essential`; al desglosar a gcc/g++/make sueltos se cayó sin que nadie lo notara. El
|
||||
# campo `patches = [...]` de una receta lo aplica hammer DEL LADO DEL HOST, fuera del sandbox
|
||||
# campo `patches = [...]` de una receta lo aplica takana DEL LADO DEL HOST, fuera del sandbox
|
||||
# ⇒ no alcanza con que el rootfs lo traiga (lo trae: `/usr/bin/patch` está en el alpine). Sin
|
||||
# él, `cairo-shared` muere con `spawn patch: No such file or directory` y detrás caen `pango`
|
||||
# y `gtk4`, o sea el stack GUI entero de GNOME por un binario de 129 KB.
|
||||
# Regla: toda herramienta que hammer invoque HOST-SIDE va en esta lista, no en `[deps]` de la
|
||||
# Regla: toda herramienta que takana invoque HOST-SIDE va en esta lista, no en `[deps]` de la
|
||||
# receta — declararla en la receta re-hashearía cairo y todo GNOME debajo para nada.
|
||||
$DNF install -y -q git gcc gcc-c++ make patch curl pkgconf-pkg-config openssl-devel \
|
||||
bubblewrap rsync ca-certificates shadow-utils xz tar findutils
|
||||
|
||||
@@ -8,9 +8,9 @@
|
||||
# El laptop manda ÓRDENES (la promoción es un `mv`, cero bytes); los DATOS van por acá.
|
||||
#
|
||||
# ── LAS DOS CREDENCIALES, Y POR QUÉ HACEN FALTA LAS DOS ────────────────────────────────────────
|
||||
# `sub1` (sólo lectura, home `hammer`) sirve para PREGUNTAR qué hay ya respaldado. Sin eso el worker
|
||||
# `sub1` (sólo lectura, home `takana`) sirve para PREGUNTAR qué hay ya respaldado. Sin eso el worker
|
||||
# resubiría el corpus entero en cada ciclo.
|
||||
# `sub2` (escritura, home `incoming`) es el BUZÓN donde deposita. No alcanza a `hammer/store`: el
|
||||
# `sub2` (escritura, home `incoming`) es el BUZÓN donde deposita. No alcanza a `takana/store`: el
|
||||
# respaldo consolidado no está dentro de su home. Ver `respaldo-subcuenta.sh`.
|
||||
#
|
||||
# ── UN SOLO rsync CON --files-from, NO UNO POR ARTEFACTO ───────────────────────────────────────
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#!/bin/sh
|
||||
# fuentes-vigia.sh — comprueba que las URLs de las recetas SIGUEN VIVAS (ADR 0013 §3).
|
||||
#
|
||||
# ── POR QUÉ ESTO EXISTE APARTE Y NO DENTRO DE `hammer build` ───────────────────────────────────
|
||||
# Desde el ADR 0013 el mirror propio se consulta ANTES que upstream, y `hammer build` no avisa
|
||||
# ── POR QUÉ ESTO EXISTE APARTE Y NO DENTRO DE `takana build` ───────────────────────────────────
|
||||
# Desde el ADR 0013 el mirror propio se consulta ANTES que upstream, y `takana build` no avisa
|
||||
# cuando lo usa. Eso es correcto para construir y PELIGROSO sin contrapeso: un mirror que sirve
|
||||
# calladamente lo que upstream ya perdió convierte un fallo ruidoso en silencio, y las URLs se van
|
||||
# muriendo una por una sin que nadie se entere. El día que el mirror se pierda, el corpus resulta
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
# . scripts/fuentes/mirror-env.sh
|
||||
# flock work/.farm-build.lock ./target/release/takana --store ./store build <receta>
|
||||
#
|
||||
# Sin esto `hammer build` va derecho a upstream, que es el comportamiento de siempre: el mirror es
|
||||
# Sin esto `takana build` va derecho a upstream, que es el comportamiento de siempre: el mirror es
|
||||
# ADITIVO y su ausencia nunca rompe un build. Por eso no está horneado en el binario — un default
|
||||
# que apunta a una máquina concreta convertiría un fallo de red en un fallo de hammer para cualquiera
|
||||
# que apunta a una máquina concreta convertiría un fallo de red en un fallo de takana para cualquiera
|
||||
# que clone el repo.
|
||||
#
|
||||
# LISTA, no una URL (ADR 0014): las dos variables aceptan varios orígenes separados por comas y se
|
||||
|
||||
@@ -72,7 +72,7 @@ def bundle(e, tmpdir):
|
||||
# El campo `commit` de una receta NO siempre es un commit: puede pinear un objeto TAG anotado.
|
||||
# Un SHA de tag es una identidad igual de buena (`git archive` lo acepta) y por eso hash_inputs
|
||||
# no lo distingue, pero `refs/heads/*` sólo apunta a commits. Se pela para la rama y, si hubo
|
||||
# que pelar, el objeto tag viaja aparte: sin él, el `cat-file -e <commit>` del lado de hammer
|
||||
# que pelar, el objeto tag viaja aparte: sin él, el `cat-file -e <commit>` del lado de takana
|
||||
# falla y el mirror queda correcto-pero-inútil justo para estas recetas.
|
||||
r = git("rev-parse", e["commit"] + "^{commit}")
|
||||
if r.returncode != 0:
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
# mirror-git-poblar.sh — espeja las fuentes GIT del corpus como bundles (ADR 0013).
|
||||
#
|
||||
# ── POR QUÉ BUNDLES SHALLOW Y NO CLONES COMPLETOS ──────────────────────────────────────────────
|
||||
# Hammer no usa la historia: lo único que hace con un repo es `git archive <commit> | tar -x`, o sea
|
||||
# Takana no usa la historia: lo único que hace con un repo es `git archive <commit> | tar -x`, o sea
|
||||
# materializar UN árbol. Un clon `--depth 1` del commit exacto basta, y para `act` son 8,9 MB en
|
||||
# lugar del repositorio entero. Con 606 fuentes git, la diferencia decide si el mirror cabe.
|
||||
#
|
||||
# El objeto se nombra por CONTENIDO igual que los tarballs: `hammer/fuentes-git/{commit}.bundle`.
|
||||
# El objeto se nombra por CONTENIDO igual que los tarballs: `takana/fuentes-git/{commit}.bundle`.
|
||||
# El commit ES su verificación — git comprueba cada objeto contra su SHA al desempaquetar, así que
|
||||
# un bundle alterado no pasa. No hace falta índice ni sha256 aparte.
|
||||
#
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
# mirror-poblar.sh — sube la caché local de tarballs al mirror de fuentes (ADR 0013).
|
||||
#
|
||||
# El mirror está direccionado por CONTENIDO: `hammer/fuentes/{sha256}.tar`, exactamente el mismo
|
||||
# El mirror está direccionado por CONTENIDO: `takana/fuentes/{sha256}.tar`, exactamente el mismo
|
||||
# nombre que usa `work/tarballs/`. Por eso subir es un rsync plano y no hace falta índice ninguno:
|
||||
# el nombre del fichero ES su verificación.
|
||||
#
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
# hydrate-gnome.sh — proyecta al FHS el cierre de runtime de una receta GNOME, desde artefactos
|
||||
# SELLADOS del store, sin rebuild ni reproduce (`hammer hydrate <hash> --into`).
|
||||
# SELLADOS del store, sin rebuild ni reproduce (`takana hydrate <hash> --into`).
|
||||
#
|
||||
# DIFERENCIA CON scripts/kde/hydrate-from-store.sh, y por qué importa: aquél resuelve el cierre desde
|
||||
# un `index.json` de repo y elige el artefacto por MTIME con un CUTOFF ("el más nuevo anterior a hoy").
|
||||
# Eso es una heurística: si dos artefactos del mismo paquete conviven en el store, la fecha no dice
|
||||
# cuál corresponde a la receta VIGENTE. Acá el cierre sale del GRAFO REAL de recetas (`[deps].build`,
|
||||
# resolución hermano→padre, la misma que usa hammer) y el artefacto se elige por `hammer hash`, que
|
||||
# resolución hermano→padre, la misma que usa takana) y el artefacto se elige por `takana hash`, que
|
||||
# calcula el ArtifactHash de la receta de HOY sin construir. O sea: cero ambigüedad, y si falta algo
|
||||
# el reporte dice exactamente qué receta y con qué hash lo buscaba.
|
||||
#
|
||||
|
||||
@@ -93,7 +93,7 @@ echo "==> inyectando arje-logind-compat (el login1 del fractal)"
|
||||
# LEYENDO /run/systemd/{sessions,users,seats}. Pero alguien tiene que ESCRIBIR esos ficheros, y ese
|
||||
# alguien es arje-logind-compat, el daemon D-Bus que se hace pasar por org.freedesktop.login1.
|
||||
# libelogind entra solo (es dep de mutter); el daemon no, porque nadie lo declara como dep de build.
|
||||
# El artefacto se elige por `hammer hash` sobre la receta —el que corresponde a la receta de HOY—, no
|
||||
# El artefacto se elige por `takana hash` sobre la receta —el que corresponde a la receta de HOY—, no
|
||||
# por el más reciente del store: con dos artefactos del mismo paquete conviviendo, la fecha no dice
|
||||
# cuál es el vigente. ALC_BIN permite inyectar un binario RECIÉN COMPILADO en vez del artefacto sellado. Es para probar
|
||||
# un cambio en arje-compat antes de commitear/pushear tawasuyu y re-sellar la receta (la receta pina
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
# hammer-install.sh — Etapa E2: instalador a disco. Vuelca el `product-rootfs` (lean: 4/4 + userland
|
||||
# takana-install.sh — Etapa E2: instalador a disco. Vuelca el `product-rootfs` (lean: 4/4 + userland
|
||||
# Rust + sshd) sobre un DISCO destino (block device real `/dev/sdX`, o un fichero pre-dimensionado para
|
||||
# pruebas) con el mismo layout GPT + GRUB BIOS de la imagen E1, pero IN-PLACE sobre el target. Tras
|
||||
# instalar, el disco arranca solo (SeaBIOS → GRUB → kernel → arje-zero PID1 → hammerd+getty+sshd).
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/sh
|
||||
# hammer-install — instalador a disco DESDE el medio live (Etapa E5 / refinamiento #1).
|
||||
# takana-install — instalador a disco DESDE el medio live (Etapa E5 / refinamiento #1).
|
||||
#
|
||||
# Se inyecta en el rootfs del producto como /usr/bin/hammer-install (ver scripts/iso-image.sh
|
||||
# INSTALLER=1). Corre DENTRO del live, como root real (arje-zero es PID1) ⇒ NO necesita unshare -r
|
||||
@@ -19,11 +19,11 @@
|
||||
# (hd0,msdos1)/boot/grub/grub.cfg → linux /boot/bzImage root=/dev/<dev>1 → wrapper /sbin/init.
|
||||
#
|
||||
# Modo de uso:
|
||||
# INTERACTIVO (sin argumentos, con TTY): hammer-install
|
||||
# INTERACTIVO (sin argumentos, con TTY): takana-install
|
||||
# TUI en busybox sh: detecta discos, el usuario elige destino + confirma el borrado, recoge
|
||||
# hostname / credencial de root / zona horaria / teclado / tamaños de partición, y recién ahí
|
||||
# particiona+instala. Es la cara humana del instalador (el /init del ISO installer lo lanza).
|
||||
# DESATENDIDO (con device): hammer-install /dev/vdb [ROOT_MB] [STORE_MB]
|
||||
# DESATENDIDO (con device): takana-install /dev/vdb [ROOT_MB] [STORE_MB]
|
||||
# Autoinstalador (lo usa iso-install-test.sh con AUTO_INSTALL). La config sale de env:
|
||||
# TAKANA_HOSTNAME / TAKANA_ROOT_PW / TAKANA_ROOT_AUTHKEYS / TAKANA_TZ / TAKANA_KEYMAP.
|
||||
# Los nombres viejos HAMMER_* se siguen aceptando (ADR 0016); gana el nuevo si están los dos.
|
||||
@@ -331,7 +331,7 @@ FDISK
|
||||
"$BB" mke2fs -F -L hammer-store "$P3" >/dev/null 2>&1
|
||||
"$BB" mke2fs -F -L hammer-state "$P4" >/dev/null 2>&1
|
||||
|
||||
# initramfs de pivote (busybox estático del live + /init: findfs LABEL=hammer-root → switch_root).
|
||||
# initramfs de pivote (busybox estático del live + /init: findfs LABEL=takana-root → switch_root).
|
||||
echo "==> armando el initramfs de pivote (busybox + /init switch_root a hammer-root)"
|
||||
IRD_TREE=/run/hammer-efi-ird
|
||||
"$BB" rm -rf "$IRD_TREE"
|
||||
@@ -397,8 +397,8 @@ INIT
|
||||
"$BB" mkdir -p "$MNT/usr/sbin"
|
||||
cp "$PAYLOAD/hammer-recover" "$MNT/usr/sbin/hammer-recover"; "$BB" chmod 0755 "$MNT/usr/sbin/hammer-recover"
|
||||
fi
|
||||
# CLI hammer (static musl) para el menú de arranque por grafo (ADR 0010): lo copia al disco desde el
|
||||
# payload; el wrapper lo invoca como `hammer boot menu`.
|
||||
# CLI takana (static musl) para el menú de arranque por grafo (ADR 0010): lo copia al disco desde el
|
||||
# payload; el wrapper lo invoca como `takana boot menu`.
|
||||
if [ -x "$PAYLOAD/hammer" ]; then
|
||||
cp "$PAYLOAD/hammer" "$MNT/usr/bin/hammer"; "$BB" chmod 0755 "$MNT/usr/bin/hammer"
|
||||
fi
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
# IRREDUCIBLE —nadie la provee— cuenta contra el <5%. Sin esa distinción la métrica mide
|
||||
# ansiedad, no viabilidad.
|
||||
#
|
||||
# `hammer build` pega en la caché si el artefacto ya está sellado (no correría nada y el
|
||||
# `takana build` pega en la caché si el artefacto ya está sellado (no correría nada y el
|
||||
# veredicto saldría vacío sin que sea un fallo). Se fuerza un build real copiando la receta con
|
||||
# otro `name` — cambia hash_inputs sin tocar el store real ni la receta del repo.
|
||||
set -eu
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
# es un artefacto sellado del store (`store/<hash>-<name>/usr/...`). La clausura es, exactamente,
|
||||
# el conjunto de ficheros que esas deps aportan — ni uno más.
|
||||
#
|
||||
# El sandbox de hammer apila cada dep con `--overlay-src` sobre el rootfs Alpine, así que el
|
||||
# El sandbox de takana apila cada dep con `--overlay-src` sobre el rootfs Alpine, así que el
|
||||
# fichero `store/<hash>-zlib/usr/include/zlib.h` aparece dentro como `/usr/include/zlib.h`. La
|
||||
# traducción es sólo quitar el prefijo del store: el resto del path YA es el path del sandbox.
|
||||
#
|
||||
|
||||
@@ -106,7 +106,7 @@ def main():
|
||||
#
|
||||
# Este bug costó caro: `/bin/busybox` está concedido (el sandbox corre `sh -c` y /bin/sh→
|
||||
# busybox), pero como `recipes/busybox.toml` existe, el clasificador propuso "declarar dep:
|
||||
# busybox" y la cosecha lo declaró en 29 recetas. Declararlo apila el busybox de hammer sobre
|
||||
# busybox" y la cosecha lo declaró en 29 recetas. Declararlo apila el busybox de takana sobre
|
||||
# el de Alpine y ROMPE el build (binutils: "cannot run C compiled programs"). Además va contra
|
||||
# la Etapa C, que reemplaza busybox por uutils. Medir bien y accionar mal.
|
||||
concedidos = set()
|
||||
@@ -142,7 +142,7 @@ def main():
|
||||
# El store es autoritativo: sabe qué ficheros aporta cada artefacto.
|
||||
declarable[p] = sorted({nombre_de(a) for a in provs})
|
||||
elif os.path.basename(p) in recetas:
|
||||
# Respaldo por catálogo: el artefacto no está construido AQUÍ, pero hammer sabe
|
||||
# Respaldo por catálogo: el artefacto no está construido AQUÍ, pero takana sabe
|
||||
# construirlo. Es declarable igual — la deuda es de la receta, no del disco.
|
||||
#
|
||||
# OJO, heurística por NOMBRE y por eso sólo un respaldo: acierta con `make`, pero
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
# harkaq-trace-build.sh — orquestador del PILOTO de traza positiva (plan-freebsd T1.1+T1.3).
|
||||
#
|
||||
# Envuelve UN `hammer build` con harkaq-trace enganchado al overlay de CADA fase: un bwrap
|
||||
# Envuelve UN `takana build` con harkaq-trace enganchado al overlay de CADA fase: un bwrap
|
||||
# por fase ⇒ una traza por fase — la misma granularidad que el veredicto de harkaq. El
|
||||
# tracer marca `/proc/<pid-bwrap>/root`: el magic-link cae en el SB del OVERLAY, que es el
|
||||
# único lugar desde donde los reads del build se ven (la marca sobre el fs del host NO los
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
# función que usan `build-state.py` y `vigia-imagen.py`. Si el perfil cambia, esto cambia solo.
|
||||
#
|
||||
# ══ EL ARTEFACTO SE ELIGE POR HASH, NO POR FECHA ═══════════════════════════════════════════════
|
||||
# `hammer hash <receta>` da el ArtifactHash de la receta de HOY sin construir. Con dos artefactos
|
||||
# `takana hash <receta>` da el ArtifactHash de la receta de HOY sin construir. Con dos artefactos
|
||||
# del mismo paquete conviviendo en el store —lo normal tras cualquier re-hash— el más reciente
|
||||
# puede ser el viejo. La fecha miente; el hash no.
|
||||
#
|
||||
|
||||
@@ -11,20 +11,20 @@
|
||||
# LoadOptions VACÍO ⇒ el kernel usa ese cmdline horneado ⇒ el EFI-stub carga \initramfs.cpio.gz desde la
|
||||
# MISMA ESP y corre /init. (En un boot con bootloader que sí pasa LoadOptions, el horneado se ignora.)
|
||||
#
|
||||
# El initramfs es MÍNIMO — sólo pivota: findfs LABEL=hammer-root → switch_root a la ext4 real. Ese
|
||||
# El initramfs es MÍNIMO — sólo pivota: findfs LABEL=takana-root → switch_root a la ext4 real. Ese
|
||||
# "initrd chico" es lo que destraba EFI-stub directo (ADR 0010 paso 2: el cuelgue del metal era por un
|
||||
# initrd de 100MB). El sistema real vive en las particiones ext4, no en RAM.
|
||||
#
|
||||
# Layout (GPT, UEFI):
|
||||
# p1 ESP FAT32 [EFI System Partition] \EFI\BOOT\BOOTX64.EFI (=bzImage) + \initramfs.cpio.gz
|
||||
# p2 / ext4 [hammer-root] el rootfs real del producto (/sbin/init → arje-zero)
|
||||
# p3 /store ext4 [hammer-store]
|
||||
# p4 /var/lib/hammer ext4 [hammer-state]
|
||||
# p2 / ext4 [takana-root] el rootfs real del producto (/sbin/init → arje-zero)
|
||||
# p3 /store ext4 [takana-store]
|
||||
# p4 /var/lib/hammer ext4 [takana-state]
|
||||
#
|
||||
# Cadena de arranque del disco: firmware UEFI → \EFI\BOOT\BOOTX64.EFI (EFI-stub) → initramfs pivote
|
||||
# (findfs LABEL=hammer-root → switch_root) → /sbin/init real → arje-zero PID1.
|
||||
# (findfs LABEL=takana-root → switch_root) → /sbin/init real → arje-zero PID1.
|
||||
#
|
||||
# La ESP se puebla con el mtools de hammer (mformat/mcopy, sin root ni loop). Las ext4 con `mke2fs -d`
|
||||
# La ESP se puebla con el mtools de takana (mformat/mcopy, sin root ni loop). Las ext4 con `mke2fs -d`
|
||||
# bajo `unshare -r` (ficheros root-owned sin sudo), igual que install-image.sh.
|
||||
#
|
||||
# Uso:
|
||||
@@ -35,7 +35,7 @@
|
||||
# ROOTFS dir rootfs a instalar (default el product-rootfs más reciente del store)
|
||||
# KERNEL bzImage EFI-stub a embeber (default store/*-linux-metal/boot/bzImage más reciente)
|
||||
# BUSYBOX busybox ESTÁTICO para el initramfs (default el del ROOTFS)
|
||||
# MTOOLS dir usr/bin de mtools de hammer (default store/*-mtools/usr/bin)
|
||||
# MTOOLS dir usr/bin de mtools de takana (default store/*-mtools/usr/bin)
|
||||
# IMG imagen de salida (default work/hammer-efi-disk.img)
|
||||
# ESP_MB MiB de la ESP FAT32 (default 64)
|
||||
# ROOT_SIZE MiB de / (default 2048)
|
||||
@@ -72,7 +72,7 @@ IRD_TREE="$STAGE/initramfs"
|
||||
ROOT_IMG="$STAGE/root.img"; STORE_IMG="$STAGE/store.img"; STATE_IMG="$STAGE/state.img"
|
||||
ESP_IMG="$STAGE/esp.img"; IRD="$STAGE/initramfs.cpio.gz"
|
||||
|
||||
# --- 1) initramfs de pivote: busybox estático + /init (findfs LABEL=hammer-root → switch_root) --------
|
||||
# --- 1) initramfs de pivote: busybox estático + /init (findfs LABEL=takana-root → switch_root) --------
|
||||
echo "==> initramfs de pivote (busybox estático + /init switch_root a hammer-root)"
|
||||
mkdir -p "$IRD_TREE/bin" "$IRD_TREE/dev" "$IRD_TREE/proc" "$IRD_TREE/sys" "$IRD_TREE/newroot"
|
||||
cp "$BUSYBOX" "$IRD_TREE/bin/busybox"; chmod 0755 "$IRD_TREE/bin/busybox"
|
||||
@@ -141,8 +141,8 @@ echo "==> staging root (hardlinks del rootfs, sin /store ni /var/lib/hammer)"
|
||||
cp -al "$ROOTFS" "$ROOT_TREE"
|
||||
rm -rf "$ROOT_TREE/store" "$ROOT_TREE/var/lib/hammer"
|
||||
mkdir -p "$ROOT_TREE/store" "$ROOT_TREE/var/lib/hammer"
|
||||
# CLI `hammer` (static musl) para el menú de arranque por grafo (ADR 0010): el hook de init lo invoca
|
||||
# como `hammer boot menu`. El producto trae arje-zero/hammerd pero NO el CLI ⇒ lo inyectamos acá si
|
||||
# CLI `takana` (static musl) para el menú de arranque por grafo (ADR 0010): el hook de init lo invoca
|
||||
# como `takana boot menu`. El producto trae arje-zero/hammerd pero NO el CLI ⇒ lo inyectamos acá si
|
||||
# está compilado (igual patrón que hammer-recover). Opcional: sin él, el wrapper saltea el menú.
|
||||
HAMMER_MUSL="${TAKANA_MUSL:-${HAMMER_MUSL:-$ROOT/target/x86_64-unknown-linux-musl/release/hammer}}"
|
||||
if [ -x "$HAMMER_MUSL" ]; then
|
||||
@@ -178,7 +178,7 @@ exec /usr/bin/arje-zero
|
||||
RINIT
|
||||
chmod +x "$ROOT_TREE/sbin/init"
|
||||
|
||||
# --- 3) ESP FAT32: BOOTX64.EFI (=bzImage EFI-stub) + initramfs.cpio.gz, poblada con mtools de hammer ---
|
||||
# --- 3) ESP FAT32: BOOTX64.EFI (=bzImage EFI-stub) + initramfs.cpio.gz, poblada con mtools de takana ---
|
||||
echo "==> ESP FAT32 (${ESP_MB}M): \\EFI\\BOOT\\BOOTX64.EFI (=bzImage) + \\initramfs.cpio.gz"
|
||||
rm -f "$ESP_IMG"; truncate -s "$(( ESP_MB * 1024 * 1024 ))" "$ESP_IMG"
|
||||
"$MTOOLS/mformat" -F -v HAMMER -i "$ESP_IMG" ::
|
||||
@@ -187,7 +187,7 @@ rm -f "$ESP_IMG"; truncate -s "$(( ESP_MB * 1024 * 1024 ))" "$ESP_IMG"
|
||||
"$MTOOLS/mcopy" -i "$ESP_IMG" "$IRD" ::/initramfs.cpio.gz
|
||||
"$MTOOLS/mdir" -i "$ESP_IMG" ::/EFI/BOOT 2>/dev/null | grep -iE 'BOOTX64|initram' || true
|
||||
|
||||
# --- 4) GPT: p1=ESP, p2=hammer-root, p3=hammer-store, p4=hammer-state --------------------------------
|
||||
# --- 4) GPT: p1=ESP, p2=takana-root, p3=takana-store, p4=takana-state --------------------------------
|
||||
SECT_MIB=2048
|
||||
ESP_SECT=$(( ESP_MB * SECT_MIB ))
|
||||
ROOT_SECT=$(( ROOT_SIZE * SECT_MIB ))
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
#
|
||||
# Layout (GPT, BIOS):
|
||||
# /dev/vda1 BIOS boot (ef02, ~2 MiB, sin fs) core.img de GRUB
|
||||
# /dev/vda2 / ext4 [hammer-root] + /boot/bzImage + /boot/grub
|
||||
# /dev/vda3 /store ext4 [hammer-store]
|
||||
# /dev/vda4 /var/lib/hammer ext4 [hammer-state]
|
||||
# /dev/vda2 / ext4 [takana-root] + /boot/bzImage + /boot/grub
|
||||
# /dev/vda3 /store ext4 [takana-store]
|
||||
# /dev/vda4 /var/lib/hammer ext4 [takana-state]
|
||||
#
|
||||
# Instalación de GRUB en USERSPACE (sin root ni loop): grub-mkimage arma core.img; grub-bios-setup
|
||||
# escribe boot.img→MBR + core.img→partición BIOS sobre el fichero imagen (I/O de bloques). Los módulos
|
||||
@@ -20,7 +20,7 @@
|
||||
#
|
||||
# Target IN-PLACE (Etapa E2): si IMG es un block device (o PREALLOC=1 sobre un fichero pre-dimensionado),
|
||||
# se instala EN SITIO — no se trunca ni se borra el nodo, sólo se valida capacidad. Esa es la base de
|
||||
# `hammer install /dev/sdX` (ver scripts/hammer-install.sh): misma lógica, apuntando a un disco real.
|
||||
# `takana install /dev/sdX` (ver scripts/hammer-install.sh): misma lógica, apuntando a un disco real.
|
||||
#
|
||||
# Variables:
|
||||
# ROOTFS dir rootfs a empaquetar (default work/builder-rootfs)
|
||||
@@ -109,7 +109,7 @@ START4=$(( START3 + STORE_SECT ))
|
||||
TOTAL_SECT=$(( START4 + STATE_SECT + 2048 ))
|
||||
BIOS_GUID="21686148-6449-6E6F-744E-656564454649" # GPT "BIOS boot partition"
|
||||
|
||||
# Target = block device (instalación in-place, Etapa E2 `hammer install /dev/sdX`) o fichero imagen.
|
||||
# Target = block device (instalación in-place, Etapa E2 `takana install /dev/sdX`) o fichero imagen.
|
||||
# Un device tiene tamaño fijo: NO se trunca ni se borra el nodo; se valida capacidad. PREALLOC=1 fuerza
|
||||
# este modo sobre un fichero pre-dimensionado (para probar el camino de device sin hardware real).
|
||||
TOTAL_BYTES=$(( TOTAL_SECT * 512 ))
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# iso-boot-test.sh — valida E5 (medio live): arma el ISO 9660 / El Torito con scripts/iso-image.sh y lo
|
||||
# arranca en QEMU desde el CD (sin -kernel ni disco), comprobando que la cadena El Torito → GRUB →
|
||||
# kernel → initramfs → /init → producto funciona ENTERA desde RAM. El xorriso que arma el ISO es el
|
||||
# construido por hammer (recipes/xorriso.toml), así que el test dogfooda también esa receta.
|
||||
# construido por takana (recipes/xorriso.toml), así que el test dogfooda también esa receta.
|
||||
#
|
||||
# Asserts: (1) GRUB arrancó del El Torito; (2) el marcador del /init live; (3) arje-zero PID1 vivo;
|
||||
# (4) sshd escuchando — i.e. el PRODUCTO llega a servicio corriendo enteramente desde el medio live.
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
# A diferencia de install-image.sh (E1, disco GPT con / persistente en vda2), un medio live arranca
|
||||
# ENTERO desde RAM: GRUB (El Torito) carga kernel + initramfs desde el ISO 9660, el kernel desempaqueta
|
||||
# el initramfs (el rootfs del producto) a un tmpfs y corre /init — sin tocar disco. Es el medio para
|
||||
# correr el instalador (`hammer install /dev/sdX`) en hardware nuevo, o para probar el sistema sin
|
||||
# correr el instalador (`takana install /dev/sdX`) en hardware nuevo, o para probar el sistema sin
|
||||
# instalarlo.
|
||||
#
|
||||
# El ISO lo ARMA `xorriso` construido por hammer desde fuente (recipes/xorriso.toml) — el host no lo
|
||||
# El ISO lo ARMA `xorriso` construido por takana desde fuente (recipes/xorriso.toml) — el host no lo
|
||||
# trae. La cadena de boot BIOS reusa los módulos GRUB i386-pc de E1, pero con el core El Torito
|
||||
# (formato i386-pc-eltorito + iso9660) en vez del core de disco (ext2 + biosdisk).
|
||||
#
|
||||
@@ -37,10 +37,10 @@ KERNEL="${KERNEL:-$(ls store/*-linux/boot/bzImage 2>/dev/null | head -1)}"
|
||||
GRUB_LIB="${GRUB_LIB:-/usr/lib/grub/i386-pc}"
|
||||
GRUB_EFI_LIB="${GRUB_EFI_LIB:-/usr/lib/grub/x86_64-efi}"
|
||||
CMDLINE="${CMDLINE:-console=ttyS0 rdinit=/init}"
|
||||
# xorriso: preferimos el construido por hammer (dogfooding de recipes/xorriso.toml); fallback al host.
|
||||
# xorriso: preferimos el construido por takana (dogfooding de recipes/xorriso.toml); fallback al host.
|
||||
XORRISO="${XORRISO:-$(ls store/*-xorriso/usr/bin/xorriso 2>/dev/null | head -1)}"
|
||||
[ -n "$XORRISO" ] && [ -x "$XORRISO" ] || XORRISO="$(command -v xorriso 2>/dev/null || true)"
|
||||
# mtools (mformat/mcopy): para poblar la ESP FAT del arranque EFI. Preferimos el de hammer (dogfooding).
|
||||
# mtools (mformat/mcopy): para poblar la ESP FAT del arranque EFI. Preferimos el de takana (dogfooding).
|
||||
MTOOLS_DIR="${MTOOLS_DIR:-$(ls -d store/*-mtools/usr/bin 2>/dev/null | head -1)}"
|
||||
|
||||
[ -d "$ROOTFS" ] || { echo "no existe ROOTFS: $ROOTFS" >&2; exit 1; }
|
||||
@@ -103,7 +103,7 @@ if [ "${INSTALLER:-0}" = 1 ]; then
|
||||
cp "$KERNEL" "$IPL/kernel/bzImage"; chmod 0644 "$IPL/kernel/bzImage"
|
||||
# Rama EFI del instalador (ADR 0010 paso 5): el kernel `linux-metal` hornea CONFIG_CMDLINE con
|
||||
# initrd=/initramfs.cpio.gz rdinit=/init ⇒ arranca por EFI-stub como \EFI\BOOT\BOOTX64.EFI sin
|
||||
# bootloader. Lo bundleamos junto al genérico; hammer-install lo usa si el live corre en UEFI.
|
||||
# bootloader. Lo bundleamos junto al genérico; takana-install lo usa si el live corre en UEFI.
|
||||
EFI_KERNEL="${EFI_KERNEL:-$(ls -dt store/*-linux-metal/boot/bzImage 2>/dev/null | head -1)}"
|
||||
if [ -n "${EFI_KERNEL:-}" ] && [ -r "$EFI_KERNEL" ]; then
|
||||
cp "$EFI_KERNEL" "$IPL/kernel/bzImage-efi"; chmod 0644 "$IPL/kernel/bzImage-efi"
|
||||
@@ -125,9 +125,9 @@ if [ "${INSTALLER:-0}" = 1 ]; then
|
||||
fi
|
||||
[ -x "$RECOVER_BIN" ] || { echo "no pude construir hammer-recover (target musl?)" >&2; exit 1; }
|
||||
install -m 0755 "$RECOVER_BIN" "$IPL/hammer-recover"
|
||||
# CLI `hammer` (static musl) para el menú de arranque por grafo (ADR 0010): el hook de init del
|
||||
# sistema instalado lo invoca como `hammer boot menu`. El producto trae arje-zero/hammerd pero NO el
|
||||
# CLI ⇒ lo bundleamos al payload; hammer-install lo copia a /usr/bin/hammer del disco.
|
||||
# CLI `takana` (static musl) para el menú de arranque por grafo (ADR 0010): el hook de init del
|
||||
# sistema instalado lo invoca como `takana boot menu`. El producto trae arje-zero/hammerd pero NO el
|
||||
# CLI ⇒ lo bundleamos al payload; takana-install lo copia a /usr/bin/hammer del disco.
|
||||
HAMMER_BIN="$ROOT/target/x86_64-unknown-linux-musl/release/hammer"
|
||||
if [ ! -x "$HAMMER_BIN" ]; then
|
||||
echo "==> compilando hammer CLI (static musl) para el payload"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# iso-install-test.sh — valida el lazo COMPLETO de instalación desde el medio live (refinamiento #1):
|
||||
# (1) arma un ISO instalador (iso-image.sh INSTALLER=1 AUTO_INSTALL=/dev/vda) con el payload de disco
|
||||
# (kernel + GRUB MBR + /usr/bin/hammer-install) bundleado;
|
||||
# (2) lo bootea con un disco EN BLANCO ⇒ el /init desatendido corre hammer-install y apaga;
|
||||
# (2) lo bootea con un disco EN BLANCO ⇒ el /init desatendido corre takana-install y apaga;
|
||||
# (3) bootea ESE disco SOLO (sin ISO, sin -kernel) ⇒ el producto instalado arranca y sirve SSH.
|
||||
# Cierra el lazo "ISO live → disco instalado → bootea solo", lo que hace la distro distribuible.
|
||||
#
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ if [ ! -x "${HARKAQ_BIN:-$HOME/.cache/harkaq}/harkaq-audit" ]; then
|
||||
else
|
||||
export HARKAQ=1 HARKAQ_BIN="${HARKAQ_BIN:-$HOME/.cache/harkaq}"
|
||||
export HARKAQ_BASE="${HARKAQ_BASE:-$HARKAQ_BIN/base.policy}" HARKAQ_TIMEOUT="${HARKAQ_TIMEOUT:-200}"
|
||||
# Nombre ÚNICO por corrida: con uno fijo, la 2ª vez `hammer build` pega en CACHÉ, no
|
||||
# Nombre ÚNICO por corrida: con uno fijo, la 2ª vez `takana build` pega en CACHÉ, no
|
||||
# construye nada, no hay veredictos y el juez dice "sin evidencia" — un falso negativo que
|
||||
# parece un problema de la receta. (Gotcha conocido; me mordió al probar el juez.)
|
||||
uniq="$n-juez-$$"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
# complete-closure.sh — completa el cierre RUNTIME de un FHS hidratado: escanea los `NEEDED` (DT_NEEDED)
|
||||
# de cada ELF del rootfs y, por cada soname AUSENTE, proyecta del store SELLADO el artefacto que lo
|
||||
# provee (`hammer hydrate`). Itera hasta punto fijo. Resuelve las deps runtime que el índice del repo NO
|
||||
# provee (`takana hydrate`). Itera hasta punto fijo. Resuelve las deps runtime que el índice del repo NO
|
||||
# lista (p.ej. libmount.so.1 de util-linux, dep de build de kcoreaddons pero .so runtime de libKF6CoreAddons).
|
||||
#
|
||||
# Sonames del SUSTRATO base (musl libc, loader, libgcc) NO se hidratan: los provee la base soberana del
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
# hydrate-fhs.sh — hidrata un FHS runtime KDE desde el repo firmado, instalando el CIERRE completo.
|
||||
#
|
||||
# `hammer install <pkg> --prefix P` sólo hidrata los archivos de <pkg>, NO de su cierre. Para un
|
||||
# `takana install <pkg> --prefix P` sólo hidrata los archivos de <pkg>, NO de su cierre. Para un
|
||||
# rootfs runtime usable (que capture todas las `.so`), hay que instalar cada paquete del cierre en el
|
||||
# MISMO prefix (igual que product-userland-from-repo.sh). El store del worker está caliente ⇒ cache-hit.
|
||||
#
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# hydrate-from-store.sh — hidrata un FHS runtime KDE PROYECTANDO artefactos SELLADOS del store, SIN
|
||||
# reproduce ni rebuild (`hammer hydrate <hash> --into`). Es la vía correcta para el escritorio completo:
|
||||
# reproduce ni rebuild (`takana hydrate <hash> --into`). Es la vía correcta para el escritorio completo:
|
||||
# `install` reproduce-desde-fuente y el hashing determinista es frágil bajo el régimen dinámico (cada
|
||||
# consumidor puede recomputar un hash distinto de qtbase → rebuild). Aquí se toma la salida ya sellada
|
||||
# por la granja.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# NVIDIA Pascal/nouveau) con render por SOFTWARE (mesa-llvmpipe soberano). USB "live" que se adapta a la
|
||||
# máquina donde se bootee. Ver [[kde-metal-qemu-desktop]] y el runbook docs/runbooks/kde-qemu-desktop.md.
|
||||
#
|
||||
# Por qué software y no iris/nouveau HW: el mesa hammer-built es swrast/llvmpipe; una mesa HW con nouveau
|
||||
# Por qué software y no iris/nouveau HW: el mesa takana-built es swrast/llvmpipe; una mesa HW con nouveau
|
||||
# hoy sólo existe como binario Alpine (rompe soberanía). llvmpipe sobre el KMS del kernel (i915 O nouveau)
|
||||
# funciona idéntico en ambas máquinas y reusa los fixes ya validados. HW-accel = iteración siguiente.
|
||||
#
|
||||
@@ -116,7 +116,7 @@ PY
|
||||
rm -f "$MERGED/etc/motd" # romper el hardlink: `cat >` escribiría el inodo COMPARTIDO con work/metal-rootfs
|
||||
cat > "$MERGED/etc/motd" <<'MOTD'
|
||||
|
||||
#-- hammer :: escritorio KDE Plasma 6 (metal, dual-GPU software) ------------
|
||||
#-- takana :: escritorio KDE Plasma 6 (metal, dual-GPU software) ------------
|
||||
Render por software (llvmpipe soberano) sobre i915 (Intel) o nouveau (NVIDIA).
|
||||
|
||||
Arrancar el escritorio: plasma-start
|
||||
|
||||
@@ -46,7 +46,7 @@ chmod 1777 "$MERGED/tmp"
|
||||
# motd: que el usuario sepa qué correr al llegar al shell.
|
||||
cat > "$MERGED/etc/motd" <<'MOTD'
|
||||
|
||||
#-- hammer :: escritorio KDE Plasma 6 (metal) ------------------------------
|
||||
#-- takana :: escritorio KDE Plasma 6 (metal) ------------------------------
|
||||
Todo construido desde fuente con hammer (musl + zig-cc), sin binarios ajenos.
|
||||
|
||||
Arrancar el escritorio: plasma-start
|
||||
|
||||
@@ -67,7 +67,7 @@ echo "==> inyectando musl (loader + libc) — los binarios KDE son dinámicos"
|
||||
# La base metal es 100% ESTÁTICA (arje-zero+busybox) y el KDE-rootfs no trae libc ⇒ el merge no tiene
|
||||
# loader musl. Los binarios KDE piden interp /lib/ld-musl-x86_64.so.1 + NEEDED libc.so (única lib base
|
||||
# que falta; el resto del closure Qt/KF6 está). Copiamos el musl soberano del host (mismo que corre la
|
||||
# mirada del laptop ⇒ ejecuta binarios hammer-musl dinámicos). En musl el loader ES libc (mismo fichero).
|
||||
# mirada del laptop ⇒ ejecuta binarios takana-musl dinámicos). En musl el loader ES libc (mismo fichero).
|
||||
MUSL="${MUSL:-/usr/lib/musl/lib/libc.so}"
|
||||
[ -r "$MUSL" ] || { echo "no encuentro musl libc ($MUSL) — set MUSL=" >&2; exit 1; }
|
||||
mkdir -p "$MERGED/usr/lib/musl/lib" "$MERGED/lib"
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
# `scripts/vigia-sonames.py` («mesa y wayland pidiendo expat y libffi»), visto desde el lado de la
|
||||
# imagen: el cierre de BUILD no es el cierre de RUNTIME.
|
||||
#
|
||||
# El artefacto se elige por `hammer hash` sobre la receta —el de la receta de HOY—, no por el más
|
||||
# El artefacto se elige por `takana hash` sobre la receta —el de la receta de HOY—, no por el más
|
||||
# reciente del store: con dos artefactos del mismo paquete conviviendo, la fecha no dice cuál rige.
|
||||
# Mismo criterio que `pid1-desde-store.sh` y que los inyectores de arje-*-compat.
|
||||
#
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
# **si algo del rootfs tiene receta, la imagen lo toma del artefacto sellado, no de la copia
|
||||
# congelada.**
|
||||
#
|
||||
# El artefacto se elige por `hammer hash` sobre la receta —el que corresponde a la receta de HOY—, no
|
||||
# El artefacto se elige por `takana hash` sobre la receta —el que corresponde a la receta de HOY—, no
|
||||
# por el más reciente del store: con dos artefactos del mismo paquete conviviendo, la fecha miente.
|
||||
# Mismo criterio que ya usaban los inyectores de arje-logind-compat y arje-polkit-compat.
|
||||
#
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
# licencias-rootfs.sh — mete los textos de licencia en un rootfs y VETA el que no cumpla.
|
||||
#
|
||||
# ── EL PUNTO DE ENTREGA ES LA IMAGEN, NO EL PAQUETE ────────────────────────────────────────────
|
||||
# El canal de paquetes de hammer no distribuye binarios: `pack` produce un `.swm` (receta de
|
||||
# El canal de paquetes de takana no distribuye binarios: `pack` produce un `.swm` (receta de
|
||||
# transformación sobre fuente pública, «NUNCA transporta binarios cocidos») e `install` REPRODUCE
|
||||
# construyendo con cache-hit del corpus. Donde sí se le entrega un binario a alguien es en la
|
||||
# **imagen instalable**, cuyo rootfs se puebla con `hammer install` sobre un prefix. Por eso la
|
||||
# **imagen instalable**, cuyo rootfs se puebla con `takana install` sobre un prefix. Por eso la
|
||||
# obligación de acompañar-el-binario-con-su-licencia se cumple ACÁ. (El SDD 19/20 decía «inyectar en
|
||||
# `hammer pack`»; era una lectura equivocada de por dónde salen los binarios.)
|
||||
# `takana pack`»; era una lectura equivocada de por dónde salen los binarios.)
|
||||
#
|
||||
# ── LO QUE HACE ────────────────────────────────────────────────────────────────────────────────
|
||||
# Para cada paquete del rootfs escribe `/usr/share/licenses/<pkg>/`: el texto canónico de cada
|
||||
|
||||
@@ -7,14 +7,14 @@
|
||||
# informes por imagen); el TEXTO es lo que hay que entregar.
|
||||
#
|
||||
# ── ⚠ DÓNDE SE ENTREGA, QUE NO ES DONDE YO CREÍA ───────────────────────────────────────────────
|
||||
# El SDD 19/20 decía «inyectar el texto en `hammer pack`». **Está mal, y por una razón de fondo**:
|
||||
# El SDD 19/20 decía «inyectar el texto en `takana pack`». **Está mal, y por una razón de fondo**:
|
||||
# `pack` produce un `.swm`, que es una RECETA DE TRANSFORMACIÓN sobre fuente pública y —por diseño
|
||||
# explícito— «NUNCA transporta binarios cocidos»; e `install` REPRODUCE construyendo, con cache-hit
|
||||
# del corpus, en vez de bajar binarios. O sea que el canal de paquetes de hammer **no distribuye
|
||||
# del corpus, en vez de bajar binarios. O sea que el canal de paquetes de takana **no distribuye
|
||||
# binarios**, y la obligación de acompañar-el-binario ahí casi no aplica.
|
||||
#
|
||||
# Donde SÍ aplica, y con toda su fuerza, es en **las IMÁGENES INSTALABLES**: el rootfs se puebla con
|
||||
# `hammer install` sobre un prefix y de ahí sale un USB/ISO lleno de binarios que se le da a alguien.
|
||||
# `takana install` sobre un prefix y de ahí sale un USB/ISO lleno de binarios que se le da a alguien.
|
||||
# Ése es el punto de entrega, y por eso el consumidor de estos textos es el armado del rootfs.
|
||||
# (La otra obligación, el espejo de FUENTES, no la toca esto y sigue pendiente: las recetas apuntan a
|
||||
# URLs de terceros que se caen.)
|
||||
|
||||
@@ -33,10 +33,10 @@
|
||||
#
|
||||
# ── EL ARREGLO ESTRUCTURAL, QUE ES OTRO TICKET ──────────────────────────────────────────────────
|
||||
# Poblar a mano tapa el agujero de hoy pero no impide que vuelva a abrirse: cada receta nueva nace
|
||||
# sin licencia. El cierre de verdad es capturarla donde ya pasa el dato gratis — hammer YA descarga y
|
||||
# sin licencia. El cierre de verdad es capturarla donde ya pasa el dato gratis — takana YA descarga y
|
||||
# extrae cada tarball al construir, así que la fase de fetch puede guardar el COPYING/LICENSE del
|
||||
# árbol junto al artefacto, sin descargas extra. Y el texto de la licencia dentro del paquete conviene
|
||||
# inyectarlo en `hammer pack`, NO en la fase install de la receta: pack es aguas abajo del
|
||||
# inyectarlo en `takana pack`, NO en la fase install de la receta: pack es aguas abajo del
|
||||
# ArtifactHash, así que cumplir la GPL no cuesta reconstruir el corpus. Ver SDD 19.
|
||||
#
|
||||
# Uso: scripts/licencias.sh informe de cobertura (default, no toca nada)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# metal-firmware.sh — inyecta el firmware del WiFi AX201 (y reg.db) en /lib/firmware de un rootfs.
|
||||
#
|
||||
# El firmware de iwlwifi son BLOBS binarios NO compilables desde fuente (Intel no libera el código); la
|
||||
# soberanía de hammer (build-from-source) no aplica acá — son datos fijos como el microcódigo de la CPU.
|
||||
# soberanía de takana (build-from-source) no aplica acá — son datos fijos como el microcódigo de la CPU.
|
||||
# Se fijan por contenido (igual que un tarball pinned): se copian de un linux-firmware ya presente y se
|
||||
# descomprimen a .ucode plano (el kernel metal NO habilita FW_LOADER_COMPRESS, así evitamos esa dep).
|
||||
#
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env sh
|
||||
# metal-usb-sdboot.sh — USB raw (GPT + ESP FAT32) que arranca hammer en metal vía systemd-boot.
|
||||
# metal-usb-sdboot.sh — USB raw (GPT + ESP FAT32) que arranca takana en metal vía systemd-boot.
|
||||
#
|
||||
# Alternativa de BRINGUP al boot por EFI-stub directo (que en el firmware del usuario se cuelga tras
|
||||
# 'Measured initrd PCR 9', por el quirk de LoadOptions del firmware). systemd-boot:
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
# Qué hace:
|
||||
# 1) Rootfs SLIM: loader musl + busybox + .so runtime tomados de .dev-fs/alpine (la musl userland
|
||||
# contra la que se construyeron los binarios), NO el 1.2G entero.
|
||||
# 2) Hydrata los artefactos hammer del stack gráfico + mirada desde el store (por hash).
|
||||
# 2) Hydrata los artefactos takana del stack gráfico + mirada desde el store (por hash).
|
||||
# 3) Cierra el grafo DT_NEEDED: escanea los ELF, y toda .so que falte la trae de .dev-fs/alpine.
|
||||
# 4) Escribe /sbin/init = lanzador (seatd + `mirada-compositor --drm --greeter`).
|
||||
# 5) Empaqueta la imagen USB con el kernel linux-generic vía scripts/metal-usb-sdboot.sh.
|
||||
@@ -137,7 +137,7 @@ else
|
||||
fi
|
||||
|
||||
# 5c) STACK GL/VULKAN por SOFTWARE + nouveau — de Alpine edge (musl, mismo libLLVM que el dev-fs).
|
||||
# Por qué NO la mesa-swrast de hammer: su softpipe basta para el COMPOSITOR, pero el greeter (wgpu)
|
||||
# Por qué NO la mesa-swrast de takana: su softpipe basta para el COMPOSITOR, pero el greeter (wgpu)
|
||||
# necesita un GL/Vulkan más completo. La mesa 26.1.1 de Alpine trae:
|
||||
# · llvmpipe (swrast_dri.so + libgallium + libLLVM.so.22) → software GL robusto (compositor),
|
||||
# · lavapipe (libvulkan_lvp.so + ICD) → software Vulkan (backend primario de wgpu),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
# nix-import.sh <attr> — Etapa G: extrae de nixpkgs la "receta" de <attr> (source+hash+deps+flags,
|
||||
# NO el binario) y la imprime como receta hammer vía `hammer import-nix`. Es la semilla del catálogo:
|
||||
# nixpkgs es el mayor set de recetas desde fuente; importamos la receta y hammer reconstruye.
|
||||
# NO el binario) y la imprime como receta takana vía `takana import-nix`. Es la semilla del catálogo:
|
||||
# nixpkgs es el mayor set de recetas desde fuente; importamos la receta y takana reconstruye.
|
||||
#
|
||||
# Requiere `nix` (con nix-command). Ej: scripts/nix-import.sh hello > recipes/hello.toml
|
||||
# Env: NIXPKGS=<flakeref> (default nixpkgs), HAMMER=<cmd> (default cargo run -q -p takana-cli --)
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
# (`/dev/sdb`, 246 G) estaba al **97%, 8,8 G libres**, con la granja escribiendo ahí.
|
||||
#
|
||||
# Y esos 3,5 G no son fuente: el árbol de `work/sources/<nombre>-<sha>` es a la vez el sitio donde
|
||||
# se extrae Y donde se compila (hammer parchea y vendorea in situ). Lo que engorda es el `target/`
|
||||
# se extrae Y donde se compila (takana parchea y vendorea in situ). Lo que engorda es el `target/`
|
||||
# de cargo, los `.o`, el `vmlinux`. Es RESIDUO DE BUILD, no código.
|
||||
#
|
||||
# ── POR QUÉ BORRARLO NO CUESTA NADA (esto es lo que hace segura la poda) ────────────────────────
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# product-boot-test.sh — valida que el `product-rootfs` ENSAMBLADO POR LA RUTA REAL
|
||||
# (`hammer bootstrap product`) bootea en QEMU y sirve SSH. A diferencia de ssh-e2e-test.sh (spike
|
||||
# (`takana bootstrap product`) bootea en QEMU y sirve SSH. A diferencia de ssh-e2e-test.sh (spike
|
||||
# que arma un rootfs sucio a mano), acá NO se ensambla nada: se toma el artefacto sellado tal cual
|
||||
# (seed de producto, passwd, sshd_config, /var/empty, sshd como card — todo ya inyectado por la capa
|
||||
# de servicios) y sólo se provisiona una authorized_keys de prueba (lo que en deploy haría el admin).
|
||||
@@ -9,7 +9,7 @@
|
||||
# bajo arje sin tocar el núcleo verificado.
|
||||
#
|
||||
# Uso: PRODUCT=<hash-o-prefijo> ./scripts/product-boot-test.sh
|
||||
# (PRODUCT = el que imprimió `hammer bootstrap product`; default: el más reciente del store)
|
||||
# (PRODUCT = el que imprimió `takana bootstrap product`; default: el más reciente del store)
|
||||
# Vars: KERNEL MEM (def 2048) KVM (def 1) PORT (def 2223) DEADLINE (def 150)
|
||||
set -euo pipefail
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"; cd "$ROOT"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# product-image-from-repo.sh — CIERRE DEL LAZO Etapa F+G: arma una imagen de producto AUTO-BOOTEABLE
|
||||
# cuyo USERLAND CLI se hidrata desde el REPO FIRMADO (no del bootstrap hardcodeado). Pipeline:
|
||||
# 1. base = product-rootfs del store (init arje-zero + musl/busybox + servicios).
|
||||
# 2. hidrata el userland rico vía `hammer install --repo --prefix --require-signed` (product-userland-from-repo.sh).
|
||||
# 2. hidrata el userland rico vía `takana install --repo --prefix --require-signed` (product-userland-from-repo.sh).
|
||||
# 3. arma el disco GRUB-booteable (install-image.sh) y, con BOOT=1, lo arranca en QEMU + handshake SSH.
|
||||
#
|
||||
# Uso: ./scripts/product-image-from-repo.sh # crea work/hammer-product-repo.img
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
# product-image.sh — empaqueta el `product-rootfs` (lean: 4/4 + userland Rust + sshd, SIN toolchain)
|
||||
# como imagen de disco AUTO-BOOTEABLE (GRUB BIOS), reusando scripts/install-image.sh. Es el cierre del
|
||||
# pipeline: del artefacto sellado por `hammer bootstrap product` a un disco que arranca solo en QEMU
|
||||
# pipeline: del artefacto sellado por `takana bootstrap product` a un disco que arranca solo en QEMU
|
||||
# (`-drive file=img`, sin -kernel) y, con red, sirve SSH.
|
||||
#
|
||||
# A diferencia de install-image.sh (que por defecto empaqueta el BUILDER rootfs con todo el toolchain),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# product-userland-from-repo.sh — arma el USERLAND del producto HIDRATÁNDOLO desde el REPO FIRMADO
|
||||
# (Etapa F+G, cierre del lazo): por cada paquete corre `hammer install --repo <repo> --prefix <rfs>
|
||||
# (Etapa F+G, cierre del lazo): por cada paquete corre `takana install --repo <repo> --prefix <rfs>
|
||||
# --require-signed`, que verifica la firma del release, reproduce desde el `.swm` (cache-hit si ya
|
||||
# está en el store) e hidrata el FHS bajo el prefix. El resultado es un árbol de userland cuya cadena
|
||||
# de suministro entera viene del repo firmado, no del bootstrap hardcodeado.
|
||||
|
||||
@@ -56,7 +56,7 @@ LOG=$(mktemp -d)
|
||||
trap 'rm -rf "$LOG"' EXIT
|
||||
|
||||
# ── LA SALIDA VA A FICHERO, NO A `$( )` ─────────────────────────────────────────────────────────
|
||||
# MEDIDO acá, y cuesta una hora si no se sabe: `SALIDA=$(timeout … hammer qorpa run …)` **se cuelga
|
||||
# MEDIDO acá, y cuesta una hora si no se sabe: `SALIDA=$(timeout … takana qorpa run …)` **se cuelga
|
||||
# para siempre** aunque `timeout` mate al hijo. La sustitución de comandos no espera al PROCESO,
|
||||
# espera a que se cierre el PIPE, y pressure-vessel deja descendientes (su logger) con el fd
|
||||
# abierto. Con redirección a fichero el mismo comando termina y devuelve su código. Es la misma
|
||||
|
||||
@@ -9,13 +9,13 @@
|
||||
# la misma dirección).
|
||||
#
|
||||
# ── POR QUÉ EN SERIE, Y NO ES NEGOCIABLE ────────────────────────────────────────────────────────
|
||||
# `hammer build` comparte `work/sources/<dep>-<sha>`. Dos builds que compartan una dep se pisan el
|
||||
# `takana build` comparte `work/sources/<dep>-<sha>`. Dos builds que compartan una dep se pisan el
|
||||
# árbol y queda ROTO PARA SIEMPRE (ADR 0012, sin decidir; ~93 de 205 recetas KDE murieron así al
|
||||
# invalidar libdrm). Todo el run va bajo UN `flock work/.farm-build.lock`, el mismo que toman los
|
||||
# scripts de la granja, así que también nos serializa con ellos y con cualquier otro agente.
|
||||
#
|
||||
# ── REANUDABLE POR CONSTRUCCIÓN ─────────────────────────────────────────────────────────────────
|
||||
# No lleva estado propio: la pregunta "¿esto ya está?" se la hace al store con `hammer hash --check`
|
||||
# No lleva estado propio: la pregunta "¿esto ya está?" se la hace al store con `takana hash --check`
|
||||
# (~2 ms, sin construir). Matarlo y relanzarlo continúa donde iba. Un run entero sobre un corpus ya
|
||||
# construido son unos segundos de puros --check.
|
||||
#
|
||||
@@ -52,10 +52,10 @@ SECO=0; [ "${1:-}" = "--seco" ] && SECO=1
|
||||
#
|
||||
# ── 2026-08-27: MIRA DÓNDE SE GASTA, NO DÓNDE ESTÁ EL REPO ──────────────────────────────────────
|
||||
# Medir `$ROOT` dejó de ser correcto al mudar el store al volumen `harkaq-cosecha`. `$ROOT` es
|
||||
# /mnt/vvv, que hammer COMPARTE con tawasuyu, y tawasuyu repuebla su target a ~62 G/h: el 27/08 se
|
||||
# /mnt/vvv, que takana COMPARTE con tawasuyu, y tawasuyu repuebla su target a ~62 G/h: el 27/08 se
|
||||
# comió los 92 G que se le habían liberado en 70 minutos y el vigía abortó la tanda por un disco
|
||||
# que hammer ya ni usaba. Un vigía que mide el disco de OTRO no protege la tanda, la mata.
|
||||
# Ahora se miden los tres sitios donde hammer SÍ crece —store, árboles de fuentes y CARGO_HOME—
|
||||
# que takana ya ni usaba. Un vigía que mide el disco de OTRO no protege la tanda, la mata.
|
||||
# Ahora se miden los tres sitios donde takana SÍ crece —store, árboles de fuentes y CARGO_HOME—
|
||||
# y se toma el mínimo. Si comparten FS, `df` repite el número y esto es inocuo.
|
||||
CARGO_H="${CARGO_HOME:-$HOME/.cargo}"
|
||||
# ── GOPATH TAMBIÉN CRECE, y llenó `/` hasta 0 BYTES el 2026-08-28 ───────────────────────────────
|
||||
@@ -78,7 +78,7 @@ libre_gb() {
|
||||
}
|
||||
ts() { date -u +%Y-%m-%dT%H:%M:%SZ; }
|
||||
|
||||
# EL ORDEN IMPORTA, aunque las deps se resuelvan solas. `hammer build` construye recursivamente lo
|
||||
# EL ORDEN IMPORTA, aunque las deps se resuelvan solas. `takana build` construye recursivamente lo
|
||||
# que le falta, así que cualquier orden termina — pero no cualquier orden es útil a mitad de camino.
|
||||
# Hay 1186 ficheros de receta y sólo ~440 alcanzan alguna imagen; el resto son catálogo y colas de
|
||||
# staging. Construyendo por orden alfabético, tras un día de máquina no habría NI UNA imagen
|
||||
@@ -146,7 +146,7 @@ mapfile -t RECETAS < "$ORDEN"
|
||||
# ── PARTICIÓN ENTRE WORKERS: SHARD=i/N ──────────────────────────────────────────────────────────
|
||||
# Con un worker el corpus son ~7 días (medido: ~9 min/receta sobre 1161). Repartirlo es la única
|
||||
# forma de bajarlo, y no hace falta planificador central: cada worker construye SU parte y las deps
|
||||
# que le falten se las construye `hammer build` solo. Al cosechar, todo converge en el mismo store
|
||||
# que le falten se las construye `takana build` solo. Al cosechar, todo converge en el mismo store
|
||||
# porque las direcciones coinciden — eso está PROBADO por el paso 4 de `farm-lab-sync.sh`, no
|
||||
# supuesto.
|
||||
#
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/sh
|
||||
# Respaldo de hammer al Storage Box de Hetzner (BX11, 1 TiB, hel1).
|
||||
# Respaldo de takana al Storage Box de Hetzner (BX11, 1 TiB, hel1).
|
||||
#
|
||||
# ── POR QUÉ UN STORAGE BOX Y NO EL VOLUMEN ──────────────────────────────────────────────────────
|
||||
# El usuario pidió respaldar «en el volumen, aunque sea montándolo aquí». **No se puede**: un Hetzner
|
||||
@@ -70,12 +70,12 @@ if [ "${1:-}" = "--listar" ]; then
|
||||
cd "$RAIZ"; mkdir -p work
|
||||
# `du -s`, NO `ls`. Un `ls` lista NOMBRES DE DIRECTORIO, y un artefacto VACÍO tiene nombre igual
|
||||
# que uno bueno ⇒ el manifiesto lo avala, `build-state.py` lo da por sellado, rsync lo baja vacío
|
||||
# y `hammer build` hace cache-hit sobre el directorio vacío: sella sin construir y sale 0. Toda la
|
||||
# y `takana build` hace cache-hit sobre el directorio vacío: sella sin construir y sale 0. Toda la
|
||||
# cadena lee el vacío como presencia, y el único síntoma aparece al final, disfrazado de éxito.
|
||||
# (2026-08-10: 7 vacíos de 1749, todos del stack gráfico, de una subida que falló callada.)
|
||||
# Cuesta 8,6 s sobre 1751 artefactos frente a un `ls` instantáneo — barato para lo que evita.
|
||||
# La shell del box no es bash y no tiene `find`, pero SÍ expande globs y SÍ tiene `du`.
|
||||
# `du -s hammer/store/[0-9a-f]*` y NO `store/*`: el glob amplio incluye `.dmerge`, la caché
|
||||
# `du -s takana/store/[0-9a-f]*` y NO `store/*`: el glob amplio incluye `.dmerge`, la caché
|
||||
# transitoria de fusión —48 GB el 2026-08-12—, y recorrerla hace que el listado tarde tanto que se
|
||||
# corta por timeout. El sufijo del glob además descarta de entrada los `.bootstrap-tmp`/`.mirror-tmp`
|
||||
# que nunca son artefactos. Un artefacto SIEMPRE empieza por hex: filtrar en el origen es más barato
|
||||
@@ -83,7 +83,7 @@ if [ "${1:-}" = "--listar" ]; then
|
||||
ssh -4 -p "$SB_PORT" -i "$KEY" -o StrictHostKeyChecking=accept-new \
|
||||
"$SB_USER@$SB_HOST" 'du -s hammer/store/[0-9a-f]*' 2>/dev/null > work/.respaldo-du.tmp
|
||||
# OJO CON EL ORDEN: recortar la ruta ANTES del awk se come el contador (`sed 's#.*/##'` es codicioso
|
||||
# y borra "5347<TAB>hammer/store/" entero), y entonces $1 es el NOMBRE, no los bloques: el filtro
|
||||
# y borra "5347<TAB>takana/store/" entero), y entonces $1 es el NOMBRE, no los bloques: el filtro
|
||||
# compara basura y devuelve cualquier cosa. Primero se filtra por número, después se recorta.
|
||||
# Campo 1 = bloques. Un directorio sin contenido da 1..4; con contenido, mucho más.
|
||||
awk '$1<=4 {sub(/.*\//,"",$2); print $2}' work/.respaldo-du.tmp \
|
||||
@@ -118,7 +118,7 @@ SSH_CMD="ssh -4 -p $SB_PORT -i $KEY -o StrictHostKeyChecking=accept-new -o Serve
|
||||
|
||||
# Opciones comunes. `--partial-dir` es lo que hace el respaldo continuable a mitad de fichero.
|
||||
# `--exclude /.dmerge`: son directorios TRANSITORIOS de fusión del store — aparecen y desaparecen
|
||||
# mientras hammer sella. rsync los empieza a copiar, se esfuman a media transferencia y devuelve 23
|
||||
# mientras takana sella. rsync los empieza a copiar, se esfuman a media transferencia y devuelve 23
|
||||
# («some files/attrs were not transferred»). Como 23 está en la lista de reintentables, el bucle los
|
||||
# reintentó **40 veces seguidas** y se rindió con el respaldo a medias (79 G de 127 G), sin que el
|
||||
# mensaje dijera nunca que la culpa era de un directorio temporal. `cosecha-cron.sh` ya los excluía;
|
||||
|
||||
@@ -148,7 +148,7 @@ STAGE0=.scratch/rust-1.91.0-prefix .scratch/run-xpy.sh \
|
||||
# PRESEED=hammerd es OBLIGATORIO con SWAP_RUST: arje-zero (monorepo tawasuyu) vendorea
|
||||
# ~1973 crates; en un rebuild in-VM completo (PRESEED=all) ese vendor desborda el
|
||||
# rootfs en RAM de la VM → ENOSPC. Con PRESEED=hammerd se preseedea el arje-zero
|
||||
# host-built (hammer-rust, locked) y sólo hammerd se reconstruye in-VM.
|
||||
# host-built (takana-rust, locked) y sólo hammerd se reconstruye in-VM.
|
||||
SWAP_RUST=1 KVM=1 MEM=16384 PRESEED=hammerd ./scripts/selfhost-verify.sh
|
||||
```
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# bootstrap.toml — hammer self-host climb hop 2 (1.91.0 -> 1.91.1), musl host.
|
||||
# stage0 rustc = the installed hammer-built 1.91.0 (bound read-only at /stage0);
|
||||
# bootstrap.toml — takana self-host climb hop 2 (1.91.0 -> 1.91.1), musl host.
|
||||
# stage0 rustc = the installed takana-built 1.91.0 (bound read-only at /stage0);
|
||||
# stage0 cargo = the run_rustc mrustc-bootstrapped 1.90.0 cargo (1.90 satisfies
|
||||
# bootstrap's "minor-1" check for a 1.91.x source). LLVM 20.1.8 reused externally.
|
||||
# extended+cargo so the install includes a hammer-built cargo 1.91.1 (final toolchain).
|
||||
# extended+cargo so the install includes a takana-built cargo 1.91.1 (final toolchain).
|
||||
change-id = "ignore"
|
||||
|
||||
[build]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# bootstrap.toml — hammer self-host climb (musl host, hermetic Alpine devfs sandbox).
|
||||
# bootstrap.toml — takana self-host climb (musl host, hermetic Alpine devfs sandbox).
|
||||
# stage0 = the mrustc-bootstrapped rustc/cargo 1.90.0 (musl host); LLVM 20.1.8 reused
|
||||
# externally; dynamic link against the devfs's system musl (crt-static = false), exactly
|
||||
# like Alpine's own rust and the run_rustc stage-3 toolchain.
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
#!/usr/bin/env bash
|
||||
# swap-rust-into-toolchain.sh — overlay the hammer-built rust 1.91.1 onto a toolchain
|
||||
# dir (default .dev-fs/alpine), so `hammer build` / `bootstrap stage1` compile the
|
||||
# 4/4 rust inputs (arje-zero, hammerd) with hammer-rust instead of Alpine's rustc.
|
||||
# swap-rust-into-toolchain.sh — overlay the takana-built rust 1.91.1 onto a toolchain
|
||||
# dir (default .dev-fs/alpine), so `takana build` / `bootstrap stage1` compile the
|
||||
# 4/4 rust inputs (arje-zero, hammerd) with takana-rust instead of Alpine's rustc.
|
||||
#
|
||||
# This is the rust tool-swap of the selfhost-verify "variante b". Unlike make/bwrap/…
|
||||
# (tools that orchestrate/copy → identical bytes), **rustc emits the 4/4 binaries**, so
|
||||
# a hammer-built rustc produces a DIFFERENT of_tree than Alpine's 9adefb82. Success
|
||||
# criterion is therefore AUTO-CONSISTENCY: stage1' built with hammer-rust == stage1''
|
||||
# rebuilt with hammer-rust (a NEW EXPECT_REF), not byte-identity with the Alpine baseline.
|
||||
# a takana-built rustc produces a DIFFERENT of_tree than Alpine's 9adefb82. Success
|
||||
# criterion is therefore AUTO-CONSISTENCY: stage1' built with takana-rust == stage1''
|
||||
# rebuilt with takana-rust (a NEW EXPECT_REF), not byte-identity with the Alpine baseline.
|
||||
#
|
||||
# The overlay is mostly ADDITIVE and fully reversible:
|
||||
# - hammer's rustc/cargo use host triple x86_64-unknown-linux-musl and hash-suffixed
|
||||
# - takana's rustc/cargo use host triple x86_64-unknown-linux-musl and hash-suffixed
|
||||
# .so names that DON'T collide with Alpine's (x86_64-alpine-linux-musl / other
|
||||
# hashes), so the new rustlib triple + librustc_driver just sit alongside Alpine's.
|
||||
# - only /usr/bin/{rustc,cargo} are REPLACED; the originals are backed up to
|
||||
# usr/bin/<name>.alpine so `--restore` can put them back.
|
||||
# The 4/4 rust recipes build NATIVE (target x86_64-linux-musl == SANDBOX_NATIVE_TARGET
|
||||
# ⇒ no --target, see takana-build/src/lib.rs:269), so hammer-rustc compiles them for
|
||||
# ⇒ no --target, see takana-build/src/lib.rs:269), so takana-rustc compiles them for
|
||||
# its OWN native triple x86_64-unknown-linux-musl.
|
||||
#
|
||||
# Usage:
|
||||
@@ -51,7 +51,7 @@ if [ "$RESTORE" = 1 ]; then
|
||||
echo " (sin backup de usr/bin/$b — ¿ya restaurado?)"
|
||||
fi
|
||||
done
|
||||
# Quitar también lo AÑADIDO por el swap: el librustc_driver de hammer (hash propio, distinto del
|
||||
# Quitar también lo AÑADIDO por el swap: el librustc_driver de takana (hash propio, distinto del
|
||||
# de Alpine) y el sysroot del triple x86_64-unknown-linux-musl (un devfs Alpine prístino NO los
|
||||
# tiene). Inertes para el rustc de Alpine, pero dejarlos es ~350 MB de cruft y deja el toolchain
|
||||
# no-prístino; lo limpiamos para que --restore sea reversibilidad total.
|
||||
@@ -70,11 +70,11 @@ fi
|
||||
|
||||
say "swap: overlay hammer-rust 1.91.1 ($PREFIX) → $TOOLCHAIN"
|
||||
|
||||
# 1) librustc_driver de hammer (hash único, no choca con el de Alpine).
|
||||
# 1) librustc_driver de takana (hash único, no choca con el de Alpine).
|
||||
cp -a "$PREFIX"/lib/librustc_driver-*.so "$TOOLCHAIN/usr/lib/"
|
||||
echo " + usr/lib/$(basename "$PREFIX"/lib/librustc_driver-*.so)"
|
||||
|
||||
# 2) sysroot de hammer para su triple nativo (dir nuevo, no choca con x86_64-alpine-linux-musl).
|
||||
# 2) sysroot de takana para su triple nativo (dir nuevo, no choca con x86_64-alpine-linux-musl).
|
||||
rm -rf "$TOOLCHAIN/usr/lib/rustlib/x86_64-unknown-linux-musl"
|
||||
cp -a "$PREFIX/lib/rustlib/x86_64-unknown-linux-musl" "$TOOLCHAIN/usr/lib/rustlib/"
|
||||
echo " + usr/lib/rustlib/x86_64-unknown-linux-musl ($(du -sh "$PREFIX/lib/rustlib/x86_64-unknown-linux-musl" | cut -f1))"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# nix, ADR 0004: sólo lee deps). Corre `sembrar_cierre` de seed-graph sobre las raíces de una sesión
|
||||
# GNOME y clasifica el cierre:
|
||||
# conocida → ya hay receta en el corpus (cache-hit potencial)
|
||||
# frontera → nixpkgs lo pide y hammer NO tiene receta → HAY QUE AUTORAR (esto es el trabajo real)
|
||||
# frontera → nixpkgs lo pide y takana NO tiene receta → HAY QUE AUTORAR (esto es el trabajo real)
|
||||
# nix-ismo/lab → ruido de nixpkgs o lo provee el sandbox (se descarta)
|
||||
#
|
||||
# Cada `frontera` es una HIPÓTESIS a clasificar/autorar por un humano (o un subagente-por-receta), NO
|
||||
@@ -33,7 +33,7 @@ MINIMA = [ # lo que una sesión GNOME necesita para BOOTEAR a un shell usable (
|
||||
]
|
||||
|
||||
# ── LA OPTIMIZACIÓN (el "yupana de gnome", no un sembrador crudo). El cierre de metadata de nixpkgs
|
||||
# SOBREESTIMA ~5-10× la frontera real de hammer, porque declara buildInputs de features MÁXIMAS. Estas
|
||||
# SOBREESTIMA ~5-10× la frontera real de takana, porque declara buildInputs de features MÁXIMAS. Estas
|
||||
# tablas son HIPÓTESIS de triage (NO verdad — la verdad emerge autorando feature-minimal), para que el
|
||||
# reporte no mienta con "465 recetas". Cada una vuelve legible por qué un candidato probablemente NO es
|
||||
# hueco. Clasificar a mano al autorar; mover entre cubetas es esperable.
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
# Cada arista sembrada se CLASIFICA, que es donde está el valor:
|
||||
# conocida — tras normalizar, hay receta con ese nombre. Arista utilizable ya.
|
||||
# frontera — no hay receta y no es un nix-ismo ⇒ candidato a nodo `wanted` nuevo.
|
||||
# nix-ismo — andamiaje de nixpkgs que en hammer no existe (hooks, wrappers, doc tooling).
|
||||
# nix-ismo — andamiaje de nixpkgs que en takana no existe (hooks, wrappers, doc tooling).
|
||||
# lab — toolchain que provee el rootfs del sandbox, no una receta (cargo/rustc).
|
||||
#
|
||||
# CALIBRADO, no supuesto (medición completa en el plan). Muestra de 60 recetas / 686 deps:
|
||||
@@ -27,7 +27,7 @@
|
||||
# transitividad la hace el grafo**, que es lo que `build-state.py` ya sabe hacer con las aristas.
|
||||
#
|
||||
# Uso: scripts/seed-graph.py --calibrar [N] [--transitivo] # mide el ruido contra recetas escritas
|
||||
# scripts/seed-graph.py --frontera <perfil> # qué pide nixpkgs y hammer no tiene → seed-frontera.json
|
||||
# scripts/seed-graph.py --frontera <perfil> # qué pide nixpkgs y takana no tiene → seed-frontera.json
|
||||
# scripts/seed-graph.py --perfil <perfil> # siembra los nodos `wanted` → seed-edges.json
|
||||
# scripts/seed-graph.py <nombre...> # siembra nombres sueltos
|
||||
# Env: NIXPKGS (def nixpkgs), NIX_ROOT (def ~/.nixstore; vacío = store del sistema)
|
||||
@@ -52,7 +52,7 @@ OUT = ROOT / "docs/state/seed-edges.json"
|
||||
FRONTERA = ROOT / "docs/state/seed-frontera.json"
|
||||
LOTE = 120 # nombres por proceso de `nix eval` (uno solo evalúa 6 en 0.7s; el costo es el arranque)
|
||||
|
||||
# --- normalización nix → hammer -------------------------------------------------------------------
|
||||
# --- normalización nix → takana -------------------------------------------------------------------
|
||||
# Mapeo por evidencia, no por intuición: cada entrada salió de una discrepancia vista en --calibrar.
|
||||
MAPA = {
|
||||
"ninja": "samurai", # hammer usa samurai como backend de meson
|
||||
@@ -66,7 +66,7 @@ MAPA = {
|
||||
"util-linux-minimal": "util-linux",
|
||||
"cacert": "ca-certificates",
|
||||
}
|
||||
# Andamiaje de nixpkgs sin contraparte en hammer: hooks del stdenv, wrappers, tooling de docs.
|
||||
# Andamiaje de nixpkgs sin contraparte en takana: hooks del stdenv, wrappers, tooling de docs.
|
||||
NIXISMOS_EXACTOS = {
|
||||
"", "which", "stdenv", "gtk-doc", "docbook-xml", "docbook-xsl", "docbook-xsl-nons",
|
||||
"docbook-xsl-ns", "docbook2X", "asciidoc", "asciidoctor", "install-shell-files",
|
||||
@@ -94,13 +94,13 @@ if _ALIAS.exists():
|
||||
NIXISMOS_INFIJOS = ("-hook",) # `cargo-build-hook.sh` no termina en `-hook`, lo contiene
|
||||
# Toolchains que provee el LAB (el rootfs del sandbox), no una receta: ninguna receta Rust declara
|
||||
# `rust` ni `cargo` en `[deps].build` — están vacías y el toolchain viene de abajo. Que nixpkgs los
|
||||
# liste no es un hueco de hammer; es que las dos distros ponen la frontera en lugares distintos.
|
||||
# liste no es un hueco de takana; es que las dos distros ponen la frontera en lugares distintos.
|
||||
PROVISTO_POR_EL_LAB = {"cargo", "rustc", "rust"}
|
||||
# Deps que hammer declara SIEMPRE y nix nunca lista porque las provee el stdenv implícito. No son
|
||||
# un fallo del sembrador: son convención del sandbox de hammer (cada dep es una capa --overlay-src).
|
||||
# Deps que takana declara SIEMPRE y nix nunca lista porque las provee el stdenv implícito. No son
|
||||
# un fallo del sembrador: son convención del sandbox de takana (cada dep es una capa --overlay-src).
|
||||
CONVENCION_HAMMER = {"make", "binutils", "linux-headers"}
|
||||
# Sufijos de VARIANTE de hammer: `zlib` y `zlib-shared` son el mismo paquete con otra decisión de
|
||||
# enlazado. nixpkgs no puede saber cuál eligió hammer ⇒ no es un fallo del sembrador, es una
|
||||
# Sufijos de VARIANTE de takana: `zlib` y `zlib-shared` son el mismo paquete con otra decisión de
|
||||
# enlazado. nixpkgs no puede saber cuál eligió takana ⇒ no es un fallo del sembrador, es una
|
||||
# decisión nuestra. Se cuenta aparte para no inflar ni el acierto ni el error.
|
||||
VARIANTES = ("-shared", "-static")
|
||||
|
||||
|
||||
+15
-15
@@ -53,7 +53,7 @@ die() { printf '\033[1;31mERROR: %s\033[0m\n' "$*" >&2; exit 1; }
|
||||
[[ -r "$KERNEL" ]] || die "no puedo leer el kernel $KERNEL (set KERNEL=…)"
|
||||
command -v qemu-system-x86_64 >/dev/null || die "falta qemu-system-x86_64"
|
||||
|
||||
# 0) Binario hammer estático (musl, crt-static) — el builder lo bootea adentro.
|
||||
# 0) Binario takana estático (musl, crt-static) — el builder lo bootea adentro.
|
||||
HAMMER_BIN="target/x86_64-unknown-linux-musl/release/hammer"
|
||||
say "build hammer estático ($HAMMER_BIN)"
|
||||
cargo build --release --target x86_64-unknown-linux-musl -p takana-cli
|
||||
@@ -71,16 +71,16 @@ say "semilla: $SEED_HASH"
|
||||
# 0c) Auto-alojamiento del COMPILADOR (variante b, pieza rust). A DIFERENCIA de make/busybox/bwrap/…
|
||||
# (herramientas que ORQUESTAN o COPIAN ⇒ bytes idénticos), **rustc EMITE los binarios del 4/4**
|
||||
# (arje-zero, hammerd): un rustc distinto ⇒ of_tree DIVERGE del baseline Alpine 9adefb82. Por eso
|
||||
# el criterio no es igualdad con Alpine sino AUTO-CONSISTENCIA: el REF se RECOMPUTA con hammer-rust
|
||||
# el criterio no es igualdad con Alpine sino AUTO-CONSISTENCIA: el REF se RECOMPUTA con takana-rust
|
||||
# (nuevo EXPECT_REF) y la VM debe reproducir ESE. El swap entra en $TOOLCHAIN, que sirve TANTO al
|
||||
# build host del REF (este script, abajo) COMO al toolchain in-VM del builder (--toolchain); se
|
||||
# restaura al salir (trap). hammer-rust se construye con scripts/rust-frontier (climb mrustc→1.91.1).
|
||||
# restaura al salir (trap). takana-rust se construye con scripts/rust-frontier (climb mrustc→1.91.1).
|
||||
# SWAP_RUST=1 → overlay .scratch/rust-1.91.1-prefix sobre /toolchain/usr/{bin,lib}.
|
||||
# RUST_PREFIX=DIR → prefix hammer-rust alternativo (default .scratch/rust-1.91.1-prefix).
|
||||
# RUST_EXPECT_REF=… → REF conocido-bueno de hammer-rust (cross-check; vacío ⇒ sólo lo imprime).
|
||||
# RUST_PREFIX=DIR → prefix takana-rust alternativo (default .scratch/rust-1.91.1-prefix).
|
||||
# RUST_EXPECT_REF=… → REF conocido-bueno de takana-rust (cross-check; vacío ⇒ sólo lo imprime).
|
||||
# ⚠️ Usá PRESEED=hammerd con SWAP_RUST: arje-zero (monorepo tawasuyu) vendorea ~1973 crates;
|
||||
# en un rebuild in-VM completo (PRESEED=all) ese vendor desborda el rootfs en RAM de la VM
|
||||
# (ENOSPC). PRESEED=hammerd preseedea el arje-zero host-built (hammer-rust, locked) y sólo
|
||||
# (ENOSPC). PRESEED=hammerd preseedea el arje-zero host-built (takana-rust, locked) y sólo
|
||||
# reconstruye hammerd in-VM. ✓ REPRODUCIBLE verificado así (2026-06-18, of_tree 7fa6cb4e).
|
||||
if [[ "${SWAP_RUST:-0}" == 1 ]]; then
|
||||
RUST_PREFIX="${RUST_PREFIX:-.scratch/rust-1.91.1-prefix}"
|
||||
@@ -91,11 +91,11 @@ if [[ "${SWAP_RUST:-0}" == 1 ]]; then
|
||||
# OJO: el input-hash del store NO incluye el rustc (recipe.hash_inputs = source+compiler+target+
|
||||
# link+patches+flags+phases+deps), así que en el ./store compartido arje-zero/hammerd quedarían
|
||||
# CACHEADOS con los bytes de Alpine y el swap sería un no-op. Usamos un store DEDICADO (store-rust)
|
||||
# para forzar el rebuild de los 4/4 con hammer-rust; así el of_tree refleja el compilador nuevo.
|
||||
# para forzar el rebuild de los 4/4 con takana-rust; así el of_tree refleja el compilador nuevo.
|
||||
STORE="${RUST_STORE:-store-rust}"
|
||||
say "SWAP_RUST: store dedicado $STORE (el ./store baseline no se toca; fuerza rebuild con hammer-rust)"
|
||||
# Con hammer-rust el REF NO es el 9adefb82 de Alpine; comparamos contra RUST_EXPECT_REF, el
|
||||
# of_tree(stage1) auto-consistente de hammer-rust 1.91.1 (reproducido 2× en host, 2026-06-17,
|
||||
# Con takana-rust el REF NO es el 9adefb82 de Alpine; comparamos contra RUST_EXPECT_REF, el
|
||||
# of_tree(stage1) auto-consistente de takana-rust 1.91.1 (reproducido 2× en host, 2026-06-17,
|
||||
# store-rust y store-rust2). Override con RUST_EXPECT_REF= para re-anclar.
|
||||
EXPECT_REF="${RUST_EXPECT_REF:-b3:7fa6cb4e70a934d72206cf3d95cf46a13f0a6464ec73855b98d547195f4ed047}"
|
||||
fi
|
||||
@@ -143,10 +143,10 @@ for m in overlay e1000; do
|
||||
done
|
||||
|
||||
# 3b) Auto-alojamiento *puro* (variante b, SDD 11 §7.2b): reemplazar piezas del toolchain Alpine por
|
||||
# recetas hammer construidas desde fuente, con Stage 2 reverificando que `of_tree(stage1')` NO
|
||||
# cambia (el make hammer compila los 4/4 igual de bit-a-bit que el de Alpine). Opt-in:
|
||||
# recetas takana construidas desde fuente, con Stage 2 reverificando que `of_tree(stage1')` NO
|
||||
# cambia (el make takana compila los 4/4 igual de bit-a-bit que el de Alpine). Opt-in:
|
||||
# SWAP_MAKE=1 → construye recipes/make.toml y lo monta sobre /toolchain/usr/bin/make.
|
||||
# SWAP_BUSYBOX=1 → monta el busybox de hammer sobre /toolchain/bin/busybox (los symlinks de
|
||||
# SWAP_BUSYBOX=1 → monta el busybox de takana sobre /toolchain/bin/busybox (los symlinks de
|
||||
# applets de Alpine — sh/sed/grep/awk/tar/find — pasan a usarlo; cp/mkdir/
|
||||
# install siguen siendo GNU coreutils, intactos).
|
||||
# SWAP_LINUX_HEADERS=1 → construye recipes/linux-headers.toml y swap-directorio de los 13 subdirs
|
||||
@@ -195,7 +195,7 @@ if [[ "${SWAP_BWRAP:-0}" == 1 ]]; then
|
||||
# Pieza 4: bwrap (bubblewrap), EL sandbox del lab. Binario estático ⇒ swap de archivo sobre
|
||||
# /toolchain/usr/bin/bwrap. Su dep libcap la construye y materializa el lab solo (deps.build).
|
||||
# bwrap es herramienta, no input del 4/4: no necesita casar byte-a-byte con Alpine, sólo aislar
|
||||
# igual (validado en host — musl rebuildeó byte-idéntico bajo hammer-bwrap).
|
||||
# igual (validado en host — musl rebuildeó byte-idéntico bajo takana-bwrap).
|
||||
say "variante b — construir bwrap (+libcap) desde fuente y swapearlo en /toolchain/usr/bin/bwrap"
|
||||
BW_HASH="$("$HAMMER" --store "$STORE" build recipes/bwrap.toml | grep -oE 'b3:[0-9a-f]{64}' | tail -1)"
|
||||
[[ "$BW_HASH" == b3:* ]] || die "no obtuve el hash sellado de bwrap"
|
||||
@@ -206,7 +206,7 @@ if [[ "${SWAP_COREUTILS:-0}" == 1 ]]; then
|
||||
# Pieza 5: GNU coreutils (cp/mkdir/ln/chmod/mv/install…). Multicall (--enable-single-binary), igual
|
||||
# layout que Alpine: un binario /bin/coreutils + ~100 symlinks. UN swap del binario rutea todos los
|
||||
# applets (los symlinks del toolchain ya apuntan a coreutils). Tool, no input: validado en host —
|
||||
# musl Y busybox rebuildearon byte-idéntico bajo hammer-coreutils.
|
||||
# musl Y busybox rebuildearon byte-idéntico bajo takana-coreutils.
|
||||
say "variante b — construir coreutils desde fuente y swapearlo en /toolchain/bin/coreutils"
|
||||
CU_HASH="$("$HAMMER" --store "$STORE" build recipes/coreutils.toml | grep -oE 'b3:[0-9a-f]{64}' | tail -1)"
|
||||
[[ "$CU_HASH" == b3:* ]] || die "no obtuve el hash sellado de coreutils"
|
||||
@@ -235,7 +235,7 @@ if [[ "$PRESEED" == "hammerd" ]]; then
|
||||
fi
|
||||
|
||||
# 4c) HAMMER_KERNEL=1 — wrapper /init para escapar del rootfs (frente kernel-from-source).
|
||||
# Con un kernel hammer-built (recipes/linux.toml) el `/` del initramfs es el rootfs ABSOLUTO del
|
||||
# Con un kernel takana-built (recipes/linux.toml) el `/` del initramfs es el rootfs ABSOLUTO del
|
||||
# mount-namespace (su propio padre, no movible) y bwrap del sandbox falla en `pivot_root: Invalid
|
||||
# argument` (el kernel host no lo exigía — quirk suyo). El fix portable (funciona en cualquier
|
||||
# kernel): un /init PID1 que copia el rootfs a un tmpfs y hace `switch_root`, dejando `/` como un
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# ssh-e2e-test.sh — SPIKE de validación (sucio, temporal) del Paso 1: probar el handshake SSH REAL
|
||||
# contra un hammer booteado en QEMU, con sshd supervisado por arje-zero como genesis card.
|
||||
# contra un takana booteado en QEMU, con sshd supervisado por arje-zero como genesis card.
|
||||
#
|
||||
# Esto NO es plomería de producto: arma un `work/rootfs-ssh-test` desechable = el 4/4 limpio del
|
||||
# store + openssh + netup (todos PRECOMPILADOS de la caché del lab, no rebuild) + una seed con un
|
||||
@@ -78,7 +78,7 @@ chmod 0600 "$RFS/root/.ssh/authorized_keys"
|
||||
|
||||
# ── 3) seed card: console-getty + un card `sshd` (Native, Restart) ───────────────────────────────
|
||||
# El card sshd arranca la red (netup, DHCP slirp), genera host keys (ssh-keygen -A) y exec sshd -D.
|
||||
# Mismo esquema validado de STAGE1_SEED_CARD (hammer-bootstrap); ULIDs nuevos A10/A11/A12.
|
||||
# Mismo esquema validado de STAGE1_SEED_CARD (takana-bootstrap); ULIDs nuevos A10/A11/A12.
|
||||
SSHD_CMD='/usr/bin/netup; /usr/bin/ssh-keygen -A; exec /usr/sbin/sshd -D -e'
|
||||
cat > "$RFS/ente/seed.card.json" <<EOF
|
||||
{
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
# RELINKEA al instalar, así que sólo en compile no basta), nunca en configure. Patrón ya usado en
|
||||
# jq/parted/shadow/procps-ng.
|
||||
#
|
||||
# Por qué esto es un script y no un gate duro dentro de `hammer build`: hacerlo fallar hoy rompe
|
||||
# Por qué esto es un script y no un gate duro dentro de `takana build`: hacerlo fallar hoy rompe
|
||||
# 28 recetas selladas de golpe, varias del sistema base (curl, util-linux, sudo). Primero se
|
||||
# arreglan con evidencia, después se cierra la puerta. Mientras tanto esto es la evidencia.
|
||||
#
|
||||
@@ -50,14 +50,14 @@ mienten=0; honestos=0; sin=0
|
||||
for f in $recetas; do
|
||||
n=$(basename "$f" .toml)
|
||||
grep -qE '^[[:space:]]*link[[:space:]]*=[[:space:]]*"static"' "$f" || continue
|
||||
# EL ARTEFACTO VIGENTE, por hash — NO el más reciente por mtime. `hammer hash` calcula el
|
||||
# EL ARTEFACTO VIGENTE, por hash — NO el más reciente por mtime. `takana hash` calcula el
|
||||
# ArtifactHash de la receta de HOY sin construir (~2ms, puro sobre las recetas). El store guarda
|
||||
# TODOS los sellados históricos de una receta (expat tenía 5); auditar el más reciente por mtime
|
||||
# sobre-reportaba (acusaba a dbus/libnl, ya sanas, por un sellado anterior a sus flags). Ahora
|
||||
# auditamos EXACTAMENTE el sellado que corresponde a la receta actual, o ninguno.
|
||||
#
|
||||
# Fallback a `ls -dt` si `hammer` no está compilado: el audit sigue corriendo, sólo vuelve a
|
||||
# poder sobre-reportar (como antes de que existiera `hammer hash`).
|
||||
# Fallback a `ls -dt` si `takana` no está compilado: el audit sigue corriendo, sólo vuelve a
|
||||
# poder sobre-reportar (como antes de que existiera `takana hash`).
|
||||
if [ -x "$HAMMER" ]; then
|
||||
h=$("$HAMMER" --store "$STORE" hash "$f" 2>/dev/null) || h=""
|
||||
if [ -n "$h" ] && [ -d "$STORE/${h#b3:}-$n" ]; then
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
# store-gc.sh — recolector de basura del store content-addressed. `hammer gc` no existe; esto es.
|
||||
# store-gc.sh — recolector de basura del store content-addressed. `takana gc` no existe; esto es.
|
||||
#
|
||||
# ── EL PROBLEMA QUE RESUELVE ────────────────────────────────────────────────────────────────────
|
||||
# El store acumula un artefacto por cada SELLADO, no uno por receta. Cuando una dep cambia, el
|
||||
@@ -8,7 +8,7 @@
|
||||
# `libqalculate`, 9 de `qt6-qtdeclarative`, 8 de `gtk4`. 51G de los 74G eran versiones anteriores.
|
||||
#
|
||||
# ── EL CRITERIO, Y POR QUÉ TIENE DOS ESCALONES ──────────────────────────────────────────────────
|
||||
# El conjunto VIVO es `hammer hash` sobre TODAS las recetas de TODAS las colas: es la respuesta a
|
||||
# El conjunto VIVO es `takana hash` sobre TODAS las recetas de TODAS las colas: es la respuesta a
|
||||
# «¿cuál es el hash vigente de esta receta HOY?», que el store solo no puede dar. Lo que no está en
|
||||
# ese conjunto es RANCIO — pero rancio se parte en dos cosas muy distintas:
|
||||
#
|
||||
|
||||
@@ -74,7 +74,7 @@ def fatal(msg):
|
||||
raise SystemExit(1)
|
||||
|
||||
|
||||
# ── El artefacto se resuelve por `hammer hash`, NUNCA por glob ────────────────────────────────
|
||||
# ── El artefacto se resuelve por `takana hash`, NUNCA por glob ────────────────────────────────
|
||||
# Con dos artefactos de la misma receta conviviendo en el store, `ls | head -1` es una ruleta: se
|
||||
# puede estar probando el de ayer y leerlo como éxito. La receta dice cuál es el vigente.
|
||||
def artefacto(receta, nombre):
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#
|
||||
# POR QUÉ EXISTE. Un guardián que nunca falló no se sabe si sirve, y uno que falla SIEMPRE se ve
|
||||
# idéntico a uno que funciona: por eso el control no es un extra, es la mitad de la prueba. El
|
||||
# método lo aportó la sesión hammer-f8 (su scripts/test-atuq-politica.py hace lo mismo con el cruce
|
||||
# método lo aportó la sesión takana-f8 (su scripts/test-atuq-politica.py hace lo mismo con el cruce
|
||||
# política↔XPI).
|
||||
#
|
||||
# NO CONSTRUYE NADA, NO TOMA EL LOCK Y NO ESCRIBE EN EL STORE: trabaja sobre copias temporales de
|
||||
|
||||
+2
-2
@@ -2,7 +2,7 @@
|
||||
# triaje.py — convierte los CANDIDATOS de la frontera en VEREDICTOS durables. (P5 del plan
|
||||
# docs/plan-catalogo-objetivo.md.)
|
||||
#
|
||||
# POR QUÉ. `seed-graph.py --frontera` produce hipótesis: 137 nombres que nixpkgs pide y hammer no
|
||||
# POR QUÉ. `seed-graph.py --frontera` produce hipótesis: 137 nombres que nixpkgs pide y takana no
|
||||
# puede construir. Clasificarlos es decisión HUMANA (la regla de la granja: el worker MIDE, el hub
|
||||
# CLASIFICA). Pero si ese juicio se queda en la cabeza o en un mensaje, se repite entero cada vez que
|
||||
# alguien vuelve a correr el sembrador. Este fichero lo hace durable y consumible por máquina.
|
||||
@@ -10,7 +10,7 @@
|
||||
# TRES VEREDICTOS, tres destinos distintos — y ahí está el valor, porque cada uno CIERRA un lazo:
|
||||
# hueco → falta de verdad ⇒ va como raíz a `targets.toml` y nace un nodo `wanted` en el grafo,
|
||||
# que el sembrador siembra y el drenaje ordena. Es lo que da trabajo nuevo al worker.
|
||||
# opcional → dep que nixpkgs habilita y hammer no quiere (gstreamer en un escritorio sin audio).
|
||||
# opcional → dep que nixpkgs habilita y takana no quiere (gstreamer en un escritorio sin audio).
|
||||
# Queda registrada para que deje de reaparecer como pregunta.
|
||||
# nix-ismo → andamiaje de nixpkgs sin contraparte acá ⇒ vuelve a `seed-graph.py` como descarte,
|
||||
# así la próxima corrida es más limpia. El sembrador aprende de su propio triaje.
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#!/bin/sh
|
||||
# E2E de la Etapa E4 (upgrades atómicos con rollback) contra el binario `hammer` REAL.
|
||||
# E2E de la Etapa E4 (upgrades atómicos con rollback) contra el binario `takana` REAL.
|
||||
#
|
||||
# Monta un store sintético con dos árboles "producto" (v1, v2), un root FHS vivo, y ejercita:
|
||||
# apply v1 → apply v2 → rollback → rollback, verificando el estado del root en cada paso.
|
||||
# Todo en host, sin VM: valida el cableado del CLI + la semántica generación/rollback de hammer-upgrade.
|
||||
# Todo en host, sin VM: valida el cableado del CLI + la semántica generación/rollback de takana-upgrade.
|
||||
#
|
||||
# Uso: scripts/upgrade-e2e-test.sh
|
||||
set -eu
|
||||
@@ -28,8 +28,8 @@ up() { "$HAMMER" --store "$STORE" upgrade --root "$ROOT" --state-root "$STATE" -
|
||||
printf 'PRISTINE-ls' > "$ROOT/usr/bin/ls"
|
||||
printf 'motd original\n' > "$ROOT/etc/motd"
|
||||
|
||||
# --- sella dos árboles producto sintéticos en el store (vía `hammer build`? no: usamos un mini-store
|
||||
# a mano replicando la forma <64hex>-<name>; el of_tree lo computa hammer al aplicar) ---
|
||||
# --- sella dos árboles producto sintéticos en el store (vía `takana build`? no: usamos un mini-store
|
||||
# a mano replicando la forma <64hex>-<name>; el of_tree lo computa takana al aplicar) ---
|
||||
seal_tree() {
|
||||
# $1=hex64 $2=name $3=builder-fn
|
||||
dir="$STORE/$1-$2"
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
# ── 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.
|
||||
# `takana 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
|
||||
@@ -51,7 +51,7 @@ ROOT="$(cd "$(dirname "$0")/.." && pwd)"; cd "$ROOT"
|
||||
STORE="${STORE:-./store}"; HAMMER="${TAKANA:-${HAMMER:-./target/release/takana}}"; N="${N:-30}"
|
||||
|
||||
# ── EL LOCK LO TOMA EL SCRIPT, Y ANTES DE TOCAR NADA ───────────────────────────────────────────
|
||||
# Este script llama a `hammer build`, y la regla 1 del repo es que todo build va envuelto en el
|
||||
# Este script llama a `takana build`, y la regla 1 del repo es que todo build va envuelto en el
|
||||
# `flock` de la granja: dos builds que compartan una dep se pisan el árbol de `work/sources` y lo
|
||||
# rompen PARA SIEMPRE (ADR 0012). Lo toma el script y no quien lo llama, por lo mismo que en
|
||||
# `poda-fuentes.sh`: así no hay forma de correrlo mal — y correrlo mal fue exactamente lo que pasó.
|
||||
@@ -68,7 +68,7 @@ if [ -z "${VERIFICAR_REPRO_CON_LOCK:-}" ]; then
|
||||
fi
|
||||
|
||||
# ── ⚠ EL APARTADERO VA DENTRO DEL STORE, NO EN /tmp ────────────────────────────────────────────
|
||||
# Para verificar hay que sacar el artefacto de su sitio y dejar que hammer lo reconstruya. Ese
|
||||
# Para verificar hay que sacar el artefacto de su sitio y dejar que takana lo reconstruya. Ese
|
||||
# `mv` era a un `mktemp -d`, o sea a `/tmp`, y ahí se juntan tres cosas que costaron un artefacto
|
||||
# el 2026-09-05:
|
||||
#
|
||||
@@ -175,7 +175,7 @@ else
|
||||
# caliente puede tardar muchos minutos acá en frío — `angle-grinder` y `amp` entraron por el
|
||||
# filtro y se comieron el disco. El tope acota el build DE ESA RECETA en LA MÁQUINA QUE LO
|
||||
# MIDIÓ, no acá.
|
||||
# · Y no cuenta la CASCADA: `hammer build` reconstruye las deps que falten en el store local, y
|
||||
# · Y no cuenta la CASCADA: `takana build` reconstruye las deps que falten en el store local, y
|
||||
# eso no está en el tiempo del sidecar. Es la misma sorpresa que dio `gjs`, cuyo grafo decía
|
||||
# «una receta» y se puso a rehacer spidermonkey entero.
|
||||
# ⇒ con disco justo, mejor pasar las recetas a mano que confiar en el tope.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# vigia-parches.py — ¿los parches de la receta AGARRAN todavía en la fuente que la receta pinea?
|
||||
#
|
||||
# ══ EL PUNTO CIEGO QUE ESTE VIGÍA CUBRE ════════════════════════════════════════════════════════
|
||||
# `hammer build` aplica los parches DESPUÉS de materializar las dependencias. En una receta hoja de
|
||||
# `takana build` aplica los parches DESPUÉS de materializar las dependencias. En una receta hoja de
|
||||
# la plataforma Gecko eso significa que el `patch` que no agarra se descubre detrás de horas de
|
||||
# compilar OTRA COSA: waterfox estrena su primer build reconstruyendo nodejs (4414 objetivos de V8,
|
||||
# a `-j2` porque el propio recipe capa por RAM). El parche que falla es el paso 3 de un camino cuyo
|
||||
@@ -10,7 +10,7 @@
|
||||
#
|
||||
# La receta de waterfox además hace una APUESTA EXPLÍCITA y escrita: la base es Gecko 153.1.0 y los
|
||||
# once parches de musl son los de Firefox 154, una release por encima. Su propio comentario dice
|
||||
# «si `patch` falla, falla TEMPRANO, antes de compilar nada». Con el orden real de `hammer build`
|
||||
# «si `patch` falla, falla TEMPRANO, antes de compilar nada». Con el orden real de `takana build`
|
||||
# eso no es cierto: falla tarde. Este vigía es lo que lo vuelve cierto.
|
||||
#
|
||||
# ══ Y EL SEGUNDO PUNTO CIEGO, QUE ES EL CARO ═══════════════════════════════════════════════════
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user