Quality Gate SonarQube — politique transitoire et trajectoire de durcissement
Chantier R5.4 (finding TST-005) · Décision mainteneur (QCM 2026-06-11) : « Assouplir puis durcir » — le Quality Gate devient bloquant immédiatement, avec des seuils transitoires réalistes, puis est durci au fil de la résorption de la dette. Ce document est la référence de la trajectoire de durcissement.
1. Contexte
Avant R5.4, le Quality Gate SonarQube était en ERROR sur 3 conditions
(new_coverage 71,3 < 80, new_reliability_rating 3 > 1,
new_security_hotspots_reviewed 0.0 < 100 — rapport du 2026-05-31) mais
aucun pipeline ne consommait ce verdict : l'analyse se terminait sans
sonar.qualitygate.wait=true ni lecture du statut, et la CI restait verte.
La politique « 80 % sur le nouveau code » était documentée mais jamais appliquée.
Depuis R5.4 :
- le gate est bloquant là où l'analyse tourne : analyse avec
sonar.qualitygate.wait=true, verdict relu via l'API (/api/qualitygates/project_status), code de sortie non nul si le gate est FAILED. L'analyse est lancée par les mainteneurs contre le SonarQube auto-hébergé (scripts/sonar-analyze.{sh,ps1}) — aucun workflow CI n'exécute SonarQube aujourd'hui (les anciens workflows Sonar ont été supprimés ; câbler l'analyse dans une voie CI planifiée reste un reliquat ouvert) ; - les seuils sont transitoires (réalistes au regard de l'état mesuré au 2026-05-31) et leur durcissement est planifié ci-dessous.
Où en est le projet au 2026-09-05 (mesuré par scripts/sonar-analyze.sh ; le rapport
complet qu'il écrit sous sonarqube/ est un artefact local que le dépôt ne versionne
pas) : le gate est OK
sur chaque condition qu'il rapporte — new_reliability_rating A (1),
new_security_rating A, new_maintainability_rating A, new_coverage 78,1 %,
new_duplicated_lines_density 0,0 % — sur 162 k lignes portant 0 bug,
0 vulnérabilité, 0 hotspot non statué (17 hotspots, tous REVIEWED) et 0 min de dette
technique. Les deux conditions que cette page décrivait rouges ou neutralisées en mai sont
donc tenues. Les seuils ci-dessous n'ont pas encore bougé : les relever relève de la
procédure du §6, pas de cette mesure.
2. Clé de projet
La clé de projet SonarQube est Orkeon (renommée le 2026-08-17 en suite
de l'audit PUB-01 — l'ancienne clé était la dernière trace du nom d'avant
renommage du projet). Cette décision remplace la
décision QCM du 2026-06-11 et assume le compromis qu'elle voulait éviter :
l'historique d'analyse côté serveur (baseline « nouveau code », tendances)
repart de la première analyse sous la nouvelle clé ; l'ancien projet reste
consultable sur l'instance auto-hébergée. La clé est surchargeable via la
variable d'environnement SONAR_PROJECT_KEY.
3. Le gate « Orkeon Transitional »
Le gate est provisionné automatiquement et de façon idempotente par les scripts d'analyse, puis associé au projet :
scripts/sonar-analyze.sh— tableQUALITY_GATE_CONDITIONS;scripts/sonar-analyze.ps1— table$QualityGateConditions.
Ces deux tables sont la source de vérité des seuils et doivent rester identiques entre elles (et synchrones avec ce document). À chaque exécution, le script crée le gate s'il n'existe pas, crée ou met à jour chaque condition dont le seuil diffère, et (ré)associe le gate au projet.
Conditions (toutes sur le nouveau code)
| Condition | Sens | Seuil transitoire (T0) | Cible finale | Justification du seuil transitoire |
|---|---|---|---|---|
new_coverage |
≥ | 70 % | 80 % | Nouveau code à 71,3 % au 2026-05-31 ; aligné sur la cible de couverture à 70 % (décision R5.2). Remonte avec R5.5/R5.6. |
new_reliability_rating |
≤ | B (2) | A (1) | B tolérait les bugs mineurs le temps de corriger les 3 bugs majeurs connus. Au 2026-05-31 le nouveau code était à C et la condition restait rouge à dessein — pression ciblée du gate bloquant sur les seules vraies causes (cf. fiche R5.4). Ces bugs ont disparu : au 2026-09-05 le nouveau code est à A (1) et le projet relève 0 bug, donc B est devenu du mou et non de la pression. Le durcissement vers A est débloqué (§5). |
new_security_rating |
≤ | A (1) | A (1) | Déjà tenue — maintenue stricte. |
new_maintainability_rating |
≤ | A (1) | A (1) | Déjà tenue — maintenue stricte. |
new_duplicated_lines_density |
≤ | 3 % | 3 % | Déjà tenue (0,56 %) — maintenue. |
new_security_hotspots_reviewed |
≥ | 0 % (neutralisée) | 100 % | Neutralisée le temps que 9 hotspots hérités soient statués (remédiation sécurité, fiche 07). Les 17 hotspots sont REVIEWED au 2026-09-05 et aucun ne reste non statué, donc la neutralisation ne masque plus rien ; le seuil 0 garde la condition visible dans le gate et les rapports jusqu'à sa montée à 100 (§5). |
4. Où le verdict est appliqué
| Surface | Mécanisme | Effet en cas de gate FAILED |
|---|---|---|
scripts/sonar-analyze.sh |
sonar.qualitygate.wait=true + relecture API du verdict (statut + conditions échouées loguées) |
exit code ≠ 0 (le rapport Markdown est tout de même généré) |
scripts/sonar-analyze.ps1 |
idem | exit code ≠ 0 |
Il n'y a pas de surface CI : aucun workflow GitHub n'exécute d'analyse
SonarQube (ce que la CI garde sur chaque PR, c'est le build -warnaserror
avec le jeu complet d'analyseurs, le gel d'API, les suites de tests et les
gates documentaires — voir État du projet).
Le verdict (statut + chaque condition échouée avec valeur réelle et seuil) est
visible dans les logs du script.
5. Trajectoire de durcissement
Chaque condition est durcie dès que son critère de passage est rempli — pas de big-bang. Récapitulatif :
| Condition | T0 (en vigueur) | Critère de passage | T1 | Critère de passage | T2 (cible) |
|---|---|---|---|---|---|
new_coverage |
70 % | R5.5 (tests contractuels Tools.Analysis) et R5.6 (dé-flake) livrés ; cible de couverture montée à 75 % (R5.2). Nouveau code mesuré à 78,1 % au 2026-09-05 |
75 % | nouveau code stable ≥ 80 % sur ~1 mois de merges | 80 % |
new_reliability_rating |
B | les 3 bugs majeurs connus corrigés (campagne de remédiation) — tenu au 2026-09-05 : 0 bug, nouveau code à A | A | — | A |
new_security_hotspots_reviewed |
0 % (neutralisée) | les 9 hotspots hérités statués sur le serveur (remédiation sécurité, fiche 07) — tenu au 2026-09-05 : 17/17 REVIEWED | 100 % | — | 100 % |
new_security_rating |
A | déjà au niveau cible | A | — | A |
new_maintainability_rating |
A | déjà au niveau cible | A | — | A |
new_duplicated_lines_density |
3 % | déjà au niveau cible | 3 % | — | 3 % |
État final (T2) : les conditions rejoignent celles du gate intégré « Sonar way ». Deux options à ce stade : basculer le projet sur « Sonar way » et supprimer « Orkeon Transitional », ou conserver le gate nommé avec les valeurs finales (préférer la première pour réduire la surface de configuration custom).
6. Procédure de modification des seuils
- Modifier la table
QUALITY_GATE_CONDITIONSdansscripts/sonar-analyze.shet$QualityGateConditionsdansscripts/sonar-analyze.ps1(les deux doivent rester identiques). - Mettre à jour le présent document (tables §3 et §5, historique §8).
- Relancer une analyse : le provisionnement idempotent pousse les nouveaux
seuils sur le serveur (
update_condition).
Ne pas modifier les seuils directement dans l'UI SonarQube : ils seraient écrasés à la prochaine exécution du script.
7. Limites connues
- Le provisionnement exige un token avec la permission « Administer Quality Gates » (et « Create Projects » pour un serveur vierge). À défaut, le script l'indique en WARN et continue : le gate actuellement associé au projet est alors appliqué (le blocage reste effectif, mais avec les seuils du serveur).
new_coveragen'est pas évaluée par SonarQube si aucune couverture n'est importée : un import de couverture cassé peut faire passer le gate à tort. C'est pourquoi le script installe ReportGenerator automatiquement.- Sur une machine headless, le fallback Docker des scripts peut être désactivé
(
SONAR_NO_DOCKER=1) : un serveur injoignable fait alors échouer la passe au lieu de booter une instance éphémère sans historique (qui rendrait le verdict « nouveau code » dénué de sens).
8. D'où vient le chiffre public de couverture
La passe SonarQube ci-dessus est locale : son rapport est écrit sous sonarqube/, que le
dépôt ne versionne pas, si bien que rien de ce qu'elle mesure n'est vérifiable par un tiers.
Le chiffre de couverture qui peut l'être vient du
workflow Coverage
(.github/workflows/coverage.yml) : à chaque push sur main et chaque semaine, il exécute
les suites unitaires et rapides — le même filtre Category!=Integration&Category!=Slow que
ci.yml — sous dotnet-coverage (épinglé), refuse une mesure vide, écrit le résumé
ReportGenerator sur la page du run et conserve coverage.cobertura.xml plus un rapport HTML
comme artefact coverage (90 jours). C'est la couverture de lignes par les suites
unitaires et rapides ; les catégories Integration et Slow tournent dans integration.yml
sans couverture. Le README renvoie au workflow et ne cite aucun nombre : un run est une
mesure, un nombre tapé est une affirmation.
9. Historique
| Date | Événement |
|---|---|
| 2026-06-11 | Création du gate « Orkeon Transitional » (T0), activation du blocage local (R5.4), alignement de la clé de projet des scripts sur la clé historique |
| 2026-08-17 | Clé de projet renommée en Orkeon (suite de l'audit PUB-01 — clé historique d'avant renommage retirée ; remplace la décision QCM du 2026-06-11, l'historique d'analyse repart sous la nouvelle clé) |
| 2026-08-18 | Document réaligné sur la réalité (DOC-02) : aucun workflow CI Sonar n'existe — l'application du verdict est locale aux scripts d'analyse ; références aux anciens workflows retirées |