Don't trigger builds for branches that already have a pull request in Azure DevOps

1.4k views Asked by At

We use Azure DevOps for continuous integration. The pipeline is configured to run a build whenever a change is pushed to a feature branch. This is desired for quick feedback.

Additionally, we have the policy for the master branch that a successful validation build is required before a feature branch can be merged. Azure DevOps now automatically triggers the corresponding validation build when a pull request (PR) is created for a feature branch.

All of this is fine, but there is an adversity: if a PR is already created and the feature branch is updated, two builds are triggered (one for the feature branch alone and one for the outcome of the merge, i.e., the validation build).

I understand that some people might want both builds, but in our case (an probably in every normal case) it would be better if only the validation build was triggered.

Question: Is there a way to tell Azure DevOps that it should ignore branch triggers for any branch that already has a PR? Workarounds with an equivalent outcome are also welcome, of course.

The question has already been posted as an issue here, but I could not find a satisfying answer in the replies (e.g., branch filters and a naming strategy do not solve the problem).

3

There are 3 answers

0
Till F. On BEST ANSWER

I have solved the issue like suggested by Shamrai. I'm adding this as another answer to share my code.

Add the following PowerShell code to a step at the beginning of the pipeline. It uses the Azure DevOps REST API to check for an existing PR of the current branch. If there is no such PR or if the current build was manually queued, or if the PR is not ready to be merged (e.g. conflicts), the build continues as normal. Otherwise, it is cancelled.

$BaseApiUri_Builds   = "https://my.tfs.server.com/MyCollection/MyProject/_apis/build/builds"
$BaseApiUri_GitRepos = "https://my.tfs.server.com/MyCollection/MyProject/_apis/git/repositories"
$AuthenicationHeader = @{ Authorization = "Bearer ${env:System_AccessToken}" }

# Cancels the current build
function Cancel-This-Build()
{
    Cancel-Build -BuildId ${env:Build_BuildId}
}

# Cancels the given build
function Cancel-Build([String] $BuildId)
{
    Write-Host "Cancelling build ${BuildId}..."
    
    $BuildApiUri = "${BaseApiUri_Builds}/${BuildId}?api-version=5.1"
    $CancelBody = @{ status = "cancelling" } | ConvertTo-Json
    Invoke-RestMethod -Uri $BuildApiUri -Method PATCH -ContentType application/json -Body $CancelBody -Header $AuthenicationHeader
}

# Detects if a validation build is queued for the given branch. This is the case if an active PR exists that does not have merge conflicts.
function Check-For-Validation-Build([String] $BranchName)
{
    Write-Host "Checking for validation builds of branch '${BranchName}' in repository ${env:Build_Repository_ID}..."
    
    $GetPRsApiUri = "${BaseApiUri_GitRepos}/${env:Build_Repository_ID}/pullrequests?api-version=5.1&searchCriteria.sourceRefName=${BranchName}"
    $PRs = Invoke-RestMethod -Uri $GetPRsApiUri -Method GET -Header $AuthenicationHeader
    
    ForEach($PR in $PRs.Value)
    {
        Write-Host "Found PR $($PR.pullRequestId) '$($PR.title)': isDraft=$($PR.isDraft), status=$($PR.status), mergeStatus=$($PR.mergeStatus)"
        
        if (!$PR.isDraft -and ($PR.mergeStatus -eq "succeeded") -and ($PR.status -eq "active"))
        {
            Write-Host "Validation build is queued for branch '${BranchName}'."
            return $true
        }
    }
    
    Write-Host "No validation build is queued for branch '${BranchName}'."
    return $false
}

# Check if a valid PR exists. If so, cancel this build, because a validation build is also queued. 
$HasValidationBuild = Check-For-Validation-Build -BranchName ${env:Build_SourceBranch}
if (($HasValidationBuild -eq $true) -and (${env:Build_Reason} -ne "Manual") -and !${env:Build_SourceBranch}.EndsWith('/merge'))
{
    Cancel-This-Build
}

Note that the System.AccessToken environment variable must be made visible for the script to work.

1
Suki Ji-MSFT On

Because that's the design, thus, both build will be triggered but you could have try with follow method to avoid the build queued at the same time.

In the build validation policy - disable automatic queuing. Or mark the PR as draft, while changes are being worked on. After this change any of the changes will only trigger CI build/pipeline, and when ready just publish the PR or queue the PR manually.

0
Shamrai Aleksander On

"Out-of-box" - you can not. However as a workaround, you can use rest API to check active pull requests and if they exist just fail your unneeded build:

  1. Get Pull Requests - Targeting a specific branch
  2. Use the system access token from your build pipeline. Access repositories, artifacts, and other resources
  3. Exit with Powershell from a build: exit 1