publier : eregion et la forge du site en un seul geste, verifie

La forge du site fait autorite et n etait nourrie que par un geste a part,
genome-pousser, qu on oublie. make publier pousse, porte, et verifie ; il refuse
si eregion porte des commits absents du poste.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Daniel Allaire 2026-09-28 17:20:06 -04:00
parent 974fabc40a
commit 91dc90f22b
4 changed files with 144 additions and 0 deletions

View file

@ -1,5 +1,21 @@
# CHANGELOG — Set-OPS
## 2026-09-28 (20) — `make publier` : eregion ET la forge du site, en un geste
**Le défaut.** La forge du site fait autorité (D-81), mais les commits naissent sur le poste
et n'y arrivent que par `make genome-pousser` — un geste à part, que rien ne force ni ne
signale. Un `git push` vers eregion la laisse en retard. Le 2026-08-26 : quatre commits,
dont le correctif du pare-feu Proxmox. Aujourd'hui même, `make genome-etat` la trouvait en
retard d'un commit.
**`make publier`** (`scripts/publier.py`), pour chaque dépôt que le runner du site porte —
la liste de `genome-pousser` (`serveur_ops_depots`), lue et non recopiée : refuse si
eregion porte des commits absents du poste (une fusion acceptée serait écrasée sur la forge
du site), signale ce qui n'est pas committé, pousse sur eregion ; puis `genome-pousser`,
puis `genome-etat`, et dit si la forge du site porte bien la tête du poste. Un refus arrête
tout AVANT la forge du site : mieux vaut la laisser en retard que la nourrir d'un dépôt
incomplet. Ce commit est le premier publié ainsi.
## 2026-09-28 (19) — Chaque semaine, chaque sauvegarde est rouverte pour de vrai
**Le trou.** `sauvegarde` disait qu'un instantané existe, récent et non vide ; rien ne disait

View file

@ -1269,6 +1269,13 @@ genome-etat: ## Ce que la forge du site porte, face au poste — ne corrige rien
playbooks/maintenance/genome_etat.yml "$${supp[@]}" $(ARGS)
.PHONY: genome-pousser
publier: ## Publie le genome d'un geste : eregion PUIS la forge du site, et le verifie — DEPOT=<nom> pour un seul
@# LE RETARD VIENT D'UN GESTE QU'ON OUBLIE (2026-09-28) : un `git push` vers eregion ne
@# met pas la forge du site a jour, et c'est elle qui fait autorite. Refuse si eregion
@# porte des commits absents du poste (une fusion acceptee) — ne pas les ecraser.
python3 scripts/publier.py $(if $(DEPOT),--depot "$(DEPOT)")
.PHONY: publier
genome-pousser: ## Le runner du site pousse le genome sur la forge du site — DEPOT=<nom> pour un seul
@set -e; \
voute="$$(dirname "$$(readlink -f underlay.yml)")/underlay.vault.yml"; \

View file

@ -144,6 +144,12 @@ runbooks:
pourquoi: >-
Second passage. Celui-ci doit finir a zero echec ; s'il n'y arrive pas, c'est
un vrai defaut, plus un maillon manquant.
- cible: publier
nature: ecriture
variables: [DEPOT]
pourquoi: >-
Le geste quotidien : eregion ET la forge du site, puis la verification. Un
`git push` seul laisse la forge du site en retard sans que rien le dise.
- cible: genome-pousser
nature: ecriture
variables: [DEPOT]

115
scripts/publier.py Normal file
View file

@ -0,0 +1,115 @@
#!/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 ;
puis `make genome-pousser`, 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)
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())