From 739b0cc1b5a72c6ed64d2362ed230b65d9995c85 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 17:57:00 +0000 Subject: [PATCH 1/9] Initial plan From d71c2393a0c79520b8b5043d6081aac600e7303c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 18:01:38 +0000 Subject: [PATCH 2/9] Add GitHub workflow annotations for Pester test failures - 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 <10873629+TravisEz13@users.noreply.github.com> --- build.psm1 | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/build.psm1 b/build.psm1 index d09b7af925d..873a0c5b8b2 100644 --- a/build.psm1 +++ b/build.psm1 @@ -1862,6 +1862,86 @@ $stack_trace "@ + # If we're in a GitHub workflow, add an annotation with file location + if ($env:GITHUB_WORKFLOW) { + $fileInfo = Get-PesterFailureFileInfo -StackTrace $stack_trace + + if ($fileInfo.File) { + # Create annotation title + $annotationTitle = "Test Failure: $description / $name" + + # Build the annotation message + $annotationMessage = $message -replace "`n", "%0A" -replace "`r" + + # Build the workflow command + $workflowCommand = "::error file=$($fileInfo.File)" + if ($fileInfo.Line) { + $workflowCommand += ",line=$($fileInfo.Line)" + } + $workflowCommand += ",title=$annotationTitle::$annotationMessage" + + Write-Host $workflowCommand + + # If available, also log 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" + } + } + } + +} + +function Get-PesterFailureFileInfo +{ + [CmdletBinding()] + param ( + [Parameter(Mandatory)] + [string]$StackTrace + ) + + # Parse stack trace to extract file path and line number + # Common patterns: + # "at line: 123 in C:\path\to\file.ps1" + # "at C:\path\to\file.ps1:123" + # "at , C:\path\to\file.ps1: line 123" + + $result = @{ + File = $null + Line = $null + } + + if ([string]::IsNullOrWhiteSpace($StackTrace)) { + return $result + } + + # Try pattern: "at line: 123 in " + if ($StackTrace -match 'at line:\s*(\d+)\s+in\s+(.+?)(?:\r|\n|$)') { + $result.Line = $matches[1] + $result.File = $matches[2].Trim() + return $result + } + + # Try pattern: "at :123" + if ($StackTrace -match 'at\s+(.+?):(\d+)(?:\r|\n|$)') { + $result.File = $matches[1].Trim() + $result.Line = $matches[2] + return $result + } + + # Try pattern: ": line 123" + if ($StackTrace -match '(.+?):\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 ($StackTrace -match '(?:at\s+|in\s+)?([A-Za-z]:[\\\/].+?\.ps[m]?1)') { + $result.File = $matches[1].Trim() + } + + return $result } function Test-XUnitTestResults From 6a2d1af0e493d234f3a9c880a08643b1426d0fd6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 18:03:42 +0000 Subject: [PATCH 3/9] Improve stack trace parsing for Pester 5 format - 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 <10873629+TravisEz13@users.noreply.github.com> --- build.psm1 | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/build.psm1 b/build.psm1 index 873a0c5b8b2..fbbd6fba0bc 100644 --- a/build.psm1 +++ b/build.psm1 @@ -1905,6 +1905,8 @@ function Get-PesterFailureFileInfo # "at line: 123 in C:\path\to\file.ps1" # "at C:\path\to\file.ps1:123" # "at , 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 @@ -1922,22 +1924,31 @@ function Get-PesterFailureFileInfo return $result } - # Try pattern: "at :123" - if ($StackTrace -match 'at\s+(.+?):(\d+)(?:\r|\n|$)') { + # Try pattern: ", :123" (Pester 5 format) + # This handles both Unix paths (/path/file.ps1:123) and Windows paths (C:\path\file.ps1:123) + if ($StackTrace -match ',\s*((?:[A-Za-z]:)?[\/\\].+?\.ps[m]?1):(\d+)') { + $result.File = $matches[1].Trim() + $result.Line = $matches[2] + return $result + } + + # Try pattern: "at :123" (without comma) + # Handle both absolute Unix and Windows paths + if ($StackTrace -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: ": line 123" - if ($StackTrace -match '(.+?):\s*line\s+(\d+)(?:\r|\n|$)') { + if ($StackTrace -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 ($StackTrace -match '(?:at\s+|in\s+)?([A-Za-z]:[\\\/].+?\.ps[m]?1)') { + if ($StackTrace -match '(?:at\s+|in\s+)?((?:[A-Za-z]:)?[\/\\].+?\.ps[m]?1)') { $result.File = $matches[1].Trim() } From 6605c893a1fd701ffdc07a7ec92c1c3c518457c3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 18:07:43 +0000 Subject: [PATCH 4/9] Fix PSScriptAnalyzer warning: rename parameter from $StackTrace to $StackTraceString - 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 <10873629+TravisEz13@users.noreply.github.com> --- build.psm1 | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/build.psm1 b/build.psm1 index fbbd6fba0bc..f5dc9782412 100644 --- a/build.psm1 +++ b/build.psm1 @@ -1864,7 +1864,7 @@ $stack_trace # If we're in a GitHub workflow, add an annotation with file location if ($env:GITHUB_WORKFLOW) { - $fileInfo = Get-PesterFailureFileInfo -StackTrace $stack_trace + $fileInfo = Get-PesterFailureFileInfo -StackTraceString $stack_trace if ($fileInfo.File) { # Create annotation title @@ -1897,12 +1897,12 @@ function Get-PesterFailureFileInfo [CmdletBinding()] param ( [Parameter(Mandatory)] - [string]$StackTrace + [string]$StackTraceString ) # Parse stack trace to extract file path and line number # Common patterns: - # "at line: 123 in C:\path\to\file.ps1" + # "at line: 123 in C:\path\to\file.ps1" (Pester 4) # "at C:\path\to\file.ps1:123" # "at , C:\path\to\file.ps1: line 123" # "at 1 | Should -Be 2, /path/to/file.ps1:123" (Pester 5) @@ -1913,12 +1913,12 @@ function Get-PesterFailureFileInfo Line = $null } - if ([string]::IsNullOrWhiteSpace($StackTrace)) { + if ([string]::IsNullOrWhiteSpace($StackTraceString)) { return $result } - # Try pattern: "at line: 123 in " - if ($StackTrace -match 'at line:\s*(\d+)\s+in\s+(.+?)(?:\r|\n|$)') { + # Try pattern: "at line: 123 in " (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 @@ -1926,7 +1926,7 @@ function Get-PesterFailureFileInfo # Try pattern: ", :123" (Pester 5 format) # This handles both Unix paths (/path/file.ps1:123) and Windows paths (C:\path\file.ps1:123) - if ($StackTrace -match ',\s*((?:[A-Za-z]:)?[\/\\].+?\.ps[m]?1):(\d+)') { + if ($StackTraceString -match ',\s*((?:[A-Za-z]:)?[\/\\].+?\.ps[m]?1):(\d+)') { $result.File = $matches[1].Trim() $result.Line = $matches[2] return $result @@ -1934,21 +1934,21 @@ function Get-PesterFailureFileInfo # Try pattern: "at :123" (without comma) # Handle both absolute Unix and Windows paths - if ($StackTrace -match 'at\s+((?:[A-Za-z]:)?[\/\\][^,]+?\.ps[m]?1):(\d+)(?:\r|\n|$)') { + 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: ": line 123" - if ($StackTrace -match '((?:[A-Za-z]:)?[\/\\][^,]+?\.ps[m]?1):\s*line\s+(\d+)(?:\r|\n|$)') { + 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 ($StackTrace -match '(?:at\s+|in\s+)?((?:[A-Za-z]:)?[\/\\].+?\.ps[m]?1)') { + if ($StackTraceString -match '(?:at\s+|in\s+)?((?:[A-Za-z]:)?[\/\\].+?\.ps[m]?1)') { $result.File = $matches[1].Trim() } From 9b85763e257bd50abc877625f9dd6a1a8199c2b1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 18:51:36 +0000 Subject: [PATCH 5/9] Convert file paths to relative paths for GitHub Actions annotations - 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 <10873629+TravisEz13@users.noreply.github.com> --- build.psm1 | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/build.psm1 b/build.psm1 index f5dc9782412..d147488ff8a 100644 --- a/build.psm1 +++ b/build.psm1 @@ -1867,6 +1867,19 @@ $stack_trace $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 / $name" @@ -1874,7 +1887,7 @@ $stack_trace $annotationMessage = $message -replace "`n", "%0A" -replace "`r" # Build the workflow command - $workflowCommand = "::error file=$($fileInfo.File)" + $workflowCommand = "::error file=$filePath" if ($fileInfo.Line) { $workflowCommand += ",line=$($fileInfo.Line)" } From 790ef4434d47c5d97e6aaba92040cbd302267eaf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 19:13:44 +0000 Subject: [PATCH 6/9] Add demonstration test for GitHub Actions annotations feature - 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 <10873629+TravisEz13@users.noreply.github.com> --- test/infrastructure/ciModule.Tests.ps1 | 86 ++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/test/infrastructure/ciModule.Tests.ps1 b/test/infrastructure/ciModule.Tests.ps1 index b7320ff49b7..056a5da1e6a 100644 --- a/test/infrastructure/ciModule.Tests.ps1 +++ b/test/infrastructure/ciModule.Tests.ps1 @@ -244,3 +244,89 @@ Describe "Install-CIPester" { } } +Describe "Show-PSPesterError GitHub Actions Annotations" { + BeforeAll { + # Import the build module which contains Show-PSPesterError + Import-Module "$PSScriptRoot/../../build.psm1" -Force + + # Set up GitHub Actions environment to trigger annotation feature + $script:originalGitHubWorkflow = $env:GITHUB_WORKFLOW + $script:originalGitHubWorkspace = $env:GITHUB_WORKSPACE + $script:originalGitHubServerUrl = $env:GITHUB_SERVER_URL + $script:originalGitHubRepository = $env:GITHUB_REPOSITORY + $script:originalGitHubRunId = $env:GITHUB_RUN_ID + + $env:GITHUB_WORKFLOW = 'test-workflow' + # Set workspace to repo root (two levels up from test/infrastructure) + $env:GITHUB_WORKSPACE = Resolve-Path "$PSScriptRoot/../.." + $env:GITHUB_SERVER_URL = 'https://github.com' + $env:GITHUB_REPOSITORY = 'PowerShell/PowerShell' + $env:GITHUB_RUN_ID = '12345' + } + + AfterAll { + # Restore original environment + if ($null -eq $script:originalGitHubWorkflow) { + Remove-Item Env:\GITHUB_WORKFLOW -ErrorAction SilentlyContinue + } else { + $env:GITHUB_WORKFLOW = $script:originalGitHubWorkflow + } + if ($null -eq $script:originalGitHubWorkspace) { + Remove-Item Env:\GITHUB_WORKSPACE -ErrorAction SilentlyContinue + } else { + $env:GITHUB_WORKSPACE = $script:originalGitHubWorkspace + } + if ($null -eq $script:originalGitHubServerUrl) { + Remove-Item Env:\GITHUB_SERVER_URL -ErrorAction SilentlyContinue + } else { + $env:GITHUB_SERVER_URL = $script:originalGitHubServerUrl + } + if ($null -eq $script:originalGitHubRepository) { + Remove-Item Env:\GITHUB_REPOSITORY -ErrorAction SilentlyContinue + } else { + $env:GITHUB_REPOSITORY = $script:originalGitHubRepository + } + if ($null -eq $script:originalGitHubRunId) { + Remove-Item Env:\GITHUB_RUN_ID -ErrorAction SilentlyContinue + } else { + $env:GITHUB_RUN_ID = $script:originalGitHubRunId + } + } + + Context "When test failure occurs in GitHub Actions" { + It "Should generate GitHub Actions annotation with relative file path - DEMONSTRATION TEST (will fail)" { + # This test intentionally fails to demonstrate the GitHub Actions annotation feature + # It can be removed once we verify the feature works in actual CI + + # Create a mock test failure XML with absolute path + $absolutePath = Join-Path $env:GITHUB_WORKSPACE "test/infrastructure/ciModule.Tests.ps1" + $xmlContent = @" + + + + + This is a demonstration failure to show GitHub Actions annotations + at line: 42 in $absolutePath + + + +"@ + + $xml = [xml]$xmlContent + $testCase = $xml.'test-results'.'test-case' + + # Capture the output from Show-PSPesterError (Write-Host goes to stream 6) + $output = Show-PSPesterError -testFailure $testCase 6>&1 | Out-String + + # Verify the annotation is generated with relative path (not absolute) + $output | Should -Match '::error file=test/infrastructure/ciModule.Tests.ps1,line=42' + + # Verify the test log link is included + $output | Should -Match 'Test logs:.*github.com/PowerShell/PowerShell/actions/runs/12345' + + # This line intentionally fails to demonstrate the annotation in CI output + 1 | Should -Be 2 -Because "This test demonstrates GitHub Actions annotations for test failures. Remove this test after verifying the feature works in CI." + } + } +} + From 3138c65488db39d1f362ce1f0658ff6f9451f5d5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 20:28:20 +0000 Subject: [PATCH 7/9] Replace demonstration test to use actual CI code path - 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 <10873629+TravisEz13@users.noreply.github.com> --- test/infrastructure/ciModule.Tests.ps1 | 86 ------------------- .../Modules/DemoGitHubAnnotations.Tests.ps1 | 30 +++++++ 2 files changed, 30 insertions(+), 86 deletions(-) create mode 100644 test/powershell/Modules/DemoGitHubAnnotations.Tests.ps1 diff --git a/test/infrastructure/ciModule.Tests.ps1 b/test/infrastructure/ciModule.Tests.ps1 index 056a5da1e6a..b7320ff49b7 100644 --- a/test/infrastructure/ciModule.Tests.ps1 +++ b/test/infrastructure/ciModule.Tests.ps1 @@ -244,89 +244,3 @@ Describe "Install-CIPester" { } } -Describe "Show-PSPesterError GitHub Actions Annotations" { - BeforeAll { - # Import the build module which contains Show-PSPesterError - Import-Module "$PSScriptRoot/../../build.psm1" -Force - - # Set up GitHub Actions environment to trigger annotation feature - $script:originalGitHubWorkflow = $env:GITHUB_WORKFLOW - $script:originalGitHubWorkspace = $env:GITHUB_WORKSPACE - $script:originalGitHubServerUrl = $env:GITHUB_SERVER_URL - $script:originalGitHubRepository = $env:GITHUB_REPOSITORY - $script:originalGitHubRunId = $env:GITHUB_RUN_ID - - $env:GITHUB_WORKFLOW = 'test-workflow' - # Set workspace to repo root (two levels up from test/infrastructure) - $env:GITHUB_WORKSPACE = Resolve-Path "$PSScriptRoot/../.." - $env:GITHUB_SERVER_URL = 'https://github.com' - $env:GITHUB_REPOSITORY = 'PowerShell/PowerShell' - $env:GITHUB_RUN_ID = '12345' - } - - AfterAll { - # Restore original environment - if ($null -eq $script:originalGitHubWorkflow) { - Remove-Item Env:\GITHUB_WORKFLOW -ErrorAction SilentlyContinue - } else { - $env:GITHUB_WORKFLOW = $script:originalGitHubWorkflow - } - if ($null -eq $script:originalGitHubWorkspace) { - Remove-Item Env:\GITHUB_WORKSPACE -ErrorAction SilentlyContinue - } else { - $env:GITHUB_WORKSPACE = $script:originalGitHubWorkspace - } - if ($null -eq $script:originalGitHubServerUrl) { - Remove-Item Env:\GITHUB_SERVER_URL -ErrorAction SilentlyContinue - } else { - $env:GITHUB_SERVER_URL = $script:originalGitHubServerUrl - } - if ($null -eq $script:originalGitHubRepository) { - Remove-Item Env:\GITHUB_REPOSITORY -ErrorAction SilentlyContinue - } else { - $env:GITHUB_REPOSITORY = $script:originalGitHubRepository - } - if ($null -eq $script:originalGitHubRunId) { - Remove-Item Env:\GITHUB_RUN_ID -ErrorAction SilentlyContinue - } else { - $env:GITHUB_RUN_ID = $script:originalGitHubRunId - } - } - - Context "When test failure occurs in GitHub Actions" { - It "Should generate GitHub Actions annotation with relative file path - DEMONSTRATION TEST (will fail)" { - # This test intentionally fails to demonstrate the GitHub Actions annotation feature - # It can be removed once we verify the feature works in actual CI - - # Create a mock test failure XML with absolute path - $absolutePath = Join-Path $env:GITHUB_WORKSPACE "test/infrastructure/ciModule.Tests.ps1" - $xmlContent = @" - - - - - This is a demonstration failure to show GitHub Actions annotations - at line: 42 in $absolutePath - - - -"@ - - $xml = [xml]$xmlContent - $testCase = $xml.'test-results'.'test-case' - - # Capture the output from Show-PSPesterError (Write-Host goes to stream 6) - $output = Show-PSPesterError -testFailure $testCase 6>&1 | Out-String - - # Verify the annotation is generated with relative path (not absolute) - $output | Should -Match '::error file=test/infrastructure/ciModule.Tests.ps1,line=42' - - # Verify the test log link is included - $output | Should -Match 'Test logs:.*github.com/PowerShell/PowerShell/actions/runs/12345' - - # This line intentionally fails to demonstrate the annotation in CI output - 1 | Should -Be 2 -Because "This test demonstrates GitHub Actions annotations for test failures. Remove this test after verifying the feature works in CI." - } - } -} - diff --git a/test/powershell/Modules/DemoGitHubAnnotations.Tests.ps1 b/test/powershell/Modules/DemoGitHubAnnotations.Tests.ps1 new file mode 100644 index 00000000000..fa05a2b9703 --- /dev/null +++ b/test/powershell/Modules/DemoGitHubAnnotations.Tests.ps1 @@ -0,0 +1,30 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +# DEMONSTRATION TEST FOR GITHUB ACTIONS ANNOTATIONS FEATURE +# This test intentionally fails to demonstrate the GitHub Actions annotation feature. +# When this test runs in CI and fails, it will generate clickable file annotations +# with direct links to this file and line number. +# +# REMOVE THIS FILE after verifying the feature works in production CI runs. + +Describe "GitHub Actions Annotations Demo" -Tag 'CI' { + Context "Demonstration of annotation feature" { + It "Should fail to demonstrate GitHub Actions annotations - REMOVE THIS TEST" { + # This test intentionally fails to show the annotation feature + # When it fails, Show-PSPesterError will generate: + # ::error file=test/powershell/Modules/DemoGitHubAnnotations.Tests.ps1,line=,title=... + # + # The annotation will appear as a clickable link in: + # - The GitHub Actions "Annotations" section + # - The PR "Files changed" tab (if in a PR) + # + # After confirming annotations appear correctly, DELETE THIS FILE. + + $expected = "Pass" + $actual = "Fail" + + $actual | Should -Be $expected -Because "This test demonstrates GitHub Actions annotations for test failures. DELETE THIS FILE after verifying the feature works." + } + } +} From b490e5264f87b31a57866f03c5d4a134ed0c21e7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 12 Feb 2026 21:31:51 +0000 Subject: [PATCH 8/9] Move annotation generation to 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 <10873629+TravisEz13@users.noreply.github.com> --- .../process-pester-results.ps1 | 56 +++++++++++++++++++ build.psm1 | 41 -------------- 2 files changed, 56 insertions(+), 41 deletions(-) diff --git a/.github/actions/test/process-pester-results/process-pester-results.ps1 b/.github/actions/test/process-pester-results/process-pester-results.ps1 index 523de3bebaa..5804bec9a94 100644 --- a/.github/actions/test/process-pester-results/process-pester-results.ps1 +++ b/.github/actions/test/process-pester-results/process-pester-results.ps1 @@ -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" + } + } + } } @" diff --git a/build.psm1 b/build.psm1 index d147488ff8a..bf175c5f7c2 100644 --- a/build.psm1 +++ b/build.psm1 @@ -1862,47 +1862,6 @@ $stack_trace "@ - # If we're in a GitHub workflow, add an annotation with file location - if ($env:GITHUB_WORKFLOW) { - $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 / $name" - - # Build the annotation message - $annotationMessage = $message -replace "`n", "%0A" -replace "`r" - - # Build the workflow command - $workflowCommand = "::error file=$filePath" - if ($fileInfo.Line) { - $workflowCommand += ",line=$($fileInfo.Line)" - } - $workflowCommand += ",title=$annotationTitle::$annotationMessage" - - Write-Host $workflowCommand - - # If available, also log 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" - } - } - } - } function Get-PesterFailureFileInfo From 0366a8bcc4ee0b519a60a82b53af4e33d03a0bdb Mon Sep 17 00:00:00 2001 From: Travis Plunk Date: Thu, 12 Feb 2026 17:04:26 -0500 Subject: [PATCH 9/9] Delete test/powershell/Modules/DemoGitHubAnnotations.Tests.ps1 --- .../Modules/DemoGitHubAnnotations.Tests.ps1 | 30 ------------------- 1 file changed, 30 deletions(-) delete mode 100644 test/powershell/Modules/DemoGitHubAnnotations.Tests.ps1 diff --git a/test/powershell/Modules/DemoGitHubAnnotations.Tests.ps1 b/test/powershell/Modules/DemoGitHubAnnotations.Tests.ps1 deleted file mode 100644 index fa05a2b9703..00000000000 --- a/test/powershell/Modules/DemoGitHubAnnotations.Tests.ps1 +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -# DEMONSTRATION TEST FOR GITHUB ACTIONS ANNOTATIONS FEATURE -# This test intentionally fails to demonstrate the GitHub Actions annotation feature. -# When this test runs in CI and fails, it will generate clickable file annotations -# with direct links to this file and line number. -# -# REMOVE THIS FILE after verifying the feature works in production CI runs. - -Describe "GitHub Actions Annotations Demo" -Tag 'CI' { - Context "Demonstration of annotation feature" { - It "Should fail to demonstrate GitHub Actions annotations - REMOVE THIS TEST" { - # This test intentionally fails to show the annotation feature - # When it fails, Show-PSPesterError will generate: - # ::error file=test/powershell/Modules/DemoGitHubAnnotations.Tests.ps1,line=,title=... - # - # The annotation will appear as a clickable link in: - # - The GitHub Actions "Annotations" section - # - The PR "Files changed" tab (if in a PR) - # - # After confirming annotations appear correctly, DELETE THIS FILE. - - $expected = "Pass" - $actual = "Fail" - - $actual | Should -Be $expected -Because "This test demonstrates GitHub Actions annotations for test failures. DELETE THIS FILE after verifying the feature works." - } - } -}