Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 78 additions & 21 deletions eng/scripts/get-maui-pr.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ function Get-PullRequestInfo {
Title = $pr.title
State = $pr.state
SHA = $pr.head.sha
MergeSHA = $pr.merge_commit_sha
Ref = $pr.head.ref
}
}
Expand Down Expand Up @@ -166,9 +167,27 @@ function Test-BuildInProgress {
}
}

function Test-BuildMatchesPullRequestCommit {
param(
[Parameter(Mandatory = $true)]
$Build,

[Parameter(Mandatory = $true)]
[string]$HeadSha,

[string]$MergeSha
)

$sourceSha = if ($Build.triggerInfo) { $Build.triggerInfo.'pr.sourceSha' } else { $null }

return $sourceSha -eq $HeadSha -or
$Build.sourceVersion -eq $HeadSha -or
((-not [string]::IsNullOrEmpty($MergeSha)) -and $Build.sourceVersion -eq $MergeSha)
}

# Get build information from GitHub Checks API, with AzDO fallback
function Get-BuildInfo {
param([string]$SHA, [int]$PrNumber)
param([string]$SHA, [string]$MergeSHA, [int]$PrNumber)

Write-Info "Looking for build artifacts for commit $($SHA.Substring(0, 7))..."

Expand All @@ -179,20 +198,16 @@ function Get-BuildInfo {
"Accept" = "application/vnd.github.v3+json"
}) -TimeoutSec 30

# Look for the main MAUI build check (not uitests)
# Look for the aggregate MAUI PR build check. Job-level checks share the
# maui-pr prefix and can point at the same build with a different result.
$buildCheck = $response.check_runs | Where-Object {
$_.name -like "maui-pr*" -and $_.name -notlike "*uitests*" -and $_.status -eq "completed" -and $_.details_url -match 'buildId='
$_.name -eq "maui-pr" -and $_.status -eq "completed" -and $_.details_url -match 'buildId='
} | Select-Object -First 1

if ($buildCheck) {
if ($buildCheck.conclusion -ne "success") {
Write-Warn "Build completed with status: $($buildCheck.conclusion)"
if (-not $Yes) {
$continue = Read-Host "Do you want to continue anyway? (y/N)"
if ($continue -ne "y" -and $continue -ne "Y") {
throw "Build was not successful. Aborting."
}
}
Write-Warn "The aggregate maui-pr build completed with status: $($buildCheck.conclusion)"
Write-Warn "Continuing because PackageArtifacts may still be available when unrelated CI legs fail."
}

# Extract build ID from details URL
Expand All @@ -213,12 +228,23 @@ function Get-BuildInfo {
# Strategy 2: Query Azure DevOps directly (handles merge commits not reported to GitHub)
Write-Info "Searching Azure DevOps directly for PR #$PrNumber builds..."
try {
$buildsUrl = "https://dev.azure.com/$AzureDevOpsOrg/$AzureDevOpsProject/_apis/build/builds?api-version=7.1&branchName=refs/pull/$PrNumber/merge&`$top=10"
$buildsUrl = "https://dev.azure.com/$AzureDevOpsOrg/$AzureDevOpsProject/_apis/build/builds?api-version=7.1&branchName=refs/pull/$PrNumber/merge&`$top=25"
$response = Invoke-RestMethod -Uri $buildsUrl -Headers @{ "User-Agent" = "MAUI-PR-Script" } -TimeoutSec 30

$completedBuild = $response.value | Where-Object {
$_.definition.name -eq "maui-pr" -and $_.status -eq "completed"
$_.definition.name -eq "maui-pr" -and
$_.status -eq "completed" -and
(Test-BuildMatchesPullRequestCommit -Build $_ -HeadSha $SHA -MergeSha $MergeSHA)
} | Select-Object -First 1

if (-not $completedBuild) {
$olderCompletedBuild = $response.value | Where-Object {
$_.definition.name -eq "maui-pr" -and $_.status -eq "completed"
} | Select-Object -First 1
if ($olderCompletedBuild) {
Write-Warn "Found completed maui-pr builds for PR #$PrNumber, but none match the current head/merge commit."
}
}

if ($completedBuild) {
# Validate build ID is numeric
Expand All @@ -236,13 +262,8 @@ function Get-BuildInfo {
}

if ($completedBuild.result -ne "succeeded") {
Write-Warn "Build completed with result: $($completedBuild.result)"
if (-not $Yes) {
$continue = Read-Host "Do you want to continue anyway? (y/N)"
if ($continue -ne "y" -and $continue -ne "Y") {
throw "Build was not successful. Aborting."
}
}
Write-Warn "The aggregate maui-pr build completed with result: $($completedBuild.result)"
Write-Warn "Continuing because PackageArtifacts may still be available when unrelated CI legs fail."
}

$buildUrl = "https://dev.azure.com/$AzureDevOpsOrg/$AzureDevOpsProject/_build/results?buildId=$($completedBuild.id)"
Expand All @@ -267,6 +288,40 @@ function Get-BuildInfo {
throw "No completed build found for PR #$PrNumber. The PR may not have triggered CI builds yet (draft PRs don't auto-trigger builds), or the build may have failed. Check: https://github.com/dotnet/maui/pull/$PrNumber"
}

function Write-PackJobStatus {
param([string]$BuildId)

try {
$timelineUrl = "https://dev.azure.com/$AzureDevOpsOrg/$AzureDevOpsProject/_apis/build/builds/$BuildId/timeline?api-version=7.1"
$response = Invoke-RestMethod -Uri $timelineUrl -Headers @{ "User-Agent" = "MAUI-PR-Script" } -TimeoutSec 30

$packRecords = $response.records | Where-Object {
($_.type -eq "Job" -or $_.type -eq "Phase") -and
($_.name -eq "Pack macOS" -or $_.name -eq "Pack Windows")
}

if (-not $packRecords) {
Write-Warn "Could not verify pack job status from the Azure DevOps timeline."
return
}

$nonSucceededPackRecords = $packRecords | Where-Object { $_.result -ne "succeeded" }
if ($nonSucceededPackRecords) {
Write-Warn "PackageArtifacts exists, but one or more pack/package-producing jobs did not report success:"
foreach ($record in $nonSucceededPackRecords) {
$result = if ($record.result) { $record.result } elseif ($record.state) { $record.state } else { "unknown" }
Write-Warn " $($record.name) ($($record.type)): $result"
}
}
else {
Write-Info "Verified pack/package-producing jobs succeeded."
}
}
catch {
Write-Warn "Could not verify pack job status: $_"
}
}

# Get artifacts from Azure DevOps
function Get-BuildArtifacts {
param([string]$BuildId)
Expand All @@ -283,6 +338,8 @@ function Get-BuildArtifacts {
if (-not $artifact) {
throw "No 'PackageArtifacts' artifact found in build $BuildId"
}

Write-PackJobStatus -BuildId $BuildId

return $artifact.resource.downloadUrl
}
Expand Down Expand Up @@ -542,7 +599,7 @@ try {
Write-Info "Current target framework: .NET $targetNetVersion.0"

Write-Step "Finding build artifacts"
$buildInfo = Get-BuildInfo -SHA $prInfo.SHA -PrNumber $PrNumber
$buildInfo = Get-BuildInfo -SHA $prInfo.SHA -MergeSHA $prInfo.MergeSHA -PrNumber $PrNumber

Write-Step "Downloading artifacts"
$downloadUrl = Get-BuildArtifacts -BuildId $buildInfo.BuildId
Expand Down Expand Up @@ -684,7 +741,7 @@ catch {
Write-Info "Troubleshooting tips:"
Write-Host " • Make sure you're in a directory containing a .NET MAUI project" -ForegroundColor Gray
Write-Host " • Verify that PR #$PrNumber exists: https://github.com/dotnet/maui/pull/$PrNumber" -ForegroundColor Gray
Write-Host " • Check if there's a completed build for this PR (look for green checkmarks)" -ForegroundColor Gray
Write-Host " • Check if there's a completed maui-pr build with PackageArtifacts for this PR" -ForegroundColor Gray
Write-Host " • Check your internet connection" -ForegroundColor Gray
Write-Host " • Visit: https://github.com/dotnet/maui/wiki/Testing-PR-Builds" -ForegroundColor Gray
exit 1
Expand Down
77 changes: 49 additions & 28 deletions eng/scripts/get-maui-pr.sh
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ handle_error() {
info "Troubleshooting tips:"
echo " • Make sure you're in a directory containing a .NET MAUI project"
echo " • Verify that PR #${pr_number:-NUMBER} exists: https://github.com/dotnet/maui/pull/${pr_number:-NUMBER}"
echo " • Check if there's a completed build for this PR (look for green checkmarks)"
echo " • Check if there's a completed maui-pr build with PackageArtifacts for this PR"
echo " • Check your internet connection"
echo " • Visit: https://github.com/dotnet/maui/wiki/Testing-PR-Builds"
exit $exit_code
Expand Down Expand Up @@ -200,6 +200,7 @@ check_build_in_progress() {
get_build_info() {
local sha="$1"
local pr_num="$2"
local merge_sha="${3:-}"

info "Looking for build artifacts for commit ${sha:0:7}..."

Expand All @@ -208,23 +209,15 @@ get_build_info() {
local checks_json
checks_json=$(curl -s -H "User-Agent: MAUI-PR-Script" -H "Accept: application/vnd.github.v3+json" ${GITHUB_AUTH_HEADER:+-H "$GITHUB_AUTH_HEADER"} "$checks_url")

# Find the main MAUI build check (not uitests)
local build_check=$(echo "$checks_json" | jq -r '.check_runs[] | select((.name | startswith("maui-pr")) and (.name | contains("uitests") | not) and .status == "completed" and (.details_url | contains("buildId="))) | @json' | head -n 1)
# Find the aggregate MAUI PR build check. Job-level checks share the
# maui-pr prefix and can point at the same build with a different result.
local build_check=$(echo "$checks_json" | jq -r '.check_runs[]? | select(.name == "maui-pr" and .status == "completed" and (.details_url | contains("buildId="))) | @json' | head -n 1)

if [ -n "$build_check" ] && [ "$build_check" != "null" ]; then
local conclusion=$(echo "$build_check" | jq -r '.conclusion')
if [ "$conclusion" != "success" ]; then
warning "Build completed with status: $conclusion"
if [ "$YES_FLAG" = true ]; then
info "Auto-accepting non-successful build (-y flag)"
else
read -p "Do you want to continue anyway? (y/N) " -n 1 -r
echo >&2
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
error "Build was not successful. Aborting."
exit 1
fi
fi
warning "The aggregate maui-pr build completed with status: $conclusion"
warning "Continuing because PackageArtifacts may still be available when unrelated CI legs fail."
fi

# Extract build ID from details URL
Expand All @@ -239,13 +232,17 @@ get_build_info() {

# Strategy 2: Query Azure DevOps directly (handles merge commits not reported to GitHub)
info "Searching Azure DevOps directly for PR #$pr_num builds..."
local builds_url="https://dev.azure.com/$AZURE_DEVOPS_ORG/$AZURE_DEVOPS_PROJECT/_apis/build/builds?api-version=7.1&branchName=refs/pull/$pr_num/merge&\$top=10"
local builds_url="https://dev.azure.com/$AZURE_DEVOPS_ORG/$AZURE_DEVOPS_PROJECT/_apis/build/builds?api-version=7.1&branchName=refs/pull/$pr_num/merge&\$top=25"
local builds_json
builds_json=$(curl -s -H "User-Agent: MAUI-PR-Script" "$builds_url" 2>/dev/null)

if [ -n "$builds_json" ]; then
local completed_build
completed_build=$(echo "$builds_json" | jq -r '[.value[] | select(.definition.name == "maui-pr" and .status == "completed")] | first | @json' 2>/dev/null || echo "")
completed_build=$(echo "$builds_json" | jq -r --arg head "$sha" --arg merge "$merge_sha" '[.value[] | select(.definition.name == "maui-pr" and .status == "completed" and (((.triggerInfo["pr.sourceSha"] // "") == $head) or ((.sourceVersion // "") == $head) or ($merge != "" and (.sourceVersion // "") == $merge)))] | first | @json' 2>/dev/null || echo "")

if { [ -z "$completed_build" ] || [ "$completed_build" == "null" ]; } && echo "$builds_json" | jq -e '[.value[] | select(.definition.name == "maui-pr" and .status == "completed")] | length > 0' >/dev/null 2>&1; then
warning "Found completed maui-pr builds for PR #$pr_num, but none match the current head/merge commit."
fi

if [ -n "$completed_build" ] && [ "$completed_build" != "null" ]; then
local azdo_build_id=$(echo "$completed_build" | jq -r '.id')
Expand All @@ -266,17 +263,8 @@ get_build_info() {
fi

if [ "$azdo_result" != "succeeded" ]; then
warning "Build completed with result: $azdo_result"
if [ "$YES_FLAG" = true ]; then
info "Auto-accepting non-successful build (-y flag)"
else
read -p "Do you want to continue anyway? (y/N) " -n 1 -r
echo >&2
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
error "Build was not successful. Aborting."
exit 1
fi
fi
warning "The aggregate maui-pr build completed with result: $azdo_result"
warning "Continuing because PackageArtifacts may still be available when unrelated CI legs fail."
fi

success "Found build ID: $azdo_build_id (via Azure DevOps)"
Expand All @@ -299,6 +287,35 @@ get_build_info() {
exit 1
}

check_pack_job_status() {
local build_id="$1"
local timeline_url="https://dev.azure.com/$AZURE_DEVOPS_ORG/$AZURE_DEVOPS_PROJECT/_apis/build/builds/$build_id/timeline?api-version=7.1"
local timeline_json

if ! timeline_json=$(curl -s -H "User-Agent: MAUI-PR-Script" "$timeline_url" 2>/dev/null); then
warning "Could not verify pack job status from the Azure DevOps timeline."
return 0
fi

local pack_count
pack_count=$(echo "$timeline_json" | jq -r '[.records[]? | select((.type == "Job" or .type == "Phase") and (.name == "Pack macOS" or .name == "Pack Windows"))] | length' 2>/dev/null || echo "0")
if [ "$pack_count" == "0" ] || [ -z "$pack_count" ]; then
warning "Could not verify pack job status from the Azure DevOps timeline."
return 0
fi

local non_succeeded
non_succeeded=$(echo "$timeline_json" | jq -r '.records[]? | select((.type == "Job" or .type == "Phase") and (.name == "Pack macOS" or .name == "Pack Windows") and .result != "succeeded") | "\(.name) (\(.type)): \(.result // .state // "unknown")"' 2>/dev/null || echo "")
if [ -n "$non_succeeded" ]; then
warning "PackageArtifacts exists, but one or more pack/package-producing jobs did not report success:"
while IFS= read -r record; do
[ -n "$record" ] && warning " $record"
done <<< "$non_succeeded"
else
info "Verified pack/package-producing jobs succeeded."
fi
}

# Get artifacts from Azure DevOps
get_build_artifacts() {
local build_id="$1"
Expand All @@ -315,6 +332,8 @@ get_build_artifacts() {
error "No 'PackageArtifacts' artifact found in build $build_id"
exit 1
fi

check_pack_job_status "$build_id"

echo "$download_url"
}
Expand Down Expand Up @@ -567,6 +586,8 @@ EOF
pr_state=$(echo "$pr_json" | jq -r '.state')
local pr_sha
pr_sha=$(echo "$pr_json" | jq -r '.head.sha')
local pr_merge_sha
pr_merge_sha=$(echo "$pr_json" | jq -r '.merge_commit_sha // ""')

info "PR #$pr_number: $pr_title"
info "State: $pr_state"
Expand All @@ -578,7 +599,7 @@ EOF

step "Finding build artifacts"
local build_id
build_id=$(get_build_info "$pr_sha" "$pr_number")
build_id=$(get_build_info "$pr_sha" "$pr_number" "$pr_merge_sha")

step "Downloading artifacts"
local download_url
Expand Down
Loading