From be91bbd2dc4478a57a4007fc32fe320da79f85e1 Mon Sep 17 00:00:00 2001 From: 5an7y Date: Mon, 22 Jun 2026 17:31:47 -0700 Subject: Add NT-version-scoped exclusions and a parallel multi-version CI matrix Exclusions: - Add MinNtTargetVersion/MaxNtTargetVersion columns to exclusions.csv so a row can apply only within an _NT_TARGET_VERSION build-number range (parsed from -NtTargetVersion, e.g. 22000 from 10.0.22000). Import-SampleExclusions filters this range at load time alongside the existing build-number range; blank = unbounded. - Exclude the samples that fail only when linking against older library sets, with the reason taken from the build logs: audio.sysvad (<=22000, KSJACK_DESCRIPTION3); network.netadaptercx.netvadapter and network.wlan.wificx (<=22621, NDIS/DDI version); powerlimit.plclient/plpolicy (<=22621, POWER_LIMIT_ATTRIBUTES); storage classpnp/storahci (<=22621, STOR_ADDRESS_TYPE_NVME, Debug-only) and storage.msdsm (Debug|x64). usb.usbview is intentionally NOT excluded: it fails on every version for a known host reason (missing .NET 4.7.2/4.8.1 targeting packs). CI: - ci.yml and ci-pr.yml: add _NT_TARGET_VERSION as a manual matrix axis (4 newest versions, latest-first) so each version x configuration x platform runs on its own parallel runner. - Build-Samples.ps1 and Join-CsvReports.ps1 now write an easy-to-scan Markdown summary to GITHUB_STEP_SUMMARY: each build job shows counts and a failures table with the first error; the report job shows per-version totals and a consolidated failures list, plus the colour-coded sample x version HTML/CSV overview. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/scripts/Join-CsvReports.ps1 | 228 +++++++++++++++++++++++++++++++----- 1 file changed, 201 insertions(+), 27 deletions(-) (limited to '.github/scripts') diff --git a/.github/scripts/Join-CsvReports.ps1 b/.github/scripts/Join-CsvReports.ps1 index 32cd9d63..b3e6cd4f 100644 --- a/.github/scripts/Join-CsvReports.ps1 +++ b/.github/scripts/Join-CsvReports.ps1 @@ -1,35 +1,209 @@ -$logsPath = Join-Path (Get-Location).Path "_logs" +<# +.SYNOPSIS + Joins the per-job Build-Samples CSV reports (one per _NT_TARGET_VERSION x configuration x + platform) into a single overview, and writes an easy-to-scan summary to the GitHub Actions + run page ($GITHUB_STEP_SUMMARY). + +.DESCRIPTION + Each build job uploads a "_logs" folder containing a report named + _overview....csv + with columns: Sample, (one combination per file). This script: + * parses the _NT_TARGET_VERSION tag and combination from every report, + * collapses each sample's combinations into one status per version, + * writes _overview.all.csv / _overview.all.htm (a colour-coded sample x version matrix), and + * appends a Markdown summary (per-version totals + a failures table) to $GITHUB_STEP_SUMMARY + so failures are obvious from the run page without opening any logs. + + The older 2-part name (_overview...csv, no version) is still + understood and bucketed under the "latest" column. +#> + +$logsPath = Join-Path (Get-Location).Path "_logs" $reportFileName = '_overview.all' -$idProperty = 'Sample' -$results = $null -Get-ChildItem -Path $logsPath -Filter '*.csv' | ForEach-Object { - $csv = Import-Csv -Path $_ - if ($results -eq $null) { - $results = $csv +if (-not (Test-Path $logsPath)) { + Write-Warning "No _logs directory found at $logsPath - nothing to report." + return +} + +# --- Friendly labels for the known _NT_TARGET_VERSION build tags -------------- +$ntLabel = @{ + '28000' = 'latest'; '26100' = '24H2'; '22621' = '22H2'; '22000' = '21H2' + '20348' = 'Server 2022'; '19041' = '2004'; '18362' = '1903'; '17763' = '1809' +} + +# --- Load every per-job CSV --------------------------------------------------- +# data[sample][tag][combo] = status +$data = @{} +$allSamples = [System.Collections.Generic.SortedSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase) +$tagSet = [System.Collections.Generic.HashSet[string]]::new() + +Get-ChildItem -Path $logsPath -Filter '_overview.*.csv' | + Where-Object { $_.Name -notlike '_overview.all.*' } | + ForEach-Object { + # _overview... -> drop _overview; last two are config/platform. + $parts = [IO.Path]::GetFileNameWithoutExtension($_.Name).Split('.') + $parts = $parts[1..($parts.Count - 1)] # drop the leading "_overview" + if ($parts.Count -ge 3) { $tag = ($parts[0..($parts.Count - 3)] -join '.') } + else { $tag = 'latest' } + [void]$tagSet.Add($tag) + + Import-Csv -Path $_.FullName | ForEach-Object { + $sample = $_.Sample + if (-not $sample) { return } + [void]$allSamples.Add($sample) + if (-not $data.ContainsKey($sample)) { $data[$sample] = @{} } + if (-not $data[$sample].ContainsKey($tag)) { $data[$sample][$tag] = @{} } + foreach ($col in ($_.PSObject.Properties.Name | Where-Object { $_ -ne 'Sample' })) { + $data[$sample][$tag][$col] = "$($_.$col)".Trim() + } + } } - else { - $results = $csv | ForEach-Object { - $id = $_.$idProperty - $match = $results | Where-Object { $_.$idProperty -eq $id } - if ($match) { - $properties = $_ | Get-Member -MemberType NoteProperty | Where-Object { $_.Name -ne $idProperty } | Select-Object -ExpandProperty Name - $newObject = New-Object PSObject - # Add ID property separately to ensure it appears first - $newObject | Add-Member -MemberType NoteProperty -Name $idProperty -Value $_.$idProperty - foreach ($property in $properties) { - $newObject | Add-Member -MemberType NoteProperty -Name $property -Value $_.$property - } - foreach ($property in ($match | Get-Member -MemberType NoteProperty | Where-Object { $_.Name -ne $idProperty } | Select-Object -ExpandProperty Name)) { - if ($properties -notcontains $property) { - $newObject | Add-Member -MemberType NoteProperty -Name $property -Value $match.$property - } - } - $newObject + +if ($tagSet.Count -eq 0) { + Write-Warning "No per-job '_overview.*.csv' reports were found in $logsPath." + return +} + +# Order versions newest-first (numeric tags descending; non-numeric last) +$tags = $tagSet | Sort-Object @{ Expression = { if ($_ -match '^\d+$') { [int]$_ } else { 0 } }; Descending = $true }, @{ Expression = { $_ } } + +function Get-Status { + # Collapse one sample/version's combinations into a single status object. + param([hashtable]$Combos) + $c = @{ Succeeded = 0; Failed = 0; Sporadic = 0; Unsupported = 0; Excluded = 0 } + $details = @() + if ($Combos) { + foreach ($k in ($Combos.Keys | Sort-Object)) { + switch ($Combos[$k]) { + 'Succeeded' { $c.Succeeded++ } 'Failed' { $c.Failed++ } 'Sporadic' { $c.Sporadic++ } + 'Unsupported' { $c.Unsupported++ } 'Excluded' { $c.Excluded++ } + } + $details += "$k = $($Combos[$k])" + } + } + $buildable = $c.Succeeded + $c.Failed + $c.Sporadic + if (-not $Combos -or $Combos.Count -eq 0) { $label = 'n/a'; $klass = 'na' } + elseif ($buildable -eq 0) { $label = '--'; $klass = 'na' } + elseif ($c.Failed -eq 0 -and $c.Sporadic -eq 0) { $label = "PASS ($($c.Succeeded)/$buildable)"; $klass = 'pass' } + elseif ($c.Failed -eq 0) { $label = "PASS* ($($c.Succeeded + $c.Sporadic)/$buildable)"; $klass = 'flaky' } + elseif ($c.Failed -eq $buildable) { $label = "FAIL ($($c.Failed)/$buildable)"; $klass = 'fail' } + else { $label = "PARTIAL ($($c.Failed) failed / $buildable)"; $klass = 'partial' } + [pscustomobject]@{ Label = $label; Class = $klass; Tooltip = ($details -join ' | '); Counts = $c; Buildable = $buildable } +} + +# --- Build per-version totals + the matrix ------------------------------------ +$totals = @{}; foreach ($t in $tags) { $totals[$t] = [pscustomobject]@{ S = 0; F = 0; O = 0; U = 0; E = 0; pass = 0; flaky = 0; partial = 0; fail = 0; na = 0 } } +$failuresList = [System.Collections.ArrayList]::new() +$csvRows = @() +$bodyRows = New-Object System.Text.StringBuilder + +foreach ($sample in $allSamples) { + $csvRow = [ordered]@{ Sample = $sample } + $cells = '' + foreach ($t in $tags) { + $combos = $null + if ($data[$sample].ContainsKey($t)) { $combos = $data[$sample][$t] } + $st = Get-Status -Combos $combos + $tt = $totals[$t] + $tt.S += $st.Counts.Succeeded; $tt.F += $st.Counts.Failed; $tt.O += $st.Counts.Sporadic + $tt.U += $st.Counts.Unsupported; $tt.E += $st.Counts.Excluded + switch ($st.Class) { 'pass' { $tt.pass++ } 'flaky' { $tt.flaky++ } 'partial' { $tt.partial++ } 'fail' { $tt.fail++ } 'na' { $tt.na++ } } + $csvRow["$t"] = $st.Label + $tip = [System.Web.HttpUtility]::HtmlEncode($st.Tooltip) + $cells += "$($st.Label)" + if ($combos) { + foreach ($k in ($combos.Keys | Sort-Object)) { + if ($combos[$k] -eq 'Failed') { [void]$failuresList.Add([pscustomobject]@{ Sample = $sample; Version = $t; Combo = $k }) } } } } + $csvRows += [pscustomobject]$csvRow + $enc = [System.Web.HttpUtility]::HtmlEncode($sample) + [void]$bodyRows.Append("$enc$cells`n") } -$results | ConvertTo-Csv | Out-File (Join-Path $logsPath "$reportFileName.csv") -$results | ConvertTo-Html -Title "Overview" | Out-File (Join-Path $logsPath "$reportFileName.htm") +Add-Type -AssemblyName System.Web -ErrorAction SilentlyContinue + +# --- CSV ---------------------------------------------------------------------- +$csvRows | Export-Csv -Path (Join-Path $logsPath "$reportFileName.csv") -NoTypeInformation + +# --- HTML (colour-coded sample x version matrix) ------------------------------ +$generated = Get-Date -Format 'yyyy-MM-dd HH:mm:ss' +$sumHead = "_NT_TARGET_VERSIONReleasePassFlakyPartialFailn/aCombos OKSporadicFailedExcludedPass rate" +$sumRows = '' +foreach ($t in $tags) { + $x = $totals[$t]; $tot = $x.pass + $x.flaky + $x.partial + $x.fail + $x.na; $elig = $tot - $x.na + $rate = if ($elig -gt 0) { '{0:N0}%' -f (100.0 * ($x.pass + $x.flaky) / $elig) } else { 'n/a' } + $rel = $ntLabel[$t]; if (-not $rel) { $rel = '' } + $sumRows += "$t$rel$($x.pass)$($x.flaky)$($x.partial)$($x.fail)$($x.na)$($x.S)$($x.O)$($x.F)$($x.E)$rate`n" +} +$matHead = "Sample" +foreach ($t in $tags) { $rel = $ntLabel[$t]; if (-not $rel) { $rel = '' }; $matHead += "$t
$rel" } +$matHead += "" + +$html = @" + +WDK Driver Samples - Build Overview + +

WDK Driver Samples — Build Overview

+
Generated: $generated  |  columns are _NT_TARGET_VERSION (library link version); hover a cell for the per-combination breakdown.
+
PASSPASS* (retry)PARTIALFAIL-- n/a
+

Summary by _NT_TARGET_VERSION

+$sumHead +$sumRows
+

Sample × _NT_TARGET_VERSION

+$matHead +$($bodyRows.ToString())
+ +"@ +$html | Out-File -FilePath (Join-Path $logsPath "$reportFileName.htm") -Encoding UTF8 + +# --- GitHub Actions run summary (Markdown) ------------------------------------ +if ($env:GITHUB_STEP_SUMMARY) { + $totalFailed = ($totals.Values | Measure-Object -Property fail -Sum).Sum + ($totals.Values | Measure-Object -Property partial -Sum).Sum + $icon = if ($failuresList.Count -gt 0) { ':x:' } else { ':white_check_mark:' } + + $md = [System.Text.StringBuilder]::new() + [void]$md.AppendLine("# $icon WDK Driver Samples — Build Overview") + [void]$md.AppendLine() + [void]$md.AppendLine("Columns are **_NT_TARGET_VERSION** (the WDK library version drivers link against). Each version was built for Debug/Release x x64/arm64.") + [void]$md.AppendLine() + [void]$md.AppendLine("## Summary by _NT_TARGET_VERSION") + [void]$md.AppendLine("| _NT_TARGET_VERSION | Release | :white_check_mark: Pass | :warning: Flaky | :large_orange_diamond: Partial | :x: Fail | :heavy_minus_sign: n/a | Pass rate |") + [void]$md.AppendLine("|---|---|---:|---:|---:|---:|---:|---:|") + foreach ($t in $tags) { + $x = $totals[$t]; $tot = $x.pass + $x.flaky + $x.partial + $x.fail + $x.na; $elig = $tot - $x.na + $rate = if ($elig -gt 0) { '{0:N0}%' -f (100.0 * ($x.pass + $x.flaky) / $elig) } else { 'n/a' } + $rel = $ntLabel[$t]; if (-not $rel) { $rel = '' } + [void]$md.AppendLine("| ``$t`` | $rel | $($x.pass) | $($x.flaky) | $($x.partial) | $($x.fail) | $($x.na) | **$rate** |") + } + [void]$md.AppendLine() + + if ($failuresList.Count -gt 0) { + [void]$md.AppendLine("## :x: Failures ($($failuresList.Count))") + [void]$md.AppendLine("| Sample | _NT_TARGET_VERSION | Config/Platform |") + [void]$md.AppendLine("|---|---|---|") + foreach ($f in ($failuresList | Sort-Object Sample, Version, Combo)) { + [void]$md.AppendLine("| ``$($f.Sample)`` | $($f.Version) | $($f.Combo.Replace('|','/')) |") + } + [void]$md.AppendLine() + [void]$md.AppendLine("> Open the matching **build** job's summary (or the ``logs-*`` artifact) for the exact compiler error.") + } + else { + [void]$md.AppendLine(":tada: **All combinations built successfully.**") + } + + $md.ToString() | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Append -Encoding utf8 +} -- cgit v1.3.1