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
39 changes: 26 additions & 13 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,10 @@ jobs:
fetch-depth: 0
- name: Install prerequisites
run: ./build.ps1 -SkipBuild -Clippy -Verbose
- name: Build
run: ./build.ps1 -Clippy -Verbose
- name: Run rust tests with code coverage
- name: Build and test with code coverage
id: rust-tests
continue-on-error: true
run: ./build.ps1 -SkipBuild -Test -CodeCoverage -ExcludePesterTests -Verbose
run: ./build.ps1 -Clippy -Test -CodeCoverage -Verbose -PesterTestGroup resources
- name: Upload coverage data
if: always()
uses: actions/upload-artifact@v4
Expand Down Expand Up @@ -110,12 +108,10 @@ jobs:
fetch-depth: 0
- name: Install prerequisites
run: ./build.ps1 -SkipBuild -Clippy -Verbose
- name: Build
run: ./build.ps1 -Clippy -Verbose
- name: Run rust tests with code coverage
- name: Build and test with code coverage
id: rust-tests
continue-on-error: true
run: ./build.ps1 -SkipBuild -Test -CodeCoverage -ExcludePesterTests -Verbose
run: ./build.ps1 -Clippy -Test -CodeCoverage -Verbose -PesterTestGroup resources
- name: Upload coverage data
if: always()
uses: actions/upload-artifact@v4
Expand Down Expand Up @@ -172,12 +168,10 @@ jobs:
fetch-depth: 0
- name: Install prerequisites
run: ./build.ps1 -SkipBuild -Clippy -Verbose
- name: Build
run: ./build.ps1 -Clippy -Verbose
- name: Run rust tests with code coverage
- name: Build and test with code coverage
id: rust-tests
continue-on-error: true
run: ./build.ps1 -SkipBuild -Test -CodeCoverage -ExcludePesterTests -Verbose
run: ./build.ps1 -Clippy -Test -CodeCoverage -Verbose -PesterTestGroup resources
- name: Upload coverage data
if: always()
uses: actions/upload-artifact@v4
Expand Down Expand Up @@ -251,8 +245,18 @@ jobs:
$baseSha = '${{ github.event.pull_request.base.sha }}'
$headSha = '${{ github.event.pull_request.head.sha }}'

# Compute the merge-base to get an accurate diff of only PR changes.
# Using base.sha directly is unreliable after rebases or when the base
# branch has advanced, since it points to the tip of the base branch at
# event time rather than the common ancestor.
$mergeBase = git merge-base $baseSha $headSha 2>$null
if ($LASTEXITCODE -eq 0 -and $mergeBase) {
Write-Verbose -Verbose "Using merge-base $mergeBase (base=$baseSha, head=$headSha)"
$baseSha = $mergeBase
}

# Determine if any Rust files changed from git diff
$changedFiles = git diff --name-only --diff-filter=ACMR "$baseSha...$headSha" -- '*.rs' | Where-Object { $_ }
$changedFiles = git diff --name-only --diff-filter=ACMR "$baseSha..$headSha" -- '*.rs' | Where-Object { $_ }
if (-not $changedFiles) {
"has_rust_changes=false" | Out-File -Append -Encoding utf8 -FilePath $env:GITHUB_OUTPUT
return
Expand Down Expand Up @@ -322,3 +326,12 @@ jobs:
## Code Coverage Report

No Rust files were changed in this PR. Coverage analysis skipped.

- name: Fail if coverage is below 70%
if: >-
steps.coverage.outputs.has_rust_changes == 'true'
&& steps.coverage.outputs.coverage_failed != 'true'
&& steps.coverage.outputs.percentage < 70
run: |
Write-Error "Code coverage is ${{ steps.coverage.outputs.percentage }}%, which is below the 70% minimum threshold."
exit 1
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,8 @@ nix = { version = "0.31.3" }
num-traits = { version = "0.2" }
# dsc-lib-osinfo
os_info = { version = "3.15" }
# dsc-lib-osinfo
version-compare = { version = "0.2" }
# dsc, dsc-lib
path-absolutize = { version = "3.1" }
# dsc-bicep-ext
Expand Down
10 changes: 8 additions & 2 deletions build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,9 @@ process {
Write-BuildProgress @progressParams -Status 'Configuring cargo-llvm-cov environment'
Remove-Item $CodeCoverageOutputPath -Force -ErrorAction Ignore
Initialize-CodeCoverage -UseCFS:$UseCFS @VerboseParam

Write-BuildProgress @progressParams -Status 'Setting llvm-cov environment variables'
$priorLlvmCovEnv = Set-LlvmCovEnvironment @VerboseParam
}
#endregion Code coverage instrumentation

Expand Down Expand Up @@ -297,7 +300,7 @@ process {
Clean = $Clean
}
Write-BuildProgress @progressParams -Status 'Compiling Rust'
Build-RustProject @buildParams -Audit:$Audit -Clippy:$Clippy -CodeCoverage:$CodeCoverage @VerboseParam
Build-RustProject @buildParams -Audit:$Audit -Clippy:$Clippy @VerboseParam
Write-BuildProgress @progressParams -Status "Copying build artifacts"
Copy-BuildArtifact @buildParams -ExecutableFile $BuildData.PackageFiles.Executable @VerboseParam
}
Expand All @@ -324,7 +327,7 @@ process {
$rustTestParams.TestFilter = $RustTestFilter
}
Write-BuildProgress @progressParams -Status "Testing Rust projects"
Test-RustProject @rustTestParams -CodeCoverage:$CodeCoverage @VerboseParam
Test-RustProject @rustTestParams @VerboseParam
}
if ($RustDocs) {
$docTestParams = @{
Expand Down Expand Up @@ -360,6 +363,9 @@ process {
Write-BuildProgress @progressParams -Status "Writing LCOV report to $CodeCoverageOutputPath"
Export-CodeCoverageReport -OutputPath $CodeCoverageOutputPath @VerboseParam

Write-BuildProgress @progressParams -Status 'Restoring environment variables'
Reset-LlvmCovEnvironment -PriorValues $priorLlvmCovEnv @VerboseParam

# Determine base and head SHAs for analysis
$baseSha = $CodeCoverageBaseSha
$headSha = $CodeCoverageHeadSha
Expand Down
114 changes: 92 additions & 22 deletions helpers.build.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -1602,8 +1602,7 @@ function Build-RustProject {
[switch]$Clean,
[switch]$UpdateLockFile,
[switch]$Audit,
[switch]$Clippy,
[switch]$CodeCoverage
[switch]$Clippy
)

begin {
Expand Down Expand Up @@ -1875,14 +1874,14 @@ function Get-ChangedRustFile {
)

process {
$changedFiles = git diff --name-only --diff-filter=ACMR "$BaseSha...$HeadSha" -- '*.rs'
$changedFiles = git diff --name-only --diff-filter=ACMR "$BaseSha..$HeadSha" -- '*.rs'
if ($LASTEXITCODE -ne 0) {
Write-Warning "Failed to detect changed files between $BaseSha and $HeadSha"
return @()
}

$result = @($changedFiles | Where-Object { $_ })
Write-Verbose "Found $($result.Count) changed Rust file(s)"
Write-Verbose -Verbose "Found $($result.Count) changed Rust file(s)"
return $result
}
}
Expand All @@ -1894,9 +1893,9 @@ function Initialize-CodeCoverage {

.DESCRIPTION
Installs cargo-llvm-cov if needed and cleans any prior coverage artifacts from the
workspace. When coverage is enabled, Build-RustProject and Test-RustProject use
`cargo llvm-cov build` and `cargo llvm-cov test --no-report` respectively, which
handle all instrumentation and profraw management internally.
workspace. After initialization, call Set-LlvmCovEnvironment to set the environment
variables that make normal `cargo build` and `cargo test` invocations produce
instrumented binaries and write profraw data.
#>
[CmdletBinding()]
param(
Expand All @@ -1919,6 +1918,77 @@ function Initialize-CodeCoverage {
}
}

function Set-LlvmCovEnvironment {
<#
.SYNOPSIS
Sets the environment variables required by cargo-llvm-cov for instrumented builds.

.DESCRIPTION
Parses the output of `cargo llvm-cov show-env` and sets the corresponding
environment variables (LLVM_PROFILE_FILE, RUSTC_WRAPPER, CARGO_LLVM_COV, etc.)
in the current process. This enables a normal `cargo build` to produce
instrumented binaries, and allows externally invoked instrumented binaries
(such as during Pester tests) to write profraw data to a location that
`cargo llvm-cov report` can discover.

.OUTPUTS
System.Collections.Hashtable — Prior values of the modified environment variables
so they can be restored with Reset-LlvmCovEnvironment.
#>
[CmdletBinding()]
[OutputType([hashtable])]
param()

process {
$showEnvOutput = cargo llvm-cov show-env 2>&1
if ($LASTEXITCODE -ne 0) {
throw "Failed to retrieve cargo-llvm-cov environment: $showEnvOutput"
}

$priorValues = @{}
foreach ($line in $showEnvOutput) {
if ($line -match '^([A-Z_][A-Z0-9_]+)=(.*)$') {
$name = $Matches[1]
# CARGO_LLVM_COV_SHOW_ENV is an output-only flag that tells
# cargo-llvm-cov to print env and exit; do not propagate it.
if ($name -eq 'CARGO_LLVM_COV_SHOW_ENV') {
continue
}
# Strip optional surrounding single quotes from the value
$value = ($Matches[2] -replace "^'", '') -replace "'$", ''
$priorValues[$name] = [System.Environment]::GetEnvironmentVariable($name)
[System.Environment]::SetEnvironmentVariable($name, $value)
Write-Verbose "Set $name=$value"
}
}

Write-Verbose -Verbose "Set $($priorValues.Count) cargo-llvm-cov environment variables"
$priorValues
}
}

function Reset-LlvmCovEnvironment {
<#
.SYNOPSIS
Restores environment variables modified by Set-LlvmCovEnvironment.

.PARAMETER PriorValues
The hashtable returned by Set-LlvmCovEnvironment containing original values.
#>
[CmdletBinding()]
param(
[Parameter(Mandatory)]
[hashtable]$PriorValues
)

process {
foreach ($entry in $PriorValues.GetEnumerator()) {
[System.Environment]::SetEnvironmentVariable($entry.Key, $entry.Value)
}
Write-Verbose -Verbose "Restored $($PriorValues.Count) environment variables"
}
}

function Export-CodeCoverageReport {
<#
.SYNOPSIS
Expand Down Expand Up @@ -2077,7 +2147,7 @@ function Get-CodeCoverageReport {
}

# Parse diff to get added line numbers in the new file
$diffOutput = git diff "$BaseSha...$HeadSha" -- $file
$diffOutput = git diff "$BaseSha..$HeadSha" -- $file
$addedLineNumbers = @()
$currentLineNum = 0

Expand All @@ -2096,14 +2166,23 @@ function Get-CodeCoverageReport {

# Find matching LCOV entry for this file
$absPath = (Resolve-Path $file).Path
$normalizedFile = $file.Replace('\', '/')
$fileCoverage = $null
foreach ($key in $lcovData.Keys) {
if ($key -eq $absPath -or $key.EndsWith("/$file") -or $key.EndsWith("\$file")) {
$normalizedKey = $key.Replace('\', '/')
if ($normalizedKey -eq $absPath -or
$normalizedKey -eq $normalizedFile -or
$normalizedKey.EndsWith("/$normalizedFile") -or
$normalizedKey.EndsWith("\$normalizedFile")) {
$fileCoverage = $lcovData[$key]
break
}
}

if (-not $fileCoverage) {
Write-Verbose -Verbose "No LCOV match for '$file' (absPath='$absPath'). LCOV keys: $($lcovData.Keys -join ', ')"
}

# Build per-line coverage map for this file (only added executable lines)
$lineCoverageMap = @{}
if ($fileCoverage) {
Expand Down Expand Up @@ -2177,8 +2256,7 @@ function Test-RustProject {
$Architecture = 'current',
[switch]$Release,
[switch]$Docs,
[string]$TestFilter,
[switch]$CodeCoverage
[string]$TestFilter
)

begin {
Expand Down Expand Up @@ -2208,18 +2286,10 @@ function Test-RustProject {
} else {
Write-Verbose -Verbose "Testing rust projects: [$members]"
}
if ($CodeCoverage) {
if (-not [string]::IsNullOrEmpty($TestFilter)) {
cargo llvm-cov test --no-report @flags -- $TestFilter
} else {
cargo llvm-cov test --no-report @flags
}
if (-not [string]::IsNullOrEmpty($TestFilter)) {
cargo test @flags -- $TestFilter
} else {
if (-not [string]::IsNullOrEmpty($TestFilter)) {
cargo test @flags -- $TestFilter
} else {
cargo test @flags
}
cargo test @flags
}

if ($null -ne $LASTEXITCODE -and $LASTEXITCODE -ne 0) {
Expand Down
1 change: 1 addition & 0 deletions lib/dsc-lib-osinfo/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ doctest = false
os_info = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
version-compare = { workspace = true }
Loading
Loading