En tant que développeurs, nous connaissons tous l'acronyme DRY : don’t repeat yourself Et donc nous savons tous qu'il faut autant que possible factoriser notre code. Pour nos Gitlab-ci, nous avons à notre disposition les templates.
Mais pourquoi donc ?
Si vous êtes un devops travaillant sur plusieurs projets sur GitLab, vous aurez vite remarqué que si vous devez modifier un pattern dans tous vos projets ce la peut vite devenir fastidieux.
Prenons le cas d'une application utilisant un grand nombre de librairies toutes standardisées utilisant à peu près tous les mêmes stages : deploy, test, .., release. Si vous deviez reporter la moindre modification sur un template à toutes cela pourrait être improductif et sources d'erreurs. Alors voyons ensemble les templates Gitlab-ci
Créer et inclure template Gitlab-ci
Pour stocker les différents composants de votre template, créez tout simplement un projet gitlab indépendant. Créez-y vos fichiers yaml.
Par exemple nous voulons définir un template d'installation de packages nodejs (le premier commentaire est le nom du projet suivi du nom du fichier) :
# template-ci/install.yml
install:
script:
- npm install
Ensuite dans vos projets d'applications, il suffit d'inclure dans votre .gitlab-ci.yml un appel à ce script.
# mon-app1/.gitlab-ci.yml
include:
- project: 'template-ci'
file: 'install.yml'
ref: 'master'
Vous remarquez certainement la balise ref
. Elle permet de pointer vers une
branche du projet de template. Cela va vous permettre de pouvoir faire évoluer
votre ci sans impacter toute votre production. En effet, dans un projet préci,
vous pourrez utiliser une branche de votre template pour le faire évoluer par
exemple. Vous pourriez également créer des branches pour des variantes de votre
template de ci.
Paramétrer un template gitlab-ci
Pour le moment, install recherche uniquement les dépendances déclarées dans le fichier package.json. Mais voilà dans un de vos projets, nous voulons lui indiquer un autre fichier de déclarations. Pour répondre à cela gitlab propose les variables d'environnement.
Pour éviter des effets indésirables je vous recommande de déclarer toutes les tâches de templates en tant que taches masquées et d'utiliser extend si nécessaire. En utilisant cette méthode une tache masquée incluse non déclarée en extend ne sera pas exécuté. Donc si on lance notre précédant pipeline d'application il ne se passera rien !
Pour déclarer une tache masquée il suffit de lui mettre un .
devant son nom. Ce
qui donne :
# template-ci/install.yml
.install:
variables:
INSTALL_DIRECTORY: '.'
script:
- cd $INSTALL_DIRECTORY
- npm install
Dans notre le ci de l'application nous devons utiliser le mot clé extends
:
# mon-app1/.gitlab-ci.yml
include:
- project: 'template-ci'
file: 'install.yml'
ref: 'master'
projet1:
extends: .install
variables:
INSTALL_DIRECTORY: 'projet1/'
projet2:
extends: .install
variables:
INSTALL_DIRECTORY: 'projet2/'
Définir des templates dans le même fichier
Il est possible de définir les templates de job dans le fichier .gitlab-ci.yml. On reprend le même principe
Exemple :
.pip_install:
stage: sanity check
image: fedora:31
before_script:
- curl https://bootstrap.pypa.io/get-pip.py -o /root/get-pip.py
- python3 /root/get-pip.py
- dnf install -y git-core
sanity check:
extends: .pip_install
script:
- pip3 install --user jinja2 PyYAML
- python3 ./src/generate_templates.py
- git diff --exit-code && exit 0 || true
- echo "some files were not generated through 'src/generate_templates.py' or
have not been committed. Please edit the files under 'src', run
'src/generate_templates.py' and then commit the result"
- exit 1
Utilisation des ancres
Une autre syntaxe possible fait appel à ce aux ancres yaml. Il s'agit d'un nom
auquel est accolé un &ancre
. Ensuite vous rappelez <<: *ancre
.
Un exemple :
.job_template: &job_configuration
image: ruby:2.6
services:
- postgres
- redis
test1:
<<: *job_configuration
script:
- test1 project
test2:
<<: *job_configuration
script:
- test2 project
Cela peut être utile si on souhaite utiliser deux template de job dans un job :
.job_template: &job_configuration
script:
- test project
tags:
- dev
.postgres_services:
services: &postgres_configuration
- postgres
- ruby
.mysql_services:
services: &mysql_configuration
- mysql
- ruby
test:postgres:
<<: *job_configuration
services: *postgres_configuration
tags:
- postgres
test:mysql:
<<: *job_configuration
services: *mysql_configuration
On peut aussi l'utiliser pour étendre des variables, mais aussi les scripts :
variables: &global-variables
SAMPLE_VARIABLE: sample_variable_value
ANOTHER_SAMPLE_VARIABLE: another_sample_variable_value
# a job that must set the GIT_STRATEGY variable, yet depend on global variables
job_no_git_strategy:
stage: cleanup
variables:
<<: *global-variables
GIT_STRATEGY: none
script: echo $SAMPLE_VARIABLE
Pour l'utiliser avec les include il faut alors utiliser des !références
:
# setup.yml
.setup:
script:
- echo creating environment
et dans le ci lui même :
include:
- local: setup.yml
.teardown:
after_script:
- echo deleting environment
test:
script:
- !reference [.setup, script]
- echo running my own command
after_script:
- !reference [.teardown, after_script]
Depuis la version 14.3 de gitlab il est possible de conditionner les include par des règles.
include:
- local: builds.yml
rules:
- if: '$INCLUDE_BUILDS == "true"'
Plus loin avec les templates gitlab-ci
Il existe de nombreux autres patterns d'utilisation des templates de ci. Le plus simple, c'est de lire ceux de gros projets héberger dans gitlab.com comme celui gitlab/release-cli ou celui de freedesktop.org