Tableaux de messages (champs, types, obligatoire, valeurs, sens), règles sémantiques et codes de motif, entre balises BEGIN/END GENERATED de OPTIMIZER_PROTOCOL.md. --check (code 1 si la prose ou la forme canonique du contrat n'est pas à jour) branché en 5/5 de tools/ci-quality.sh. --enums-from pose les listes fermées du contrat depuis le dump du code. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
140 lines
7.0 KiB
Bash
Executable File
140 lines
7.0 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 — CINQ 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)
|
|
# 4. Un verdict de test peut-il dépendre de l'HEURE ? (tools/check-simulation-clock.py)
|
|
# 5. La prose du protocole est-elle GÉNÉRÉE depuis son contrat ?
|
|
# (tools/etm-powersync-gen-protocol-doc --check : tableaux de messages à jour, contrat
|
|
# OpenRPC sous sa forme canonique). Ici, l'artefact généré EST commité — la prose est
|
|
# lue par des humains et des optimiseurs tiers — donc on régénère et on refuse un écart.
|
|
#
|
|
# ── 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/5 · 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/5 · 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/5 · 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"
|
|
|
|
# --- 4. Un verdict de test peut-il dépendre de l'HEURE ? ---------------------
|
|
titre "4/5 · horloge des déclencheurs"
|
|
if sortie="$(python3 tools/check-simulation-clock.py 2>&1)"; then
|
|
ok "${sortie#✓ }"
|
|
else
|
|
ko "un déclencheur d'arbitrage tourne à l'horloge murale sans garde"
|
|
printf '\n'; printf '%s\n' "$sortie" | sed '1d;s/^/ /'
|
|
reparer "entourer l'appel de #ifndef ENERGY_SIMULATION, comme ses voisins"
|
|
fi
|
|
|
|
# --- 5. La prose du protocole est-elle générée depuis le contrat ? -----------
|
|
titre "5/5 · protocole optimiseur : prose générée depuis le contrat OpenRPC"
|
|
if sortie="$(python3 -I tools/etm-powersync-gen-protocol-doc --check 2>&1)"; then
|
|
ok "${sortie#✓ }"
|
|
else
|
|
ko "docs/OPTIMIZER_PROTOCOL.md ou le contrat OpenRPC n'est pas à jour"
|
|
printf '\n'; printf '%s\n' "$sortie" | head -20 | sed 's/^/ /'
|
|
reparer "python3 tools/etm-powersync-gen-protocol-doc (puis relire le diff et commiter)"
|
|
fi
|
|
|
|
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"
|