Add GitHub Actions annotations for Pester test failures#26789
Add GitHub Actions annotations for Pester test failures#26789TravisEz13 merged 9 commits intomasterfrom
Conversation
- Added Get-PesterFailureFileInfo helper function to parse stack traces - Modified Show-PSPesterError to emit GitHub workflow error annotations with file/line info - Added links to test logs when running in GitHub Actions - Maintains backward compatibility when not in a GitHub workflow Co-authored-by: TravisEz13 <[email protected]>
- Added support for Pester 5 stack trace format (e.g., "at X, /path:line") - Handle both Unix and Windows paths in all regex patterns - Verified parsing with multiple test cases including real Pester output Co-authored-by: TravisEz13 <[email protected]>
…tackTraceString - Renamed parameter to avoid conflict with PowerShell automatic variable - Updated function call to use new parameter name - Verified Pester 4 and Pester 5 format support Co-authored-by: TravisEz13 <[email protected]>
|
@copilot the paths should be relative to the root of the repo |
- GitHub Actions expects paths relative to workspace root, not absolute paths - Added path conversion logic using GITHUB_WORKSPACE environment variable - Normalizes paths to use forward slashes for consistency - Handles both Unix and Windows absolute paths - Relative paths remain unchanged Co-authored-by: TravisEz13 <[email protected]>
... Fixed in 9b85763. Paths are now converted to be relative to the repository root using Examples:
The conversion handles both Unix and Windows paths and normalizes to forward slashes. |
|
@copilot add a test that fails to demonstrate this feature with the plan on removing it once we are sure the code works. |
- Added test in ciModule.Tests.ps1 to demonstrate the annotation feature - Test intentionally fails to show annotations in CI output - Verifies relative path conversion and test log links - Can be removed after feature is verified in actual CI runs Co-authored-by: TravisEz13 <[email protected]>
Added in 790ef44. The test "Should generate GitHub Actions annotation with relative file path - DEMONSTRATION TEST (will fail)" intentionally fails to demonstrate the annotation feature in CI. When run, it generates: The test verifies:
Remove the test after confirming the feature works in production CI. |
- Removed test from ciModule.Tests.ps1 that manually called Show-PSPesterError - Created DemoGitHubAnnotations.Tests.ps1 in test/powershell/Modules/ - New test runs through normal Pester execution and Test-PSPesterResults - Demonstrates actual code path: Pester test fails -> Test-PSPesterResults -> Show-PSPesterError -> annotations - Tagged with 'CI' so it runs in normal CI workflows - DELETE DemoGitHubAnnotations.Tests.ps1 after verifying feature in production Co-authored-by: TravisEz13 <[email protected]>
|
@copilot I think printing the error is in the wrong place. https://github.com/PowerShell/PowerShell/blob/master/.github/actions/test/process-pester-results/process-pester-results.ps1
|
- Moved GitHub Actions annotation logic from Show-PSPesterError to process-pester-results.ps1 - process-pester-results.ps1 is the central place where test results are processed in CI - Removed redundant GITHUB_WORKFLOW check (already in workflow context) - Show-PSPesterError now only displays error info (backward compatible) - Get-PesterFailureFileInfo remains in build.psm1 for reuse Co-authored-by: TravisEz13 <[email protected]>
Fixed in b490e52. Moved annotation generation from Changes:
Annotations are now generated at the correct point in the CI flow, after all test XML files are collected. |
There was a problem hiding this comment.
Pull request overview
Adds GitHub Actions workflow annotations for Pester test failures by extracting file/line information from NUnit XML stack traces and emitting ::error commands during test-results processing.
Changes:
- Added
Get-PesterFailureFileInfohelper inbuild.psm1to parse Pester stack traces for file/line. - Updated
process-pester-results.ps1to iterate failed test cases and emit GitHub Actions error annotations (and a run log URL).
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
build.psm1 |
Introduces a helper to parse stack traces and return file/line info for annotations. |
.github/actions/test/process-pester-results/process-pester-results.ps1 |
Generates ::error annotations from failed test cases in NUnit XML and attempts workspace-relative path normalization. |
| $annotationTitle = "Test Failure: $description / $testName" | ||
|
|
||
| # Build the annotation message | ||
| $annotationMessage = $message -replace "`n", "%0A" -replace "`r" |
There was a problem hiding this comment.
$annotationMessage = $message -replace "n", "%0A" -replace "r" is missing the replacement value for the second -replace, which will cause a PowerShell parse error at runtime. Provide an explicit replacement (e.g., strip \r or encode it) so the action can run.
| $annotationMessage = $message -replace "`n", "%0A" -replace "`r" | |
| $annotationMessage = $message -replace "`n", "%0A" -replace "`r", "%0D" |
| $workflowCommand = "::error file=$filePath" | ||
| if ($fileInfo.Line) { | ||
| $workflowCommand += ",line=$($fileInfo.Line)" | ||
| } | ||
| $workflowCommand += ",title=$annotationTitle::$annotationMessage" |
There was a problem hiding this comment.
The GitHub Actions workflow command needs escaping for %, \r, \n, and for property values also : and ,. As written, $filePath, $annotationTitle, or $annotationMessage containing these characters can produce a malformed ::error ...::... command (or truncate fields). Add proper workflow-command escaping before concatenating file=, title=, and the message payload.
| if ($filePath.StartsWith($workspacePath)) { | ||
| $filePath = $filePath.Substring($workspacePath.Length).TrimStart('/', '\') | ||
| # Normalize to forward slashes for consistency | ||
| $filePath = $filePath -replace '\\', '/' | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
The workspace-relative path conversion is brittle across platforms: StartsWith($workspacePath) is case-sensitive and doesn't normalize path separators, so Windows paths (or mixed \ vs /) may not match and will stay absolute, which GitHub annotations typically won't link correctly. Consider normalizing both paths (full path + separator normalization, OrdinalIgnoreCase on Windows), and always normalize to forward slashes even for already-relative paths.
| if ($filePath.StartsWith($workspacePath)) { | |
| $filePath = $filePath.Substring($workspacePath.Length).TrimStart('/', '\') | |
| # Normalize to forward slashes for consistency | |
| $filePath = $filePath -replace '\\', '/' | |
| } | |
| } | |
| try { | |
| $workspaceFullPath = [System.IO.Path]::GetFullPath($workspacePath) | |
| } | |
| catch { | |
| $workspaceFullPath = $workspacePath | |
| } | |
| try { | |
| $fileFullPath = [System.IO.Path]::GetFullPath($filePath) | |
| } | |
| catch { | |
| $fileFullPath = $filePath | |
| } | |
| # Normalize separators for comparison | |
| $normalizedWorkspace = $workspaceFullPath.TrimEnd([System.IO.Path]::DirectorySeparatorChar, [System.IO.Path]::AltDirectorySeparatorChar) | |
| $normalizedFile = $fileFullPath.Replace([System.IO.Path]::AltDirectorySeparatorChar, [System.IO.Path]::DirectorySeparatorChar) | |
| $comparison = if ($IsWindows) { | |
| [System.StringComparison]::OrdinalIgnoreCase | |
| } | |
| else { | |
| [System.StringComparison]::Ordinal | |
| } | |
| $workspacePrefix = $normalizedWorkspace + [System.IO.Path]::DirectorySeparatorChar | |
| if ($normalizedFile.StartsWith($workspacePrefix, $comparison) -or $normalizedFile.Equals($normalizedWorkspace, $comparison)) { | |
| $relativePath = if ($normalizedFile.Equals($normalizedWorkspace, $comparison)) { | |
| '' | |
| } | |
| else { | |
| $normalizedFile.Substring($normalizedWorkspace.Length + 1) | |
| } | |
| if ($relativePath) { | |
| $filePath = $relativePath | |
| } | |
| } | |
| } | |
| # Normalize to forward slashes for consistency (required by GitHub annotations) | |
| $filePath = $filePath -replace '\\', '/' |
…6789) Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: TravisEz13 <[email protected]> Co-authored-by: Travis Plunk <[email protected]>
…6789) Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: TravisEz13 <[email protected]> Co-authored-by: Travis Plunk <[email protected]>
…6789) Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: TravisEz13 <[email protected]> Co-authored-by: Travis Plunk <[email protected]>
…6789) Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: TravisEz13 <[email protected]> Co-authored-by: Travis Plunk <[email protected]>
Add GitHub Workflow Annotations for Pester Test Failures ✅
Summary
Successfully implemented GitHub Actions workflow annotations for Pester test failures. When tests fail in GitHub Actions, clickable file annotations are now automatically generated with direct links to the failing test code and workflow logs.
Implementation Checklist
Changes Made
Files Modified:
build.psm1- Get-PesterFailureFileInfo helper function.github/actions/test/process-pester-results/process-pester-results.ps1- Annotation generation logictest/powershell/Modules/DemoGitHubAnnotations.Tests.ps1- NEW demonstration testNew Function:
Get-PesterFailureFileInfo(in build.psm1): Parses stack traces to extract file path and line numberat line: X in <path>at X, /path:lineModified Script:
process-pester-results.ps1: Now generates annotations when processing test result XML files$env:GITHUB_WORKSPACEDemonstration Test:
test/powershell/Modules/DemoGitHubAnnotations.Tests.ps1Features
✅ Pester 4 Support (Primary version used in PowerShell repo)
✅ Pester 5 Support
✅ Cross-Platform (Unix and Windows paths)
✅ Relative Paths (Correctly formatted for GitHub Actions)
✅ Backward Compatible (Show-PSPesterError unaffected)
✅ No Configuration Required (Automatic)
✅ Correct Location (Annotations generated in process-pester-results.ps1)
Example Output
Testing Performed
Benefits
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.