Azure DevOps YAML Pipelines prevent newer runs if old one queued - azure-devops

I have a pipeline with a number of stages, e.g.:
Canary -> Dev -> UAT -> Prod
For each stage we have approvals set so a team member has to allow a build to proceed to the next stage. Sometimes we have long periods between stages being approved (e.g. someone needs to do some testing and feedback outside of devops we can proceed), therefore it can be hard to remember we have an old run pending approval unless we specifically check - which is prone to human error.
But if I have one build say queued at UAT awaiting push to production how can I prevent a second run which includes the changes from the first run (as they are both running off master) being allowed to proceeded.
E.g. I need newer jobs to be aware of older runs still pending approval to proceed.
Thanks!

There is no default way to prevent newer runs if old one queued, but you can try the following workaround:
You could add a script in the pipeline to check state of the latest pipeline run. If the state is inProgress, cancel the build. If the state is completed, proceed the build.
You should use Runs - List api to get state of the latest pipeline run. The script looks like:
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user,$token)))
$uri = "https://dev.azure.com/$org/$projectName/_apis/pipelines/$pipelineid/runs?api-version=6.0-preview.1"
$result = Invoke-RestMethod -Uri $uri -Method Get -Body $json -ContentType "application/json" -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)}
$latestrun = $result.value.state[0]
Write-Host "$latestrun"
Then you could refer the following case to cancel the build if $latestrun is inProgress:
Is it possible to cancel a Azure DevOps pipeline Job programmatically?

Related

In an Azure Devops pipeline, how can I detect and cancel other build jobs from the same Git branch?

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.
Is an Azure DevOps build pipeline, is there a way to cancel one pipeline job from another job?
Azure devops build pipeline depends on other build pipeline
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.
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
}
}
}
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
}

Execute Mutliple release pipeline

I have 50+ release pipelines for the production environment and each time doing production has to manually approve each of these pipelines for deployment.
Is there any way to automate and in a single click, all the pipeline gets approved and gets deployed?
The only way I am aware of accomplishing this would be to combine the 50+ pipelines into one master release and have those dependent on a separate stage who is deploying to a gated environment. Assign an approval on that environment. Thus once that stage in the "approval environment" is approved all subsequent stages will run.
In Azure DevOps, we can configure approvals, It checks all the resources used in that stage, such as source code and follow the target server deployed, this should let managers to check and agree this deploy, batch approval is meaningless.
If you insist on approving multiple release pipeline stages at the same time. we recommend that you can remove these approvals.
Or we can use API to list all Approvals and get the approval id, then update status of an approval.
In addition, if there are multiple approvals in the pipeline, it will only approve the current stage.
Steps:
Create a PAT token->create a build pipeline->click the tab variable and add the variable pat = {Token}, then set it to secret variable->add task powershell and enter the following script to approve.
$connectionToken="$(pat)"
$base64AuthInfo= [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($connectionToken)"))
$PipelineUrl = "https://vsrm.dev.azure.com/{Org name}/{Project name}/_apis/release/approvals?api-version=5.1"
$Pipelines = (Invoke-RestMethod -Uri $PipelineUrl -Method Get -UseDefaultCredential -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)})
$ ApprovalId = $Pipelines.value.id
Write-Host $ ApprovalId
# List all Approvals and get approval id
ForEach ($Id in $ ApprovalId)
{
$baseUrl = "https://vsrm.dev.azure.com/{Org name}/{Project name}/_apis/release/approvals/$($Id)?api-version=5.1"
$body ="{
`"status`": `"approved`"
}"
$response = Invoke-RestMethod -Uri $baseUrl -ContentType "application/json" -Body $body -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)} -Method PATCH
}

Managing a CI/CD Pipeline from another Pipeline - Azure Devops

I have a pipeline(Say A). In it, I have written a PowerShell script which helps me to update a particular package in the solution. After merging the changed code with the master branch using this PowerShell script, it automatically triggers another pipeline(say B) whose triggering depends on the changes in master. I have to control the triggering of this pipeline B from Pipeline A - like get the status of the triggered pipeline B, disable the trigger of pipeline B from A, etc. Please help me with this scenario.
You can use a powershell task to call build rest api to get the status of another pipeline(ie. Pipeline B).
First to get the latest build of Pipeline B, you can use below rest api.
GET https://dev.azure.com/{organization}/{project}/_apis/build/builds?definitions={definitions}&$top={$top}&api-version=5.1
Below is the inline script example in the powershell task to get the build status.
$uri = "$(System.TeamFoundationCollectionUri)$(System.TeamProject)/_apis/build/builds?definitions={definitionId}&`$top=1&api-version=5.1"
 
$result =Invoke-WebRequest -Uri $uri -Method Get -ContentType "application/json" -Headers $headers = #{ Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN"}
$status = $result.value[0].status
$env:SYSTEM_ACCESSTOKEN is the predefined variable with which you can refer to the access token directly in the scripts.
To cancel pipeline B in pipeline A you can call update Build rest api. See below example. First get the build from above api, then update the status to cancelling
$build = $result.value[0]
$uriupdate = "$(System.TeamFoundationCollectionUri)$(System.TeamProject)/_apis/build/builds/$($build.id)?api-version=5.1"
$build.status = "cancelling"
$body = $build | ConvertTo-Json -Depth 10
$update = Invoke-RestMethod -Uri $uriupdate -Headers #{Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN"} -ContentType "application/json" -Method patch -Body $body
To skip a build when pushing the changes, you can just include [skip ci] in the commit message as Shamrai mentioned.
git commit -m message [skip ci]
I have to control the triggering of this pipeline B from Pipeline A -
like get the status of the triggered pipeline B, disable the trigger
of pipeline B from A, etc.
You can use REST API with PowerShell to control your builds: Builds - List.
To disable the trigger, add scip ci into your commit message: Skipping CI for individual commits
You can use output variable in powershell task. And based on that you can control the next job to execute. This way you don't have to use multiple build pipelines instead multiple jobs in a single pipeline.
You can refer the Microsoft document here

Azure DevOps integration in Sentry: Associate commits

Did someone manage to integrate Azure DevOps in Sentry (sentry.io)? I stuck on "Associate commits with a Release" (see: https://docs.sentry.io/workflow/releases/?platform=browser#associate-commits-with-a-release)
I can not figure out a way how I can tell Sentry (by API) which commit ids are associated with a current release/deploy. How can I add a task to the pipeline which will post the commit ids to Sentry API? Or is there some other way to do it?
In azure devops, the Powershell task also support curl. So, you can execute the api in powershell task of VSTS pipeline directly.
In release pipeline, there has a pre-defined release variable, it stores the commit id which is associated with the current release pipeline: $(Release.Artifacts.{alias}.SourceVersion). Here alias is the artifacts name, and you can get it by getting $(Release.PrimaryArtifactSourceAlias).
First, create variables like this:
Then you can apply the variable $(id) into that API, and execute the api in powershell task:
"refs": [{
"commit":"$(id)"
}]
Now, the commit id could be packed into the body of this api, and send to the Sentry server.
If there has multiple commits associate with this release, since the variable $(Release.Artifacts.{alias}.SourceVersion) I mentioned above only store the latest commit message, here you may need add additional scripts to get what you want by Build id.
In release pipeline, with $(Build.BuildId) you can get the corresponding buildid which associate with this release. And then, you could get the commits(changes) by using this API:
GET https://dev.azure.com/{organization}/{project}/_apis/build/changes?fromBuildId={fromBuildId}&toBuildId={toBuildId}&api-version=5.1-preview.2
You could apply these powershell script into your task without change anything because this script is universal among powershell-ise, powershell command line and powershell task in VSTS.
$token = "{PAT token}"
$url="https://dev.azure.com/{org name}/{project name}/_apis/build/changes?fromBuildId={id1}&toBuildId={id2}"
$token = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($token)"))
$response = Invoke-RestMethod -Uri $url -Headers #{Authorization = "Basic $token"} -Method Get
Write-Host "results = $($response.value.id | ConvertTo-Json -Depth 100)"
Now, you could get the list of commits which associate with the build and corresponding release.

Check previous build information in VSTS (VSTS API)

Are previous build variables accessible during execution of a VSTS build? For example, can I get $(Build.SourceVersion) or $(Build.QueuedBy) of the previous build?
I can get current build information through the build variables like $(Build.SourceVersion) but can I get something like $(Build.Previous.SourceVersion)?
There aren’t the built-in variables for previous build information, the workaround is that you can call Builds REST API (can be filter status, such as completed, inProgress) through PowerShell during this build. (The first item of the result is the newest one)
$base64authinfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $User, $Password)))
$responseFromGet = Invoke-RestMethod -Method Get -ContentType application/json -Uri $Uri -Headers #{Authorization=("Basic {0}" -f $base64authinfo)}
Some articles about calling REST API: Calling VSTS APIs with PowerShell, VSTS/TFS REST API: The basics and working with builds and releases
You can use value of System.AccessToken variable as password (Check Allow scripts to access OAuth token option in Options tab) and username can be anything.
No. "Previous" is a nebulous concept when you're talking about things that can run in parallel. What if you have 3 builds running concurrently?