#!/bin/bash # GENERE par Set-OPS (role serveur_ops_site). Ne pas editer a la main. # # SONDE « genome-a-jour » — la forge du site porte-t-elle ce que le poste a publie ? # # Contrat : docs/supervision-conception.md — une ligne, 0/1/3. # # POURQUOI UN FILET (2026-09-28). La forge du site fait autorite (D-81) ; les commits naissent # sur le poste. `make publier` pousse sur eregion PUIS sur la forge du site — mais un # `git push` fait a la main, ou un runner injoignable au moment de publier, la laisse en # retard sans rien dire. Le 2026-08-26 : quatre commits, dont un correctif du pare-feu. # # LE REPERE EST EREGION, OU CHAQUE COMMIT ARRIVE EN PREMIER — comme repere seulement : eregion # reste hors du chemin du genome. On ne compare que les depots qu'eregion laisse LIRE SANS # IDENTIFIANT (`serveur_ops_site_genome_repere`) : donner au site un jeton pour les depots # prives le lierait a une forge heritee. Les autres sont NOMMES comme non compares. # # UN ECART NEUF N'EST PAS UN RETARD : `make publier` pousse eregion une minute avant la forge # du site. On retient depuis quand chaque ecart dure, et on ne parle qu'au-dela de # `serveur_ops_site_genome_tolerance_min`. set -uo pipefail python3 - <<'EOF' import json, subprocess, time, os FORGE = "https://forge.{{ domaine_interne }}/{{ serveur_ops_forge_organisation | default('genome') }}" DEPOTS = {{ serveur_ops_depots | default([]) | to_json }} REPERE = {{ serveur_ops_site_genome_repere | to_json }} TOLERANCE = {{ serveur_ops_site_genome_tolerance_min }} * 60 ETAT = "{{ serveur_ops_site_genome_etat }}" def tete(url, branche): r = subprocess.run(["git", "ls-remote", url, f"refs/heads/{branche}"], capture_output=True, text=True, timeout=8, env=dict(os.environ, GIT_TERMINAL_PROMPT="0")) return r.stdout.split()[0] if r.returncode == 0 and r.stdout.strip() else None try: vus = json.load(open(ETAT)) except (OSError, ValueError): vus = {} maintenant = time.time() en_retard, illisibles, a_jour, non_compares, neufs = [], [], [], [], [] for d in DEPOTS: nom, branche = d["depot"], d.get("branche", "main") if nom not in REPERE: non_compares.append(nom) continue try: site, ref = tete(f"{FORGE}/{nom}.git", branche), tete(REPERE[nom], branche) except subprocess.TimeoutExpired: site = ref = None if not site or not ref: illisibles.append(nom + (" (forge du site)" if not site else " (repere)")) continue if site == ref: a_jour.append(nom) vus.pop(nom, None) continue depuis = vus.setdefault(nom, {"depuis": maintenant, "site": site, "repere": ref})["depuis"] minutes = int((maintenant - depuis) / 60) (en_retard if maintenant - depuis > TOLERANCE else neufs).append( f"{nom} (forge {site[:7]}, repere {ref[:7]}, depuis {minutes} min)") with open(ETAT + ".tmp", "w") as f: json.dump(vus, f) os.replace(ETAT + ".tmp", ETAT) nc = f" Non compares (prives sur le repere) : {', '.join(non_compares)}." if non_compares else "" perf = f"| a_jour={len(a_jour)} en_retard={len(en_retard)} non_compares={len(non_compares)}" if en_retard: print(f"FORGE DU SITE EN RETARD : {'; '.join(en_retard)}. `make publier`.{nc} {perf}") raise SystemExit(1) if illisibles and not a_jour: print(f"Rien de comparable : {', '.join(illisibles)} illisible(s).{nc} {perf}") raise SystemExit(3) suite = f" Ecart recent, dans la tolerance : {'; '.join(neufs)}." if neufs else "" ill = f" Illisible(s) : {', '.join(illisibles)}." if illisibles else "" tete = f"Forge du site a jour : {', '.join(a_jour)}." if a_jour else "Aucun retard au-dela de la tolerance." print(f"{tete}{suite}{ill}{nc} {perf}") raise SystemExit(0) EOF