From 91dc90f22b927c36ade9443bdcd9e6815177ea50 Mon Sep 17 00:00:00 2001 From: Daniel Allaire Date: Mon, 28 Sep 2026 17:20:06 -0400 Subject: [PATCH] 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 --- CHANGELOG.md | 16 +++++ Makefile | 7 ++ docs/runbooks-construction.yml | 6 ++ scripts/publier.py | 115 +++++++++++++++++++++++++++++++++ 4 files changed, 144 insertions(+) create mode 100644 scripts/publier.py diff --git a/CHANGELOG.md b/CHANGELOG.md index c32b7f6..4f2d869 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/Makefile b/Makefile index 87ca05b..8e6fdf6 100644 --- a/Makefile +++ b/Makefile @@ -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= 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= pour un seul @set -e; \ voute="$$(dirname "$$(readlink -f underlay.yml)")/underlay.vault.yml"; \ diff --git a/docs/runbooks-construction.yml b/docs/runbooks-construction.yml index 84fb23a..6933d3b 100644 --- a/docs/runbooks-construction.yml +++ b/docs/runbooks-construction.yml @@ -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] diff --git a/scripts/publier.py b/scripts/publier.py new file mode 100644 index 0000000..66c6f64 --- /dev/null +++ b/scripts/publier.py @@ -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())