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
4 changes: 2 additions & 2 deletions .github/README-AI.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ Reusable skills in `.github/skills/` that agents can invoke:
- **`verify-tests-fail-without-fix/`** - Verifies UI tests catch bugs (auto-detects mode based on git diff)
- **`write-ui-tests/`** - Creates UI tests for issues following MAUI conventions
- **`write-xaml-tests/`** - Creates XAML unit tests for parsing, XamlC, and source generation issues
- **`pr-build-status/`** - Retrieves Azure DevOps build status for PRs
- **`azdo-build-investigator/`** - MAUI-specific CI investigation context (works with `ci-analysis` from arcade-skills plugin)

### Recent Improvements (January 2026)

Expand Down Expand Up @@ -365,7 +365,7 @@ For issues or questions about the AI agent instructions:

**Agent Files**:
- 4 agent files (pr.md, pr/post-gate.md, sandbox-agent.md, write-tests-agent.md)
- 5 skills (try-fix, verify-tests-fail-without-fix, write-ui-tests, write-xaml-tests, pr-build-status)
- 5 skills (try-fix, verify-tests-fail-without-fix, write-ui-tests, write-xaml-tests, azdo-build-investigator)
- All validated and consistent with consolidated structure

**Automation**:
Expand Down
5 changes: 0 additions & 5 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -312,11 +312,6 @@ Skills are modular capabilities that can be invoked directly or used by agents.
- **Two modes**: Verify failure only (test creation) or full verification (test + fix)
- **Used by**: After creating tests, before considering PR complete

8. **pr-build-status** (`.github/skills/pr-build-status/SKILL.md`)
- **Purpose**: Retrieves Azure DevOps build information for PRs (build IDs, stage status, failed jobs)
- **Trigger phrases**: "check build for PR #XXXXX", "why did PR build fail", "get build status"
- **Used by**: When investigating CI failures

8. **run-integration-tests** (`.github/skills/run-integration-tests/SKILL.md`)
- **Purpose**: Build, pack, and run .NET MAUI integration tests locally
- **Trigger phrases**: "run integration tests", "test templates locally", "run macOSTemplates tests", "run RunOniOS tests"
Expand Down
13 changes: 13 additions & 0 deletions .github/copilot/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"extraKnownMarketplaces": {
"dotnet-arcade-skills": {
"source": {
"source": "github",
"repo": "dotnet/arcade-skills"
}
}
},
"enabledPlugins": {
"dotnet-dnceng@dotnet-arcade-skills": true
}
}
22 changes: 22 additions & 0 deletions .github/scripts/BuildAndRunHostApp.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,28 @@ if ($Category) {
if ($Platform -eq "android") {
Write-Info "Clearing Android logcat buffer before test..."
& adb -s $DeviceUdid logcat -c

# Dismiss any ANR dialogs that may have appeared during build/deploy.
# The emulator can sit idle during long builds, causing SystemUI ANR.
Write-Info "Dismissing any system dialogs before test..."
& adb -s $DeviceUdid shell am broadcast -a android.intent.action.CLOSE_SYSTEM_DIALOGS 2>$null
& adb -s $DeviceUdid shell input keyevent KEYCODE_ENTER 2>$null
& adb -s $DeviceUdid shell input keyevent KEYCODE_BACK 2>$null
Start-Sleep -Seconds 1
& adb -s $DeviceUdid shell input keyevent KEYCODE_WAKEUP 2>$null
& adb -s $DeviceUdid shell input keyevent KEYCODE_MENU 2>$null
Start-Sleep -Seconds 1

# Check for lingering ANR dialogs via window dump
$windowDump = & adb -s $DeviceUdid shell dumpsys window 2>$null | Select-String "Application Not Responding|ANR"
if ($windowDump) {
Write-Warn "ANR dialog detected — force-dismissing..."
& adb -s $DeviceUdid shell input keyevent KEYCODE_HOME 2>$null
Start-Sleep -Seconds 2
& adb -s $DeviceUdid shell am broadcast -a android.intent.action.CLOSE_SYSTEM_DIALOGS 2>$null
& adb -s $DeviceUdid shell input keyevent KEYCODE_BACK 2>$null
Start-Sleep -Seconds 1
}
}

# Capture test start time for iOS logs
Expand Down
8 changes: 5 additions & 3 deletions .github/scripts/Review-PR.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -131,12 +131,14 @@ if (-not $ghVersion) {
}
Write-Host " ✅ GitHub CLI: $ghVersion" -ForegroundColor Green

# Check Copilot CLI
$copilotVersion = copilot --version 2>$null
if (-not $copilotVersion) {
# Check Copilot CLI - use Get-Command (reliable) then get version with merged streams
$copilotCmd = Get-Command copilot -ErrorAction SilentlyContinue
if (-not $copilotCmd) {
Write-Error "Copilot CLI is not installed. Install with: npm install -g @github/copilot"
exit 1
}
$copilotVersion = (& copilot --version 2>&1 | Out-String).Trim()
if (-not $copilotVersion) { $copilotVersion = $copilotCmd.Source }
Write-Host " ✅ Copilot CLI: $copilotVersion" -ForegroundColor Green

# Check PR exists
Expand Down
49 changes: 43 additions & 6 deletions .github/scripts/shared/Build-AndDeploy.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -85,19 +85,56 @@ if ($Platform -eq "android") {
Write-Info "Build command: dotnet build $($buildArgs -join ' ')"

$buildStartTime = Get-Date
$maxAttempts = 2
$buildExitCode = 1

for ($attempt = 1; $attempt -le $maxAttempts; $attempt++) {
if ($attempt -gt 1) {
Write-Warn "Retrying build/deploy (attempt $attempt of $maxAttempts)..."

# Uninstall any MAUI test packages to clear bad state
$installedPkg = & adb shell pm list packages 2>$null | Select-String "maui" | ForEach-Object { ($_ -replace "package:", "").Trim() }
if ($installedPkg) {
foreach ($pkg in $installedPkg) {
Write-Info "Uninstalling $pkg before retry..."
& adb uninstall $pkg 2>$null
}
}

# Restart ADB server to recover from broken pipe / transient errors
Write-Info "Restarting ADB server..."
& adb kill-server 2>$null
Start-Sleep -Seconds 2
& adb start-server
Start-Sleep -Seconds 2
& adb wait-for-device
Start-Sleep -Seconds 3
}

& dotnet build @buildArgs
$buildExitCode = $LASTEXITCODE

if ($buildExitCode -eq 0) {
break
}

if ($attempt -lt $maxAttempts) {
Write-Warn "Build/deploy failed (attempt $attempt). ADB0010/broken-pipe errors are transient on API 30 — will retry."
}
}

# Build and deploy in one step (Run target handles both)
& dotnet build @buildArgs

$buildExitCode = $LASTEXITCODE
$buildDuration = (Get-Date) - $buildStartTime

if ($buildExitCode -ne 0) {
Write-Error "Build/deploy failed with exit code $buildExitCode"
Write-Error "Build/deploy failed after $maxAttempts attempts with exit code $buildExitCode"
exit $buildExitCode
}

Write-Success "Build and deploy completed in $($buildDuration.TotalSeconds) seconds"
if ($attempt -gt 1) {
Write-Success "Build and deploy succeeded on attempt $attempt in $($buildDuration.TotalSeconds) seconds"
} else {
Write-Success "Build and deploy completed in $($buildDuration.TotalSeconds) seconds"
}

#endregion

Expand Down
14 changes: 8 additions & 6 deletions .github/scripts/shared/Start-Emulator.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ if ($Platform -eq "android") {
# Check if DeviceUdid is an AVD name (not an emulator-XXXX format)
if ($DeviceUdid -and $DeviceUdid -notmatch "^emulator-\d+$") {
# DeviceUdid is likely an AVD name - check if it's in the AVD list
$avdList = emulator -list-avds 2>$null
# Force array output - single AVD returns a string which breaks -contains
[string[]]$avdList = @(emulator -list-avds 2>$null)
if ($avdList -contains $DeviceUdid) {
Write-Info "DeviceUdid '$DeviceUdid' is an AVD name. Will boot this emulator..."
$selectedAvd = $DeviceUdid
Expand Down Expand Up @@ -103,7 +104,8 @@ if ($Platform -eq "android") {

# Get list of available AVDs (if not already set from parameter)
if (-not $selectedAvd) {
$avdList = emulator -list-avds 2>$null
# Force array output - single AVD returns a string which breaks indexing
[string[]]$avdList = @(emulator -list-avds 2>$null)

if (-not $avdList -or $avdList.Count -eq 0) {
Write-Error "No Android emulators found. Please create an Android Virtual Device (AVD) using Android Studio."
Expand All @@ -119,7 +121,7 @@ if ($Platform -eq "android") {
# Selection priority:
# 1. API 34 device (matches CI provisioning)
# 2. API 30 Nexus device
# 3. Any API 30 device
# 3. Any API 30 device (matches names like "Emulator_30", "API_30_xxx", etc.)
# 4. Any Nexus device
# 5. First available device

Expand All @@ -132,16 +134,16 @@ if ($Platform -eq "android") {

# Try to find API 30 Nexus device
if (-not $selectedAvd) {
$api30Nexus = $avdList | Where-Object { $_ -match "API.*30" -and $_ -match "Nexus" } | Select-Object -First 1
$api30Nexus = $avdList | Where-Object { $_ -match "30" -and $_ -match "Nexus" } | Select-Object -First 1
if ($api30Nexus) {
$selectedAvd = $api30Nexus
Write-Info "Selected API 30 Nexus device: $selectedAvd"
}
}

# Try to find any API 30 device
# Try to find any API 30 device (match "30" anywhere in name)
if (-not $selectedAvd) {
$api30Device = $avdList | Where-Object { $_ -match "API.*30" } | Select-Object -First 1
$api30Device = $avdList | Where-Object { $_ -match "30" } | Select-Object -First 1
if ($api30Device) {
$selectedAvd = $api30Device
Write-Info "Selected API 30 device: $selectedAvd"
Expand Down
71 changes: 71 additions & 0 deletions .github/skills/azdo-build-investigator/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
---
name: azdo-build-investigator
description: "Investigate CI failures for dotnet/maui PRs — build errors, Helix test logs, and binlog analysis. Use when asked about failing checks, CI status, test failures, 'why is CI red', 'build failed', 'what's failing on PR', Helix failures, or device test failures."
metadata:
author: dotnet-maui
version: "2.0"
---

# dotnet/maui CI Investigation Context

This skill provides MAUI-specific context for CI investigation. Use it together with the `ci-analysis` skill (loaded from the `dotnet-dnceng@dotnet-arcade-skills` plugin via `.github/copilot/settings.json`).

> **First**: invoke the `ci-analysis` skill — it handles the core investigation workflow using `Get-CIStatus.ps1` and `gh` CLI (with MCP tools as optional enhancements if available). This skill provides MAUI-specific corrections and context on top of that.

## Script Location

The `ci-analysis` skill and its `Get-CIStatus.ps1` script are loaded automatically from the `dotnet/arcade-skills` plugin (configured in `.github/copilot/settings.json` via `enabledPlugins`). The CLI caches scripts to `~/.copilot/installed-plugins/dotnet-arcade-skills/`. No manual download is needed.

## MAUI CI Pipelines

> ⚠️ The `ci-analysis` skill's reference doc lists `maui-public` as the MAUI pipeline — **this is outdated**. The correct pipeline names are below.

| Pipeline Name | Definition ID | Purpose |
|---------------|---------------|---------|
| `maui-pr` | **302** | Main build — check this first |
| `maui-pr-devicetests` | **314** | Helix device tests (iOS, Android, Windows, MacCatalyst) |
| `maui-pr-uitests` | **313** | Appium-based UI tests |

**Organization**: `dnceng-public` / project `public`

**Investigation priority order**: `maui-pr` → `maui-pr-devicetests` → `maui-pr-uitests`

Most failures are in `maui-pr`. Device test failures appear in `maui-pr-devicetests`. Focus on the first failing pipeline before checking others.

## MAUI-Specific Quirks

### XHarness Exit-0 Blind Spot

XHarness (used for iOS/Android device tests in `maui-pr-devicetests`) **exits with code 0 even when tests fail**. This means:
- The ADO job shows ✅ "Succeeded"
- `ci-analysis` may report no failures
- But actual test failures are hidden inside the Helix work items

**How to detect hidden test failures**: Query the `ResultSummaryByBuild` Helix API endpoint:
```
GET https://helix.dot.net/api/2019-06-17/jobs/{correlationId}/aggregated
```
Look for `Failed` > 0 in the response even when the ADO build job shows green.

When `ci-analysis` reports a `maui-pr-devicetests` build as passing but the PR has a `s/agent-gate-failed` label or the user suspects device test failures, always cross-check Helix `ResultSummaryByBuild`.

### Container Artifact Binlogs

MAUI build artifacts are **Container type**, not `PipelineArtifact`. This means:
- `az pipelines runs artifact download` does **not** work for binlogs
- Artifact names are like `Windows_NT_Build Windows (Debug)_Attempt1` (not `binlog`)
- Download requires a Bearer token from `az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798`
- Use the ADO File Container API: `/_apis/resources/Containers/{id}?api-version=5.0-preview&$format=OctetStream`

If available, use the `mcp-binlog-tool` MCP server to analyze downloaded `.binlog` files. This is optional — the core investigation workflow works without it via `gh` CLI and REST APIs.

## Common MAUI Failure Patterns

| Pattern | Where | Notes |
|---------|-------|-------|
| `error CS####` | `maui-pr` | C# compiler error — check file/line |
| `error XA####` | `maui-pr` | Android build error |
| `XamlC` | `maui-pr` | XAML compiler — usually missing type or bad binding |
| `XHarness timeout` | `maui-pr-devicetests` Helix logs | Test killed by infrastructure; may be transient |
| `No test result files found` | `maui-pr-devicetests` Helix logs | Tests never ran or app crashed on launch |
| UI test screenshot diff | `maui-pr-uitests` | Visual regression; check baseline images |
114 changes: 0 additions & 114 deletions .github/skills/pr-build-status/SKILL.md

This file was deleted.

Loading
Loading