[ADD] déploiement : la VM clone le dépôt distant, pas ce checkout

« Le problème est revenu » — alors qu'il était corrigé la veille. La VM ne
reçoit pas le checkout d'ici : elle CLONE la branche depuis le dépôt distant.
Tout ce qui tourne dedans — install_proxmox.sh, les scripts d'installation, le
Makefile — vient donc de là.

Vécu deux fois de suite. Le correctif de /etc/hosts était commité ici, absent
du distant : chaque VM déployée ensuite recevait l'ancien script, et le même
défaut revenait à l'identique. Rien ne le disait, et il a fallu comparer les
deux versions du fichier à la main pour comprendre. Soixante-et-onze commits
séparaient les deux.

L'écart est donc dit AVANT de déployer, là où l'on peut encore renoncer : le
nombre, les trois premiers sujets, et « git push ». Sur les deux voies, car
les deux clonent.

Une branche que le distant ne connaît pas n'est pas un écart — c'est une
question qui ne se pose pas. La dire quand même vaudrait un avertissement à
chaque déploiement d'une branche neuve.

--- EN ---

"The problem came back" — though it had been fixed the day before. The VM does
not receive this checkout: it CLONES the branch from the remote. Everything
that runs inside it — install_proxmox.sh, the install scripts, the Makefile —
comes from there.

Twice in a row. The /etc/hosts fix was committed here and absent from the
remote: every VM deployed afterwards got the old script, and the same defect
returned unchanged. Nothing said so, and it took comparing both versions of
the file by hand to understand. Seventy-one commits separated them.

The gap is therefore stated BEFORE deploying, where you can still back out:
the count, the first three subjects, and "git push". On both paths, since both
clone.

A branch the remote does not know is not a gap — it is a question that does
not arise. Saying it anyway would mean a warning on every deployment of a new
branch.

Assisted-by: Claude Opus 5
(cherry picked from commit de27be5e736eb6e9bd01efd292e01c3b2231f91a)
This commit is contained in:
Mathieu Benoit 2026-08-25 06:19:21 -04:00
parent 98c2355ca0
commit 93b6256dba
5 changed files with 166 additions and 0 deletions

View file

@ -1483,6 +1483,13 @@ class ProxmoxMenuMixin:
f" ERPLibre : {spec['install'].get('label') or ''}"
f" ({spec['install'].get('branch')})"
)
# La VM CLONE la branche depuis le dépôt distant : tout ce qui
# tourne dedans — install_proxmox.sh compris — vient de là, pas
# d'ici. Un correctif non poussé est invisible pour elle.
for ligne in self._qemu_branch_gap_lines(
spec["install"].get("branch") or ""
):
print(f" {ligne}")
return self._is_yes_default_yes(
input(f"\n{t('Deploy this VM now? (Y/n): ')}")
)

View file

@ -885,6 +885,12 @@ class QemuDeployMixin:
f" {t('Install:')} {t('branch')} {br_txt}, "
f"{t('profile')} {lb_txt}, {env}"
)
# La VM ne reçoit pas CE checkout : elle CLONE la branche depuis
# le dépôt distant. Un correctif commité ici et non poussé n'y est
# donc pas, et le défaut « revient » alors qu'il est corrigé —
# vécu deux fois de suite sur install_proxmox.sh.
for ligne in self._qemu_branch_gap_lines(br_txt):
print(f" {ligne}")
else:
print(f" {t('Install:')} {t('no')}")
flavour = spec.get("desktop")

View file

@ -2537,6 +2537,67 @@ class QemuManageMixin:
nom = (res.stdout or "").strip()
return "" if res.returncode or nom == "HEAD" else nom
@staticmethod
def _qemu_branch_gap(branche):
"""Combien de commits LOCAUX manquent à origin/<branche>, et lesquels.
Rend (nombre, [sujets]) — (0, []) quand il n'y a rien à dire, ou quand
la question ne se pose pas (pas de dépôt, pas de distant).
Pourquoi le déploiement s'en soucie : la VM ne reçoit PAS le checkout
d'ici, elle CLONE la branche depuis le dépôt distant. Tout ce qui
tourne dans la VM — install_proxmox.sh, les scripts d'installation, le
Makefile — vient donc de là.
Vécu deux fois de suite. Un correctif de install_proxmox.sh, commité
ici, absent du distant : chaque VM déployée ensuite recevait l'ancien
script, et le défaut « revenait » alors qu'il était corrigé. Rien ne
le disait ; il a fallu comparer les deux versions à la main.
"""
if not branche:
return 0, []
try:
res = subprocess.run(
[
"git",
"log",
"--oneline",
"--no-decorate",
f"origin/{branche}..HEAD",
],
capture_output=True,
text=True,
timeout=15,
)
except (OSError, subprocess.SubprocessError):
return 0, []
# Une branche inconnue du distant, ou aucun distant : ce n'est pas un
# écart à signaler, c'est une question qui ne se pose pas.
if res.returncode:
return 0, []
sujets = [
ligne.strip()
for ligne in (res.stdout or "").splitlines()
if ligne.strip()
]
return len(sujets), sujets
def _qemu_branch_gap_lines(self, branche, limite=3):
"""Les lignes à dire avant de déployer, ou []."""
nombre, sujets = self._qemu_branch_gap(branche)
if not nombre:
return []
lignes = [
f"⚠ {t('The VM clones')} origin/{branche}, "
f"{t('not this checkout.')}",
f" {nombre} {t('local commit(s) are missing there:')}",
]
lignes += [f" {s}" for s in sujets[:limite]]
if nombre > limite:
lignes.append(f" … {nombre - limite} {t('more')}")
lignes.append(f" → git push {t('to deploy your own work.')}")
return lignes
def _qemu_branch_list(self):
"""Branches distantes d'ERPLibre, triées. Vide si le réseau manque.

View file

@ -3382,6 +3382,22 @@ TRANSLATIONS = {
"fr": "rejouer install_proxmox.sh sur l'hôte : il corrige /etc/hosts et empêche cloud-init de le défaire.",
"en": "replay install_proxmox.sh on the host: it fixes /etc/hosts and stops cloud-init undoing it.",
},
"The VM clones": {
"fr": "La VM clone",
"en": "The VM clones",
},
"not this checkout.": {
"fr": "et non ce checkout.",
"en": "not this checkout.",
},
"local commit(s) are missing there:": {
"fr": "commit(s) local(aux) y manquent :",
"en": "local commit(s) are missing there:",
},
"to deploy your own work.": {
"fr": "pour déployer votre propre travail.",
"en": "to deploy your own work.",
},
"No storage able to hold a VM disk.": {
"fr": "Aucun stockage capable d'héberger un disque de VM.",
"en": "No storage able to hold a VM disk.",

View file

@ -686,6 +686,82 @@ class TestUnParcMixte(unittest.TestCase):
self.assertEqual(vu["kw"]["desktop"], "")
class TestLaVmCloneLeDepotDistant(unittest.TestCase):
"""« Le problème est revenu » — alors qu'il était corrigé.
La VM ne reçoit pas le checkout d'ici : elle CLONE la branche depuis le
dépôt DISTANT. Tout ce qui tourne dedans — install_proxmox.sh, les
scripts d'installation, le Makefile — vient donc de là. Un correctif
commité ici et non poussé lui est invisible.
Vécu deux fois de suite : la correction de /etc/hosts était dans le
checkout depuis la veille, absente du distant, et chaque VM déployée
ensuite recevait l'ancien script. Il a fallu comparer les deux versions à
la main pour le voir. Rien ne le disait."""
def _todo(self, sortie, code=0):
import sys
sys.argv = ["todo.py"]
from script.todo.todo import TODO
todo = TODO.__new__(TODO)
faux = mock.Mock(returncode=code, stdout=sortie)
return todo, faux
def test_the_gap_is_counted_and_named(self):
todo, faux = self._todo(
"abc1234 [FIX] un correctif\ndef5678 [ADD] autre chose\n"
)
with mock.patch("subprocess.run", return_value=faux):
nombre, sujets = todo._qemu_branch_gap("develop")
self.assertEqual(nombre, 2)
self.assertIn("[FIX] un correctif", sujets[0])
def test_nothing_to_say_when_the_remote_is_up_to_date(self):
todo, faux = self._todo("")
with mock.patch("subprocess.run", return_value=faux):
self.assertEqual(todo._qemu_branch_gap("develop"), (0, []))
self.assertEqual(todo._qemu_branch_gap_lines("develop"), [])
def test_an_unknown_remote_branch_is_not_a_gap(self):
# « origin/xyz » inconnu fait échouer git : ce n'est pas un écart à
# signaler, c'est une question qui ne se pose pas. Le dire quand même
# serait un avertissement à chaque déploiement d'une branche neuve.
todo, faux = self._todo("", code=128)
with mock.patch("subprocess.run", return_value=faux):
self.assertEqual(todo._qemu_branch_gap("nouvelle"), (0, []))
def test_no_branch_asks_nothing(self):
todo, _faux = self._todo("")
self.assertEqual(todo._qemu_branch_gap(""), (0, []))
def test_the_long_list_is_trimmed_but_counted(self):
todo, faux = self._todo(
"\n".join(f"c{i} sujet {i}" for i in range(10))
)
with mock.patch("subprocess.run", return_value=faux):
lignes = todo._qemu_branch_gap_lines("develop", limite=2)
texte = " ".join(lignes)
self.assertIn("10", texte, "le nombre TOTAL doit rester lisible")
self.assertIn("8", texte, "et ce qui n'est pas montré, dit")
self.assertIn("git push", texte)
def test_both_screens_say_it_before_deploying(self):
# L'avertissement ne vaut que là où on peut encore renoncer.
import inspect
from script.todo.proxmox_menu import ProxmoxMenuMixin
from script.todo.qemu_deploy import QemuDeployMixin
for fn in (
ProxmoxMenuMixin._pve_confirm_spec,
QemuDeployMixin._qemu_print_recap,
):
with self.subTest(fonction=fn.__name__):
self.assertIn("_qemu_branch_gap_lines", inspect.getsource(fn))
class TestLePontQuiNeMeneraitNullePart(unittest.TestCase):
"""Le pont NAT était écrit AVANT qu'on sache si le NAT existe.