Aggiunge backup no-GUI per scheduler

This commit is contained in:
2026-07-01 12:27:59 +02:00
parent 118f01f81a
commit d5bff3d8e4
5 changed files with 121 additions and 1 deletions

View File

@@ -46,6 +46,44 @@ python -m bakrest.tray_app
La tray app legge lo stato prodotto dal servizio e offre il comando `Backup e spegni`. Se il backup fallisce, lo spegnimento non viene eseguito. La tray app legge lo stato prodotto dal servizio e offre il comando `Backup e spegni`. Se il backup fallisce, lo spegnimento non viene eseguito.
## Backup senza GUI per Task Scheduler
Lo stesso entry point della tray puo' eseguire il backup senza mostrare icone o finestre:
```powershell
python -m bakrest.tray_app --nogui
```
Per testare senza spegnere il PC:
```powershell
python -m bakrest.tray_app --nogui --no-shutdown
```
In alternativa si puo' chiamare direttamente il modulo dedicato:
```powershell
python -m bakrest.backup_nogui
```
Questo percorso e' pensato per Task Scheduler, per esempio su evento di disconnessione o fine sessione. Se il server non e' raggiungibile o il backup fallisce, il comando termina con errore e non spegne il PC.
Azione Task Scheduler consigliata in sviluppo:
```text
Programma: C:\Python314\python.exe
Argomenti: -m bakrest.tray_app --nogui
Avvia in: C:\devel\bak&rest
```
Per una prova senza spegnimento:
```text
Programma: C:\Python314\python.exe
Argomenti: -m bakrest.tray_app --nogui --no-shutdown
Avvia in: C:\devel\bak&rest
```
## Avvio GUI configurazione ## Avvio GUI configurazione
```powershell ```powershell

View File

@@ -58,6 +58,8 @@ La tray app deve:
La tray app e' il componente piu' adatto per eseguire `rsync` e richiedere lo spegnimento, perche' queste sono azioni esplicite dell'utente. La tray app e' il componente piu' adatto per eseguire `rsync` e richiedere lo spegnimento, perche' queste sono azioni esplicite dell'utente.
La stessa logica di backup deve essere disponibile anche senza interfaccia grafica, tramite opzione `--nogui`, per permettere l'esecuzione da Task Scheduler alla disconnessione o fine sessione. In modalita' no-GUI non devono comparire icone tray o finestre di messaggio: gli errori devono essere registrati nei log e il processo deve restituire un codice di uscita diverso da zero.
### GUI CustomTkinter ### GUI CustomTkinter
La GUI di configurazione deve permettere di: La GUI di configurazione deve permettere di:
@@ -398,6 +400,7 @@ dist/
BakRestWatchdog.exe BakRestWatchdog.exe
BakRestTray.exe BakRestTray.exe
BakRestConfig.exe BakRestConfig.exe
BakRestNoGui.exe
``` ```
## Decisioni aperte ## Decisioni aperte

View File

@@ -0,0 +1,52 @@
from __future__ import annotations
import argparse
from .backup import run_backup, shutdown_after_backup
from .config import load_config
from .logging_setup import close_logger, configure_logging
def run_nogui_backup(shutdown: bool = True) -> int:
config = load_config()
logger = configure_logging(config.storage.logs_dir, "backup-nogui")
logger.info("BakRest no-GUI backup started")
try:
result = run_backup(config, logger)
if not result.success:
logger.error("BakRest no-GUI backup failed: %s", result.message)
return _exit_code_for_error(result.error_code)
logger.info("BakRest no-GUI backup completed: %s", result.message)
if shutdown:
shutdown_after_backup(config, logger)
else:
logger.info("Shutdown skipped by command line")
return 0
finally:
close_logger(logger)
def main() -> int:
parser = argparse.ArgumentParser(description="Run Bak&Rest backup without GUI")
parser.add_argument(
"--no-shutdown",
action="store_true",
help="Run the backup but do not shut down the computer",
)
args = parser.parse_args()
return run_nogui_backup(shutdown=not args.no_shutdown)
def _exit_code_for_error(error_code: str | None) -> int:
if error_code == "server_unreachable":
return 20
if error_code == "rsync_start_failed":
return 30
if error_code == "rsync_failed":
return 31
return 1
if __name__ == "__main__":
raise SystemExit(main())

View File

@@ -1,5 +1,6 @@
from __future__ import annotations from __future__ import annotations
import argparse
import json import json
import logging import logging
import threading import threading
@@ -11,6 +12,7 @@ from PIL import Image, ImageDraw
import pystray import pystray
from .backup import run_backup, shutdown_after_backup from .backup import run_backup, shutdown_after_backup
from .backup_nogui import run_nogui_backup
from .config import AppConfig, load_config from .config import AppConfig, load_config
from .logging_setup import configure_logging from .logging_setup import configure_logging
from .registry import ChangeRegistry from .registry import ChangeRegistry
@@ -101,7 +103,22 @@ def _show_error_message(title: str, message: str) -> None:
root.destroy() root.destroy()
def main() -> int: def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Bak&Rest tray app")
parser.add_argument(
"--nogui",
action="store_true",
help="Run backup and shutdown without tray icon",
)
parser.add_argument(
"--no-shutdown",
action="store_true",
help="With --nogui, run the backup but do not shut down the computer",
)
args = parser.parse_args(argv)
if args.nogui:
return run_nogui_backup(shutdown=not args.no_shutdown)
config = load_config() config = load_config()
logger = configure_logging(config.storage.logs_dir, "tray") logger = configure_logging(config.storage.logs_dir, "tray")
app = BakRestTrayApp(config, logger) app = BakRestTrayApp(config, logger)

View File

@@ -0,0 +1,10 @@
from __future__ import annotations
from bakrest.backup_nogui import _exit_code_for_error
def test_nogui_exit_codes_are_specific() -> None:
assert _exit_code_for_error("server_unreachable") == 20
assert _exit_code_for_error("rsync_start_failed") == 30
assert _exit_code_for_error("rsync_failed") == 31
assert _exit_code_for_error(None) == 1