Skip to content

Commit 8893be8

Browse files
committed
fix: normalize JaCoCo coverage before CodeScene gate
1 parent 6e8aaea commit 8893be8

6 files changed

Lines changed: 162 additions & 44 deletions

File tree

.github/actions/check-coverage/action.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,21 @@ runs:
2323
unzip -oq "$(pwd)/cs-coverage.zip"
2424
chmod +x "$(pwd)/cs-coverage"
2525
26+
- name: Normalize JaCoCo coverage paths
27+
shell: bash
28+
run: |
29+
set -euo pipefail
30+
shopt -s globstar nullglob
31+
files=( ${{ inputs.coverage-files }} )
32+
if [ ${#files[@]} -eq 0 ]; then
33+
echo "No coverage files matched '${{ inputs.coverage-files }}' for normalization."
34+
exit 0
35+
fi
36+
37+
for file in "${files[@]}"; do
38+
pwsh -NoLogo -NoProfile -File ./scripts/build/ci/Repair-CodeSceneJaCoCoCoverage.ps1 -Path "$file"
39+
done
40+
2641
- name: Run CodeScene coverage gate check
2742
shell: bash
2843
env:

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
5555
- `Test-NovaBuild` and `% nova test` no longer emit `Remove-Item -Recurse` progress bars during the Pester run; `$global:ProgressPreference` is saved and restored around the test execution so CI logs stay clean.
5656
- Code coverage in `Test-NovaBuild` now runs against the source files, instead of the built `dist/<ModuleName>/<ModuleName>.psm1` file so measured coverage percentages and line numbers align with the deployed module.
5757
- `Invoke-CodeSceneAnalysis.ps1` now normalizes Pester's JaCoCo output before upload so the `<class sourcefilename>` and `<sourcefile name>` entries are bare filenames and `package + sourcefile` resolves to real repository paths. This fixes the CodeScene `cs-coverage upload` failure "The coverage data does not contains any records related to the current repo" that appeared after the source-mirrored coverage migration.
58+
- The pull-request CodeScene coverage gate now normalizes downloaded JaCoCo coverage artifacts before running `cs-coverage check`, so PRs no longer report `0.0%` coverage across the board because of unnormalized `package + sourcefile` paths.
5859
- `Invoke-CodeSceneAnalysis.ps1` now only invokes the `cs-coverage upload … --metric branch-coverage` step when the JaCoCo report actually contains `<counter type="BRANCH">` entries, so Pester reports that only emit line counters no longer fail the CodeScene step with "Requested metric is not present in coverage data".
5960
- Pester coverage configuration now uses explicit private-folder depth globs instead of `src/private/**/*.ps1`, so nested helpers such as `src/private/build/manifest/` and `src/private/quality/duplicates/` are included in `artifacts/coverage.xml` during the full `Test-NovaBuild` run.
6061

scripts/build/ci/Invoke-CodeSceneAnalysis.ps1

Lines changed: 1 addition & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -103,49 +103,6 @@ function Invoke-CodeSceneAnalysisTrigger {
103103
}
104104
}
105105

106-
function Repair-PesterJaCoCoCoveragePath {
107-
param([Parameter(Mandatory)][string]$Path)
108-
109-
[xml]$document = Get-Content -LiteralPath $Path -Raw
110-
$changed = Repair-CodeSceneJaCoCoNodePathAttribute -Nodes $document.SelectNodes('//class[@sourcefilename]') -AttributeName 'sourcefilename'
111-
$changed = (Repair-CodeSceneJaCoCoNodePathAttribute -Nodes $document.SelectNodes('//sourcefile[@name]') -AttributeName 'name') -or $changed
112-
113-
if ($changed) {
114-
$document.Save($Path)
115-
Write-Host "Normalized JaCoCo sourcefile paths in '$Path' so package + sourcefile resolve to repo files."
116-
}
117-
}
118-
119-
function Repair-CodeSceneJaCoCoNodePathAttribute {
120-
param(
121-
[Parameter(Mandatory)]$Nodes,
122-
[Parameter(Mandatory)][string]$AttributeName
123-
)
124-
125-
$changed = $false
126-
foreach ($node in $Nodes) {
127-
$changed = (Repair-CodeSceneJaCoCoNodeFileName -Node $node -AttributeName $AttributeName) -or $changed
128-
}
129-
130-
return $changed
131-
}
132-
133-
function Repair-CodeSceneJaCoCoNodeFileName {
134-
param(
135-
[Parameter(Mandatory)]$Node,
136-
[Parameter(Mandatory)][string]$AttributeName
137-
)
138-
139-
$original = $Node.GetAttribute($AttributeName)
140-
$normalized = [System.IO.Path]::GetFileName($original)
141-
if ($normalized -eq $original) {
142-
return $false
143-
}
144-
145-
$Node.SetAttribute($AttributeName, $normalized)
146-
return $true
147-
}
148-
149106
function Test-JaCoCoBranchCoverageAvailable {
150107
param([Parameter(Mandatory)][string]$Path)
151108

@@ -167,7 +124,7 @@ $accessToken = Get-RequiredCodeSceneValue -Name 'CS_ACCESS_TOKEN'
167124

168125
if ($shouldUploadCoverage) {
169126
$resolvedCoveragePath = Resolve-CodeSceneCoveragePath -CoveragePath $CoveragePath
170-
Repair-PesterJaCoCoCoveragePath -Path $resolvedCoveragePath
127+
& (Join-Path $PSScriptRoot 'Repair-CodeSceneJaCoCoCoverage.ps1') -Path $resolvedCoveragePath
171128

172129
if (-not (Get-Command -Name 'cs-coverage' -ErrorAction SilentlyContinue)) {
173130
throw "The 'cs-coverage' CLI was not found on PATH. Install the CodeScene coverage upload tool before running this script."
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
param(
2+
[Parameter(Mandatory)][string[]]$Path
3+
)
4+
5+
Set-StrictMode -Version Latest
6+
$ErrorActionPreference = 'Stop'
7+
8+
function Repair-CodeSceneJaCoCoNodePathAttribute {
9+
param(
10+
[Parameter(Mandatory)]$Nodes,
11+
[Parameter(Mandatory)][string]$AttributeName
12+
)
13+
14+
$changed = $false
15+
foreach ($node in $Nodes) {
16+
$changed = (Repair-CodeSceneJaCoCoNodeFileName -Node $node -AttributeName $AttributeName) -or $changed
17+
}
18+
19+
return $changed
20+
}
21+
22+
function Repair-CodeSceneJaCoCoNodeFileName {
23+
param(
24+
[Parameter(Mandatory)]$Node,
25+
[Parameter(Mandatory)][string]$AttributeName
26+
)
27+
28+
$original = $Node.GetAttribute($AttributeName)
29+
$normalized = [System.IO.Path]::GetFileName($original)
30+
if ($normalized -eq $original) {
31+
return $false
32+
}
33+
34+
$Node.SetAttribute($AttributeName, $normalized)
35+
return $true
36+
}
37+
38+
function Repair-CodeSceneJaCoCoCoverageFile {
39+
param([Parameter(Mandatory)][string]$Path)
40+
41+
[xml]$document = Get-Content -LiteralPath $Path -Raw
42+
$changed = Repair-CodeSceneJaCoCoNodePathAttribute -Nodes $document.SelectNodes('//class[@sourcefilename]') -AttributeName 'sourcefilename'
43+
$changed = (Repair-CodeSceneJaCoCoNodePathAttribute -Nodes $document.SelectNodes('//sourcefile[@name]') -AttributeName 'name') -or $changed
44+
45+
if ($changed) {
46+
$document.Save($Path)
47+
Write-Host "Normalized JaCoCo sourcefile paths in '$Path' so package + sourcefile resolve to repo files."
48+
}
49+
}
50+
51+
foreach ($item in $Path) {
52+
$resolvedPath = (Resolve-Path -LiteralPath $item -ErrorAction Stop).Path
53+
Repair-CodeSceneJaCoCoCoverageFile -Path $resolvedPath
54+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
BeforeAll {
2+
$script:actionPath = Join-Path $PSScriptRoot '..' '.github' 'actions' 'check-coverage' 'action.yml'
3+
$script:actionContent = Get-Content -LiteralPath $script:actionPath -Raw
4+
}
5+
6+
Describe 'check-coverage action' {
7+
It 'normalizes JaCoCo coverage paths before running the gate check' {
8+
$script:actionContent | Should -Match 'Normalize JaCoCo coverage paths'
9+
$script:actionContent | Should -Match 'Repair-CodeSceneJaCoCoCoverage\.ps1'
10+
}
11+
12+
It 'keeps the gate check on the configured coverage glob' {
13+
$script:actionContent | Should -Match 'cs-coverage"\s+check\s+--verbose\s+--coverage-files\s+"\$\{\{\s*inputs\.coverage-files\s*\}\}"'
14+
}
15+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
BeforeAll {
2+
$script:repairCoverageScriptPath = (Resolve-Path -LiteralPath (Join-Path $PSScriptRoot '..' 'scripts' 'build' 'ci' 'Repair-CodeSceneJaCoCoCoverage.ps1')).Path
3+
4+
function Invoke-RepairCodeSceneJaCoCoCoverageTestScript {
5+
[CmdletBinding()]
6+
param(
7+
[Parameter(Mandatory)][string]$RunnerContent
8+
)
9+
10+
$runnerPath = Join-Path $TestDrive 'Run-RepairCodeSceneJaCoCoCoverage.ps1'
11+
$content = @"
12+
$RunnerContent
13+
14+
if (`$null -ne `$LASTEXITCODE) {
15+
exit `$LASTEXITCODE
16+
}
17+
18+
if (`$?) {
19+
exit 0
20+
}
21+
22+
exit 1
23+
"@
24+
Set-Content -LiteralPath $runnerPath -Value $content -Encoding utf8
25+
26+
$output = & pwsh -NoLogo -NoProfile -File $runnerPath 2>&1
27+
return [pscustomobject]@{
28+
ExitCode = $LASTEXITCODE
29+
Output = @($output)
30+
}
31+
}
32+
}
33+
34+
Describe 'Repair-CodeSceneJaCoCoCoverage' {
35+
It 'normalizes class and sourcefile names to bare filenames' {
36+
$coveragePath = Join-Path $TestDrive 'coverage.xml'
37+
Set-Content -LiteralPath $coveragePath -Encoding utf8 -Value @'
38+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
39+
<report name="Pester">
40+
<package name="src/private/build">
41+
<class name="src/private/build/Foo" sourcefilename="private/build/Foo.ps1" />
42+
<sourcefile name="private/build/Foo.ps1" />
43+
</package>
44+
</report>
45+
'@
46+
47+
$runnerContent = "& '$script:repairCoverageScriptPath' -Path '$coveragePath'"
48+
$result = Invoke-RepairCodeSceneJaCoCoCoverageTestScript -RunnerContent $runnerContent
49+
50+
$result.ExitCode | Should -Be 0 -Because ($result.Output -join [Environment]::NewLine)
51+
[xml]$rewritten = Get-Content -LiteralPath $coveragePath -Raw
52+
$rewritten.SelectSingleNode('//class[@sourcefilename]').GetAttribute('sourcefilename') | Should -Be 'Foo.ps1'
53+
$rewritten.SelectSingleNode('//sourcefile[@name]').GetAttribute('name') | Should -Be 'Foo.ps1'
54+
}
55+
56+
It 'leaves already normalized filenames unchanged' {
57+
$coveragePath = Join-Path $TestDrive 'coverage-already-normalized.xml'
58+
Set-Content -LiteralPath $coveragePath -Encoding utf8 -Value @'
59+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
60+
<report name="Pester">
61+
<package name="src/private/build">
62+
<class name="src/private/build/Foo" sourcefilename="Foo.ps1" />
63+
<sourcefile name="Foo.ps1" />
64+
</package>
65+
</report>
66+
'@
67+
68+
$runnerContent = "& '$script:repairCoverageScriptPath' -Path '$coveragePath'"
69+
$result = Invoke-RepairCodeSceneJaCoCoCoverageTestScript -RunnerContent $runnerContent
70+
71+
$result.ExitCode | Should -Be 0 -Because ($result.Output -join [Environment]::NewLine)
72+
[xml]$rewritten = Get-Content -LiteralPath $coveragePath -Raw
73+
$rewritten.SelectSingleNode('//class[@sourcefilename]').GetAttribute('sourcefilename') | Should -Be 'Foo.ps1'
74+
$rewritten.SelectSingleNode('//sourcefile[@name]').GetAttribute('name') | Should -Be 'Foo.ps1'
75+
}
76+
}

0 commit comments

Comments
 (0)