Corre `takana outdated --json` contra el índice firmado y deja el resultado en un fichero de estado. No actualiza nada: instalar es reproducir y verificar, y eso no se cuelga de un cron — el aviso dice qué comando correr. Tres cosas deliberadas: · Sólo grita cuando la LISTA de novedades difiere de la del ciclo anterior. Un guardián que repite lo mismo cada 30 min se deja de leer, y entonces no avisa de nada. · La huella es nombre+versión publicada, no el JSON entero: éste trae contadores que se mueven solos y harían «cambiar» un estado idéntico. · Un repo caído NO se resume en «al día»: lo dice, sale ≠ 0 y conserva el último estado bueno. Probado en los tres caminos contra un repo servido por HTTP en la caja.
85 lines
4.2 KiB
Bash
Executable File
85 lines
4.2 KiB
Bash
Executable File
#!/bin/sh
|
|
# avisar-actualizaciones.sh — pregunta al repo si hay novedades para lo que esta máquina tiene
|
|
# instalado, y AVISA sólo cuando la respuesta cambia.
|
|
#
|
|
# ── QUÉ HACE Y QUÉ NO ───────────────────────────────────────────────────────────────────────────
|
|
# Corre `takana outdated --json` (que no construye, no baja `.swm` y no escribe nada: lee el índice
|
|
# firmado y la DB de instalados) y deja el resultado en un fichero de estado. **No actualiza nada**:
|
|
# instalar es reproducir y verificar, y eso no se cuelga de un cron. El aviso dice qué correr.
|
|
#
|
|
# ── POR QUÉ SÓLO AVISA CUANDO CAMBIA ────────────────────────────────────────────────────────────
|
|
# Un guardián que grita lo mismo cada 30 minutos se deja de leer, y entonces no avisa de nada — es
|
|
# la lección que ya está escrita en `vigia-subcomandos.py` ([[guardianes-el-ruido-los-mata]]).
|
|
# Así que el ciclo normal es una línea sola («al día», o «3 novedades, las mismas de antes») y el
|
|
# grito sale cuando la LISTA de novedades difiere de la del ciclo anterior.
|
|
#
|
|
# ── LA FIRMA NO ES OPCIONAL ACÁ ─────────────────────────────────────────────────────────────────
|
|
# Se pasa `--trust`: decidir «hay que actualizar» a partir de un catálogo que nadie firmó es la
|
|
# forma exacta de que te instalen lo que quieran. `outdated` aborta si el índice está manipulado
|
|
# (BAD-SIG) e informa si está sin firmar o firmado por una clave desconocida — ese informe sale en
|
|
# el log y en el estado, no se traga.
|
|
#
|
|
# Uso: scripts/servidor/avisar-actualizaciones.sh
|
|
# */30 * * * * cd /work/sergio/takana && scripts/servidor/avisar-actualizaciones.sh \
|
|
# >>work/avisar-actualizaciones.log 2>&1
|
|
#
|
|
# Env: REPO origen(es) del catálogo — dir local o lista HTTP(S) separada por comas
|
|
# DB DB de instalados (def /var/lib/hammer/installed.json)
|
|
# TRUST directorio de claves de confianza (def ./trust)
|
|
# STATE fichero de estado (def work/actualizaciones.json)
|
|
# TAKANA binario (def ./target/release/takana)
|
|
set -eu
|
|
ROOT="$(cd "$(dirname "$0")/../.." && pwd)"; cd "$ROOT"
|
|
|
|
REPO="${REPO:-https://repo.gioser.net}"
|
|
DB="${DB:-/var/lib/hammer/installed.json}"
|
|
TRUST="${TRUST:-$ROOT/trust}"
|
|
STATE="${STATE:-$ROOT/work/actualizaciones.json}"
|
|
TAKANA="${TAKANA:-$ROOT/target/release/takana}"
|
|
|
|
[ -x "$TAKANA" ] || { echo "!! falta $TAKANA (cargo build --release)" >&2; exit 2; }
|
|
mkdir -p "$(dirname "$STATE")"
|
|
|
|
NUEVO="$STATE.nuevo"
|
|
if ! "$TAKANA" outdated --repo "$REPO" --db "$DB" --trust "$TRUST" --json >"$NUEVO" 2>"$NUEVO.err"; then
|
|
# Un repo caído o un índice manipulado NO se resume en «al día»: se dice, y se deja el estado
|
|
# anterior intacto para no borrar la última respuesta buena.
|
|
echo "$(date -u +%FT%TZ) ✗ no pude preguntarle al repo $REPO:"
|
|
sed 's/^/ /' "$NUEVO.err"
|
|
rm -f "$NUEVO" "$NUEVO.err"
|
|
exit 1
|
|
fi
|
|
rm -f "$NUEVO.err"
|
|
|
|
# La huella es la LISTA de novedades (nombre+versión publicada), no el JSON entero: el JSON trae
|
|
# contadores que se mueven solos (p.ej. `no_instalados`) y harían «cambiar» un estado idéntico.
|
|
huella() { [ -f "$1" ] || { echo ""; return; }; python3 - "$1" <<'PY'
|
|
import json, sys
|
|
try:
|
|
d = json.load(open(sys.argv[1]))
|
|
except Exception:
|
|
print(""); raise SystemExit
|
|
print(",".join(sorted(f"{n['name']}={n['publicada']}" for n in d.get("novedades", []))))
|
|
PY
|
|
}
|
|
|
|
ANTES="$(huella "$STATE")"
|
|
AHORA="$(huella "$NUEVO")"
|
|
mv "$NUEVO" "$STATE"
|
|
|
|
N=$(python3 -c 'import json,sys; print(len(json.load(open(sys.argv[1])).get("novedades",[])))' "$STATE")
|
|
SELLO="$(date -u +%FT%TZ)"
|
|
|
|
if [ "$N" = "0" ]; then
|
|
echo "$SELLO · al día contra $REPO"
|
|
exit 0
|
|
fi
|
|
|
|
if [ "$AHORA" = "$ANTES" ]; then
|
|
echo "$SELLO · $N novedad(es), las mismas del ciclo anterior (estado en $STATE)"
|
|
exit 0
|
|
fi
|
|
|
|
echo "$SELLO ⚠ HAY $N NOVEDAD(ES) en $REPO:"
|
|
"$TAKANA" outdated --repo "$REPO" --db "$DB" --trust "$TRUST" | sed 's/^/ /'
|