Le colis du genome ne portait que la branche : les etiquettes de release n'arrivaient jamais sur la forge du site. Il les porte desormais, le runner les pousse sans forcer, et la forge est relue par son API. publier.py pousse aussi les etiquettes vers eregion. Le wiki, jamais publie sur la forge du site, l'est. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
117 lines
5.6 KiB
Python
117 lines
5.6 KiB
Python
#!/usr/bin/env python3
|
|
"""Publier le genome : eregion ET la forge du site, en un seul geste.
|
|
|
|
POURQUOI (2026-09-28). La forge du SITE fait autorite (D-81) : un ecosysteme s'y reproduit.
|
|
Mais les commits naissent sur le poste, et n'y arrivent que par `make genome-pousser` — un
|
|
geste A PART, que rien ne force ni ne signale. Un `git push` vers eregion ne la met pas a
|
|
jour. Le 2026-08-26, elle avait quatre commits de retard, dont le correctif du pare-feu
|
|
Proxmox : un ecosysteme reconstruit ce jour-la aurait reproduit le defaut qu'on croyait
|
|
repare. Le 2026-09-28 encore, `make genome-etat` la trouvait en retard d'un commit.
|
|
|
|
Le retard vient d'un geste qu'on oublie : on fait du geste un seul geste.
|
|
|
|
CE QUE CA FAIT, pour chaque depot que le runner du site porte (`serveur_ops_depots`, la MEME
|
|
liste que `genome-pousser` — lue, pas recopiee) :
|
|
1. recuperer eregion, et REFUSER si eregion porte des commits que le poste n'a pas (une
|
|
demande de fusion acceptee, par exemple) : publier sans les integrer les ecraserait
|
|
sur la forge du site ;
|
|
2. signaler ce qui n'est pas committe — cela ne part pas ;
|
|
3. pousser sur eregion, la branche ET les etiquettes (2026-10-07 : les releases n'allaient
|
|
nulle part d'elles-memes ; une etiquette deplacee est refusee, une release ne bouge pas) ;
|
|
puis `make genome-pousser`, qui porte aussi les etiquettes et verifie que la forge les a,
|
|
puis `make genome-etat` : la forge du site doit porter la meme tete que le poste, sinon on
|
|
le dit.
|
|
|
|
make publier tous les depots du genome
|
|
make publier DEPOT=ops-chezlepro
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
RACINE = Path(__file__).resolve().parent.parent
|
|
DEPOTS = RACINE.parent
|
|
|
|
|
|
def git(depot: Path, *args: str) -> subprocess.CompletedProcess:
|
|
return subprocess.run(["git", "-C", str(depot), *args], capture_output=True, text=True)
|
|
|
|
|
|
def genome() -> list[dict]:
|
|
r = subprocess.run(["ansible-inventory", "-i", "scripts/site_inventaire.py", "--host", "site-ops-01"],
|
|
capture_output=True, text=True, cwd=RACINE, env=os.environ)
|
|
try:
|
|
return json.loads(r.stdout).get("serveur_ops_depots") or []
|
|
except ValueError:
|
|
raise SystemExit("Inventaire du site illisible : la liste du genome est inconnue.\n" + r.stderr[:300])
|
|
|
|
|
|
def main() -> int:
|
|
ap = argparse.ArgumentParser(description=__doc__.splitlines()[0])
|
|
ap.add_argument("--depot", default="", help="un seul depot (nom sur la forge)")
|
|
args = ap.parse_args()
|
|
|
|
liste = [d for d in genome() if not args.depot or d["depot"] == args.depot]
|
|
if not liste:
|
|
raise SystemExit(f"Aucun depot du genome ne s'appelle « {args.depot} ».")
|
|
|
|
refus = []
|
|
for d in liste:
|
|
depot = DEPOTS / d["dest"]
|
|
if not (depot / ".git").is_dir():
|
|
print(f" - {d['depot']:<17} pas de copie sur ce poste — rien a publier")
|
|
continue
|
|
branche = git(depot, "branch", "--show-current").stdout.strip()
|
|
if git(depot, "remote", "get-url", "eregion").returncode != 0:
|
|
print(f" - {d['depot']:<17} aucun remote `eregion`")
|
|
continue
|
|
git(depot, "fetch", "-q", "eregion")
|
|
distant = f"eregion/{branche}"
|
|
if git(depot, "rev-parse", "--verify", "-q", distant).returncode == 0:
|
|
manquants = git(depot, "rev-list", "--count", f"HEAD..{distant}").stdout.strip()
|
|
if manquants not in ("", "0"):
|
|
refus.append(d["depot"])
|
|
print(f" !! {d['depot']:<17} eregion porte {manquants} commit(s) absent(s) du poste "
|
|
f"— les integrer d'abord (`git pull --rebase eregion {branche}`)")
|
|
continue
|
|
sale = [l for l in git(depot, "status", "--porcelain").stdout.splitlines() if l.strip()]
|
|
if sale:
|
|
print(f" ~ {d['depot']:<17} {len(sale)} changement(s) NON committe(s) — ils ne partent pas")
|
|
avance = git(depot, "rev-list", "--count", f"{distant}..HEAD").stdout.strip() or "?"
|
|
r = git(depot, "push", "-q", "eregion", branche, "refs/tags/*:refs/tags/*")
|
|
if r.returncode != 0:
|
|
refus.append(d["depot"])
|
|
print(f" !! {d['depot']:<17} push refuse : {r.stderr.strip()[:120]}")
|
|
continue
|
|
print(f" ok {d['depot']:<17} eregion a jour ({avance} commit(s) pousse(s))")
|
|
|
|
if refus:
|
|
print(f"\nREFUS : {', '.join(refus)}. Rien n'a ete porte a la forge du site — la corriger "
|
|
f"en retard vaut mieux que la corriger avec un depot incomplet.")
|
|
return 2
|
|
|
|
print("\nPorter a la forge du site (runner) :")
|
|
env = dict(os.environ, **({"DEPOT": args.depot} if args.depot else {}))
|
|
if subprocess.run(["make", "--no-print-directory", "genome-pousser"] + ([f"DEPOT={args.depot}"] if args.depot else []),
|
|
cwd=RACINE, env=env, capture_output=True).returncode != 0:
|
|
print(" ECHEC de `make genome-pousser` : eregion est a jour, la forge du site NON.")
|
|
return 3
|
|
r = subprocess.run(["make", "--no-print-directory", "genome-etat"], cwd=RACINE,
|
|
capture_output=True, text=True)
|
|
for ligne in r.stdout.splitlines():
|
|
if '"msg": "' in ligne and " : poste " in ligne:
|
|
print(" " + ligne.split('"msg": "', 1)[1].replace("\\n", " ").rstrip('"'))
|
|
if r.returncode != 0:
|
|
print(" La forge du site ne porte PAS la tete du poste — voir `make genome-etat`.")
|
|
return 4
|
|
print("Publie : eregion et la forge du site portent la tete du poste.")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|