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
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'
    

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

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.