Set-OPS-Public/scripts/ma_forge.py
Daniel Allaire f307a23345 le wiki suit le genome : un locataire n en est pas depositaire
Decision : seuls les SITES portent la forge, le cache APT et les artefacts. Meme
geste que le retrait de serveur_artefacts d un plan de locataire, un cran plus
loin — le site fournit tout ce dont un tenant a besoin pour venir au monde.

La generalisation de forge_amorcer a un locataire est revenue en arriere : elle
implementait le modele ecarte, et la garder en ferait un piege.

ma_forge ne derive plus MA forge mais la forge qui porte MON genome. Le signal
est deja au plan : serveur_ops_forge_externe dit je lis mon genome ailleurs. Qui
le declare consulte, il ne republie pas.

Reste vrai : une forge de locataire peut exister pour LE CODE DE SES GENS —
c est l offre Atelier. Ce n est pas l endroit ou vit le moteur.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Crgis8CxCWkAGFA1ecBz3q
2026-09-14 14:41:23 -04:00

106 lines
4.8 KiB
Python
Executable file

#!/usr/bin/env python3
"""L'adresse du wiki de la forge QUI PORTE MON GENOME.
CHAQUE RUNNER PUBLIE POUR SA FORGE (decide le 2026-09-14).
La premiere idee etait qu'un controleur pousse vers plusieurs forges, avec un temoin
portant une liste. Elle CENTRALISAIT : un seul poste aurait eu besoin d'un acces sur
chaque forge, et le temoin serait devenu un fait global que personne ne detient.
Le modele retenu suit la ligne du reste du systeme — les voutes, les runners, les
sauvegardes : **chacun sert les siens**. Le runner du site publie sur la forge du site ;
celui d'un locataire sur la forge de ce locataire. Un employe de TechnoLibre lit la forge
de TechnoLibre, pas celle de son hebergeur.
DEUX NOTIONS A NE PAS CONFONDRE, et c'est ce qui a rendu le sujet trouble :
la forge AMONT (`serveur_ops_forge_amont`) ou je LIS le genome
MA forge (derivee ici) ou je SERS les miens
Le runner d'un locataire lit sur la forge du SITE et sert sur la SIENNE. Les deux adresses
different, et le wiki appartient a la seconde.
LE WIKI SUIT LE GENOME (decide le 2026-09-14). Un locataire ne porte PAS le moteur : il le
lit sur la forge de son hebergeur, comme il y prend ses paquets et son gabarit. Lui faire
publier la documentation du moteur sur SA forge reviendrait a l'en rendre depositaire —
donc a lui imposer l'organisation, les depots, le miroir a tenir synchrone et la preuve
qu'il ne derive pas. Pour un service qu'il ne rend a personne.
LE SIGNAL EST DEJA AU PLAN : `serveur_ops_forge_externe: true` dit « je lis mon genome
ailleurs ». Un ecosysteme qui le declare ne publie pas — il consulte, la ou le genome vit.
CE QUI RESTE VRAI D'UNE FORGE DE LOCATAIRE : elle peut exister, et servir LE CODE DE SES
GENS — depots, tickets, revues. C'est l'offre « Atelier ». Ce n'est simplement pas
l'endroit ou vit le moteur.
CE QUI N'EST PAS DERIVABLE, ET C'EST DIT PLUTOT QUE DEVINE : le poste du mainteneur n'est
le runner d'aucun ecosysteme. Il publie vers le domicile PUBLIC du projet, qui n'est la
forge d'aucun plan. La, `WIKI_REMOTE` reste explicite — et il l'emporte toujours.
"""
from __future__ import annotations
import sys
from pathlib import Path
RACINE = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(RACINE / "scripts"))
def ma_forge() -> tuple[str | None, str]:
"""(url du wiki, explication). `None` quand rien n'est derivable."""
try:
from inventory_rules import charger_applications, instance_courante
except Exception as e:
return None, f"moteur illisible ({e.__class__.__name__})"
inst = instance_courante()
if not inst:
return None, "aucune instance montee"
# `charger_applications` VEUT UN CHEMIN, pas rien. L'appeler sans argument levait un
# `TypeError` que le `except` transformait en « plan illisible » — un message poli pour
# une faute de programmation. Le voila nomme.
try:
apps = (charger_applications(Path(inst) / "plan" / "applications.yml")
or {}).get("applications") or {}
except (OSError, ValueError) as e:
return None, f"plan illisible ({e.__class__.__name__})"
# UN ECOSYSTEME QUI LIT SON GENOME AILLEURS NE LE PUBLIE PAS. Le wiki suit le genome :
# il vit la ou le moteur vit, et un locataire n'est pas son depositaire.
gv = Path(inst) / "inventories" / "principal" / "group_vars" / "serveur_ops.yml"
if gv.is_file():
try:
import yaml
d = yaml.safe_load(gv.read_text(encoding="utf-8")) or {}
except (OSError, yaml.YAMLError):
d = {}
if d.get("serveur_ops_forge_externe"):
amont = d.get("serveur_ops_forge_amont") or "sa forge amont"
return None, (f"{Path(inst).resolve().name} lit son genome sur {amont} — "
"le wiki s'y consulte, il ne s'y republie pas d'ici")
# LE NOM DU SERVICE, PAS CELUI DE LA MACHINE. Un FQDN expose survit au demenagement du
# service ; `forge-01` non. C'est la meme regle que partout ailleurs dans ce depot.
for nom, app in (apps.items() if isinstance(apps, dict) else []):
if str((app or {}).get("groupe")) != "serveur_forgejo":
continue
exposes = (app or {}).get("expose") or []
if not exposes:
return None, "`serveur_forgejo` est au plan mais n'expose aucun nom"
fqdn = str(exposes[0])
depot = RACINE.name.lower()
return (f"ssh://git@{fqdn}/genome/{depot}.wiki.git",
f"forge de l'ecosysteme monte ({Path(inst).resolve().name}), exposee en {fqdn}")
return None, f"{Path(inst).resolve().name} ne declare aucun `serveur_forgejo`"
if __name__ == "__main__":
url, pourquoi = ma_forge()
if url:
print(url)
print(f"note : {pourquoi}", file=sys.stderr)
raise SystemExit(0)
print(f"note : pas de forge derivable — {pourquoi}", file=sys.stderr)
raise SystemExit(1)