Conditional dependent job in Azure Devops YAML pipelines
Asked Answered
H

5

26

I'm building a complex pipeline in yaml and I'm trying to create a dependency between two jobs such that the latter job runs after the former, but only if the former is set to run based on a parameter. I can't seem to wrap my head around whether this is doable or not.

I have a pipeline defined like this:

parameters:
- name: doJobA
  type: boolean

stages:
  jobs:
  - job: JobA
    condition: eq('${{ parameters.doJobA }}', true)
    # ... details removed for brevity

  - job: JobB
    dependsOn: JobA
    # ... details removed for brevity

JobB should run after JobA if parameters.doJobA is true, or immediately if parameters.doJobA is false. Simply adding the dependsOn condition causes JobB to be skipped if the JobA condition is not met which makes sense, but I'd like it to run regardless.

Is it possible to define a conditional dependsOn in this manner?

EDIT: I've run into an additional problem with this that renders the solution below unusable. I need the condition to depend upon a variable set by an earlier running PowerShell script and not based on parameters.

Hexangular answered 28/5, 2020 at 6:21 Comment(0)
T
46

Simpler solution from https://elanderson.net/2020/05/azure-devops-pipelines-depends-on-with-conditionals-in-yaml/

parameters:
- name: doJobA
  type: boolean

stages:
  jobs:
  - job: JobA
    condition: eq('${{ parameters.doJobA }}', true)
    # ... details removed for brevity

  - job: JobB
    dependsOn: JobA
    condition: in(dependencies.JobA.result, 'Succeeded', 'Skipped')
    # ... details removed for brevity
Tarry answered 21/9, 2020 at 13:11 Comment(1)
This works for me - but I get a weird behavior after implementing it - when I try to cancel the stage, JobB keeps running until completion. If I remove the condition, JobB will immediately cancel when the stage is cancelled.Augustine
R
4

Here is the code sample I came up with (you can see the example). Job 2 is always run, and is run after Job 1 if Job 1 runs.

updated YAML

- job: One
  condition: eq('${{ parameters.DoJobOne }}', true)
  pool:
    vmImage: 'windows-2019'
  steps:
  - powershell: |
      throw "simulate Job One failing"
      echo "##vso[task.setvariable variable=JobOneRan;isOutput=true]true"
    name: setvarStep
  - script: |
      echo $(setvarStep.JobOneRan)          
    name: echovariable


- job: Two
  condition: and(always(), eq('${{ parameters.DoJobOne }}', eq(dependencies.One.outputs['setvarStep.JobOneRan'], true)))
  dependsOn: One
  pool:
    vmImage: 'windows-2019'
  variables:
    myVariableFromJobOne: $[ dependencies.One.outputs['setvarStep.JobOneRan'] ]
  steps:
  - script: echo $(myVariableFromJobOne)
    name: echovariable

Hope that helps.

Wes

Rightly answered 29/5, 2020 at 21:51 Comment(5)
I don' think' this solves the original question of having Job 2 run only based on a condition of Job 1 runningGlovsky
Wouldn't this always run job 2 even if job 1 fails?Hexangular
Yes if Job 1 fails, Job 2 will run. My understanding of the requirement was this: "JobB should run after JobA if parameters.doJobA is true, or immediately if parameters.doJobA is false. "Rightly
Thanks for the update Wes. Although the answer requires each job to set a variable through some script I think it's quite elegant.Hexangular
When looking up this issue, I just saw there is a much simpler solution now: https://mcmap.net/q/516472/-conditional-dependent-job-in-azure-devops-yaml-pipelinesTarry
H
2

I've found a slightly inelegant solution. By combining expressions with boolean parameters I'm able to do what I need, but it is a bit tricky:

parameters:
- name: doJobA
  type: boolean

stages:
  jobs:
  - job: JobA
    condition: eq('${{ parameters.doJobA }}', true)
    # ... details removed for brevity

  - job: JobB
    ${{ if eq(parameters.doJobA, true) }}:
      dependsOn: JobA
      condition: succeeded()
    # ... details removed for brevity

Here I insert a dependent clause only if the parameter doJobA is true. Otherwise it is not present. In order to ensure that JobB only runs if JobA succeeds I must also add a condition, but only if dependsOn is present.

The result is that the job runs immediately if doJobA is false because the resulting yaml will not contain any dependsOn or condition entries, but in the other case it will depend on the successful execution of JobA.

I'm still hoping there is a better way to achieve this though as this seems a bit complex (imo).

Edit: This solution only works for static properties and not dynamic variables.

Hexangular answered 28/5, 2020 at 6:45 Comment(3)
Thanks for sharing your solution here, would you please accept your solution as the answer? So it would be helpful for other members who get the same issue to find the solution easily. Have a nice day:)Agentival
Unfortunately as I wrote in an edit on the initial question this does not solve my problem. This solution only works for static properties and not dynamically set variables which I need in order to get my template working. If you have any suggestions I'd greatly appreciate it!Hexangular
This approach worked successfully for me with the only change I made being to define an expression for when doJobA is falseKoloski
S
1

The following solution works if your JobA parameter is a string. (Also works for boolean parameters)

parameters:
- name: doJobA
  type: string

stages:
  jobs:
  - job: JobA
    condition: eq('${{ parameters.doJobA }}', 'string')
    # ... details removed for brevity

  - job: JobB
    dependsOn: JobA
    condition: or(
                  and(succeeded(), eq('${{ parameters. doJobA }}', 'yourstring')), 
                  and(always(), ne('${{ parameters. doJobA }}', 'yourstring'))
                )

    # ... details removed for brevity
Seignior answered 14/11, 2022 at 13:14 Comment(0)
W
0

I had a similar case when I had a singe pipeline working with various environments and for one of them I needed mandatory approval checks. The problem with approval checks is that they are implemented in the TFS 'Environment' object and triggered every time object is touched. Therefore this code didn't work:

jobs:
- deployment: approvalChecksJob
  condition: eq('${{ parameters.restoreEnvironment }}', 'production')
  environment:
    name: 'ApprovalRequirements'
  strategy:
    ...

- job: dbRestoreJob
  dependsOn: approvalChecksJob
  condition: in(dependencies.approvalChecksJob.result, 'Succeeded', 'Skipped')
  steps:
    ...

What I end up doing is making the whole job to be skipped based on a condition and then making 'dependsOn' also conditional:

jobs:
- ${{ if eq(parameters.restoreEnvironment, 'production') }}:
  - deployment: approvalChecksJob
    environment:
      name: 'ApprovalRequirements'
    strategy:
      ...

- job: dbRestoreJob
  ${{ if eq(parameters.restoreEnvironment, 'production') }}:
    dependsOn: approvalChecksJob
  steps:
    ...
Wojcik answered 4/7, 2024 at 7:2 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.