Skip to content

Commit 1362f7c

Browse files
committed
feat(#194): simplify CI to single test run with JaCoCo coverage upload
- Remove second Invoke-Pester run from Invoke-NovaModuleToolsCI.ps1; Test-NovaBuild now runs once and produces artifacts/coverage.xml - Remove Cobertura conversion helpers, SetSourcePath guard, and Get-CiPesterConfiguration/Get-CiTestPath functions from CI script - Update Invoke-CodeSceneAnalysis.ps1 to resolve artifacts/coverage.xml as the default JaCoCo coverage path instead of *.cobertura.xml - Upload coverage twice: --format jacoco --metric line-coverage and --metric branch-coverage - Add --format jacoco to cs-coverage check in check-coverage action - Update Tests.yml coverage-files glob to **/coverage.xml - Update CodeSceneAnalysis tests for JaCoCo double-upload behaviour - Update testing-policy instructions to document single-run JaCoCo flow
1 parent ea5606a commit 1362f7c

7 files changed

Lines changed: 34 additions & 73 deletions

File tree

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,5 @@ runs:
3030
CS_ACCESS_TOKEN: ${{ inputs.access-token }}
3131
run: |
3232
set -euo pipefail
33-
find "$(pwd)" -name '*.cobertura.xml' -print
33+
find "$(pwd)" -name 'coverage.xml' -print
3434
"$(pwd)/cs-coverage" check --verbose --coverage-files "${{ inputs.coverage-files }}"

.github/instructions/testing-policy.instructions.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ Use this file when changing production code, tests, coverage behavior, or CI tes
3636
## Coverage and CodeScene
3737

3838
- CI coverage is generated by `./scripts/build/ci/Invoke-NovaModuleToolsCI.ps1`.
39-
- The Cobertura artifact is reused by the CodeScene PR coverage gate and by the develop/manual CodeScene analysis flow.
39+
- `Test-NovaBuild` runs once and produces a JaCoCo coverage report at `artifacts/coverage.xml`.
40+
- The JaCoCo artifact is reused by the CodeScene PR coverage gate and by the develop/manual CodeScene analysis flow.
41+
- The CodeScene analysis upload sends coverage twice: once for `line-coverage` and once for `branch-coverage`.
4042
- If CodeScene flags coverage or duplication, fix the underlying test design instead of suppressing the warning casually.
4143

4244
## Common pitfalls

.github/workflows/Tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ jobs:
119119
with:
120120
access-token: ${{ secrets.CS_ACCESS_TOKEN }}
121121
project-url: ${{ format('{0}/v2/projects/{1}', vars.CS_URL, vars.CS_PROJECT_ID) }}
122-
coverage-files: '**/pester-coverage.cobertura.xml'
122+
coverage-files: '**/coverage.xml'
123123

124124
codescene-analysis:
125125
runs-on: ubuntu-latest

scripts/build/ci/Invoke-CodeSceneAnalysis.ps1

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -58,18 +58,12 @@ function Resolve-CodeSceneCoveragePath {
5858
return (Resolve-Path -LiteralPath $CoveragePath -ErrorAction Stop).Path
5959
}
6060

61-
$artifactsDir = Join-Path (Get-Location) 'artifacts'
62-
$coverageCandidates = @(Get-ChildItem -LiteralPath $artifactsDir -Filter '*.cobertura.xml' -File -Recurse -ErrorAction SilentlyContinue | Sort-Object FullName)
63-
if (-not $coverageCandidates) {
64-
throw "No Cobertura coverage file was found under '$artifactsDir'. Provide -CoveragePath explicitly or run the CI coverage workflow first."
61+
$defaultPath = Join-Path (Get-Location) 'artifacts' 'coverage.xml'
62+
if (-not (Test-Path -LiteralPath $defaultPath)) {
63+
throw "No JaCoCo coverage file was found at '$defaultPath'. Provide -CoveragePath explicitly or run the CI coverage workflow first."
6564
}
6665

67-
if ($coverageCandidates.Count -gt 1) {
68-
$candidateList = ($coverageCandidates | ForEach-Object FullName) -join ', '
69-
throw "Multiple Cobertura coverage files were found under '$artifactsDir'. Provide -CoveragePath explicitly. Candidates: $candidateList"
70-
}
71-
72-
return $coverageCandidates[0].FullName
66+
return $defaultPath
7367
}
7468

7569
function Invoke-CodeSceneAnalysisTrigger {
@@ -128,9 +122,14 @@ if ($shouldUploadCoverage) {
128122
throw "The 'cs-coverage' CLI was not found on PATH. Install the CodeScene coverage upload tool before running this script."
129123
}
130124

131-
& cs-coverage upload --format 'cobertura' --metric 'line-coverage' $resolvedCoveragePath
125+
& cs-coverage upload --format 'jacoco' --metric 'line-coverage' $resolvedCoveragePath
126+
if ($LASTEXITCODE -ne 0) {
127+
throw "CodeScene line-coverage upload failed with exit code $LASTEXITCODE."
128+
}
129+
130+
& cs-coverage upload --format 'jacoco' --metric 'branch-coverage' $resolvedCoveragePath
132131
if ($LASTEXITCODE -ne 0) {
133-
throw "CodeScene coverage upload failed with exit code $LASTEXITCODE."
132+
throw "CodeScene branch-coverage upload failed with exit code $LASTEXITCODE."
134133
}
135134
}
136135

scripts/build/ci/Invoke-NovaModuleToolsCI.ps1

Lines changed: 1 addition & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -5,42 +5,6 @@ param(
55

66
Set-StrictMode -Version Latest
77

8-
. (Join-Path $PSScriptRoot 'CodeSceneCoverageMap.ps1')
9-
. (Join-Path $PSScriptRoot 'CodeSceneCoverageXml.ps1')
10-
. (Join-Path $PSScriptRoot 'CoverageLowReport.ps1')
11-
12-
function Get-CiTestPath {
13-
param([Parameter(Mandatory)][pscustomobject]$ProjectInfo)
14-
15-
if ($ProjectInfo.BuildRecursiveFolders) {
16-
return $ProjectInfo.TestsDir
17-
}
18-
19-
return [System.IO.Path]::Join($ProjectInfo.TestsDir, '*.Tests.ps1')
20-
}
21-
22-
function Get-CiPesterConfiguration {
23-
param(
24-
[Parameter(Mandatory)][pscustomobject]$ProjectInfo,
25-
[Parameter(Mandatory)][string]$ArtifactsDirectory,
26-
[string[]]$ExcludedTags = @()
27-
)
28-
29-
$configuration = New-PesterConfiguration
30-
$configuration.Run.Path = Get-CiTestPath -ProjectInfo $ProjectInfo
31-
$configuration.Run.PassThru = $true
32-
$configuration.Filter.ExcludeTag = @($ExcludedTags)
33-
$configuration.TestResult.Enabled = $true
34-
$configuration.TestResult.OutputFormat = 'JUnitXml'
35-
$configuration.TestResult.OutputPath = (Join-Path $ArtifactsDirectory 'pester-junit.xml')
36-
$configuration.CodeCoverage.Enabled = $true
37-
$configuration.CodeCoverage.Path = @($ProjectInfo.ModuleFilePSM1)
38-
$configuration.CodeCoverage.OutputFormat = 'Cobertura'
39-
$configuration.CodeCoverage.OutputPath = (Join-Path $ArtifactsDirectory 'pester-coverage.cobertura.xml')
40-
41-
return $configuration
42-
}
43-
448
function Copy-NovaModuleToolsTestResultIfPresent {
459
param(
4610
[Parameter(Mandatory)][string]$ProjectRoot,
@@ -58,7 +22,6 @@ Set-Location $repoRoot
5822
New-Item -ItemType Directory -Path $OutputDirectory -Force | Out-Null
5923

6024
Import-Module NovaModuleTools -ErrorAction Stop
61-
Import-Module Pester -ErrorAction Stop
6225

6326
Invoke-NovaBuild
6427

@@ -68,10 +31,6 @@ Remove-Module $projectInfo.ProjectName -ErrorAction SilentlyContinue
6831
Import-Module $builtModulePath -Force
6932
$projectInfo = Get-NovaProjectInfo
7033

71-
if (-not $projectInfo.SetSourcePath) {
72-
throw "Code coverage upload requires project.json to set SetSourcePath=true so dist line coverage can be remapped back to src/ files for CodeScene."
73-
}
74-
7534
$novaModuleToolsTestFailed = $false
7635
try {
7736
if (@($ExcludeTag).Count -gt 0) {
@@ -86,11 +45,6 @@ try {
8645
Copy-NovaModuleToolsTestResultIfPresent -ProjectRoot $projectInfo.ProjectRoot -ArtifactsDirectory $OutputDirectory
8746
}
8847

89-
$configuration = Get-CiPesterConfiguration -ProjectInfo $projectInfo -ArtifactsDirectory $OutputDirectory -ExcludedTags $ExcludeTag
90-
$result = Invoke-Pester -Configuration $configuration
91-
Convert-CoberturaCoverageToSourcePath -CoveragePath (Join-Path $OutputDirectory 'pester-coverage.cobertura.xml') -BuiltModulePath $projectInfo.ModuleFilePSM1 -RepoRoot $projectInfo.ProjectRoot
92-
Write-CoverageLowReport -CoveragePath (Join-Path $OutputDirectory 'pester-coverage.cobertura.xml') -OutputPath (Join-Path $OutputDirectory 'coverage-low.txt')
93-
94-
if ($novaModuleToolsTestFailed -or $result.FailedCount -gt 0) {
48+
if ($novaModuleToolsTestFailed) {
9549
exit 1
9650
}

src/resources/agentic-copilot/.github/instructions/testing-policy.instructions.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ Use this file when changing production code, tests, coverage behavior, or CI tes
3636
## Coverage and quality tooling
3737

3838
- CI coverage is generated by the repository-specific CI helper when one exists.
39-
- Coverage artifacts should stay compatible with the repository quality flow when one exists.
39+
- `Test-NovaBuild` runs once and produces a JaCoCo coverage report at `artifacts/coverage.xml`.
40+
- The JaCoCo artifact is reused by the quality tooling PR coverage gate and by the develop/manual quality tooling analysis flow.
41+
- The quality tooling analysis upload sends coverage twice: once for `line-coverage` and once for `branch-coverage`.
4042
- If quality tooling flags coverage or duplication, fix the underlying test design instead of suppressing the warning casually.
4143

4244
## Common pitfalls

tests/CodeSceneAnalysis.Tests.ps1

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,15 @@ function Invoke-WebRequest {
8282
}
8383

8484
It 'still uploads coverage when CoveragePath is provided' {
85-
$coveragePath = Join-Path $TestDrive 'pester-coverage.cobertura.xml'
85+
$coveragePath = Join-Path $TestDrive 'coverage.xml'
8686
$uploadLogPath = Join-Path $TestDrive 'cs-coverage-upload.txt'
87-
Set-Content -LiteralPath $coveragePath -Value '<coverage />' -Encoding utf8
87+
Set-Content -LiteralPath $coveragePath -Value '<report />' -Encoding utf8
8888

8989
$runnerContent = @"
9090
function cs-coverage {
9191
param([Parameter(ValueFromRemainingArguments = `$true)][string[]]`$ArgumentList)
9292
93-
Set-Content -LiteralPath '$uploadLogPath' -Value (`$ArgumentList -join ' ') -Encoding utf8
93+
Add-Content -LiteralPath '$uploadLogPath' -Value (`$ArgumentList -join ' ') -Encoding utf8
9494
`$global:LASTEXITCODE = 0
9595
}
9696
@@ -104,21 +104,23 @@ function cs-coverage {
104104
$result = Invoke-CodeSceneAnalysisTestScript -RunnerContent $runnerContent
105105

106106
$result.ExitCode | Should -Be 0 -Because ($result.Output -join [Environment]::NewLine)
107-
(Get-Content -LiteralPath $uploadLogPath -Raw) | Should -BeLike "upload --format cobertura --metric line-coverage $coveragePath*"
107+
$uploadLog = Get-Content -LiteralPath $uploadLogPath -Raw
108+
$uploadLog | Should -BeLike "upload --format jacoco --metric line-coverage $coveragePath*"
109+
$uploadLog | Should -Match 'upload --format jacoco --metric branch-coverage'
108110
}
109111

110112
It 'uploads coverage from the artifacts folder when UploadCoverage is requested without CoveragePath' {
111113
$artifactsDir = Join-Path $TestDrive 'artifacts'
112-
$coveragePath = Join-Path $artifactsDir 'ci-generated.cobertura.xml'
114+
$coveragePath = Join-Path $artifactsDir 'coverage.xml'
113115
$uploadLogPath = Join-Path $TestDrive 'cs-coverage-upload-discovered.txt'
114116
New-Item -ItemType Directory -Path $artifactsDir -Force | Out-Null
115-
Set-Content -LiteralPath $coveragePath -Value '<coverage />' -Encoding utf8
117+
Set-Content -LiteralPath $coveragePath -Value '<report />' -Encoding utf8
116118

117119
$runnerContent = @"
118120
function cs-coverage {
119121
param([Parameter(ValueFromRemainingArguments = `$true)][string[]]`$ArgumentList)
120122
121-
Set-Content -LiteralPath '$uploadLogPath' -Value (`$ArgumentList -join ' ') -Encoding utf8
123+
Add-Content -LiteralPath '$uploadLogPath' -Value (`$ArgumentList -join ' ') -Encoding utf8
122124
`$global:LASTEXITCODE = 0
123125
}
124126
@@ -133,10 +135,12 @@ Set-Location '$TestDrive'
133135
$result = Invoke-CodeSceneAnalysisTestScript -RunnerContent $runnerContent
134136

135137
$result.ExitCode | Should -Be 0 -Because ($result.Output -join [Environment]::NewLine)
136-
(Get-Content -LiteralPath $uploadLogPath -Raw) | Should -BeLike "upload --format cobertura --metric line-coverage $coveragePath*"
138+
$uploadLog = Get-Content -LiteralPath $uploadLogPath -Raw
139+
$uploadLog | Should -BeLike "upload --format jacoco --metric line-coverage $coveragePath*"
140+
$uploadLog | Should -Match 'upload --format jacoco --metric branch-coverage'
137141
}
138142

139-
It 'fails clearly when UploadCoverage is requested but no Cobertura artifact exists' {
143+
It 'fails clearly when UploadCoverage is requested but no JaCoCo artifact exists' {
140144
$emptyWorkingDir = Join-Path $TestDrive 'no-coverage-artifacts'
141145
New-Item -ItemType Directory -Path $emptyWorkingDir -Force | Out-Null
142146

@@ -156,6 +160,6 @@ Set-Location '$emptyWorkingDir'
156160
$result = Invoke-CodeSceneAnalysisTestScript -RunnerContent $runnerContent
157161

158162
$result.ExitCode | Should -Not -Be 0
159-
($result.Output -join [Environment]::NewLine) | Should -Match 'No Cobertura coverage file was found under'
163+
($result.Output -join [Environment]::NewLine) | Should -Match 'No JaCoCo coverage file was found at'
160164
}
161165
}

0 commit comments

Comments
 (0)