azure-devopsazure-pipelinesazure-pipelines-yaml

Azure pipeline - Dynamically rendered expression inside if expression


We have a frontend monorepo build pipeline and trying to implement a dependency between build jobs.

First thing we define is the libraries to be looped over:

parameters:
  - name: librariesToBePublished
    default:
      - my-awesome-package-1
      - my-awesome-package-2
      - my-awesome-package-3
    type: object

Then we have the dependencies among these libraries:

  - name: dependencies_my_awesome_package_2
    displayName: "Library dependencies of my-awesome-package-2"
    default:
      - my-awesome-package-1
    type: object

  - name: dependencies_my_awesome_package_3
    displayName: "Library dependencies of my-awesome-package-3"
    default:
      - my-awesome-package-1
      - my-awesome-package-2
    type: object

So, the my-awesome-package-2 should not be built and deployed before my-awesome-package-1 since it is depending on it. The job to build and publish that should wait until its dependency is built and published.

This is the job definition:

- stage: Publish
    displayName: "Publish Libraries"
    jobs:
      - ${{ each package in parameters.librariesToBePublished }}:
          - job: Build_${{replace(package, '-', '_')}}
            displayName: "Build ${{package}}"
            ${{ if parameters['${{ format('dependencies_{0}', replace(package, '-', '_')) }}'] }}:
              dependsOn:
                - ${{ each dep in parameters['${{ format('dependencies_{0}', replace(package, '-', '_')) }}'] }}:
                  - Build_${{replace(dep, '-', '_')}}
            condition: succeeded()

When we try to echo the expression $[parameters['${{format('libDependencies_{0}', replace(package, '-', '_') ) }}'] it works but not in if and each parts.

What we are missing here?


Solution

  • Instead of using a string array:

    parameters:
      - name: libraries
        type: object
        default:
          - lib1
          - lib2
          - lib3
    

    Consider using a complex object, where you specify the dependencies for each library:

    parameters:
      - name: libraries
        type: object
        default:
          - name: lib1
            displayName: 'Library 1'
            dependsOn: []
          - name: lib2
            displayName: 'Library 2'
            dependsOn:
              - lib1
          - name: lib3
            displayName: 'Library 3'
            dependsOn:
              - lib1
              - lib2
    

    Full working example:

    parameters:
      - name: libraries
        type: object
        default:
          - name: lib1
            displayName: 'Library 1'
            dependsOn: []
          - name: lib2
            displayName: 'Library 2'
            dependsOn:
              - lib1
          - name: lib3
            displayName: 'Library 3'
            dependsOn:
              - lib1
              - lib2
    
    trigger: none
    
    pool: Default
    
    jobs:
      - ${{ each library in parameters.libraries }}:
        - job: ${{ library.name }}
          displayName: ${{ library.displayName }}
          dependsOn: ${{ library.dependsOn }}
          steps:
            - checkout: none
            - script: echo ${{ library.name }}
    

    Advantages: