ci: build Go test binaries while fixtures are provisioned (#2656)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
This commit is contained in:
Jan-Otto Kröpke
2026-10-10 18:15:14 +02:00
committed by GitHub
co-authored by Claude Opus 5.5
parent 465fccbe69
commit d25d2328f2
5 changed files with 155 additions and 117 deletions
+51
View File
@@ -0,0 +1,51 @@
# Builds the race-enabled test binaries ahead of time, so CI can link them in
# the background while Windows fixtures are provisioned. run-go-tests.ps1 runs
# the binaries listed in manifest.json.
param(
[Parameter(Mandatory)]
[string]$OutputDirectory
)
$ErrorActionPreference = 'Stop'
Set-StrictMode -Version 3
New-Item -ItemType Directory -Force -Path $OutputDirectory | Out-Null
$OutputDirectory = (Resolve-Path $OutputDirectory).Path
$listing = go list -f '{{if or .TestGoFiles .XTestGoFiles}}{{.ImportPath}}{{"\t"}}{{.Dir}}{{end}}' ./...
if ($LASTEXITCODE -ne 0) { throw "go list failed with exit code $LASTEXITCODE" }
$packages = @($listing | Where-Object { $_ } | ForEach-Object {
$importPath, $dir = $_ -split "`t", 2
[pscustomobject]@{
ImportPath = $importPath
Dir = $dir
Base = $importPath.Split('/')[-1]
Binary = $null
}
})
# `go test -c -o dir/` names binaries after the last import path element, so
# packages sharing that name (internal/collector, pkg/collector) build separately.
$groups = $packages | Group-Object Base
$shared = @($groups | Where-Object Count -eq 1 | ForEach-Object { $_.Group[0] })
$clashing = @($groups | Where-Object Count -gt 1 | ForEach-Object { $_.Group })
foreach ($package in $shared) {
$package.Binary = "$($package.Base).test.exe"
}
go test -race -c -o "$OutputDirectory/" @($shared.ImportPath)
if ($LASTEXITCODE -ne 0) { throw "go test -c failed with exit code $LASTEXITCODE" }
foreach ($package in $clashing) {
$package.Binary = ($package.ImportPath -replace '[/.]', '_') + '.test.exe'
go test -race -c -o (Join-Path $OutputDirectory $package.Binary) $package.ImportPath
if ($LASTEXITCODE -ne 0) { throw "go test -c $($package.ImportPath) failed with exit code $LASTEXITCODE" }
}
$packages | Select-Object ImportPath, Dir, Binary |
ConvertTo-Json -AsArray |
Set-Content -Encoding utf8 (Join-Path $OutputDirectory 'manifest.json')
Write-Host "Built $($packages.Count) test binaries in $OutputDirectory"
+49
View File
@@ -0,0 +1,49 @@
# Runs the test binaries built by build-go-tests.ps1 like `go test -v ./...`:
# each binary runs in its package directory and packages run in parallel.
param(
[Parameter(Mandatory)]
[string]$BinaryDirectory,
[string]$Timeout = '10m',
[int]$ThrottleLimit = [Environment]::ProcessorCount
)
$ErrorActionPreference = 'Stop'
Set-StrictMode -Version 3
$BinaryDirectory = (Resolve-Path $BinaryDirectory).Path
$manifest = Get-Content (Join-Path $BinaryDirectory 'manifest.json') -Raw | ConvertFrom-Json
$runs = $manifest | ForEach-Object -ThrottleLimit $ThrottleLimit -Parallel {
$package = $_
$timeout = $using:Timeout
$binary = Join-Path $using:BinaryDirectory $package.Binary
# Native commands start in the runspace location, so tests see their
# package directory as the working directory, as with `go test`.
Set-Location $package.Dir
$started = Get-Date
$output = & $binary '-test.v' "-test.timeout=$timeout" 2>&1 |
ForEach-Object { $_.ToString() }
$exitCode = $LASTEXITCODE
$elapsed = ((Get-Date) - $started).TotalSeconds
$status = if ($exitCode -eq 0) { 'ok ' } else { 'FAIL' }
[pscustomobject]@{
ImportPath = $package.ImportPath
ExitCode = $exitCode
# Print each package as one block so parallel output does not interleave.
Text = (@($output) + ("{0}`t{1}`t{2:N3}s" -f $status, $package.ImportPath, $elapsed)) -join "`n"
}
} | ForEach-Object {
Write-Host $_.Text
$_
}
$failed = @($runs | Where-Object ExitCode -ne 0)
if ($failed.Count -gt 0) {
Write-Host "Failed packages:"
$failed | ForEach-Object { Write-Host " $($_.ImportPath) (exit code $($_.ExitCode))" }
exit 1
}
exit 0
-64
View File
@@ -1,64 +0,0 @@
param([int]$TestExitCode)
$ErrorActionPreference = "Stop"
$prefix = "github.com/prometheus-community/windows_exporter/internal/collector/"
$required = @($env:WINDOWS_EXPORTER_TEST_COLLECTORS.Split(','))
$results = @{}
$text = [System.Text.StringBuilder]::new()
$lines = if (Test-Path test-results.jsonl) { Get-Content test-results.jsonl } else { @() }
foreach ($line in $lines) {
try {
$event = $line | ConvertFrom-Json -ErrorAction Stop
} catch {
[void]$text.AppendLine($line)
continue
}
if ($event.Output) { [void]$text.Append($event.Output) }
if (-not $event.Package -or -not $event.Package.StartsWith($prefix) -or $event.Test -ne "TestCollector") {
continue
}
$name = $event.Package.Substring($prefix.Length)
if (-not $results.ContainsKey($name)) {
$results[$name] = @{ State = "not completed"; Notes = @() }
}
if ($event.Action -in @("pass", "skip", "fail")) {
$results[$name].State = $event.Action
}
if ($event.Output -match 'collector \S+ (?:is not supported|has empty optional instance groups): (.+)') {
$results[$name].Notes += $Matches[1].Trim()
}
}
$text.ToString() | Set-Content test-output.txt -NoNewline -Encoding utf8
# Aborted tests must leave required fixtures visible as not run.
foreach ($name in $required) {
if (-not $results.ContainsKey($name)) {
$results[$name] = @{ State = "not run"; Notes = @() }
}
}
$testState = if (-not $lines) { "not run" } elseif ($TestExitCode -eq 0) { "passed" } else { "failed" }
$summary = @(
"### Windows feature tests: $env:WINDOWS_TEST_OS"
""
"Go suite: **$testState**. Required fixtures cannot skip. Empty optional instance groups remain listed below."
""
"| Collector | Required | Live test | Unavailable features / notes |"
"| --- | --- | --- | --- |"
)
foreach ($name in $results.Keys | Sort-Object) {
$fixture = if ($name -in $required) { "yes" } else { "no" }
$state = switch ($results[$name].State) {
"pass" { "passed" }
"skip" { "unavailable" }
"fail" { "failed" }
default { $results[$name].State }
}
$notes = ($results[$name].Notes | Sort-Object -Unique) -join '; '
$notes = $notes.Replace('|', '\|')
$summary += "| $name | $fixture | $state | $notes |"
}
$summary | Set-Content feature-summary.md -Encoding utf8
if ($env:GITHUB_STEP_SUMMARY) {
$summary | Add-Content $env:GITHUB_STEP_SUMMARY
}