From 0830c895ebd42d1f548e9b152d965d0ca421b850 Mon Sep 17 00:00:00 2001 From: Mathieu Benoit Date: Tue, 28 Jul 2026 04:40:18 +0000 Subject: [PATCH] [FIX] script todo: reliable curl/git/make install + copyable logs Root cause of "git/make: command not found": the bootstrap ran "apt-get update -qq && apt-get install -y $PKGS". Inside an && list, set -e does NOT abort on failure, so when apt-get update failed (flaky VM network) the install was silently skipped and the script marched on without git/make. Now: "apt-get update || true; apt-get install -y $PKGS" (update best-effort, install mandatory), followed by an explicit "command -v curl git make" check that exits with a clear message ("Outil manquant ... (reseau de la VM ?)") instead of a cryptic failure later. Shared by the streamed and the monitored install paths. Dashboard: add "c" to copy the selected VM's full log to the clipboard (OSC 52, works over SSH) with a notification; the ssh bar documents Shift+drag for native terminal selection; on close, print a ready-to- copy "tail -n +1 /*.log" to share the logs. Co-Authored-By: Claude Opus 4.8 (1M context) --- script/todo/qemu_install_monitor.py | 22 +++++++++++++++++++++- script/todo/todo.py | 16 ++++++++++++++-- script/todo/todo_i18n.py | 4 ++++ 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/script/todo/qemu_install_monitor.py b/script/todo/qemu_install_monitor.py index 7384d7c..3d16034 100644 --- a/script/todo/qemu_install_monitor.py +++ b/script/todo/qemu_install_monitor.py @@ -133,6 +133,7 @@ def run_monitor(manifest_path: str) -> None: ("q", "quit", "Quitter (détaché)"), ("s", "ssh", "SSH"), ("f", "follow", "Suivre"), + ("c", "copy_log", "Copier log"), ] def __init__(self): @@ -171,7 +172,10 @@ def run_monitor(manifest_path: str) -> None: vm = self._vm_by_name(self._selected) bar = self.query_one("#sshbar", Static) if vm: - bar.update(f" {vm['ssh']} (s = ouvrir SSH)") + bar.update( + f" {vm['ssh']} (s = SSH · c = copier le log · " + "Maj+glisser = sélectionner)" + ) def _load_selected_log(self, reset=False): vm = self._vm_by_name(self._selected) @@ -229,4 +233,20 @@ def run_monitor(manifest_path: str) -> None: with self.suspend(): os.system(f"ssh {SSH_OPTS} erplibre@{vm['ip']} || true") + def action_copy_log(self) -> None: + """Copie le log complet de la VM sélectionnée dans le + presse-papiers (OSC 52 ; marche aussi à travers SSH).""" + vm = self._vm_by_name(self._selected) + if not vm: + return + try: + text = Path(vm["log"]).read_text(errors="replace") + except OSError: + return + self.copy_to_clipboard(text) + self.notify( + f"Log de {vm['name']} copié ({len(text)} car.)", + title="Presse-papiers", + ) + Monitor().run() diff --git a/script/todo/todo.py b/script/todo/todo.py index 7fba57a..7c43606 100755 --- a/script/todo/todo.py +++ b/script/todo/todo.py @@ -1677,13 +1677,22 @@ class TODO: # (Fedora). apt-get update car cloud-init n'a pas rafraîchi apt. "PKGS='curl git make'; " "if command -v apt-get >/dev/null 2>&1; then " - "sudo apt-get update -qq && sudo apt-get install -y $PKGS; " + # update best-effort (|| true) puis install OBLIGATOIRE : sans le + # « || true », un « apt-get update » en échec (réseau) sautait + # l'install sans erreur (liste &&) -> git/make absents ensuite. + "sudo apt-get update -qq || true; " + "sudo apt-get install -y $PKGS; " "elif command -v dnf >/dev/null 2>&1; then " "sudo dnf install -y $PKGS; " "elif command -v yum >/dev/null 2>&1; then " "sudo yum install -y $PKGS; " "else echo 'Aucun gestionnaire de paquets (apt/dnf/yum)'; " "exit 1; fi; " + # Vérifie explicitement que tout est là : erreur nette plutôt + # qu'un « command not found » cryptique plus loin. + "for t in curl git make; do command -v $t >/dev/null 2>&1 || " + '{ echo "Outil manquant apres installation: $t ' + '(reseau de la VM ?)"; exit 1; }; done; ' "mkdir -p ~/git; " "if [ ! -d ~/git/erplibre/.git ]; then " f"git clone --branch {shlex.quote(branch)} " @@ -1725,7 +1734,10 @@ class TODO: print( f"\n{t('Monitor closed. Installs keep running in the background.')}" ) - print(f" {t('Logs:')} {os.path.dirname(manifest)}") + logdir = os.path.dirname(manifest) + print(f" {t('Logs:')} {logdir}") + # Commande prête à copier pour relire/partager tous les logs. + print(f" {t('Read the logs:')} tail -n +1 {logdir}/*.log") def _qemu_install_erplibre_vm(self, name, ssh_key, branch): """Clone ERPLibre (branche donnée) dans ~/git/erplibre de la VM puis diff --git a/script/todo/todo_i18n.py b/script/todo/todo_i18n.py index 1c07925..1b6edb9 100644 --- a/script/todo/todo_i18n.py +++ b/script/todo/todo_i18n.py @@ -1443,6 +1443,10 @@ TRANSLATIONS = { "fr": "Logs :", "en": "Logs:", }, + "Read the logs:": { + "fr": "Lire les logs :", + "en": "Read the logs:", + }, "Install textual for the dashboard (pip).": { "fr": "Installez textual pour le dashboard (pip).", "en": "Install textual for the dashboard (pip).",