Aller au contenu
CI/CD & Automatisation medium

Rapports qualité GitLab CI/CD

16 min de lecture

logo gitlab

Vos tests passent mais vous ne voyez que "Job succeeded" ? GitLab peut afficher les résultats détaillés directement dans les merge requests : tests échoués, couverture de code, problèmes de qualité. Ce guide vous montre comment configurer les rapports de qualité.

À la fin de ce module, vous saurez :

  • Configurer des rapports JUnit : afficher les tests échoués dans la MR
  • Extraire la couverture de code : afficher le % global et le diff ligne par ligne
  • Intégrer les rapports de qualité : ESLint, SonarQube au format Code Climate
  • Activer les rapports de sécurité : SAST, secrets, dépendances
  • Utiliser dotenv : passer des variables entre jobs
  • Éviter les erreurs courantes : when: always, formats de rapport

Avant de continuer, assurez-vous de maîtriser :

GitLab supporte plusieurs types de rapports, chacun avec son affichage dédié : un rapport n'est pas un artefact ordinaire, GitLab le parse au lieu de se contenter de le stocker. C'est ce qui lui permet d'afficher un résultat dans l'interface de la merge request au lieu d'un fichier à télécharger. En contrepartie, le format attendu n'est pas négociable : un XML valide mais d'un autre schéma est ingéré sans erreur et n'affiche rien.

TypeAffichageFormat
junitTests échoués dans MRJUnit XML
coverage_reportDiff de couverture ligne par ligneCobertura XML
codequalityProblèmes de qualité dans MRCode Climate JSON
sastVulnérabilités de codeSARIF/GitLab JSON
secret_detectionSecrets détectésGitLab JSON
dependency_scanningVulnérabilités dépendancesGitLab JSON
dotenvVariables pour jobs suivantsKEY=value

Le format JUnit XML est le dénominateur commun des frameworks de test : Jest, pytest, Go et Maven savent tous le produire, parfois via un plugin. GitLab s'en sert pour lister les tests en échec avec leur message d'erreur, et pour signaler ceux qui régressent par rapport à la branche cible. Sans ce rapport, un pipeline rouge oblige à ouvrir les logs du job pour savoir ce qui a cassé.

Le rapport JUnit affiche les tests échoués directement dans l'onglet "Tests" de la MR. Le principe est identique dans les quatre langages ci-dessous : demander au lanceur de tests d'écrire un fichier XML, puis le déclarer dans artifacts:reports:junit.

test:
image: node:20@sha256:8f693eaa7e0a8e71560c9a82b55fd54c2ae920a2ba5d2cde28bac7d1c01c9ba5
script:
- npm ci
- npm test -- --reporters=jest-junit
artifacts:
when: always # Important : même si les tests échouent
reports:
junit: junit.xml

Installez le reporter :

Fenêtre de terminal
npm install --save-dev jest-junit

Et configurez jest.config.js :

module.exports = {
reporters: [
'default',
['jest-junit', { outputDirectory: '.', outputName: 'junit.xml' }]
]
};

Le rapport n'apparaît pas dans le job mais dans la merge request, une fois le pipeline terminé. GitLab compare le fichier au dernier rapport de la branche cible, d'où la notion de nouvel échec :

Dans la MR, vous verrez :

  • Onglet Tests avec le nombre de tests passés/échoués
  • Liste des tests échoués avec le message d'erreur
  • Comparaison avec la branche cible (nouveaux échecs)

GitLab traite la couverture par deux mécanismes indépendants, souvent confondus. Le mot-clé coverage: extrait un pourcentage global depuis la sortie du job, celui qui alimente le badge et la valeur affichée sur la MR. Le rapport coverage_report: fait autre chose : il colore les lignes couvertes dans le diff. Les deux se cumulent, et la plupart des équipes ont besoin des deux.

GitLab peut extraire le % de couverture depuis les logs avec une regex : c'est le texte affiché par l'outil de test qui est analysé, pas un fichier. Il faut donc que la commande produise réellement ce résumé dans la sortie du job, sinon la valeur reste vide.

test:
script:
- npm test -- --coverage
coverage: '/All files[^|]*\|[^|]*\s+([\d\.]+)/'

La regex doit capturer un nombre (groupe de capture). Exemples par framework :

FrameworkRegex
Jest/All files[^|]*|[^|]*\s+([\d\.]+)/
pytest-cov/TOTAL\s+\d+\s+\d+\s+(\d+)%/
Go/coverage:\s(\d+\.\d+)%/
JaCoCo/Total.*?(\d+%)/

Afficher la couverture ligne par ligne (Cobertura)

Section intitulée « Afficher la couverture ligne par ligne (Cobertura) »

Pour voir quelles lignes sont couvertes dans le diff de la MR, il faut un rapport Cobertura, un XML qui liste les lignes couvertes fichier par fichier. Les chemins qu'il contient doivent correspondre à ceux du dépôt, sinon GitLab ne relie aucune ligne au diff :

test:
script:
- npm test -- --coverage --coverageReporters=cobertura
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml

Un seul passage de tests peut alimenter les deux mécanismes : --coverageReporters=text imprime le résumé que la regex capture, cobertura écrit le XML du diff. Oublier le reporter text est l'erreur classique, la couverture reste alors à zéro alors que le diff, lui, est bien coloré.

test:
script:
- npm test -- --coverage --coverageReporters=text --coverageReporters=cobertura
coverage: '/All files[^|]*\|[^|]*\s+([\d\.]+)/'
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml

Le rapport codequality remonte dans la MR les problèmes introduits par les lignes modifiées : dette, complexité, règles de lint. C'est ce qui rend la remarque utile, personne ne corrige les 4 000 avertissements d'un dépôt existant, mais tout le monde corrige les trois qu'il vient d'ajouter. GitLab n'impose aucun outil, seulement un format d'échange.

GitLab affiche les problèmes de qualité dans la MR si vous utilisez le format Code Climate, un JSON qui décrit chaque problème avec sa sévérité, son fichier et sa ligne. La plupart des linters savent le produire via un formatter dédié.

code_quality:
image: node:20@sha256:8f693eaa7e0a8e71560c9a82b55fd54c2ae920a2ba5d2cde28bac7d1c01c9ba5
script:
- npm ci
- npx eslint --format gitlab src/ > gl-code-quality-report.json || true
artifacts:
reports:
codequality: gl-code-quality-report.json

Le formatter maintenu par GitLab convertit la sortie d'ESLint au format attendu, sans script intermédiaire. L'option -o écrit directement le fichier, ce qui évite la redirection shell et le risque d'y capturer une ligne de log parasite qui invaliderait le JSON.

Fenêtre de terminal
npm install --save-dev @gitlab-formatters/eslint-formatter
code_quality:
script:
- npx eslint -f @gitlab-formatters/eslint-formatter -o gl-code-quality-report.json src/
artifacts:
reports:
codequality: gl-code-quality-report.json

GitLab fournit un template officiel : il exécute un scanner générique dans un conteneur, détecte les langages du dépôt et produit le rapport sans que vous ayez à configurer de linter. Pratique pour démarrer, mais il demande le service Docker dans vos runners et reste moins précis qu'un linter réglé pour votre projet.

include:
- template: Code-Quality.gitlab-ci.yml
code_quality:
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"

Les scanners de sécurité fournis par GitLab s'activent par un simple include: de template : chacun ajoute un job qui produit un rapport dans le format JSON maison. Ces jobs tournent quelle que soit l'édition ; ce qui change avec l'édition, c'est l'affichage des résultats dans la merge request et le tableau de bord de vulnérabilités.

Le SAST analyse le code source à la recherche de motifs dangereux, injection SQL ou désérialisation non contrôlée par exemple. Le template sélectionne automatiquement les analyseurs correspondant aux langages détectés dans le dépôt, il n'y a rien à déclarer.

include:
- template: Security/SAST.gitlab-ci.yml
sast:
stage: test
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == "main"

Ce scanner cherche des jetons et clés dans les fichiers versionnés et, sur une MR, dans les commits ajoutés. Un secret trouvé doit être considéré comme compromis et révoqué : le retirer du code ne suffit pas, il reste dans l'historique Git.

include:
- template: Security/Secret-Detection.gitlab-ci.yml
secret_detection:
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"

Le scanner de dépendances lit les fichiers de verrouillage (package-lock.json, poetry.lock, go.sum) et confronte les versions installées aux vulnérabilités connues. Il ne trouve donc rien si le lockfile n'est pas versionné.

include:
- template: Security/Dependency-Scanning.gitlab-ci.yml

Le rapport dotenv permet de passer des variables d'un job à l'autre : chaque ligne CLE=valeur du fichier devient une variable d'environnement dans les jobs suivants. C'est la réponse propre au besoin classique de transmettre un numéro de version ou l'URL d'un déploiement, là où un export dans le script: disparaît avec le conteneur du job.

build:
stage: build
script:
- VERSION=$(cat version.txt)
- echo "VERSION=$VERSION" >> build.env
artifacts:
reports:
dotenv: build.env
deploy:
stage: deploy
needs: [build]
script:
- echo "Déploiement version $VERSION"

Quatre réglages font la différence entre des rapports qui servent vraiment et des rapports que personne ne regarde. Ils portent tous sur le moment où le rapport est produit et sur son effet sur la merge request.

Par défaut, GitLab n'envoie les artefacts que si le job réussit. Sur un job de test, c'est exactement l'inverse du besoin : le rapport disparaît au moment précis où vous en avez besoin.

test:
script: npm test
artifacts:
when: always # Sinon pas de rapport si les tests échouent
reports:
junit: junit.xml

Un même job peut déclarer plusieurs rapports : inutile de dupliquer l'exécution des tests pour produire séparément le JUnit et la couverture. Un seul passage, plusieurs formats de sortie, et la MR affiche tout.

test:
script:
- npm test -- --coverage --reporters=jest-junit
coverage: '/All files[^|]*\|[^|]*\s+([\d\.]+)/'
artifacts:
when: always
reports:
junit: junit.xml
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml

Les rapports ne s'affichent dans une merge request que si le pipeline a bien été déclenché pour cette MR. La première règle assure ce déclenchement, la seconde conserve un pipeline sur main pour disposer d'une référence de comparaison.

test:
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == "main"

Brancher un linter en mode bloquant sur un projet existant fait échouer la première MR venue, et l'équipe désactive le job dans la semaine. allow_failure: true rend le rapport visible sans bloquer, le temps de résorber la dette avant de durcir la règle.

code_quality:
script: npm run lint -- -f gitlab
allow_failure: true # Informatif, ne bloque pas la MR
artifacts:
reports:
codequality: gl-code-quality-report.json

Le point commun de ces cinq pannes : le job passe au vert, aucune erreur n'est levée, et pourtant la MR reste vide. GitLab ne signale pas un rapport illisible, il l'ignore. Quand un rapport n'apparaît pas, vérifiez donc d'abord le format produit et la présence réelle du fichier dans les artefacts du job.

ErreurCauseSolution
Rapport non affichéFormat incorrectVérifier le format (JUnit XML, Cobertura)
Tests échoués invisibleswhen: always manquantAjouter when: always aux artefacts
Couverture 0%Regex ne matche pasTester la regex sur les logs
Code quality videFormat non Code ClimateUtiliser un formatter GitLab
"Test summary" manquantFichier XML invalideValider le XML généré

Chaque type de rapport répond à une question différente et exige son propre format. Le tableau ci-dessous sert de mémo au moment d'écrire un .gitlab-ci.yml. La colonne de droite retient le détail qui fait échouer l'affichage quand on l'oublie.

RapportUsagePoint clé
junitTests échoués dans MRwhen: always obligatoire
coverage:% global dans le badgeRegex sur les logs
coverage_report:Diff ligne par ligneFormat Cobertura
codequality:Problèmes ESLint/SonarQubeFormat Code Climate
sast:Vulnérabilités de codeGitLab Ultimate pour MR
dotenv:Variables entre jobsPropagé via needs:

Appliquez ces pratiques dans le Lab 10, Rapports qualité.

Testez vos connaissances sur les rapports de qualité GitLab.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

10 questions
5 min.
70% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn