- F2 : nouvelle vue SYSTÈME (état/uptime + charge, CPU %, mémoire, disque,
réseau ↓/↑, batterie, température). Les I/O sont déportées en thread
(asyncio.to_thread) ; rafraîchissement 2 s (deltas CPU/réseau). Si la
température n'est pas lisible (ni /sys/class/thermal ni lm-sensors), on
propose d'installer lm-sensors avec la touche « i » (commande selon l'OS —
apt/dnf/pacman — affichée puis validée). read_temperature essaie
/sys/class/thermal d'abord (sans dépendance).
- Vue Kanban GRILLE : clic sur le TITRE d'une case -> elle s'AGRANDIT
(row-span, prend toute la hauteur d'une colonne) ; re-clic -> taille
normale (classe kbig).
Validé headless : vue système peuplée (mém/disque/temp), F2/F3 basculent,
clic titre en grille -> kbig on/off.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Après exécution d'une commande choisie dans la télémétrie, on propose de
REVENIR (r) ou de quitter. En revenant, la vue ET la position du curseur
sont RESTAURÉES (chemin porté par chaque nœud/carte ; run_tui prend/rend un
`state`).
- Vue Kanban : F4 fait défiler la DISPOSITION —
columns (une rangée de colonnes) / swimlanes (une rangée par menu de
niveau 1) / grid (grille 3 colonnes). F3 bascule Arbre/Kanban.
Validé headless : F3/F4 cyclent les dispositions, sélection -> action+état,
relance avec `state` -> curseur restauré sur le bon nœud.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Ticks ASYNCHRONES : les I/O bloquantes (lecture des logs, stat disque,
subprocess « virsh list », /proc) sont déportées en THREAD via
asyncio.to_thread ; seules les mises à jour d'UI restent sur la boucle
d'événements Textual -> plus de gel, même sous forte charge ou disque lent.
_collect_table (thread) rassemble statut+disque+télémétrie, l'application
au tableau se fait ensuite sur la boucle. _tick_log/_tick_domstate idem.
Helper _read_new (lecture incrémentale) réutilisé.
- Touche « w » : si aucun navigateur CLI n'est présent, on PROPOSE de
l'installer selon l'OS (apt Ubuntu/Debian, dnf Fedora, pacman Arch — nos 4
systèmes) : la commande est AFFICHÉE puis exécutée après validation (o/N).
Validé headless : ticks async mettent l'UI à jour ; browser_install_command
renvoie la bonne commande selon l'OS.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Le TUI de télémétrie devient aussi un LANCEUR, avec deux vues :
- Sélectionner une COMMANDE (feuille de l'arbre, ou carte du Kanban) +
Entrée -> le TUI se ferme et la commande est EXÉCUTÉE (getattr(self, méthode)
(**kwargs)). Les kwargs littéraux sont extraits du code (ex. Aperçu ->
_qemu_deploy(dry_run=True)), donc la commande est rejouée à l'identique.
- Vue KANBAN : une colonne par menu contenant des commandes (issu du code),
cartes = commandes exécutables + compteur de visites du menu.
- F3 bascule entre vue Arbre et vue Kanban. Résumé mis à jour (Entrée =
exécuter, F3 = vue).
_dispatch capture désormais (méthode, kwargs) ; les feuilles de l'arbre
portent la méthode en data ; run_tui renvoie (méthode, kwargs) à exécuter.
Validé headless : 11 colonnes Kanban, kwargs (dry_run/production_ready)
capturés, bascule F3, capture de l'action à exécuter.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
La télémétrie n'affichait que les chemins effectivement visités. Elle
observe désormais les MÉTADONNÉES DE NAVIGATION issues du CODE : todo.py est
analysé (AST) pour reconstruire l'arbre RÉEL des menus et commandes.
- build_code_tree() lit _MENU_LABELS + les listes « choices » + le dispatch
« status == N: self.X() » de chaque menu pour bâtir l'arborescence complète
(sous-menus = cibles présentes dans _MENU_LABELS ; sinon commandes-feuilles,
libellées par leur prompt_description).
- Le TUI affiche cet arbre COMPLET (tous les menus, même jamais visités) avec
le compteur de visites en surimpression sur chaque menu (via la télémétrie
persistée). Repli sur l'arbre des seuls chemins visités si l'analyse échoue.
Validé : l'arbre reconstruit couvre Execute -> Deploy -> SSH/QEMU (+ toutes
leurs commandes : Resize, Delete, …), Git -> Git local server, GPT code ->
RTK, etc. ; compteurs corrects (menus visités > 0, autres 0).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Nouvelle entrée « [5] Télémétrie de navigation (TUI) » dans le menu principal.
- Enregistrement : Todo._menu_header (seul constructeur du fil d'Ariane)
appelle todo_telemetry.record(fil) à chaque affichage de menu. Dédup des
ré-affichages consécutifs -> on ne compte que les TRANSITIONS de
navigation. Persistant dans ~/.erplibre/todo_telemetry.json. Best-effort :
ne casse jamais la navigation.
- Visualisation : todo_telemetry.run_tui() ouvre un TUI Textual affichant
l'ARBRE des fonctionnalités visitées (chaque nœud = un menu, avec son
nombre de visites), trié par usage décroissant. Touches : q (quitter),
r (réinitialiser), e (tout déplier). Sommaire : total navigations + menus.
Validé headless : enregistrement + dédup, arbre imbriqué à compteurs,
montage du TUI, peuplement, touches.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Pendant le suivi, une VM pouvait « stagner » sans qu'on sache pourquoi
(mise en pause, ou effacée à côté). Le dashboard interroge désormais l'état
libvirt (« virsh list --all ») à INTERVALLE LENT (toutes les 10 s, un seul
appel pour tout le parc ; le tableau applique le cache à chaque tick de 2 s) :
- VM en pause (virsh suspend) -> État « ⏸ pause » (non terminal, peut
reprendre).
- VM absente de virsh (effacée pendant l'attente) -> État « ❌ effacée »,
terminal (on cesse de lire son log).
Sinon on garde le statut basé sur le log (⏳ / ✅ / ❌).
Validé headless : running -> ⏳, paused -> ⏸ pause, absente -> ❌ effacée.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Nouvelle entrée dans le menu QEMU/KVM (section Gérer). Elle :
- affiche le disque principal (qcow2 via domblklist) + « qemu-img info »
(taille virtuelle + réelle) et l'état de la VM ;
- demande +NG (agrandir), -NG (réduire) ou NG (taille cible) ;
- applique : agrandissement À CHAUD si la VM tourne (virsh blockresize),
sinon qemu-img resize ; réduction via qemu-img resize --shrink (VM éteinte
obligatoire + avertissement fort : le FS invité n'est PAS réduit, risque
de perte de données) ;
- propose ensuite d'étendre le FS invité via SSH (growpart + resize2fs /
xfs_growfs / btrfs, device et type de FS détectés).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Ajouts au suivi d'installation (dashboard Textual) :
- Télémétrie hôte (barre dédiée, MAJ 2 s) : CPU % = charge / nb CPU, et
disque du dossier des images (utilisé/total/libre) — utile pour anticiper
le remplissage du disque (cas vécu).
- Colonne « Disque » par VM : taille RÉELLE du qcow2 (st_blocks) -> on voit
quelle VM grossit.
- ETA « hypothèse » : les durées d'install sont MÉMORISÉES par architecture
dans ~/.erplibre/qemu-install/stats.json ; l'ETA du parc = médiane
historique (par arch, repli global) - temps écoulé, affichée au sous-titre.
- Touche « w » : ouvre l'UI web de la VM (Odoo :8069) dans un navigateur CLI
(browsh/carbonyl/w3m/links/elinks/lynx, le 1er trouvé ; sinon notifie quoi
installer). Surtout utile quand l'install est terminée.
Helpers module : load_stats/record_duration/eta_reference, _fmt_size/_fmt_secs,
vm_disk_path/disk_actual_size, cli_browser. run_monitor(run_app=False) expose
l'app pour test headless.
Validé headless (run_test) : montage, ticks, enregistrement des durées, ETA,
télémétrie, touches f/w — sans crash.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Encore du lag à 16 VM : update_cell était appelé pour CHAQUE ligne à chaque
tick (état + « Durée »), or la Durée — dérivée d'un `started` global — est
identique sur toutes les lignes et se réécrivait sans cesse -> re-render
permanent de la DataTable.
- _set_cell : n'appelle update_cell (donc ne re-render) que si la valeur
CHANGE. Mesuré : 312 -> 18 update_cell sur 10 ticks × 16 VM.
- La durée « live » passe dans le SOUS-TITRE (une seule mise à jour) ; la
colonne Durée n'est écrite qu'à la complétion (durée finale figée).
- Le NOMBRE de VM est affiché : titre « … (N VM) », sous-titre
« k/N terminées · mm:ss ».
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Le TUI de suivi laggait à 30 VM et se figeait quand l'I/O ralentissait
(disque plein / logs effacés). Cause : à chaque tick (1 s) on lisait le
fichier log ENTIER de CHAQUE VM pour le statut + on relisait tout le log
sélectionné, en synchrone sur la boucle d'événements Textual.
Corrections :
- read_status ne lit que les 4 derniers Ko (le marqueur de sortie est sur
la dernière ligne) : un log de 2 Mo passe de « tout lire » à 0,1 ms.
- _load_selected_log lit de façon INCRÉMENTALE (seek à l'offset) au lieu de
relire tout le fichier.
- Les VM TERMINÉES ne sont plus relues (cache _final).
- _tick_table (2 s) et _tick_log (1 s) séparés et PROTÉGÉS par try/except :
une erreur I/O transitoire (disque plein, log supprimé) ne tue plus la
boucle -> l'interface reste réactive aux touches.
- RichLog borné (max_lines=5000).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sur un gros parc (30 VM, ~18 émulées), la résolution d'IP « bloquait » 10 min
en silence sur les VM lentes. Trois causes traitées :
- Joignabilité par PING (ICMP) d'abord, TCP:22 en repli. Le ping répond dès
que le réseau de la VM est up, BIEN AVANT sshd : on ne retenait pas une VM
qui a déjà son IP juste parce que sshd (lent en émulation) n'était pas prêt.
Le ping distingue toujours le bail actif (répond) du bail périmé (non).
- IP cherchée sur PLUSIEURS sources : lease (dnsmasq), agent (qemu-guest-
agent DANS la VM) et arp (table ARP hôte). Le bail dnsmasq peut être vide
sous forte charge alors que la VM a une IP (constaté sur ce parc).
- BATTEMENT toutes les 30 s listant les VM encore en attente + timeout PAR VM
ramené à 5 min (au lieu de 10) dans la phase de résolution -> plus de
silence prolongé, et on n'attend pas indéfiniment une VM sans IP.
NB : déployer la matrice complète (30 VM dont ~18 émulées TCG) sature un seul
hôte ; certaines VM émulées n'obtiennent pas de bail DHCP à temps (limite de
capacité, pas un bug). La résolution dégrade désormais proprement.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Améliore le suivi du déploiement multi-VM :
- Compteur de COMPLÉTION devant chaque résultat (« [1/26] », « [2/26] »… =
ordre où les tâches TERMINENT), en plus de l'ID de préparation stable
(« [7/26] ») — utile car l'exécution parallèle rend les résultats
désordonnés.
- Temps d'exécution PAR ÉTAPE : durée de chaque VM (déploiement, résolution
d'IP) + bilan de phase (« Bilan déploiement : 24 OK, 2 en échec, 26 VM,
3m10s » ; « IP résolues : 25/26 (2m). »).
- SOMMAIRE TOTAL encadré en fin de flux (VM déployées, total avec
existantes, temps total).
Helper _fmt_dur (« 45s » / « 2m05s »).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Chaque question oui/non affiche désormais explicitement la valeur par défaut
appliquée si on laisse vide, ex. :
« Suivi interactif (dashboard) ? (o/N, défaut : non) : »
Les 18 prompts passent par _is_yes(input(...)) : une réponse vide vaut NON.
On l'indique donc dans les traductions (fr « (o/N, défaut : non) », en
« (y/N, default: no) »). Seules les VALEURS i18n changent ; les clés (donc
les appels t(...) dans todo.py) sont inchangées.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sur un gros parc (ex. 26 VM), on ne voyait ni combien de VM allaient être
déployées ni lesquelles étaient en cours (résultats parallèles dans le
désordre). Deux améliorations :
- Le prompt de parallélisme affiche le NOMBRE de VM à déployer :
« Déploiements en parallèle (défaut : 24, 26 VM) : ». Pour cela, la
séparation à-créer / déjà-existantes est faite AVANT le prompt (on connaît
le vrai compte).
- Chaque VM reçoit un ID « k/N » suivant l'ordre de préparation, affiché
dans les résultats de déploiement (« ✅ [3/26] nom ») et dans la résolution
d'IP (« [5/30] nom : ip »). L'ID reste stable même si les résultats
reviennent dans le désordre -> on suit lesquelles sont traitées.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Le menu QEMU avait deux commandes de déploiement redondantes : [1] « Déployer
une VM » (mono, noms/ressources personnalisés) et [4] « Déployer l'infra »
(multi, matrice distro×version×archi). Elles sont FUSIONNÉES en une seule,
[1] « Déployer une ou plusieurs VM » (_qemu_deploy), qui gère aussi bien 1 VM
que N, avec :
- renommage GRANULAIRE des VM à la demande (_qemu_customize_names) : noms
auto par défaut, on en renomme certains par numéros séparés de virgules ;
- multiplicateur de ressources (x1..x4) déjà présent ;
- aperçu dry-run ([2]) qui passe par le même flux et imprime les commandes
deploy_qemu (--dry-run) via le helper partagé _qemu_build_deploy_parts.
Menu simplifié : [1] Déployer une/plusieurs VM, [2] Aperçu (dry-run),
[3] Télécharger une image, puis Gérer/Catalogue renumérotés. Code mort retiré
(_qemu_deploy_vm, _qemu_prompt_arch, _normalize_disk_size,
_qemu_offer_ssh_config).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Quand le téléchargement échoue sur tous les miroirs, le message indique
désormais le CHEMIN de destination visé et une commande prête à copier pour
reprendre manuellement :
Destination : /var/lib/.../fedora-cloud-41-aarch64.qcow2
Reprendre le téléchargement manuellement :
sudo curl -fL -C - -o <dest> \
<url>
puis relancez le déploiement (l'image en cache sera réutilisée).
curl -C - reprend un .part partiel (ou repart de zéro), -f échoue proprement
sur une erreur HTTP. Complète la détection de téléchargement incomplet.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Le menu Deploy listait 11 entrées « SSH - … » à plat, ce qui le surchargeait.
Elles sont déplacées dans un sous-menu « SSH (hôte distant)… »
(prompt_execute_deploy_ssh), comme le sous-menu QEMU. Le menu Deploy tient
désormais en 5 entrées :
── Local ──
[1] Cloner ERPLibre localement
[2] Configurer sshfs
── Distant & services ──
[3] SSH (hôte distant)… -> sous-menu (11 opérations SSH)
[4] NTFY
[5] QEMU/KVM…
Le fil d'Ariane affiche « TODO › Execute › Deploy › SSH » dans le sous-menu.
Les libellés et actions SSH sont inchangés (traductions réutilisées).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Avant le déploiement infra, on demande un multiplicateur de ressources :
x1 (minimum catalogue) .. x4. Il multiplie la RAM (base = minimum de la
version) et les vCPU (base 2), en bornant les vCPU au nombre de cœurs de
l'hôte et en signalant si la RAM totale dépasse la RAM libre.
- Le prompt affiche les ressources de l'hôte (vCPU, RAM libre) et, pour
chaque multiplicateur, le total RAM + vCPU/VM avec un ⚠ si ça dépasse.
- Le plan reflète les ressources effectives (vCPU + RAM×mult par VM).
- Les jobs passent --memory et --vcpus effectifs à deploy_qemu (avant,
l'infra laissait le minimum catalogue + 2 vCPU par défaut).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Après un déploiement infra, la résolution d'IP tournait EN SÉRIE (boucle
ssh_config puis install), jusqu'à 10 min par VM émulée, SANS aucune sortie :
le dashboard « n'ouvrait jamais » (impression de blocage), d'autant qu'une
VM qui ne boote pas (IP jamais attribuée) bloquait tout le reste.
Fix : _qemu_resolve_ips résout les IP de toutes les VM EN PARALLÈLE, avec
progression affichée par VM, une seule fois, réutilisée pour ~/.ssh/config
ET l'installation (plus de double résolution). _qemu_install_erplibre_*
acceptent une IP/ip_map déjà résolue.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Une VM arm64 (Fedora aarch64 émulée) restait FIGÉE au firmware (CPU ~2 %,
aucune IP, seed non appliqué). Deux causes :
1) Téléchargement tronqué non détecté. _download_one ne vérifiait pas que le
nombre d'octets reçus == Content-Length. Une connexion coupée en cours
laissait un .part TRONQUÉ, validé comme « complet » (tmp.replace) : un
qcow2 valide mais VIDE (128 Kio, juste l'en-tête, 5 Gio virtuels) ->
disque sans OS -> pas de boot. Pire, ce cache corrompu était réutilisé.
Fix : contrôle de complétude (done < total -> exception -> miroir suivant
ou échec net, jamais de commit d'un fichier tronqué).
2) arm64 : « --boot uefi » simple laissait libvirt choisir l'AAVMF « secure »
(clés Microsoft enrôlées) -> pas de boot. On désactive Secure Boot comme
pour x86 (secure-boot=no -> AAVMF_CODE.no-secboot.fd).
Validé : redéploiement Fedora 42 aarch64 avec image complète + firmware
non-SB -> CPU 111 % (boot actif), IP en ~80 s, SSH OK (erplibre, aarch64,
Fedora 42 Cloud, sudo OK).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Le log d'installation commence désormais par un en-tête identifiant
l'installation : date, VM, distribution + version, architecture, branche, IP.
Écrit dès la création du log (plus jamais vide au démarrage).
_qemu_install_erplibre_monitored déduit distro/version/arch de chaque VM
(nom via _qemu_infra_name + arch via virsh dumpxml) et les passe à
launch_installs, qui écrit l'en-tête et les stocke dans le manifeste.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
L'install ERPLibre échouait sur une VM Ubuntu 26.04 :
« configure: error: no acceptable C compiler found in $PATH » -> pyenv ne
compile pas Python -> pas de venv -> pip/poetry/repo absents.
Cause : install_dev.sh ne listait Ubuntu que jusqu'à 25.10. Sur 26.04 il
tombait dans le « else » (« Your version is not supported… : 26.04 ») et
n'exécutait donc PAS install_debian_dependency.sh -> ni build-essential ni
gcc installés. Le catalogue de déploiement propose pourtant 26.04.
Fix : ajout de 26.04 à la liste des versions Ubuntu supportées.
Note : Ubuntu 26.04 utilise uutils (rust-coreutils), bogué en big-endian ;
sur s390x un « sort » peut paniquer (non bloquant ici) — problème amont.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Menu « Déployer l'infra ERPLibre » : la sélection d'architecture propose
désormais [all] = toutes les architectures supportées. L'architecture
devient une dimension à part entière de la matrice de déploiement.
- [all] archis + [all] catalogue : une VM par (distro, version, archi
publiée par cette distro). Ex. ubuntu -> amd64/arm64/s390x, debian/fedora
-> amd64/arm64, arch -> amd64 (30 VM au total avec le catalogue courant).
- [all] archis + [granulaire] : la liste à plat inclut l'archi ([amd64]/
[arm64]/[s390x]) ; on choisit des combinaisons précises par virgules.
- [all] archis + [principal] : version par défaut de chaque distro × chaque
archi supportée.
- Une archi précise (arm64/s390x) restreint le catalogue aux distros qui la
publient (inchangé).
selected porte l'archi par élément ; les noms sont suffixés par archi pour
les non-natives (erplibre-ubuntu-2604-s390x) -> pas de collision. --arch
transmis par VM. Chaque distro ne reçoit que les archis QU'ELLE publie.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Symptôme : ~/.ssh/config (et l'install) pointaient sur .31 alors que la VM
était joignable sur .32 -> SSH KO (« No route to host »), console OK.
Cause : au boot, une image cloud demande d'abord une IP DHCP avec son
hostname par défaut (« ubuntu ») -> 1er bail ; puis cloud-init fixe le vrai
hostname et le client redemande -> 2e bail (IP différente). La MÊME MAC a
donc DEUX baux ; le 1er (« ubuntu ») devient périmé. Le code retenait
aveuglément le PREMIER IPv4 de « virsh domifaddr » = le bail périmé. Plus
visible sur s390x/arm64 émulé (boot lent -> les deux baux coexistent).
Fix :
- todo._qemu_vm_ip : renvoie en priorité l'IP dont le bail dnsmasq porte le
hostname == nom de la VM (bail définitif), sinon une IP JOIGNABLE (sshd up,
test TCP:22), sinon le dernier bail — jamais le 1er au hasard. Attente
portée à 10 min (boot émulé lent). Helpers _qemu_lease_candidates /
_qemu_ip_reachable / _qemu_lease_ip_for_host.
- deploy_qemu.wait_for_ip : même logique (IP joignable, sinon la plus
récente) pour l'IP affichée en fin de déploiement standalone.
Validé sur la VM s390x réelle : candidats [.31, .32] -> choisit .32
(hostname-match + seule joignable) ; ssh via alias OK.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1) Nommage : _qemu_infra_name ajoute un suffixe d'architecture quand elle
diffère de la native de l'hôte -> déployer un s390x sur un hôte amd64
donne « erplibre-ubuntu-2604-s390x » (au lieu de « erplibre-ubuntu-2604 »).
Évite les collisions de noms entre archis et rend l'archi visible. Passé
dans les 3 appelants (VM unique + plan + jobs de l'infra).
2) Log d'installation vide : sur une archi ÉMULÉE (s390x/arm64 sur x86) le
boot prend plusieurs minutes ; pendant ce temps l'attente n'écrivait rien
-> le log restait VIDE et paraissait « bloqué ». _launch_one écrit
désormais un en-tête d'attente immédiat + un battement toutes les ~30 s,
et l'attente sshd/cloud-init passe de ~12 à ~20 min (boot émulé lent).
_qemu_wait_ssh (chemin streamé) : timeout porté à 20 min également.
Validé : nommage (amd64/natif sans suffixe, s390x/arm64 suffixés) ; le log
se remplit dès le départ (en-tête + « ... 30s »).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Ajoute l'architecture arm64 (aarch64) au déploiement, en plus d'amd64
(x86_64) et s390x. Disponibilité réelle vérifiée (juillet 2026) :
- arm64 : Ubuntu, Debian, Fedora (Arch : pas d'image cloud aarch64 -> rejet).
- s390x : Ubuntu seulement (inchangé).
deploy_qemu.py :
- host_arch() : arch native de l'hôte ; toute arch différente est ÉMULÉE
(TCG, --virt-type qemu) — plus de liste FOREIGN_ARCHES figée.
- virt_install : arm64 -> --arch aarch64 --machine virt --boot uefi (firmware
AAVMF résolu par libvirt) ; s390x inchangé ; x86 UEFI/OVMF inchangé.
- ensure_emulator(arch) généralisé : installe qemu-system-aarch64 + firmware
UEFI AAVMF (arm64) ou qemu-system-s390x (s390x), selon le gestionnaire de
paquets, avec vérif de présence (binaire + firmware pour arm64).
- Validation --arch tôt via ARCH_DISTRO_SUPPORT (message clair si la distro
ne publie pas l'arch). Les URLs d'images arm64 marchent déjà via les alias
(fedora/arch aarch64 ; ubuntu/debian arm64).
todo.py : menus d'architecture (VM unique + infra) proposent amd64/arm64/
s390x selon la distro ; l'architecture NATIVE de l'hôte est marquée d'un *
(défaut) ; les autres sont signalées « émulé, lent ». Le catalogue infra est
restreint aux distros publiant l'arch choisie (arm64 -> ubuntu/debian/fedora,
s390x -> ubuntu).
install_debian_dependency.sh : wkhtmltopdf (deb amd64 en dur) ignoré
best-effort sur toute arch non-x86_64 (au lieu d'avorter l'install).
Validé : dry-runs Ubuntu/Debian/Fedora arm64 (bonnes URLs + virt-install
aarch64/virt/uefi/qemu), rejet Arch arm64, non-régression s390x, menus et
filtrage simulés (natif *, arm64 par distro).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
repo sync échouait sur Fedora :
git: 'daemon' is not a git command.
fatal: cannot obtain manifest git://127.0.0.1:9418/ -> Connection refused
Unable to sync manifest ./manifest/git_manifest_erplibre.xml
Cause : sur Fedora la sous-commande « git daemon » N'EST PAS fournie par le
paquet « git » de base (contrairement à Debian/Ubuntu/Arch) mais par le
paquet séparé « git-daemon ». ERPLibre sert son manifeste via un « git
daemon » local (git://127.0.0.1:9418/) pendant repo sync ; sans le binaire,
le daemon ne démarre pas -> connexion refusée -> synchro du manifeste KO.
Fix : ajout de git-daemon aux dépendances Fedora.
Validé sur VM Fedora 42 réelle : avant, « git daemon » rc=1 ; après
« dnf install git-daemon », rc=0, et un git daemon local sert bien
git://127.0.0.1:9418/ (git ls-remote renvoie les refs).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Menu « Déployer l'infra ERPLibre » :
- Nouveau choix [granulaire] dans la sélection des distributions : affiche
la liste À PLAT de TOUTES les versions du catalogue (distro + version,
défauts marqués d'un *) et permet d'en choisir plusieurs par leurs numéros
séparés par des virgules (ex. « 1,3,12 »). Complète [all] et [principal].
- Demande l'ARCHITECTURE du parc en début de flux, par défaut l'architecture
NATIVE de l'hôte (amd64/arm64/s390x via uname -m). s390x est proposé mais
émulé (lent) et n'a d'images cloud que pour Ubuntu -> le catalogue est
alors restreint à Ubuntu (les autres distros sont ignorées avec un
avertissement). --arch est transmis à deploy_qemu.py quand ≠ amd64.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Après le passage à pacman, l'init PostgreSQL échouait :
« /usr/bin/postgres: /usr/lib/libm.so.6: version GLIBC_2.44 not found ».
Cause : Arch est en rolling release et NE SUPPORTE PAS les mises à jour
partielles. Sur une image cloud dont la glibc date du build de l'image, un
« pacman -S <paquet récent> » (postgresql 18.4) installe un binaire lié à
une glibc plus récente que celle du système -> symbole introuvable.
Fix : mise à jour COMPLÈTE (pacman -Syu) avant d'installer, dans les deux
endroits : install_arch_linux.sh (deps OS) et le bootstrap Arch de
_qemu_erplibre_remote_cmd (todo.py, remplace -Syy par -Syu).
Validé sur VM Arch réelle : après -Syu, « postgres (PostgreSQL) 18.4 » OK,
initdb OK, service postgresql actif, superuser erplibre créé, psql connecte.
Chaîne complète confirmée : gcc 16.1.1, make, node, psql tous présents.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Deux échecs d'install ERPLibre sur VM constatés dans les logs :
Arch — install_arch_linux.sh utilisait « yay » (helper AUR ABSENT d'une
image cloud et qui refuse de tourner en root) et n'installait JAMAIS
base-devel : sur une image cloud fraîche rien ne s'installait, donc pas de
compilateur C -> pyenv : « no acceptable C compiler found » -> build de
Python 3.12 échoué -> pas de venv -> pip/poetry/repo absents. Réécrit avec
pacman (dépôts officiels) : base-devel (gcc/make), deps de build pyenv
(openssl/zlib/xz/tk/…), PostgreSQL (+ initdb, Arch ne l'initialise pas),
Node/npm, deps Odoo en best-effort paquet par paquet (pacman refuse toute
la transaction sur un seul nom inconnu). wkhtmltopdf (AUR) : ignoré
proprement.
Fedora — « Connection closed by remote host » (exit 255) dès le début :
« cloud-init status --wait » tournait dans une session SSH UNIQUE tuée
quand cloud-init régénère les clés d'hôte et REDÉMARRE sshd au 1er boot.
On attend désormais la FIN de cloud-init via des connexions COURTES
successives (chaque tentative survit à un redémarrage de sshd), AVANT de
lancer l'install — dans les deux chemins : _qemu_wait_ssh (streamé) et
_launch_one (détaché/monitoré). On matche sur le TEXTE de « cloud-init
status » (done/disabled/error/degraded) car son code de sortie n'est pas
fiable selon la version.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sur apt, qemu-system-misc NE contient PAS l'émulateur s390x (il fournit
alpha/avr/hppa/… mais pas s390x) : ensure_s390x_emulator installait donc
un paquet inutile puis échouait (« qemu-system-s390x toujours absent »).
Le binaire est fourni par le paquet dédié qemu-system-s390x.
Validé de bout en bout : déploiement réel Ubuntu 24.04 s390x émulé (TCG) ->
domaine type=qemu, machine s390-ccw-virtio, emulator qemu-system-s390x ->
boot OK, IP DHCP, SSH par clé (erplibre), sudo OK, uname -m = s390x
(IBM z15, /proc/sysinfo Type: 8561).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Ajoute --arch s390x au déploiement QEMU. Réalité constatée (juillet 2026) :
seul Ubuntu publie des images cloud s390x (cloud-images.ubuntu.com : OK) ;
Debian et Fedora n'en publient pas (404), Arch ne cible que x86_64/aarch64.
s390x est donc scopé à Ubuntu et rejeté proprement pour les autres distros.
deploy_qemu.py :
- FOREIGN_ARCHES / S390X_DISTROS : s390x n'est valide que pour Ubuntu.
- virt_install : sur s390x -> --arch s390x, --machine s390-ccw-virtio,
--virt-type qemu (émulation TCG, pas de KVM sur hôte x86), console SCLP
(pas de série ISA), amorçage IPL/zipl (aucun --boot UEFI/BIOS). Les
disques/réseau bus=virtio sont mappés en virtio-ccw par libvirt.
- ensure_s390x_emulator : installe qemu-system-s390x au besoin
(apt: qemu-system-misc, dnf: qemu-system-s390x, pacman:
qemu-emulators-full, zypper: qemu-s390).
- Avertit que s390x est ÉMULÉ (lent) sur un hôte x86.
todo.py : le menu « Déployer une VM » demande l'architecture (s390x proposé
uniquement pour Ubuntu, avec avertissement lenteur) et passe --arch.
install_debian_dependency.sh : wkhtmltopdf n'a pas de build s390x -> skip
best-effort au lieu d'avorter l'install (la branche s390x rust-all existait
déjà pour compiler les wheels).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fedora install stalled: the log stopped right after boot with no exit
marker. Cause: cloud-init was running "dnf -y install qemu-guest-agent
openssh-server" at first boot; on this host's slow VM network that dnf
took many minutes (metadata refresh + download), so cloud-init stayed
"running" and the install (which waits for cloud-init) never proceeded.
Fix: do NOT install any package via cloud-init. sshd is already present
in every cloud image (verified Ubuntu/Debian/Fedora/Arch) and is just
enabled via runcmd; the tools (curl/git/make…) are installed by the
ERPLibre bootstrap with optimised mirrors. cloud-init now finishes in
seconds (Fedora: "status: done" at 10s instead of blocking 5+ min).
--package still adds a cloud-init packages block on demand.
Also: when ERPLibre is installed via the infra menu, add
ERPLIBRE_EXTRA_DISK_GB (5G) to each VM's disk, because the image minimum
left only ~97 MB free after installation. Drop the now-useless
"--package git" (the bootstrap installs git).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Arch install failed at once: "error: failed to synchronize all
databases (unable to lock database)" then "reflector: command not
found". Cause: cloud-init was still running its own pacman (installing
qemu-guest-agent/openssh) and held /var/lib/pacman/db.lck when our
bootstrap started, so every pacman call failed and reflector never got
installed.
Fix: the remote install now waits for cloud-init to finish first
("cloud-init status --wait", 15 min timeout) before touching any package
manager — this releases the apt/dnf/pacman lock cloud-init holds during
its package stage (helps all distros). For Arch, also drop a stale
db.lck when no pacman is actually running. Verified live: cloud-init
done, no lock, reflector + refresh + curl/git/make all succeed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Dashboard: the info bar under the log now shows the selected VM's log
file path (in addition to its ssh command), so it can be opened/shared.
Install bootstrap: each package-manager branch first refreshes the repos
so the VM is as fast as possible, and Arch (pacman) is now supported:
- apt: apt-get update
- dnf: dnf makecache (dnf5 picks the fastest mirrors)
- pacman (Arch): reflector selects the 20 fastest HTTPS mirrors ->
pacman -Syy -> install curl/git/make (Arch was previously unsupported
by the bootstrap and would abort with "no package manager").
Validated live on the Arch VM: reflector + refresh + install all OK.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
cloud-init reported "status: error" on Arch because the seed's packages
list requested "openssh-server", which does not exist on Arch (the
package is "openssh"). Pick the SSH server package per distro: "openssh"
on Arch, "openssh-server" elsewhere. Verified on Arch: cloud-init now
finishes with "status: done", openssh installed, sshd active.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Arch VMs booted (after the Secure Boot fix) but eth0 stayed DOWN and
cloud-init never finished -> no IP, no SSH. Cause found in the guest:
cloud-init's Arch renderer writes the systemd-networkd [Match] Name from
the ethernet KEY of the v2 config (ignoring "match:"), so our key
"primary" produced "Name=primary" which matches no interface (Arch's NIC
is eth0). Rename the key to "eth0": Arch now renders "Name=eth0" and
DHCPs; Debian/Fedora/Ubuntu still use "match: name: e*" (verified Debian
redeploy: enp1s0 UP with an IP), so no regression.
Arch is now fully working end to end: boot, DHCP IP, SSH by key, user
erplibre in sudo, passwordless sudo, console login erplibre/erplibre.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
OVMF Secure Boot rejected Arch's unsigned GRUB with "Access Denied" ->
"No bootable option" -> dropped to the firmware menu, never booted.
Disable Secure Boot in the UEFI boot (firmware.feature secure-boot=no):
Arch now boots and cloud-init applies user/password/hostname/ssh-key
(console login erplibre/erplibre works). Ubuntu/Debian/Fedora also boot
without Secure Boot (their signed shim is not required), no regression.
Note: Arch networking (eth0 stays down, cloud-init does not finish) is
still under investigation and tracked separately.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Arch (rolling release, single "latest" version) joins ubuntu/debian/
fedora: official cloud image geo.mirror.pkgbuild.com/images/latest/
Arch-Linux-x86_64-cloudimg.qcow2 (cloud-init included), osinfo=archlinux
(known locally), UEFI boot + virtio seed like the others. ERPLibre
already has install_arch_linux.sh. Exposed in the infra/deploy menus.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
wkhtmltopdf 0.12.6.1-3 no longer ships a fedora-* RPM (my URL 404'd).
The AlmaLinux 9 (EL9) RPM is compatible with Fedora — tested on the
Fedora 42 VM: "dnf install <almalinux9 rpm>" resolves the deps and
"wkhtmltopdf --version" reports 0.12.6.1 (with patched qt). Use it, with
an AlmaLinux 8 fallback.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Tested make install_os on a real Fedora 42 VM (exit 0: gcc 15, psql,
node 22 installed). Two fixes from the run:
- postgresql-setup --initdb failed with "invalid locale settings"
because Fedora cloud images ship no LANG; force a valid locale via
PGSETUP_INITDB_OPTIONS=--locale=C.UTF-8 and wipe a partial data dir
first (a failed init leaves /var/lib/pgsql/data/log behind and blocks
the retry). Verified: cluster PG 16 initialised, service active,
erplibre superuser created.
- the dev-tools group was referenced by display name ("C Development
Tools and Libraries", "No match"); use the group id c-development.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
From the QEMU install logs: Debian 12 succeeded, Ubuntu 24.04 and
Fedora 42 failed.
Ubuntu (install_debian_dependency.sh):
- apt failed with "Could not get lock" (cloud-init/unattended-upgrades
hold it on first boot) -> all apt-get calls now use
DPkg::Lock::Timeout=600 so apt waits for the lock.
- "postgis" is not a package on Ubuntu 24.04, so the postgresql line
exited 1 before installing build-essential -> no C compiler -> pyenv
could not build Python 3.12.10. PostGIS is now best-effort (tries
postgis, then postgresql-postgis) and never aborts; postgresql-contrib
is added.
Fedora (new install_fedora_dependency.sh, wired into install_dev.sh):
- install_dev.sh dispatched Fedora to the apt script; it now has a
fedora / ID_LIKE branch calling a dnf-based dependency installer
(dev tools, postgresql-server + initdb, pyenv build deps, node, etc.),
using --refresh --skip-unavailable to tolerate mirror/name issues.
Bootstrap (todo.py): the dnf "curl git make" install hit a GPG/checksum
failure on a fresh image; it now uses --refresh and retries after
"dnf clean all".
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
After "Opening the interactive monitor..." the install now lists every
VM's log file path, so they can be read or shared even when leaving the
dashboard before the installs finish.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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 <logdir>/*.log" to share the logs.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Deploying ERPLibre (single VM [1] and infra [4]) now asks "Interactive
monitoring dashboard?". When yes, the installs run DETACHED (setsid, one
log file + exit marker per VM) and a Textual dashboard opens:
- left: a table of VMs with live status (⏳ running / ✅ done / ❌ failed
with exit code) and elapsed time;
- right: the selected VM's log, tailed live (f toggles follow);
- footer: the VM's ssh command; s suspends the dashboard and SSHes in;
- q quits back to the menu — the installs keep running detached, so you
can leave before they finish and reopen later on the same log dir.
New module qemu_install_monitor.py holds the detached launcher and the
Textual app. The remote install script is factored into
_qemu_erplibre_remote_cmd. Falls back gracefully if textual is missing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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) <noreply@anthropic.com>
The infra distribution prompt now accepts "principal" (alongside
numbers and "all"): it deploys the default version of each distro —
ubuntu 24.04, debian 12, fedora 42 — one VM each. The default version is
also marked with a * in the distro listing so it is visible.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Minimal cloud images ship without make (and often without curl), so the
ERPLibre install cloned the repo then died on "make: command not found".
The remote bootstrap now installs curl, git and make via apt (Debian/
Ubuntu) or dnf/yum (Fedora) — running apt-get update first since
cloud-init did not refresh the lists — before cloning and running
make install_os / make install_odoo_18.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Installation was very noisy: poetry ran "install -vvv" and repo sync /
git daemon ran with -v/--verbose. They are now quiet by default
(poetry -q, repo sync -q, no git daemon --verbose) and the detailed
logs come back only when EL_VERBOSE=1.
Applied to install_locally.sh (poetry) and every manifest script (repo
sync + git daemon). env_var.sh documents EL_VERBOSE and respects a value
already set in the environment, so "EL_VERBOSE=1 make install_odoo_18"
works.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
"Deploy a new VM" now asks, after a successful deploy, whether to
install ERPLibre into ~/git/erplibre (pick a branch), just like the
infra command.
Installing ERPLibre no longer only clones the repo: after the clone it
runs "make install_os" then "make install_odoo_18" over SSH (the
erplibre user has passwordless sudo from cloud-init). The Odoo target is
a constant (ERPLIBRE_ODOO_TARGET) and both the single-VM and infra flows
use the same install path.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The cleanup command now runs, each listed and confirmed independently:
1. orphan files (disks/seeds/.part/nvram) — as before;
2. ghost domains: libvirt domains whose disk no longer exists ->
offer destroy + undefine --nvram;
3. stale codename-named Ubuntu images (noble/resolute/... left by the
move to /releases/ version-named images) -> targeted delete;
4. orphan ~/.ssh/config entries: "Host erplibre-*" blocks with no
matching VM (personal SSH hosts are never touched);
5. stale libvirt DHCP leases whose MAC belongs to no VM (best-effort
rewrite of the dnsmasq status file + SIGHUP; they also self-expire);
6. the full base-image cache remains an explicit opt-out at the end.
Detections verified read-only on the host.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
New Manage entry that finds and lists QEMU leftovers with no matching
libvirt domain, then asks before deleting:
- working disks /var/lib/libvirt/images/<name>.qcow2
- cloud-init seeds .../iso/<name>-seed.iso
- interrupted downloads (*.part)
- orphan UEFI nvram files
Sizes are shown human-readable with a total. Cached base cloud images
(reusable) are listed and offered separately, since removing them forces
a re-download; this also cleans the stale codename-named duplicates left
by the switch to /releases/ version-named images.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Three papercuts hit when creating a VM from the menu:
1. Disk size "30" was passed verbatim to qemu-img resize, which read it
as 30 BYTES and failed ("use --shrink"). The prompt now shows units
(e.g. 30G, 1T) and a bare number is normalised to GB ("30" -> "30G"),
in the menu and in deploy_qemu.py itself.
2. The VM name is no longer required: leaving it blank uses the default
erplibre-<distro>-<version> (e.g. erplibre-ubuntu-2604). Distro and
version are now asked first so the default can be offered.
3. The menu ignored the deploy exit code and carried on to "wait for the
VM IP" even after a failure, hanging forever with the error scrolled
off. It now checks the return code and stops with a clear message.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Debian 13 (trixie) genericcloud dropped the BIOS/GRUB-pc bootloader, so
SeaBIOS looped forever on "Booting Debian GNU/Linux" and the install
never completed. Boot via UEFI (OVMF) by default: trixie boots, and
Ubuntu/Debian 12/Fedora keep working (verified Debian 13 and Ubuntu
24.04 end to end). Add a --bios opt-out for hosts without OVMF, and pull
the UEFI firmware (ovmf / edk2-ovmf) with the libvirt/QEMU stack. VM
deletion already removes the nvram (undefine --nvram).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
When the exact osinfo id is missing from the local osinfo-db (e.g.
ubuntu26.04, fedora43/44), the fallback was detect=on,require=off, which
made virt-install fall back to "generic" and warn "VM performance may
suffer". Fall back instead to the latest KNOWN osinfo of the same distro
(ubuntu26.04 -> ubuntu25.10, fedora44 -> fedora42): proper virtio/OS
defaults, no warning. detect=on,require=off remains only if nothing of
the family is known.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Debian VMs came up with no configured user, no hostname and no SSH key,
so neither the console (erplibre/erplibre) nor SSH worked. Two causes,
found via the guest cloud-init.log:
1. The NoCloud seed was attached as a CD-ROM. Debian's initramfs does
not load the CD driver (sr_mod) at the init-local stage, so the
"cidata" volume was invisible and cloud-init fell back to an empty
DMI seed (Ubuntu tolerates the CD). Attach the seed as a read-only
virtio disk instead: virtio-blk is in the initramfs, the label is
seen immediately, and the user-data is applied. Works for Ubuntu too.
2. The user was added to group "admin", which does not exist on Debian
(it does on Ubuntu) -> useradd failed and the user was never created.
Use "sudo" (present on both) instead.
Validated end to end: Debian 12 and Ubuntu 24.04 both get the erplibre
user (in sudo), the hostname, console login erplibre/erplibre and SSH by
key.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The infra "Parallel deployments" prompt defaulted to a fixed 4. It now
defaults to the host CPU count (os.cpu_count()), still capped by the
number of selected VMs, with a fallback of 4 when the count is unknown.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
_write_ssh_config_entry used a DOTALL (?ms) regex to remove an existing
"Host <name>" block; with DOTALL, ".*" crossed newlines and matched from
the block to the end of the file, so re-adding an already-present host
deleted every entry after it. Dropping DOTALL (keep MULTILINE, match
indented lines with [^\n]*) removes only the target block and preserves
the rest. Not a parallelism issue — the writes are sequential.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Interim Ubuntu releases (24.10, 25.04) were removed from cloud-images
/current/ once EOL, so their image URL 404'd. Switch the Ubuntu image
URL to /releases/<version>/release/ubuntu-<version>-server-cloudimg-
<arch>.img, which stays available for every published release, and drop
the EOL interim versions from the catalogue (keep supported LTS + 25.10
+ 26.04).
Also:
- silence the "crypt is deprecated" DeprecationWarning (openssl fallback
already covers Python 3.13+);
- a 404 during download now reports "image not found (EOL/removed), pick
a supported LTS" instead of blaming connectivity;
- add a NoCloud network-config (DHCP on e*) to the seed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The infra fleet was deployed one VM at a time, each blocking up to 90s
waiting for its DHCP lease. Deploys now run concurrently through a
ThreadPoolExecutor (default min(count, 4), promptable), with each job's
output captured and printed per VM as it finishes (no interleaving). VMs
are created with --no-wait-ip so workers return quickly; IPs are then
collected once for the ~/.ssh/config step. Preferred over shelling out
to GNU parallel: no external dependency, grouped output, and it stays
integrated with the ssh-config and ERPLibre-clone steps.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
cloud-init's package_update/package_upgrade forced apt to hit the distro
mirrors on first boot; on a slow or unreachable network that stalls
cloud-init and delays SSH availability. They are now off by default
(SSH is already present in the cloud images and enabled via runcmd);
--apt-update opts back in (and runs upgrade too unless --no-upgrade).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
VMs were created with user "erplibre" but NO password (lock_passwd:
true), so the serial console refused every login and only SSH-by-key
worked — hence "can't connect".
- The todo deploy flow (single VM and infra) now sets a default console
password "erplibre" for the "erplibre" user, in addition to the SSH
key, so virsh console and password SSH both work. It is shown as
"Console/SSH login: erplibre / erplibre" and can be changed at the
single-VM prompt.
- The console entry now prints the default login and the Ctrl+] hint.
- After creating a VM, offer to add it to ~/.ssh/config (Host <name>,
HostName <ip>, User erplibre) so "ssh <name>" just works; the infra
flow asks once and adds every VM. Existing blocks for the same Host
are replaced, the rest of the file is preserved, mode kept 600.
Note: VMs created before this change have no console password — delete
and redeploy them to get the erplibre/erplibre login.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Group the longer menus with section headers (reordering items so the
numbering stays sequential within each section):
- Execute: Development / Data / Sources & documentation / AI &
automation / Deployment, network & security / Preferences.
- QEMU/KVM: Deployment / Manage / Catalog.
- Database: Backup / Restore / Danger zone.
- RTK: Setup / Status / Optimize.
- Config: Generate / Advanced.
The QEMU config-entry lookup now skips section rows so appended
makefile entries still map to the right number.
Also in the QEMU menu:
- new "Open the console on a VM" entry (lists VMs, asks which, reminds
Ctrl+] to quit, runs virsh console);
- "Show a VM IP address" accepts "all" to print every VM's IP.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fill_help_info() now renders {"section": "..."} entries as a header line
without consuming a number, so numbering stays continuous over the real
commands (the hardcoded elif chains keep matching). The Deploy menu is
split into "Local", "SSH (remote host)" and "Virtualization &
notifications", making the long SSH block easy to scan.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
New QEMU/KVM menu entry to remove one, several or all VMs. It lists the
defined domains, lets you pick by number or "all", asks whether to also
delete the disk images (the qcow2 working disk + the seed ISO), shows
what will be removed and asks for confirmation. Each VM is powered off
(destroy) then undefined (--nvram, with a fallback for older virsh);
disks are only deleted when explicitly requested.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Debian deploy appeared frozen: cloud.debian.org sent this host to an
unreachable mirror and urlretrieve had no timeout, so it hung forever;
worse, output was block-buffered under the todo pipe so nothing showed.
- Stream downloads via urlopen with a 30s per-operation timeout: a dead
mirror now fails fast instead of hanging.
- Try several Debian mirrors in order (cloud.debian.org, then two
acc.umu.se mirrors) — first responsive one wins.
- Reconfigure stdout to line-buffered in main() so headers and progress
appear live even when captured by the menu.
- Add openssh-server to the cloud-init packages and a runcmd enabling
ssh/sshd, so every VM (Debian genericcloud included) is SSH-reachable.
- Add a timeout to the SHA256SUMS fetch too.
Validated: debian-11 fails over from cloud.debian.org to gemmei and
completes; debian-12 downloads fully.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Generalise the _is_yes() helper (y/yes/o/oui) across the whole file so
French answers work everywhere, and add _is_no() (n/no/non) for the
default-yes prompts. Converted: system-install, Pycharm, SSH-password
(default yes via _is_no), template overwrite, keep-temp-database (was
locale-gated, now accepts both), git-repo fetch and the mobile
personalize/debug/picture prompts. Drop the now-unused get_lang import.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The confirmations only matched "y", but the French prompts show "(o/N)",
so answering "o" (oui) was treated as no — the infra deployment aborted
with "Annulé." right after the user confirmed. Add a _is_yes() helper
(y/yes/o/oui) and use it for the infra deploy confirmation, the ERPLibre
install prompt, the disk-overwrite prompt and the SHA256 verify prompt.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
New QEMU/KVM entry that stands up a fleet of minimal VMs, one per
selected cloud image. It:
- lets you pick distros then versions (multi-select, "all" per level, or
the whole catalogue), reading the specs straight from deploy_qemu.py so
there is no duplication;
- prints a plan with each VM's minimum RAM/disk, the total concurrent RAM
and virtual disk, and the host's available RAM, warning when the fleet
cannot all run at once;
- deploys sequentially after confirmation (minimum sizing per version),
skipping VMs that already exist;
- optionally clones ERPLibre into ~/git/erplibre on each VM, asking which
branch (list fetched via git ls-remote) and pulling git in through
cloud-init.
Naming is erplibre-<distro>-<version> (e.g. erplibre-ubuntu-2404).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Every menu now prints a breadcrumb line (e.g. "📍 TODO › Execute ›
Deploy › QEMU/KVM") right above "Command:", so it is always clear where
you are and the path can be copied to describe a menu unambiguously.
The trail is derived from the call stack via a method-name -> label map,
so no menu method had to change: fill_help_info and the three inline
menus just render self._menu_header() instead of t("Command:").
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Generalise the deployer beyond Ubuntu with a distro registry and a new
--distro flag (ubuntu default, debian, fedora). Each distro keeps its
own codenames, osinfo ids and per-version minimum RAM/disk. Image URLs
are built per distro (Ubuntu current/, Debian latest/, Fedora resolved
from the release index since it has no "latest" link).
Add --list-images to print the whole catalogue with specs, exposed in
the todo menu ("List available images and specs"); the deploy/download
flows now prompt for the distro first.
Resilient osinfo: when the local osinfo-db does not know an id (e.g.
ubuntu26.04, fedora43+), fall back to virt-install detect=on,require=off
instead of failing. Ubuntu 26.04 (resolute) is now enabled.
Also: throttle the download progress to integer-percent steps (single
updating line on a TTY, at most 101 lines when captured by the menu),
and skip the spurious "network is already active" error by checking the
libvirt network state before net-start.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The "Show a VM IP address" entry prompted for a VM name with no visible
list, so the user had to guess the name/ID. It now runs virsh list --all
first and asks for a "VM name or ID", making the expected input obvious.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the fixed 8192 MB / 4 vCPU / 20G defaults with the minimum
resources required by the chosen Ubuntu version (libosinfo/osinfo-db
values): 20.04 -> 2048 MB/5G, 22.04 -> 2048 MB/10G, 24.04+ -> 3072
MB/20G. --vcpus now defaults to 2. This stops a small host from being
starved by an oversized default (an 8 GB VM failed to allocate on a
6.7 GB host) and silences the libvirt "less than recommended" warning.
In the todo menu, leaving the RAM/vCPU/disk fields blank now means
"version minimum" (the flag is simply not passed) instead of forcing
8192/4/20G. Any explicit value still overrides. README regenerated.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Expose the QEMU deploy script from the interactive assistant so users
can create, preview, download and manage Ubuntu VMs without memorising
CLI flags. Adds a QEMU/KVM entry under Deploy, its fr/en translations,
and an extensible qemu_from_makefile section in todo.json.
Generated by Claude Code 2.1.210 claude-opus-4-8
Co-Authored-By: Mathieu Benoit <mathben@technolibre.ca>
The script previously required a manually supplied image path and only
installed the client tools, so a bare host aborted at virt-install with
a missing libvirt-sock. It now derives and downloads the Ubuntu cloud
image from --version, detects and installs the full libvirt+QEMU stack
(daemon and system emulator included) through the host package manager,
then enables libvirtd. Adds a --download-only mode, a hypervisor
preflight check, clean error messages instead of Python tracebacks, and
a bilingual usage guide (README).
Generated by Claude Code 2.1.210 claude-opus-4-8
Co-Authored-By: Mathieu Benoit <mathben@technolibre.ca>
Give users a guided, confirmation-gated way to drop one or all
databases from the interactive CLI. Previously this meant running
make db_drop_all or odoo_bin db --drop by hand, which is easy to
mistype and offers no safeguard. The new entry requires an explicit
'oui'/'yes' (default no) before any irreversible deletion.
Generated by Claude Code 2.1.191 claude-sonnet-4-6
Co-Authored-By: Mathieu Benoit <mathben@technolibre.ca>
Add a one-command installer for the ntfy push notification server
(Ubuntu/Debian and Arch Linux), wired into the todo.py Deploy menu.
Users can now deploy a local ntfy server from the CLI and subscribe
to topics from their mobile device (ntfy app) to receive push
notifications from ERPLibre.
Generated by Claude Code 2.1.101 model claude-sonnet-4-6
Co-Authored-By: Mathieu Benoit <mathben@technolibre.ca>
Enable deploying and managing ERPLibre on remote servers via SSH
directly from make and the interactive todo.py CLI, since only
local deployment was previously supported.
- New conf/make.ssh.Makefile with 11 targets: ssh_check, ssh_push,
ssh_install, ssh_run, ssh_stop, ssh_restart, ssh_status, ssh_logs,
ssh_make, ssh_install_systemd, ssh_install_nginx
- Variables: SSH_HOST (required), SSH_USER, SSH_PORT, SSH_KEY,
SSH_PATH, SSH_TARGET, SSH_DOMAIN, SSH_ADMIN_EMAIL
- Execute > Deploy menu extended with 11 SSH options in todo.py
- All strings translated fr/en in todo_i18n.py
Generated by Claude Code 2.1.101 model claude-sonnet-4-6
Co-Authored-By: Mathieu Benoit <mathben@technolibre.ca>
@capacitor/cli v8.x requires Node.js >=22.0.0. The install script
was pinning NODE_MAJOR=20, causing a fatal error when installing
the mobile app via todo.py.
Generated by Claude Code 2.1.101 model claude-sonnet-4-6
Co-Authored-By: Mathieu Benoit <mathben@technolibre.ca>
Poetry install and git-repo sync are independent (different write
paths). Running them sequentially wastes time. Split install_locally.sh
into EL_PHASE=setup|poetry|all phases so install_locally_dev.sh can
background the repo sync while poetry runs in the foreground, reducing
total install time by up to 50% on slow connections.
Set EL_PARALLEL_INSTALL=0 to restore sequential behavior for debugging.
Generated by Claude Code 2.1.88 model claude-sonnet-4-6
Co-Authored-By: Mathieu Benoit <mathben@technolibre.ca>