How can I delete an Azure DevOps build definition that it claims is retained by a release? - azure-devops

I'm trying to delete an Azure DevOps build definition but it won't let me since it says:
"One or more builds associated with the requested pipeline(s) are retained by a release. The pipeline(s) and builds will not be deleted."
However there's no obvious way to see what release is causing a build to be retained. I tried searching online, of course, but all the examples/screenshots of how to do this in the web UI are showing UI from several iterations ago of the Azure DevOps website so none of the controls look the same anymore. I don't see a lock icon anywhere, for example.
How can I find the releases that are holding onto these build definitions so I can delete them and then delete the build definition?
Thanks!

When you open the build pipeline to see its detailed build records, you can see the relevant release name and its link:
On old pipeline version, there had a lock icon which can obvious let us know it is retained. In fact, the lock icon not only means it is retained by release, manual build retain also show this icon. But, seems we missed this obvious icon while we expand the new sprint.
As a workaround to get list of builds which retained by release, here has a short script can help you achieve by using Rest api:
$token = "{PAT token}"
$url ="https://dev.azure.com/{org name}/{project name}/_apis/build/builds?api-version=5.1"
$token = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(":$($token)"))
$response = Invoke-RestMethod -Uri $url -Headers #{Authorization = "Basic $token"} -Method Get
$results = $response.value | Where {$_.retainedByRelease -eq "true"} #|
Write-Host "results = $($results.id | ConvertTo-Json -Depth 100)"
First, use list builds api to get all builds that in current project. Then for the builds which retained by release, since there has a parameter can indicate it: retainedByRelease here I use $_.retainedByRelease -eq "true" to get the builds list which actual retained by release:
The above script is very universal, can be used in Powershell-ise and Powershell Command Line and the Powershell task of VSTS without change anything.
Update in 11/19:
Based on #Auth's comment, if want to get its associated release, the most easiest way is find the build, and then check its associate release pipeline as the screenshot shown I shared above.
If this does not satisfied what you want, and the previous API we used does not include any releases info in that, so here, you need use this API: Releases - Get Release:
GET https://vsrm.dev.azure.com/{org name}/{project name}/_apis/release/releases?sourceId={project id}:{build definition id}&api-version=5.1
In this API, you need specified the project id:build definition id to filter releases.
With the icon missing, will let the corresponding team know and try to add it in the future sprint.

I have had this issue when some one delete a repository that was associated with a "POC" pipeline. I was not able to delete the pipeline.
When I tried to delete the pipeline, I got following message:
One or more builds associated with the requested pipeline(s) are retained by a release. The pipeline(s) and builds will not be deleted.
I did following steps to delete the pipeline:
My Goal was to delete PublishBuildArtifacts-Infrastructure pipeline as shown in image. So I clicked on the pipeline.
I see list of Builds (Runs) that is associated with my pipeline. I click on the 3 dots and click on View Retention Lease.
Then I click Remove all.
Now you can delete your build.
Repeat steps 2-4 for each run. When you have delete all builds, now you can delete the pipeline by clicking on the 3 dot menu, as shown in the image below.
Enjoy!

Related

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

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).
"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:
Get Pull Requests - Targeting a specific branch
Use the system access token from your build pipeline. Access repositories, artifacts, and other resources
Exit with Powershell from a build: exit 1
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.
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.

Is it possible to change the name of a build based on the branch name in Azure Pipelines?

Most of my builds are from either feature branches or develop, and so I tend to use a known build variable to track the build number such as:
variables:
- group: BuildVars
name: $(BuildPrefix)$(Rev:r)
This works and provides me with a nicely formatted build version that I can then follow through into releases, etc:
However, when we're planning a release, we name our branches after the release, for example: release/1.1, and I'd like to have the build name reference that instead of the hardcoded (previous) version.
I know that I can reference the branch name via the Build.SourceBranch variable, but I don't see an obvious way to read and modify that outside of a build step, by which time I believe it's too late? I don't really want to have to manually change the BuildPrefix variable until the release has been deployed out to production.
Building on from this would then be the ability to append appropriate pre-release tags, etc. based on the branch name...
you can always update the build name during the execution of a build using this:
- pwsh: |
Write-Host "##vso[build.updatebuildnumber]value_goes_here"
so you could calculate the value in the same (or previous step) and update the build name with that value
Is it possible to change the name of a build based on the branch name in Azure Pipelines?
The answer is yes.
The solution we currently use is add a Run Inline Powershell task to update build number based on the Build_SourceBranchName:
$branch = $Env:Build_SourceBranchName
Write-Host "Current branch is $branch"
if ($branch -eq "Dev")
{
Write-Host "##vso[build.updatebuildnumber]$DevBuildNumber"
}
elseif ($branch -eq "Beta")
{
Write-Host "##vso[build.updatebuildnumber]$BetaBuildNumber"
}
elseif ($branch -eq "Test")
{
Write-Host "##vso[build.updatebuildnumber]$TestBuildNumber"
}
Check the Logging Command during the build for some more details.
Hope this helps.

Access SourceBranchName in Release pipeline

I'm trying to release my buildartifacts to a specific folder based on the name of the sourcebranch which upon creating a pull request triggered the build and therefor the release.
I've managed to so far to get:
write-host $env:RELEASE_TRIGGERINGARTIFACT_ALIAS
$triggerAlias = $env:RELEASE_TRIGGERINGARTIFACT_ALIAS
This alias (from my point of view) is the primary artifcat alias which I need to access
Release.Artifacts.{Primary artifact alias}.SourceBranchName
based on this documentation. So how do I combine the alias to get the sourcebranchname
$env:RELEASE_ARTIFACTS_{$triggerAlias}_SOURCEBRANCHNAME
This doesn't seem to be working and neither does
$env:RELEASE_ARTIFACTS_$($triggerAlias)_SOURCEBRANCHNAME
Any advice is much appreciated.
You can read the variable in this way:
$triggerAlias = $env:RELEASE_TRIGGERINGARTIFACT_ALIAS
$branchNameVariable = "RELEASE_ARTIFACTS_$($triggerAlias)_SOURCEBRANCHNAME"
#Get the value of the environment variable Release.Artifacts.{alias}.SourceBranchName
$branchName = (Get-item env:$branchNameVariable).Value

Create bug in a different project when build fails

We have setup a build definition which creates a bug and assigns it to "requestor" incase the build fails. See below :
By default, the bugs are created in the same project. How do I change this to create the bugs in a different project ? The reason is, our work item tracking is in a different project than our Git repos (due to reshuffling and what not). Note that both the projects are under the same account.
Looking at the tool tip, it provides some sample fields, is there a corresponding field for "project" ? (System.Project ?!)
The Create work item on failure option in VSTS build can only create work item in the same project.
The workaround to create a Bug if build failed can be achieved by adding a PowerShell task. Detail steps as below:
1. Add a PowerShell in the end of your build definition
In the PowerShell script, you can create a Bug for a different project by REST API. Below is an example script:
$witType="Bug"
$witTitle="Build $(build.buildNumber) failed"
$u="https://account.visualstudio.com/DefaultCollection/project/_apis/wit/workitems/`$$($witType)?api-version=1.0"
$body="[
{
`"op`": `"add`",
`"path`": `"/fields/System.Title`",
`"value`": `"$($witTitle)`"
}
]"
$user = "user"
$token = "PAT"
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user,$token)))
$result=Invoke-RestMethod -Method PATCH -Uri $u -Headers #{Authorization=("Basic {0}" -f $base64AuthInfo)} -ContentType "application/json-patch+json" -Body $body
2. Specify Run this task option as Even if a previous task has failed, unless the build was canceled
From Second task to the end task (PowerShell task), change Run this task option as Even if a previous task has failed, unless the build was canceled:
So when build fails, a Bug will be create to the different project as you specified in PowerShell script.
You can not do this out-of-the-box, but ...
You could do it using the API, something like this:
1 - Add a task to the build process to call a rest API and call the api to create a workitem. See https://www.visualstudio.com/en-us/docs/integrate/api/wit/work-items#create-a-work-item
2 - Change your agent phase to allow access to the token. Your task will need this.
3 - Change your task in Control Options and change the "Run this task" to "Custom condition" and fill the field with
eq(variables['Agent.JobStatus'], 'Failed')
Ref https://learn.microsoft.com/en-us/vsts/build-release/concepts/process/conditions?view=vsts
This should do it.

TFS API: Clone Build Definition Environment

I want to clone an existing environment within a build definition.
This is possible through the TFS GUI, but it doesn't seem like the API natively supports it.
So far, I've tried the following (in PowerShell):
Download build definition and deserialize
[void][System.Reflection.Assembly]::LoadFile("$pwd\Newtonsoft.Json.dll")
$TargetDefinitionName = "Name"
$TargetDefinitionID = ($DefinitionsOverview | Where-Object { $_.name -eq $TargetDefinitionName } | Select-Object -First 1).id
$TargetDefinitionURL = "$TfsUri/$TargetDefinitionID"
$TargetDefinitionJSON = Invoke-WebRequest -Uri "$TargetDefinitionURL" -UseDefaultCredentials
$DeserializedBuildDefinition = [Newtonsoft.Json.JsonConvert]::DeserializeObject($TargetDefinitionJSON.Content)
$DeserializedBuildDefinition.ToString()
Duplicate JSON block that represents environment, change unique properties
$NewEnvironmentString = $DeserializedBuildDefinition.environments[4].ToString()
$DeserializedBuildDefinition.environments.Add(5)
$DeserializedBuildDefinition.environments[5] = $NewEnvironmentString
$DeserializedBuildDefinition.environments[5].name.value = "NewEnvironment"
$DeserializedBuildDefinition.environments[5].rank.value = "6"
$DeserializedBuildDefinition.environments[5].id.value = "1665"
$DeserializedBuildDefinition.revision.value = "20"
Serialize JSON and post back (with new environment)
$SerializedBuildDefinition = [Newtonsoft.Json.JsonConvert]::SerializeObject($DeserializedBuildDefinition)
$PostData = [System.Text.Encoding]::UTF8.GetBytes($SerializedBuildDefinition)
$Headers = #{ "Accept" = "api-version=2.0-preview" }
$Response = Invoke-WebRequest -UseDefaultCredentials -Uri
$TargetDefinitionURL -Headers $Headers `
-Method Put -Body $PostData -ContentType "application/json"
$Response.StatusDescription
PROBLEM: $Response.StatusDescription gives "OK," but no new environment appears in the build definition.
One thought is that, in addition to 'name,' 'ID,' and 'rank, there are other values that need to be unique per environment that I'm missing.
I've also tried cloning the environment manually, saving the JSON representation of the definition, deleting the environment, and posting the JSON back. The new environment still doesn't show up.
Any help would be appreciated.
Future: Yaml based build definitions as code are well on their way, at least to VSTS. This won't help an immediate need but probably offers a better and more lightweight future approach.
When you copy an environment, you need to change "id", "name", and "rank". And the “id” need to be changed to "0". Also, try to use api-version=2.3-preview.1.
Then use the api of update a release definition to update the definition:
PUT http://tfs2015:8080/tfs/teamprojectcollection/teamProject/_apis/release/definitions?api-version=2.3-preview.1
{
……
}
I have tested with TFS 2015 Update 4. Api could clone an environment successfully.
I created a tool for cloning TFS builds, as (with TFS 2015 update 1 at least) the web 'clone' function does not clone the 'Repository' or 'Triggers' tab. Some of our build steps were a little complicated so I also wrote some simple transforms to automatically set the values according to the target branch.
I used the BuildHttpClient object in Microsoft.TeamFoundation.Build2.WebApi assembly to get the original build definition (BuildDefinition object, using GetDefinitionAsync() method), transform any build steps values, then call CreateDefinitionAsync() on BuildHttpClient to create the new build.
Works very reliably and under the hood it appears to use the TFS REST API.
If there was any interest, I'd be happy to create a more in depth tutorial on this.