cResults
cResults

Reputation: 793

How to prevent a YAML Deployment Pipeline from running when the Build Pipeline doesn't produce an artifact

We have a build pipeline that gets triggered for a CI build that only publishes artifacts when the build succeeds and is not a PR build nor a Scheduled build.

We only want the release pipeline defined below to run when the build pipeline publishes an artifact. If it doesn't publish an artifact the triggered release pipeline fails.

My understanding that I believe is from reading learn.microsoft.com was that if the resource doesn't produce an artifact it won't trigger a resource trigger (I currently can't find support for that claim). We just added a scheduled nightly build for running some long running tests to the build pipeline. As configured the conditions on the publish tasks prevented an artifact from being created but the release pipeline is still being triggered and results in a failure.

trigger: none
pr: none
resources:
  pipelines:
  - pipeline: API-Deployment 
    source: API-Build  
    trigger: 
      branches:
        include: 
        - refs/heads/main

How do we prevent the release pipeline from running when the build pipeline is triggered by a scheduled or a PR build? Or how can we inspect to find out whether or not the triggering build produced an artifact so that we can prevent stages/jobs/tasks from running and failing when an artifact doesn't exist?

Upvotes: 2

Views: 1432

Answers (2)

cResults
cResults

Reputation: 793

The way we ended up addressing this thanks to the help of @Leo Liu-MSFT was by adding a PrepStage that all first tier stages depends upon that uses Azure DevOps REST APIs to call the artifacts endpoint for the triggering pipeline. Then set the hasArtifacts variable that is used in the condition of subsequent stages that depend on PrepStage.

Below is the YAML of the prep stage and an example of a stage that depends on it.

stages:
  - stage: PrepStage
    displayName: Prep Stage
    jobs:
      - job: JA
        displayName: Deployment Prep Job
        cancelTimeoutInMinutes: 1
        steps:
          - checkout: none
          - task: PowerShell@2
            name: setHasArtifacts
            inputs:
              targetType: 'inline'
              script: |
                $url = "$($env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI)$env:SYSTEM_TEAMPROJECTID/_apis/build/builds/$(triggerPipeRunID)/artifacts?api-version=4.1"
                Write-Host "URL: $url"
                $artifacts = Invoke-RestMethod -Uri $url -Headers @{ Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN" }
                $artifactCount = $artifacts.count
                Write-Host "Artifact Count: $artifactCount"

                if ($artifactCount -gt 0) {
                    Write-Host "Set Variable hasArtifacts to True"
                    Write-Host "##vso[task.setvariable variable=hasArtifacts;isOutput=true]True"
                }
                else {
                    Write-Host "Set Variable hasArtifacts to False"
                    Write-Host "##vso[task.setvariable variable=hasArtifacts;isOutput=true]False"
                }
            env:
               SYSTEM_ACCESSTOKEN: $(System.AccessToken)
          - script: echo "hasArtifacts - $(setHasArtifacts.hasArtifacts)"
            name: echovar

  - stage: SB
    dependsOn: PrepStage
    condition: and(succeeded(), eq(dependencies.PrepStage.outputs['JA.setHasArtifacts.hasArtifacts'], 'True'))
    jobs:
      - job: JB
        condition: and(succeeded(), eq(stageDependencies.PrepStage.JA.outputs['setHasArtifacts.hasArtifacts'], True))
        variables:
          varFromStageA: $[ stageDependencies.PrepStage.JA.outputs['JA.setHasArtifacts.hasArtifacts'] ]
          varFromStageA2: $[ stageDependencies.PrepStage.JA.outputs['setHasArtifacts.hasArtifacts'] ]
        steps:
        - checkout: none
        - script: |
            echo "varFromStageA: $(varFromStageA)"      
            echo "varFromStageA2: $(varFromStageA2)"

A couple of things worthy of note that were hard to find. First notice the two different syntaxes for referencing the variable hasArtifacts in the conditions on stage SB and job JB. We are only using the condition at the stage level, but I put both here because of how hard it was to find out how to reference a variable set in one stage as a condition of a stage that depends upon it. Both of these conditions work.

The second thing to take note of is variable $(triggerPipeRunID) which is used in the url of the rest api request. It is defined above the stages

trigger: none
pr: none
resources:
  pipelines:
  - pipeline: API-Deployment #Azure DevOps has lots of names for this. Most common seems to be "PipelineIdentifier" see variable below.  Another is less descriptive name is "Alias".
    source: API-Build  #this is the name of the build pipeline as specified in Azure DevOps Pipelines UI
    trigger: 
      branches:
        include: 
        - refs/heads/main

variables:
- name: triggerPipeRunID
  value: $(resources.pipeline.API-Deployment.runID)

This is working for us now. While it still triggers the Release pipeline even when the API-Build doesn't produce an artifact, the deployment stages are skipped and the pipeline run doesn't get recorded as a failure.

Upvotes: 1

Leo Liu
Leo Liu

Reputation: 76890

Thanks for posting in Developer Community.

I'm afraid there is no such out-of-the-box way to prevent the release pipeline from running when the build pipeline doesn't produce an artifact when you use the resource pipeline trigger.

That's because artifact resource triggers are not currently supported. You could check the document Define resources in YAML

Current support resource trigger type:

resources:
  pipelines: [ pipeline ]  
  builds: [ build ]
  repositories: [ repository ]
  containers: [ container ]
  packages: [ package ]
  webhooks: [ webhook ]

On the other hand, when we use pipeline resource triggers, there is no artifact filter for us to restrict whether an artifact is included in the resouorce:

Evaluation of artifact version.

As workaround, we could use the Classic pipeline instead of the YAML for the release pipeline, so that we could select the artifact source:

image.png

If you still want use the YAML, you could remove the pipeline resource triggers, then add the one powershell task to invoke REST API to trigger the release pipeline with the condition:

and(succeeded(), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI'))

Upvotes: 0

Related Questions