Dans ce chapitre, nous allons utiliser GitLab afin d’implémenter les différentes étapes de l’intégration continue pour garantir que notre code fonctionne à tout moment. Vous allez commencer par implémenter les étapes de compilation et de tests.
Lors de la création d’un repository sur Gitlab, est créée par défaut une branche principale appelée main. Même s’il existe des notions de branche, la création d’une branche doit être évitée le plus possible, privilégiant le développement sur la branche principale ; cela évite de maintenir plusieurs versions en parallèle. Ce genre de pratique est appelé trunk-based development.
Si l’utilisation du trunk-based development n’est pas possible pour votre équipe, une autre approche possible est d’utiliser ce qu’on appelle des feature branches. Le principe d’une feature branch est de créer une branche pour chaque nouvelle fonctionnalité que l’on veut ajouter dans l’application, avant de merger celle-ci dans la branche principale à la fin du développement de la fonctionnalité. Ces feature branches permettent alors à chaque développeur de pouvoir travailler indépendamment les uns des autres, sans que leur développement n’impacte négativement les autres.
Clonez le projet sur votre poste
Pour pouvoir commencer à travailler sur le projet Gitlab, afin de récupérer le code source, nous allons dans un premier temps cloner le repository Git. Je vous invite à suivre les instructions de la vidéo ci-dessous.
Avec ces manipulations, nous avons appris comment cloner de manière sécurisée notre repository sur notre poste de travail afin de commencer les développements que l’on a planifiés précédemment.
Activez l’intégration continue sur votre projet avec GitLab
Nous allons maintenant ajouter un pipeline d’intégration continue, afin d’implémenter les différentes étapes que nous avons vues précédemment. Les phases de ce pipeline seront lancées successivement, lors de chaque nouveau push du code sur le repository. Voici à quoi ressemblera le pipeline :
Pour activer l’intégration continue sur GitLab, le plus simple est de cliquer sur le bouton Set up CI/CD sur la page d’accueil du projet. Cette commande va créer le fichier gitlab-ci.yml dans votre projet. C’est sur ce fichier que vous décrirez tout votre pipeline CI/CD avec la syntaxe YAML.
Puis ajouter les lignes suivantes dans le fichier :
stages:
- build
- test
cache:
paths:
- .m2/repository
key: "$CI_JOB_NAME"
build_job:
stage: build
image: eclipse-temurin:17-jdk-alpine
script:
- ./mvnw compile
-Dhttps.protocols=TLSv1.2
-Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository
-Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=WARN
-Dorg.slf4j.simpleLogger.showDateTime=true
-Djava.awt.headless=true
--batch-mode --errors --fail-at-end --show-version -DinstallAtEnd=true -DdeployAtEnd=true
test_job:
stage: test
image: eclipse-temurin:17-jdk-alpine
script:
- ./mvnw test
-Dhttps.protocols=TLSv1.2
-Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository
-Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=WARN
-Dorg.slf4j.simpleLogger.showDateTime=true
-Djava.awt.headless=true
--batch-mode --errors --fail-at-end --show-version -DinstallAtEnd=true -DdeployAtEnd=true
Ce fichier est la pierre angulaire de l’implémentation d’un pipeline dans GitLab. Ce fichier s’appelle.gitlab-ci.yml
et c’est ici que nous allons définir notre pipeline. Dans cet exemple, nous avons implémenté deux étapes :
l’étape de compilation avec la tâche
build_job
;l’étape des tests avec la tâche
test_job
.
Découvrons maintenant le fichier bloc par bloc.
Définissez les étapes du pipeline
Dans un premier temps, je définis les étapes de mon pipeline avec le mot cléstages
. Ce mot clé permet de définir l’ordre des étapes. Ici, la première étape va être lebuild
et ensuite lestests
.
Ça veut dire que les tâches (ou jobs en anglais) associées à l’étapebuild
(icibuild_job
) vont s’exécuter en premier, puis les tâches de l’étapetest
(icitest_job
).
Accélérez les étapes avec le cache
Le deuxième bloc, avec le mot clécache
, est utilisé pour accélérer toutes nos étapes. Effectivement, dans le cas d’une compilation Java avec Maven (notre cas), cette compilation récupère beaucoup de dépendances et de librairies externes. Ces librairies sont stockées dans le répertoire.m2
.
Grâce à l’utilisation du mot clécache
et de la variable prédéfinie de GitLab$CI_JOB_NAME
, ce répertoire est commun à tous les jobs du pipeline.
Définissez les jobs à effectuer
Ensuite, je déclare deux jobs, correspondant chacun à une des étapes de notre pipeline d’intégration continue. Dans ces deux jobs, nous voyons que nous avons trois lignes différentes. Découvrons à quoi ces lignes correspondent :
stage
: c’est le nom de l’étape qui va apparaître dans notre pipeline d’intégration continue. Cela correspond aussi austage
auquel sera exécuté le job ;script
: ce sont les lignes de script à lancer afin d’exécuter l’étape. Ici, nous lançons le script Maven suivant son lifecycle. Dans la partiebuild
, nous lançons la compilation et dans la partietest
, nous lançons les tests de l’application. D’autres options sont définies afin d’accélérer le temps de traitement de ces lignes. Lescript
va alors télécharger Maven, l’outil de compilation, toutes les dépendances de l’application, et lancer la compilation du projet ;image
: c’est l’image Docker qui va être lancée par GitLab afin d’exécuter les lignes de script que nous avons définies. Ici, l’imageeclipse-temurin:17-jdk-alpine
, qui contient déjà Java 17, va être lancée afin de pouvoir compiler le projet. Une fois le fichier sauvegardé, le pipeline de build se lance et vous devriez voir les différentes étapes se lancer (ici, l’étape de build et l’étape de test).
Lors de l’étape detest
, le pipeline va exécuter les tests unitaires déjà présents au sein du projet. L’objectif de cette étape est de s’assurer de lancer les tests écrits par les développeurs. Si un seul de ces tests échoue, le pipeline s’arrête.
Ces tests doivent s’exécuter de la manière la plus rapide possible, afin d’avoir un feedback le plus rapide lui aussi. Pour arriver à ce niveau, il est nécessaire que les tests unitaires n’aient aucune dépendance vis-à-vis de systèmes externes, comme une base de données ou même le système de fichiers de la machine.
Les tests unitaires apportent trois atouts à la production :
trouver les erreurs plus facilement. Les tests sont exécutés durant tout le développement, permettant de visualiser si le code fraîchement écrit correspond au besoin ;
sécuriser la maintenance. Lors d’une modification d’un programme, les tests unitaires signalent les éventuelles régressions. En effet, certains tests peuvent échouer à la suite d’une modification, il faut donc soit réécrire le test pour le faire correspondre aux nouvelles attentes, soit corriger l’erreur se situant dans le code ;
documenter le code. Les tests unitaires peuvent servir de complément à la documentation ; il est très utile de lire les tests pour comprendre comment s’utilise une méthode. De plus, il est possible que la documentation ne soit plus à jour, mais les tests, eux, correspondent à la réalité de l’application.
L’ensemble des tests unitaires doit être relancé après une modification du code, afin de vérifier qu’il n’y ait pas de régressions (l’apparition de nouveaux dysfonctionnements).
Si les tests échouent, le rôle du développeur est alors de :
soit corriger le test qu’il a écrit, car la fonctionnalité a évolué et le test ne correspond plus ;
soit faire évoluer le code, car le test a détecté un bug.
Il existe d’autres types de tests à lancer, qui dépendent des environnements où l’application est déployée, comme les tests de performance ou d’acceptation utilisateur en préproduction ; ou encore les smoke tests en production afin de garantir le fonctionnement normal de l’application en production.
Lancez votre pipeline CI/CD
Pour voir le pipeline complet, il suffit de cliquer sur le sous-menu Pipelines dans le menu CI/CD.
En cliquant sur le statut running du pipeline, nous avons plus de détails sur ce pipeline, les jobs associés ainsi que leurs statuts.
Corrigez les bugs de votre application
Afin de démontrer la valeur d’un workflow typique de CI/CD, nous allons introduire un bug dans l’application. Ainsi, nous verrons comment le pipeline de CI/CD le détecte et le corrige. Suivez les étapes dans la vidéo ci-dessous.
Dans ce screencast, vous avez modifié un fichier sur votre poste en local. Une fois ce fichier modifié, vous avez poussé vos changements et constaté le lancement du pipeline. Le fichier poussé contenait intentionnellement un bug afin de voir comment créer un bug associé à un statut de pipeline non réussi. Vous avez enfin modifié de nouveau le fichier afin de corriger le bug et poussé les changements sur une nouvelle branche afin de pouvoir faire une Merge Request et voir le bug corrigé automatiquement lors du succès du pipeline.
En résumé
Le pipeline d’intégration continue permet d’implémenter les étapes de la CI dans GitLab.
Pour implémenter le pipeline, il faut définir ses étapes, les accélérer avec le cache et définir les jobs à effectuer.
Lors de l’étape de test, le pipeline exécute les tests unitaires existants au sein du projet.
Le pipeline de CI/CD détecte les bugs et les corrige.
Maintenant que vous avez intégré et testé votre code, vous allez pouvoir mettre en place les deux dernières étapes de l’intégration continue dans le chapitre suivant.