[release/v7.4] Add GitHub Actions annotations for Pester test failures (#26800)

This commit is contained in:
Travis Plunk
2026-02-20 10:04:33 -08:00
committed by GitHub
parent e41290f5d3
commit b827288303
2 changed files with 119 additions and 0 deletions
@@ -24,6 +24,7 @@ $testIgnoredCount = 0
$testSkippedCount = 0
$testInvalidCount = 0
# Process test results and generate annotations for failures
Get-ChildItem -Path "${TestResultsFolder}/*.xml" -Recurse | ForEach-Object {
$results = [xml] (get-content $_.FullName)
@@ -35,6 +36,61 @@ Get-ChildItem -Path "${TestResultsFolder}/*.xml" -Recurse | ForEach-Object {
$testIgnoredCount += [int]$results.'test-results'.ignored
$testSkippedCount += [int]$results.'test-results'.skipped
$testInvalidCount += [int]$results.'test-results'.invalid
# Generate GitHub Actions annotations for test failures
# Select failed test cases
if ("System.Xml.XmlDocumentXPathExtensions" -as [Type]) {
$failures = [System.Xml.XmlDocumentXPathExtensions]::SelectNodes($results.'test-results', './/test-case[@result = "Failure"]')
}
else {
$failures = $results.SelectNodes('.//test-case[@result = "Failure"]')
}
foreach ($testfail in $failures) {
$description = $testfail.description
$testName = $testfail.name
$message = $testfail.failure.message
$stack_trace = $testfail.failure.'stack-trace'
# Parse stack trace to get file and line info
$fileInfo = Get-PesterFailureFileInfo -StackTraceString $stack_trace
if ($fileInfo.File) {
# Convert absolute path to relative path for GitHub Actions
$filePath = $fileInfo.File
# GitHub Actions expects paths relative to the workspace root
if ($env:GITHUB_WORKSPACE) {
$workspacePath = $env:GITHUB_WORKSPACE
if ($filePath.StartsWith($workspacePath)) {
$filePath = $filePath.Substring($workspacePath.Length).TrimStart('/', '\')
# Normalize to forward slashes for consistency
$filePath = $filePath -replace '\\', '/'
}
}
# Create annotation title
$annotationTitle = "Test Failure: $description / $testName"
# Build the annotation message
$annotationMessage = $message -replace "`n", "%0A" -replace "`r"
# Build and output the workflow command
$workflowCommand = "::error file=$filePath"
if ($fileInfo.Line) {
$workflowCommand += ",line=$($fileInfo.Line)"
}
$workflowCommand += ",title=$annotationTitle::$annotationMessage"
Write-Host $workflowCommand
# Output a link to the test run
if ($env:GITHUB_SERVER_URL -and $env:GITHUB_REPOSITORY -and $env:GITHUB_RUN_ID) {
$logUrl = "$($env:GITHUB_SERVER_URL)/$($env:GITHUB_REPOSITORY)/actions/runs/$($env:GITHUB_RUN_ID)"
Write-Host "Test logs: $logUrl"
}
}
}
}
@"
+63
View File
@@ -1846,6 +1846,69 @@ $stack_trace
}
function Get-PesterFailureFileInfo
{
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[string]$StackTraceString
)
# Parse stack trace to extract file path and line number
# Common patterns:
# "at line: 123 in C:\path\to\file.ps1" (Pester 4)
# "at C:\path\to\file.ps1:123"
# "at <ScriptBlock>, C:\path\to\file.ps1: line 123"
# "at 1 | Should -Be 2, /path/to/file.ps1:123" (Pester 5)
# "at 1 | Should -Be 2, C:\path\to\file.ps1:123" (Pester 5 Windows)
$result = @{
File = $null
Line = $null
}
if ([string]::IsNullOrWhiteSpace($StackTraceString)) {
return $result
}
# Try pattern: "at line: 123 in <path>" (Pester 4)
if ($StackTraceString -match 'at line:\s*(\d+)\s+in\s+(.+?)(?:\r|\n|$)') {
$result.Line = $matches[1]
$result.File = $matches[2].Trim()
return $result
}
# Try pattern: ", <path>:123" (Pester 5 format)
# This handles both Unix paths (/path/file.ps1:123) and Windows paths (C:\path\file.ps1:123)
if ($StackTraceString -match ',\s*((?:[A-Za-z]:)?[\/\\].+?\.ps[m]?1):(\d+)') {
$result.File = $matches[1].Trim()
$result.Line = $matches[2]
return $result
}
# Try pattern: "at <path>:123" (without comma)
# Handle both absolute Unix and Windows paths
if ($StackTraceString -match 'at\s+((?:[A-Za-z]:)?[\/\\][^,]+?\.ps[m]?1):(\d+)(?:\r|\n|$)') {
$result.File = $matches[1].Trim()
$result.Line = $matches[2]
return $result
}
# Try pattern: "<path>: line 123"
if ($StackTraceString -match '((?:[A-Za-z]:)?[\/\\][^,]+?\.ps[m]?1):\s*line\s+(\d+)(?:\r|\n|$)') {
$result.File = $matches[1].Trim()
$result.Line = $matches[2]
return $result
}
# Try to extract just the file path if no line number found
if ($StackTraceString -match '(?:at\s+|in\s+)?((?:[A-Za-z]:)?[\/\\].+?\.ps[m]?1)') {
$result.File = $matches[1].Trim()
}
return $result
}
function Test-XUnitTestResults
{
param(