[FIX] qemu form: tick nothing in the catalog by default

The form opened with the four main versions already ticked, so the plan showed
four VMs nobody had asked for. F5 on an untouched form would have created
them. Deploying is expensive and hard to undo — the list now starts empty and
the choice is made, not inherited.

The « * » still marks each distro's main version, and F7 ticks exactly those
four in one keystroke, so nothing is lost but the presumption.

With an empty list a « 0 VM · 0 vCPU » total teaches nothing, so the footer
says what to do instead: tick what to deploy, F7 main versions, F6 all.

Verified headless: no box ticked on open, F5 producing no spec at all, F7
giving back the four, and the ticks surviving a switch to « all
architectures » (4 of 30) since identity is distro/version/arch, not rank.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Mathieu Benoit 2026-08-01 06:27:25 -04:00
parent 7c1016552b
commit 25cc780005
2 changed files with 33 additions and 14 deletions

View file

@ -453,33 +453,36 @@ def run_deploy_form(ctx, run_app: bool = True):
t("Disk"),
t("Status"),
)
self._reload_catalog(select_defaults=True)
self._reload_catalog(first_load=True)
# -- catalogue et recalcul ------------------------------------- #
def _entries(self):
return catalog.get(self.arch, [])
def _reload_catalog(self, select_defaults=False):
"""Recharge la liste à cocher après un changement d'architecture.
Les cases cochées sont conservées quand la même entrée existe
encore (l'identité est (distro, version, arch), pas le rang)."""
def _reload_catalog(self, first_load=False):
"""(Re)charge la liste à cocher.
RIEN n'est coché d'avance : déployer coûte cher, et une case
pré-cochée ferait créer une VM que personne n'a demandée. Le « * »
marque toujours la version principale, et F7 les coche toutes.
Après un changement d'architecture, les cases déjà cochées sont
conservées quand l'entrée existe encore — l'identité est
(distro, version, archi), pas le rang dans la liste."""
widget = self.query_one("#f_catalog", SelectionList)
keep = set()
if not select_defaults:
keep = {
keep = (
set()
if first_load
else {
entry_key(self._entries_before[i])
for i in widget.selected
if i < len(self._entries_before)
}
)
widget.clear_options()
entries = self._entries()
for i, e in enumerate(entries):
on = (
e.get("default", False)
if select_defaults
else (entry_key(e) in keep)
)
widget.add_option((entry_label(e), i, on))
widget.add_option((entry_label(e), i, entry_key(e) in keep))
self._entries_before = entries
self._recompute()
@ -525,6 +528,14 @@ def run_deploy_form(ctx, run_app: bool = True):
f"{r['disk_gb']}G",
f"{icon}{r['note']}",
)
if not self.rows:
# Rien de coché : un total à zéro n'apprend rien, on dit
# plutôt comment remplir la liste.
self.query_one("#totals", Static).update(
f" {t('Tick what to deploy')}"
f"{t('F7 main versions · F6 all')}"
)
return
n, cpus, ram, disk = plan_totals(self.rows)
warn = ""
if free_ram and ram > free_ram:

View file

@ -1475,6 +1475,14 @@ TRANSLATIONS = {
"fr": "toutes",
"en": "all",
},
"Tick what to deploy": {
"fr": "Cocher ce qu'on veut déployer",
"en": "Tick what to deploy",
},
"F7 main versions · F6 all": {
"fr": "F7 versions principales · F6 tout",
"en": "F7 main versions · F6 all",
},
"exists - skipped": {
"fr": "existe — ignorée",
"en": "exists - skipped",