Patrick Schurig 010a7da0f4 ci: le contrôle de qualité documentaire — écrit, éprouvé, PAS en service
Étape 4 du chantier de documentation. tools/ci-quality.sh porte les trois
vérifications qui se posent au même endroit — elles partagent la même sortie
(doc-generated/) et la même commande de réparation, les séparer ferait trois jobs
qui régénèrent trois fois la même chose :

  1. l'index des règles est VRAI (code de retour de gen-rules.py) ;
  2. la doc Doxygen est complète — 0 avertissement, DoD §5 ;
  3. l'index est REPRODUCTIBLE : deux passes, un seul résultat.

PAS EN SERVICE, et l'extension le garantit : ci/gitea-workflow.yml.disabled ne
déclenche rien tant qu'il n'est pas déplacé dans .gitea/workflows/. La raison est
datée dans le fichier — forge muette, plus de trente commits en attente ici et
plus de soixante côté app ; un job activé à l'aveugle échouerait au moment précis
où tout le monde pousse, et le premier réflexe serait de le désactiver.

CHAQUE ÉCHEC DONNE LA COMMANDE À LANCER. Un job qui annonce « l'index est
périmé » sans dire quoi faire se contourne en le désactivant : c'est le chemin de
moindre effort, et il gagne toujours.

VÉRIFIÉ AVANT D'ÊTRE DÉCLARÉ PRÊT, et ça ne passait pas du premier coup.

— Les 26 avertissements Doxygen de ce matin sont CORRIGÉS, pas contournés par un
  cliquet sur un compte de référence : c'était de la dette DoD-5 (retours et
  paramètres non documentés dans relayrouter.h, energyarbitrator.h, loadconfig.h,
  les trois clearFault(), ILoadAdapter::updateSoftConfig), plus deux que j'avais
  introduits — un \rule{} dans un titre \par, et un \param energyLogs qui ne
  correspond à aucun argument. Le seuil est donc ZÉRO, le seul qui ne rote pas.

— Et le script lui-même échouait sur un dépôt SAIN : `grep -c ... || echo 0`
  affiche 0 PUIS sort en 1 quand il ne trouve rien, si bien que le « || » empile
  un second zéro et que le test entier casse. Corrigé ici et dans gen-doc.sh, qui
  portait le même piège. Une commande de mesure dont l'échec produit une lecture
  fausse — le motif de la semaine, appliqué à l'outil.

CE QUI N'EST PAS VÉRIFIÉ, et pourquoi : « régénérer et refuser si ça diffère de
ce qui est commité » n'a rien à comparer, doc-generated/ étant dans .gitignore.
Le risque qu'un tel diff attraperait — un artefact commité qui dérive de sa
source — a été SUPPRIMÉ en ne le commitant pas, pas déplacé. Ce qui reste à
garantir est que l'index soit vrai (contrôle 1) et déterministe (contrôle 3).

État au 2026-08-29 : ./tools/ci-quality.sh → RC=0, 113 règles, 0 avertissement.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015F7G5VeaPVSMeVNjiGj36p
2026-08-29 11:33:46 +02:00

115 lines
5.6 KiB
Bash
Executable File

#!/bin/bash
# SPDX-License-Identifier: GPL-3.0-or-later
# Copyright (C) 2025 - 2026, Patrick Schurig / ETM PowerSync
#
# Contrôle de qualité documentaire — TROIS vérifications, un seul endroit.
#
# ./tools/ci-quality.sh
#
# Elles se posent au même endroit parce qu'elles partagent la même sortie
# (`doc-generated/`) et la même commande de réparation. Les séparer ferait trois
# jobs qui regénèrent trois fois la même chose.
#
# 1. L'INDEX DES RÈGLES est-il VRAI ? (tools/gen-rules.py, code de retour)
# Casse sur : un identifiant cité et jamais défini ; un identifiant qui n'existe
# qu'en ligne de tableau ; un domicile ambigu ; un numéro retiré réattribué.
# 2. La DOC DOXYGEN est-elle complète ? (0 avertissement — DoD §5 d'AGENTS.md)
# 3. Le tout est-il REPRODUCTIBLE ? (deux passes donnent le même index)
#
# ── CE QU'ON NE VÉRIFIE PAS, ET POURQUOI ────────────────────────────────────
#
# « Régénérer et refuser si ça diffère de ce qui est commité » n'a rien à comparer :
# `doc-generated/` est dans .gitignore, décision du 2026-08-29 — versionner une page
# générée produirait un diff à chaque édition de spec et des conflits sur un fichier
# que personne ne relit. Le risque qu'un tel diff attrape — un artefact commité qui
# dérive de sa source — n'existe pas quand l'artefact n'est pas commité : il a été
# SUPPRIMÉ, pas déplacé. Ce qui reste à garantir est que l'index soit VRAI, et c'est
# la vérification 1. La 3 couvre le reste : un générateur non déterministe.
#
# ── ÉTAT : ÉCRIT, PAS EN SERVICE ────────────────────────────────────────────
#
# Aucun déclencheur automatique n'est posé. `ci/gitea-workflow.yml.disabled` porte le
# workflow prêt à l'emploi et la marche à suivre pour l'activer. La raison est datée :
# au 2026-08-29 la forge ne répond pas et plus de trente commits attendent en local.
# Un job activé à l'aveugle échouerait à son premier passage — au moment précis où
# tout le monde pousse — et le premier réflexe serait de le désactiver. Un garde-fou
# désactivé une fois ne se réactive jamais.
#
# Il PASSE sur l'état du dépôt au 2026-08-29 : index RC=0 (113 règles), doxygen 0
# avertissement. Vérifié avant d'être déclaré prêt, parce qu'un job qu'on n'a jamais
# vu passer n'est pas un job, c'est une intention.
set -u
RACINE="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$RACINE" || exit 1
ROUGE=""; VERT=""; GRAS=""; FIN=""
if [ -t 1 ]; then ROUGE=$'\e[31m'; VERT=$'\e[32m'; GRAS=$'\e[1m'; FIN=$'\e[0m'; fi
echec=0
titre() { printf '\n%s── %s %s\n' "$GRAS" "$1" "$FIN"; }
ko() { printf '%s✗ %s%s\n' "$ROUGE" "$1" "$FIN"; echec=1; }
ok() { printf '%s✓ %s%s\n' "$VERT" "$1" "$FIN"; }
# Toute panne doit dire QUOI LANCER. Un job qui annonce « l'index est périmé » sans
# donner la commande se contourne en le désactivant — c'est le chemin de moindre
# effort, et il gagne toujours.
reparer() { printf ' → réparer : %s\n' "$1"; }
# --- 1. L'index des règles est-il vrai ? ------------------------------------
titre "1/3 · index des règles"
if python3 tools/gen-rules.py; then
ok "index complet — aucun orphelin, aucun domicile ambigu"
else
ko "l'index des règles est INCOMPLET (détail ci-dessus)"
reparer "définir la règle manquante dans specs/, ou la déclarer dans tools/rules-retired.txt"
reparer "puis : python3 tools/gen-rules.py"
fi
# --- 2. La doc est-elle complète ? ------------------------------------------
titre "2/3 · documentation Doxygen"
if ! command -v doxygen >/dev/null 2>&1 || ! command -v dot >/dev/null 2>&1; then
ko "doxygen ou graphviz absent — la vérification n'a PAS eu lieu"
reparer "apt install doxygen graphviz"
else
./tools/gen-doc.sh >/dev/null 2>&1
JOURNAL="$RACINE/doc-generated/warnings.txt"
# `wc -l` et non `grep -c` : grep -c AFFICHE 0 puis SORT EN 1 quand il ne trouve rien,
# si bien qu'un « || echo 0 » empile un second zéro et que le test entier échoue sur un
# dépôt SAIN. Une commande de mesure dont l'échec produit une lecture fausse — le motif
# de la semaine, appliqué à l'outil (AGENTS.md, « Lire le journal de la box »).
N=0
[ -f "$JOURNAL" ] && N="$(grep -i "warning" "$JOURNAL" 2>/dev/null | wc -l)"
if [ "$N" -eq 0 ]; then
ok "0 avertissement"
else
ko "$N avertissement(s) Doxygen — DoD §5 : toute méthode publique d'etm/ se documente"
printf '\n'; sed 's|^.*/energyplugin/| |' "$JOURNAL" | head -20
reparer "./tools/gen-doc.sh puis lire doc-generated/warnings.txt"
fi
fi
# --- 3. Le générateur est-il déterministe ? ---------------------------------
titre "3/3 · reproductibilité de l'index"
A="$(mktemp)"; B="$(mktemp)"
python3 tools/gen-rules.py >/dev/null 2>&1; cp doc-generated/RULES.md "$A"
python3 tools/gen-rules.py >/dev/null 2>&1; cp doc-generated/RULES.md "$B"
if diff -q "$A" "$B" >/dev/null; then
ok "deux passes, un seul résultat"
else
ko "l'index n'est PAS reproductible — deux passes ont divergé"
printf '\n'; diff "$A" "$B" | head -20
reparer "un ordre d'itération dépend d'un QHash ou du système de fichiers : trier"
fi
rm -f "$A" "$B"
printf '\n'
if [ "$echec" -eq 0 ]; then
printf '%s✓ qualité documentaire : tout passe.%s\n' "$VERT" "$FIN"
else
printf '%s✗ qualité documentaire : au moins une vérification a échoué.%s\n' "$ROUGE" "$FIN"
printf ' Tout relancer d'"'"'un coup : ./tools/ci-quality.sh\n'
fi
exit "$echec"