Azure pipeline stage that depends on mutually exclusive stages
Azure DevOps YAML pipelines provide flexibility for creating complex build and deployment flows. They allow you to store these pipelines as Pipeline as Code (PaC) within the codebase, which improves version control and collaboration.
In this article, we will discuss how to sequence stages in YAML pipelines using the "dependsOn
" property. This property allows you to define dependencies between stages, enabling different deployment paths based on these dependencies. You can easily create the following types of flows:
- Parallel deployments
- Sequential deployments
- Parallel deployments where all parallel stages must succeed to proceed to the next stage
- Parallel deployments where success in some parallel stages allows moving to the next stage
Furthermore, we will address a common scenario that often leads to the creation of separate pipelines. Specifically, we will explore how to handle
a stage that depends on mutually exclusive previous stages
In the example pipeline design below, it is straightforward to create two stages, Stage2
and Stage3
, that depend on Stage1
.
stages:
stage: Stage2
dependsOn: Stage1
# stage2 steps
stage: Stage3
dependsOn: Stage1
# stage3 steps
Similarly, if deployment to Stage2
and Stage3
is required to move to Stage4
. Then you can build as shown below
stages:
stage: Stage4
dependsOn:
- Stage2
- Stage3
# stage4 steps
Till now everything was quite easy!
Now for the tricky part, if you are tasked to allow deployment to Stage4
- when any one of the previous mutual stages executes successfully.
Just having dependsOn
as shown in previous example will block proceeding to Stage4
, if one of the previous stages didn’t run.
In this case, dependsOn
should be coupled with pipeline conditions as shown below to achieve a mutually exclusive flow.
variables:
executeStage3: $[eq(variables['Build.SourceBranch'], 'refs/head/hotfix')]
stages:
- stage: Stage2
condition: and(succeeded(), eq(variables.executeStage3, false))
# stage 2 steps
- stage: Stage3
condition: and(succeeded(), eq(variables.executeStage3, true))
# stage 3 steps
- stage: Stage4
dependsOn:
- Stage2
- Stage3
condition: or(succeeded('Stage2'), and(eq(variables.executeStage3, true), succeeded('Stage3'))
# stage 4 steps
In this approach, when you have a flow that should pass through Stage3
, the condition causes Stage2
to be skipped instead of blocking it. Likewise, when you have a flow that should pass through Stage2
, the condition causes Stage3
to be skipped.
By using this method, you can build a stage that depends on mutually exclusive stages, thus avoiding the need to create separate YAML pipelines. However, it is worth noting that this logic can become cumbersome when dealing with three or more stages in exclusion or in complex combinations.
Based on my situation, the above solution worked effectively. I hope this explanation clarifies the approach and proves helpful to you.