From ef1f3f6c8a665eefa862f36aa71030426d7c2cce Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Mon, 9 Feb 2026 12:27:05 -0600 Subject: [PATCH 1/5] ci-analysis: Add Azure CLI guidance for AzDO pipeline investigation - Add 'Deep Investigation with Azure CLI' section with auth checking - Add pipeline definition and build querying examples using az CLI - Add guidance to examine pipeline YAML in dotnet/dotnet - Prefer az CLI commands over manual REST API construction - Add tip #8 about verifying az auth before REST calls --- .github/skills/ci-analysis/SKILL.md | 91 +++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/.github/skills/ci-analysis/SKILL.md b/.github/skills/ci-analysis/SKILL.md index 5c3686b054256b..ca15ccd2843fd0 100644 --- a/.github/skills/ci-analysis/SKILL.md +++ b/.github/skills/ci-analysis/SKILL.md @@ -172,6 +172,96 @@ Canceled jobs (typically from timeouts) often still have useful artifacts. The H **Key insight**: "Canceled" ≠ "Failed". Always check artifacts before concluding results are lost. +## Deep Investigation with Azure CLI + +When the script and GitHub APIs aren't enough (e.g., investigating internal pipeline definitions, downloading build artifacts, querying build timelines directly), you can use the Azure CLI with the `azure-devops` extension. + +> 💡 **Prefer `az pipelines` / `az devops` commands over raw REST API calls.** The CLI handles authentication, pagination, and JSON output formatting. Only fall back to manual `Invoke-RestMethod` calls when the CLI doesn't expose the endpoint you need (e.g., artifact download URLs, specialized timeline queries). The CLI's `--query` (JMESPath) and `-o table` flags are powerful for filtering without extra scripting. + +### Checking Azure CLI Authentication + +Before making direct AzDO API calls, verify the CLI is installed and authenticated: + +```powershell +# Ensure az is on PATH (Windows may need a refresh after install) +$env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path", "User") + +# Check if az CLI is available +az --version 2>$null | Select-Object -First 1 + +# Check if logged in and get current account +az account show --query "{name:name, user:user.name}" -o table 2>$null + +# If not logged in, prompt the user to authenticate: +# az login # Interactive browser login +# az login --use-device-code # Device code flow (for remote/headless) + +# Set defaults for dotnet's internal AzDO org +az devops configure --defaults organization=https://dev.azure.com/dnceng project=internal + +# Get an AzDO PAT for direct REST API calls +$token = (az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798 --query accessToken -o tsv) +$headers = @{ "Authorization" = "Bearer $token" } +``` + +> ⚠️ If `az` is not installed, use `winget install -e --id Microsoft.AzureCLI` (Windows) then `az extension add --name azure-devops`. Ask the user to authenticate if needed. + +### Querying Pipeline Definitions and Builds + +When investigating build failures, it's often useful to look at the pipeline definition itself to understand what stages, jobs, and templates are involved. + +**Use `az` CLI commands first** — they're simpler and handle auth automatically: + +```powershell +# Find a pipeline definition by name +az pipelines list --name "dotnet-unified-build" --query "[].{id:id, name:name, path:path}" -o table + +# Get pipeline definition details (shows YAML path, triggers, etc.) +az pipelines show --id 1330 --query "{id:id, name:name, yamlPath:process.yamlFilename, repo:repository.name}" -o table + +# List recent builds for a pipeline (with filtering) +az pipelines runs list --pipeline-ids 1330 --branch "refs/heads/main" --top 5 --query "[].{id:id, result:result, finish:finishTime}" -o table + +# Get a specific build's details +az pipelines runs show --id $buildId --query "{id:id, result:result, sourceBranch:sourceBranch}" -o table + +# List build artifacts +az pipelines runs artifact list --run-id $buildId --query "[].{name:name, type:resource.type}" -o table +``` + +**Fall back to REST API** only when the CLI doesn't expose what you need (e.g., build timelines, artifact downloads): + +```powershell +# Get build timeline (stages, jobs, tasks with results and durations) — no CLI equivalent +$token = (az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798 --query accessToken -o tsv) +$headers = @{ "Authorization" = "Bearer $token" } +$timelineUrl = "https://dev.azure.com/dnceng/internal/_apis/build/builds/$buildId/timeline?api-version=7.1" +$timeline = (Invoke-RestMethod -Uri $timelineUrl -Headers $headers) +$timeline.records | Where-Object { $_.result -eq "failed" -and $_.type -eq "Job" } + +# Download a specific artifact (e.g., build logs with binlogs) — no CLI equivalent for zip download +$artifactName = "Windows_Workloads_x64_BuildPass2_BuildLogs_Attempt1" +$downloadUrl = "https://dev.azure.com/dnceng/internal/_apis/build/builds/$buildId/artifacts?artifactName=$artifactName&api-version=7.1&`$format=zip" +Invoke-WebRequest -Uri $downloadUrl -Headers $headers -OutFile "$env:TEMP\artifact.zip" +``` + +### Examining Pipeline YAML + +For VMR/unified builds, the pipeline YAML lives in the dotnet/dotnet repository. To understand what a specific job does: + +```powershell +# Check pipeline YAML in dotnet/dotnet repo +# The VMR uses: eng/pipelines/unified-build.yml (or similar) +# Use GitHub MCP server to fetch the file: +# github-mcp-server-get_file_contents owner:dotnet repo:dotnet path:eng/pipelines/unified-build.yml +``` + +This is especially useful when: +- A job name doesn't clearly indicate what it builds +- You need to understand stage dependencies (why a job was canceled) +- You want to find which template defines a specific step +- Investigating whether a pipeline change caused new failures + ## Tips 1. Read PR description and comments first for context @@ -181,3 +271,4 @@ Canceled jobs (typically from timeouts) often still have useful artifacts. The H 5. Binlogs in artifacts help diagnose MSB4018 task failures 6. Use the MSBuild MCP server (`binlog.mcp`) to search binlogs for Helix job IDs, build errors, and properties 7. If checking CI status via `gh pr checks --json`, the valid fields are `bucket`, `completedAt`, `description`, `event`, `link`, `name`, `startedAt`, `state`, `workflow`. There is **no `conclusion` field** — `state` contains `SUCCESS`/`FAILURE` directly +8. When investigating internal AzDO pipelines, check `az account show` first to verify authentication before making REST API calls From 7ff5e00fe73d2f8140d234611ae4281dce08991f Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Mon, 9 Feb 2026 12:29:59 -0600 Subject: [PATCH 2/5] Fix pipeline YAML guidance: all arcade repos use eng/pipelines/ --- .github/skills/ci-analysis/SKILL.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/skills/ci-analysis/SKILL.md b/.github/skills/ci-analysis/SKILL.md index ca15ccd2843fd0..88e5a641fbb0ac 100644 --- a/.github/skills/ci-analysis/SKILL.md +++ b/.github/skills/ci-analysis/SKILL.md @@ -247,13 +247,19 @@ Invoke-WebRequest -Uri $downloadUrl -Headers $headers -OutFile "$env:TEMP\artifa ### Examining Pipeline YAML -For VMR/unified builds, the pipeline YAML lives in the dotnet/dotnet repository. To understand what a specific job does: +All dotnet repos that use arcade put their pipeline definitions under `eng/pipelines/`. Use `az pipelines show` to find the YAML file path, then fetch it: ```powershell -# Check pipeline YAML in dotnet/dotnet repo -# The VMR uses: eng/pipelines/unified-build.yml (or similar) -# Use GitHub MCP server to fetch the file: +# Find the YAML path for a pipeline +az pipelines show --id 1330 --query "{yamlPath:process.yamlFilename, repo:repository.name}" -o table + +# Fetch the YAML from the repo (example: dotnet/runtime's runtime-official pipeline) +# github-mcp-server-get_file_contents owner:dotnet repo:runtime path:eng/pipelines/runtime-official.yml + +# For VMR unified builds, the YAML is in dotnet/dotnet: # github-mcp-server-get_file_contents owner:dotnet repo:dotnet path:eng/pipelines/unified-build.yml + +# Templates are usually in eng/pipelines/common/ or eng/pipelines/templates/ ``` This is especially useful when: From c22e2c93638bd6a6418efb81cd21f0e571adf84d Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Mon, 9 Feb 2026 12:32:16 -0600 Subject: [PATCH 3/5] Remove az devops defaults, use explicit --org/-p per command Multiple agents running concurrently would stomp on each other's global az devops config. Pass org/project explicitly instead. --- .github/skills/ci-analysis/SKILL.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/.github/skills/ci-analysis/SKILL.md b/.github/skills/ci-analysis/SKILL.md index 88e5a641fbb0ac..4e2e8d56e140c6 100644 --- a/.github/skills/ci-analysis/SKILL.md +++ b/.github/skills/ci-analysis/SKILL.md @@ -196,9 +196,6 @@ az account show --query "{name:name, user:user.name}" -o table 2>$null # az login # Interactive browser login # az login --use-device-code # Device code flow (for remote/headless) -# Set defaults for dotnet's internal AzDO org -az devops configure --defaults organization=https://dev.azure.com/dnceng project=internal - # Get an AzDO PAT for direct REST API calls $token = (az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798 --query accessToken -o tsv) $headers = @{ "Authorization" = "Bearer $token" } @@ -206,6 +203,8 @@ $headers = @{ "Authorization" = "Bearer $token" } > ⚠️ If `az` is not installed, use `winget install -e --id Microsoft.AzureCLI` (Windows) then `az extension add --name azure-devops`. Ask the user to authenticate if needed. +> ⚠️ **Do NOT use `az devops configure --defaults`** — it writes to a global config file and will cause conflicts if multiple agents are running concurrently. Always pass `--org` and `--project` (or `-p`) explicitly on each command. + ### Querying Pipeline Definitions and Builds When investigating build failures, it's often useful to look at the pipeline definition itself to understand what stages, jobs, and templates are involved. @@ -213,20 +212,23 @@ When investigating build failures, it's often useful to look at the pipeline def **Use `az` CLI commands first** — they're simpler and handle auth automatically: ```powershell +$org = "https://dev.azure.com/dnceng" +$project = "internal" + # Find a pipeline definition by name -az pipelines list --name "dotnet-unified-build" --query "[].{id:id, name:name, path:path}" -o table +az pipelines list --name "dotnet-unified-build" --org $org -p $project --query "[].{id:id, name:name, path:path}" -o table # Get pipeline definition details (shows YAML path, triggers, etc.) -az pipelines show --id 1330 --query "{id:id, name:name, yamlPath:process.yamlFilename, repo:repository.name}" -o table +az pipelines show --id 1330 --org $org -p $project --query "{id:id, name:name, yamlPath:process.yamlFilename, repo:repository.name}" -o table # List recent builds for a pipeline (with filtering) -az pipelines runs list --pipeline-ids 1330 --branch "refs/heads/main" --top 5 --query "[].{id:id, result:result, finish:finishTime}" -o table +az pipelines runs list --pipeline-ids 1330 --branch "refs/heads/main" --top 5 --org $org -p $project --query "[].{id:id, result:result, finish:finishTime}" -o table # Get a specific build's details -az pipelines runs show --id $buildId --query "{id:id, result:result, sourceBranch:sourceBranch}" -o table +az pipelines runs show --id $buildId --org $org -p $project --query "{id:id, result:result, sourceBranch:sourceBranch}" -o table # List build artifacts -az pipelines runs artifact list --run-id $buildId --query "[].{name:name, type:resource.type}" -o table +az pipelines runs artifact list --run-id $buildId --org $org -p $project --query "[].{name:name, type:resource.type}" -o table ``` **Fall back to REST API** only when the CLI doesn't expose what you need (e.g., build timelines, artifact downloads): From 785ffc338861a63dab68d05e23605f94c62f052c Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Mon, 9 Feb 2026 12:34:10 -0600 Subject: [PATCH 4/5] Address review: fix intro wording, use AAD token not PAT terminology --- .github/skills/ci-analysis/SKILL.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/skills/ci-analysis/SKILL.md b/.github/skills/ci-analysis/SKILL.md index 4e2e8d56e140c6..cbfdc3ae67e6d4 100644 --- a/.github/skills/ci-analysis/SKILL.md +++ b/.github/skills/ci-analysis/SKILL.md @@ -174,7 +174,7 @@ Canceled jobs (typically from timeouts) often still have useful artifacts. The H ## Deep Investigation with Azure CLI -When the script and GitHub APIs aren't enough (e.g., investigating internal pipeline definitions, downloading build artifacts, querying build timelines directly), you can use the Azure CLI with the `azure-devops` extension. +When the script and GitHub APIs aren't enough (e.g., investigating internal pipeline definitions or downloading build artifacts), you can use the Azure CLI with the `azure-devops` extension. > 💡 **Prefer `az pipelines` / `az devops` commands over raw REST API calls.** The CLI handles authentication, pagination, and JSON output formatting. Only fall back to manual `Invoke-RestMethod` calls when the CLI doesn't expose the endpoint you need (e.g., artifact download URLs, specialized timeline queries). The CLI's `--query` (JMESPath) and `-o table` flags are powerful for filtering without extra scripting. @@ -196,9 +196,9 @@ az account show --query "{name:name, user:user.name}" -o table 2>$null # az login # Interactive browser login # az login --use-device-code # Device code flow (for remote/headless) -# Get an AzDO PAT for direct REST API calls -$token = (az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798 --query accessToken -o tsv) -$headers = @{ "Authorization" = "Bearer $token" } +# Get an AAD access token for AzDO REST API calls +$accessToken = (az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798 --query accessToken -o tsv) +$headers = @{ "Authorization" = "Bearer $accessToken" } ``` > ⚠️ If `az` is not installed, use `winget install -e --id Microsoft.AzureCLI` (Windows) then `az extension add --name azure-devops`. Ask the user to authenticate if needed. @@ -235,8 +235,8 @@ az pipelines runs artifact list --run-id $buildId --org $org -p $project --query ```powershell # Get build timeline (stages, jobs, tasks with results and durations) — no CLI equivalent -$token = (az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798 --query accessToken -o tsv) -$headers = @{ "Authorization" = "Bearer $token" } +$accessToken = (az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798 --query accessToken -o tsv) +$headers = @{ "Authorization" = "Bearer $accessToken" } $timelineUrl = "https://dev.azure.com/dnceng/internal/_apis/build/builds/$buildId/timeline?api-version=7.1" $timeline = (Invoke-RestMethod -Uri $timelineUrl -Headers $headers) $timeline.records | Where-Object { $_.result -eq "failed" -and $_.type -eq "Job" } From 431a6809442bdf28c571166427dad46d19201365 Mon Sep 17 00:00:00 2001 From: Larry Ewing Date: Mon, 9 Feb 2026 12:44:01 -0600 Subject: [PATCH 5/5] Address review round 2: extension check, buildId note, missing --org --- .github/skills/ci-analysis/SKILL.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/skills/ci-analysis/SKILL.md b/.github/skills/ci-analysis/SKILL.md index cbfdc3ae67e6d4..13177698c00a33 100644 --- a/.github/skills/ci-analysis/SKILL.md +++ b/.github/skills/ci-analysis/SKILL.md @@ -201,7 +201,7 @@ $accessToken = (az account get-access-token --resource 499b84ac-1321-427f-aa17-2 $headers = @{ "Authorization" = "Bearer $accessToken" } ``` -> ⚠️ If `az` is not installed, use `winget install -e --id Microsoft.AzureCLI` (Windows) then `az extension add --name azure-devops`. Ask the user to authenticate if needed. +> ⚠️ If `az` is not installed, use `winget install -e --id Microsoft.AzureCLI` (Windows). The `azure-devops` extension is also required — install or verify it with `az extension add --name azure-devops` (safe to run if already installed). Ask the user to authenticate if needed. > ⚠️ **Do NOT use `az devops configure --defaults`** — it writes to a global config file and will cause conflicts if multiple agents are running concurrently. Always pass `--org` and `--project` (or `-p`) explicitly on each command. @@ -209,7 +209,7 @@ $headers = @{ "Authorization" = "Bearer $accessToken" } When investigating build failures, it's often useful to look at the pipeline definition itself to understand what stages, jobs, and templates are involved. -**Use `az` CLI commands first** — they're simpler and handle auth automatically: +**Use `az` CLI commands first** — they're simpler and handle auth automatically. Set `$buildId` from a runs list or from the AzDO URL: ```powershell $org = "https://dev.azure.com/dnceng" @@ -253,7 +253,7 @@ All dotnet repos that use arcade put their pipeline definitions under `eng/pipel ```powershell # Find the YAML path for a pipeline -az pipelines show --id 1330 --query "{yamlPath:process.yamlFilename, repo:repository.name}" -o table +az pipelines show --id 1330 --org $org -p $project --query "{yamlPath:process.yamlFilename, repo:repository.name}" -o table # Fetch the YAML from the repo (example: dotnet/runtime's runtime-official pipeline) # github-mcp-server-get_file_contents owner:dotnet repo:runtime path:eng/pipelines/runtime-official.yml