Sistema de hooks para Timeshift que respalda y restaura imágenes UKI (Unified Kernel Images) en sistemas con Btrfs + Secure Boot.
- Concepto
- Solución
- Plataformas Soportadas
- Instalación
- Desinstalación
- Uso
- Integración con pacman
- Soporte de kernel-install
- Configuración
- Detección de la partición de arranque
- Seguridad
- Depuración e Integración de Logs
- Desarrollo y CI
- Changelog
- Licencia
En sistemas Linux con systemd-boot y Secure Boot, las imágenes UKI (ficheros .efi) residen en la partición EFI System Partition (ESP).
El problema: Timeshift protege la raíz (/), pero la partición ESP queda fuera. Si restauras un snapshot antiguo, el kernel en / (módulos) y el UKI en la ESP (kernel binario) no coincidirán, impidiendo el arranque o el funcionamiento de módulos.
Nota: Este proyecto es específico para systemd-boot. Si usas GRUB, no necesitas estos hooks porque GRUB gestiona los kernels de forma diferente.
Este proyecto sincroniza los UKIs con los snapshots de Btrfs mediante hooks:
-
Backup Hook (
/etc/timeshift/backup-hooks.d/90-backup-uki)- Timeshift lo ejecuta después de crear el snapshot (
run_post_backup_hooks) y le exportaTS_SNAPSHOT_PATH - Detecta dinámicamente la ESP (verifica PARTTYPE para evitar USBs)
- Respalda UKIs en
/etc/timeshift/uki-backup/ - Escribe los UKIs dentro del snapshot recién creado vía
TS_SNAPSHOT_PATH(Btrfs:@/etc/timeshift/uki-backup/, rsync:localhost/etc/timeshift/uki-backup/— el árbol rsync de Timeshift vive en<snapshot>/localhost/), dejándolo autocontenido - Selectivo: solo copia UKIs que cambiaron (comparación SHA256 per-file)
- Purga UKIs obsoletos (
PRUNE_OLD_UKIS=true): cada snapshot viaja solo con el UKI del kernel actual (uname -r); elimina versiones anteriores y el preset clásicoarch-linux.efien máquinas conkernel-install - Purga la partición de arranque (
PRUNE_ESP_UKIS=true, v3.5): elimina los UKIs versionados cuyo kernel ya no está instalado.kernel-installno poda, así que sin esto/boot/EFI/Linuxacumula UKIs de kernels desinstalados y cada uno se convierte en una entrada más del menú de arranque de systemd-boot - Limpia archivos
.baky.sha256huérfanos automáticamente
- Timeshift lo ejecuta después de crear el snapshot (
-
Restore Hook (
/etc/timeshift/restore-hooks.d/90-restore-uki)- Se ejecuta después de restaurar
- Detecta y monta la ESP dinámicamente (incluso en entornos chroot/Live USB)
- Verifica espacio disponible antes de copiar
- Devuelve los UKIs a la ESP con copia atómica (
mktemp + mv) - Verifica integridad: calcula el SHA256 de cada UKI respaldado y lo compara con el checksum guardado (
<UKI>.efi.sha256); aborta si no coincide - Inteligente: salta archivos que ya son idénticos en el destino (evita escrituras innecesarias a la ESP)
| Distribución | Estado | Notas |
|---|---|---|
| Arch Linux / Manjaro / EndeavourOS | ✅ Verificado en hardware real | Incluye paquete AUR (timeshift-uki-hooks-git) |
| Debian 13 | 🟡 Verificado en VM (KVM) | Ciclo completo Btrfs snapshot → UKI autocontenido → restore in-system → arranque OK (systemd-boot + kernel-install/EFI/Linux). ⏳ Quedan Secure Boot firmado y el disparo automático |
| Ubuntu / Linux Mint / Pop!_OS | Ídem con apt | |
| Fedora | Ídem con dnf | |
| openSUSE | Ídem con zypper | |
| Void Linux | Ídem con xbps | |
| Alpine Linux | Ídem con apk (requiere bash) | |
| Gentoo y otros con systemd | Herramientas estándar GNU (util-linux, coreutils) |
Qué es realmente portable: los hooks solo usan bash + util-linux (findmnt, lsblk, mountpoint) + coreutils (sha256sum, stat, df). Lo específico de cada distribución se reduce a install.sh (que detecta el gestor de paquetes) y al empaquetado: el núcleo —los dos hooks— no toca ningún gestor de paquetes ni ningún init system, porque los ejecuta Timeshift vía run-parts. La CI cubre la lógica de ambos hooks (el backup contra un árbol temporal, el restore con namespaces + chroot), pero nunca una ESP real de verdad. Y lo que no se ha verificado en otras distribuciones es la integración completa, que es la parte específica de cada sistema (ver la limitación siguiente).
⚠️ Esta es una limitación abierta, no un problema conocido con solución. Queinstall.shsepa manejar seis gestores de paquetes no demuestra que los hooks funcionen en esas distribuciones. Arch Linux se ha probado en una máquina real, de principio a fin (snapshot → UKIs dentro del snapshot → restauración → arranque correcto) y Debian 13 en una VM KVM (ver "Estado de la verificación" al final de esta sección). En el resto no se ha ejecutado ni un ciclo completo de backup/restore.
Estado de la verificación (2026-10-08) — Debian 13 en VM KVM: ciclo completo Btrfs verificado con Timeshift 26.09 (fork linuxmint) y layout @/@home + subvolúmenes independientes para /var/log y /var/cache/apt: snapshot → UKIs escritos dentro del snapshot (@/etc/timeshift/uki-backup/) → timeshift --restore desde el sistema en marcha → arranque correcto. De las 4 condiciones de abajo, la verificación cumple la 1 y la 3 (arranque tras restaurar, y kernel-install con layout EFI/Linux en Debian 13 + systemd-boot). Siguen abiertas la 2 y la 4: fuera de Arch no hay disparo automático documentado (los hooks solo corren con timeshift --create/--restore manuales) y el UKI restaurado no se ha probado firmado con las claves de Secure Boot de la máquina (el OVMF de la VM no enrula esas claves). Esta verificación además encontró dos bugs reales, ya corregidos en este repo (ver CHANGELOG): el restore hook cogía los UKIs del sistema vivo en vez del snapshot en el restore Btrfs in-system, y el backup hook no reconocía el layout rsync de Timeshift.
Para levantar esta limitación, una distribución debe cumplir las cuatro condiciones:
- Que el sistema arranque después de restaurar, no solo que el script termine sin error. Un restore correcto en el log puede dejar en la ESP un UKI que no arranca; es el fallo que no se vería en una prueba de scripts.
- Que algo dispare los hooks. El único automatismo documentado es
00-timeshift-autosnap.hook, un hook de pacman (Integración con pacman). Fuera de Arch no hay equivalente documentado, así que los hooks quedan instalados pero nadie los invoca. Untimeshift --createmanual sí los ejecuta; lo que falta es el disparo automático previo a una actualización de paquetes. kernel-installcon layoutEFI/Linux. La lógica de purga asume UKIs con nombre<machine-id>-<kver>.efi; ese es el layout dekernel-installen todas las distribuciones, pero casi solo Arch lo usa por defecto.- Secure Boot con las claves de esa máquina: firmar el UKI restaurado contra las claves de
db(shim + MOK en Debian/Fedora,sbctlen Arch).
Lo que sí es independiente de la distribución, y por eso no es descartable que funcione tal cual: las rutas /etc/timeshift/*-hooks.d/ son las del propio Timeshift upstream, y /usr/lib/modules/<kver> es igual en Arch, Fedora, Debian 12+, openSUSE y Void.
El acoplamiento real del proyecto no es a una distribución, sino al gestor de arranque: presupone UKIs en
EFI/Linuxsobre vfat, es decir systemd-boot. Con GRUB no hay UKIs que respaldar en ninguna distribución.
Init systems: los hooks no dependen del init system (los ejecuta Timeshift vía run-parts); el restore hook está pensado para funcionar también desde un chroot en un Live USB.
yay -S timeshift-uki-hooks-gitMantiene los hooks bajo propiedad de pacman: los actualiza con el sistema y no se descuadran.
git clone https://github.com/jairogaleano/timeshift-uki-hooks.git
cd timeshift-uki-hooks
sudo ./install.sh
install.shdeja los ficheros fuera del gestor de paquetes. Si ya tienes el paquete AUR instalado, no lo mezcles: o actualizas el paquete, o no usas el instalador. De lo contrario,pacman -Qkkmarcará los hooks como alterados y una actualización del paquete los sobrescribirá sin avisar.
- Detecta tu distribución y gestor de paquetes.
- Verifica e instala dependencias faltantes (
util-linux,coreutils). - Comprueba el entorno: avisa si Timeshift no está instalado, si no hay ningún
EFI/Linuxen las vfat montadas, o si los hooks actuales pertenecen a un paquete. - Crea los directorios de hooks en
/etc/timeshift/. - Limpia versiones anteriores.
- Instala los scripts con nombres canónicos para compatibilidad con
run-parts. - Aplica permisos de ejecución.
- Timeshift instalado y configurado
- Btrfs como sistema de archivos raíz
- systemd-boot como gestor de arranque
- Secure Boot habilitado (opcional pero recomendado)
sudo rm -f /etc/timeshift/backup-hooks.d/90-backup-uki
sudo rm -f /etc/timeshift/restore-hooks.d/90-restore-uki
sudo rm -rf /etc/timeshift/uki-backup/ls /etc/timeshift/backup-hooks.d/
ls /etc/timeshift/restore-hooks.d/
# No deben mostrar archivos 90-backup-uki ni 90-restore-ukiNota: Los directorios
/etc/timeshift/backup-hooks.d/y/etc/timeshift/restore-hooks.d/pueden quedarse vacíos. Timeshift los ignora si están vacíos.
Una vez instalados, los hooks se ejecutan automáticamente:
-
Al crear un snapshot (manual o programado):
- Timeshift crea el snapshot y luego ejecuta
90-backup-uki(run_post_backup_hooks) - El hook escribe los UKIs en
/etc/timeshift/uki-backup/y dentro del snapshot recién creado (víaTS_SNAPSHOT_PATH) - El snapshot queda autocontenido: UKIs y módulos del kernel coincidentes
- Timeshift crea el snapshot y luego ejecuta
-
Al restaurar un snapshot:
- Timeshift ejecuta
90-restore-ukidespués de restaurar - Los UKIs se devuelven a la ESP
- El sistema queda consistente y arrancable
- Timeshift ejecuta
En Arch Linux, el hook 00-timeshift-autosnap.hook (incluido en el paquete timeshift) crea un snapshot automáticamente antes de cada actualización de paquetes. Nuestros hooks se ejecutan dentro de ese ciclo:
sudo pacman -Syu
└─ 00-timeshift-autosnap.hook (pre-transacción)
└─ timeshift-autosnap
└─ timeshift --create
├─ Snapshot creado
└─ 90-backup-uki (después del snapshot)
├─ Copia UKIs de ESP → /etc/timeshift/uki-backup/
└─ Copia UKIs de ESP → snapshot (TS_SNAPSHOT_PATH)
Configuración (/etc/timeshift-autosnap.conf):
| Parámetro | Valor por defecto | Descripción |
|---|---|---|
skipAutosnap |
false |
Saltar la creación automática de snapshots |
deleteSnapshots |
true |
Eliminar snapshots antiguos automáticamente |
maxSnapshots |
3 |
Cantidad máxima de snapshots a conservar |
minHoursBetweenSnapshots |
18 |
Horas mínimas entre snapshots consecutivos |
Restaurar un snapshot (el restore hook se ejecuta automáticamente):
sudo timeshift --restoreLos UKIs se devuelven a la ESP y el sistema queda consistente y arrancable.
⚠️ Este automatismo es específico de Arch:00-timeshift-autosnap.hookes un hook de pacman y no tiene equivalente documentado en otras distribuciones. Fuera de Arch los hooks sí se ejecutan contimeshift --createytimeshift --restoremanuales, pero nada los dispara antes de actualizar el sistema. Ver la limitación conocida.
# Ver logs en tiempo real
tail -f /var/log/timeshift.log
# Verificar que los hooks están instalados
ls -la /etc/timeshift/backup-hooks.d/90-backup-uki
ls -la /etc/timeshift/restore-hooks.d/90-restore-uki
# Verificar respaldo actual
ls -la /etc/timeshift/uki-backup/
# Verificar ESP montada
findmnt -t vfatFlujo automático (con timeshift-autosnap):
# pacman crea el snapshot automáticamente antes de actualizar
sudo pacman -Syu
# 1. timeshift-autosnap hook se ejecuta (pre-transacción)
# 2. Se crea el snapshot
# 3. 90-backup-uki escribe los UKIs dentro del snapshot (autocontenido)
# 4. Se instalan las actualizaciones
# Si algo sale mal después de la actualización:
sudo timeshift --restore
# 5. 90-restore-uki devuelve los UKIs de ese snapshot a la ESP
# 6. ReiniciarFlujo manual:
# 1. Crear snapshot (el hook se ejecuta automáticamente)
sudo timeshift --create --comments "Antes de actualizar kernel"
# 2. Actualizar sistema
sudo pacman -Syu
# 3. Si algo sale mal, restaurar
sudo timeshift --restore
# 4. El hook restaura los UKIs automáticamente
# 5. Reiniciar y verificar que todo funcionaDesde v3.2 los hooks soportan de forma completa la arquitectura kernel-install de systemd, además de los presets clásicos de mkinitcpio.
Con kernel-install (layout uki, configurado en /etc/kernel/install.conf) los UKIs se generan con nombres versionados y se instalan en $BOOT/EFI/Linux/:
$BOOT/EFI/Linux/<machine-id>-<kernel-version>.efi
Ejemplo (Arch + Secure Boot):
/boot/EFI/Linux/c2224fefe655409688eccb14500b0429-7.1.6-arch1-1.efi
Como coexisten múltiples versiones de kernel a la vez, al restaurar un snapshot antiguo es crítico que la partición de arranque quede exactamente igual que cuando se tomó ese snapshot:
- El backup hook (v3.3) respalda solo el UKI vigente — el del kernel actualmente en ejecución (
uname -r) — y lo escribe dentro del snapshot recién creado (víaTS_SNAPSHOT_PATH), dejando cada snapshot autocontenido. ConPRUNE_OLD_UKIS=true(por defecto) además purga del respaldo los.efique no corresponden al kernel actual: versiones anteriores de UKIs kernel-install y el preset clásicoarch-linux.efi(legacy tras el cambio a kernel-install). Así cada snapshot viaja solo con su UKI y al restaurar se devuelve exactamente el del momento de la snapshot. - El backup hook (v3.5) purga además la propia partición de arranque:
kernel-installno borra los UKIs de kernels que se desinstalan, así que/boot/EFI/Linuxse llenaba de UKIs muertos que, con autodetección de systemd-boot, aparecían como entradas extra en el menú de arranque. ConPRUNE_ESP_UKIS=true(por defecto) se eliminan los UKIs versionados<machine-id>-<kver>.eficuyo kernel ya no tiene/usr/lib/modules/<kver>. Nunca toca el UKI del kernel en ejecución, ni los de kernels instalados que no estén en ejecución, ni el preset clásicoarch-linux.efi(sin versión en el nombre no hay nada que decidir). - El restore hook (v3.3) hace sync inverso: además de copiar los UKIs del snapshot, elimina de la partición de arranque cualquier
.efique no esté en el snapshot (PRUNE_UKIS=true). Si quedara un UKI de un kernel más nuevo (cuyos módulos ya no existen en el root restaurado), el sistema fallaría al arrancar — exactamente el problema de "unknown filesystem type" tras una actualización.
Nota sobre el nombre del kernel:
uname -res el kernel en ejecución, no el instalado. Si actualizas el kernel y no reinicias, una segunda transacción de pacman creará un snapshot cuyo UKI de respaldo es el del kernel antiguo. El UKI nuevo no se pierde (está en la ESP y se regenera conkernel-install), pero ese snapshot no lo incluye.
Para desactivar la limpieza:
- Restore: edita
/etc/timeshift/restore-hooks.d/90-restore-ukiy ponPRUNE_UKIS=false. - Backup (respaldo): edita
/etc/timeshift/backup-hooks.d/90-backup-ukiy ponPRUNE_OLD_UKIS=false(conserva todos los UKIs versionados acumulados enuki-backup/). - Backup (ESP): pon
PRUNE_ESP_UKIS=false(conserva en/boot/EFI/Linuxlos UKIs de kernels desinstalados).
XBOOTLDR: en sistemas con
/booten una partición XBOOTLDR independiente (ej. dual-boot Windows + Arch), los hooks la detectan y validan por su GUID (bc13c2ff-...) igual que la ESP.
kernel-install no regenera el UKI solo con un pacman -Syu: necesita su propio hook de pacman. Si usas kernel-install como generador:
# Opción A: hook de kernel-install para pacman (AUR), enmascarando los de mkinitcpio
yay -S pacman-hook-kernel-install
sudo ln -s /dev/null /etc/pacman.d/hooks/60-mkinitcpio-remove.hook
sudo ln -s /dev/null /etc/pacman.d/hooks/90-mkinitcpio-install.hook
# Opción B: regenerar manualmente tras cada actualización de kernel
sudo kernel-install --boot-path=/boot --esp-path=/efi add \
"$(cat /usr/lib/modules/*/version | head -1)" /boot/vmlinuz-linuxAlternativa simple: el preset de mkinitcpio con
default_uki=(p. ej./boot/EFI/Linux/arch-linux.efi) sigue siendo el camino documentado y se regenera solo en cadapacman -Syu. Los hooks funcionan igual en ambos casos.
Ambos hooks se configuran editando las constantes de la cabecera de cada script.
| Variable | Hook | Por defecto | Efecto |
|---|---|---|---|
PRUNE_OLD_UKIS |
backup | true |
Purga del respaldo los UKIs que no son del kernel en ejecución |
PRUNE_ESP_UKIS |
backup | true |
Purga de la partición de arranque los UKIs cuyo kernel ya no está instalado |
PRUNE_UKIS |
restore | true |
Sincroniza la partición de arranque con el snapshot restaurado (elimina los UKIs que no están en él) |
ESP_MIN_FREE_MB |
restore | 20 |
Margen libre que se quiere dejar en la ESP tras restaurar |
El espacio necesario se calcula en tiempo de ejecución como ESP_MIN_FREE_MB + el tamaño de los UKIs a restaurar. Hasta v3.4 era un umbral fijo de 50 MB, menor que un UKI típico (~75 MB), por lo que el aviso de "poco espacio" no podía dispararse cuando realmente no cabía.
Variables de entorno (para pruebas; no hace falta tocarlas en producción):
| Variable | Por defecto | Para qué |
|---|---|---|
TSUKI_UKI_DIR |
detección automática | Fuerza el directorio de UKIs de origen |
TSUKI_BACKUP_DIR |
/etc/timeshift/uki-backup |
Fuerza el directorio de respaldo |
TSUKI_LOG_FILE |
/var/log/timeshift.log |
Fuerza el archivo de log |
TS_SNAPSHOT_PATH lo exporta Timeshift; el hook también lo acepta a mano.
Ambos hooks tienen que decidir en qué partición están los UKIs. No hay variable de configuración para eso: se detecta sola, y conviene entender cómo, porque es la misma regla en los dos hooks desde v3.6.
Una partición es candidata si está montada y tiene el directorio EFI/Linux. La presencia de ese directorio es la evidencia: es donde kernel-install deja los UKIs, así que existe en la partición correcta y no en la otra.
| Backup hook | Restore hook | |
|---|---|---|
Rutas estándar (/boot, /efi, /boot/efi) |
primera montada con EFI/Linux |
primera montada con EFI/Linux |
| Otras vfat montadas | EFI/Linux + PARTTYPE ESP/XBOOTLDR |
EFI/Linux + PARTTYPE ESP/XBOOTLDR |
| Crea el directorio si falta | no | no |
El PARTTYPE (ESP c12a7328 o XBOOTLDR bc13c2ff) solo se exige cuando hay que buscar fuera de las rutas estándar, que es donde cabe confundirse con un USB. En las rutas estándar basta con EFI/Linux.
Si ninguna partición candidata, los hooks lo dicen y no escriben nada. En el restore significa que la restauración no se lleva a cabo; no significa que los UKIs acaben en una partición cualquiera.
Hasta v3.5 el restore hook aceptaba el primer punto de montaje de /boot, /efi, /boot/efi sin comprobar nada, y creaba el destino con mkdir -p. En una máquina con una sola partición de arranque eso es inofensivo, pero en el layout habitual de dual-boot era un bug:
p1 ESP c12a7328 → /efi ← Windows (o el bootloader), SIN UKIs
p5 XBOOTLDR bc13c2ff → /boot ← los UKIs de Linux aquí
Si /boot no estaba montado, el hook caía en /efi, creaba /efi/EFI/Linux y escribía ahí los UKIs. Los UKIs reales de /boot no se restauraban y, con PRUNE_UKIS=true, además se borraba de esa partición los .efi que no estuvieran en el respaldo. El error se persistía, porque el mkdir -p dejaba el directorio ya creado para la siguiente ejecución.
El caso más probable era el chroot de un Live USB (el "peor caso" que describe ARCHITECTURE.md): ahí es fácil montar
/efipor costumbre y olvidar/boot.
Cómo comprobar tu máquina:
# La partición de los UKIs es la única vfat con un EFI/Linux con contenido
findmnt -t vfat -o TARGET,SOURCE
sudo ls -d /boot/EFI/Linux /efi/EFI/Linux 2>&1 # solo debe existir el correctoLos UKIs en uso son los que tienen el nombre <machine-id>-<uname -r>.efi (compruébalo con uname -r).
Si el hook aborta con No se encontro la particion de arranque, casi siempre es que la partición de los UKIs no está montada: móntala y vuelve a lanzar el restore. Si está realmente vacía, crea el directorio a mano (mkdir -p <mnt>/EFI/Linux); el hook no lo hace por ti precisamente para no dejar UKIs donde no había ninguno.
- ✅ Integridad: Verificación SHA256 obligatoria antes de restaurar.
- ✅ Atomicidad: Uso de copias temporales y
mvpara evitar archivos corruptos. - ✅ Secure Boot: No modifica firmas; solo preserva los binarios ya firmados.
- ✅ Detección de ESP: Verifica PARTTYPE para no confundir con USBs.
El backup hook escribe junto a cada UKI un archivo de checksum con el nombre completo del UKI más el sufijo .sha256 (p. ej. arch-linux.efi.sha256, c2224fef-...-7.1.8-arch1-3.efi.sha256). Contiene únicamente el hash SHA-256 en hex (sin nombres de archivo):
sha256sum /boot/EFI/Linux/c2224fef-*-7.1.8-arch1-3.efi | awk '{print $1}'
# se guarda en /etc/timeshift/uki-backup/<mismo-nombre>.efi.sha256El restore hook, antes de copiar, calcula el SHA-256 de cada UKI respaldado y lo compara con su .sha256:
- Si coincide → restaura (o salta si el destino ya es idéntico, evitando escrituras innecesarias a la ESP).
- Si no coincide → aborta con
ERROR: Checksum falló(posible corrupción del respaldo). - Si falta el
.sha256→ avisa conWARNy continúa sin verificación (no bloquea la restauración).
Los .sha256 huérfanos (sin su UKI asociado) se limpian automáticamente en cada ejecución del hook.
Este proyecto se integra directamente con el sistema de registros de Timeshift para facilitar el mantenimiento y la visibilidad:
- Logs Unificados: Los mensajes de los hooks se inyectan en
/var/log/timeshift.log. Esto permite ver en un solo lugar tanto las acciones de Timeshift como el estado de la sincronización de los UKIs. - Rotación Automática: Timeshift gestiona internamente la limpieza y rotación de estos logs (manteniendo las últimas sesiones). Al integrarse aquí, los registros de este proyecto se depuran automáticamente, evitando el crecimiento indefinido de archivos en
/var/log.
| Síntoma | Causa probable | Solución |
|---|---|---|
| "No se pudo detectar la ESP" | USB conectado o ESP no montada | Desconectar USB o montar ESP manualmente |
| "Checksum falló" | UKI corrupto en respaldo | Verificar integridad del SSD con SMART |
| "Espacio insuficiente" | ESP casi llena | Con v3.5 el aviso calcula el espacio real que hace falta; si aparece, PRUNE_ESP_UKIS ya habrá liberado los UKIs de kernels desinstalados |
| Hooks no se ejecutan | Permisos incorrectos | chmod +x /etc/timeshift/*-hooks.d/90-*-uki |
| Tras restaurar, el sistema no arranca o el UKI no está donde debería | El restore hook eligió otra vfat montada (dual-boot) | Ver Detección de la partición de arranque |
| El menú de arranque ofrece kernels viejos | UKIs acumulados en la ESP | Con v3.5 se purgan solos en el siguiente snapshot; para hacerlo ya: rm manual de /boot/EFI/Linux/<machine-id>-<kver>.efi de kernels ya desinstalados |
Si tu ESP se monta con
dmask=0077(opción habitual desystemd-fstab-generatoren vfat), los UKIs quedan en700y solo root puede leerlos, también dentro de/etc/timeshift/uki-backup/. No afecta a los hooks (se ejecutan como root), pero impide comprobarlos a mano con tu usuario normal.
Cada push a main ejecuta .github/workflows/ci.yml, que comprueba:
- Sintaxis:
bash -nsobreinstall.sh, ambos hooks y los dos tests. - ShellCheck en nivel
warning(sin excepciones: el nivel está limpio). - Coherencia de versión:
VERSIONes la única fuente de verdad y la CI falla siREADME.md,CHANGELOG.md, ambos hooks oinstall.shno la declaran igual. Antes la versión se escribía a mano en cada fichero y ya había derivado (el título del README se quedó en v3.2 mientras el código iba por v3.4). - Tests:
tests/smoke.sh(backup hook) +tests/boot-partition.sh(restore hook), que es el que invoca al primero.
./tests/smoke.sh # ejecuta los dostests/smoke.sh ejecuta el backup hook de verdad contra un árbol temporal (mktemp -d) usando las variables TSUKI_*, sin tocar el sistema. Comprueba la purga de la ESP, el respaldo selectivo con su .sha256, los dos layouts de snapshot (Btrfs y rsync) y la idempotencia de una segunda ejecución.
tests/boot-partition.sh cubre el restore hook, que hasta v3.5 no tenía ninguna cobertura. La clave es que no necesita una ESP real ni privilegios: se re-ejecuta dentro de un namespace de usuario (unshare -r -m -p -f) y levanta un chroot mínimo con bind mounts de /usr, /dev, /sys y /proc. Dentro, /boot y /efi se pueden montar o dejar como directorios planos, así que se ejercita el código de producción tal cual, sin variables de entorno ni seams que solo usen los tests. Cubre 9 casos: los cuatro layouts de partición con UKIs en /boot y/o /efi, los dos escenarios de dual-boot con la partición de los UKIs sin montar (justo los que fallaban antes del fix), el caso "ninguna sirve" y tres de verificación de checksum.
Si las user namespaces estuvieran deshabilitadas, el test falla en lugar de saltarse en silencio: un test que se salta solo no es cobertura.
⚠️ La CI corre enubuntu-latesty eso no verifica ninguna distro. Es el único entorno donde el proyecto se ejecuta de forma automatizada, y no es el de producción: los hooks corren dentro de un chroot simulado, sobre un árbol de ficheros, nunca contra una ESP de verdad ni contra un arranque real. La CI no cuenta como verificación en hardware (ver la limitación conocida); de hecho, los dos fallos que corrigió elchrootdeboot-partition.sheran típicos de Ubuntu (/lib64como directorio real en vez de symlink, yawkdesapareciendo al no existir/etc/alternatives).
Para el historial completo de cambios, ver CHANGELOG.md.
- El restore hook elegía la partición de arranque equivocada (bug, no solo fragilidad). Aceptaba el primer mountpoint de
/boot,/efi,/boot/efisin comprobar nada y creaba el destino conmkdir -p. Con dos vfat montadas y solo una con UKIs (dual-boot), si la partición correcta no estaba montada, escribía los UKIs en la otra y —conPRUNE_UKIS=true— además borraba de ella los.efique no estuvieran en el respaldo. Elmkdir -phacía que el error quedara "confirmado". Ahora una partición solo es candidata si está montada y tieneEFI/Linux, y si no hay ninguna el hook aborta con un error claro en vez de adivinar. Era justo lo que documentamos como limitación en v3.5. - Los dos hooks ya no pueden apuntar a particiones distintas: ambos aplican la misma regla (
is_boot_partition_dir). - Un
.sha256con formatosha256sumabortaba la restauración: se comparaba el fichero entero contra el hash pelado, así que un.sha256con formato<hash> <nombre>fallaba siempre aunque el hash fuese correcto. Ahora se aceptan ambos formatos. - El fallback que monta particiones ya no acepta la primera que se monte bien: se reintenta la resolución y solo se usa lo que la validación acepte.
- El restore hook por fin tiene cobertura automática (
tests/boot-partition.sh, 9 casos): namespace de usuario + chroot mínimo con bind mounts, sin necesitar una ESP real ni privilegios. Simula los cuatro layouts de partición y los dos escenarios de dual-boot que fallaban antes.
PRUNE_ESP_UKIS(backup hook): purga de la partición de arranque los UKIs versionados cuyo kernel ya no está instalado.kernel-installno los borra nunca, así que/boot/EFI/Linuxacumulaba UKIs de kernels desinstalados que, con autodetección de systemd-boot, se colgaban como entradas extra en el menú de arranque.- Espacio de la ESP calculado en tiempo de ejecución (restore hook): antes era un umbral fijo de 50 MB, menor que un UKI típico (~75 MB), así que el aviso de "poco espacio" no podía dispararse. Ahora es
ESP_MIN_FREE_MB(20) + el tamaño de los UKIs a restaurar. - Hooks parametrizables por entorno (
TSUKI_UKI_DIR,TSUKI_BACKUP_DIR,TSUKI_LOG_FILE): permiten ejecutar el backup hook contra un árbol de pruebas sin tocar el sistema. - CI (
.github/workflows/ci.yml):bash -n, ShellCheck sin excepciones, coherencia de versión contraVERSIONy smoke test del backup hook (tests/smoke.sh). Antes el repositorio no tenía ninguna verificación automática. VERSION: fichero único de versión; la CI obliga a que todos los ficheros lo declaren igual.- Los hooks de git eran
644(no ejecutables en un clon recién hecho). Ahora son755. - Restauración: los UKIs se conservan dentro de cada snapshot, así que el directorio de respaldo ocupa ~75 MB adicionales por snapshot (Btrfs no deduplica entre subvolúmenes).
- Correcciones de documentación: título del README sincronizado con la versión real, tabla de plataformas ajustada a lo que está probado,
ARCHITECTURE.mdy este README alineados conPRUNE_OLD_UKIS, y eliminas dos afirmaciones que no se correspondían con el código (la detección de ESP por PARTTYPE sí existe en el restore hook, yIN_CHROOTnunca se usó para ajustar rutas). install.sh: comprueba que Timeshift esté instalado y que exista unEFI/Linuxen alguna vfat, y avisa si los hooks actuales pertenecen a un paquete (su uso deja los ficheros fuera del gestor de paquetes y hace quepacman -Qkklos marque como alterados).
trap cleanup EXITen el restore hook: si el script aborta a mitad (checksum falla, cp falla, signal), el trap restaura el modo RO de la ESP y la desmonta si fue montada manualmente. Evita que la ESP quede montada RW o colgada.skip_pruneencopy_ukis(): la segunda llamada (snapshot) omite la purge de obsoletos, evitando I/O innecesario.
PRUNE_OLD_UKIS(backup hook): cada snapshot viaja solo con el UKI del sistema actual (uname -r). Con layoutkernel-installse eliminan del respaldo los.efiobsoletos (versiones anteriores y el preset clásicoarch-linux.efilegacy), evitando que UKIs de kernels viejos se acumulen en las snapshots y sean devueltos a la partición de arranque al restaurar. Configurable conPRUNE_OLD_UKIS.- Fix en el restore hook: la ruta del checksum apuntaba a
"<UKI>".sha256en lugar de"<UKI>.efi.sha256", por lo que la verificación de integridad y el "salto si idéntico" nunca se ejecutaban. Corregido.
Este software está bajo la licencia GNU General Public License v3.0.
Contribuciones: Proyecto mantenido por Jairo Galeano.