Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .github/actions/Get-PesterCodeCoverage/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ This GitHub Action is a part of the [PSModule framework](https://github.com/PSMo
## Features

- Combines multiple code coverage reports from parallel test runs
- Generates markdown/HTML tables showing missed & executed commands
- Generates Markdown/HTML tables showing missed & executed commands
- Displays analyzed files and coverage statistics
- Generates a generic missed-path report (Markdown + JSON)
- Configurable step summary sections
- Threshold enforcement for minimum code coverage

Expand Down Expand Up @@ -40,6 +41,10 @@ This GitHub Action is a part of the [PSModule framework](https://github.com/PSMo

## Outputs

| Name | Description |
| ---- | ----------- |
| `MissedPathReportPath` | Folder path containing `CodeCoverage-MissedPaths.md` and `CodeCoverage-MissedPaths.json` |

### GitHub Step Summary

The action generates a detailed summary visible in the GitHub Actions UI:
Expand All @@ -53,6 +58,7 @@ The action generates a detailed summary visible in the GitHub Actions UI:
- **Missed Commands**: HTML table with code snippets
- **Executed Commands**: HTML table with code snippets
- **Analyzed Files**: List of covered files
- **Missed Paths**: Aggregated missed-command counts grouped by file path

Example summary:

Expand Down
5 changes: 5 additions & 0 deletions .github/actions/Get-PesterCodeCoverage/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ inputs:
description: The target for code coverage.
required: false

outputs:
MissedPathReportPath:
description: Path to the generated missed-path report folder.
value: ${{ steps.Get-PesterCodeCoverage.outputs.MissedPathReportPath }}

runs:
using: composite
steps:
Expand Down
79 changes: 79 additions & 0 deletions .github/actions/Get-PesterCodeCoverage/src/main.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,73 @@ $success = $coveragePercent -ge $coveragePercentTarget
$statusIcon = $success ? '✅' : '❌'
$stats | Format-Table -AutoSize | Out-String

$missedPathGroups = $codeCoverage.CommandsMissed |
Where-Object { -not [string]::IsNullOrWhiteSpace($_.File) } |
Group-Object -Property File |
Sort-Object -Property @(
@{ Expression = 'Count'; Descending = $true },
@{ Expression = 'Name'; Descending = $false }
)

$missedPaths = foreach ($group in $missedPathGroups) {
$lines = $group.Group |
Where-Object { $_.Line -is [ValueType] } |
ForEach-Object { [int]$_.Line } |
Sort-Object -Unique
$missedLines = if ($lines.Count -eq 0) { '' } else { $lines -join ', ' }
[pscustomobject]@{
Path = $group.Name
MissedCommands = [int]$group.Count
MissedLines = $missedLines
}
}

$missedPathReportPath = 'CodeCoverage-MissedPaths'
$null = New-Item -Path $missedPathReportPath -ItemType Directory -Force

$missedPathReport = [ordered]@{
GeneratedAtUtc = [DateTime]::UtcNow.ToString('o')
CoveragePercent = [double]$coveragePercent
CoverageTarget = [double]$coveragePercentTarget
MissedPaths = @($missedPaths)
}

$missedPathReportJsonPath = Join-Path -Path $missedPathReportPath -ChildPath 'CodeCoverage-MissedPaths.json'
$missedPathReportMarkdownPath = Join-Path -Path $missedPathReportPath -ChildPath 'CodeCoverage-MissedPaths.md'
$missedPathReport | ConvertTo-Json -Depth 10 | Set-Content -Path $missedPathReportJsonPath -Encoding utf8NoBOM

$missedPathMarkdown = [System.Collections.Generic.List[string]]::new()
$null = $missedPathMarkdown.Add('# Code Coverage Missed Paths')
$null = $missedPathMarkdown.Add('')
$null = $missedPathMarkdown.Add("| Coverage | Target | Missed Paths | Missed Commands |")
$null = $missedPathMarkdown.Add("| --- | --- | --- | --- |")
$coverageSummaryRow = '| {0}% | {1}% | {2} | {3} |' -f (
[Math]::Round($coveragePercent, 2)
), (
[Math]::Round($coveragePercentTarget, 2)
), $missedPaths.Count, $codeCoverage.CommandsMissedCount
$null = $missedPathMarkdown.Add($coverageSummaryRow)
$null = $missedPathMarkdown.Add('')
$null = $missedPathMarkdown.Add('## Paths')
$null = $missedPathMarkdown.Add('| Path | Missed Commands | Missed Lines |')
$null = $missedPathMarkdown.Add('| --- | ---: | --- |')
if ($missedPaths.Count -eq 0) {
$null = $missedPathMarkdown.Add('| _None_ | 0 | |')
} else {
foreach ($pathEntry in $missedPaths) {
$safePath = ([string]$pathEntry.Path).Replace('|', '\|')
$safeLines = ([string]$pathEntry.MissedLines).Replace('|', '\|')
$null = $missedPathMarkdown.Add("| $safePath | $($pathEntry.MissedCommands) | $safeLines |")
}
}

$missedPathMarkdown | Set-Content -Path $missedPathReportMarkdownPath -Encoding utf8NoBOM
Write-Output 'Missed path report generated:'
$missedPaths | Select-Object -First 20 | Format-Table -Property Path, MissedCommands, MissedLines -AutoSize | Out-String
if ($env:GITHUB_OUTPUT) {
"MissedPathReportPath=$missedPathReportPath" >> $env:GITHUB_OUTPUT
}

# Build HTML table for 'missed' commands
$tableheader = @'
<table>
Expand Down Expand Up @@ -300,6 +367,18 @@ LogGroup 'Step Summary - Set step summary' {
}
}
}

Details "Missed paths [$($missedPaths.Count)]" {
if ($missedPaths.Count -eq 0) {
Paragraph {
Write-Output 'No missed paths were detected.'
}
} else {
Table {
$missedPaths | Select-Object -First 100
}
}
}
}
}

Expand Down
8 changes: 8 additions & 0 deletions .github/workflows/Get-CodeCoverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,11 @@ jobs:
Prerelease: ${{ fromJson(inputs.Settings).Prerelease }}
Verbose: ${{ fromJson(inputs.Settings).Verbose }}
Version: ${{ fromJson(inputs.Settings).Version }}

- name: Upload CodeCoverage-MissedPaths artifact
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: CodeCoverage-MissedPaths
path: CodeCoverage-MissedPaths
if-no-files-found: warn
Loading