Skip to content
On this page

Contexte d'exécution des pipelines

Runners

Les runners sont les agents où s'exécutent les étapes des pipelines.

Chaque runner a des caractéristiques qui lui sont propres (Windows vs Linux vs macOS, linux distrib, CPU arch, outils à dispo...).

Les runners sont identifiés et utilisés au moyen de tags.

yaml
my job:
  tags:
    - ruby
    - postgres
  script:
    - ...

Si plusieurs tags sont indiqué dans le job, le runner doit avoir tous les tags pour être choisi.

!

Possible d'utiliser des variables CICD dans les tags (Gitlab 14.1 [ref])

Runner scopes

L'utilisation d'un runner par une pipeline dépend du scope du runner, c.a.d là où il a été enregistré :

  • un runner enregistré au niveau d'un projet n'est utilisable que par celui ci
  • un runner enregistré au niveau d'un groupe est accessible par tous les projets enfants de ce groupe (quelque soit la profondeur de la hiérarchie)
  • un runner enregistré au niveau d'une instance de gitlab est utilisable par tous les projets de cette instance

L'assignation des jobs aux runners est différente selon le type de runner :

  • groupe & projet : utilisation d'une queue FIFO
  • instance : utilisation d'une file d'attente (fair usage queue), ceci pour éviter que des projets ne créent des centaines de jobs et saturent tous les runners partagés disponibles

!

[ref]

Runners executor

L'executor est le programme qui tourne sur le runner et qui est en charge d'exécuter les jobs.

Les différents types d'executors supportés :

  • Shell
  • SSH
  • Docker
  • Virtual box
  • Parrallels
  • Kubernetes
  • Custom

Certaines fonctionnalités du .gitlab-ci.yml ne sont pas supportées selon les executors :

ExecutorSSHShellVirtualBox/ParallelsDockerKubernetes
Secure Variables
GitLab Runner Exec command
.gitlab-ci.yml: image
.gitlab-ci.yml: services
.gitlab-ci.yml: cache
.gitlab-ci.yml: artifacts
Passing artifacts between stages
Use GitLab Container Registry private imagesn/an/an/a
Interactive Web terminal✓ (UNIX)

* via $CUSTOM_ENV_CI_JOB_IMAGE

[ref]

Notes sur les executors Docker et Kubernetes

Les executors Docker et Kubernetes lancent les jobs qui leur sont associés dans des conteneurs. Il est donc nécessaire de définir l'image à utiliser pour le job :

  1. Au sein du job
yaml
python job:
  image: python:3.11
  script: python --version
  1. Au sein du runner avec une image par défaut

Les runners proposés dans l'offre SaaS de Gitlab ont un Docker executor avec ruby:3.1 comme image par défaut

Variables

Gitlab CI propose un méchanisme de variables permettant d'accéder à différentes informations liées au projet dans lequel la pipeline s'exécute, de contrôler son exécution ainsi qu'éviter des répétitions.

Définitions des variables

Les variables de CICD peuvent être définies à différents endroits :

  • Pour toute une pipeline, avec le keyword global:variables
    • les variables définies ainsi sont accessibles dans tous les jobs de la pipeline
  • Pour un job en particulier, avec le keyword [job]:variables

Exemple :

yaml
variables:
  GLOBAL_VAR: "A global variable"

job1:
  variables:
    JOB_VAR: "A job variable"
  script:
    - echo "Variables are '$GLOBAL_VAR' and '$JOB_VAR'"

job2:
  script:
    - echo "Variables are '$GLOBAL_VAR' and '$JOB_VAR'"

Il est également possible de définir des variables CICD au niveau d'un projet, d'un groupe voire même d'une instance Gitlab. Dans ce cas :

  • les variables de CICD définies au niveau d'un projet sont accessibles dans toutes les pipelines s'exécutant dans ce projet
  • les variables de CICD définies au niveau d'un groupe sont accessibles dans toutes les pipelines des projets enfant du groupe (quelque soit la profondeur de la hiérarchie)
  • les variables de CICD définies au niveau d'une instance Gitlab sont accessibles dans toutes les pipelines des projets de cette instance

!

Précédence des variables

Dans le cas où une variable de CICD est définie à plusieurs endroits, certaines règles sont en place pour déterminer la précédence (de la plus forte à la plus faible) :

  1. Toutes ces variables sont au niveau le plus haut :
    • Variables lors d'un trigger via l'API
    • Variables fournies à une pipeline programmée
    • Variables fournies lors d'un run manuel
    • Variables fournies lors d'un lancement d'une pipeline via l'API REST
  2. Variables de projet
  3. Variables de groupes
    • If the same variable name exists in a group and its subgroups, the job uses the value from the closest subgroup. For example, if you have Group > Subgroup 1 > Subgroup 2 > Project, the variable defined in Subgroup 2 takes precedence.
  4. Variables d'instance
  5. Variables des reports dotenv
  6. Variables définies dans les jobs du .gitlab-ci.yml
  7. Variables définies au niveau global dans le .gitlab-ci.yml
  8. Variables liées au déploiement
  9. Variables prédéfinies

[ref]

Variables prédéfinies

Un certain nombre de variables de CICD sont prédéfinies et accessibles au sein de tous les jobs.

  • CI_COMMIT_*: différentes infos sur le commit sur lequel pointe la pipeline (auteur, SHA, message...)
  • CI_JOB_*: différentes infos sur le job en cours d'exécution (nom, stage...)
    • CI_JOB_TOKEN: un token temporaire émis par l'instance de Gitlab pour chaque job et permettant de s'authentifier sur certains endpoint de l'API Gitlab [ref]
  • CI_PIPELINE_*: différents infos sur la pipeline dans laquelle le job courant s'excute
  • CI_PROJECT_*: différents infos sur le project dans lequel le job courant s'excute
  • CI_MERGE_REQUEST_*: différentes infos sur la merge request liée à la pipeline, seulement si $CI_PIPELINE_SOURCE == 'merge_request_event'

[ref]

Où et comment utiliser les variables

Il est possible d'utiliser des variables CICD à différents endroits de la config dans le .gitlab-ci.yml.

yaml
variables:
  BASE_PYTHON_IMAGE: python

stages:
  - tests

test python 3.8:
  stage: tests
  variables:
    PYTHON_VERSION: '3.8'
  image: ${BASE_PYTHON_IMAGE}:${PYTHON_VERSION}
  script:
    - echo "Running tests for python ${PYTHON_VERSION}"
    - pytest

test python 3.9:
  stage: tests
  variables:
    PYTHON_VERSION: '3.9'
  image: ${BASE_PYTHON_IMAGE}:${PYTHON_VERSION}
  script:
    - echo "Running tests for python ${PYTHON_VERSION}"
    - pytest

test python 3.10:
  stage: tests
  variables:
    PYTHON_VERSION: '3.10'
  image: ${BASE_PYTHON_IMAGE}:${PYTHON_VERSION}
  script:
    - echo "Running tests for python ${PYTHON_VERSION}"
    - pytest

[ref]

Notes sur la syntaxe des variables

Le remplacement des variables CICD utlisées dans un pipeline par leur valeur est fait à différents moments, selon l'endroits de leur utilisation : instance Gitlab, runner ou shell d'éxecution. La syntaxe autorisée pour les variables diffère selon l'outil effectuant le remplacement :

  • Instance Gitlab : $variable${variable} ou %variable%
  • Runner : $variable ou ${variable}
  • Shell d'éxecution : selon le shell utilisé

[ref]

Sécurisation des variables de CICD

Les variables de CICD définies au sein du fichier de config .gitlab-ci.yml ne doivent pas être utilisées pour contenir des données sensibles (token, mpd...), puisque le fichier de config est accessible librement (modulo la visibilité du repo au sein de l'instance).

Les variables définies au niveau d'un projet, d'un groupe ou d'une instance peuvent être marquées comme sensibles, et ainsi être protégées de la modification ainsi que leur affichage dans les logs.

INFO

La valeur des variables à masquer doit respecter quelques contraintes pour pouvoir être correctement masquée dans les logs. Penser à utiliser l'encodage en base64 pour éviter des problèmes de caractères invalides. [ref]

WARNING

Ce mécanisme est du best-effort. Pour augmenter la sécurité de ces secrets, on peut préférer les variables de type file (pour éviter des leaks via des env|printenv), voire même l'utilisation de vrais outils dédiés à la gestion des secrets (cf Jour 2 - Intégration avec des outils externes)

Règles d'exécution conditionnelle

Comme le fichier .gitlab-ci.yml est le seul fichier de configuration des pipelines de CICD, il est nécesaire de pouvoir controler à quel moment et sous quelles conditions les jobs sont ajoutés aux pipelines, afin de pouvoir créer des workflows plus complexes.

Il est ainsi possible de définir des règles indiquant dans quel cas ajouter les jobs à la pipeline, à l'aide des keyword rulesou only/except

INFO

only/except ne sont plus activement maintenus, il est recommandé d'utiliser rules pour définir les règles d'ajout conditionnel des jobs aux pipelines [ref]

WARNING

Les règles d'ajout d'un job à une pipelines sont évaluées lors de la création de la pipeline. Ainsi il n'est pas possible d'utiliser des variables dont la valeur est définie lors de l'exécution de la pipeline.

Seules les variables prédéfinies, ou celles définies dans le fichier de config, au niveau du projet, d'un groupe parent ou de l'instance peuvent être utilisées.

Plusieurs contrôles sont possibles :

  • rules:if: contrôle sur une expression booléenne [ref]
  • rules:changes: contrôles sur un/des fichier(s) changé(s) dans la branche/le commit en question via un ou plusieurs patterns [ref]
    • Si plusieurs pattern sont fournis, la règle est considérée vraie si au moins un des pattern match (ie. un OR est appliqué)
  • rules:exists: contrôles sur un/des fichier(s) existant(s) dans la branche/le commit en question via un ou plusieurs patterns [ref]
    • Si plusieurs pattern sont fournis, la règle est considérée vraie si au moins un des pattern match (ie. un OR est appliqué)

Si plusieurs conditions sont fournies dans une même règles, elles doivent toutes matcher pour que le job soit ajouté dans la pipeline (ie. un AND est appliqué entre toutes les conditions).

Si plusieurs règles sont indiquées, elles sont évaluées de haut en bas. La première qui match est utilisée. Si aucun règle ne match, le job n'est pas ajouté.

[ref]

Paramètres des jobs

Les jobs ont différents paramètres qui contrôlent différents aspects de leur comportement :

  • [job]:variables: permet de définir ou redéfinir une ou plusieurs variables accessibles au sein du job en question [ref]
  • [job]:when: indique sous quelles conditions lancer le job (voir détail ci-dessous) [ref]
  • [job]:allow_failure: indique si le job est autoriser à échouer ou non. allow_failure: true permet de continuer l'exécution de la pipeline quelque soit le résultat du job [ref]
    • Si un job ayant allow_failure: true échoue il sera marqué comme étant en succès avec un warning. Ce warning se répercutera sur la pipeline qui sera elle aussi marquée comme succès avec un warning (sauf un autre job échoue)

Ces paramètres peuvent être définis ou mis à jour en utilisant des différentes conditions :

  • rules:when: indique sous quelle condition lancer un job. [ref]
    • Ce keyword a la précédence dans le cas où whenest aussi défini au niveau du job
  • rules:variables: une liste de variables définies ou mises à jours pour le job [ref]
    • Si une variable est définie à la fois dans le job et dans une rules, la valeur fournie au niveau de la rule aura la priorité
  • rules:allow_failure: indique si le job est autorisé à échouer ou non. allow_failure: true permet de continuer l'exécution de la pipeline quelque soit le résultat du job [ref]
    • Un job ajouté avec allow_failure: true est considéré comme étant tout le temps en succès

À propos du keyword when

Ce keyword indique sous quelle condition lancer un job :

  • on_success: exécute le job uniquement lorsque tous les jobs précédents sont considérées en succès
  • on_failure : exécute le job uniquement lorsque au moins une tâche à une étape antérieure échoue
  • manual: lance le job manuellement
  • never : ne pas ajouter le job dans la pipeline (ne peut être utilisé que dans les rules)
  • always : exécuter le job quel que soit le statut des jobs aux étapes précédentes
  • delayed: retarde l’exécution d’un job pendant une durée spécifiée

Exemples

Construire une nouvelle version d'une image Docker que si le Dockerfile a changé ou bien qu'il y ait une nouvelle version (aka un nouveau tag) :

yaml
docker build:
  stage: build
  rules:
    - changes:
        - Dockerfile
        - docker/scripts/*
      variables:
        VERSION: dev
    - if: $CI_COMMIT_TAG
      variables:
        VERSION: $CI_COMMIT_TAG
    - when: never
  script: docker build -t my-image:$VERSION .

Déployer sur un environment particulier que dans le cas d'un commit sur une branche donnée :

yaml
deploy:
  stage: deploy
  rules:
    - if: $CI_COMMIT_BRANCH == "rct"
      variables:
        ENVIRONMENT: "RCT"
    - when: never
  script:
    - echo "Deploy to $ENVIRONMENT"
    - ...

Lancer des jobs manuellement

Un job est considéré manuel lorsqu'il est ajouté avec l'attribut when: manual. Il y a deux types de jobs manuels :

  1. jobs bloquants : allow_failure: false
    • Pipeline en status bloqué tant que le job n'a pas été lancé manuellement.
  2. jobs optionnels : allow_failure: true
    • Les jobs suivants sont lancés, le job courant n'est pas lancé tant qu'une action manuelle n'a pas été faite.

Le comportement par défaut est le suivant :

  • Si le job a when: manual défini au niveau du job, par défaut allow_failure: true
  • Si le job a when: manual défini au niveau d'une rule, par défaut allow_failure: false
yaml
stages:
  - stage 1
  - stage 2
  - stage 3

job 1:
  stage: stage 1
  script:
    - echo 'Running job from stage 1'

job 2:
  stage: stage 2
  rules:
    - when: manual
      allow_failure: true
  script:
    - echo 'Running job from stage 2'

job 3:
  stage: stage 3
  script:
    - echo 'Running job from stage 3'

Contrôler l'exécution des pipelines

Il est possible de définir des règles contrôlant dans quel cas les pipelines sont créées, à l'aide du keyword workflow:rules.

Les contrôles possibles sont les mêmes que pour les jobs.

yaml
workflow:
  rules:
  - if: $CI_COMMIT_TITLE =~ /-draft$/
    when: never
  - if: $CI_PIPELINE_SOURCE == "merge_request_event"
  - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

[ref]

Evénement déclenchant une pipeline

Une pipeline est toujours associée à un évenement qui l'a déclenchée. 12 différents types d'évenements existent, les plus courants :

  • push: Pour les pipelines associées à un evnt git push (commit, branche, tag)
  • merge_request_event: Pour les pipelines associées à une MR.
  • api, trigger: Pour les pipelines créées via l'API de pipelines ou bien à l'aide d'un trigger token.
  • pipeline: Pour les pipelines multi-projet créées à via l'API avec le CI_JOB_TOKEN, ou bien en utilisant le mot clé trigger.
  • schedule: Pour les pipelines programmées.
  • parent_pipeline: Pour les pipelines enfants créées via une pipeline parente dans la stratégie de pipelines parent/enfant.

[ref]

https://about.gitlab.com/blog/2022/02/22/parent-child-vs-multi-project-pipelines/https://docs.gitlab.com/ee/ci/pipelines/downstream_pipelines.html?tab=Parent-child+pipelinehttps://blog.stephane-robert.info/post/gitlab-trigger/

Artefacts

Les artefacts sont des fichiers/dossiers générés par des jobs. Ils sont rassemblés une fois l'exécution du job terminé, compressés par le runner et puis envoyés à l'instance Gitlab pour être stockés.

Ils peuvent être utilisé pour transmettre des résultats intermédiaires d'un job à un autre au sein d'une pipeline, ou bien mis à disposition des utilisateurs au sein de l'interface Gitlab.

yaml
pdf:
  script: xelatex mycv.tex
  artifacts:
    paths:
      - mycv.pdf

Configuration des artefacts

  • artifacts:paths et artifacts:exclude: une liste de chemins ou glob pattern pour identifier les fichiers ou dossiers à, respectivement, inclure ou exclure de la collecte des artefacts [ref]
  • artifacts:expire_in: permet d'indiquer la période de conservation des artefacts collectés. Par défaut à 30 jours [ref]
  • artifacts:when: indique dans quel cas collecter les artefacts, selon le statut du job [ref]

Partage d'artefacts entre jobs

Par défaut, un job va récupérer tous les artefacts des jobs précédents terminés en succès.

Il est possible de choisir les artefacts à récupérer dans un job avec [job].dependencies.

[ref]

yaml
stages:
  - build
  - test
  - package
  - upload

build macos 10:
  stage: build
  script:
    - echo "Building in macOS"
    - mkdir -p binaries
    - touch binaries/macos-10
    - sleep $((1 + $RANDOM % 10))
  artifacts:
    paths:
      - binaries/

build macos 11:
  stage: build
  script:
    - echo "Building in macOS"
    - mkdir -p binaries
    - touch binaries/macos-11
    - sleep $((1 + $RANDOM % 10))
  artifacts:
    paths:
      - binaries/

build debian 10:
  stage: build
  script:
    - echo "Building in debian"
    - mkdir -p binaries
    - touch binaries/debian-10
    - sleep $((1 + $RANDOM % 10))
  artifacts:
    paths:
      - binaries/

build debian 11:
  stage: build
  script:
    - echo "Building in debian"
    - mkdir -p binaries
    - touch binaries/debian-11
    - sleep $((1 + $RANDOM % 10))
  artifacts:
    paths:
      - binaries/

test macos 10:
  stage: test
  script:
    - echo "Testing in macOS"
    - ls -lA binaries/
    - mkdir -p tests
    - touch tests/macos-10
  needs:
    - build macos 10
  artifacts:
    paths:
      - tests/

test macos 11:
  stage: test
  script:
    - echo "Testing in macOS"
    - ls -lA binaries/
    - mkdir -p tests
    - touch tests/macos-11
  needs:
    - build macos 11
  artifacts:
    paths:
      - tests/

test debian 10:
  stage: test
  script:
    - echo "Testing in debian"
    - ls -lA binaries/
    - mkdir -p tests
    - touch tests/debian-10
  needs:
    - build debian 10
  artifacts:
    paths:
      - tests/

test debian 11:
  stage: test
  script:
    - echo "Testing in debian"
    - ls -lA binaries/
    - mkdir -p tests
    - touch tests/debian-11
  needs:
    - build debian 11
  artifacts:
    paths:
      - tests/

package macos:
  stage: package
  script:
    - echo "Packaging macOS"
    - ls -lA binaries/
    - mkdir -p packages
    - tar -czvf packages/macos.tar.gz binaries
  needs:
    - build macos 10
    - build macos 11
  artifacts:
    paths:
      - packages/

package debian:
  stage: package
  script:
    - echo "Packaging debian"
    - ls -lA binaries/
    - mkdir -p packages
    - tar -czvf packages/debian.tar.gz binaries
  needs:
    - build debian 10
    - build debian 11
  artifacts:
    paths:
      - packages/

upload:
  stage: upload
  script:
    - echo "Uploading generated binaries"
    - ls -lA binaries/
    - ls -lA packages/
    - ls -lA tests/

Rapports extrait d'artefacts

Certains type d'artefacts générés par différents outils peuvent être marqués comme étant des rapports au format bien identifiés, et ainsi être utilisés pour apporter plus d'informations à différents endroits de Gitlab (page de merge requests, section "Sécurité"...).

De nombreux types de rapports sont disponibles, mais la plus part pour les versions premium et ultimate.

[ref]

Rapports de tests

yaml
my job:
  artifacts:
    reports:
      junit: report.xml

Le rapport doit être au format JUnit. Il s'agit d'un format conçu à l'origine pour du Java, mais de nombreux framework de tests de différents langages le supportent.

Dans le cas des pipelines de merge request, le résultat du rapport est affiché au sein de la merge request.

!

Exemples :

yaml
golang:
  stage: test
  script:
    - go install gotest.tools/gotestsum@latest
    - gotestsum --junitfile report.xml --format testname
  artifacts:
    reports:
      junit: report.xml

python:
  stage: test
  script:
    - pytest --junitxml=report.xml
  artifacts:
    reports:
      junit: report.xml

[ref]

Rapport de couverture

yaml
my job:
  artifacts:
    reports:
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml

Le rapport doit être au format Cobertura. Il s'agit d'un format conçu à l'origine pour du Java, mais de nombreux framework de tests de différents langages le supportent.

Dans le cas des pipelines de merge request, le résultat du rapport est affiché au sein de la merge request.

! !

Exemples :

yaml
golang:
  stage: test
  script:
    - go install
    - go test ./... -coverprofile=coverage.txt -covermode count
    - go get github.com/boumenot/gocover-cobertura
    - go run github.com/boumenot/gocover-cobertura < coverage.txt > coverage.xml
  artifacts:
    reports:
      coverage_report:
        coverage_format: cobertura
        path: coverage.xml

python:
  stage: test
  script:
    - pip install pytest pytest-cov
    - pytest --cov --cov-report term --cov-report xml:coverage.xml
  artifacts:
    reports:
      coverage_report:
        coverage_format: cobertura
        path: coverage.xml

[ref]

Built using VitePress. Released under the MIT License.