Skip to content

Commit

Permalink
feat: added optional DaysToKeep parameter to Save-AzDevOpsBuild (#383)
Browse files Browse the repository at this point in the history
Co-authored-by: Stijn Moreels <[email protected]>
Co-authored-by: Pim Simons <[email protected]>
  • Loading branch information
3 people authored May 2, 2023
1 parent 6207b8b commit 2baf459
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 25 deletions.
23 changes: 18 additions & 5 deletions docs/preview/03-Features/powershell/azure-devops.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,18 +175,31 @@ Example of how to use this function in an Azure DevOps pipeline:

Saves/retains a specific Azure DevOps pipeline run.

| Parameter | Mandatory | Description |
| --------------- | --------- | ---------------------------------------------------------------------------|
| `ProjectId` | yes | The Id of the Project where the build that must be retained can be found |
| `BuildId` | yes | The Id of the build that must be retained |
| Parameter | Mandatory | Description |
| --------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `ProjectId` | yes | The Id of the project where the build that must be retained can be found |
| `BuildId` | yes | The Id of the build that must be retained |
| `DaysToKeep` | no | The number of days to keep the Azure DevOps pipeline run, if not supplied the Azure DevOps pipeline run will be saved indefinitely |

**Example**

Saving an Azure DevOps pipeline run indefinitely

```powershell
PS> Save-AzDevOpsBuild `
-ProjectId $(System.TeamProjectId) `
-BuildId $(Build.BuildId)
# Saved Azure DevOps build with build ID $BuildId in project $ProjectId
# Saved Azure DevOps build indefinitely with build ID $BuildId in project $ProjectId
```

Saving an Azure DevOps pipeline run for 10 days

```powershell
PS> Save-AzDevOpsBuild `
-ProjectId $(System.TeamProjectId) `
-BuildId $(Build.BuildId) `
-DaysToKeep 10
# Saved Azure DevOps build for 10 days with build ID $BuildId in project $ProjectId
```

> 💡 The variables $(System.TeamProjectId) and $(Build.BuildId) are predefined Azure DevOps variables. Information on them can be found here: https://docs.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml
Expand Down
5 changes: 3 additions & 2 deletions src/Arcus.Scripting.DevOps/Arcus.Scripting.DevOps.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,11 @@ Export-ModuleMember -Function Set-AzDevOpsArmOutputsToPipelineVariables
function Save-AzDevOpsBuild {
param(
[Parameter(Mandatory = $true)][string] $ProjectId = $(throw "ProjectId is required"),
[Parameter(Mandatory = $true)][string] $BuildId = $(throw "BuildId is required")
[Parameter(Mandatory = $true)][string] $BuildId = $(throw "BuildId is required"),
[Parameter(Mandatory = $false)][int] $DaysToKeep
)

. $PSScriptRoot\Scripts\Save-AzDevOpsBuild.ps1 -ProjectId $ProjectId -BuildId $BuildId
. $PSScriptRoot\Scripts\Save-AzDevOpsBuild.ps1 -ProjectId $ProjectId -BuildId $BuildId -DaysToKeep $DaysToKeep
}

Export-ModuleMember -Function Save-AzDevOpsBuild
29 changes: 19 additions & 10 deletions src/Arcus.Scripting.DevOps/Scripts/Save-AzDevOpsBuild.ps1
Original file line number Diff line number Diff line change
@@ -1,26 +1,35 @@
param(
[Parameter(Mandatory = $true)][string] $ProjectId = $(throw "ProjectId is required"),
[Parameter(Mandatory = $true)][string] $BuildId = $(throw "BuildId is required")
[Parameter(Mandatory = $true)][string] $BuildId = $(throw "BuildId is required"),
[Parameter(Mandatory = $false)][int] $DaysToKeep
)

$retentionPayload = @{
keepforever='true'
if ($DaysToKeep -eq '' -Or $DaysToKeep -eq 0) {
$daysValid = 99999
} else {
$daysValid = $DaysToKeep
}

$requestBody = $retentionPayload | ConvertTo-Json -Depth 1 -Compress
$retentionPayload = @{ daysValid = $daysValid; definitionId = $env:SYSTEM_DEFINITIONID; ownerId = "User:$env:BUILD_REQUESTEDFORID"; protectPipeline = $true; runId = $BuildId };
$requestBody = ConvertTo-Json @($retentionPayload);

$collectionUri = $env:SYSTEM_COLLECTIONURI
if ($collectionUri.EndsWith('/') -eq $false) {
$collectionUri = $collectionUri + '/'
$collectionUri = $collectionUri + '/'
}

$requestUri = "$collectionUri" + "$ProjectId/_apis/build/builds/" + $BuildId + "?api-version=6.0"
$urlEncodedProjectId = [uri]::EscapeDataString($ProjectId)
$requestUri = "$collectionUri" + "$urlEncodedProjectId/_apis/build/retention/leases?api-version=7.0"

Write-Verbose "Saving Azure DevOps build with build ID $BuildId in project $ProjectId by posting '$requestBody' to '$requestUri'..."
$response = Invoke-WebRequest -Uri $requestUri -Method Patch -Body $requestBody -ContentType "application/json" -Headers @{ Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN" }
Write-Verbose "Saving Azure DevOps build for $daysValid days with build ID $BuildId in project $ProjectId by posting '$requestBody' to '$requestUri'..."
$response = Invoke-WebRequest -Uri $requestUri -Method Post -Body $requestBody -ContentType "application/json" -Headers @{ Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN" }

if ($response.StatusCode -ne 200) {
throw "Unable to retain Azure DevOps build indefinetely with build ID $BuildId in project $ProjectId. API request returned statuscode $($response.StatusCode)"
throw "Unable to retain Azure DevOps build with build ID $BuildId in project $ProjectId. API request returned statuscode $($response.StatusCode)"
}

Write-Host "Saved Azure DevOps build with build ID $BuildId in project $ProjectId" -ForegroundColor Green
if ($DaysToKeep -eq '') {
Write-Host "Saved Azure DevOps build indefinitely with build ID $BuildId in project $ProjectId" -ForegroundColor Green
} else {
Write-Host "Saved Azure DevOps build for $DaysToKeep days with build ID $BuildId in project $ProjectId" -ForegroundColor Green
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@ InModuleScope Arcus.Scripting.DevOps {
& $PSScriptRoot\Connect-AzAccountFromConfig.ps1 -config $config
}
Context "Save Azure DevOps build" {
It "Saves the Azure DevOps build indefinetely" {
It "Saves the Azure DevOps build indefinitely" {
# Arrange
$projectId = $env:SYSTEM_TEAMPROJECTID
$buildId = $env:BUILD_BUILDID
$collectionUri = $env:SYSTEM_COLLECTIONURI
$requestUri = "$collectionUri" + "$projectId/_apis/build/builds/" + $buildId + "?api-version=6.0"
$requestUri = "$collectionUri" + "$projectId/_apis/build/builds/" + $buildId + "/leases?api-version=7.0"
$headers = @{ Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN" }
try {
# Act
Expand All @@ -21,12 +21,49 @@ InModuleScope Arcus.Scripting.DevOps {
# Assert
$getResponse = Invoke-WebRequest -Uri $requestUri -Method Get -Headers $headers
$json = ConvertFrom-Json $getResponse.Content
$json.keepForever | Should -Be $true
foreach ($lease in $json.value) {
$lease.protectPipeline | Should -Be $true
$date = Get-Date -Year 2200 -Month 1 -Day 1
$lease.validUntil | Should -BeGreaterThan $date
}
} finally {
$retentionPayload = @{ keepforever='false' }
$requestBody = $retentionPayload | ConvertTo-Json -Compress
$patchResponse = Invoke-WebRequest -Uri $requestUri -Method Patch -Headers $headers -Body $requestBody -ContentType "application/json"
$patchResponse.StatusCode | Should -Be 200
$getResponse = Invoke-WebRequest -Uri $requestUri -Method Get -Headers $headers
$json = ConvertFrom-Json $getResponse.Content
foreach ($lease in $json.value) {
$deleteUri = "$collectionUri" + "$projectId/_apis/build/retention/leases?ids=" + $lease.leaseId + "&api-version=7.0"
$deleteResponse = Invoke-WebRequest -Uri $deleteUri -Method Delete -Headers $headers
$deleteResponse.StatusCode | Should -Be 204
}
}
}
It "Saves the Azure DevOps build for 10 days" {
# Arrange
$projectId = $env:SYSTEM_TEAMPROJECTID
$buildId = $env:BUILD_BUILDID
$collectionUri = $env:SYSTEM_COLLECTIONURI
$requestUri = "$collectionUri" + "$projectId/_apis/build/builds/" + $buildId + "/leases?api-version=7.0"
$headers = @{ Authorization = "Bearer $env:SYSTEM_ACCESSTOKEN" }
try {
# Act
Save-AzDevOpsBuild -ProjectId $projectId -BuildId $buildId -DaysToKeep 10

# Assert
$getResponse = Invoke-WebRequest -Uri $requestUri -Method Get -Headers $headers
$json = ConvertFrom-Json $getResponse.Content
foreach ($lease in $json.value) {
$lease.protectPipeline | Should -Be $true
$expectedDate = (Get-Date).AddDays(10)
$actualDate = [DateTime]$lease.validUntil
$actualDate.ToUniversalTime().ToString("yyyy-MM-dd") | Should -Be $expectedDate.ToUniversalTime().ToString("yyyy-MM-dd")
}
} finally {
$getResponse = Invoke-WebRequest -Uri $requestUri -Method Get -Headers $headers
$json = ConvertFrom-Json $getResponse.Content
foreach ($lease in $json.value) {
$deleteUri = "$collectionUri" + "$projectId/_apis/build/retention/leases?ids=" + $lease.leaseId + "&api-version=7.0"
$deleteResponse = Invoke-WebRequest -Uri $deleteUri -Method Delete -Headers $headers
$deleteResponse.StatusCode | Should -Be 204
}
}
}
It "Sets the DevOps variable group description with the release name" -Skip {
Expand Down
18 changes: 17 additions & 1 deletion src/Arcus.Scripting.Tests.Unit/Arcus.Scripting.DevOps.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ InModuleScope Arcus.Scripting.DevOps {
# Act and Assert
{ Save-AzDevOpsBuild -ProjectId $projectId -BuildId $buildId } | Should -Throw
}
It "Save-AzDevOpsBuild succeeds when API call does return success-code" {
It "Save-AzDevOpsBuild indefinitely succeeds when API call does return success-code" {
# Arrange
$env:SYSTEM_COLLECTIONURI = "https://dev.azure.com/myorganization/"
$env:ACCESS_TOKEN = "mocking accesstoken"
Expand All @@ -221,6 +221,22 @@ InModuleScope Arcus.Scripting.DevOps {
# Act and Assert
{ Save-AzDevOpsBuild -ProjectId $projectId -BuildId $buildId } | Should -Not -Throw
}
It "Save-AzDevOpsBuild for 10 days succeeds when API call does return success-code" {
# Arrange
$env:SYSTEM_COLLECTIONURI = "https://dev.azure.com/myorganization/"
$env:ACCESS_TOKEN = "mocking accesstoken"
$projectId = "abc123"
$buildId = 128

Mock Invoke-WebRequest {
$statusCode = 200
$response = New-Object System.Net.Http.HttpResponseMessage $statusCode
return $response
} -ModuleName Arcus.Scripting.DevOps

# Act and Assert
{ Save-AzDevOpsBuild -ProjectId $projectId -BuildId $buildId -DaysToKeep 10} | Should -Not -Throw
}
It "Save-AzDevOpsBuild correctly builds API endpoint when CollectionUri has trailing slash" {
# Arrange
$env:SYSTEM_COLLECTIONURI = "https://dev.azure.com/myorganization/"
Expand Down

0 comments on commit 2baf459

Please sign in to comment.