From 078c61ffd9e8bbec5703bb8cd5eab925fbbff4c0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 19:12:08 +0000 Subject: [PATCH 1/9] Initial plan From 5d8328e9f5cec65952e53e8e9496b1399284bfd3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 19:15:49 +0000 Subject: [PATCH 2/9] Add GitHub UI reporting for Pester test results and coverage Co-authored-by: blindzero <13959569+blindzero@users.noreply.github.com> --- .github/workflows/ci.yml | 52 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5277b7f6..e491c1eb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,6 +20,7 @@ jobs: permissions: contents: read actions: write + checks: write strategy: fail-fast: false @@ -43,6 +44,57 @@ jobs: artifacts/test-results.xml artifacts/coverage.xml + - name: Publish test results + if: always() && (github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository) + uses: dorny/test-reporter@v1 + with: + name: Pester Tests (${{ matrix.os }}) + path: artifacts/test-results.xml + reporter: dotnet-nunit + fail-on-error: false + + - name: Publish coverage report + if: always() && matrix.os == 'ubuntu-latest' && (github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository) + uses: madrapps/jacoco-report@v1.7.1 + with: + paths: artifacts/coverage.xml + token: ${{ secrets.GITHUB_TOKEN }} + min-coverage-overall: 0 + min-coverage-changed-files: 0 + title: Code Coverage Report + update-comment: true + skip-if-no-changes: false + + - name: Add coverage to job summary + if: always() && matrix.os == 'ubuntu-latest' + shell: pwsh + run: | + $coverageXml = 'artifacts/coverage.xml' + if (Test-Path $coverageXml) { + [xml]$coverage = Get-Content $coverageXml + $report = $coverage.report + + # JaCoCo XML structure: counter elements with type and covered/missed attributes + $lineCounter = $report.counter | Where-Object { $_.type -eq 'LINE' } | Select-Object -First 1 + + if ($lineCounter) { + $covered = [int]$lineCounter.covered + $missed = [int]$lineCounter.missed + $total = $covered + $missed + + if ($total -gt 0) { + $percentage = [math]::Round(($covered / $total) * 100, 2) + $summary = @" + ## Code Coverage Summary + + - **Coverage:** $percentage% + - **Lines Covered:** $covered / $total + "@ + $summary | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Append + } + } + } + lint: name: PSScriptAnalyzer runs-on: ubuntu-latest From 8b47a71e16130e852a4e67dfc24de14343a4976e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 19:16:37 +0000 Subject: [PATCH 3/9] Update documentation to reflect GitHub UI reporting Co-authored-by: blindzero <13959569+blindzero@users.noreply.github.com> --- CONTRIBUTING.md | 2 ++ docs/advanced/testing.md | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b300db60..5d29e526 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -151,6 +151,8 @@ Outputs: - `artifacts/test-results.xml` (NUnitXml) - `artifacts/coverage.xml` (coverage report) +> **Note:** In CI, these artifacts are automatically published in GitHub's UI as checks and PR comments, so reviewers don't need to download artifacts to see test results or coverage. + ### Run static analysis (PSScriptAnalyzer) Run PSScriptAnalyzer using the repository settings: diff --git a/docs/advanced/testing.md b/docs/advanced/testing.md index e83f7bb4..3ad84b9a 100644 --- a/docs/advanced/testing.md +++ b/docs/advanced/testing.md @@ -69,6 +69,14 @@ Expected outputs: - `artifacts/test-results.xml` (NUnitXml test results) - `artifacts/coverage.xml` (code coverage report; format depends on configuration) +In addition to uploading these artifacts, CI automatically publishes: + +- **Test results** as a GitHub Check (visible in PR checks and workflow runs) +- **Code coverage** as a GitHub Check with inline PR comments +- **Coverage summary** in the workflow run summary + +This allows reviewers to see test failures and coverage directly in GitHub's UI without downloading artifacts. + ## Static analysis IdLE uses **PSScriptAnalyzer** as a CI quality gate to enforce baseline style and correctness rules. From 71497ef977a077ec1f88984f712dbdd6a3581a80 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 19:20:07 +0000 Subject: [PATCH 4/9] Fix coverage summary script to correctly parse JaCoCo XML Co-authored-by: blindzero <13959569+blindzero@users.noreply.github.com> --- .github/workflows/ci.yml | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e491c1eb..6be68b94 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -72,25 +72,23 @@ jobs: $coverageXml = 'artifacts/coverage.xml' if (Test-Path $coverageXml) { [xml]$coverage = Get-Content $coverageXml - $report = $coverage.report - # JaCoCo XML structure: counter elements with type and covered/missed attributes - $lineCounter = $report.counter | Where-Object { $_.type -eq 'LINE' } | Select-Object -First 1 - - if ($lineCounter) { - $covered = [int]$lineCounter.covered - $missed = [int]$lineCounter.missed - $total = $covered + $missed + # Get the report element and its direct counter children (JaCoCo XML format) + $reportElement = $coverage.SelectSingleNode('//report') + if ($reportElement) { + $counters = $reportElement.ChildNodes | Where-Object { $_.Name -eq 'counter' } + $lineCounter = $counters | Where-Object { $_.type -eq 'LINE' } | Select-Object -First 1 - if ($total -gt 0) { - $percentage = [math]::Round(($covered / $total) * 100, 2) - $summary = @" - ## Code Coverage Summary - - - **Coverage:** $percentage% - - **Lines Covered:** $covered / $total - "@ - $summary | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Append + if ($lineCounter) { + $covered = [int]$lineCounter.covered + $missed = [int]$lineCounter.missed + $total = $covered + $missed + + if ($total -gt 0) { + $percentage = [math]::Round(($covered / $total) * 100, 2) + $summary = "## Code Coverage Summary`n`n- **Coverage:** $percentage%`n- **Lines Covered:** $covered / $total" + $summary | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Append + } } } } From 9952c699c8f7e44f1f430dc53f7f5a917ace9f6a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 19:22:28 +0000 Subject: [PATCH 5/9] Improve coverage script readability and performance Co-authored-by: blindzero <13959569+blindzero@users.noreply.github.com> --- .github/workflows/ci.yml | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6be68b94..caf2f2f3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -73,9 +73,9 @@ jobs: if (Test-Path $coverageXml) { [xml]$coverage = Get-Content $coverageXml - # Get the report element and its direct counter children (JaCoCo XML format) - $reportElement = $coverage.SelectSingleNode('//report') - if ($reportElement) { + # Get the report element (root) and its direct counter children (JaCoCo XML format) + $reportElement = $coverage.DocumentElement + if ($reportElement -and $reportElement.LocalName -eq 'report') { $counters = $reportElement.ChildNodes | Where-Object { $_.Name -eq 'counter' } $lineCounter = $counters | Where-Object { $_.type -eq 'LINE' } | Select-Object -First 1 @@ -86,7 +86,15 @@ jobs: if ($total -gt 0) { $percentage = [math]::Round(($covered / $total) * 100, 2) - $summary = "## Code Coverage Summary`n`n- **Coverage:** $percentage%`n- **Lines Covered:** $covered / $total" + + # Build summary using array and join for better readability + $summaryLines = @( + '## Code Coverage Summary' + '' + "- **Coverage:** $percentage%" + "- **Lines Covered:** $covered / $total" + ) + $summary = $summaryLines -join "`n" $summary | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Append } } From dd179f3b0576cc69cd886f76484d862e69772224 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 19:44:58 +0000 Subject: [PATCH 6/9] Add strict mode and error handling to coverage summary script Co-authored-by: blindzero <13959569+blindzero@users.noreply.github.com> --- .github/workflows/ci.yml | 61 +++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 23 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index caf2f2f3..d3a1d45c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,36 +69,51 @@ jobs: if: always() && matrix.os == 'ubuntu-latest' shell: pwsh run: | + Set-StrictMode -Version Latest + $ErrorActionPreference = 'Stop' + $coverageXml = 'artifacts/coverage.xml' if (Test-Path $coverageXml) { - [xml]$coverage = Get-Content $coverageXml - - # Get the report element (root) and its direct counter children (JaCoCo XML format) - $reportElement = $coverage.DocumentElement - if ($reportElement -and $reportElement.LocalName -eq 'report') { - $counters = $reportElement.ChildNodes | Where-Object { $_.Name -eq 'counter' } - $lineCounter = $counters | Where-Object { $_.type -eq 'LINE' } | Select-Object -First 1 + try { + [xml]$coverage = Get-Content $coverageXml - if ($lineCounter) { - $covered = [int]$lineCounter.covered - $missed = [int]$lineCounter.missed - $total = $covered + $missed + # Get the report element (root) and its direct counter children (JaCoCo XML format) + $reportElement = $coverage.DocumentElement + if ($reportElement -and $reportElement.LocalName -eq 'report') { + $counters = $reportElement.ChildNodes | Where-Object { $_.Name -eq 'counter' } + $lineCounter = $counters | Where-Object { $_.type -eq 'LINE' } | Select-Object -First 1 - if ($total -gt 0) { - $percentage = [math]::Round(($covered / $total) * 100, 2) - - # Build summary using array and join for better readability - $summaryLines = @( - '## Code Coverage Summary' - '' - "- **Coverage:** $percentage%" - "- **Lines Covered:** $covered / $total" - ) - $summary = $summaryLines -join "`n" - $summary | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Append + if ($lineCounter -and + $lineCounter.PSObject.Properties['covered'] -and + $lineCounter.PSObject.Properties['missed']) { + try { + $covered = [int]$lineCounter.covered + $missed = [int]$lineCounter.missed + $total = $covered + $missed + + if ($total -gt 0) { + $percentage = [math]::Round(($covered / $total) * 100, 2) + + # Build summary using array and join for better readability + $summaryLines = @( + '## Code Coverage Summary' + '' + "- **Coverage:** $percentage%" + "- **Lines Covered:** $covered / $total" + ) + $summary = $summaryLines -join "`n" + $summary | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Append + } + } + catch { + Write-Host 'Skipping coverage summary due to invalid or malformed coverage data.' + } } } } + catch { + Write-Host 'Skipping coverage summary due to error reading coverage file.' + } } lint: From dc204c3dcc476b2b64c881063b1d7949e396b0ac Mon Sep 17 00:00:00 2001 From: Matthias <13959569+blindzero@users.noreply.github.com> Date: Mon, 26 Jan 2026 20:55:02 +0100 Subject: [PATCH 7/9] Fix: change $_.Name to $_.LocalName Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d3a1d45c..c55de60b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -80,7 +80,7 @@ jobs: # Get the report element (root) and its direct counter children (JaCoCo XML format) $reportElement = $coverage.DocumentElement if ($reportElement -and $reportElement.LocalName -eq 'report') { - $counters = $reportElement.ChildNodes | Where-Object { $_.Name -eq 'counter' } + $counters = $reportElement.ChildNodes | Where-Object { $_.LocalName -eq 'counter' } $lineCounter = $counters | Where-Object { $_.type -eq 'LINE' } | Select-Object -First 1 if ($lineCounter -and From 546ea5b399bfbd8a9a455f10c24c15140aead9ca Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 20:08:10 +0000 Subject: [PATCH 8/9] Fix test reporter: use java-junit instead of dotnet-nunit for NUnit 2.5 XML Co-authored-by: blindzero <13959569+blindzero@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c55de60b..b6fdf818 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,7 +50,7 @@ jobs: with: name: Pester Tests (${{ matrix.os }}) path: artifacts/test-results.xml - reporter: dotnet-nunit + reporter: java-junit fail-on-error: false - name: Publish coverage report From 969f5a8770266a4e3d1936c1b54971eda1247b2d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 26 Jan 2026 20:18:01 +0000 Subject: [PATCH 9/9] Change Pester output from NUnitXml to JUnitXml format for test reporter compatibility Co-authored-by: blindzero <13959569+blindzero@users.noreply.github.com> --- CONTRIBUTING.md | 2 +- docs/advanced/testing.md | 2 +- tools/Invoke-IdlePesterTests.ps1 | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5d29e526..6e36c8ee 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -148,7 +148,7 @@ To generate CI-like artifacts (test results + coverage) under `artifacts/`: Outputs: -- `artifacts/test-results.xml` (NUnitXml) +- `artifacts/test-results.xml` (JUnitXml) - `artifacts/coverage.xml` (coverage report) > **Note:** In CI, these artifacts are automatically published in GitHub's UI as checks and PR comments, so reviewers don't need to download artifacts to see test results or coverage. diff --git a/docs/advanced/testing.md b/docs/advanced/testing.md index 3ad84b9a..109272fd 100644 --- a/docs/advanced/testing.md +++ b/docs/advanced/testing.md @@ -66,7 +66,7 @@ The CI pipeline produces test artifacts under the `artifacts/` folder and upload Expected outputs: -- `artifacts/test-results.xml` (NUnitXml test results) +- `artifacts/test-results.xml` (JUnitXml test results) - `artifacts/coverage.xml` (code coverage report; format depends on configuration) In addition to uploading these artifacts, CI automatically publishes: diff --git a/tools/Invoke-IdlePesterTests.ps1 b/tools/Invoke-IdlePesterTests.ps1 index d37a7a1e..4daae10b 100644 --- a/tools/Invoke-IdlePesterTests.ps1 +++ b/tools/Invoke-IdlePesterTests.ps1 @@ -7,7 +7,7 @@ This script is the canonical entry point for running Pester in the IdLE reposito It is designed to be: - deterministic (fixed artifact paths under repo root) -- CI-friendly (NUnitXml results + coverage output on demand) +- CI-friendly (JUnitXml results + coverage output on demand) - robust against different working directories (resolves paths relative to repo root) The script ensures Pester is available and imports it before running tests. @@ -17,11 +17,11 @@ Path to the tests folder. Defaults to 'tests' relative to the repository root. .PARAMETER CI Enables CI mode: -- Writes NUnitXml test results to -TestResultsPath +- Writes JUnitXml test results to -TestResultsPath - Enables code coverage and writes a coverage report to -CoverageOutputPath .PARAMETER TestResultsPath -Path to the NUnitXml test results file. Defaults to 'artifacts/test-results.xml' +Path to the JUnitXml test results file. Defaults to 'artifacts/test-results.xml' relative to the repository root. .PARAMETER EnableCoverage @@ -214,7 +214,7 @@ $config.Output.Verbosity = 'Detailed' if ($emitTestResults -and $resolvedTestResultsPath) { $config.TestResult.Enabled = $true - $config.TestResult.OutputFormat = 'NUnitXml' + $config.TestResult.OutputFormat = 'JUnitXml' $config.TestResult.OutputPath = $resolvedTestResultsPath }