build(deps-dev): bump @xmldom/xmldom from 0.8.13 to 0.8.15 in /UniversalDeviceToolkit.Electron #771
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |