CI/CD & Ops

GitLab CI

Stages, runners, cache, artifacts, et le comparatif avec Azure DevOps.

Vérifié en septembre 2026 · outils aux versions citées dans le cours · environ 17 min

GitLab CI/CD est intégré à GitLab : un fichier .gitlab-ci.yml, à la racine du dépôt, décrit le pipeline, et GitLab en crée un à chaque push, puis confie chaque job à un runner. Deux faits commandent presque tout le reste. La configuration est résolue à la création du pipeline — fichiers inclus, règles, entrées — avant qu'un seul job tourne. Et chaque job s'exécute à part, le plus souvent dans un conteneur neuf, sans rien hériter des autres que ce qu'il télécharge : un cache ou des artifacts. Ce cours suit la documentation de GitLab 19.4, sorti en septembre 2026, et finit par une comparaison avec Azure DevOps.

Stages, jobs et needs

Toute clé de premier niveau qui n'est pas un mot-clé global (default, include, stages, variables, workflow) est un job ; celle dont le nom commence par un point est un job caché, jamais exécuté, et l'en-tête spec d'un fichier inclus vit à part, dans son propre document. Un job porte un script, une liste de commandes exécutées dans l'ordre : la première qui rend un code non nul fait échouer le job. stages donne l'ordre des stages ; les jobs d'un même stage tournent en parallèle, et le stage suivant ne démarre que si tous ont réussi. Un job sans stage va dans test. default fixe des valeurs que chaque job reçoit s'il ne les définit pas lui-même, sans fusion : un job qui écrit sa propre image ignore celle de default. Écrire image ou cache à la racine, hors de default, est déprécié.

# .gitlab-ci.yml, a la racine du depot
stages:                  # l'ordre des stages ; sans cette cle : .pre, build, test, deploy, .post
  - build
  - test

default:                 # recopie dans chaque job qui ne definit pas deja la cle
  image: mcr.microsoft.com/dotnet/sdk:10.0

compiler:                # une cle racine qui n'est pas un mot-cle global est un job
  stage: build
  script:                # la premiere commande qui echoue arrete le job, en echec
    - dotnet restore Boutique.slnx
    - dotnet build Boutique.slnx -c Release --no-restore

tester-api:
  stage: test            # attend que tous les jobs du stage build aient reussi
  script:
    - dotnet test Boutique.slnx -c Release

tester-front:            # meme stage que tester-api : les deux tournent en parallele
  stage: test
  image: node:24         # remplace l'image de default pour ce seul job
  script:
    - cd web
    - npm ci
    - npx ng test --watch=false

Les stages sont une barrière grossière : le test de l'API attend la compilation du front. needs remplace cette barrière par des dépendances explicites, qui forment un graphe orienté sans cycle : un job démarre dès que les jobs qu'il cite ont réussi, même s'ils sont dans son propre stage, et needs: [] le fait partir à la création du pipeline. Un job en cite au plus cinquante, limite par défaut.

stages:
  - build
  - test
  - deploy

api:construire:
  stage: build
  script:
    - ./scripts/construire-api.sh

front:construire:
  stage: build
  script:
    - ./scripts/construire-front.sh

api:tester:
  stage: test
  needs: ["api:construire"]     # demarre des que api:construire a fini, sans attendre le front
  script:
    - ./scripts/tester-api.sh

front:tester:
  stage: test
  needs: ["front:construire"]
  script:
    - ./scripts/tester-front.sh

verifier-format:
  stage: test
  needs: []                     # aucune dependance : demarre des la creation du pipeline
  script:
    - ./scripts/verifier-format.sh

deployer:
  stage: deploy                 # sans needs : attend la fin de tous les stages precedents
  script:
    - ./scripts/deployer.sh

rules plutôt que only et except

rules décide, à la création du pipeline, si un job y figure. Les règles se lisent dans l'ordre et la première qui correspond l'emporte ; si aucune ne correspond, le job n'est pas ajouté. Une règle teste une expression (if), des fichiers modifiés (changes) ou présents (exists), et peut changer le when du job : on_success par défaut, manual, delayed, always, ou never pour l'exclure. Comme elles sont évaluées avant tout job, elles ne voient que les variables connues à ce moment : une variable qu'un script définit n'y existe pas.

GitLab distingue les pipelines de branche, créés par défaut à chaque push, et les pipelines de merge request, qu'il ne crée que si un job ou workflow le demande. Un job qui accepte les deux sources produit le piège classique.

tester:
  script:
    - dotnet test Boutique.slnx
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_PIPELINE_SOURCE == "push"

# Un push sur une branche qui a une merge request ouverte cree deux pipelines :
# un pipeline de branche (source push) et un pipeline de merge request.
# Les tests tournent deux fois.

workflow: rules tranche une fois pour tout le pipeline : pipeline de merge request quand elle existe, pipeline de branche sinon — y compris planifié ou lancé par l'API, tant qu'aucune merge request n'est ouverte —, et un pipeline par étiquette. Les jobs n'ont plus qu'à dire quand ils servent, et le déploiement, dans le stage deploy, attend les tests.

workflow:                       # decide si le pipeline entier est cree
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
      when: never                 # une merge request est ouverte : son pipeline suffit
    - if: $CI_COMMIT_BRANCH       # sinon, un pipeline de branche
    - if: $CI_COMMIT_TAG          # et un pipeline pour chaque etiquette

tester:                           # sans rules : present dans tout pipeline que workflow laisse passer
  stage: test
  script:
    - dotnet test Boutique.slnx

deployer:
  stage: deploy                   # apres le stage test : jamais sans tests reussis
  script:
    - ./scripts/deployer.sh
  rules:                          # la premiere regle vraie decide ; aucune vraie : pas de job
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
    - if: $CI_COMMIT_TAG
      when: manual

only et except sont dépréciés, et la documentation renvoie à rules sans donner de motif. Le piège pratique tient à leur défaut implicite : un job qui n'a ni rules ni workflow: rules reçoit only: [branches, tags], donc ne tourne jamais dans un pipeline de merge request. Mélanger dans un même pipeline des jobs à only et des jobs à rules peut ainsi créer des pipelines en double, et la documentation prévient que ces défauts différents donnent des problèmes difficiles à diagnostiquer. rules dit tout explicitement, et la migration est mécanique.

# Deprecie : only
publier:
  script:
    - ./scripts/publier.sh
  only:
    - main          # la branche main
    - schedules     # et les pipelines planifies
# Le meme job ecrit avec rules. Les deux ne se melangent pas dans un job :
# "key may not be used with `rules`" refuse la configuration.
publier:
  script:
    - ./scripts/publier.sh
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
    - if: $CI_PIPELINE_SOURCE == "schedule"

Runners et exécuteurs

Un runner est une machine où tourne l'application GitLab Runner, enregistrée auprès de l'instance ; il prend dans la file d'attente les jobs qui lui correspondent. Selon sa portée, c'est un runner d'instance, offert à tous les projets, de groupe, ou de projet ; « partagé » et « spécifique » sont les anciens noms des premier et dernier. Sur GitLab.com, les runners hébergés sont des runners d'instance : chaque job y reçoit une machine virtuelle éphémère, supprimée juste après, et un job sans tags part sur saas-linux-small-amd64, avec ruby:3.1 comme image si le job n'en donne pas. Un runner auto-hébergé se justifie pour atteindre un réseau privé, pour du matériel particulier, ou pour garder des caches chauds.

L'exécuteur dit comment le runner exécute le job. Docker lance le job dans un conteneur de son image ; Kubernetes, un pod ; Docker Autoscaler et Instance créent des machines à la demande. Shell, SSH, VirtualBox, Parallels et Custom sont en maintenance, sans nouvelles fonctions, et Docker Machine est déprécié. L'exécuteur Shell ne nettoie rien entre deux jobs et ne protège pas le système de fichiers du runner : un job peut y lire le jeton du runner et le code des autres jobs. Les tags choisissent le runner : il doit porter toutes les étiquettes du job, et un job sans étiquette reste en attente si aucun runner n'accepte les jobs non étiquetés.

tester-api:
  image: mcr.microsoft.com/dotnet/sdk:10.0   # le conteneur du job (executeur Docker ou Kubernetes)
  tags:
    - saas-linux-medium-amd64      # GitLab.com : runner heberge de 4 vCPU et 16 Go
  script:
    - dotnet test Boutique.slnx -c Release

tests-integration:
  tags:                            # le runner doit porter toutes ces etiquettes
    - boutique
    - reseau-interne
  script:
    - ./scripts/tests-integration.sh   # atteint une base qui n'est pas exposee

Cache contre artifacts

Les deux mécanismes copient des fichiers d'un job à l'autre, et ne promettent pas la même chose. Le cache sert aux dépendances téléchargées. Il est stocké par le runner, ou dans un stockage objet si le cache distribué est configuré, et partagé par clé entre jobs et entre pipelines ; par défaut, les pipelines des branches protégées ont le leur, suffixé -protected. La documentation le dit sans détour : c'est une optimisation, qui n'est pas garantie. Les artifacts sont la sortie d'un job : envoyés à GitLab à la fin du job, rattachés à son pipeline, et téléchargés automatiquement par les jobs des stages suivants, ou seulement depuis les jobs cités dans needs. Ils expirent après expire_in, trente jours par défaut sauf réglage de l'instance.

Passer une publication par le cache marche sur un seul runner, puis cesse de marcher.

stages:
  - build
  - deploy

api:construire:
  stage: build
  image: mcr.microsoft.com/dotnet/sdk:10.0
  script:
    - dotnet publish src/Boutique.Api/Boutique.Api.csproj -c Release -o publie/api
  cache:
    key: api-publiee
    paths:
      - publie/api/

deployer:recette:
  stage: deploy
  image: alpine:3.24
  cache:
    key: api-publiee
    paths:
      - publie/api/
    policy: pull
  script:
    # Rien ne garantit que publie/api soit la : un autre runner, sans cache distribue,
    # ne l'a pas. Et avec un cache partage, la cle api-publiee est la meme pour tous
    # les pipelines : c'est peut-etre la publication d'un autre commit.
    - ./scripts/deployer.sh recette publie/api

La sortie voyage en artifact ; le cache ne garde que les paquets, qu'un job sait toujours retélécharger. Les deux n'acceptent que des chemins sous le répertoire du projet, d'où NUGET_PACKAGES, que NuGet lit pour situer ses paquets. Une clé calculée par key: files ne change qu'avec le fichier qui décide des versions.

stages:
  - build
  - deploy

variables:
  NUGET_PACKAGES: $CI_PROJECT_DIR/.nuget/packages   # le cache ne lit que sous le projet

api:construire:
  stage: build
  image: mcr.microsoft.com/dotnet/sdk:10.0
  cache:                          # les paquets NuGet : un gain de temps, jamais une necessite
    key:
      files:
        - Directory.Packages.props  # gestion centralisee des paquets ; sans ce fichier : nuget-default
      prefix: nuget
    paths:
      - .nuget/packages/
  script:
    - dotnet publish src/Boutique.Api/Boutique.Api.csproj -c Release -o publie/api
  artifacts:                      # la sortie du job : envoyee a GitLab, rattachee a ce pipeline
    paths:
      - publie/api/
    expire_in: 30 days

deployer:recette:
  stage: deploy
  image: alpine:3.24
  needs:
    - api:construire              # telecharge les artifacts de ce job, dans ce pipeline
  script:
    - ./scripts/deployer.sh recette publie/api

Une forme particulière d'artifact, artifacts: reports, est lue par GitLab lui-même : junit affiche les résultats de test dans la merge request, et dotenv transmet des variables aux jobs suivants. Le rapport junit attend du JUnit XML ; pour .NET, la documentation de GitLab le fait produire à dotnet test par un logger, celui du paquet JunitXml.TestLogger, comme le fait le job api:tester du pipeline complet, plus bas.

Variables et secrets

Une variable se définit dans le fichier, à la racine ou sur un job, et le job l'emporte. Toutes deviennent des variables d'environnement du job, lues avec la syntaxe du shell : $NOM en sh ou bash, $env:NOM en PowerShell. GitLab en prédéfinit des dizaines, CI_COMMIT_BRANCH, CI_PIPELINE_SOURCE, CI_PROJECT_DIR. Les variables du fichier sont réservées à la configuration non sensible : quiconque lit le dépôt les lit.

variables:                        # racine : valeur par defaut de chaque job
  CONFIGURATION: Release
  DOTNET_NOLOGO: "true"           # une valeur de variable est toujours une chaine

api:tester:
  image: mcr.microsoft.com/dotnet/sdk:10.0
  variables:
    CONFIGURATION: Debug          # le job l'emporte sur la racine
  script:
    # Variables predefinies et variables du fichier, lues avec la syntaxe du shell :
    # bash si l'image l'a, comme celle-ci, sinon sh
    - echo "Branche $CI_COMMIT_REF_NAME, commit $CI_COMMIT_SHORT_SHA"
    - dotnet test Boutique.slnx -c "$CONFIGURATION"

Un secret écrit dans le fichier est dans l'historique Git pour toujours, et un echo le recopie dans le journal.

variables:
  JETON_DEPLOIEMENT: "exemple-3f9c2a71d8e4"   # en clair dans le depot, pour quiconque le lit

deployer:production:
  environment: production
  script:
    - echo "Deploiement avec $JETON_DEPLOIEMENT"   # et en clair dans le journal du job
    - ./scripts/deployer.sh production

Il se déclare dans les paramètres CI/CD du projet ou du groupe. Masquée, sa valeur devient [MASKED] dans les journaux, à certaines conditions, dont tenir sur une ligne, sans espace, en huit caractères au moins ; « Masked and hidden » la rend en plus illisible dans l'interface, et ne se choisit qu'à la création. Protégée, elle n'existe que dans les pipelines des branches et tags protégés, et, si le projet l'autorise, dans les pipelines de merge request entre deux branches protégées du projet, jamais depuis un fork : ailleurs, le script lit une chaîne vide. Une portée d'environment la réserve aux jobs qui déploient vers cet environment. Une variable de projet l'emporte aussi sur celle du fichier du même nom. Le masquage n'est pas une protection contre un script malveillant, prévient la documentation : c'est la revue des modifications du .gitlab-ci.yml qui en protège.

# JETON_DEPLOIEMENT est cree dans Settings > CI/CD > Variables :
#   Visibility : Masked and hidden   -> [MASKED] dans le journal, illisible dans l'interface
#   Protect variable                 -> seulement pour les branches et tags proteges
#   Environment scope : production   -> seulement pour les jobs de cet environment
deployer:production:
  environment: production
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH   # main est une branche protegee : la variable existe
  script:
    - ./scripts/deployer.sh production   # le script lit $JETON_DEPLOIEMENT dans son environnement

include, extends et inputs

include assemble la configuration à partir de plusieurs fichiers : du même dépôt (local), d'un autre projet (project, avec ref), d'une URL (remote), des modèles fournis par GitLab (template) ou d'un composant du catalogue CI/CD (component). Les fichiers inclus sont lus en premier puis fusionnés, et le .gitlab-ci.yml l'emporte. Inclure le fichier d'un autre projet revient à prendre une dépendance : sa modification ne déclenche rien, d'où une étiquette protégée ou un SHA complet dans ref.

# .gitlab-ci.yml
include:
  - local: /ci/front.yml                   # meme depot, meme branche
  - project: boutique/modeles-ci           # un autre projet de l'instance
    ref: v2.1.0                            # une etiquette ; un SHA complet est plus sur encore
    file: /dotnet.yml
  - component: $CI_SERVER_FQDN/boutique/composants/[email protected]
    inputs:
      stage: test
      seuil: critical

# Un job de meme nom que dans un fichier inclus fusionne avec lui,
# et c'est ce fichier-ci qui l'emporte.

Un composant est un fichier templates/nom.yml d'un projet, ou un dossier templates/nom/ avec son template.yml, référencé par une étiquette, un SHA ou une branche. Son en-tête spec: inputs, séparé du reste par ---, déclare des entrées typées, avec valeur par défaut, liste de valeurs ou expression régulière ; $[[ inputs.nom ]] les remplace à la création du pipeline.

# templates/audit-npm.yml, dans le projet boutique/composants
spec:
  inputs:
    stage:
      default: test
    seuil:
      options: [low, moderate, high, critical]   # toute autre valeur fait echouer la creation
      default: high
---
audit-npm:
  stage: $[[ inputs.stage ]]         # remplace a la creation du pipeline, avant toute fusion
  image: node:24
  script:
    - cd web
    - npm audit --audit-level=$[[ inputs.seuil ]]

Le cours YAML a montré les ancres et la clé de fusion, que GitLab accepte à l'intérieur d'un seul fichier. extends traverse les fichiers inclus : un job hérite d'un autre, souvent d'un job caché dont le nom commence par un point, sur onze niveaux au plus. La fusion est profonde pour les correspondances, mais un tableau hérité est remplacé, pas complété.

.dotnet:                               # job cache : un point devant le nom, jamais execute
  image: mcr.microsoft.com/dotnet/sdk:10.0
  variables:
    NUGET_PACKAGES: $CI_PROJECT_DIR/.nuget/packages
  before_script:
    - dotnet restore Boutique.slnx

api:tester:
  extends: .dotnet
  variables:
    CONFIGURATION: Debug                 # correspondance : fusionnee, NUGET_PACKAGES reste
  before_script:
    - dotnet --info                      # tableau : remplace, dotnet restore a disparu
  script:
    - dotnet test Boutique.slnx -c $CONFIGURATION --no-restore   # echoue : rien n'est restaure

L'étiquette !reference recopie un fragment d'un autre job à l'endroit voulu, y compris depuis un fichier inclus : c'est elle qui compose un tableau.

.dotnet:
  image: mcr.microsoft.com/dotnet/sdk:10.0
  variables:
    NUGET_PACKAGES: $CI_PROJECT_DIR/.nuget/packages
  before_script:
    - dotnet restore Boutique.slnx

api:tester:
  extends: .dotnet
  variables:
    CONFIGURATION: Debug
  before_script:
    - !reference [.dotnet, before_script]   # insere les commandes de .dotnet, meme d'un fichier inclus
    - dotnet --info
  script:
    - dotnet test Boutique.slnx -c $CONFIGURATION --no-restore

Environments et pipeline complet

Un job qui porte environment est un déploiement : GitLab garde l'historique de ce qui a été déployé où, et crée l'environment s'il n'existe pas. url l'ouvre depuis la merge request, on_stop et action: stop le démontent, auto_stop_in l'arrête passé un délai. Un nom construit sur une variable donne un environment par branche, une « review app ».

deployer:revue:
  stage: deploy
  script:
    - ./scripts/deployer.sh "$CI_ENVIRONMENT_SLUG"   # le meme nom que dans l'url
  environment:
    name: revue/$CI_COMMIT_REF_SLUG                  # un environment par branche, cree a la volee
    url: https://$CI_ENVIRONMENT_SLUG.boutique.example   # slug : nom tronque a 24 caracteres
    on_stop: arreter:revue
    auto_stop_in: 3 days                             # arrete s'il n'est pas redeploye entre-temps
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

arreter:revue:
  stage: deploy
  script:
    - ./scripts/supprimer.sh "$CI_ENVIRONMENT_SLUG"
  environment:
    name: revue/$CI_COMMIT_REF_SLUG
    action: stop                    # lance aussi quand la merge request est fusionnee ou fermee
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
      when: manual
      allow_failure: true           # ne bloque ni le pipeline ni la merge request

Pour un déploiement manuel, when: manual écrit dans rules rend le job bloquant : le pipeline s'arrête là, au statut blocked, alors que le même when écrit hors de rules crée un job facultatif. resource_group empêche deux pipelines de déployer en même temps au même endroit. Restreindre qui peut déployer se fait hors du YAML, par les environments protégés, et les approbations de déploiement s'y ajoutent ; les deux exigent l'offre Premium ou Ultimate.

Le pipeline suivant lance ensemble quatre jobs sans dépendance entre eux — construire et tester, pour l'API ASP.NET Core 10 et pour le front Angular —, qui convergent vers la recette, déployée à chaque push sur main ; la production suit sur un clic. Une merge request construit et teste sans rien déployer, car CI_COMMIT_BRANCH n'existe pas dans son pipeline.

# .gitlab-ci.yml : l'API .NET 10 (src/, tests/) et le front Angular (web/)
workflow:
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
      when: never
    - if: $CI_COMMIT_BRANCH

stages:
  - build
  - test
  - deploy

variables:
  CONFIGURATION: Release
  NUGET_PACKAGES: $CI_PROJECT_DIR/.nuget/packages

.dotnet:
  image: mcr.microsoft.com/dotnet/sdk:10.0
  cache:
    key:
      files:
        - Directory.Packages.props   # gestion centralisee des paquets
      prefix: nuget
    paths:
      - .nuget/packages/
  before_script:
    - dotnet restore Boutique.slnx

.front:
  image: node:24                   # @angular/core 22.1 : ^22.22.3 || ^24.15.0 || >=26.0.0
  cache:
    key:
      files:
        - web/package-lock.json
      prefix: npm
    paths:
      - web/.npm/
  before_script:
    - cd web
    - npm ci --cache .npm --prefer-offline

api:construire:
  extends: .dotnet
  stage: build
  script:
    - dotnet publish src/Boutique.Api/Boutique.Api.csproj -c $CONFIGURATION --no-restore -o publie/api
  artifacts:
    paths:
      - publie/api/
    expire_in: 30 days             # revenir a un ancien deploiement relance son job, qui en a besoin

api:tester:
  extends: .dotnet
  stage: test
  needs: []                        # n'utilise aucun artifact : demarre tout de suite
  script:
    # Les projets de test referencent le paquet JunitXml.TestLogger (8.0.0 en septembre 2026)
    - >-
      dotnet test Boutique.slnx -c $CONFIGURATION --no-restore
      --logger "junit;LogFilePath=$CI_PROJECT_DIR/resultats/{assembly}.xml;MethodFormat=Class"
  artifacts:
    when: always                   # les resultats d'un echec sont les plus utiles
    reports:
      junit: resultats/*.xml       # affiches dans la merge request

front:construire:
  extends: .front
  stage: build
  script:
    - npm run build
  artifacts:
    paths:
      - web/dist/web/browser/
    expire_in: 30 days

front:tester:
  extends: .front
  stage: test
  needs: []
  script:
    - npx ng test --watch=false

.deployer:
  stage: deploy
  image: alpine:3.24
  before_script:
    - apk add --no-cache rsync openssh-client-default   # absents de l'image alpine
  script:
    # Le script du depot, en sh POSIX (alpine n'a pas bash), copie les deux dossiers vers la
    # cible par rsync sur SSH. La cle vient de CLE_DEPLOIEMENT : variable de type File (le job
    # recoit le chemin d'un fichier), protegee, definie une fois par environment. Une cle privee
    # tient sur plusieurs lignes : elle ne peut pas etre masquee.
    - ./scripts/deployer.sh "$CI_ENVIRONMENT_NAME" publie/api web/dist/web/browser

deployer:recette:
  extends: .deployer
  needs: ["api:construire", "api:tester", "front:construire", "front:tester"]
  environment:
    name: recette
    url: https://recette.boutique.example
  resource_group: recette          # deux pipelines ne deploient jamais en meme temps
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

deployer:production:
  extends: .deployer
  needs:
    - api:construire               # sans eux, aucun artifact : needs ne telecharge que ses jobs
    - front:construire
    - job: deployer:recette
      artifacts: false
  environment:
    name: production
    url: https://boutique.example
  resource_group: production
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
      when: manual                 # bloquant : le pipeline reste "blocked" jusqu'au clic

Le cours Docker emballe les mêmes applications en images ; ce pipeline, lui, livre des fichiers. Il n'a pas été exécuté : ses clés suivent la référence de docs.gitlab.com pour GitLab 19.4. Un parseur YAML ne voit pas une clé mal orthographiée ; l'éditeur de pipeline de GitLab valide le fichier, simule sa création sur la branche par défaut et affiche la configuration complète, fichiers inclus compris.

GitLab CI et Azure Pipelines

Les deux outils rangent des jobs en stages exécutés dans l'ordre, et leurs jobs ne partagent aucun fichier. Deux différences changent la façon d'écrire. Azure Pipelines lit une variable par trois syntaxes, qui ne sont pas évaluées au même moment ; GitLab n'a dans son YAML qu'une forme de référence, $NOM, développée selon le mot-clé par GitLab avant l'envoi du job, par le runner, ou par le shell du script, qui applique alors sa propre syntaxe, et y ajoute $[[ inputs.nom ]], remplacé à la création du pipeline. Et les secrets s'y comportent à l'inverse : Azure n'exporte pas un secret dans l'environnement d'un script sans env, GitLab exporte toute variable accessible au job, masquée ou non.

Le reste tient à l'outillage et au contrôle. Azure Pipelines installe ses outils par des tâches versionnées, comme UseDotNet@2 ; GitLab les prend dans l'image du job, et partage des morceaux de pipeline par des composants. Les deux sortent les approbations du YAML, mais Azure les pose sur une ressource, qu'il s'agisse d'un environment, d'une service connection ou d'un groupe de variables, alors que GitLab les rattache aux environments protégés, et seulement dans ses offres payantes.

QuestionGitLab CIAzure Pipelines
Unité d'exécutionjob et ses lignes de scriptjob et ses steps : scripts ou tâches Nom@version
Ordrestages, puis needsordre des stages, puis dependsOn
Machinerunner, choisi par tags ; outils apportés par l'image agent d'un pool, demands ; outils apportés par des tâches
Déclencheurs push, merge request, planification, API, filtrés par workflow: rulestrigger, pr, stratégie de branche
Condition d'un jobrules, à la création du pipelinecondition au démarrage, ${{ if }} à la compilation
Réutilisationinclude, extends, composants, ancres dans un fichiertemplates à paramètres typés, extends, pas d'ancres
Fichiers d'un job à l'autreartifacts, téléchargés d'office par les stages suivantspublish et download explicites, sauf en déploiement
Cachemot-clé cachetâche Cache@2
Valeur calculée transmiserapport dotenv d'un jobvariable de sortie, isOutput=true
Qui peut déployerenvironments protégés, approbations (Premium), hors YAMLapprobations et vérifications sur la ressource, hors YAML
Un déploiement à la foisresource_groupverrou exclusif, lockBehavior
Ce cours vous a servi ? Offrir un café Signaler une erreur