Mise à jour : 7 janvier 2025
Lecture : 20 min
Vous débutez dans l'intégration continue ? Apprenez à créer votre premier pipeline CI avec GitLab. Lisez notre guide complet.

Imaginons que vous ne connaissiez rien au concept d’intégration continue (CI) ni à son rôle clé dans le cycle de vie du développement logiciel. À présent, supposons que vous travaillez sur un projet pour lequel l'intégralité du code est répartie dans seulement deux fichiers. Pour garantir le bon fonctionnement de ce projet, il est impératif que la concaténation de ces deux fichiers contienne la phrase « Hello world ». Toute la réussite du projet repose sur cette simple phrase, car sans elle, tout serait compromis. Conscient de cet enjeu, votre meilleur développeur logiciel a décidé de créer un script qui s'exécute dès qu’un nouveau morceau de code est envoyé aux clients. Voici à quoi cela ressemble :
cat file1.txt file2.txt | grep -q "Hello world"
Même si, en l'état, ce script permet d'exécuter notre tâche, son déclenchement reste manuel. Et, avec une équipe de développement composée de 10 personnes, vous n'êtes pas à l'abri d'une erreur humaine qui pourrait vous coûter très cher. La preuve en est, pas plus tard que la semaine dernière, un nouveau membre de votre équipe a oublié d'exécuter le script, provoquant des erreurs de compilation pour trois de vos clients. Vous prenez donc la décision de résoudre ce problème, une bonne fois pour toutes, en utilisant le pipeline d'intégration et de livraison continues de GitLab. Par chance, votre code est déjà sur la plateforme. Il ne vous reste plus qu'à vous lancer.
À la lecture de la documentation de GitLab, nous savons qu'il suffit de réunir deux lignes de code dans un fichier appelé .gitlab-ci.yml:
test:
script: cat file1.txt file2.txt | grep -q 'Hello world'
Nous le validons et constatons que la compilation s'est déroulée avec succès
Maintenant, remplaçons « world » par « Africa » dans le deuxième fichier et voyons ce qui se passe :
Comme nous pouvions le prévoir, la compilation a échoué.
Nous avons désormais mis en place l'automatisation des tests.
À partir de maintenant, GitLab CI exécutera notre script de test dès que nous effectuerons un push du code vers le dépôt de code source dans l'environnement DevOps.
Remarque : dans l'exemple ci-dessus, nous supposons que file1.txt et file2.txt existent sur l'hôte du runner. Pour exécuter cet exemple dans GitLab, utilisez le code ci-dessous, qui crée d'abord les fichiers, puis exécute le script.
test:
before_script:
- echo "Hello " > | tr -d "\n" | > file1.txt
- echo "world" > file2.txt
script: cat file1.txt file2.txt | grep -q 'Hello world'
Pour simplifier notre démonstration, nous partons du principe que ces fichiers existent déjà sur l'hôte. Nous n'allons donc pas les créer dans les étapes suivantes.
La prochaine étape consiste à empaqueter le code avant de l'envoyer aux clients. Alors, pourquoi ne pas automatiser aussi cette partie du processus de développement logiciel ? Pour cela, tout ce que nous devons faire est de définir un autre job pour l'intégration continue. Commençons par nommer notre job « package » :
test:
script: cat file1.txt file2.txt | grep -q 'Hello world'
package:
script: cat file1.txt file2.txt | gzip > package.gz
Nous avons maintenant deux onglets :
Cependant, nous avons oublié de spécifier que le nouveau fichier est un artefact de compilation, afin qu’il puisse être téléchargé. Nous pouvons corriger cela en ajoutant une section artefacts :
test:
script: cat file1.txt file2.txt | grep -q 'Hello world'
package:
script: cat file1.txt file2.txt | gzip > packaged.gz
artifacts:
paths:
- packaged.gz
Vérifions que tout est en place :
Félicitations ! Tout semble fonctionnel. En revanche, dans la configuration actuelle, les jobs s'exécutent en parallèle. Cela signifie que notre application pourra être empaquetée, et ce, même si les tests échouent. Pour éviter que cela ne se produise, nous allons devoir exécuter les jobs de manière séquentielle.
Pour éviter d'empaqueter une application contenant des erreurs, nous allons faire en sorte d'exécuter le job « package » uniquement si les tests sont préalablement réussis. Pour commencer, définissons l'ordre d'exécution en établissant des étapes spécifiques :
stages:
- test
- package
test:
stage: test
script: cat file1.txt file2.txt | grep -q 'Hello world'
package:
stage: package
script: cat file1.txt file2.txt | gzip > packaged.gz
artifacts:
paths:
- packaged.gz
Cela devrait maintenant fonctionner. Nous souhaitons également garantir que la compilation (qui est représentée par la concaténation dans notre exemple) ne s'exécute qu'une seule fois. En effet, cette étape pouvant être chronophage, il serait dommage de l'exécuter inutilement. Pour éviter cela, définissons une étape supplémentaire :
stages:
- compile
- test
- package
compile:
stage: compile
script: cat file1.txt file2.txt > compiled.txt
artifacts:
paths:
- compiled.txt
test:
stage: test
script: cat compiled.txt | grep -q 'Hello world'
package:
stage: package
script: cat compiled.txt | gzip > packaged.gz
artifacts:
paths:
- packaged.gz
Jetons un œil à nos artefacts :
Tout a l'air de fonctionner. En revanche, il faudrait éviter de rendre le fichier « compile » téléchargeable. Pour cela, nous allons rendre nos artefacts temporaires expirables en définissant expire_in à « 20 minutes ».
compile:
stage: compile
script: cat file1.txt file2.txt > compiled.txt
artifacts:
paths:
- compiled.txt
expire_in: 20 minutes
Maintenant, notre configuration semble plutôt complète :
Jusqu'ici, tout va bien. Cependant, en regardant de plus près, nos compilations semblent toujours lentes. Prenons un moment pour regarder les journaux (logs) :
En observant de plus près, nous remarquons la mention suivante : ruby:3.1. Cela signifie que GitLab.com utilise des images Docker pour exécuter nos compilations, et qu’il utilise par défaut, l'image ruby:3.1.
Cette image contient certainement de nombreux paquets dont nous n'avons pas besoin. Dans un souci d'optimisation de notre pipeline CI, il serait donc préférable de changer d'image. Après une courte recherche sur Google, nous découvrons qu'il existe une image Linux presque vierge appelée alpine. Nous allons alors l'utiliser. Pour cela, nous devrons ajouter image: alpine au fichier .gitlab-ci.yml.
Et voilà ! Nous avons maintenant réduit le temps de compilation de presque trois minutes :
Vous pouvez également trouver des images libres de droits sur mysql, Python, Java et php. Il est facile, alors, d'en choisir une pour notre pile technologique.
Note : il sera toujours préférable d'utiliser une image qui ne contient aucun logiciel supplémentaire dont vous n'avez pas besoin, car cela minimise grandement le temps de téléchargement.
Imaginons maintenant un scénario un peu plus complexe. Par exemple, un nouveau client souhaite que nous empaquetions notre application au format .iso plutôt qu'en .gz.
Étant donné que le pipeline d'intégration continue gère tout le processus, et que les images ISO peuvent être créées avec la commande mkisofs, il suffit d'ajouter un job supplémentaire.
Voici à quoi notre configuration devrait ressembler :
image: alpine
stages:
- compile
- test
- package
# ... "compile" and "test" jobs are skipped here for the sake of compactness
pack-gz:
stage: package
script: cat compiled.txt | gzip > packaged.gz
artifacts:
paths:
- packaged.gz
pack-iso:
stage: package
script:
- mkisofs -o ./packaged.iso ./compiled.txt
artifacts:
paths:
- packaged.iso
Notez que les noms des jobs ne doivent pas être nécessairement identiques. S'ils l'étaient, il serait impossible de faire s'exécuter les jobs en parallèle dans la même étape du processus de développement logiciel.
Ainsi, dans l'exemple qui suit, ignorez le fait que les jobs et étapes portent le même nom.
Quoi qu'il en soit, la compilation échoue :
Le problème vient de mkisofs qui n'est pas inclus dans l'image alpine. Nous devons donc d'abord l'installer.
Selon le site Web d’Alpine Linux, mkisofs fait partie des paquets xorriso et cdrkit. Voici les commandes que nous devons exécuter pour installer un paquet :
echo "ipv6" >> /etc/modules # enable networking
apk update # update packages list
apk add xorriso # install package
Ces commandes s'exécutent de la même manière que toute autre commande au sein du processus d'intégration continue. La liste complète des commandes que nous devons transmettre à la section script devrait ressembler à ceci :
script:
- echo "ipv6" >> /etc/modules
- apk update
- apk add xorriso
- mkisofs -o ./packaged.iso ./compiled.txt
Cependant, pour des raisons sémantiques, plaçons les commandes liées à l'installation du paquet dans before_script.
Notez que si vous utilisez before_script au niveau supérieur d'une configuration, alors les commandes s'exécuteront avant tous les jobs. Dans notre cas, nous voulons simplement qu'elles s'exécutent avant un job spécifique.
Plus haut, nous avons configuré des étapes pour que les jobs d'empaquetage ne s'exécutent qu'à la condition que les tests réussissent. Mais, que se passerait-il si nous voulions bouleverser le séquencement des étapes en exécutant certains jobs plus tôt qu'initialement prévu ?
Dans certains cas, le séquencement traditionnel des étapes peut ralentir la durée globale d'exécution du pipeline CI/CD. Pour éviter cela, nous pouvons choisir de personnaliser le séquencement de nos jobs.
Par exemple : imaginons que notre étape de test comprenne des tests lourds, prenant beaucoup de temps à s'exécuter. Supposons aussi que ces tests ne soient pas nécessairement liés aux jobs d’empaquetage. Dans ce cas, il serait préférable que les jobs d’empaquetage puissent démarrer sans attendre la fin de ces tests. C'est là qu'interviennent les graphes acycliques orientés. Ces derniers visent à rompre l'ordre normal d'exécution des jobs (ordre séquentiel) grâce à la création de dépendances entre certains jobs. Vous pouvez ainsi définir un ordre personnalisé pour exécuter les différents jobs de votre pipeline CI.
Grâce au mot-clé needs, GitLab crée des dépendances entre les jobs et leur permet de s'exécuter plus tôt, dès que leurs jobs dépendants sont terminés.
Dans l'exemple ci-dessous, les jobs d’empaquetage commenceront à s'exécuter dès que le test sera terminé. Ainsi, à l'avenir, si quelqu'un ajoute d'autres tests à l'étape de test, les jobs d’empaquetage commenceront à s'exécuter avant la fin des nouveaux tests :
pack-gz:
stage: package
script: cat compiled.txt | gzip > packaged.gz
needs: ["test"]
artifacts:
paths:
- packaged.gz
pack-iso:
stage: package
before_script:
- echo "ipv6" >> /etc/modules
- apk update
- apk add xorriso
script:
- mkisofs -o ./packaged.iso ./compiled.txt
needs: ["test"]
artifacts:
paths:
- packaged.iso
Voici notre version définitive de .gitlab-ci.yml:
image: alpine
stages:
- compile
- test
- package
compile:
stage: compile
before_script:
- echo "Hello " | tr -d "\n" > file1.txt
- echo "world" > file2.txt
script: cat file1.txt file2.txt > compiled.txt
artifacts:
paths:
- compiled.txt
expire_in: 20 minutes
test:
stage: test
script: cat compiled.txt | grep -q 'Hello world'
pack-gz:
stage: package
script: cat compiled.txt | gzip > packaged.gz
needs: ["test"]
artifacts:
paths:
- packaged.gz
pack-iso:
stage: package
before_script:
- echo "ipv6" >> /etc/modules
- apk update
- apk add xorriso
script:
- mkisofs -o ./packaged.iso ./compiled.txt
needs: ["test"]
artifacts:
paths:
- packaged.iso
Nous venons de créer un pipeline ! Ainsi, nous avons trois étapes séquentielles avec les jobs pack-gz et pack-iso qui s'exécutent en parallèle à l'intérieur de l'étape d'empaquetage :

Nous allons maintenant découvrir comment améliorer notre pipeline d'intégration continue.
L'objectif clé du développement logiciel avec une approche DevOps est de réussir à créer des applications offrant une excellente expérience utilisateur. Avec cet objectif en tête, pourquoi ne pas renforcer le cycle de développement logiciel en cherchant à détecter d'éventuels bogues dès le début du processus ? Pour ce faire, ajoutons des tests à notre pipeline CI. De cette façon, nous pourrons résoudre les problèmes le plus tôt possible. Par chance, le pipeline CI de GitLab nous facilite la tâche en proposant des templates de tests prêts à l'emploi. Tout ce que nous avons à faire, c'est d'inclure ces templates dans la configuration de notre pipeline CI. Dans cet exemple, nous allons réaliser des tests d'accessibilité :
stages:
- accessibility
variables:
a11y_urls: "https://about.gitlab.com https://www.example.com"
include:
- template: "Verify/Accessibility.gitlab-ci.yml"
Personnalisez la variable a11y_urls pour répertorier les URL des pages web à tester avec Pa11y et GitLab Code Quality.
include:
- template: Jobs/Code-Quality.gitlab-ci.yml
GitLab facilite la consultation du rapport de test directement dans la zone du widget de la merge request. Ce widget vous permet de voir la revue de code, l'état du pipeline et les résultats des tests au même endroit. La capture d'écran ci-dessous montre à quel point ce widget facilite le travail de vos équipes.
