Conditions des pipelines

Azure DevOps Services | Azure DevOps Server 2022 | Azure DevOps Server 2019

Cet article décrit les conditions dans lesquelles une étape, tâche ou segment Azure Pipelines s’exécute, et comment spécifier différentes conditions. Pour plus de contexte sur les étapes, tâches et segments, veuillez consulter la section Concepts clés pour Azure Pipelines.

  • Par défaut, une tâche ou une étape s’exécute si elle ne dépend d’aucune autre tâche ou étape, ou si toutes ses dépendances sont terminées et réussies. Cette exigence s’applique non seulement aux dépendances directes, mais aussi à leurs dépendances indirectes, calculées de manière récursive.

  • Par défaut, un segment s’exécute si aucune erreur n’a encore été rencontrée dans sa tâche et si le segment immédiatement précédent est terminé.

Vous pouvez remplacer ou personnaliser ce comportement en forçant une étape, une tâche ou un segment à s’exécuter, même si une dépendance précédente échoue, ou en spécifiant une condition personnalisée.

Remarque

Cet article aborde les fonctionnalités des pipelines YAML. Pour les pipelines classiques, vous pouvez spécifier certaines conditions dans lesquelles les tâches ou les étapes s’exécutent dans les Options de contrôle de chaque tâche, et dans les Options supplémentaires pour une tâche dans un pipeline de publication.

Conditions dans lesquelles une étape, tâche ou segment s’exécute

Dans la définition YAML du pipeline, vous pouvez spécifier les conditions suivantes pour qu’une étape, une tâche ou un segment s’exécute :

  • Uniquement lorsque toutes les dépendances directes et indirectes précédentes avec le même pool d'agents réussissent. Si vous disposez de différents pools d'agents, ces étapes ou travaux s'exécutent simultanément. Cette condition est la condition par défaut si aucune condition n'est définie dans le YAML.

  • Même si une dépendance précédente échoue, à moins que l'exécution ne soit annulée. Utilisez succeededOrFailed() dans le fichier YAML pour cette condition.

  • Même si une dépendance précédente échoue, et même si l'exécution est annulée. Utilisez always() dans le fichier YAML pour cette condition.

  • Uniquement en cas d'échec d'une dépendance précédente. Utilisez failed() dans le fichier YAML pour cette condition.

  • Conditions personnalisées.

Par défaut, les étapes, tâches et segments s’exécutent si toutes les dépendances directes et indirectes réussissent. Ce statut est équivalent à la spécification condition: succeeded(). Pour plus d’informations, consultez la section Fonction du statut succeeded.

Lorsque vous spécifiez une propriété condition pour une étape, une tâche ou un segment, vous remplacez le comportement par défaut condition: succeeded(). Spécifier vos propres conditions peut faire en sorte que votre étape, tâche ou segment s’exécute même si la build est annulée. Assurez-vous que les conditions que vous écrivez tiennent compte de l’état de l’étape ou de la tâche parente.

L’exemple YAML suivant montre les conditions always() et failed(). Le segment dans la première tâche s’exécute même si des dépendances échouent ou si la build est annulée. La deuxième tâche ne s’exécute que si la première tâche échoue.

jobs:
- job: Foo

  steps:
  - script: echo Hello!
    condition: always() # this step runs, even if the build is canceled

- job: Bar
  dependsOn: Foo
  condition: failed() # this job runs only if Foo fails

Vous pouvez également définir et utiliser des variables dans les conditions. L’exemple suivant définit et utilise une variable isMain pour désigner main comme Build.SourceBranch.

variables:
  isMain: $[eq(variables['Build.SourceBranch'], 'refs/heads/main')]

stages:
- stage: A
  jobs:
  - job: A1
    steps:
      - script: echo Hello Stage A!

- stage: B
  condition: and(succeeded(), eq(variables.isMain, true))
  jobs:
  - job: B1
    steps:
      - script: echo Hello Stage B!
      - script: echo $(isMain)

Important

Les conditions sont évaluées pour déterminer s’il faut démarrer une étape, une tâche ou un segment. Ainsi, rien de ce qui est calculé au moment de l’exécution à l’intérieur de cette unité de travail n’est disponible. Par exemple, si vous avez une tâche qui définit une variable à l’aide d’une expression de runtime avec la syntaxe $[ ], vous ne pouvez pas utiliser cette variable dans une condition personnalisée dans cette tâche.

Conditions personnalisées

Si les conditions intégrées ne répondent pas à vos besoins, vous pouvez spécifier des conditions personnalisées. Vous rédigez les conditions sous forme d’expressions dans les définitions de pipelines YAML.

L’agent évalue l’expression en commençant par la fonction la plus interne et en procédant vers l’extérieur. Le résultat final est une valeur booléenne qui détermine si la tâche, la tâche ou l’étape doit s’exécuter. Pour un guide complet de la syntaxe, veuillez consulter la section Expressions.

Si l’une de vos conditions permet que la tâche s’exécute même après l’annulation de la build, spécifiez une valeur raisonnable pour le délai d’annulation afin que ces tâches aient suffisamment de temps pour se terminer après l’annulation de l’exécution par l’utilisateur.

Résultats des conditions lorsque une build est annulée

Annuler une build ne signifie pas que toutes ses étapes, tâches ou segments s’arrêtent. Quelles étapes, tâches ou segments s’arrêtent dépendent des conditions que vous avez spécifiées, et du point d’exécution du pipeline où vous avez annulé la build. Si une étape, tâche ou segment parente est ignorée, la tâche ne s’exécute pas, quelle que soit sa condition.

Une étape, une tâche ou un segment s’exécute chaque fois que ses conditions évaluent à true. Si votre condition ne tient pas compte de l’état de la tâche parente, la tâche pourrait s’exécuter même si sa parente est annulée. Pour contrôler si les étapes, tâches ou segments avec des conditions s’exécutent lorsqu’une build est annulée, assurez-vous d’inclure une fonction de vérification du statut de la tâche dans vos conditions.

Les exemples suivants montrent les résultats de diverses conditions définies sur des étapes, tâches ou segments lorsque la build est annulée.

Exemple d’étape 1

Dans le pipeline suivant, par défaut stage2 dépendrait de stage1, mais stage2 a une condition définie pour s’exécuter chaque fois que la branche source est main, indépendamment du statut de stage1.

Si vous lancez une build sur la branche main et l’annulez pendant que stage1 est en cours d’exécution, stage2 s’exécute toujours, car eq(variables['Build.SourceBranch'], 'refs/heads/main') évalue à true.

stages:
- stage: stage1
  jobs:
  - job: A
    steps:
      - script: echo 1; sleep 30
- stage: stage2
  condition: eq(variables['Build.SourceBranch'], 'refs/heads/main')
  jobs:
  - job: B
    steps:
      - script: echo 2

Exemple d’étape 2

Dans le pipeline suivant, stage2 dépend de stage1 par défaut. La tâche B dans stage2 a une condition définie. Si vous lancez une build sur la branche main et l’annulez pendant que stage1 est en cours d’exécution, stage2 ne s’exécute pas, même si elle contient une tâche dont la condition évalue à true.

La raison est que stage2 a la valeur par défaut condition: succeeded(), qui prend la valeur false quand stage1 est annulé. Par conséquent, stage2 est ignoré et aucun de ses travaux ne s’exécute.

stages:
- stage: stage1
  jobs:
  - job: A
    steps:
      - script: echo 1; sleep 30
- stage: stage2
  jobs:
  - job: B
    condition: eq(variables['Build.SourceBranch'], 'refs/heads/main')
    steps:
      - script: echo 2

Exemple d’étape 3

Dans le pipeline suivant, par défaut stage2 dépend de stage1, et le segment à l’intérieur de la tâche B a une condition définie.

Si vous lancez une build sur la branche main et l’annulez pendant que stage1 est en cours d’exécution, stage2 ne s’exécute pas, même si elle contient un segment dans la tâche B dont la condition évalue à true. La raison est que stage2 est ignoré en réponse à l’annulation de stage1.

stages:
- stage: stage1
  jobs:
  - job: A
    steps:
      - script: echo 1; sleep 30
- stage: stage2
  jobs:
  - job: B
    steps:
      - script: echo 2
        condition: eq(variables['Build.SourceBranch'], 'refs/heads/main')

Exemple de tâche 1

Dans le pipeline YAML suivant, la tâche B dépend de la tâche A par défaut, mais la tâche B a une condition définie pour s’exécuter chaque fois que la branche source est main. Si vous lancez une build sur la branche main et l’annulez pendant que la tâche A est en cours d’exécution, la tâche B s’exécute toujours, car eq(variables['Build.SourceBranch'], 'refs/heads/main') évalue à true.

jobs:
- job: A
  steps:
  - script: sleep 30
- job: B
  dependsOn: A 
  condition: eq(variables['Build.SourceBranch'], 'refs/heads/main')
  steps:
    - script: echo step 2.1

Si vous voulez que la tâche B s’exécute uniquement lorsque la tâche A réussit et que la source de la build est la branche main, votre condition doit être and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main')).

Exemple de tâche 2

Dans le pipeline suivant, la tâche B dépend de la tâche A par défaut. Si vous lancez une build sur la branche main et l’annulez pendant que la tâche A est en cours d’exécution, la tâche B ne s’exécute pas, même si son segment a une condition qui évalue à true.

La raison est que le travail B a la valeur par défaut condition: succeeded(), qui prend la valeur false quand le travail A est annulé. Par conséquent, le travail B est ignoré et aucune de ses étapes ne s’exécute.

jobs:
- job: A
  steps:
  - script: sleep 30
- job: B
  dependsOn: A 
  steps:
    - script: echo step 2.1
      condition: eq(variables['Build.SourceBranch'], 'refs/heads/main', succeeded())
      

Exemple de segment

Vous pouvez également avoir des conditions sur des étapes.

Dans le pipeline suivant, le segment 2.3 a une condition définie pour s’exécuter chaque fois que la branche source est main. Si vous lancez une build sur la branche main et l’annulez pendant que les segments 2.1 ou 2.2 sont en cours d’exécution, le segment 2.3 s’exécute toujours, car eq(variables['Build.SourceBranch'], 'refs/heads/main') évalue à true.

steps:
  - script: echo step 2.1
  - script: echo step 2.2; sleep 30
  - script: echo step 2.3
    condition: eq(variables['Build.SourceBranch'], 'refs/heads/main')

Paramètres de conditions

Le tableau suivant montre des exemples de paramètres condition pour produire divers résultats.

Remarque

Release.Artifacts.{artifact-alias}.SourceBranch est équivalent à Build.SourceBranch.

Résultat souhaité Exemple de paramètre de condition
S’exécuter si la branche source est main, même si la tâche ou l’étape parent(e) ou précédente a échoué ou a été annulée. eq(variables['Build.SourceBranch'], 'refs/heads/main')
S’exécuter si la branche source est main et que la tâche ou l’étape parent(e) ou précédente a réussi. and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
S’exécuter si la branche source n’est pas main, et que la tâche ou l’étape parent(e) ou précédente a réussi. and(succeeded(), ne(variables['Build.SourceBranch'], 'refs/heads/main'))
S’exécuter pour les branches de sujet utilisateur, si la tâche ou l’étape parent(e) ou précédente a réussi. and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/heads/users/'))
S’exécuter pour les builds d’intégration continue (CI), si la tâche ou l’étape parent(e) ou précédente a réussi. and(succeeded(), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI'))
S’exécuter si la build a été déclenchée par une stratégie de branche pour une pull request, et que la tâche ou l’étape parent(e) ou précédente a échoué. and(failed(), eq(variables['Build.Reason'], 'PullRequest'))
S’exécuter pour une build planifiée, même si la tâche ou l’étape parent(e) ou précédente a échoué ou a été annulée. eq(variables['Build.Reason'], 'Schedule')
S’exécuter si une variable est définie sur true, même si la tâche ou l’étape parent(e) ou précédente a échoué ou a été annulée. eq(variables['System.debug'], true)

Remarque

Vous pouvez définir une condition pour s’exécuter si une variable est nulle (chaîne vide). Comme toutes les variables sont traitées comme des chaînes dans Azure Pipelines, une chaîne vide est équivalente à null dans le pipeline suivant :

variables:
- name: testEmpty
  value: ''

jobs:
  - job: A
    steps:
    - script: echo testEmpty is blank
    condition: eq(variables.testEmpty, '')

Paramètres dans les conditions

L’expansion des paramètres se produit avant que les conditions ne soient prises en compte. Ainsi, lorsque vous déclarez un paramètre dans le même pipeline qu’une condition, vous pouvez intégrer le paramètre dans la condition. Le script dans le YAML suivant s’exécute car parameters.doThing est vrai.

parameters:
- name: doThing
  default: true
  type: boolean

steps:
- script: echo I did a thing
  condition: and(succeeded(), ${{ eq(parameters.doThing, true) }})

Le condition dans le pipeline précédent combine deux fonctions : succeeded() et ${{ eq(parameters.doThing, true) }}. La fonction succeeded() vérifie si l’étape précédente a réussi. La fonction succeeded() renvoie true car il n’y avait pas d’étape précédente.

La fonction ${{ eq(parameters.doThing, true) }} vérifie si le paramètre doThing est égal à true. Comme la valeur par défaut pour doThing est true, la condition renvoie true par défaut sauf si le pipeline définit une valeur différente.

Paramètres de template dans les conditions

Lorsque vous transmettez un paramètre à un template, vous devez soit définir la valeur du paramètre dans votre template soit utiliser templateContext pour passer le paramètre au template.

Par exemple, le fichier parameters.yml suivant déclare le paramètre doThing et une valeur par défaut :

# parameters.yml
parameters:
- name: doThing
  default: true # value passed to the condition
  type: boolean

jobs:
  - job: B
    steps:
    - script: echo I did a thing
    condition: ${{ eq(parameters.doThing, true) }}

Le code du pipeline fait référence au template parameters.yml. La sortie du pipeline est I did a thing car le paramètre doThing est vrai.

# azure-pipeline.yml
parameters:
- name: doThing
  default: true 
  type: boolean

trigger:
- none

extends:
  template: parameters.yml

Pour plus d’exemples de paramètres de template, consultez la Référence d’utilisation des templates.

Variables de sortie de tâche utilisées dans les conditions de tâches suivantes

Vous pouvez rendre une variable disponible pour de futurs travaux et la spécifier dans une condition. Les variables disponibles pour les tâches futures doivent être marquées comme variables de sortie multi-tâches en utilisant isOutput=true, comme dans le code suivant :

jobs:
- job: Foo
  steps:
  - bash: |
      echo "This is job Foo."
      echo "##vso[task.setvariable variable=doThing;isOutput=true]Yes" #set variable doThing to Yes
    name: DetermineResult
- job: Bar
  dependsOn: Foo
  condition: eq(dependencies.Foo.outputs['DetermineResult.doThing'], 'Yes') #map doThing and check the value
  steps:
  - script: echo "Job Foo ran and doThing is Yes."

Variables créées dans un segment utilisé dans les conditions de segments suivants

Vous pouvez créer une variable disponible pour les segments futurs à spécifier dans une condition. Les variables créées à partir de segments sont disponibles pour les segments futurs par défaut et n’ont pas besoin d’être marquées comme variables de sortie multi-tâches.

Il y a des choses importantes à noter concernant l’étendue des variables créées à partir de segments.

  • Les variables créées dans un segment d’une tâche sont limitées aux segments de la même tâche.
  • Les variables créées dans un segment sont disponibles dans les segments suivants uniquement en tant que variables d’environnement.
  • Les variables créées dans une étape ne peuvent pas être utilisées dans l’étape qui les définit.

L’exemple suivant montre comment créer une variable de pipeline dans un segment et utiliser cette variable dans la condition et le script d’un segment suivant.

steps:

# This step creates a new pipeline variable: doThing. This variable is available to subsequent steps.
- bash: |
    echo "##vso[task.setvariable variable=doThing]Yes"
  displayName: Step 1

# This step is able to use doThing, so it uses doThing in its condition
- script: |
    # Access the variable from Step 1 as an environment variable.
    echo "Value of doThing (as DOTHING env var): $DOTHING."
  displayName: Step 2
  condition: and(succeeded(), eq(variables['doThing'], 'Yes')) # or and(succeeded(), eq(variables.doThing, 'Yes'))

Forum aux questions

Comment déclencher un travail si un travail précédent a réussi avec des problèmes ?

Vous pouvez utiliser le résultat de la tâche précédente dans une condition. Par exemple, dans le YAML suivant, la condition eq(dependencies.A.result,'SucceededWithIssues') permet à la tâche B de s’exécuter car la tâche A a réussi avec des problèmes.

jobs:
- job: A
  displayName: Job A
  continueOnError: true # next job starts even if this one fails
  steps:
  - script: echo Job A ran
  - script: exit 1

- job: B
  dependsOn: A
  condition: eq(dependencies.A.result,'SucceededWithIssues') # targets the result of the previous job 
  displayName: Job B
  steps:
  - script: echo Job B ran

J’ai annulé ma build, mais elle est toujours en cours d’exécution. Pourquoi ?

Vous pouvez rencontrer ce problème si une condition configurée dans une étape n’inclut pas une fonction de vérification du statut de la tâche. Pour résoudre le problème, ajoutez une fonction de vérification de l’état du travail à la condition.

Si vous annulez une tâche alors qu’elle est dans l’étape de mise en file d’attente mais non en cours d’exécution, toute la tâche est annulée, y compris toutes les autres étapes. Pour plus d’informations, consultez la section Résultats des conditions lorsque une build est annulée plus haut dans cet article.