[ADD] qemu guide: dire comment démarrer le bureau, sur les VM qui en ont un

Une VM graphique peut arriver sur une console texte — graphical.target est
atteinte avant que le paquet du bureau soit là, et « systemctl enable gdm » rend
0 sans rien faire sur Debian et Ubuntu, où l'unité n'a pas de WantedBy. La
commande qui répare tient sur une ligne ; encore faut-il la lire quelque part.

Le guide de connexion porte donc un bloc « Bureau », avec l'état et le
« enable --now ». Il n'apparaît que si la VM a été déployée avec un bureau :
sur un serveur, ces commandes ne mèneraient à aucune unité. Le drapeau existait
déjà côté déploiement, il n'y avait qu'à le passer.

--- EN ---

A graphical VM can land on a text console — graphical.target is reached before
the desktop package exists, and "systemctl enable gdm" returns 0 doing nothing
on Debian and Ubuntu, where the unit has no WantedBy. The command that repairs
it is one line; it still has to be readable somewhere.

The login guide therefore carries a "Desktop" block, with the state and the
"enable --now". It only shows when the VM was deployed with a desktop: on a
server those commands would point at no unit. The flag already existed on the
deploy side; it only had to be passed along.

Assisted-by: Claude Opus 5
This commit is contained in:
Mathieu Benoit 2026-08-20 03:32:49 -04:00
parent 49bc354d25
commit 75dbeb3258
2 changed files with 83 additions and 0 deletions

View file

@ -1660,6 +1660,26 @@ SERVICE_GUIDE: tuple[tuple[str, str, str], ...] = (
) )
# N'apparaît que sur une VM déployée AVEC un bureau. Vécu : GNOME installé,
# gdm3 installé, cible graphique par défaut… et la console restait en mode texte.
# graphical.target était déjà atteinte quand le paquet est arrivé, et une cible
# active ne rattrape pas un service ajouté après coup. « enable » seul n'y change
# rien sur Debian et Ubuntu — l'unité n'a pas de WantedBy, seulement un alias —
# d'où le « --now », qui démarre.
DESKTOP_GUIDE: tuple[tuple[str, str, str], ...] = (
(
"systemctl status display-manager",
"état du bureau graphique",
"graphical desktop state",
),
(
"sudo systemctl enable --now gdm",
"le démarrer (« --now » : enable seul ne suffit pas)",
'start it ("--now": enable alone does nothing)',
),
)
def erplibre_guide( def erplibre_guide(
el_dir: str, el_make: str = "", editor: str = "" el_dir: str, el_make: str = "", editor: str = ""
) -> tuple[tuple[str, str, str], ...]: ) -> tuple[tuple[str, str, str], ...]:
@ -1770,11 +1790,14 @@ def build_motd(
el_dir: str = "", el_dir: str = "",
el_make: str = "", el_make: str = "",
editor: str = "", editor: str = "",
desktop: bool = False,
) -> str: ) -> str:
"""Texte du /etc/motd de la VM. Fonction PURE : aucun I/O, donc testable. """Texte du /etc/motd de la VM. Fonction PURE : aucun I/O, donc testable.
La section ERPLibre n'apparaît qu'avec `el_dir` : une VM déployée sans La section ERPLibre n'apparaît qu'avec `el_dir` : une VM déployée sans
installation ne doit pas annoncer un dépôt et un service qui n'existent pas. installation ne doit pas annoncer un dépôt et un service qui n'existent pas.
Le bloc « Bureau » suit la même règle avec `desktop` : sur un serveur, ces
deux commandes ne mèneraient à aucune unité.
""" """
body: list[str] = [] body: list[str] = []
mgr = DISTRO_PKG.get(distro, "") mgr = DISTRO_PKG.get(distro, "")
@ -1795,6 +1818,14 @@ def build_motd(
body.append("") body.append("")
el_rows = erplibre_guide(el_dir, el_make, editor) el_rows = erplibre_guide(el_dir, el_make, editor)
body += motd_block("ERPLibre", el_rows, lang, gloss_col(el_rows)) body += motd_block("ERPLibre", el_rows, lang, gloss_col(el_rows))
if desktop:
body.append("")
body += motd_block(
_pick(("Bureau", "Desktop"), lang),
DESKTOP_GUIDE,
lang,
gloss_col(DESKTOP_GUIDE),
)
body.append("") body.append("")
body += motd_block( body += motd_block(
_pick(("Système", "System"), lang), sys_rows, lang, narrow _pick(("Système", "System"), lang), sys_rows, lang, narrow
@ -2016,6 +2047,7 @@ def guide_files(args: argparse.Namespace) -> list[tuple[str, str, str, str]]:
args.erplibre_dir, args.erplibre_dir,
args.erplibre_make, args.erplibre_make,
editor, editor,
bool(args.desktop),
), ),
"", "",
) )

View file

@ -352,5 +352,56 @@ class TestInstallerGuideNames(unittest.TestCase):
) )
class TestDesktopBlock(unittest.TestCase):
"""Le bloc « Bureau » : présent seulement là où un bureau existe.
Vécu : une VM graphique restait sur une console texte, GNOME installé et
gdm3 installé — graphical.target était déjà atteinte quand le paquet est
arrivé. La commande qui répare tient sur une ligne, encore faut-il la lire
quelque part. Sur un serveur, elle ne mènerait à aucune unité : le bloc
n'y apparaît pas.
"""
def _motd(self, desktop):
return dq.build_motd(
"ubuntu",
"26.04",
"amd64",
"fr",
"~/git/erplibre",
"install_odoo_18",
"vim",
desktop,
)
def test_a_server_gets_no_desktop_block(self):
self.assertNotIn("Bureau", self._motd(False))
def test_a_graphical_vm_gets_it(self):
self.assertIn("Bureau", self._motd(True))
def test_it_carries_the_command_that_repairs(self):
"""« --now » et non « enable » seul : sur Debian et Ubuntu, l'unité n'a
pas de WantedBy, et « enable » rend 0 sans rien faire."""
motd = self._motd(True)
self.assertIn("systemctl enable --now gdm", motd)
self.assertIn("systemctl status display-manager", motd)
def test_it_says_why_now_matters(self):
self.assertIn("--now", self._motd(True))
def test_it_stays_inside_the_frame(self):
"""Le guide est encadré : une ligne trop longue casse la boîte."""
lines = self._motd(True).splitlines()
width = max(len(line) for line in lines)
border = [line for line in lines if line.startswith("╭")][0]
self.assertEqual(len(border), width)
def test_the_default_is_no_block(self):
"""Un appelant qui n'en sait rien n'annonce pas un bureau."""
motd = dq.build_motd("ubuntu", "26.04", "amd64", "fr")
self.assertNotIn("Bureau", motd)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()