5

I'm using hosted Azure DevOps with our code in Azure Git Repos. We used to use the "Classic" UI-based pipeline editor, but are moving to YAML templates for our build/release stages.

In the past I configured CI/CD so that when code is committed to the master branch via a pull request, it would fire off a build and then a Development deployment. The other release stages would wait for approval before the code moved to that stage. A new release would cancel any previous releases that haven't been deployed to their respective environments.

With YAML deployment stages what I'm finding is that when the master branch triggers a build, it deploys to the Development environment, but the pipeline is stuck in a waiting state because the other stages haven't been approved. As a result, the run isn't marked as "complete", and eventually the other stages will time out and be marked as failed. Additionally, previous runs of the pipeline are not cancelled, so multiple runs are stacked up in a waiting state.

Ideally what I'd like to see is that a new build will cancel all previous runs of the pipeline. I'd like to see the run marked as "complete" once it deploys to Development, and be able to deploy to other stages manually after the fact.

Has anybody else out there wanted to do the same thing? Am I just thinking about this all wrong and should be doing it a different way?

Frank Hoffman
  • 887
  • 1
  • 11
  • 16

1 Answers1

1

Manually deploy to stages is not support in yaml pipeline currently. Please check this open issue.

You can try adding dependsOn and condition for each stage. For below example yaml pipeline. Stage Build will start to run only after stage Start successfully complete, Then Stage Build will wait for approval, Stage Release willnot be triggered until Stage Build is approved and successfully finished.

You can define the pr trigger and set autocancel=true (the default is true)to cancel previous runs if new changes were pushed to the same pr.

The batch property for trigger can achieve a similar effect. It will not start a new run if the current pr in still in building.

trigger:
  batch: boolean # batch changes if true (the default); start a new build for every push if false
  branches:
    include:

_

pr:
  autoCancel: true
  branches:
    include:
    - master

stages:
- stage: Start
  jobs:
    - job: A
      pool:
        vmImage: windows-latest
      steps:
      - powershell: |
          echo "i am job a"

- stage: Build
  dependsOn: Start
  condition: succeeded()
  jobs:
  - deployment: Dev
    displayName: deploy Web App
    pool:
      vmImage: 'Ubuntu-16.04'
  # creates an environment if it doesn't exist
    environment: 'Dev'
    strategy:
    # default deployment strategy, more coming...
      runOnce:
        deploy:
          steps:
          - script: echo "i am dev environment"


- stage: Release
  dependsOn: Build
  condition: succeeded()
  jobs:
  - deployment: Environ
    displayName: deploy Web App
    pool:
      vmImage: 'Ubuntu-16.04'
  # creates an environment if it doesn't exist
    environment: 'Environment'
    strategy:
    # default deployment strategy, more coming...
      runOnce:
        deploy:
          steps:
          - script: echo "i am Environment environment"

Update: Cancel in progress builds via powershell scripts.

You can add a powershell task at the top of your pipeline to call build api. Below scripts get all the in progress builds and cancel them except current build.

- task: PowerShell@2

      inputs:
        targetType: inline
        script: |

          $header = @{ Authorization = "Bearer $(system.accesstoken)" }
          $buildsUrl = "$(System.TeamFoundationCollectionUri)$(System.TeamProject)/_apis/build/builds?api-version=5.1"
          echo $buildsUrl
          $builds = Invoke-RestMethod -Uri $buildsUrl -Method Get -Header $header

          $buildsToStop = $builds.value.Where({ ($_.status -eq 'inProgress') -and ($_.definition.name -eq "$(Build.DefinitionName)") -and ($_.id -ne $(Build.BuildId))})

          ForEach($build in $buildsToStop)
          {
            echo $build.id
            $build.status = "cancelling"
            $body = $build | ConvertTo-Json -Depth 10
            $urlToCancel = "$(System.TeamFoundationCollectionUri)$(System.TeamProject)/_apis/build/builds/$($build.id)?api-version=5.1"
            echo $urlToCancel
            Invoke-RestMethod -Uri $urlToCancel -Method Patch -ContentType application/json -Body $body -Header $header
          }

In order your pipeline to have the permission to cancel the current running build. You need go to your pipeline, click on the 3dots and choose Manage security

enter image description here

Then set the Stop builds permission to Allow for user Project Collection Build Service(projectName),

enter image description here

Levi Lu-MSFT
  • 27,483
  • 2
  • 31
  • 43
  • 1
    I do have dependencies between stages currently. The Development stage depends on the Build stage, and the Test stage depends on the Development stage. Approvals are set up on the Test stage, but not Development. The result is that every time a PR is merged, it deploys to Development, but waits for approval to the Test stage. Since PR's happen quite often, this results in a lot of pipeline runs waiting for approval. These are never automatically cancelled and eventually time out. Additionally, I found that the code coverage tab never gets displayed, because the run is never marked as complete. – Frank Hoffman Jan 30 '20 at 21:17
  • You can have a try with batch property in the trigger, or use pr trigger. I updated above answer. – Levi Lu-MSFT Jan 31 '20 at 07:12
  • I'm not sure this is exactly what I'm after. In the case of a PR trigger (not an option for Azure Repos), it only cancels previous runs for the same PR. The batch option (true by default) looks like it just cuts down on the number of runs, but doesn't cancel all previous runs. – Frank Hoffman Jan 31 '20 at 16:00
  • You can add a powershell task to the top of your pipeline. The powershell task run scripts to cancel in progress builds before the following tasks are executed. Please check above update. – Levi Lu-MSFT Feb 01 '20 at 15:04
  • This is getting closer to what I'm after but it doesn't seem to be finding the builds that match that where clause. Part of it is my lack of experience with the syntax. I did some debugging and found that $buildsToStop seemed to be empty. I also had to add quotes around '$(Build.DefinitionName)'. Were you able to get this going on a sample pipeline? I'm not sure what I'm missing. I tried altering the where clause to just include the definition name and no other criteria but it still seemed to come up empty. – Frank Hoffman Feb 03 '20 at 16:00
  • Hi @FrankHoffman I updated my script. I tested and It worked for me. Please also be noted to set the Stop builds permission to Allow for user Project Collection Build Service(projectName), – Levi Lu-MSFT Feb 03 '20 at 16:44
  • Thank you so much for your help on this. This has been very good info and has accomplished what I wanted it to do! You have earned the upvote :) – Frank Hoffman Feb 04 '20 at 15:28