From b4ca67289a9d3a2e7356ed12dcd0bf3a7b793264 Mon Sep 17 00:00:00 2001 From: Daniel Allaire Date: Tue, 7 Jul 2026 12:49:17 -0400 Subject: [PATCH] =?UTF-8?q?GUI=20:=20persister=20les=20journaux=20d'ex?= =?UTF-8?q?=C3=A9cution?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Chaque action (creer-vm/verifier/deployer) écrit un journal horodaté /logs/--.log en plus du streaming live. Si le navigateur se déconnecte, l'exécution continue et le log est capturé jusqu'au bout (au lieu d'être interrompue). node --check OK, serveur HTTP 200. Co-Authored-By: Claude Opus 4.8 --- CHANGELOG.md | 5 +++++ scripts/inventory_gui.py | 24 +++++++++++++++++++++--- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f6f3e2d..a2a98ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,11 @@ ## 2026-07-07 (soir) ### Ajouté +- **GUI : persistance des journaux d'exécution.** Chaque action lancée depuis l'interface + (créer-vm / vérifier / déployer) écrit désormais, **en plus du streaming live**, un journal + horodaté `/logs/--.log` (gitignoré). Bonus : si le navigateur se + déconnecte, l'exécution **continue** et le journal est capturé jusqu'au bout (au lieu d'être + interrompue) — utile pour un déploiement qu'on ne veut pas voir avorter à la fermeture d'un onglet. - **GUI : vues « Flux » et « Couches » (lecture seule).** Deux nouveaux onglets rendent visibles dans l'interface deux registres jusque-là en ligne de commande seulement : **Flux** = la matrice d'audit réseau (rôle · sens · port · pair · chiffrement coloré · raison, depuis les `meta/flux.yml`), diff --git a/scripts/inventory_gui.py b/scripts/inventory_gui.py index d181c50..ad18af3 100644 --- a/scripts/inventory_gui.py +++ b/scripts/inventory_gui.py @@ -12,6 +12,7 @@ import secrets import socket import subprocess import tempfile +import time import threading from concurrent.futures import ThreadPoolExecutor from pathlib import Path @@ -2053,6 +2054,7 @@ class Gestionnaire(BaseHTTPRequestHandler): self.repondre_json(409, {"erreur": "Une execution est deja en cours."}) return fichier_vault = None + journal = None try: cible = {"deployer": "deployer", "pousser": "deployer", "creer": "creer-vm"}.get(mode, "verifier-deploiement") @@ -2066,7 +2068,14 @@ class Gestionnaire(BaseHTTPRequestHandler): self.send_header("Content-Type", "text/plain; charset=utf-8") self.send_header("Cache-Control", "no-cache") self.end_headers() - self._ecrire_flux(f"$ make {cible} HOTE={hote}\n\n") + # Persistance : un journal horodaté par exécution, en plus du streaming live. + dossier_logs = self.inventaire.parents[2] / "logs" + dossier_logs.mkdir(parents=True, exist_ok=True) + chemin_log = dossier_logs / f"{hote}-{cible}-{time.strftime('%Y%m%dT%H%M%S')}.log" + journal = open(chemin_log, "w", encoding="utf-8") + entete = f"$ make {cible} HOTE={hote}\n\n" + journal.write(entete) + self._ecrire_flux(entete) proc = subprocess.Popen( ["make", cible, f"HOTE={hote}"], cwd=str(RACINE), env=env, @@ -2075,8 +2084,11 @@ class Gestionnaire(BaseHTTPRequestHandler): ) try: for ligne in proc.stdout: + journal.write(ligne) if not self._ecrire_flux(ligne): - proc.terminate() + # Navigateur déconnecté : on continue à journaliser jusqu'au bout. + for reste in proc.stdout: + journal.write(reste) break finally: rc = proc.wait() @@ -2091,8 +2103,14 @@ class Gestionnaire(BaseHTTPRequestHandler): marquer_serveur_actif(hote) except Exception: pass - self._ecrire_flux(f"\n__FIN__ rc={rc}\n") + journal.write(f"\n__FIN__ rc={rc}\n") + self._ecrire_flux(f"\n[journal : {os.path.relpath(chemin_log, RACINE)}]\n__FIN__ rc={rc}\n") finally: + if journal: + try: + journal.close() + except OSError: + pass if fichier_vault: try: os.remove(fichier_vault)