0

How do I write an Azure Pipeline script that detects whether any other CI build jobs are running using the same Git branch, and cancels those other jobs?

  • I want to cancel only CI build jobs. Any PR build jobs and manually triggered jobs from the same Git branch should be ignored, and allowed to continue running.

  • Any build jobs from other Git branches should also be ignored.

The Azure DevOps VM is a self-hosted Windows VM, so the task must be a PowerShell or Windows script, not bash. The source is in Bitbucket Cloud -- this is important, because ADO handles Bitbucket Cloud repositories differently from other repositories.

If a canned task is available, I can use it as well.

The following questions are related, but they do not directly address this use case.

Ray Depew
  • 573
  • 1
  • 9
  • 22

3 Answers3

1

You can first use the API "Builds - List" to list all the builds which have been trigged but not completed.

GET https://dev.azure.com/{organization}/{project}/_apis/build/builds?reasonFilter={reasonFilter}&statusFilter={statusFilter}&branchName={branchName}&repositoryId={repositoryId}&api-version=6.0

For your case,

  • The value of reasonFilter should be batchedCI and individualCI.
  • The value of statusFilter should be inProgress, notStarted and postponed.
  • The value of branchName is the branch you specify.
  • The value of repositoryId is the ID of your Git repository.

Then use the API "Builds - Update Build" to cancel all the builds (except the current build) in a loop.

Bright Ran-MSFT
  • 5,190
  • 1
  • 5
  • 12
1

You can add powershell script step into your build definition to check active builds on the same branch. As an example

$user = ""
$token = "$(System.AccessToken)"
$buildDef = "$(System.DefinitionId)"
$branchName = "$(Build.SourceBranch)"
$teamProject = "$(System.TeamProject)"
$orgUrl = "$(System.CollectionUri)"
$buildId = $(Build.BuildId) -as [int]

$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user,$token)))

$uriGetActiveBuilds = "$orgUrl/$teamProject/_apis/build/builds?definitions=$buildDef&statusFilter=inProgress&branchName=$branchName&api-version=5.1"


$resultStatus = Invoke-RestMethod -Uri $uriGetActiveBuilds -Method Get -ContentType "application/json" -Headers @{Authorization=("Basic {0}" -f $base64AuthInfo)}


if ($resultStatus.count -gt 0)
{
    foreach ($build in $resultStatus.value)
    {
        $bid = $build.id -as [int]
        if ($buildId -gt $bid) //if exists a lower value of the build id on the same branch, the current build should be stoped
        {
            exit 1 
        }
    }
}
Shamrai Aleksander
  • 13,096
  • 3
  • 24
  • 31
  • That's good for starters. But how do I stop the previous builds? I guess I need to replace the `exit 1` statement with some kind of `kill $build.id` URL. – Ray Depew Feb 18 '22 at 15:02
  • @RayDepew you do not need to stop the previous builds. Because each build will check running builds and fails if they exist. So you will not have parallel builds on one branch. – Shamrai Aleksander Feb 18 '22 at 15:25
0

The answer from @Shamrai-Alexsandr cancels the current build, but what I want to do was cancel all other builds (that is, CI builds on the current branch) still in progress.

The answer from @bright-ran-msft gave me enough clues to combine @bright's solution with @shamrai's solution, replacing the exit 1 with code that cancels the other builds:

        if ($buildId -gt $bid) 
        {
            $build.status = "Cancelling"
            $cancelRequest = $build | ConvertTo-Json -Depth 10
            $uriCancel = "$orgUrl$teamProject/_apis/build/builds/$($build.id)?api-version=6.0"
            $resultOfCancel = Invoke-RestMethod -Uri $uriCancel -Method Patch -ContentType "application/json" -body $cancelRequest -Headers @{Authorization=("Basic {0}" -f $base64AuthInfo)}
            Write-Host "Result of Cancel request: " $resultOfCancel.status
        }
Ray Depew
  • 573
  • 1
  • 9
  • 22