sergio/takana
> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…
⎇ f33df351cfd0c70b5909b01d1f0ff4105adf9010
scripts/gnome/inyectar-cards.py
History
Who touched what
raw
arjectl en la imagen: relanzar en caliente — y el genesis NO alcanzaba para eso
1#!/usr/bin/env python3
2# inyectar-cards.py — mete las Cards de servicio de un perfil en el `genesis` de la seed (SDD 30 §4c).
3#
4# Por qué es un fichero y no un heredoc dentro del script de imagen: el script ya tiene un heredoc de
5# python (el del console-getty) y anidar dos es la receta del error mudo — el terminador del interno
6# cierra el externo y la mitad del programa se ejecuta como shell. Pasó al escribir esto.
7#
8# NO genera las Cards: las recibe ya emitidas por `takana service-cards`, que es la única
9# implementación de la traducción `[[service]]` → Card. Acá sólo se COMPONE la seed.
10#
11# ── LAS CARDS VAN A DOS SITIOS, Y HACEN FALTA LOS DOS (2026-09-14) ──────────────────────────────
12# El `genesis` de la seed dice qué arranca AL BOOT. `arjectl start|restart <label>` no lee de ahí:
13# usa `SpawnCardFromDisk`, que carga `/etc/arje/cards.d/<label>.json`. Medido en la imagen del
14# perfil `servidor`:
15#
16# $ arjectl start gitea
17# Error: arje-zero rechazó: card gitea: No such file or directory
18# (buscada en /etc/arje/cards.d/gitea.json)
19#
20# O sea: con sólo el genesis, un servicio que muere tras agotar su backoff **no se puede relanzar
21# sin reiniciar la máquina**. Escribir las dos no es duplicar: son dos preguntas distintas —qué
22# arranca solo, y qué se puede encarnar a pedido— y arje las responde desde sitios distintos.
23import json, os, sys
24
25seed_p, cards_p = sys.argv[1], sys.argv[2]
26cards_dir = sys.argv[3] if len(sys.argv) > 3 else None
27seed = json.load(open(seed_p))
28cards = json.load(open(cards_p))
29
30# Idempotente por `label`: re-armar la imagen no puede duplicar Entes. Dos Cards con el mismo label
31# no son "dos servicios": para el grafo de arje son una colisión de identidad, y el segundo se
32# perdería en silencio o pelearía con el primero por el mismo nombre del bus.
33ya = {g.get("label") for g in seed.get("genesis", [])}
34puestos = [c["label"] for c in cards if c["label"] not in ya]
35seed.setdefault("genesis", []).extend(c for c in cards if c["label"] not in ya)
36
37tmp = seed_p + ".new"
38with open(tmp, "w") as f:
39 json.dump(seed, f, indent=2)
40os.chmod(tmp, 0o644)
41# Reemplazar la ENTRADA de directorio, no escribir en sitio: la seed del árbol fundido es un hardlink
42# read-only al artefacto del store, y escribirle encima mutaría el store.
43os.replace(tmp, seed_p)
44print(" cards en el genesis:", ", ".join(puestos) or "(ninguna nueva)")
45
46# El store en disco que `arjectl` consulta. Se escriben TODAS, no sólo las nuevas: una card que ya
47# estaba en el genesis (p. ej. `sshd`, que viene del product-rootfs) tampoco es relanzable sin esto.
48if cards_dir:
49 os.makedirs(cards_dir, exist_ok=True)
50 for c in cards:
51 dst = os.path.join(cards_dir, f"{c['label']}.json")
52 tmp = dst + ".new"
53 with open(tmp, "w") as f:
54 json.dump(c, f, indent=2)
55 os.chmod(tmp, 0o644)
56 os.replace(tmp, dst) # `replace`, no `write`: el árbol fundido hardlinkea al store
57 print(" cards en", cards_dir + ":", ", ".join(c["label"] for c in cards))