1

I have a job in azure-pipelines that receives a variable number of dependencies. It could have zero dependencies, or "n" dependencies depending what previous jobs were executed before this one.

Let's see an example:

file: "dependent-job.yml"

parameters:
- name: Dependencies
 type: Array
 default:
 - Job1
 - Job2
Jobs:
 - job: Variousdependencies
 name: MyJob
 ${{ if parameters.Dependencies }} 
 dependsOn: ${{ parameters.Dependencies }}
 condition: |
 and(eq(dependencies.Job1.result, 'Succeeded'),
 eq(dependencies.Job2.result, 'Succeeded'),
 eq(variables[MyJobEnabled,'True'),
 eq('${{ parameters.ForceJob }}','True')
 )

If I have 3 dependencies, I need to add another dependency in condition:

 condition: |
 and(eq(dependencies.Job1.result, 'Succeeded'),
 eq(dependencies.Job2.result, 'Succeeded'),
 eq(dependencies.Job3.result, 'Succeeded'),
 eq(variables[MyJobEnabled,'True'),
 eq('${{ parameters.ForceJob }}','True')
 )

I would like to generate the lines that check if the previous jobs ended sucessfuly using the each syntax. This is what I tried:

 condition: |
 and(${{ each job in dependencies }}
 eq(dependencies.{{ job }}.result, 'Succeeded'),
 eq(variables[MyJobEnabled,'True'),
 eq('${{ parameters.ForceJob }}','True')
 )

but I received the following error when trying to run it:

The directive 'each' is not allowed in this context. Directives are not supported for expressions that are embedded within a string. Directives are only supported when the entire value is an expression.

2 Answers 2

0

Consider adding a condition property to the Dependencies parameter and use filtered arrays to dynamically set the dependencies and conditions of the job.

Sample pipeline:

pool:
 vmImage: ubuntu-latest
trigger: none
parameters:
 - name: ForceJob
 type: boolean
 default: true
 - name: Dependencies
 type: object
 default: 
 - jobName: Job1
 condition: eq(dependencies.Job1.result, 'Succeeded')
 exitCode: 0 # Change to 1 to simulate a failed job
 - jobName: Job2
 condition: eq(dependencies.Job2.result, 'Succeeded')
 exitCode: 0 # Change to 1 to simulate a failed job
variables:
 - name: MyJobEnabled
 value: True
jobs:
 - ${{ each dependency in parameters.Dependencies }}:
 - job: ${{ dependency.jobName }}
 displayName: ${{ dependency.jobName }}
 steps:
 - checkout: none
 - script: |
 echo "Job: ${{ dependency.jobName }}"
 exit ${{ dependency.exitCode }}
 displayName: 'Display job name'
 - job: AnotherJob
 dependsOn: ${{ parameters.Dependencies.*.jobName }}
 condition: |
 and(
 ${{ coalesce(join(', ', parameters.Dependencies.*.condition), 'True') }},
 eq(variables['MyJobEnabled'], 'True'),
 eq('${{ parameters.ForceJob }}', 'True')
 )
 steps:
 - checkout: none
 - script: |
 echo "conditions: ${{ join(', ', parameters.Dependencies.*.condition) }}"
 displayName: 'Display conditions'

Notes:

  • coalesce(..., 'True') will generate a dummy condition in case Dependencies parameter is an empty array
  • join(', ', parameters.Dependencies.*.condition) will generate eq(dependencies.Job1.result, 'Succeeded'), eq(dependencies.Job2.result, 'Succeeded') for the conditions set in the sample pipeline
answered Jan 9, 2025 at 16:52
Sign up to request clarification or add additional context in comments.

Comments

0

Azure DevOps compile-time expressions do not support nested expressions in a single scalar value.

There is a way to accomplish what you want:

  1. Create a template for your job that accepts the conditions as an array of strings (object). In that template, you concatenate the strings together to assemble a single scalar value.

    # template - job with dynamic conditions
    parameters:
    - name: name
     type: string
    - name: displayName
     type: string
     default: ''
    - name: dependsOn
     type: object
     default: []
    - name: condition
     type: object
     default: []
    - name: steps
     type: stepList
    jobs:
    - job: ${{ parameters.name }}
     displayName: ${{ coalesce( parameters.displayName, parameters.name ) }}
     ${{ if ne(length(parameters.condition), 0) }}:
     condition: ${{ join( '', parameters.condition) }}
     ${{ if ne(length(parameters.dependsOn), 0) }}:
     dependsOn: ${{ parameters.dependsOn }}
     steps: 
     - ${{ parameters.steps }}
    
  2. In the calling template, dynamically construct the array of condition statements. It looks really weird, and you need to include the , statements as they would normally appear in the condition.

    # pipeline
    parameters:
    - name: dependencies
     type: object
     default:
     - job1
     - job2
    - name: forceJob
     type: boolean
     default: false
    jobs:
    - job: job1
     steps:
     - script: echo 'job1'
    - job: job2
     steps:
     - script: echo 'job2' 
    - template: job-with-dynamic-conditions.yml
     parameters:
     name: variousDependences
     dependsOn: ${{ parameters.dependencies }}
     condition: 
     - and(
     - ${{ each job in parameters.dependencies }}:
     - eq(dependencies.${{ job }}.result, 'Succeeded'),
     - eq(variables['jobEnabled'], 'true'),
     - eq( '${{ parameters.forceJob }}', 'true')
     - )
     steps:
     - script: echo 'steps go here'
    
answered Jan 9, 2025 at 16:09

2 Comments

I tried this approach, but got "A sequence was not expected" in the condition.
'sequence was not expected' occurs when you attempt to create a sequence in a scalar value. The parameters.condition is a parameter of type object which supports sequences. Take a closer look.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.