Skip to content

Bump coverlet.collector from 6.0.4 to 8.0.0 #139

Bump coverlet.collector from 6.0.4 to 8.0.0

Bump coverlet.collector from 6.0.4 to 8.0.0 #139

Workflow file for this run

# Sequential PR validation workflow with coverage gating
# Stage 1: Linux tests with 90% coverage requirement
# Stage 2: Windows and macOS tests (only if Linux passes)
# Stage 3: .NET Framework 4.x tests on Windows (only if Stage 2 passes)
name: PR Checks v2 (Gated)
permissions:
contents: read
on:
pull_request:
branches:
- main
paths-ignore:
- '**.md'
- 'docs/**'
jobs:
# ============================================================================
# STAGE 1: Linux - .NET Core/5+ Tests with Coverage Gate
# ============================================================================
test-linux-core:
name: "Stage 1: Linux Tests (.NET 5.0-10.0) + Coverage Gate"
runs-on: ubuntu-latest
if: github.repository != 'Chris-Wolfgang/repo-template'
steps:
- name: Checkout code
uses: actions/checkout@v4
# Fix for .NET 5.0 on Ubuntu 22.04+ - install libssl1.1
- name: Install OpenSSL 1.1 for .NET 5.0
run: |
wget https://archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2_amd64.deb
sudo dpkg -i libssl1.1_1.1.1f-1ubuntu2_amd64.deb
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
5.0.x
6.0.x
7.0.x
8.0.x
9.0.x
10.0.x
- name: Check Code Formatting
run: |
# dotnet format is built into .NET 6+ SDK (no separate installation needed)
# Find solution file
solution=$(find . -maxdepth 2 \( -name "*.sln" -o -name "*.slnx" \) | head -n 1)
if [ -n "$solution" ]; then
echo "Checking formatting for: $solution"
dotnet format "$solution" --verify-no-changes --verbosity diagnostic
else
echo "No solution file found, checking entire workspace"
dotnet format --verify-no-changes --verbosity diagnostic
fi
- name: Restore and build (exclude .NET Framework 4.x projects)
run: |
echo "Finding all projects in solution..."
# Get list of all projects from solution file
if [ -f "*.sln" ]; then
sln_file=$(ls *.sln | head -n 1)
echo "Using solution file: $sln_file"
fi
# Find all .csproj, .vbproj, and .fsproj files
# Exclude those with 'dotnet4' or 'DotNet4' in the path (case-insensitive)
projects=$(find . -type f \( -name "*.csproj" -o -name "*.vbproj" -o -name "*.fsproj" \) | grep -iv "dotnet4")
if [ -z "$projects" ]; then
echo "No projects found!"
exit 1
fi
echo "=========================================="
echo "Projects to build (excluding .NET Framework 4.x):"
echo "=========================================="
echo "$projects"
echo ""
# Restore each project
echo "Restoring projects..."
for proj in $projects; do
echo "Restoring: $proj"
dotnet restore "$proj" || exit 1
done
echo ""
echo "Building projects..."
# Build each project
for proj in $projects; do
echo "Building: $proj"
dotnet build "$proj" --no-restore --configuration Release || exit 1
done
echo ""
echo "✅ All compatible projects built successfully"
- name: Run tests with coverage (.NET Core 5.0 - 10.0)
run: |
# Find all test projects
test_projects=$(find ./tests -type f -name "*.csproj")
if [ -z "$test_projects" ]; then
echo "❌ No test projects found in ./tests directory!"
exit 1
fi
echo "=========================================="
echo "Found test projects:"
echo "=========================================="
echo "$test_projects"
echo ""
# Test each framework individually to ensure all are tested
frameworks=(net5.0 net6.0 net7.0 net8.0 net9.0 net10.0)
for test_proj in $test_projects; do
echo "=========================================="
echo "Testing project: $test_proj"
echo "=========================================="
for fw in "${frameworks[@]}"; do
echo "Testing framework: $fw"
dotnet test "$test_proj" \
--configuration Release \
--framework "$fw" \
--collect:"XPlat Code Coverage" \
--results-directory "./TestResults" \
--logger "console;verbosity=minimal" || exit 1
done
echo ""
done
- name: Install ReportGenerator
run: dotnet tool install -g dotnet-reportgenerator-globaltool
- name: Generate coverage report
run: |
reportgenerator \
-reports:"TestResults/**/coverage.cobertura.xml" \
-targetdir:"CoverageReport" \
-reporttypes:"Html;TextSummary;MarkdownSummaryGithub;CsvSummary"
- name: Enforce 90% coverage threshold
run: |
if [ ! -f CoverageReport/Summary.txt ]; then
echo "❌ Coverage report not generated!"
exit 1
fi
echo "Coverage Summary:"
cat CoverageReport/Summary.txt
echo ""
# Validate that the report contains expected content
if ! grep -q "Summary" CoverageReport/Summary.txt; then
echo "❌ Coverage report format is invalid!"
exit 1
fi
failed_projects=""
threshold=90
while read -r line; do
# Match lines with module names and percentages
if echo "$line" | grep -qE '^[^ ].*[0-9]+%$' && ! echo "$line" | grep -q '^Summary'; then
module=$(echo "$line" | awk '{print $1}')
percent=$(echo "$line" | awk '{print $NF}' | tr -d '%')
echo "Checking module: '$module' - Coverage: ${percent}%"
if [ "$percent" -lt "$threshold" ]; then
echo " ❌ FAIL: Below ${threshold}% threshold"
failed_projects="$failed_projects $module (${percent}%)"
else
echo " ✅ PASS: Meets ${threshold}% threshold"
fi
fi
done < CoverageReport/Summary.txt
if [ -n "$failed_projects" ]; then
echo ""
echo "=========================================="
echo "❌ COVERAGE GATE FAILED"
echo "=========================================="
echo "Projects below ${threshold}% coverage: $failed_projects"
echo ""
echo "Stage 1 failed. Windows, macOS, and .NET Framework tests will NOT run."
exit 1
else
echo ""
echo "=========================================="
echo "✅ COVERAGE GATE PASSED"
echo "=========================================="
echo "All projects meet ${threshold}% coverage threshold."
echo "Proceeding to Stage 2 (Windows and macOS tests)."
fi
- name: Upload Linux coverage results
if: always()
uses: actions/upload-artifact@v4
with:
name: coverage-linux
path: |
TestResults/
CoverageReport/
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: build-output
path: |
src/**/bin/Release
tests/**/bin/Release
# ============================================================================
# STAGE 2: Windows & macOS - .NET Core/5+ Tests (Gated by Stage 1)
# ============================================================================
test-windows-core:
name: "Stage 2a: Windows Tests (.NET 5.0-10.0)"
runs-on: windows-latest
needs: test-linux-core
if: github.repository != 'Chris-Wolfgang/repo-template'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
5.0.x
6.0.x
7.0.x
8.0.x
9.0.x
10.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build solution
run: dotnet build --no-restore --configuration Release
- name: Run tests (.NET Core 5.0 - 10.0)
shell: pwsh
run: |
$testProjects = Get-ChildItem -Path './tests' -Recurse -Filter '*.csproj'
if ($testProjects.Count -eq 0) {
Write-Error "❌ No test projects found in ./tests directory!"
exit 1
}
Write-Host "==========================================" -ForegroundColor Cyan
Write-Host "Found test projects:" -ForegroundColor Cyan
Write-Host "==========================================" -ForegroundColor Cyan
$testProjects | ForEach-Object { Write-Host $_.FullName -ForegroundColor White }
Write-Host ""
$frameworks = @('net5.0', 'net6.0', 'net7.0', 'net8.0', 'net9.0', 'net10.0')
foreach ($testProj in $testProjects) {
Write-Host "==========================================" -ForegroundColor Cyan
Write-Host "Testing project: $($testProj.FullName)" -ForegroundColor Cyan
Write-Host "==========================================" -ForegroundColor Cyan
foreach ($fw in $frameworks) {
Write-Host "Testing framework: $fw" -ForegroundColor Yellow
dotnet test $testProj.FullName `
--configuration Release `
--framework $fw `
--logger "console;verbosity=normal"
if ($LASTEXITCODE -ne 0) {
Write-Error "Tests failed for $fw in $($testProj.Name)"
exit 1
}
}
Write-Host ""
}
test-macos-core:
name: "Stage 2b: macOS Tests (.NET 6.0-10.0)"
runs-on: macos-latest
needs: test-linux-core
if: github.repository != 'Chris-Wolfgang/repo-template'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
6.0.x
7.0.x
8.0.x
9.0.x
10.0.x
- name: Restore and build (exclude .NET Framework 4.x projects)
run: |
echo "Finding all projects in solution..."
# Find all .csproj, .vbproj, and .fsproj files
# Exclude those with 'dotnet4' or 'DotNet4' in the path (case-insensitive)
projects=$(find . -type f \( -name "*.csproj" -o -name "*.vbproj" -o -name "*.fsproj" \) | grep -iv "dotnet4")
if [ -z "$projects" ]; then
echo "No projects found!"
exit 1
fi
echo "=========================================="
echo "Projects to build (excluding .NET Framework 4.x):"
echo "=========================================="
echo "$projects"
echo ""
# Restore each project
echo "Restoring projects..."
for proj in $projects; do
echo "Restoring: $proj"
dotnet restore "$proj" || exit 1
done
echo ""
echo "Building projects..."
# Build each project
for proj in $projects; do
echo "Building: $proj"
dotnet build "$proj" --no-restore --configuration Release || exit 1
done
echo ""
echo "✅ All compatible projects built successfully"
- name: Run tests (.NET 6.0 - 10.0 only - ARM64 compatible)
run: |
# Find all test projects
test_projects=$(find ./tests -type f -name "*.csproj")
if [ -z "$test_projects" ]; then
echo "❌ No test projects found in ./tests directory!"
exit 1
fi
echo "=========================================="
echo "Found test projects:"
echo "=========================================="
echo "$test_projects"
echo ""
# Skip .NET Core 5.0 and .NET 5.0 - no ARM64 support on macOS
frameworks=(net6.0 net7.0 net8.0 net9.0 net10.0)
for test_proj in $test_projects; do
echo "=========================================="
echo "Testing project: $test_proj"
echo "=========================================="
for fw in "${frameworks[@]}"; do
echo "Testing framework: $fw"
dotnet test "$test_proj" \
--configuration Release \
--framework "$fw" \
--logger "console;verbosity=normal" || exit 1
done
echo ""
done
- name: Display macOS architecture info
if: always()
run: |
echo ""
echo "=========================================="
echo "ℹ️ macOS Testing Notes"
echo "=========================================="
echo "Architecture: $(uname -m)"
echo ""
echo "Skipped frameworks (no ARM64 support):"
echo " - .NET 5.0 ❌"
echo ""
echo "Tested frameworks (ARM64 compatible):"
echo " - .NET 6.0 ✅"
echo " - .NET 7.0 ✅"
echo " - .NET 8.0 ✅"
echo " - .NET 9.0 ✅"
echo " - .NET 10.0 ✅"
echo ""
echo ".NET Core 5.0 are tested on Linux and Windows"
# ============================================================================
# STAGE 3: Windows - .NET Framework 4.x Tests (Gated by Stage 2)
# ============================================================================
test-windows-framework:
name: "Stage 3: Windows .NET Framework Tests (4.6.2-4.8.1)"
runs-on: windows-latest
needs: [test-windows-core, test-macos-core]
if: github.repository != 'Chris-Wolfgang/repo-template'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
8.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build solution
run: dotnet build --no-restore --configuration Release
- name: Run .NET Framework tests (4.6.2 - 4.8.1)
shell: pwsh
run: |
$testProjects = Get-ChildItem -Path './tests' -Recurse -Filter '*.csproj'
if ($testProjects.Count -eq 0) {
Write-Error "❌ No test projects found in ./tests directory!"
exit 1
}
Write-Host "==========================================" -ForegroundColor Cyan
Write-Host "Found test projects:" -ForegroundColor Cyan
Write-Host "==========================================" -ForegroundColor Cyan
$testProjects | ForEach-Object { Write-Host $_.FullName -ForegroundColor White }
Write-Host ""
$frameworks = @('net462', 'net472', 'net48', 'net481')
foreach ($testProj in $testProjects) {
Write-Host "==========================================" -ForegroundColor Cyan
Write-Host "Testing project: $($testProj.FullName)" -ForegroundColor Cyan
Write-Host "==========================================" -ForegroundColor Cyan
foreach ($fw in $frameworks) {
Write-Host "Testing framework: $fw" -ForegroundColor Yellow
dotnet test $testProj.FullName `
--configuration Release `
--framework $fw `
--logger "console;verbosity=normal"
if ($LASTEXITCODE -ne 0) {
Write-Error "Tests failed for $fw in $($testProj.Name)"
exit 1
}
}
Write-Host ""
}
Write-Host ""
Write-Host "==========================================" -ForegroundColor Green
Write-Host "✅ ALL STAGES PASSED" -ForegroundColor Green
Write-Host "==========================================" -ForegroundColor Green
Write-Host "Stage 1: Linux tests + 90% coverage ✅" -ForegroundColor Green
Write-Host "Stage 2: Windows & macOS tests ✅" -ForegroundColor Green
Write-Host "Stage 3: .NET Framework 4.x tests ✅" -ForegroundColor Green
Write-Host ""
Write-Host "PR is ready to merge! 🎉" -ForegroundColor Green
# ============================================================================
# Security Scan (Runs in parallel with tests)
# ============================================================================
security-scan:
name: "Security Scan (DevSkim)"
runs-on: ubuntu-latest
if: github.repository != 'Chris-Wolfgang/repo-template'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install DevSkim CLI
run: dotnet tool install --global Microsoft.CST.DevSkim.CLI
- name: Run DevSkim security scan
run: |
devskim analyze \
--source-code . \
--file-format text \
--output-file devskim-results.txt \
-E \
--ignore-rule-ids DS176209 \
--ignore-globs "**/api/**,**/CoverageReport/**,**/TestResults/**"
- name: Display security scan results
if: always()
run: |
if [ -f devskim-results.txt ]; then
echo "=========================================="
echo "DevSkim Security Scan Results"
echo "=========================================="
cat devskim-results.txt
echo ""
if grep -qi "error\|critical\|high" devskim-results.txt; then
echo "⚠️ Security issues detected - review required"
else
echo "✅ No critical security issues found"
fi
else
echo "✅ No security issues found"
fi
- name: Upload security scan results
if: always()
uses: actions/upload-artifact@v4
with:
name: devskim-results
path: devskim-results.txt
if-no-files-found: warn
# ============================================================================
# CodeQL Analysis (Runs in parallel with tests)
# ============================================================================
codeql-analysis:
name: "CodeQL"
runs-on: ubuntu-latest
if: github.repository != 'Chris-Wolfgang/repo-template'
permissions:
actions: read
contents: read
security-events: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: csharp
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
8.0.x
- name: Build for CodeQL Analysis
run: |
echo "Building solution for CodeQL analysis..."
# Find solution file (.sln or .slnx)
solution=$(find . -maxdepth 2 \( -name "*.sln" -o -name "*.slnx" \) | head -n 1)
if [ -n "$solution" ]; then
echo "Found solution: $solution"
dotnet restore "$solution"
dotnet build "$solution" --configuration Release --no-restore
else
echo "No solution file found, building all projects..."
dotnet restore
dotnet build --configuration Release --no-restore
fi
echo "✅ Build completed for CodeQL analysis"
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:csharp"