L'intégration continue fonctionne en poussant de petits morceaux de code vers la base de code de votre application hébergée dans un dépôt Git, et à chaque poussée, un pipeline est exécutez pour construire, tester et valider les changements de code avant de les fusionner dans la branche principale.
La livraison et le déploiement continus consistent en une étape supplémentaire de l'IC, qui consiste à déployer votre application en production à chaque poussée vers la branche par défaut du dépôt.
Ces méthodologies vous permettent de détecter les bogues et les erreurs dès le début du cycle de développement, garantissant que tout le code déployé en production est conforme aux normes de code que vous avez établies pour votre application.
Le service GitLab CI (Continuous Integration) fait partie du GitLab qui gère le projet et l'interface utilisateur et permet d'effectuer des tests unitaires sur chaque commit et indique par un message d'avertissement l'échec de la construction.
GitLab CI/CD est configuré par un fichier appelé .gitlab-ci.yml placé à la racine du dépôt. Ce fichier crée un pipeline, qui s'exécute pour les modifications du code dans le dépôt. Les pipelines sont constitués d'une ou plusieurs étapes qui s'exécutent dans l'ordre et peuvent contenir chacune un ou plusieurs travaux qui s'exécutent en parallèle. Ces travaux (ou scripts) sont exécutés par l'agent GitLab Runner.
Pour avoir un runner partagé (Shared) celui-ci doit être attaché depuis l'admin de gitlab.
Écrire deux fois le même travail peut déjà devenir assez désordonné et difficile à maintenir. Plus vous ajoutez de tâches, plus la situation se dégrade.
Mais ne vous inquiétez pas, YAML a tout prévu.
Les “anchors”, “extends” et les “include” vous permettent de réutiliser des parties de votre configuration et de les étendre.
Si vous souhaitez que votre commande soit exécutée avant tous les travaux, tout le temps, le plus simple est d'extraire le code commun sous forme de clé before_script au début du fichier :
before_script: - echo 'prepare' build: script: - echo 'build' test: script: - echo 'test'
Bien que cela ne vous donne pas beaucoup de flexibilité, c'est un moyen très rapide et direct de réutiliser des commandes.
Plutôt que de s'amuser à positionner un “-” devant chaque commande, on va plutôt placer la chose suivante: “script: |”.
Par exemple :
script:
- GOOS=linux GOARCH=amd64 go build -o builds/${PROJECT_NAME} src/main.go
- ./builds/${PROJECT_NAME}
Par :
script: | GOOS=linux GOARCH=amd64 go build -o builds/${PROJECT_NAME} src/main.go ./builds/${PROJECT_NAME}
C'est une astuce qui n'est pas souvent utilisée mais qu'il est tellement pratique!
Vous pouvez ainsi déclarer de la configuration et l'appeler un plusieurs fois, génial pour éviter de se répéter!
stages: - echo .echo: &echo #keys (jobs in this case) with a dot in front are hidden keys and won't be executed by GitLab stage: echo script: - echo $ECHO_TEXT echo-hello: <<: *echo variables: ECHO_TEXT: "Hello world!" echo-bye: <<: *echo variables: ECHO_TEXT: "Bye bye!"
Dans notre dernier exemple, nous créons un job modèle echo contenant notre scène et notre script. Le job echo est ensuite prolongé dans echo-hello et echo-bye.
Voici un exemple concret dans le dépôt public de Icinga 2 : https://git.icinga.com/packaging/rpm-icinga2
Attention:
Attention tout de même, cette fonction a ses limites, en effet, il n'est pas possible d'utiliser les anchors sur plusieurs fichiers lorsque vous utilisez la directive include.
Les anchors ne sont valables que dans le fichiers dans lequel elles ont été définies.
La directive extends est un excellent moyen de réutiliser certaines parties de YAML à plusieurs endroits (une sorte de 'template'), par exemple:
.go_image_template:
image:
name: go:latest
test:
extends: .go_image_template
script:
- echo "Testing"
deploy:
extends: .go_image_template
script:
- echo "Deploying"
Dans l'exemple, je créé une clé cachée (car elle commence par un “.”) et haut de mon fichier que je peux ensuite appeler à travers les différentes étapes (test, deploy).
Bien sûr, on peut étendre les fonctionnalités afin d'obtenir des choses bien plus complexes et de réduire la complexité et de rendre la pipeline plus lisible.
Note:
Si vous souhaitez réutiliser du code dans plusieurs fichiers, vous pouvez utiliser le mot-clé extends. Cela fonctionne de la même manière que l'héritage - vous définissez “extends” que d'autres jobs peuvent ensuite étendre.
Bon, pour éviter de se répéter, on peut inclure un template (un fichier YAML) grâce à la directive include dans notre pipeline actuelle.
Le fichier peut être soit local, soit distant dans un autre dépôt. 🐱🏍
On pourrait très bien créer un dépôt qui contient uniquement nos templates pour tous nos projets. Ce qui est très pratique si on souhaite partir sur une pratique DevSecOps où l'équpe sécu pourrait founir des templates ayant les prérequis sécu! 😊
Pour l'exemple (fichier dans le repo templates):
stages: - test variables: ENV: "none" test: script: - echo "Testing in ${ENV}"
Et dans notre fichier gitlab-ci.yml:
include: 'https://gitlab.com/templates/raw/master/base-testing.yml' variables: ENV: "dev"
Nous pourrons ainsi réutiliser le fichier base-testing.yml défini dans notre dépôt nommé templates de modèles de CI, et nous écraserons simplement toutes les variables avec leur valeur du fichier de base.
Il est possible de déclarer des fonctions shell directement dans le fichier .gitlab-ci.yml.
Pour ce faire:
.tools: &tools | function say_hello() { echo "Hello $1" } function addition() { echo "$(($1 + $2))" } before_script: # Appeler "tools" avant d'exécuter le script! - *tools
Pour aller encore plus loin on peut les inclure dans notre fichier template et les appeler avec la directive include (appelé le fichier que les détient).
Pour les appeler:
stages:
- hello
use_the_function:
stage: hello
tags:
- shell
script: |
say_hello "Coucou Gitlab!"
addition 1 2 # retourne ainsi 3 ^^
Toujours dans une démarche pour éviter les répétitions, ont voit là, toutes les possibilités avec ce fichier qui sont énormes!