From 8d8d0750b0e438e28e9772940893b5f67be5aa14 Mon Sep 17 00:00:00 2001 From: Mathieu Benoit Date: Tue, 28 Jul 2026 04:10:33 +0000 Subject: [PATCH] [FIX] script todo: wait for SSH before the ERPLibre install Right after boot the VM's sshd may not be up yet (notably on Fedora), so the install failed with "Connection refused". _qemu_install_erplibre_vm now polls SSH (ssh ... true, BatchMode) for up to 3 minutes and skips with a clear message if it never answers, instead of failing instantly. Co-Authored-By: Claude Opus 4.8 (1M context) --- script/todo/todo.py | 33 +++++++++++++++++++++++++++++++++ script/todo/todo_i18n.py | 8 ++++++++ 2 files changed, 41 insertions(+) diff --git a/script/todo/todo.py b/script/todo/todo.py index dd178de..0123cff 100755 --- a/script/todo/todo.py +++ b/script/todo/todo.py @@ -1638,6 +1638,30 @@ class TODO: # Cible d'installation Odoo exécutée dans la VM (défaut ERPLibre 1.6.0). ERPLIBRE_ODOO_TARGET = "install_odoo_18" + @staticmethod + def _qemu_wait_ssh(ip, user="erplibre", timeout=180): + """Attend que le SSH réponde (le sshd peut mettre du temps à démarrer + après le boot, surtout sur Fedora). True si joignable.""" + opts = ( + "-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null " + "-o ConnectTimeout=8 -o BatchMode=yes" + ) + deadline = time.time() + timeout + while time.time() < deadline: + try: + res = subprocess.run( + f"ssh {opts} {user}@{ip} true", + shell=True, + capture_output=True, + timeout=15, + ) + except (OSError, subprocess.SubprocessError): + res = None + if res is not None and res.returncode == 0: + return True + time.sleep(5) + return False + def _qemu_install_erplibre_vm(self, name, ssh_key, branch): """Clone ERPLibre (branche donnée) dans ~/git/erplibre de la VM puis exécute « make install_os » et « make install_odoo_18 ».""" @@ -1647,6 +1671,15 @@ class TODO: f" {name}: {t('no IP obtained, ERPLibre install skipped.')}" ) return + # Attend que le SSH soit prêt (évite « Connection refused » quand + # l'install démarre avant le sshd de la VM). + print(f" {name} ({ip}): {t('waiting for SSH...')}") + if not self._qemu_wait_ssh(ip): + print( + f" {name} ({ip}): " + f"{t('SSH not reachable, ERPLibre install skipped.')}" + ) + return remote = ( "set -e; " # Outils d'amorçage (absents des images cloud minimales) : diff --git a/script/todo/todo_i18n.py b/script/todo/todo_i18n.py index fd07bb8..4ccaaf9 100644 --- a/script/todo/todo_i18n.py +++ b/script/todo/todo_i18n.py @@ -1407,6 +1407,14 @@ TRANSLATIONS = { "fr": "aucune IP obtenue, installation ERPLibre ignorée.", "en": "no IP obtained, ERPLibre install skipped.", }, + "waiting for SSH...": { + "fr": "attente du SSH...", + "en": "waiting for SSH...", + }, + "SSH not reachable, ERPLibre install skipped.": { + "fr": "SSH injoignable, installation ERPLibre ignorée.", + "en": "SSH not reachable, ERPLibre install skipped.", + }, "Parallel deployments (default:": { "fr": "Déploiements en parallèle (défaut :", "en": "Parallel deployments (default:",