servidor: el aviso periódico de novedades del repo, que sólo grita cuando cambia
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.
This commit is contained in:
Executable
+84
@@ -0,0 +1,84 @@
|
|||||||
|
#!/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/^/ /'
|
||||||
Reference in New Issue
Block a user