113 lines
4.7 KiB
Python
113 lines
4.7 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
"""`genome_colis.py` : le colis porte la branche ET les releases jusqu'à la forge du site.
|
||
|
|
|
||
|
|
POURQUOI (2026-10-07). Le colis ne portait que `main`. Les étiquettes de release
|
||
|
|
n'arrivaient donc jamais sur la forge du site, qui fait autorité (D-81) : `v2026.08.21` et
|
||
|
|
`v2026.10.07` y manquaient, et il a fallu les pousser à la main.
|
||
|
|
|
||
|
|
Sur de vrais dépôts git, dans un répertoire jetable (aucun réseau) :
|
||
|
|
- un poste, une forge nue, un runner cloné depuis la forge ;
|
||
|
|
- le poste fait un commit et étiquette une release ; `emballer`, puis `recevoir` sur le
|
||
|
|
runner, avec la forge pour `pushurl` ;
|
||
|
|
- la forge porte la nouvelle tête ET l'étiquette ; un dépôt sans étiquette passe aussi ;
|
||
|
|
- une étiquette déplacée sur le poste est REFUSÉE : une release ne bouge pas.
|
||
|
|
"""
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import json
|
||
|
|
import os
|
||
|
|
import subprocess
|
||
|
|
import sys
|
||
|
|
import tempfile
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
RACINE = Path(__file__).resolve().parents[2]
|
||
|
|
COLIS = RACINE / "scripts" / "genome_colis.py"
|
||
|
|
ENV = {**os.environ, "GIT_AUTHOR_NAME": "t", "GIT_AUTHOR_EMAIL": "t@t", "GIT_COMMITTER_NAME": "t",
|
||
|
|
"GIT_COMMITTER_EMAIL": "t@t", "GIT_CONFIG_GLOBAL": os.devnull, "GIT_CONFIG_NOSYSTEM": "1"}
|
||
|
|
|
||
|
|
ECHECS: list[str] = []
|
||
|
|
|
||
|
|
|
||
|
|
def verifier(cond: bool, msg: str) -> None:
|
||
|
|
print(("OK " if cond else "ECHEC ") + msg)
|
||
|
|
if not cond:
|
||
|
|
ECHECS.append(msg)
|
||
|
|
|
||
|
|
|
||
|
|
def git(*args: str, cwd: Path | None = None) -> str:
|
||
|
|
r = subprocess.run(["git", *args], cwd=cwd, capture_output=True, text=True, env=ENV)
|
||
|
|
if r.returncode != 0:
|
||
|
|
raise RuntimeError(f"git {' '.join(args)} : {r.stderr.strip()}")
|
||
|
|
return r.stdout.strip()
|
||
|
|
|
||
|
|
|
||
|
|
def colis(*args: str) -> subprocess.CompletedProcess:
|
||
|
|
return subprocess.run([sys.executable, str(COLIS), *args], capture_output=True, text=True, env=ENV)
|
||
|
|
|
||
|
|
|
||
|
|
def monter(d: Path) -> tuple[Path, Path, Path]:
|
||
|
|
forge, poste, runner = d / "forge.git", d / "poste", d / "runner"
|
||
|
|
git("init", "-q", "--bare", "-b", "main", str(forge))
|
||
|
|
git("init", "-q", "-b", "main", str(poste))
|
||
|
|
(poste / "a").write_text("1")
|
||
|
|
git("add", "a", cwd=poste)
|
||
|
|
git("commit", "-q", "-m", "un", cwd=poste)
|
||
|
|
git("push", "-q", str(forge), "main", cwd=poste)
|
||
|
|
git("clone", "-q", str(forge), str(runner))
|
||
|
|
return forge, poste, runner
|
||
|
|
|
||
|
|
|
||
|
|
def porter(poste: Path, runner: Path, forge: Path, d: Path) -> subprocess.CompletedProcess:
|
||
|
|
paquet = d / "x.bundle"
|
||
|
|
e = colis("emballer", str(poste), str(paquet))
|
||
|
|
if e.returncode != 0:
|
||
|
|
return e
|
||
|
|
return colis("recevoir", str(runner), str(paquet), str(forge))
|
||
|
|
|
||
|
|
|
||
|
|
def main() -> int:
|
||
|
|
with tempfile.TemporaryDirectory() as d:
|
||
|
|
d = Path(d)
|
||
|
|
forge, poste, runner = monter(d)
|
||
|
|
|
||
|
|
# Un depot sans etiquette : rien ne change pour lui.
|
||
|
|
(poste / "a").write_text("2")
|
||
|
|
git("commit", "-q", "-am", "deux", cwd=poste)
|
||
|
|
r = porter(poste, runner, forge, d)
|
||
|
|
verifier(r.returncode == 0 and git("rev-parse", "main", cwd=forge) == git("rev-parse", "HEAD", cwd=poste),
|
||
|
|
f"sans etiquette : la forge porte la tete du poste ({r.stderr.strip()[-160:]})")
|
||
|
|
|
||
|
|
# Une release annotee.
|
||
|
|
(poste / "a").write_text("3")
|
||
|
|
git("commit", "-q", "-am", "trois", cwd=poste)
|
||
|
|
git("tag", "-a", "v1", "-m", "release v1", cwd=poste)
|
||
|
|
r = porter(poste, runner, forge, d)
|
||
|
|
recu = json.loads(r.stdout) if r.returncode == 0 else {}
|
||
|
|
verifier(r.returncode == 0 and "v1" in git("tag", "-l", cwd=forge).split(),
|
||
|
|
f"la release voyage jusqu'a la forge ({r.stderr.strip()[-160:]})")
|
||
|
|
verifier(git("rev-parse", "v1", cwd=forge) == git("rev-parse", "v1", cwd=poste),
|
||
|
|
"la forge porte le MEME objet d'etiquette que le poste (annotee, pas recreee)")
|
||
|
|
verifier(recu.get("etiquettes") == ["v1"], f"le runner dit les etiquettes qu'il porte ({recu.get('etiquettes')})")
|
||
|
|
e = json.loads(colis("emballer", str(poste), str(d / "y.bundle")).stdout)
|
||
|
|
verifier(e.get("etiquettes") == ["v1"], "l'emballage dit les etiquettes du poste")
|
||
|
|
|
||
|
|
# Une release deplacee sur le poste : refusee, la forge garde l'ancienne.
|
||
|
|
ancienne = git("rev-parse", "v1", cwd=forge)
|
||
|
|
(poste / "a").write_text("4")
|
||
|
|
git("commit", "-q", "-am", "quatre", cwd=poste)
|
||
|
|
git("tag", "-f", "-a", "v1", "-m", "v1 deplacee", cwd=poste)
|
||
|
|
r = porter(poste, runner, forge, d)
|
||
|
|
verifier(r.returncode != 0 and git("rev-parse", "v1", cwd=forge) == ancienne,
|
||
|
|
f"une etiquette deplacee est refusee, la forge garde la release ({r.stderr.strip()[-120:]})")
|
||
|
|
|
||
|
|
if ECHECS:
|
||
|
|
print(f"\n{len(ECHECS)} echec(s).")
|
||
|
|
return 1
|
||
|
|
print("\nLe colis porte la branche et les releases ; une release ne bouge pas.")
|
||
|
|
return 0
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
sys.exit(main())
|