Bonnes pratiques appliquées au dépôt
Inventaire des conventions et bonnes pratiques réellement en place dans ce dépôt — code, documentation, normes, process. Chaque entrée renvoie à la décision qui la fonde (un ADR) ou à l’outil/la convention qui l’applique.
Cette page applique l’ADR 0061 (posture d’adoption des bonnes pratiques) : elle rend visible et vérifiable le corpus de pratiques que le dépôt a adoptées au fil de ses décisions. C’est une page de référence (ADR 0059) — elle liste, elle ne décide pas (le pourquoi vit dans chaque ADR cité).
94 pratiques recensées sur 6 domaines. Liste tenue à jour à la main lors d’une revue ; la conformité à ces pratiques est, elle, vérifiée automatiquement (
pnpm check:gouvernance, ADR 0060).
Sommaire
Section intitulée « Sommaire »Six domaines : Reproductibilité, validation & preuves (14) · Gouvernance documentaire (14) · Git, commits & merge (12) · CI, lint & qualité de code (20) · Sécurité & supply-chain (18) · Langage & architecture du code (16).
[[toc]]
Par culture d’ingénierie (vue transverse)
Section intitulée « Par culture d’ingénierie (vue transverse) »Deux grilles orthogonales lisent le même corpus. Les sections suivantes
classent les pratiques par mécanisme (ce que le script check_gouvernance
vérifie). Cette section les regroupe par culture d’ingénierie — la lecture «
quelles cultures le dépôt revendique »
(ADR 0062). Une même pratique peut
relever de plusieurs cultures.
| Culture | Statut | Ce qu’elle recouvre ici | ADR pivots |
|---|---|---|---|
| GitOps | ✅ en place | Git source de vérité, merge-commit, pas de push direct, Argo CD + Gitea, patron apply | 0022, 0037, 0044 |
| DataOps | ✅ en place | Dagster, lineage OpenLineage/Marquez, base managée CNPG, contrat d’interface, pas de PII | 0026, 0028, 0033, 0041, 0043 |
| DevSecOps | ✅ en place | digests multi-arch, actions par SHA, Trivy IaC, secrets non versionnés, PSA, etcd chiffré, WireGuard | 0006, 0014, 0019, 0023 |
| IaC | ✅ en place | catalogue de topologies déclaratif, idempotence Ansible prouvée, provisioning OpenTofu | 0023, 0032, 0033 |
| Platform Engineering | 🔶 en construction | paved roads (catalogue) + contrat plateforme↔consommateur ; IDP self-service à venir | 0023, 0043, 0056 |
| MLOps | 🔶 à venir | socle DataOps = prérequis ; aucun composant ML déployé | — |
| SRE | 🔶 partiel | drift detection (state.sh), fraîcheur, etcd backup/RPO, rollback ; sans SLO/error budget | 0042, 0014 |
| FinOps (efficience) | 🔶 partiel | métrologie ressources (Prometheus), capacité Ceph, sizing banc ; à formaliser | 0016 |
| FinOps (coût €) | ❌ écarté | bare-metal non facturé, mono-tenant — pas de chargeback (rouvrirait avec la topo cloud) | — |
Détail et justification : ADR 0062.
Mapping aux référentiels externes notés/normés
Section intitulée « Mapping aux référentiels externes notés/normés »Les passages d’audit du 2026-06-16 ont confronté le dépôt à des référentiels externes (notations cyber, notations & normes externes). Plusieurs étaient revendiqués de fait mais non mappés : on les explicite ici, contrôle par contrôle, conformément à l’ADR 0080. Les référentiels déjà câblés (SemVer, Keep a Changelog, Conventional Commits, Diátaxis) sont couverts par les sections par mécanisme ci-dessous ; les référentiels écartés (DORA sans prod, ISO 27001/25010, SRE error-budget) le sont par choix tracé (cf. passages d’audit).
FAIR (Findable / Accessible / Interoperable / Reusable) — le dépôt-objet-de-
recherche, pas les données (côté atlas,
ADR 0041) :
| Facette | Statut | Preuve |
|---|---|---|
| Findable | ✅ fort | DOI Zenodo concept (CITATION.cff), badge DOI au README, dépôt public |
| Accessible | ✅ fort | dépôt public, LICENSE (MIT) + NOTICE, releases taguées, archive Zenodo pérenne |
| Interoperable | 🔶 moyen | formats ouverts (YAML/Markdown), CITATION.cff ; pas de métadonnées schema.org |
| Reusable | ✅ fort | licence claire, CITATION.cff + provenance, ADR Nygard, manifeste |
OpenGitOps (CNCF, 4 principes) — le dépôt revendique « GitOps » sans citer le référentiel ; il le satisfait sur la couche applicative :
| Principe | Statut | Preuve |
|---|---|---|
| Déclaratif | ✅ PASS | manifestes K8s, Argo CD Applications |
| Versionné & immuable | ✅ PASS | Git source de vérité, merge-commit, images par digest (ADR 0006) |
| Tiré automatiquement | ✅ PASS (applicatif) | Argo CD + Gitea (pull) ; bootstrap impératif assumé |
| Réconcilié en continu | ✅ PASS (applicatif) | Argo CD self-heal ; bootstrap/state.sh (drift 7 couches) côté infra |
Reproductibilité, validation & preuves
Section intitulée « Reproductibilité, validation & preuves »| Pratique | Source |
|---|---|
La preuve de validation d’une brique d’infra est un run e2e from-scratch sur banc (depuis down, d’une traite, sans intervention manuelle entre les phases) — jamais le passage au vert du lint, qui reste obligatoire mais seulement comme filet bon marché. | ADR 0034, ADR 0052 |
On corrige le CODE d’installation (manifeste, rôle Ansible, harnais), jamais l’état du cluster : kubectl patch/apply/create manuel est réservé au diagnostic, et tout correctif repart dans le code versionné puis est re-prouvé par un run. | ADR 0046, ADR 0052 |
Le banc se monte par des CHEMINS d’installation nommés (socle, atlas, storage-real, cluster-dataops) à intention de preuve distincte — l’ancien agrégat ambigu all est supprimé ; l’ordre des couches et les dépendances inter-phases sont une propriété du code, pas de l’opérateur. | ADR 0045 |
| L’idempotence d’un rôle/playbook est prouvée par REJEU : chaque phase Ansible est jouée deux fois et le 2e passage doit donner changed=0 ; un changed_when:true fautif est attrapé là (gate, pas Molecule). | ADR 0052, ADR 0051 |
Un chemin de reprise (rescue: d’un rôle à effet de bord non idempotent : kubeadm init/join) ne compte comme prouvé que s’il a été exercé par une vraie FAUTE INJECTÉE, dans l’ordre opposable : 1er run échoue → compensation tracée (kubeadm reset) → re-jeu du même chemin réussit. | ADR 0052, ADR 0050 |
| Tout drift révélé par un run e2e (écart que le lint ne voit pas) est tracé et catégorisé dans un registre indexé unique (symptôme, cause-racine, correctif, statut), réutilisé comme savoir transverse. | ADR 0034, ADR 0023, ADR 0046 |
| Un garde-fou CI vérifie la FRAÎCHEUR des preuves de banc PAR CHEMIN (atlas 7 j et storage-real 30 j obligatoires, cluster-dataops 90 j warn-only) et alerte — sans bloquer les PR — quand le dernier run consigné d’un chemin dépasse sa cadence ; un atlas frais ne masque plus un storage-real périmé. | ADR 0042, ADR 0045 |
| La source de fraîcheur est un champ daté machine-lisible versionné (pas le mtime Git, non préservé par le checkout CI) : chaque run from-scratch complété appende une entrée datée à l’historique des runs (id, date ISO 8601 UTC, branche, commit, profil, topologie, phases…). | ADR 0042 |
| Le déterminisme des inputs est imposé : images/versions épinglées par digest d’index multi-arch, et valeurs de déploiement dérivées du terrain ou surchargées (jamais codées en dur), pour que le résultat soit reproductible. | ADR 0052, ADR 0006, ADR 0023 |
| Traçabilité commit ↔ run : tout résultat consigné porte le commit exact qui l’a produit, sinon il n’est pas reproductible (on ne sait pas quel code relancer). | ADR 0052 |
| Chaque couche/phase déclare ce qui prouve sa réussite via trois niveaux : assertion pure unitaire (bats hors cluster), gate de phase bloquant sur cluster réel (exit≠0 sinon), et scénario e2e ; toute nouvelle couche ajoute son gate et un chemin n’est validé que si tous les gates de ses couches passent. | ADR 0045, ADR 0017 |
| La logique de décision non triviale est extraite en fonctions PURES (ni ansible, ni ssh, ni réseau ; verdict STATUS|message sur stdout) testables hors banc — bash orchestre, les fonctions pures sont testées par bats, le code complexe migre vers Python (uv/pytest). | ADR 0017 |
| Le banc ne pouvant pas tourner en CI (nested virt, arm64, ~30 min), un check STATIQUE garde-fou vérifie en revue que le contrat d’interface cluster→atlas reste aligné sur le socle réel (platform/), attrapant une dérive (rename de Service, endpoint déplacé) qui ne se verrait sinon qu’au run. | ADR 0043 |
| Un run aidé à la main (état complété hors code) est une preuve INVALIDE : il doit être consigné comme tel avec réserve d’honnêteté et marqué « à re-prouver from-scratch » ; on ne réécrit jamais un résultat passé pour le rendre propre. | ADR 0052, ADR 0023 |
Gouvernance documentaire
Section intitulée « Gouvernance documentaire »| Pratique | Source |
|---|---|
| Toute décision structurante est tracée par un ADR au format Nygard léger (Contexte / Décision / Statut / Conséquences), numéroté et immuable — jamais en bullets dans un TODO. | ADR 0057 · CLAUDE.md |
| Un ADR est immuable : un choix qui change n’est pas réécrit mais marqué ‘Superseded by NNNN’ (ou Deprecated) ; le statut Proposed/Accepted/Superseded est porté dans l’index. | ADR 0057 |
| Frontière de gouvernance par temporalité : un ADR DÉCIDE (immuable), un plan MET EN ŒUVRE (vivant), une issue EXÉCUTE (fermable), une PR LIVRE — rôles non chevauchants ; interdiction de mettre paliers/checklist/TODO dans un ADR. | ADR 0057 |
| Un ADR avec mise en œuvre échelonnée a un plan dédié, THÉMATIQUE et vivant (plan-<thème>.md, non daté), qui référence en en-tête l’ADR fondateur et porte une section « Suivi » obligatoire (paliers cochables, issues créées #NNN, état d’achèvement, renvoi aux runs de preuve). | ADR 0057 |
| L’audit qualité est séparé en une GRILLE permanente (dimensions + critères + méthode, qui ne périme pas) et des PASSAGES datés append-only (notes /5 à une date) ; un passage renvoie aux ADR pour les pourquoi au lieu de les paraphraser, et ses manques deviennent des issues. | ADR 0058 |
| Honnêteté de l’historique : on ne réécrit pas un résultat ou un audit passé pour le rendre ‘propre’ ; les passages d’audit et les RESULTS de banc sont empilés (append-only), datés, et un passage périmé est explicitement marqué plutôt qu’écrasé. | ADR 0058, ADR 0052, ADR 0023 |
| Toute page Markdown versionnée est ‘atteignable’ depuis la documentation (entrée sidebar VitePress ou cible d’un lien depuis une page atteignable) — pas de page orpheline. | ADR 0029 |
| Les liens et ancres internes de la documentation doivent être valides : le build VitePress échoue sur lien/ancre mort, et lychee vérifie les liens internes vers fichiers/ancres. | ADR 0029, ADR 0059 · VitePress |
| La documentation suit la typologie Diátaxis : une page = un mode dominant (Tutorial / How-to / Reference / Explanation) ; les modes se relient par câblage Markdown inline standard dirigé par l’intention, jamais par recopie ni bandeau ‘voir aussi’. | ADR 0059 |
| Valeurs génériques dans TOUT le contenu versionné, y compris la prose (docs, ADR, RUNBOOK) : le dépôt est un catalogue de topologies, pas l’infra réelle d’un auteur ; on génère des valeurs-exemple concrètes (IP 10.0.0.0/22, nœuds cp1/node1…, « l’organisation »), on garde les briques portant une décision (Ceph, Cilium…), et les spécificités réelles vivent dans une config locale gitignorée doublée d’un .example versionné. | ADR 0023 |
| Un écart révélé par un run e2e (drift) que le lint ne voyait pas est consigné dans un registre indexé unique (id Lnn stable, portée, symptôme, cause, correctif, statut corrige/caduc/ouvert) ; les pages de synthèse et journaux y réfèrent par id sans dupliquer le détail, et un drift non résolu reste explicitement ‘ouvert’. | ADR 0023, ADR 0058, ADR 0046 |
| Posture d’adoption bornée : biais ADOPTIF par défaut, mais aucune bonne pratique n’est adoptée contre un ADR Accepted (il faut d’abord superseder), ni sans la tracer par un ADR si elle est structurante — le principe incline la réponse, il ne dispense pas de la délibération ADR. | ADR 0061 |
| Reproductibilité comme principe-chapeau de la documentation des preuves : tout résultat consigné porte le commit exact qui l’a produit, un état complété à la main est marqué ‘à re-prouver’, et la fraîcheur des preuves est surveillée. | ADR 0052, ADR 0042 |
| Le numéro d’ADR et l’index ADR sont une ressource partagée : en cas de collision entre branches parallèles, on renumérote au rebase et on corrige l’index + toutes les références (les ajouts au même endroit de l’index collisionnent au merge). | CONTRIBUTING.md |
Git, commits & merge
Section intitulée « Git, commits & merge »| Pratique | Source |
|---|---|
| Tout message de commit doit suivre la convention Conventional Commits (type(scope?): sujet), avec types validés (feat, fix, docs, ci, chore, perf, refactor, etc.). | commitlint |
| Le sujet du commit doit être en minuscules (interdiction de sentence-case, start-case, pascal-case, upper-case) et l’en-tête limité à 100 colonnes, le corps à 100 colonnes par ligne. | commitlint |
| Le message de commit ne doit contenir aucune adresse email — ce qui interdit de facto les trailers Co-Authored-By: (qui portent un email). | lefthook |
| Les hooks lefthook ne doivent jamais être contournés (—no-verify, LEFTHOOK=0 interdits) ; la CI rejoue les mêmes contrôles pour qu’un bypass local ne passe pas. | CLAUDE.md |
| Les PR sont fusionnées en merge commit (et non en squash ni rebase), pour préserver l’historique fin de chaque commit dans main. | ADR 0037 |
| Chaque commit d’une PR (pas seulement le titre) doit être propre et conforme, car il atterrit tel quel dans main et alimente le changelog. | ADR 0037 · CONTRIBUTING.md |
| Le push direct sur la branche main est interdit ; tout changement passe par une branche et une PR. | lefthook |
| Le merge d’une PR sur main est conditionné à la réussite de 13 checks CI requis (branche à jour avec main, strict: true) et à la résolution des conversations. | Convention propre au dépôt : branch protection Git |
| Le versionnement et la publication des releases sont automatisés via release-please : les Conventional Commits déterminent le bump semver, une PR de release est ouverte puis auto-mergée (une release par jour, la nuit, en merge commit). | ADR 0006, ADR 0037 · release-please |
| Les actions GitHub référencées dans les workflows sont épinglées par SHA de commit immuable (jamais @vX ni @branch), avec le tag de référence en commentaire. | ADR 0006 |
| Les branches suivent la convention <type>/<courte-description> et les issues sont fermées via « Closes #N » placé dans la description de la PR. | CONTRIBUTING.md |
| Une PR = un type/scope cohérent (deux capacités indépendantes => deux PR), pour garder un historique et un changelog lisibles. | ADR 0037 · CONTRIBUTING.md |
CI, lint & qualité de code
Section intitulée « CI, lint & qualité de code »| Pratique | Source |
|---|---|
Une cible agrégée pnpm lint enchaîne tous les linters/tests en une commande unique (format prettier, yamllint, shellcheck, kubeconform, ansible-lint, jscpd, ruff, garde-fous Python, bats, unittest), reproductible en local avant de pousser. | CLAUDE.md |
| Formatage automatique des YAML/JSON/Markdown via Prettier, vérifié (—check) avant commit, avant push et en CI ; config dédiée (2 espaces, single-quote, proseWrap=always et printWidth 80 pour le Markdown). | ADR 0049 · prettier |
| Lint YAML par yamllint avec règles personnalisées (line-length 160 en warning, indentation 2, truthy désactivé pour les clés K8s), appliqué partout sauf manifestes vendored. | yamllint |
| Lint shell par shellcheck à zéro warning sur tous les *.sh, avec couverture des fichiers échappant à l’extension : template bash etcd-snapshot.sh.j2 (forcé —shell=bash) et scripts Perl (perl -c). | ADR 0017, ADR 0049 · shellcheck |
| Validation des manifestes Kubernetes par kubeconform en mode strict, avec catalogue de schémas CRD (datreeio), à partir des seuls fichiers versionnés (git ls-files) pour exclure les YAML gitignorés générés par le banc. | kubeconform |
Lint Ansible par ansible-lint en profil production, plus une vérification de syntaxe (—syntax-check) de chaque playbook qui complète le lint en garantissant que tout playbook se parse (includes, rôles, templating), en utilisant l’inventaire générique versionné hosts.example.yaml. | ADR 0023 · ansible-lint |
| Détection de duplication de code (copier-coller) par jscpd avec un seuil de 5% maximum (minTokens 70) couvrant markdown, yaml, shell et json ; les bundles vendored sont exclus. | jscpd |
| Lint et formatage Python par ruff (version épinglée 0.15.16), règles E/F/I/UP/B/SIM, line-length 100, exécuté via uv pour un environnement reproductible (uv.lock committé). | ADR 0017, ADR 0006 · ruff |
| Lint Markdown structurel par markdownlint-cli2, config tolérante ciblant les vrais défauts de structure (pas le style typographique déjà géré par prettier : MD013/MD033/MD041/MD036/MD034 désactivées, MD024 siblings_only). | markdownlint |
| Scan de posture sécurité IaC (manifestes K8s, config) par Trivy en mode config, échec sur HIGH/CRITICAL, avec allowlist ciblée PAR CHEMIN et justifiée par finding (jamais d’ignore global sans justification écrite). | ADR 0010 · CLAUDE.md |
| Vérification des liens Markdown : VitePress échoue sur lien mort interne au build de docs, complété par lychee en mode hors-ligne (valide liens internes/fichiers, ignore les externes flaky). | ADR 0029 · VitePress |
| Garde-fou « toute page Markdown versionnée est atteignable depuis la documentation » contrôlé automatiquement par un script Python (détecte les pages orphelines, ce que VitePress ne fait pas). | ADR 0029 |
| Garde-fou « contrat d’interface cluster→atlas aligné sur les manifestes platform/ » contrôlé automatiquement par un script Python (le contrat machine-lisible ne diverge pas du code réel). | ADR 0043 |
| Conventional Commits forcés sur TOUTE la plage de commits d’une PR (pas seulement le titre), sujet en minuscules, longueurs contrôlées ; les commits historiques pré-CC sont ignorés via un filtre regex plutôt qu’une liste fragile. | ADR 0037 · commitlint |
| Interdiction des adresses e-mail dans les messages de commit (et donc pas de Co-Authored-By), refusée dès le commit. | lefthook |
| Interdiction du push direct sur main (impose branche + PR), bloqué au pre-push ; les hooks lefthook ne doivent jamais être bypassés (—no-verify / LEFTHOOK=0 interdits). | lefthook |
| Factorisation de la logique en libs bash partagées et testées : fonctions PURES de classification (state-classify.sh, health-classify.sh) et helpers SSH (ssh-report.sh) sourcées par les frontaux, testées sans cluster par bats ; helpers de scénarios de banc factorisés dans lib.sh mais seulement quand strictement identiques (pas de factorisation des fonctions qui divergent). | ADR 0053 |
| Tests unitaires des fonctions bash pures par bats et des scripts Python par unittest, exécutés en local et en CI. | ADR 0017 |
| Manifestes upstream vendored (bundles volumineux Ceph, cert-manager, argocd, monitoring, CloudNativePG, Dagster, Marquez) exclus de façon cohérente de prettier, yamllint, jscpd et kubeconform, et RBAC inhérent allowlisté par chemin dans .trivyignore.yaml. | ADR 0006 · CLAUDE.md |
| Actions GitHub épinglées par SHA de commit (avec commentaire de version) et installations reproductibles (pnpm —frozen-lockfile, uv sync sur uv.lock committé) dans tous les jobs CI. | ADR 0006 |
Sécurité & supply-chain
Section intitulée « Sécurité & supply-chain »| Pratique | Source |
|---|---|
| Toute image de conteneur est épinglée par digest sha256, et ce digest doit pointer un INDEX multi-arch (image.index / manifest.list), jamais un manifeste single-arch (sinon exec format error sur arm64). | ADR 0006 |
| Les GitHub Actions (uses:) sont épinglées par SHA de commit 40 caractères (jamais @vX ni @branch, mutables), avec en commentaire le tag de référence (# v4) pour la lisibilité. | ADR 0006 |
| Les versions des composants (K8s patch figé, Cilium, Rook, Ceph, containerd, charts, outils dev) sont gelées dans une matrice unique, sans bump silencieux : chaque montée passe par une PR dédiée après vérification de la compat croisée. | ADR 0006 |
| Veille de dépendances automatisée (Renovate) en PR dédiées, jamais d’auto-merge, rythme mensuel groupé, et alertes de vulnérabilité traitées à tout moment. | ADR 0006 · Renovate |
| Reproductibilité de l’environnement Python/CI par lockfile committé : uv.lock fige l’arbre exact des dépendances (esprit du pinning par digest). | ADR 0006 |
| Scan de posture sécurité IaC (Trivy config) sur manifestes K8s et Dockerfiles, qui échoue la CI sur tout HIGH/CRITICAL non explicitement allowlisté. | ADR 0010 · Trivy |
| Secrets/mots de passe non versionnés : génération robuste côté Ansible, stockage local gitignoré, et fichiers modèles *.example versionnés à la place des valeurs réelles. | ADR 0023 |
| Chiffrement at-rest des Secrets dans etcd (provider secretbox), clé générée sur le nœud au bootstrap (0600 root), une seule fois et jamais commitée. | ADR 0014 |
| Audit-policy API server activée (niveau Metadata, exclusions du bruit) pour journaliser qui/quoi/quand sur le cluster, avec rotation des logs. | ADR 0014 |
| Pod Security Admission (baseline en enforce, restricted en warn) sur les namespaces applicatifs maison pour bloquer les pods privileged/hostPath/hostNetwork. | ADR 0014 |
| Chiffrement transparent du trafic pod-to-pod inter-nœuds par WireGuard, avec gestion des clés par Cilium (pas de KMS), appliqué à l’install ET à l’upgrade, et échec visible si le datapath n’est pas réellement chiffré. | ADR 0019 |
| Observabilité réseau Hubble (relay + CLI hubble observe) activée SANS UI, pour éviter d’exposer une surface web supplémentaire. | ADR 0019 |
| Pare-feu hôte UFW : politique entrante deny par défaut, tout l’inter-nœuds autorisé sur le CIDR cluster, SSH limité (anti-brute-force) à la plage d’administration, NodePort restreint au réseau cluster, logging activé. | ADR 0003, ADR 0023 |
| Durcissement SSH des nœuds : authentification par clé uniquement, root désactivé, AllowUsers restreint, MaxAuthTries et timeouts d’inactivité, posé de façon idempotente via un drop-in sshd. | Convention bootstrap (responsabilité unique de fir |
| Durcissement OS opt-in : mises à jour de sécurité automatiques (unattended-upgrades), détection d’intrusion fail2ban, journalisation auditd, surveillance disque smartd — chaque couche activée explicitement par tag. | Convention bootstrap/security (toutes couches OPT- |
| Snapshots etcd protégés par permissions strictes (répertoire 0700, fichiers 0600 root) et copiés hors-nœud sur poste de confiance, et jamais versionnés (ils contiennent tous les Secrets). | ADR 0014 |
| Manifestes upstream vendored (bundles volumineux) appliqués tels quels et exclus des linters de style, mais leur RBAC inhérent (large par fonction) est allowlisté Trivy par chemin avec justification. | ADR 0021 · CLAUDE.md |
| Les messages de commit ne doivent contenir aucune adresse e-mail ni Co-Authored-By, et les hooks lefthook ne sont jamais contournés (—no-verify / LEFTHOOK=0 interdits). | CLAUDE.md |
Langage & architecture du code
Section intitulée « Langage & architecture du code »| Pratique | Source |
|---|---|
| Le choix du langage se fait par catégorie d’action (doctrine outil-par-action pondérée par le coût de diversité) et non par optimalité locale de chaque script : bash orchestre les CLIs, Ansible converge l’état Kubernetes, jq parse, Python porte la logique non triviale, bats/pytest testent. | ADR 0049, ADR 0017, ADR 0033 |
| L’orchestration de CLIs externes (kubectl/ceph/ssh/vagrant, helpers de banc, hooks git) reste en bash robuste : set -euo pipefail et shellcheck à 0 warning ; on ne réécrit pas par opportunisme le bash qui marche (pas de portage gratuit vers Go ou Python). | ADR 0017, ADR 0049 |
| Le parsing de sorties de CLIs se fait sur du JSON via jq (kubectl -o json/-o jsonpath, ceph -f json), jamais awk/grep/cut sur des sorties humaines. | ADR 0017, ADR 0049 |
| La logique non triviale (parcours de graphe, validation structurée au-delà de jq, calculs, traitement texte) va en Python (uv + ruff, tests obligatoires), pas en bash contorsionné ; Python est un second langage de plein droit. | ADR 0017, ADR 0049 |
| Node.js n’est pas un langage de script applicatif du dépôt : c’est uniquement le runtime d’outils (VitePress, prettier, commitlint, jscpd, lefthook, bats). Un nouveau besoin de logique va en Python. | ADR 0017, ADR 0049 |
| La convergence d’état sur les nœuds et l’apply Kubernetes idempotent de la couche plateforme (addons, opérateurs, CRDs, manifestes figés) sont faits en Ansible (kubernetes.core.k8s/k8s_info), pas en shell impératif : Ansible est le moteur de convergence idempotent. | ADR 0033, ADR 0049 |
| Le bootstrap NU (kubeadm, CRI, init/join/rollback, avant que le cluster existe) reste en bash + kubectl sans collection (zéro dépendance kubernetes.core), distinct de la couche plateforme. | ADR 0033, ADR 0049 |
| Les gates de convergence s’appuient sur l’état réel du cluster via k8s_info + retries/until (et non sur un sleep ou un k8s_exec WebSocket fragile) ; on gate sur le .status du CR. | ADR 0033, ADR 0049 |
| On exploite les directives natives d’Ansible plutôt que de réimplémenter en command/shell : changed_when reflète le vrai changement (jamais true constant), check_mode: false sur tout command dont le stdout pilote une logique aval, server_side_apply via le module pour CRDs/gros bundles, pré-check idempotent qui skip avant un effet de bord, redémarrage de service toujours par handler notify. | ADR 0051, ADR 0049 |
| Aucune constante dépendant du déploiement (IP, devices disque, tailles, comptes, seuils) n’est codée en dur dans une tâche : elle vit dans defaults/main.yaml (défaut = valeur d’exemple générique = prod) surchargeable par group_vars non versionné ; les constantes intrinsèques (chemins/ports/versions épinglées) restent inline. | ADR 0051, ADR 0023, ADR 0033 |
| Modèle de reprise/transactionnalité d’un rôle Ansible : tout step non-idempotent est gardé par un marqueur creates: (checkpoint), écrit dans une tâche séparée APRÈS succès (pas via redirection >> qui créerait le marqueur même en échec) ; le rescue compense les étapes à effet de bord (kubeadm reset —force, retrait du demi-état du seul step) puis re-fail, et reste diagnostique pour les étapes déclaratives-idempotentes. | ADR 0050 |
| Les preuves de compensation (rescue) sont injectées CÔTÉ HARNAIS (kill du process kubeadm, suppression du marqueur entre deux runs), jamais via une variable inject_fault lue par le rôle de production : un rôle prod n’embarque aucun hook de test. | ADR 0050, ADR 0046 |
| La logique de décision isolable (classification, comptage, parsing) est extraite en fonctions pures dans des libs bash sourçables et testée au comportement par bats (shellcheck valide la syntaxe, bats le comportement) ; un script de logique sans test n’est pas mergeable. | ADR 0017, ADR 0049 |
| Tout code Python de logique est couvert par des tests (unittest stdlib / pytest), non mergeable sans test ; le code jetable de validation e2e est explicitement exclu du lint (pas du code de production). | ADR 0017, ADR 0049 |
| La surcharge d’un manifeste figé (helm template vendored) se fait par patch strategic-merge kubernetes.core.k8s ciblé sur le champ concerné, jamais en emballant le rendu dans un template Jinja (.j2) qui divergerait du rendu upstream. | ADR 0033, ADR 0049 |
| Exceptions délibérément maintenues en bash et tracées : cni.sh (arme les features Cilium — kube-proxy replacement, LB-IPAM, Gateway API — valeurs dérivées du terrain au bootstrap, doit tourner dans la VM sans repo ; les UI sont exposées en L4 hors Gateway depuis l’ADR 0092, cilium-expo retiré), state.sh/report.sh (diagnostic lecture seule multi-couches — Ansible = convergence, pas reporting), cleanup.sh, first-access.sh, gitea-init.sh, harnais de banc et émetteur OpenLineage jetable, Mailpit (puits SMTP de test). | ADR 0049, ADR 0092 |