Description for succeeded() says:
> With no arguments, evaluates to True only if all previous jobs in the dependency graph succeeded or partially succeeded.
If a stage without any explicit dependencies has its condition explicitly set to succeeded(), it appears that it will be skipped if the stage immediately before it is skipped. We should provide clear documentation for how all of these conditions work with skipped dependencies, explicit or implicit. It'd also be an improvement to explain if there are any behavior differences between conditions in job and stage contexts.
⚠Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.
@kevinhartman Can you give an example of what you are running into? Are you using in(variables['Agent.JobStatus'], 'Succeeded', 'SucceededWithIssues') for the stage condition?
@juliakm
We ran into a similar issue and if you're skipping a stage the succeeded gets set to false. If you look at the following pipeline link, at the Release: azure-storage-blob
https://dev.azure.com/azure-sdk/internal/_build/results?buildId=348193&view=logs&j=a658c784-dbd2-56de-6d5c-dbbf5e4dd371
Verify release version <-- we let this step run and it passed
Create release tag <-- we had a pipeline variable to skip this step which has the following condition: and(succeeded(), ne(variables['Skip.TagRepository'], 'true')) and you can see that this expanded to Expanded: and(True, ne('true', 'true'))
Publish to Maven Central <-- this step has the following condition: and(succeeded(), ne(variables['Skip.PublishPackage'], 'true')) but when you look at what it expands to you'll notice that since the previous step was skipped, succeeded was set to false false. Expanded: and(False, ne(variables['Skip.PublishPackage'], 'true')) and that was pretty much the end of that pipeline. Skipping a step does cause succeeded() to get set to false. This kind of make the skip variables moot and, in our case, caused a bunch of work since we couldn't skip a stage and have subsequent stages run.
@JimSuplizio exactly. When a stage gets skipped, it appears that succeeded() for the next stage no longer comes back as true.
As a work-around, our team has had success (no pun intended) using:
condition: not(or(failed(), canceled())`
This works because the previous stage being skipped does not set a failed status.
@juliakm, I'm not sure if this behavior (succeeded() evaluating as false for stages that come after one that's skipped) is a product bug, or just an unintuitive behavior that could benefit from better docs.
Correct me if I'm wrong, but I believe that a stage that does not specify any dependencies in its dependsOn list implicity depends on the previous stage, which would explain why this behavior occurs (if B depends on A, then B should not run if A does not run). Because the dependency is implicit, it's very unclear that this is the behavior.
Perhaps a more intuitive behavior would be to only set succeeded to false if a stage takes an explicit dependency on the skipped stage.
This issue hasn't been updated in more than 180 days, so we've closed it. If you feel the issue is still relevant and needs fixed, please reopen it and we'll take another look. We appreciate your feedback and apologize for any inconvenience.