Relecture adversaire du commit précédent : il avait CRÉÉ un danger. Le rapport
s'écrivant maintenant VM par VM, celui de la descente EN COURS est le plus
récent, et « --detruire » l'aurait choisi — qm destroy --purge sur l'arbre que
le processus installait encore. Deux garde-fous : un PID dans le rapport, et
un refus net tant qu'un autre deep_proxmox.py tourne. Le second est nécessaire
car une descente déjà lancée a l'ancien module en mémoire.
Reconnu par ARGUMENT, pas par sous-chaîne : mon propre « pgrep -f
deep_proxmox.py » de surveillance donnait deux faux positifs sur trois.
Trois autres, mêmes preuves :
- un rapport vide plus récent masquait celui qui nommait les VM réelles ;
- detruire() ne créditait jamais l'étage 1 : le décompte était décalé de un
dans tous les cas, donc l'avertissement sortait toujours ;
- _ssh_config_drop_hosts prenait l'indentation pour de la syntaxe. Sur un bloc
au corps non indenté, seule la ligne Host partait et ssh rattachait
« StrictHostKeyChecking no » au bloc précédent — un serveur de production.
Et un bloc partagé (« Host prod-db vm-a ») partait en entier.
Les cinq correctifs meurent sous mutation.
--- EN ---
Adversarial review of the previous commit: it had CREATED a hazard. With the
report now written VM by VM, the RUNNING descent's is the most recent, and
"--detruire" would have picked it — qm destroy --purge on the tree the process
was still installing. Two guards: a PID in the report, and a flat refusal
while another deep_proxmox.py runs. The second is needed because an
already-running descent holds the old module in memory.
Matched by ARGUMENT, not substring: my own monitoring "pgrep -f
deep_proxmox.py" produced two false positives out of three.
Three more, same evidence:
- a newer empty report masked the one naming the real VMs;
- detruire() never credited level 1: the count was off by one in every case,
so the warning always fired;
- _ssh_config_drop_hosts took indentation for syntax. On a block with an
unindented body only the Host line went, and ssh attached
"StrictHostKeyChecking no" to the preceding block — a production server.
And a shared block ("Host prod-db vm-a") went entirely.
All five fixes die under mutation.
Assisted-by: claude-opus-5
(cherry picked from commit 7d348d976f96e420b4fec4d879911b658a730ffa)
|
||
|---|---|---|
| .. | ||
| auto_ask.py | ||
| database_manager.py | ||
| deploy_form_extras.py | ||
| deploy_form_lib.py | ||
| deploy_form_plan.py | ||
| kdbx_manager.py | ||
| logo_ascii.txt | ||
| longtest_menu.py | ||
| migration_form.py | ||
| migration_stats.py | ||
| migration_status.py | ||
| migration_status_tui.py | ||
| proxmox_deploy_form.py | ||
| proxmox_menu.py | ||
| qemu_access.py | ||
| qemu_deploy.py | ||
| qemu_deploy_form.py | ||
| qemu_hardware.py | ||
| qemu_install.py | ||
| qemu_install_monitor.py | ||
| qemu_manage.py | ||
| qemu_menu.py | ||
| README.base.md | ||
| README.fr.md | ||
| README.md | ||
| source_todo.sh | ||
| textual_setup.py | ||
| todo.json | ||
| todo.py | ||
| todo_example.json | ||
| todo_file_browser.py | ||
| todo_i18n.py | ||
| todo_prefs.py | ||
| todo_telemetry.py | ||
| todo_upgrade.py | ||
| version_manager.py | ||
TODO is an assistant robot to use ERPLibre
Execute it with ./script/todo/todo.py or make todo.
For a new project, copy todo_example.json to private/todo/todo_override.json | private/todo/todo_override_private.json and edit it.
The mail/ package is the mail client reachable from Assistant > Mail:
several IMAP/SMTP accounts, a local cache, and a Textual TUI. See
../../doc/EMAIL.md.
Where the code lives
todo.py carries the menus and the general helpers. Everything around a
single subject sits in its own file, and the whole thing is assembled by
mixins on the TODO class — one file, one subject, its header states its
boundary.
| File | What it owns |
|---|---|
todo.py |
the menus, the configuration, the general helpers |
qemu_menu.py |
the QEMU/KVM menu, the image catalogue, the statistics |
qemu_deploy.py |
deciding then running a deployment |
qemu_install.py |
the recipes run inside a VM |
qemu_manage.py |
lifecycle, disks, hardware, cleanup, addresses |
qemu_access.py |
SSH, tunnels, consoles, Android emulator |
proxmox_menu.py |
the same, on a REMOTE Proxmox VE host |
The two deployment forms — libvirt here, Proxmox over there — ask the same questions, so they share a foundation rather than each holding a copy:
| File | What it owns |
|---|---|
deploy_form_lib.py |
pure logic (sizes, plan, totals, spec), the shared CSS, the resource-row factory, the progress view |
deploy_form_plan.py |
the plan's gestures: overrides, locks, copies, renaming, free values |
qemu_deploy_form.py |
what QEMU/KVM adds: desktops, tools, branches, install profiles |
proxmox_deploy_form.py |
what Proxmox adds: host, storage, bridge, VMID, address |
A form inherits PlanMixin and provides three hooks: which presets each
resource offers, the name a VM would fall back to, and what a lock freezes.
test_todo_deploy_form_lib.py fails if a form redefines a gesture the
foundation already carries — that is what keeps the architecture from drifting
back into two copies.