1
votes

In Azure DevOps (YAML pipeline), we have a stages that should be run only after another set of stages have been skipped.

In the example below, the parameter copyStages_UAT can be amended by users when triggering a manual run, meaning it's impossible to hard-code the dependsOn and condition properties, so necessitating the use of the directive each.

- template: ../Stages/stage--code--depoly-to-environment.yml
  parameters:
    name: Deploy_PRD_UKS
    displayName: Deploy PRD - UK South
    dependsOn:
    - ${{ each uatStage in parameters.copyStages_UAT }}:
      - Roll_Back_${{ uatStage.name }}
    variables:
    - template: ../Variables/variables--code--global.yml
    - template: ../Variables/variables--code--prd.yml
    environment: PRD

This stage above works in a pipeline, however because a successful run results in stages defined in dependsOn being skipped, sadly then Azure DevOps will also skip this stage.

To counter this, I'm trying to add a condition to check whether or not the previous stages were all skipped.

condition: >-
  and(replace(
    ${{ each uatStage in parameters.copyStages_UAT }}:
      eq(dependencies.Roll_Back_${{ uatStage.name }}.result, 'Skipped'), 
  ), ', )', ' )')

Unfortunately though, it seems as though I cannot use the directive each in this context -

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.

As condition can only be a string, how can I leverage expressions and/or directives to construct my desired condition?

Example of desired YAML

Assuming the following value was given for the parameter copyStages_UAT -

- name: UAT_UKS
  displayName: UAT - UK South
- name: UAT_UKW
  displayName: UAT - UK West

This is how the YAML should be compiled. I'm not worried out the format of the condition, as long as the relevant checks are included.

- template: ../Stages/stage--code--depoly-to-environment.yml
  parameters:
    name: Deploy_PRD_UKS
    displayName: Deploy PRD - UK South
    dependsOn:
    - Roll_Back_UAT_UKS
    - Roll_Back_UAT_UKW
    condition: >-
      and(
        eq(dependencies.Roll_Back_UAT_UKS.result, 'Skipped'),
        eq(dependencies.Roll_Back_UAT_UKW.result, 'Skipped')
      )
    variables:
    - template: ../Variables/variables--code--global.yml
    - template: ../Variables/variables--code--prd.yml
    environment: PRD
1

1 Answers

0
votes

Updated:

To summary your demand, you are looking for a expression that you can use it in condition while the dependsOn value are dynamic. And this stage should run only after another set of dependent stages are all skipped.

As far as I know and tested, this can not be achieved via each.

For further confirmation, I discussed this scenario with our pipeline PM who is more familiar with each and YAML pipeline.

Same as me, he also think this isn't possible to achieve. Because ${{ each }} expects to be the outermost part of a mapping key or value, so you can’t nest it into a condition string.


Work around:

You could fake it by having a hard-coded stage which depends on the dynamic list, figures out if they were all skipped, and sets an output variable. Then the real final job would only depend on that “decider” job, and its condition would depend on the contents of the output variable.

Figuring out that all upstream dependencies were skipped is something of an exercise for you. You might be able to dynamically construct one step per stage. Those steps map the stage’s status to a variable with a known name scheme. Then the final (hardcoded) step iterates the environment variables of the known name scheme and decides whether the next stage should proceed.

And... yes, I am aware how ugly that sounds