Skip to content

Commit 6e85883

Browse files
Update common Docker engineering infrastructure with latest
1 parent b0f639a commit 6e85883

5 files changed

Lines changed: 177 additions & 2 deletions

File tree

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
#!/usr/bin/env pwsh
2+
# Lightweight wrapper for authenticated Azure DevOps REST API calls.
3+
# Uses `az account get-access-token` for bearer token auth.
4+
#
5+
# Usage:
6+
# . ./AzureDevOps.ps1
7+
# $response = Invoke-AzDORestMethod -Organization myorg -Project myproject `
8+
# -Endpoint "pipelines/42/runs" -Method POST -Body @{ resources = @{} }
9+
10+
$ErrorActionPreference = "Stop"
11+
12+
function Get-AzDOAccessToken {
13+
<#
14+
.SYNOPSIS
15+
Returns a bearer token for Azure DevOps.
16+
#>
17+
18+
# Well-known Entra ID application ID for Azure DevOps
19+
$tokenJson = az account get-access-token --resource "499b84ac-1321-427f-aa17-267ca6975798" 2>&1
20+
if ($LASTEXITCODE -ne 0) {
21+
throw "Failed to get access token. Run 'az login' first. Output: $tokenJson"
22+
}
23+
24+
$parsed = $tokenJson | ConvertFrom-Json
25+
return $parsed.accessToken
26+
}
27+
28+
function Invoke-AzDORestMethod {
29+
<#
30+
.SYNOPSIS
31+
Calls an Azure DevOps REST API endpoint with automatic authentication.
32+
.PARAMETER Organization
33+
Azure DevOps organization name (not the full URL).
34+
.PARAMETER Project
35+
Azure DevOps project name.
36+
.PARAMETER Endpoint
37+
API path after _apis/ (e.g. "pipelines/42/runs", "build/builds").
38+
.PARAMETER Method
39+
HTTP method. Defaults to GET.
40+
.PARAMETER Body
41+
Request body as a hashtable. Automatically converted to JSON.
42+
.PARAMETER ApiVersion
43+
API version. Defaults to 7.1.
44+
#>
45+
[CmdletBinding()]
46+
param(
47+
[Parameter(Mandatory)][string] $Organization,
48+
[Parameter(Mandatory)][string] $Project,
49+
[Parameter(Mandatory)][string] $Endpoint,
50+
[string] $Method = "GET",
51+
[hashtable] $Body,
52+
[string] $ApiVersion = "7.1"
53+
)
54+
55+
$token = Get-AzDOAccessToken
56+
$headers = @{
57+
Authorization = "Bearer $token"
58+
"Content-Type" = "application/json"
59+
}
60+
61+
$uri = "https://dev.azure.com/$Organization/$Project/_apis/$($Endpoint)?api-version=$ApiVersion"
62+
63+
$params = @{
64+
Uri = $uri
65+
Headers = $headers
66+
Method = $Method
67+
}
68+
69+
if ($Body) {
70+
$params.Body = $Body | ConvertTo-Json -Depth 10
71+
}
72+
73+
return Invoke-RestMethod @params
74+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/usr/bin/env pwsh
2+
# Retrieves a build log by log ID.
3+
# Usage:
4+
# ./Get-BuildLog.ps1 -Organization dnceng -Project internal -BuildId 12345 -LogId 47
5+
6+
[CmdletBinding()]
7+
param(
8+
[Parameter(Mandatory)][string] $Organization,
9+
[Parameter(Mandatory)][string] $Project,
10+
[Parameter(Mandatory)][int] $BuildId,
11+
[Parameter(Mandatory)][int] $LogId
12+
)
13+
14+
$ErrorActionPreference = "Stop"
15+
16+
. "$PSScriptRoot/AzureDevOps.ps1"
17+
18+
Invoke-AzDORestMethod `
19+
-Organization $Organization `
20+
-Project $Project `
21+
-Endpoint "build/builds/$BuildId/logs/$LogId"
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#!/usr/bin/env pwsh
2+
# Prints the build timeline as an indented tree with result indicators.
3+
# Usage:
4+
# ./Show-BuildTimeline.ps1 -Organization dnceng -Project internal -BuildId 12345
5+
# ./Show-BuildTimeline.ps1 -Organization dnceng -Project internal -BuildId 12345 -ShowAllTasks
6+
7+
[CmdletBinding()]
8+
param(
9+
[Parameter(Mandatory)][string] $Organization,
10+
[Parameter(Mandatory)][string] $Project,
11+
[Parameter(Mandatory)][int] $BuildId,
12+
[switch] $ShowAllTasks
13+
)
14+
15+
$ErrorActionPreference = "Stop"
16+
17+
. "$PSScriptRoot/AzureDevOps.ps1"
18+
19+
$build = Invoke-AzDORestMethod `
20+
-Organization $Organization `
21+
-Project $Project `
22+
-Endpoint "build/builds/$BuildId"
23+
24+
Write-Host "# Build $BuildId - $($build.definition.name)"
25+
Write-Host ""
26+
Write-Host "- Status: $($build.status) $(if ($build.result) { "($($build.result))" })"
27+
Write-Host "- Branch: $($build.sourceBranch)"
28+
Write-Host "- Queued: $($build.queueTime)"
29+
Write-Host "- URL: $($build._links.web.href)"
30+
Write-Host ""
31+
32+
$timeline = Invoke-AzDORestMethod `
33+
-Organization $Organization `
34+
-Project $Project `
35+
-Endpoint "build/builds/$BuildId/timeline"
36+
37+
$records = $timeline.records
38+
39+
# Build a lookup of children grouped by parentId
40+
$childrenOf = @{}
41+
foreach ($record in $records) {
42+
$parentId = $record.parentId
43+
if (-not $parentId) { $parentId = "" }
44+
if (-not $childrenOf.ContainsKey($parentId)) {
45+
$childrenOf[$parentId] = [System.Collections.Generic.List[object]]::new()
46+
}
47+
$childrenOf[$parentId].Add($record)
48+
}
49+
50+
# Sort children by order within each group
51+
foreach ($key in @($childrenOf.Keys)) {
52+
$childrenOf[$key] = $childrenOf[$key] | Sort-Object { $_.order }
53+
}
54+
55+
function Write-TimelineNode([string] $nodeId, [int] $depth) {
56+
$children = $childrenOf[$nodeId]
57+
if (-not $children) { return }
58+
59+
foreach ($child in $children) {
60+
$isTask = $child.type -eq "Task"
61+
$isFailing = $child.result -in @("failed", "canceled", "abandoned") -or $child.state -eq "inProgress"
62+
if ($isTask -and -not $ShowAllTasks -and -not $isFailing) { continue }
63+
64+
$indent = " " * $depth
65+
$status = if ($child.result) { $child.result } else { $child.state }
66+
67+
$logId = $child.log.id
68+
$logLabel = if ($logId) { " #$logId" } else { "" }
69+
Write-Host "${indent}- $($child.type)$logLabel | $($child.name) | $status"
70+
Write-TimelineNode $child.id ($depth + 1)
71+
}
72+
}
73+
74+
Write-Host "## Build Timeline"
75+
Write-Host ""
76+
Write-TimelineNode "" 0
77+
Write-Host ""

eng/docker-tools/templates/jobs/publish.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,9 @@ jobs:
241241
#
242242
# https://github.com/dotnet/docker-tools/issues/1698 tracks making this command no longer depend
243243
# on individual step displayNames.
244+
#
245+
# Skipped for PR builds because manifest lists are not created in PR builds (see post-build.yml).
246+
# Without manifest lists present in image-info.json, the postPublishNotification fails with a NRE.
244247
- script: >
245248
$(runImageBuilderCmd) postPublishNotification
246249
'$(publishNotificationRepoName)'
@@ -266,7 +269,7 @@ jobs:
266269
$(dryRunArg)
267270
$(imageBuilder.commonCmdArgs)
268271
displayName: Post Publish Notification
269-
condition: and(always(), eq(variables['publishNotificationsEnabled'], 'true'))
272+
condition: and(always(), eq(variables['publishNotificationsEnabled'], 'true'), ne(variables['Build.Reason'], 'PullRequest'))
270273
271274
- powershell: |
272275
# Default to current build number if parameter was not overridden

eng/docker-tools/templates/variables/docker-images.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
variables:
2-
imageNames.imageBuilderName: mcr.microsoft.com/dotnet-buildtools/image-builder:2936017
2+
imageNames.imageBuilderName: mcr.microsoft.com/dotnet-buildtools/image-builder:2939624
33
imageNames.imageBuilder: $(imageNames.imageBuilderName)
44
imageNames.imageBuilder.withrepo: imagebuilder-withrepo:$(Build.BuildId)-$(System.JobId)
55
imageNames.testRunner: mcr.microsoft.com/dotnet-buildtools/prereqs:azurelinux3.0-docker-testrunner

0 commit comments

Comments
 (0)