feat: better updater

This commit is contained in:
2026-03-22 16:41:16 +01:00
parent b8e505af29
commit 53a4a7eb88
5 changed files with 424 additions and 34 deletions

View File

@@ -4,6 +4,18 @@ let
username = "usuario";
learningml-desktop = pkgs.callPackage ./pkgs/learningml-desktop.nix { };
andaredConnectScript = pkgs.writeShellScriptBin "andared-connect" (builtins.readFile ./andared-connect.sh);
labUpdateMonitor = pkgs.writeShellScriptBin "lab-update-monitor" (builtins.readFile ./update-monitor.sh);
labUpdateLauncher = pkgs.writeShellScriptBin "lab-update-launcher" (builtins.readFile ./update-launcher.sh);
labUpdateDesktop = pkgs.makeDesktopItem {
name = "lab-updates";
desktopName = "Actualizaciones del laboratorio";
genericName = "Registro y lanzador de actualizaciones";
comment = "Sigue la actualizacion activa, revisa registros o lanza una nueva actualizacion";
exec = "lab-update-launcher";
icon = "system-software-update";
terminal = false;
categories = [ "System" "Settings" ];
};
in
{
imports =
@@ -222,7 +234,14 @@ in
learningml-desktop
wireshark
andaredConnectScript
labUpdateMonitor
labUpdateLauncher
labUpdateDesktop
xdg-user-dirs
libnotify
util-linux
kdePackages.kdialog
kdePackages.konsole
libreoffice-qt
hunspell
hunspellDicts.es_ES
@@ -334,7 +353,7 @@ in
};
systemd.services.lab-updater = {
description = "actualizaciones automáticas del sistema";
path = with pkgs; [ git nix coreutils kdePackages.kdialog libnotify config.system.build.nixos-rebuild ];
path = with pkgs; [ git nix coreutils systemd util-linux libnotify config.system.build.nixos-rebuild ];
serviceConfig = {
Type = "oneshot";
ExecStart = "/etc/nixos/update.sh";

View File

@@ -28,3 +28,10 @@ El sistema instalado deja preparada una conexión de NetworkManager para `Andare
- En Plasma, basta con abrir el selector de redes, pulsar `Andared_Corporativo` e introducir las credenciales del usuario.
- Alternativamente, desde terminal se puede usar `andared-connect` para que `nmcli` pida las credenciales de forma interactiva.
- Si `TTLS` no funciona en un centro concreto, prueba `andared-connect peap`.
## actualizaciones
- Se puede seguir lanzando la actualización manual con `su -c /etc/nixos/update.sh`. El script ahora guarda un registro en `/var/log/lab-updates` y envía la notificación final a la sesión activa de KDE.
- En el menú de aplicaciones de Plasma aparece `Actualizaciones del laboratorio`.
- Si ya hay una actualización en marcha, el lanzador se engancha automáticamente a su registro activo.
- Si no hay ninguna en curso, el lanzador permite iniciar una nueva actualización y seguir el registro, ver el último registro o abrir la carpeta con el historial.

77
update-launcher.sh Executable file
View File

@@ -0,0 +1,77 @@
#!/bin/bash
set -euo pipefail
monitor_bin="${LAB_UPDATE_MONITOR_BIN:-lab-update-monitor}"
pick_terminal() {
local candidate
for candidate in konsole xterm; do
if command -v "$candidate" >/dev/null 2>&1; then
printf '%s\n' "$candidate"
return 0
fi
done
return 1
}
open_terminal() {
local terminal="$1"
shift
case "$terminal" in
konsole)
exec "$terminal" --hold -e "$@"
;;
xterm)
exec "$terminal" -hold -e "$@"
;;
esac
}
main() {
local terminal choice log_dir
terminal="$(pick_terminal)" || {
if command -v kdialog >/dev/null 2>&1; then
kdialog --error "No se ha encontrado ningun terminal compatible para abrir los registros."
else
echo "No se ha encontrado ningun terminal compatible para abrir los registros." >&2
fi
exit 1
}
if "$monitor_bin" --has-active-update; then
open_terminal "$terminal" "$monitor_bin" --watch
fi
if command -v kdialog >/dev/null 2>&1; then
choice="$(
kdialog \
--title "Actualizaciones del laboratorio" \
--menu "Que quieres hacer?" \
run "Ejecutar actualizacion y seguir registro" \
watch "Ver el ultimo registro" \
folder "Abrir la carpeta de registros"
)" || exit 0
else
choice="run"
fi
case "$choice" in
run)
open_terminal "$terminal" "$monitor_bin" --run
;;
watch)
open_terminal "$terminal" "$monitor_bin" --watch
;;
folder)
log_dir="$("$monitor_bin" --print-log-dir)"
exec xdg-open "$log_dir"
;;
esac
}
main "$@"

200
update-monitor.sh Executable file
View File

@@ -0,0 +1,200 @@
#!/bin/bash
set -euo pipefail
log_dir=""
active_log_file=""
active_pid_file=""
latest_log_link=""
init_paths() {
if [ -d /var/log/lab-updates ]; then
log_dir="/var/log/lab-updates"
else
log_dir="${XDG_STATE_HOME:-$HOME/.local/state}/lab-updates"
fi
active_log_file="$log_dir/active.log"
active_pid_file="$log_dir/active.pid"
latest_log_link="$log_dir/latest.log"
}
is_pid_alive() {
local pid="${1:-}"
[ -n "$pid" ] && [ -d "/proc/$pid" ]
}
active_pid() {
if [ -r "$active_pid_file" ]; then
head -n 1 "$active_pid_file" 2>/dev/null || true
fi
}
active_log() {
local log=""
if [ -r "$active_log_file" ]; then
log="$(head -n 1 "$active_log_file" 2>/dev/null || true)"
if [ -n "$log" ] && [ -e "$log" ]; then
printf '%s\n' "$log"
return 0
fi
fi
return 1
}
latest_log() {
local log=""
if [ -L "$latest_log_link" ] && [ -e "$latest_log_link" ]; then
log="$(readlink -f "$latest_log_link" 2>/dev/null || true)"
fi
if [ -z "$log" ] || [ ! -e "$log" ]; then
log="$(ls -1t "$log_dir"/update-*.log 2>/dev/null | head -n 1 || true)"
fi
if [ -n "$log" ] && [ -e "$log" ]; then
printf '%s\n' "$log"
return 0
fi
return 1
}
has_active_update() {
local pid
pid="$(active_pid)"
is_pid_alive "$pid"
}
watch_log() {
local log=""
local follow=0
if has_active_update; then
log="$(active_log || true)"
follow=1
fi
if [ -z "$log" ]; then
log="$(latest_log || true)"
fi
if [ -z "$log" ]; then
echo "Todavia no hay registros de actualizaciones."
exit 1
fi
if [ "$follow" -eq 1 ]; then
echo "Siguiendo registro: $log"
tail -n 200 -F "$log"
else
echo "Mostrando el ultimo registro: $log"
tail -n 200 "$log"
fi
}
start_update_and_watch() {
local before_latest=""
local start_ts=""
local pkexec_pid=""
local log=""
if has_active_update; then
watch_log
return 0
fi
if ! command -v pkexec >/dev/null 2>&1; then
echo "No se ha encontrado pkexec para iniciar la actualizacion." >&2
exit 1
fi
before_latest="$(latest_log || true)"
start_ts="$(date +%s)"
echo "Solicitando permisos de administrador para iniciar la actualizacion..."
pkexec /etc/nixos/update.sh >/dev/null 2>&1 &
pkexec_pid="$!"
for _ in $(seq 1 180); do
if has_active_update; then
log="$(active_log || true)"
[ -n "$log" ] && break
fi
log="$(latest_log || true)"
if [ -n "$log" ] && [ "$log" != "$before_latest" ]; then
break
fi
if [ -n "$log" ] && [ "$(stat -c %Y "$log" 2>/dev/null || echo 0)" -ge "$start_ts" ]; then
break
fi
if [ ! -d "/proc/$pkexec_pid" ]; then
break
fi
sleep 1
done
if [ -z "$log" ]; then
wait "$pkexec_pid" || true
echo "No se ha podido encontrar el registro de la actualizacion."
exit 1
fi
echo "Siguiendo registro: $log"
tail -n 200 -F "$log"
}
usage() {
cat <<'EOF'
Uso: lab-update-monitor [--watch|--run|--run-or-watch|--has-active-update|--print-log-dir]
--watch Sigue la actualizacion en curso o el ultimo registro disponible.
--run Inicia una actualizacion con permisos de administrador y sigue su registro.
--run-or-watch Si hay una actualizacion activa, se engancha a ella; si no, inicia una.
--has-active-update Sale con codigo 0 si hay una actualizacion activa.
--print-log-dir Muestra la carpeta de registros.
EOF
}
main() {
init_paths
case "${1:---run-or-watch}" in
--watch)
watch_log
;;
--run)
start_update_and_watch
;;
--run-or-watch)
if has_active_update; then
watch_log
else
start_update_and_watch
fi
;;
--has-active-update)
has_active_update
;;
--print-log-dir)
printf '%s\n' "$log_dir"
;;
--help|-h)
usage
;;
*)
usage >&2
exit 1
;;
esac
}
main "$@"

153
update.sh Normal file → Executable file
View File

@@ -4,16 +4,6 @@ set -euo pipefail
cd "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# When running from a systemd timer/cron, set display variables
# so that kdialog/notify-send can reach the X11 session.
if [ -z "${DISPLAY:-}" ]; then
export DISPLAY=":0"
dbus_addr="$(systemctl --user show-environment 2>/dev/null | grep '^DBUS_SESSION_BUS_ADDRESS=' | cut -d= -f2-)" || true
if [ -n "${dbus_addr:-}" ]; then
export DBUS_SESSION_BUS_ADDRESS="$dbus_addr"
fi
fi
force_update=0
for arg in "$@"; do
case "$arg" in
@@ -27,43 +17,140 @@ for arg in "$@"; do
esac
done
timestamp="$(date +%Y%m%d-%H%M%S)"
log_dir="/var/log/lab-updates"
mkdir -p "$log_dir" 2>/dev/null || log_dir="${XDG_STATE_HOME:-$HOME/.local/state}/lab-updates"
mkdir -p "$log_dir"
lock_file="$log_dir/update.lock"
active_log_file="$log_dir/active.log"
active_pid_file="$log_dir/active.pid"
latest_log_link="$log_dir/latest.log"
timestamp="$(date +%Y%m%d-%H%M%S)"
log_file="$log_dir/update-$timestamp.log"
touch "$log_file"
chmod 0644 "$log_file"
exec 9>"$lock_file"
if ! flock -n 9; then
current_log=""
if [ -r "$active_log_file" ]; then
current_log="$(head -n 1 "$active_log_file" 2>/dev/null || true)"
fi
echo "Ya hay otra actualización en marcha."
if [ -n "$current_log" ]; then
echo "Registro activo: $current_log"
fi
exit 0
fi
printf '%s\n' "$log_file" > "$active_log_file"
printf '%s\n' "$$" > "$active_pid_file"
ln -sfn "$log_file" "$latest_log_link"
chmod 0644 "$active_log_file" "$active_pid_file"
exec > >(tee -a "$log_file") 2>&1
cleanup() {
local code="$1"
if [ -r "$active_pid_file" ] && [ "$(head -n 1 "$active_pid_file" 2>/dev/null || true)" = "$$" ]; then
rm -f "$active_log_file" "$active_pid_file"
fi
if [ "$code" -ne 0 ]; then
notify "Actualizacion fallida" "La actualizacion ha fallado (codigo $code)." error
fi
exit "$code"
}
desktop_user=""
desktop_uid=""
desktop_display=""
discover_desktop_session() {
local session uid user state type remote class display
while read -r session uid user _; do
[ -n "$session" ] || continue
state="$(loginctl show-session "$session" -p State --value 2>/dev/null || true)"
type="$(loginctl show-session "$session" -p Type --value 2>/dev/null || true)"
remote="$(loginctl show-session "$session" -p Remote --value 2>/dev/null || true)"
class="$(loginctl show-session "$session" -p Class --value 2>/dev/null || true)"
display="$(loginctl show-session "$session" -p Display --value 2>/dev/null || true)"
[ "$state" = "active" ] || continue
[ "$remote" = "no" ] || continue
[ "$class" = "user" ] || continue
case "$type" in
x11|wayland)
desktop_user="$user"
desktop_uid="$uid"
desktop_display="$display"
return 0
;;
esac
done < <(loginctl list-sessions --no-legend 2>/dev/null || true)
return 1
}
run_in_desktop_session() {
if [ -z "$desktop_user" ] || [ -z "$desktop_uid" ]; then
discover_desktop_session || return 1
fi
local -a env_vars=(
"XDG_RUNTIME_DIR=/run/user/$desktop_uid"
"DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$desktop_uid/bus"
"PATH=$PATH"
)
if [ -n "$desktop_display" ]; then
env_vars+=("DISPLAY=$desktop_display")
fi
runuser -u "$desktop_user" -- env "${env_vars[@]}" "$@"
}
notify() {
local title="$1"
local message="$2"
local mode="${3:-info}"
local full_message urgency
if command -v kdialog >/dev/null 2>&1 && { [ -n "${DISPLAY:-}" ] || [ -n "${WAYLAND_DISPLAY:-}" ]; }; then
case "$mode" in
reboot)
if kdialog \
--title "$title" \
--yes-label "Reiniciar ahora" \
--no-label "Más tarde" \
--yesno "$message\n\nRegistro: $log_file"; then
systemctl reboot
fi
;;
error)
kdialog --title "$title" --error "$message\n\nRegistro:\n$log_file"
;;
*)
kdialog --title "$title" --msgbox "$message\n\nRegistro: $log_file"
;;
esac
elif command -v notify-send >/dev/null 2>&1; then
notify-send -t 0 --urgency=critical "$title" "$message\nRegistro: $log_file"
printf -v full_message '%s\n\nRegistro: %s' "$message" "$log_file"
case "$mode" in
error)
urgency="critical"
;;
*)
urgency="normal"
;;
esac
if command -v notify-send >/dev/null 2>&1; then
run_in_desktop_session \
notify-send \
--app-name="Actualizaciones del laboratorio" \
--icon="system-software-update" \
--urgency="$urgency" \
-t 0 \
"$title" \
"$full_message" \
&& return 0
fi
printf '%s\n%s\n' "$title" "$full_message" >&2
}
trap 'code=$?; [ "$code" -eq 0 ] || notify "Actualizacion fallida" "La actualizacion ha fallado (codigo $code)." error' EXIT
trap 'cleanup "$?"' EXIT
trap 'exit 130' INT TERM
echo "Actualizando el sistema..."
echo "Guardando registro en: $log_file"
@@ -85,4 +172,4 @@ else
fi
nixos-rebuild switch --flake path:.#nixos
notify "Actualizacion completada" "La actualizacion del sistema ha terminado correctamente." reboot
notify "Actualizacion completada" "La actualizacion del sistema ha terminado correctamente. Reinicia el equipo cuando te venga bien."