In my last blog post, I described retention policies in the TFS 2017 build system. I described how different it is from the the retention policies we get in XAML build system.
One of the limitations I found in the new style retention policy is that I couldn't retain a specific number of builds for each status. We needed to do it for some builds that are triggered very frequently (once every couple of minutes) and check if there is some work to be done. If it found work, it would do it, otherwise it will reschedule a build for itself after a couple of minutes. Another scenario, where you might have a lot of builds is when you it is triggered by a commit of a very busy repository.
So, in order for us to retain only "N" builds for each status, we created a Powershell Module to clean up builds. In the module, we create a command-let that takes as parameter the name of the build, the number of builds to keep, the result filter and tag filter. Our command-let looks as following
We create a PowerShell Module file for the above command let. To set up the Powershell module, we modified the PSModulePath environment variable as first step of our build to include the module path. Then to set-it all up we added a PowerShell task group calling the Cleanup-Builds command in an inline script as shown below
Our build definition looks like below
One of the limitations I found in the new style retention policy is that I couldn't retain a specific number of builds for each status. We needed to do it for some builds that are triggered very frequently (once every couple of minutes) and check if there is some work to be done. If it found work, it would do it, otherwise it will reschedule a build for itself after a couple of minutes. Another scenario, where you might have a lot of builds is when you it is triggered by a commit of a very busy repository.
So, in order for us to retain only "N" builds for each status, we created a Powershell Module to clean up builds. In the module, we create a command-let that takes as parameter the name of the build, the number of builds to keep, the result filter and tag filter. Our command-let looks as following
***************************************************
.SYNOPSISCleans up all builds for the given build definition keeping the latest N number of builds where n is passed a parameterIf a status is provided, it would only keep N builds with the given status.DESCRIPTIONPATCH https://{instance}/DefaultCollection/{project}/_apis/build/builds/{buildId}?api-version={version}Uses api-version 2.0 to update the build result***************************************************function Cleanup-Builds([string] $tfsCollection,[string] $tfsProject,[string] $buildDefinitionName,[int] $numberOfBuildsToKeep = 10,[string] $result="",[string] $tagsFilter = ""){if (${env:system.debug} -eq $true) {$VerbosePreference="Continue"}if ($status -eq ""){Write-Verbose "Deleting all but the latest $numberOfBuildsToKeep builds for definition $buildDefinitionName."}else{Write-Verbose "Deleting all but the latest $numberOfBuildsToKeep builds for definition $buildDefinitionName with status $status."}$buildDefinition = Find-BuildDefinition($buildDefinitionName)if ($buildDefinition -eq $null) {Write-Error "No build definition found $buildDefinitionName"return}$buildDefinitionId = $buildDefinition.id$query = [uri]::EscapeUriString("$tfsCollection$tfsProject/_apis/build/builds?api-version=2.0&definitions=$buildDefinitionId&queryOrder=2&resultFilter=$result&tagFilters=$tagsFilter&`$top=5000")$builds = Invoke-RestMethod -Method GET -UseDefaultCredentials -ContentType "application/json" -Uri $query$retainedBuild = 0$deletedBuildCount = 0for ($i = $builds.Count - 1; $i -gt -1; $i--) {$build = $builds.value[$i]$buildId = $build.id$buildNumber = $build.buildNumbertry {$query = [uri]::EscapeUriString("$tfsCollection$tfsProject/_apis/build/builds/$buildId/tags?api-version=2.0")$tagFound = $false# Not delete the latest numberOfBuildsToKeep buildsif ( ($retainedBuild -lt $numberOfBuildsToKeep)) {$retainedBuild = $retainedBuild + 1}else {Write-Verbose "Deleting build $buildNumber"$query = [uri]::EscapeUriString("$tfsCollection$tfsProject/_apis/build/builds/$buildId`?api-version=2.0")Invoke-RestMethod -Method DELETE -UseDefaultCredentials -ContentType "application/json" -Uri $query$deletedBuildCount = $deletedBuildCount + 1}}catch {Write-Error "StatusCode:" + $_.Exception.Response.StatusCode.value__ +"`r`nStatusDescription:" + $_.Exception.Response.StatusDescription}}Write-Output "Deleted $deletedBuildCount builds for build definition $buildDefinitionName"}
We create a PowerShell Module file for the above command let. To set up the Powershell module, we modified the PSModulePath environment variable as first step of our build to include the module path. Then to set-it all up we added a PowerShell task group calling the Cleanup-Builds command in an inline script as shown below
Our build definition looks like below