gioser

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…

TOML 188Rust 55Shell 30Markdown 281211 Definiciones

⎇ a63af7b53156d7eb80faff7e899b6d556abb93ee scripts/servidor/avisar-actualizaciones.sh Shell · 94 líneas · 4.9 KB Historia crudo

el aviso, enchufado en la caja — y «al día» con cero instalados era una mentira cómoda a63af7b53 · Sergio ·

1#!/bin/sh
2# avisar-actualizaciones.sh — pregunta al repo si hay novedades para lo que esta máquina tiene
3# instalado, y AVISA sólo cuando la respuesta cambia.
4#
5# ── QUÉ HACE Y QUÉ NO ───────────────────────────────────────────────────────────────────────────
6# Corre `takana outdated --json` (que no construye, no baja `.swm` y no escribe nada: lee el índice
7# firmado y la DB de instalados) y deja el resultado en un fichero de estado. **No actualiza nada**:
8# instalar es reproducir y verificar, y eso no se cuelga de un cron. El aviso dice qué correr.
9#
10# ── POR QUÉ SÓLO AVISA CUANDO CAMBIA ────────────────────────────────────────────────────────────
11# Un guardián que grita lo mismo cada 30 minutos se deja de leer, y entonces no avisa de nada — es
12# la lección que ya está escrita en `vigia-subcomandos.py` ([[guardianes-el-ruido-los-mata]]).
13# Así que el ciclo normal es una línea sola («al día», o «3 novedades, las mismas de antes») y el
14# grito sale cuando la LISTA de novedades difiere de la del ciclo anterior.
15#
16# ── LA FIRMA NO ES OPCIONAL ACÁ ─────────────────────────────────────────────────────────────────
17# Se pasa `--trust`: decidir «hay que actualizar» a partir de un catálogo que nadie firmó es la
18# forma exacta de que te instalen lo que quieran. `outdated` aborta si el índice está manipulado
19# (BAD-SIG) e informa si está sin firmar o firmado por una clave desconocida — ese informe sale en
20# el log y en el estado, no se traga.
21#
22# Uso:    scripts/servidor/avisar-actualizaciones.sh
23#         */30 * * * * cd /work/sergio/takana && scripts/servidor/avisar-actualizaciones.sh \
24#                        >>work/avisar-actualizaciones.log 2>&1
25#
26# Env:    REPO   origen(es) del catálogo — dir local o lista HTTP(S) separada por comas
27#         DB     DB de instalados (def /var/lib/hammer/installed.json)
28#         TRUST  directorio de claves de confianza (def ./trust)
29#         STATE  fichero de estado (def work/actualizaciones.json)
30#         TAKANA binario (def ./target/release/takana)
31set -eu
32ROOT="$(cd "$(dirname "$0")/../.." && pwd)"; cd "$ROOT"
33
34REPO="${REPO:-https://repo.gioser.net}"
35DB="${DB:-/var/lib/hammer/installed.json}"
36TRUST="${TRUST:-$ROOT/trust}"
37STATE="${STATE:-$ROOT/work/actualizaciones.json}"
38TAKANA="${TAKANA:-$ROOT/target/release/takana}"
39
40[ -x "$TAKANA" ] || { echo "!! falta $TAKANA (cargo build --release)" >&2; exit 2; }
41mkdir -p "$(dirname "$STATE")"
42
43NUEVO="$STATE.nuevo"
44if ! "$TAKANA" outdated --repo "$REPO" --db "$DB" --trust "$TRUST" --json >"$NUEVO" 2>"$NUEVO.err"; then
45    # Un repo caído o un índice manipulado NO se resume en «al día»: se dice, y se deja el estado
46    # anterior intacto para no borrar la última respuesta buena.
47    echo "$(date -u +%FT%TZ) ✗ no pude preguntarle al repo $REPO:"
48    sed 's/^/    /' "$NUEVO.err"
49    rm -f "$NUEVO" "$NUEVO.err"
50    exit 1
51fi
52rm -f "$NUEVO.err"
53
54# La huella es la LISTA de novedades (nombre+versión publicada), no el JSON entero: el JSON trae
55# contadores que se mueven solos (p.ej. `no_instalados`) y harían «cambiar» un estado idéntico.
56huella() { [ -f "$1" ] || { echo ""; return; }; python3 - "$1" <<'PY'
57import json, sys
58try:
59    d = json.load(open(sys.argv[1]))
60except Exception:
61    print(""); raise SystemExit
62print(",".join(sorted(f"{n['name']}={n['publicada']}" for n in d.get("novedades", []))))
63PY
64}
65
66ANTES="$(huella "$STATE")"
67AHORA="$(huella "$NUEVO")"
68mv "$NUEVO" "$STATE"
69
70N=$(python3 -c 'import json,sys; print(len(json.load(open(sys.argv[1])).get("novedades",[])))' "$STATE")
71INSTALADOS=$(python3 -c 'import json,sys; print(json.load(open(sys.argv[1])).get("instalados",0))' "$STATE")
72SELLO="$(date -u +%FT%TZ)"
73
74# ⚠ «Al día» con CERO paquetes instalados es verdad y no dice nada — peor: se lee como «comprobado
75# y correcto» cuando lo cierto es que no había nada que comprobar. Medido la primera vez que corrió
76# en la caja, cuya DB de instalados ni existe. Un aviso que suena igual cuando vigila que cuando no
77# vigila nada es exactamente cómo se deja de mirar un aviso.
78if [ "$INSTALADOS" = "0" ]; then
79    echo "$SELLO · nada instalado por takana en esta máquina (DB $DB) — no hay qué comparar contra $REPO"
80    exit 0
81fi
82
83if [ "$N" = "0" ]; then
84    echo "$SELLO · al día contra $REPO ($INSTALADOS paquete(s) comprobado(s))"
85    exit 0
86fi
87
88if [ "$AHORA" = "$ANTES" ]; then
89    echo "$SELLO · $N novedad(es), las mismas del ciclo anterior (estado en $STATE)"
90    exit 0
91fi
92
93echo "$SELLO ⚠ HAY $N NOVEDAD(ES) en $REPO:"
94"$TAKANA" outdated --repo "$REPO" --db "$DB" --trust "$TRUST" | sed 's/^/    /'