Skip to content

build(deps-dev): bump @xmldom/xmldom from 0.8.13 to 0.8.15 in /UniversalDeviceToolkit.Electron #771

build(deps-dev): bump @xmldom/xmldom from 0.8.13 to 0.8.15 in /UniversalDeviceToolkit.Electron

build(deps-dev): bump @xmldom/xmldom from 0.8.13 to 0.8.15 in /UniversalDeviceToolkit.Electron #771

Workflow file for this run

name: CI Tests
on:
workflow_dispatch:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build-test-and-smoke:
runs-on: windows-2022
timeout-minutes: 25
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Setup .NET
uses: actions/setup-dotnet@v6
with:
dotnet-version: '10.0.x'
- name: Cache NuGet packages
uses: actions/cache@v6
with:
path: ~/.nuget/packages
key: nuget-${{ runner.os }}-${{ hashFiles('**/Directory.Packages.props') }}
- name: Restore solution
run: dotnet restore UniversalDeviceToolkit.sln --locked-mode
- name: Build solution
env:
# MSB3554: parallel project builds raced on UniversalDeviceToolkit.Lib's
# obj\...\*.resources output ("being used by another process"). -m:1
# serializes project builds so the GenerateResource task cannot collide,
# and disabling node reuse releases file handles between build steps.
MSBUILDDISABLENODEREUSE: 1
run: dotnet build UniversalDeviceToolkit.sln --configuration Release --no-restore -m:1 --disable-build-servers
- name: Verify Windows test prerequisites
shell: pwsh
run: ./Scripts/Test-WindowsTestEnvironment.ps1
# Contracts (Guard/Security) fail-fast, then Fast, parallel unit, then stateful.
- name: Assert culture naming convention (fail fast)
shell: pwsh
run: ./Scripts/Assert-CultureNaming.ps1
- name: Test (Contracts - fail fast)
run: dotnet test UniversalDeviceToolkit.Tests.Contracts/UniversalDeviceToolkit.Tests.Contracts.csproj --framework net10.0-windows10.0.26100.0 --configuration Release --no-build --logger "trx;LogFileName=UniversalDeviceToolkit.Tests.Contracts.trx"
- name: Test (Fast unit tests)
run: dotnet test UniversalDeviceToolkit.Fast.Tests/UniversalDeviceToolkit.Fast.Tests.csproj --framework net10.0-windows10.0.26100.0 --configuration Release --no-build --logger "trx;LogFileName=UniversalDeviceToolkit.Fast.Tests.trx"
- name: Test (Unit)
run: dotnet test UniversalDeviceToolkit.Tests/UniversalDeviceToolkit.Tests.csproj --framework net10.0-windows10.0.26100.0 --configuration Release --no-build --logger "trx;LogFileName=UniversalDeviceToolkit.Tests.trx" --collect:"XPlat Code Coverage" --settings UniversalDeviceToolkit.Tests/coverlet.runsettings
- name: Test (Stateful)
run: dotnet test UniversalDeviceToolkit.Tests.Stateful/UniversalDeviceToolkit.Tests.Stateful.csproj --framework net10.0-windows10.0.26100.0 --configuration Release --no-build --logger "trx;LogFileName=UniversalDeviceToolkit.Tests.Stateful.trx"
- name: Upload code coverage
uses: actions/upload-artifact@v7
if: always()
with:
name: code-coverage
path: |
**/*coverage.cobertura.xml
**/*coverage.opencover.xml
if-no-files-found: warn
- name: Upload Windows test results and diagnostics
uses: actions/upload-artifact@v7
if: always()
with:
name: windows-test-results-${{ github.run_id }}
path: |
**/UniversalDeviceToolkit.Tests.Contracts.trx
**/UniversalDeviceToolkit.Fast.Tests.trx
**/UniversalDeviceToolkit.Tests.trx
**/UniversalDeviceToolkit.Tests.Stateful.trx
**/TestResults/**/*.png
**/TestResults/**/*.dmp
**/TestResults/**/*.log
if-no-files-found: warn
- name: Check for vulnerable packages
shell: pwsh
run: |
$reportJson = dotnet list package --vulnerable --include-transitive --format json | Out-String
$scanExitCode = $LASTEXITCODE
Write-Host $reportJson
try {
$report = $reportJson | ConvertFrom-Json -ErrorAction Stop
}
catch {
if ($scanExitCode -ne 0) {
throw "NuGet vulnerability scan failed before a JSON report could be parsed."
}
throw
}
function Get-Vulnerabilities {
param([object]$Node)
if ($null -eq $Node) {
return
}
if ($Node -is [array]) {
foreach ($item in $Node) {
Get-Vulnerabilities -Node $item
}
return
}
if ($Node -isnot [pscustomobject]) {
return
}
$vulnerabilitiesProperty = $Node.PSObject.Properties['vulnerabilities']
if ($null -ne $vulnerabilitiesProperty -and $null -ne $vulnerabilitiesProperty.Value) {
foreach ($vulnerability in @($vulnerabilitiesProperty.Value)) {
[pscustomobject]@{
Package = [string]$Node.id
Version = [string]$Node.resolvedVersion
Severity = [string]$vulnerability.severity
AdvisoryUrl = [string]$vulnerability.advisoryUrl
}
}
}
foreach ($property in $Node.PSObject.Properties) {
if ($property.Name -ne 'vulnerabilities') {
Get-Vulnerabilities -Node $property.Value
}
}
}
function Test-BlockingSeverity {
param([string]$Severity)
return $Severity -match '^(High|Critical|2|3)$'
}
$vulnerabilities = @(Get-Vulnerabilities -Node $report)
$blockingVulnerabilities = @($vulnerabilities | Where-Object { Test-BlockingSeverity -Severity $_.Severity })
foreach ($vulnerability in $vulnerabilities) {
$message = "$($vulnerability.Package) $($vulnerability.Version) has $($vulnerability.Severity) vulnerability $($vulnerability.AdvisoryUrl)"
if (Test-BlockingSeverity -Severity $vulnerability.Severity) {
Write-Host "::error title=Blocking NuGet vulnerability::$message"
}
else {
Write-Host "::warning title=NuGet vulnerability::$message"
}
}
if ($blockingVulnerabilities.Count -gt 0) {
throw "Blocking NuGet vulnerabilities found: $($blockingVulnerabilities.Count) High/Critical advisory or advisories."
}
if ($scanExitCode -ne 0 -and $vulnerabilities.Count -eq 0) {
throw "NuGet vulnerability scan failed with exit code $scanExitCode."
}
- name: Publish test results
uses: dorny/test-reporter@v3
continue-on-error: true
with:
name: Test results
path: '**/*.trx'
reporter: dotnet-trx
- name: Smoke CLI help and version
shell: pwsh
run: |
[xml]$props = Get-Content -Raw 'Directory.Build.props'
$group = $props.Project.PropertyGroup | Select-Object -First 1
$expectedVersion = [string]$group.ReleaseVersion
if ([string]::IsNullOrWhiteSpace($expectedVersion) -or $expectedVersion -match '\$\(') {
$majorVersion = [string]$group.MajorVersion
$minorVersion = [string]$group.MinorVersion
$patchVersion = [string]$group.PatchVersion
if ([string]::IsNullOrWhiteSpace($majorVersion) -or [string]::IsNullOrWhiteSpace($minorVersion) -or [string]::IsNullOrWhiteSpace($patchVersion)) {
throw 'Could not resolve expected CLI version from Directory.Build.props.'
}
$expectedVersion = "$majorVersion.$minorVersion.$patchVersion"
}
# Solution builds map Release|Any CPU -> Release|x64, so outputs live under bin\x64\...
$cliCandidates = @(
(Join-Path $PWD 'UniversalDeviceToolkit.CLI\bin\x64\Release\net10.0-windows10.0.26100.0\win-x64\udt.exe'),
(Join-Path $PWD 'UniversalDeviceToolkit.CLI\bin\Release\net10.0-windows10.0.26100.0\win-x64\udt.exe'),
(Join-Path $PWD 'UniversalDeviceToolkit.CLI\bin\x64\Release\net10.0-windows10.0.26100.0\win-x64\udt-cli.exe'),
(Join-Path $PWD 'UniversalDeviceToolkit.CLI\bin\Release\net10.0-windows10.0.26100.0\win-x64\udt-cli.exe')
)
$cliExePath = $cliCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1
if (-not $cliExePath) {
throw "CLI executable not found. Tried:`n$($cliCandidates -join "`n")"
}
$cliDllPath = [System.IO.Path]::ChangeExtension($cliExePath, '.dll')
if (-not (Test-Path $cliDllPath)) {
throw "CLI assembly not found at $cliDllPath"
}
$help = (& dotnet $cliDllPath --help 2>&1 | Out-String).Trim()
if ($LASTEXITCODE -ne 0) {
throw "CLI help failed with exit code $LASTEXITCODE`n$help"
}
if ($help -notmatch 'Usage:' -or $help -notmatch 'Commands:') {
throw "CLI help output missing expected sections.`n$help"
}
$version = (& dotnet $cliDllPath --version 2>&1 | Out-String).Trim()
if ($LASTEXITCODE -ne 0) {
throw "CLI version failed with exit code $LASTEXITCODE`n$version"
}
if (-not $version.StartsWith($expectedVersion)) {
throw "CLI version '$version' does not start with expected '$expectedVersion'."
}
Write-Host "CLI smoke passed for version $version"
electron-ui-tests:
runs-on: windows-2022
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Setup Node
uses: actions/setup-node@v7
with:
node-version: '22'
cache: npm
cache-dependency-path: UniversalDeviceToolkit.Electron/package-lock.json
- name: Install Electron dependencies
working-directory: UniversalDeviceToolkit.Electron
run: npm ci
- name: Check source Unicode
run: node Tools/CheckSourceUnicode/check-unicode.mjs
- name: Lint Electron UI
working-directory: UniversalDeviceToolkit.Electron
run: npm run lint
- name: Typecheck Electron UI
working-directory: UniversalDeviceToolkit.Electron
run: npm run typecheck
- name: Test Electron UI
working-directory: UniversalDeviceToolkit.Electron
run: npm test
cross-platform-cli:
strategy:
fail-fast: false
matrix:
os: [ ubuntu-latest, macos-latest, windows-2022 ]
runs-on: ${{ matrix.os }}
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Setup .NET
uses: actions/setup-dotnet@v6
with:
dotnet-version: 10.0.x
- name: Cache NuGet packages
uses: actions/cache@v6
with:
path: ~/.nuget/packages
key: nuget-${{ runner.os }}-${{ hashFiles('**/Directory.Packages.props') }}
- name: Restore cross-platform tests
run: dotnet restore UniversalDeviceToolkit.CrossPlatform.Tests/UniversalDeviceToolkit.CrossPlatform.Tests.csproj --locked-mode
- name: Build cross-platform tests
run: dotnet build UniversalDeviceToolkit.CrossPlatform.Tests/UniversalDeviceToolkit.CrossPlatform.Tests.csproj --configuration Release --no-restore
- name: Test cross-platform diagnostics
run: dotnet test UniversalDeviceToolkit.CrossPlatform.Tests/UniversalDeviceToolkit.CrossPlatform.Tests.csproj --configuration Release --no-build --logger "trx;LogFileName=UniversalDeviceToolkit.CrossPlatform.Tests.trx" --collect:"XPlat Code Coverage" --settings UniversalDeviceToolkit.Tests/coverlet.runsettings
- name: Smoke diagnostics CLI
shell: pwsh
run: |
$project = 'UniversalDeviceToolkit.CrossPlatform/UniversalDeviceToolkit.CrossPlatform.csproj'
$status = dotnet run --project $project --configuration Release --no-build -- status
if ($LASTEXITCODE -ne 0) {
throw "status command failed with exit code $LASTEXITCODE"
}
if (($status | Out-String) -notmatch 'cross-platform diagnostics') {
throw "status command did not print diagnostics header."
}
$hardware = dotnet run --project $project --configuration Release --no-build -- hardware
if ($LASTEXITCODE -ne 0) {
throw "hardware command failed with exit code $LASTEXITCODE"
}
if (($hardware | Out-String) -notmatch 'Hardware identity') {
throw "hardware command did not print hardware identity."
}
$telemetry = dotnet run --project $project --configuration Release --no-build -- telemetry
if ($LASTEXITCODE -ne 0) {
throw "telemetry command failed with exit code $LASTEXITCODE"
}
if (($telemetry | Out-String) -notmatch 'System telemetry') {
throw "telemetry command did not print system telemetry."
}
$power = dotnet run --project $project --configuration Release --no-build -- power
if ($LASTEXITCODE -ne 0) {
throw "power command failed with exit code $LASTEXITCODE"
}
if (($power | Out-String) -notmatch 'Power status') {
throw "power command did not print power status."
}
$profile = dotnet run --project $project --configuration Release --no-build -- profile
if ($LASTEXITCODE -ne 0) {
throw "profile command failed with exit code $LASTEXITCODE"
}
if (($profile | Out-String) -notmatch 'Power profile') {
throw "profile command did not print power profile."
}
$controls = dotnet run --project $project --configuration Release --no-build -- controls
if ($LASTEXITCODE -ne 0) {
throw "controls command failed with exit code $LASTEXITCODE"
}
if (($controls | Out-String) -notmatch 'Hardware controls') {
throw "controls command did not print hardware controls."
}
$support = dotnet run --project $project --configuration Release --no-build -- support
if ($LASTEXITCODE -ne 0) {
throw "support command failed with exit code $LASTEXITCODE"
}
if (($support | Out-String) -notmatch 'Device support') {
throw "support command did not print device support."
}
$doctor = dotnet run --project $project --configuration Release --no-build -- doctor
if ($LASTEXITCODE -ne 0 -and $LASTEXITCODE -ne 1) {
throw "doctor command failed with unexpected exit code $LASTEXITCODE"
}
if (($doctor | Out-String) -notmatch 'Doctor report') {
throw "doctor command did not print doctor report."
}
$json = dotnet run --project $project --configuration Release --no-build -- json | Out-String
if ($LASTEXITCODE -ne 0) {
throw "json command failed with exit code $LASTEXITCODE"
}
$payload = $json | ConvertFrom-Json
if ($payload.ProductName -ne 'Universal Device Toolkit') {
throw "json command returned unexpected product name '$($payload.ProductName)'."
}
if (-not $payload.Capabilities -or $payload.Capabilities.Count -lt 1) {
throw "json command returned no capabilities."
}
if (-not $payload.Telemetry) {
throw "json command returned no telemetry object."
}
- name: Publish test results
uses: dorny/test-reporter@v3
if: always()
continue-on-error: true
with:
name: Cross-platform test results (${{ matrix.os }})
path: '**/UniversalDeviceToolkit.CrossPlatform.Tests.trx'
reporter: dotnet-trx
- name: Upload cross-platform test results and diagnostics
uses: actions/upload-artifact@v7
if: always()
with:
name: cross-platform-test-results-${{ matrix.os }}-${{ github.run_id }}
path: |
**/UniversalDeviceToolkit.CrossPlatform.Tests.trx
**/TestResults/**/*.dmp
**/TestResults/**/*.log
if-no-files-found: warn
- name: Upload cross-platform coverage
uses: actions/upload-artifact@v7
if: always()
with:
name: code-coverage-${{ matrix.os }}
path: |
**/*coverage.cobertura.xml
**/*coverage.opencover.xml
if-no-files-found: warn