fix: harden Windows broker uninstall and release CI #349
Workflow file for this run
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 | |
| permissions: {} | |
| on: | |
| pull_request: | |
| push: | |
| branches: | |
| - master | |
| concurrency: | |
| group: "${{ github.workflow }}-${{ github.ref }}" | |
| cancel-in-progress: true | |
| env: | |
| CMAKE_BUILD_CONFIG: Debug | |
| DRIVER_BUILD_CONFIG: Release | |
| OPENCPPCOVERAGE_VERSION: '0.9.9.0' | |
| PYTHON_VERSION: '3.14' | |
| jobs: | |
| setup_release: | |
| name: Setup Release | |
| outputs: | |
| publish_release: ${{ steps.setup_release.outputs.publish_release }} | |
| release_body: ${{ steps.setup_release.outputs.release_body }} | |
| release_commit: ${{ steps.setup_release.outputs.release_commit }} | |
| release_generate_release_notes: ${{ steps.setup_release.outputs.release_generate_release_notes }} | |
| release_tag: ${{ steps.setup_release.outputs.release_tag }} | |
| release_version: ${{ steps.setup_release.outputs.release_version }} | |
| permissions: | |
| contents: write | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Setup Release | |
| id: setup_release | |
| uses: LizardByte/actions/actions/release_setup@d0ae7f82215a479fe2b74f4088c53ee6460513dd # v2026.728.214955 | |
| with: | |
| dotnet: true | |
| github_token: ${{ secrets.GITHUB_TOKEN }} | |
| build: | |
| name: Build (${{ matrix.name }}) | |
| needs: | |
| - setup_release | |
| - windows_driver | |
| permissions: | |
| contents: read | |
| runs-on: ${{ matrix.os }} | |
| defaults: | |
| run: | |
| shell: ${{ matrix.shell }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - name: Linux-GCC | |
| os: ubuntu-latest | |
| shell: bash | |
| kind: unix | |
| cc: gcc | |
| cxx: g++ | |
| gcov_executable: gcov | |
| - name: Linux-Clang | |
| os: ubuntu-latest | |
| shell: bash | |
| kind: unix | |
| cc: clang | |
| cxx: clang++ | |
| # Clang writes LLVM coverage notes, so gcovr needs llvm-cov's gcov compatibility mode. | |
| gcov_executable: llvm-cov gcov | |
| - name: macOS | |
| os: macos-latest | |
| shell: bash | |
| kind: unix | |
| cc: clang | |
| cxx: clang++ | |
| gcov_executable: gcov | |
| - name: Windows-MinGW-UCRT64 | |
| os: windows-latest | |
| shell: msys2 {0} | |
| kind: msys2 | |
| cc: gcc | |
| cxx: g++ | |
| msystem: ucrt64 | |
| toolchain: ucrt-x86_64 | |
| gcov_executable: gcov | |
| - name: Windows-MSVC | |
| os: windows-2022 | |
| shell: pwsh | |
| kind: msvc | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| submodules: recursive | |
| - name: Setup Dependencies Linux | |
| if: runner.os == 'Linux' | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y \ | |
| build-essential \ | |
| clang \ | |
| cmake \ | |
| libevdev-dev \ | |
| libinput-dev \ | |
| libsdl2-dev \ | |
| libx11-dev \ | |
| libxtst-dev \ | |
| llvm \ | |
| ninja-build \ | |
| pkg-config | |
| kernel_modules_package="linux-modules-extra-$(uname -r)" | |
| if apt-cache show "${kernel_modules_package}" >/dev/null 2>&1; then | |
| sudo apt-get install -y "${kernel_modules_package}" | |
| else | |
| echo "::warning::${kernel_modules_package} is unavailable; relying on the runner image kernel modules." | |
| fi | |
| sudo tee /etc/udev/rules.d/99-libvirtualhid-ci.rules >/dev/null <<'EOF' | |
| SUBSYSTEM=="hidraw", KERNEL=="hidraw*", ENV{HID_PHYS}=="libvirtualhid/uhid/*", MODE="0666", TAG+="uaccess" | |
| SUBSYSTEM=="hidraw", KERNEL=="hidraw*", ATTRS{name}=="(libvirtualhid)*", MODE="0666", TAG+="uaccess" | |
| SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="(libvirtualhid)*", MODE="0666", TAG+="uaccess" | |
| SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="libvirtualhid*", MODE="0666", TAG+="uaccess" | |
| EOF | |
| sudo udevadm control --reload-rules | |
| for module in uhid uinput; do | |
| if ! sudo modprobe "${module}"; then | |
| message="Unable to load ${module}; tests requiring /dev/${module}" | |
| message="${message} will fail unless the device already exists." | |
| echo "::warning::${message}" | |
| fi | |
| done | |
| if ! sudo modprobe hid_playstation; then | |
| echo "::warning::Unable to load hid_playstation; SDL HIDAPI will exercise the native hidraw output path." | |
| fi | |
| for node in /dev/uhid /dev/uinput; do | |
| if [[ -e "${node}" ]]; then | |
| sudo chmod a+rw "${node}" | |
| else | |
| echo "::error::${node} does not exist after module setup." | |
| exit 1 | |
| fi | |
| done | |
| - name: Setup Dependencies macOS | |
| if: runner.os == 'macOS' | |
| run: | | |
| brew install \ | |
| cmake \ | |
| ninja | |
| - name: Setup Dependencies Windows MinGW | |
| if: matrix.kind == 'msys2' | |
| uses: msys2/setup-msys2@66cd2cce69caa17b53920067426061ca1de3a884 # v2.32.0 | |
| with: | |
| msystem: ${{ matrix.msystem }} | |
| update: true | |
| install: >- | |
| mingw-w64-${{ matrix.toolchain }}-cmake | |
| mingw-w64-${{ matrix.toolchain }}-ninja | |
| mingw-w64-${{ matrix.toolchain }}-toolchain | |
| - name: Setup Dependencies Windows MSVC | |
| if: matrix.kind == 'msvc' | |
| run: | | |
| choco install opencppcoverage --version=${{ env.OPENCPPCOVERAGE_VERSION }} --yes --no-progress | |
| $openCppCoverageDir = "${env:ProgramFiles}\OpenCppCoverage" | |
| if (!(Test-Path (Join-Path $openCppCoverageDir "OpenCppCoverage.exe"))) { | |
| $openCppCoverageDir = "${env:ProgramFiles(x86)}\OpenCppCoverage" | |
| } | |
| if (!(Test-Path (Join-Path $openCppCoverageDir "OpenCppCoverage.exe"))) { | |
| throw "OpenCppCoverage.exe was not found after Chocolatey install." | |
| } | |
| $openCppCoverageDir | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append | |
| - name: Setup python | |
| id: setup-python | |
| if: matrix.kind != 'msvc' | |
| uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 | |
| with: | |
| python-version: ${{ env.PYTHON_VERSION }} | |
| - name: Setup uv | |
| if: matrix.kind != 'msvc' | |
| uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 | |
| with: | |
| enable-cache: true | |
| - name: Sync Python tools | |
| if: matrix.kind != 'msvc' | |
| env: | |
| MSYS2_PATH_TYPE: inherit | |
| UV_PYTHON: ${{ steps.setup-python.outputs.python-path }} | |
| run: | | |
| uv sync --project third-party/lizardbyte-common --locked --only-group test-c \ | |
| --no-python-downloads \ | |
| --no-install-project | |
| - name: Configure | |
| if: matrix.kind != 'msvc' | |
| env: | |
| BRANCH: ${{ github.head_ref || github.ref_name }} | |
| BUILD_VERSION: ${{ needs.setup_release.outputs.release_version }} | |
| CC: ${{ matrix.cc }} | |
| COMMIT: ${{ needs.setup_release.outputs.release_commit }} | |
| CXX: ${{ matrix.cxx }} | |
| run: | | |
| cmake \ | |
| -DBUILD_DOCS=OFF \ | |
| -DBUILD_EXAMPLES=ON \ | |
| -DBUILD_TESTS=ON \ | |
| -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_CONFIG} \ | |
| -DLIBVIRTUALHID_BUILD_TOOLS=ON \ | |
| -B cmake-build-ci \ | |
| -G Ninja \ | |
| -S . | |
| - name: Configure MSVC | |
| if: matrix.kind == 'msvc' | |
| env: | |
| BRANCH: ${{ github.head_ref || github.ref_name }} | |
| BUILD_VERSION: ${{ needs.setup_release.outputs.release_version }} | |
| COMMIT: ${{ needs.setup_release.outputs.release_commit }} | |
| run: | | |
| cmake ` | |
| -DBUILD_DOCS=OFF ` | |
| -DBUILD_EXAMPLES=ON ` | |
| -DBUILD_TESTS=ON ` | |
| -DLIBVIRTUALHID_BUILD_TOOLS=ON ` | |
| -A x64 ` | |
| -B cmake-build-ci ` | |
| -G "Visual Studio 17 2022" ` | |
| -S . | |
| - name: Build | |
| run: cmake --build cmake-build-ci --config ${{ env.CMAKE_BUILD_CONFIG }} --parallel 2 | |
| - name: Download Windows driver installer artifact | |
| if: runner.os == 'Windows' | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: windows-driver-installer | |
| path: windows-driver-installer | |
| - name: Trust Windows driver catalog signer | |
| if: runner.os == 'Windows' | |
| shell: pwsh | |
| run: | | |
| $certificate = Get-ChildItem ` | |
| -LiteralPath .\windows-driver-installer ` | |
| -Filter *.cer ` | |
| -ErrorAction SilentlyContinue | | |
| Select-Object -First 1 | |
| if (!$certificate) { | |
| Write-Host "No separate driver signing certificate artifact was provided." | |
| return | |
| } | |
| $imported = Import-Certificate ` | |
| -FilePath $certificate.FullName ` | |
| -CertStoreLocation "Cert:\LocalMachine\TrustedPublisher" | |
| foreach ($cert in $imported) { | |
| Write-Host "Trusted driver publisher certificate $($cert.Subject) [$($cert.Thumbprint)]." | |
| } | |
| - name: Install Windows driver installer | |
| if: runner.os == 'Windows' | |
| shell: pwsh | |
| timeout-minutes: 10 | |
| run: | | |
| $installer = Get-ChildItem -LiteralPath .\windows-driver-installer -Filter *.msi | Select-Object -First 1 | |
| if (!$installer) { | |
| throw "Windows driver installer artifact did not contain an MSI." | |
| } | |
| $logPath = Join-Path $env:RUNNER_TEMP "libvirtualhid-driver-install.log" | |
| $driverLogPath = Join-Path $env:ProgramData "libvirtualhid\install-driver.log" | |
| $setupApiLogPath = Join-Path $env:windir "inf\setupapi.dev.log" | |
| $process = Start-Process ` | |
| -FilePath msiexec.exe ` | |
| -ArgumentList @("/i", $installer.FullName, "/qn", "/norestart", "/L*v", $logPath) ` | |
| -PassThru ` | |
| -NoNewWindow | |
| if (!$process.WaitForExit([int] [TimeSpan]::FromMinutes(5).TotalMilliseconds)) { | |
| Get-Content -LiteralPath $logPath -Tail 200 -ErrorAction SilentlyContinue | |
| Get-Content -LiteralPath $driverLogPath -Tail 200 -ErrorAction SilentlyContinue | |
| Get-Content -LiteralPath $setupApiLogPath -Tail 300 -ErrorAction SilentlyContinue | |
| Stop-Process -Id $process.Id -Force -ErrorAction SilentlyContinue | |
| throw "Windows driver installer timed out after 5 minutes." | |
| } | |
| if ($process.ExitCode -notin @(0, 3010)) { | |
| Get-Content -LiteralPath $logPath -ErrorAction SilentlyContinue | |
| Get-Content -LiteralPath $driverLogPath -ErrorAction SilentlyContinue | |
| Get-Content -LiteralPath $setupApiLogPath -Tail 300 -ErrorAction SilentlyContinue | |
| throw "Windows driver installer exited with code $($process.ExitCode)." | |
| } | |
| - name: Enable GitHub Actions evaluation window | |
| if: runner.os == 'Windows' | |
| shell: pwsh | |
| run: | | |
| $serviceName = "libvirtualhid_broker" | |
| $serviceRegistryPath = "HKLM:\SYSTEM\CurrentControlSet\Services\$serviceName" | |
| New-ItemProperty ` | |
| -LiteralPath $serviceRegistryPath ` | |
| -Name Environment ` | |
| -PropertyType MultiString ` | |
| -Value @("GITHUB_ACTIONS=true") ` | |
| -Force | Out-Null | |
| Restart-Service -Name $serviceName -Force | |
| (Get-Service -Name $serviceName).WaitForStatus("Running", [TimeSpan]::FromSeconds(15)) | |
| - name: Verify Windows test driver package | |
| if: runner.os == 'Windows' | |
| shell: pwsh | |
| run: | | |
| if ("${{ matrix.kind }}" -eq "msys2") { | |
| $env:PATH = "C:\msys64\${{ matrix.msystem }}\bin;C:\msys64\usr\bin;$env:PATH" | |
| $gamepadAdapterPath = "$env:GITHUB_WORKSPACE\cmake-build-ci\examples\gamepad_adapter.exe" | |
| } else { | |
| $gamepadAdapterPath = Join-Path ` | |
| "$env:GITHUB_WORKSPACE\cmake-build-ci\examples\$env:CMAKE_BUILD_CONFIG" ` | |
| "gamepad_adapter.exe" | |
| } | |
| $profiles = @("generic", "xone", "xseries", "ds4", "ds5", "switch") | |
| foreach ($profile in $profiles) { | |
| .\scripts\windows\test-installed-driver.ps1 ` | |
| -GamepadAdapterPath $gamepadAdapterPath ` | |
| -Profile $profile ` | |
| -Verbose | |
| } | |
| - name: Run gamepad adapter example | |
| run: cmake --build cmake-build-ci --config ${{ env.CMAKE_BUILD_CONFIG }} --target run_gamepad_adapter_example | |
| - name: Prepare report directory | |
| run: cmake -E make_directory cmake-build-ci/reports | |
| - name: Run tests | |
| id: test | |
| if: matrix.kind != 'msvc' | |
| working-directory: cmake-build-ci/tests | |
| run: ./test_libvirtualhid --gtest_color=yes --gtest_output=xml:../reports/junit.xml | |
| - name: Run tests MSVC | |
| id: test_msvc | |
| if: matrix.kind == 'msvc' | |
| run: | | |
| $openCppCoverage = (Get-Command OpenCppCoverage.exe -ErrorAction SilentlyContinue).Source | |
| if (!$openCppCoverage) { | |
| $candidates = @( | |
| "${env:ProgramFiles}\OpenCppCoverage\OpenCppCoverage.exe", | |
| "${env:ProgramFiles(x86)}\OpenCppCoverage\OpenCppCoverage.exe" | |
| ) | |
| $openCppCoverage = $candidates | Where-Object { Test-Path $_ } | Select-Object -First 1 | |
| } | |
| if (!$openCppCoverage) { | |
| throw "OpenCppCoverage.exe was not found." | |
| } | |
| # The broker test hook compiles a private copy only for failure injection. | |
| & $openCppCoverage ` | |
| --sources "$env:GITHUB_WORKSPACE\examples" ` | |
| --sources "$env:GITHUB_WORKSPACE\src" ` | |
| --sources "$env:GITHUB_WORKSPACE\tools" ` | |
| --excluded_sources "$env:GITHUB_WORKSPACE\src\platform\windows\broker" ` | |
| "--export_type=cobertura:$env:GITHUB_WORKSPACE\cmake-build-ci\reports\coverage.xml" ` | |
| --working_dir "$env:GITHUB_WORKSPACE\cmake-build-ci\tests" ` | |
| -- ` | |
| "$env:GITHUB_WORKSPACE\cmake-build-ci\tests\$env:CMAKE_BUILD_CONFIG\test_libvirtualhid.exe" ` | |
| --gtest_color=yes ` | |
| "--gtest_output=xml:$env:GITHUB_WORKSPACE\cmake-build-ci\reports\junit.xml" | |
| - name: Normalize MSVC coverage paths | |
| if: >- | |
| always() && | |
| matrix.kind == 'msvc' && | |
| (steps.test_msvc.outcome == 'success' || steps.test_msvc.outcome == 'failure') | |
| run: | | |
| $coveragePath = Join-Path $env:GITHUB_WORKSPACE "cmake-build-ci\reports\coverage.xml" | |
| if (!(Test-Path $coveragePath)) { | |
| return | |
| } | |
| [xml] $coverage = Get-Content $coveragePath | |
| $workspace = $env:GITHUB_WORKSPACE.Replace('\', '/') | |
| foreach ($node in $coverage.SelectNodes('//*[@filename]')) { | |
| $filename = $node.GetAttribute('filename').Replace('\', '/') | |
| if ($filename.StartsWith("${workspace}/")) { | |
| $filename = $filename.Substring($workspace.Length + 1) | |
| } | |
| $node.SetAttribute('filename', $filename) | |
| } | |
| foreach ($source in $coverage.SelectNodes('//source')) { | |
| $source.InnerText = '.' | |
| } | |
| $coverage.Save($coveragePath) | |
| - name: Generate gcov report | |
| id: test_report | |
| if: >- | |
| always() && | |
| matrix.kind != 'msvc' && | |
| (steps.test.outcome == 'success' || steps.test.outcome == 'failure') | |
| working-directory: cmake-build-ci | |
| env: | |
| GCOV_EXECUTABLE: ${{ matrix.gcov_executable }} | |
| MSYS2_PATH_TYPE: inherit | |
| run: | | |
| # The broker test hook compiles a private copy only for failure injection. | |
| uv run --project ../third-party/lizardbyte-common --locked --no-sync gcovr . -r .. \ | |
| --filter ../examples/ \ | |
| --filter ../src/ \ | |
| --filter ../tools/ \ | |
| --gcov-executable "${GCOV_EXECUTABLE}" \ | |
| --exclude ../src/platform/windows/broker/ \ | |
| --exclude ../tests/ \ | |
| --exclude ../third-party/ \ | |
| --exclude-noncode-lines \ | |
| --exclude-throw-branches \ | |
| --exclude-unreachable-branches \ | |
| --verbose \ | |
| --xml-pretty \ | |
| -o reports/coverage.xml | |
| - name: Uninstall Windows driver installer | |
| if: >- | |
| always() && | |
| runner.os == 'Windows' | |
| shell: pwsh | |
| run: | | |
| $installer = Get-ChildItem -LiteralPath .\windows-driver-installer ` | |
| -Filter *.msi ` | |
| -ErrorAction SilentlyContinue | | |
| Select-Object -First 1 | |
| if ($installer) { | |
| $logPath = Join-Path $env:RUNNER_TEMP "libvirtualhid-driver-uninstall.log" | |
| $process = Start-Process ` | |
| -FilePath msiexec.exe ` | |
| -ArgumentList @("/x", $installer.FullName, "/qn", "/norestart", "/L*v", $logPath) ` | |
| -PassThru ` | |
| -NoNewWindow | |
| if (!$process.WaitForExit([int] [TimeSpan]::FromMinutes(5).TotalMilliseconds)) { | |
| Get-Content -LiteralPath $logPath -Tail 200 -ErrorAction SilentlyContinue | |
| Stop-Process -Id $process.Id -Force -ErrorAction SilentlyContinue | |
| throw "Windows driver installer uninstall timed out after 5 minutes." | |
| } | |
| if ($process.ExitCode -notin @(0, 3010)) { | |
| Get-Content -LiteralPath $logPath -ErrorAction SilentlyContinue | |
| throw "Windows driver installer uninstall exited with code $($process.ExitCode)." | |
| } | |
| } | |
| - name: Install | |
| run: cmake --install cmake-build-ci --config ${{ env.CMAKE_BUILD_CONFIG }} --prefix cmake-build-ci/install | |
| - name: Upload install artifact | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: install-${{ matrix.name }} | |
| path: cmake-build-ci/install | |
| if-no-files-found: error | |
| - name: Upload report artifact | |
| if: >- | |
| always() && | |
| ( | |
| steps.test_report.outcome == 'success' || | |
| steps.test_msvc.outcome == 'success' || | |
| steps.test_msvc.outcome == 'failure' | |
| ) | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: reports-${{ matrix.name }} | |
| path: cmake-build-ci/reports | |
| if-no-files-found: error | |
| windows_driver: | |
| name: Windows Driver Installer | |
| needs: setup_release | |
| permissions: | |
| contents: read | |
| runs-on: windows-2022 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| submodules: recursive | |
| - name: Setup dotnet | |
| uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0 | |
| with: | |
| dotnet-version: '10.x' | |
| - name: Configure Windows driver package | |
| shell: pwsh | |
| env: | |
| BRANCH: ${{ github.head_ref || github.ref_name }} | |
| BUILD_VERSION: ${{ needs.setup_release.outputs.release_version }} | |
| COMMIT: ${{ needs.setup_release.outputs.release_commit }} | |
| run: | | |
| $certificatePath = Join-Path $env:GITHUB_WORKSPACE "cmake-build-driver\certificates\libvirtualhid-ci-test.cer" | |
| cmake ` | |
| -DBUILD_DOCS=OFF ` | |
| -DBUILD_EXAMPLES=ON ` | |
| -DBUILD_TESTS=OFF ` | |
| -DLIBVIRTUALHID_BUILD_WINDOWS_DRIVER=ON ` | |
| -DLIBVIRTUALHID_ENABLE_PACKAGING=ON ` | |
| "-DLIBVIRTUALHID_DRIVER_TEST_CERTIFICATE=$certificatePath" ` | |
| -A x64 ` | |
| -B cmake-build-driver ` | |
| -G "Visual Studio 17 2022" ` | |
| -S . | |
| - name: Build Windows driver package | |
| shell: pwsh | |
| run: >- | |
| cmake --build cmake-build-driver | |
| --config ${{ env.DRIVER_BUILD_CONFIG }} | |
| --target libvirtualhid_windows_catalog libvirtualhid_broker gamepad_adapter virtualhid_control | |
| --parallel 2 | |
| - name: Validate Azure signing configuration | |
| if: >- | |
| github.event_name == 'push' && | |
| needs.setup_release.outputs.publish_release == 'true' && | |
| vars.AZURE_SIGNING_ACCOUNT == '' | |
| shell: pwsh | |
| run: throw "Release builds must use Azure Trusted Signing for the Windows driver package." | |
| - name: Sign Windows driver package with local test certificate | |
| if: >- | |
| github.event_name == 'pull_request' || | |
| needs.setup_release.outputs.publish_release != 'true' | |
| shell: pwsh | |
| run: | | |
| $packagePath = Join-Path ` | |
| $env:GITHUB_WORKSPACE ` | |
| "cmake-build-driver\src\platform\windows\driver\package\$env:DRIVER_BUILD_CONFIG" | |
| $certificatePath = Join-Path ` | |
| $env:GITHUB_WORKSPACE ` | |
| "cmake-build-driver\certificates\libvirtualhid-ci-test.cer" | |
| .\scripts\windows\sign-driver-package.ps1 ` | |
| -PackagePath $packagePath ` | |
| -CertificatePath $certificatePath | |
| - name: Locate Windows driver catalog | |
| id: driver_catalog | |
| if: >- | |
| github.event_name == 'push' && | |
| needs.setup_release.outputs.publish_release == 'true' && | |
| vars.AZURE_SIGNING_ACCOUNT != '' | |
| shell: pwsh | |
| run: | | |
| $catalogPath = Join-Path ` | |
| $env:GITHUB_WORKSPACE ` | |
| "cmake-build-driver\src\platform\windows\driver\package\$env:DRIVER_BUILD_CONFIG\libvirtualhid.cat" | |
| "path=$catalogPath" >> $env:GITHUB_OUTPUT | |
| - name: Sign Windows driver package with Azure Trusted Signing | |
| if: >- | |
| github.event_name == 'push' && | |
| needs.setup_release.outputs.publish_release == 'true' && | |
| vars.AZURE_SIGNING_ACCOUNT != '' | |
| uses: azure/trusted-signing-action@c7ab2a863ab5f9a846ddb8265964877ef296ee82 # v2.0.0 | |
| with: | |
| azure-client-id: ${{ secrets.AZURE_CLIENT_ID }} | |
| azure-client-secret: ${{ secrets.AZURE_CLIENT_SECRET }} | |
| azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }} | |
| certificate-profile-name: ${{ vars.AZURE_SIGNING_CERT_PROFILE }} | |
| endpoint: ${{ vars.AZURE_SIGNING_ENDPOINT }} | |
| files: | | |
| ${{ steps.driver_catalog.outputs.path }} | |
| signing-account-name: ${{ vars.AZURE_SIGNING_ACCOUNT }} | |
| - name: Package Windows driver installer | |
| shell: pwsh | |
| run: | | |
| Push-Location .\cmake-build-driver | |
| cpack -G WIX -C $env:DRIVER_BUILD_CONFIG | |
| $packageExitCode = $LASTEXITCODE | |
| Pop-Location | |
| if ($packageExitCode -ne 0) { | |
| exit $packageExitCode | |
| } | |
| New-Item -ItemType Directory -Force -Path artifacts | Out-Null | |
| Copy-Item ` | |
| -LiteralPath .\cmake-build-driver\cpack_artifacts\libvirtualhid.msi ` | |
| -Destination ` | |
| ".\artifacts\libvirtualhid-Windows-AMD64-driver-installer.msi" | |
| - name: Export Azure driver signing certificate | |
| if: >- | |
| github.event_name == 'push' && | |
| needs.setup_release.outputs.publish_release == 'true' && | |
| vars.AZURE_SIGNING_ACCOUNT != '' | |
| shell: pwsh | |
| run: | | |
| $catalogPath = Join-Path ` | |
| $env:GITHUB_WORKSPACE ` | |
| "cmake-build-driver\src\platform\windows\driver\package\$env:DRIVER_BUILD_CONFIG\libvirtualhid.cat" | |
| $signature = Get-AuthenticodeSignature -FilePath $catalogPath | |
| if ($signature.Status -ne "Valid") { | |
| throw "Azure signed driver catalog is not valid: $($signature.StatusMessage)" | |
| } | |
| if (!$signature.SignerCertificate) { | |
| throw "Azure signed driver catalog did not expose a signer certificate." | |
| } | |
| $certificatePath = Join-Path $env:GITHUB_WORKSPACE "artifacts\libvirtualhid-driver-signing.cer" | |
| New-Item -ItemType Directory -Force -Path (Split-Path -Parent $certificatePath) | Out-Null | |
| Export-Certificate -Cert $signature.SignerCertificate -FilePath $certificatePath -Force | Out-Null | |
| Write-Host ( | |
| "Exported Azure driver signing certificate " + | |
| "$($signature.SignerCertificate.Subject) [$($signature.SignerCertificate.Thumbprint)]." | |
| ) | |
| - name: Sign Windows driver installer with Azure Trusted Signing | |
| if: >- | |
| github.event_name == 'push' && | |
| needs.setup_release.outputs.publish_release == 'true' && | |
| vars.AZURE_SIGNING_ACCOUNT != '' | |
| uses: azure/trusted-signing-action@c7ab2a863ab5f9a846ddb8265964877ef296ee82 # v2.0.0 | |
| with: | |
| azure-client-id: ${{ secrets.AZURE_CLIENT_ID }} | |
| azure-client-secret: ${{ secrets.AZURE_CLIENT_SECRET }} | |
| azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }} | |
| certificate-profile-name: ${{ vars.AZURE_SIGNING_CERT_PROFILE }} | |
| endpoint: ${{ vars.AZURE_SIGNING_ENDPOINT }} | |
| files-folder: artifacts | |
| files-folder-filter: msi | |
| files-folder-recurse: false | |
| signing-account-name: ${{ vars.AZURE_SIGNING_ACCOUNT }} | |
| - name: Validate release signing identities | |
| if: >- | |
| github.event_name == 'push' && | |
| needs.setup_release.outputs.publish_release == 'true' && | |
| vars.AZURE_SIGNING_ACCOUNT != '' | |
| shell: pwsh | |
| run: | | |
| $catalogPath = Join-Path ` | |
| $env:GITHUB_WORKSPACE ` | |
| "cmake-build-driver\src\platform\windows\driver\package\$env:DRIVER_BUILD_CONFIG\libvirtualhid.cat" | |
| $installerPath = Get-ChildItem -LiteralPath .\artifacts -Filter *.msi | | |
| Select-Object -ExpandProperty FullName -First 1 | |
| if (!$installerPath) { | |
| throw "The signed Windows driver installer was not found." | |
| } | |
| $catalogSignature = Get-AuthenticodeSignature -FilePath $catalogPath | |
| $installerSignature = Get-AuthenticodeSignature -FilePath $installerPath | |
| foreach ($signature in @($catalogSignature, $installerSignature)) { | |
| if ($signature.Status -ne "Valid" -or !$signature.SignerCertificate) { | |
| throw "A release signature is invalid: $($signature.StatusMessage)" | |
| } | |
| } | |
| if ($catalogSignature.SignerCertificate.Subject -cne ` | |
| $installerSignature.SignerCertificate.Subject) { | |
| throw "The catalog and MSI were signed with different identities." | |
| } | |
| Write-Host ( | |
| "Validated release signer " + | |
| "$($installerSignature.SignerCertificate.Subject) " + | |
| "[$($installerSignature.SignerCertificate.Thumbprint)]." | |
| ) | |
| - name: Debug wix | |
| if: always() | |
| shell: pwsh | |
| run: | | |
| Get-Content .\cmake-build-driver\cpack_artifacts\_CPack_Packages\win64\WIX\wix.log ` | |
| -ErrorAction SilentlyContinue | |
| - name: Upload Windows driver installer artifact | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: windows-driver-installer | |
| path: artifacts | |
| if-no-files-found: error | |
| codecov: | |
| name: Codecov-${{ matrix.flag }} | |
| if: >- | |
| always() && | |
| (needs.build.result == 'success' || needs.build.result == 'failure') && | |
| startsWith(github.repository, 'LizardByte/') | |
| needs: build | |
| permissions: | |
| contents: read | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - build_name: Linux-GCC | |
| flag: Linux-GCC | |
| has_coverage: true | |
| - build_name: Linux-Clang | |
| flag: Linux-Clang | |
| has_coverage: true | |
| - build_name: macOS | |
| flag: macOS | |
| has_coverage: true | |
| - build_name: Windows-MinGW-UCRT64 | |
| flag: Windows-MinGW-UCRT64 | |
| has_coverage: true | |
| - build_name: Windows-MSVC | |
| flag: Windows-MSVC | |
| has_coverage: true | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Download report artifact | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: reports-${{ matrix.build_name }} | |
| path: _reports | |
| - name: Debug coverage file | |
| if: matrix.has_coverage | |
| run: cat _reports/coverage.xml | |
| - name: Upload test coverage | |
| if: matrix.has_coverage | |
| uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 | |
| with: | |
| disable_search: true | |
| fail_ci_if_error: true | |
| files: ./_reports/coverage.xml | |
| report_type: coverage | |
| flags: ${{ matrix.flag }} | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| verbose: true | |
| - name: Upload test results | |
| uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 | |
| with: | |
| disable_search: true | |
| fail_ci_if_error: true | |
| files: ./_reports/junit.xml | |
| report_type: test_results | |
| flags: ${{ matrix.flag }} | |
| token: ${{ secrets.CODECOV_TOKEN }} | |
| verbose: true | |
| release: | |
| name: Release | |
| if: >- | |
| always() && | |
| needs.setup_release.outputs.publish_release == 'true' && | |
| needs.build.result == 'success' && | |
| needs.windows_driver.result == 'success' && | |
| startsWith(github.repository, 'LizardByte/') | |
| needs: | |
| - build | |
| - windows_driver | |
| - setup_release | |
| permissions: | |
| contents: read | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Download install artifact (Linux-GCC) | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: install-Linux-GCC | |
| path: install-Linux-GCC | |
| - name: Download install artifact (Linux-Clang) | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: install-Linux-Clang | |
| path: install-Linux-Clang | |
| - name: Download install artifact (macOS) | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: install-macOS | |
| path: install-macOS | |
| - name: Download install artifact (Windows-MinGW-UCRT64) | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: install-Windows-MinGW-UCRT64 | |
| path: install-Windows-MinGW-UCRT64 | |
| - name: Download install artifact (Windows-MSVC) | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: install-Windows-MSVC | |
| path: install-Windows-MSVC | |
| - name: Download Windows driver installer artifact | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: windows-driver-installer | |
| path: windows-driver-installer | |
| - name: Package install artifacts | |
| run: | | |
| mkdir -p artifacts | |
| for name in Linux-GCC Linux-Clang macOS Windows-MinGW-UCRT64 Windows-MSVC; do | |
| release_name="${name}" | |
| case "${name}" in | |
| Windows-MinGW-UCRT64) release_name="Windows-AMD64-MinGW-UCRT64" ;; | |
| Windows-MSVC) release_name="Windows-AMD64-MSVC" ;; | |
| esac | |
| zip -r \ | |
| "artifacts/libvirtualhid-${release_name}.zip" \ | |
| "install-${name}" | |
| done | |
| cp windows-driver-installer/*.msi artifacts/ | |
| - name: Validate release metadata | |
| env: | |
| RELEASE_COMMIT: ${{ needs.setup_release.outputs.release_commit }} | |
| RELEASE_TAG: ${{ needs.setup_release.outputs.release_tag }} | |
| RELEASE_VERSION: ${{ needs.setup_release.outputs.release_version }} | |
| run: | | |
| test -n "${RELEASE_TAG}" | |
| test -n "${RELEASE_VERSION}" | |
| test "${RELEASE_COMMIT}" = "${GITHUB_SHA}" | |
| test -s "artifacts/libvirtualhid-Linux-GCC.zip" | |
| test -s "artifacts/libvirtualhid-Linux-Clang.zip" | |
| test -s "artifacts/libvirtualhid-macOS.zip" | |
| test -s "artifacts/libvirtualhid-Windows-AMD64-driver-installer.msi" | |
| test -s "artifacts/libvirtualhid-Windows-AMD64-MinGW-UCRT64.zip" | |
| test -s "artifacts/libvirtualhid-Windows-AMD64-MSVC.zip" | |
| - name: Create/Update GitHub Release | |
| if: needs.setup_release.outputs.publish_release == 'true' | |
| uses: LizardByte/actions/actions/release_create@d0ae7f82215a479fe2b74f4088c53ee6460513dd # v2026.728.214955 | |
| with: | |
| allowUpdates: false | |
| body: ${{ needs.setup_release.outputs.release_body }} | |
| draft: true | |
| generateReleaseNotes: ${{ needs.setup_release.outputs.release_generate_release_notes }} | |
| name: ${{ needs.setup_release.outputs.release_tag }} | |
| prerelease: false | |
| tag: ${{ needs.setup_release.outputs.release_tag }} | |
| token: ${{ secrets.GH_BOT_TOKEN }} | |
| virustotal_api_key: ${{ secrets.VIRUSTOTAL_API_KEY }} |