From 5c0ce12339f7894c9cd0b3d9699e375b45998ad5 Mon Sep 17 00:00:00 2001 From: Mack Straight Date: Sat, 3 Oct 2026 10:29:22 -0400 Subject: [PATCH] ci: build a test-signed MSI installer Adds an installer job that builds SarInstallerActions and the WiX SarInstaller project around the test-signed driver and the x64/x86 SarAsio builds, and uploads it with the test certificate and install steps as sar-installer-x64-testsigned. WiX comes from the pinned 3.14 binaries zip, checked by hash. Product.wxs takes its input locations and version from preprocessor variables (defaulting to the old relative paths), the wixproj finds the difxapp wixlibs through $(WixExtDir) instead of a hard-coded WiX 3.10 install, and SarInstallerActions also looks in the WiX 3.14 SDK's include directory. CI now also runs on pushes to vnext and on every PR, so PRs stacked on other branches get builds. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/build.yml | 109 +++++++++++++++++- BUILDING.md | 8 +- SarInstaller/Product.wxs | 21 ++-- SarInstaller/SarInstaller.wixproj | 4 +- .../SarInstallerActions.vcxproj | 8 +- 5 files changed, 131 insertions(+), 19 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2ff6df7..fc83a93 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,10 +11,10 @@ name: build-and-package on: workflow_dispatch: push: - branches: [master] + branches: [master, vnext] tags: ["v*"] + # Every PR, including ones stacked on other PR branches or on vnext. pull_request: - branches: [master] permissions: contents: read @@ -242,3 +242,108 @@ jobs: name: sar-asio-${{ matrix.platform }} path: artifacts/${{ matrix.platform }}/ if-no-files-found: error + + # --------------------------------------------------------------------------- + # MSI installer around the TEST-SIGNED driver, for manual testing on machines + # with test signing enabled. A release MSI needs the attestation-signed + # driver instead; that comes after signing, not from this job. + # --------------------------------------------------------------------------- + installer: + name: installer (x64, test-signed) + needs: [driver, usermode] + runs-on: windows-2022 + env: + WIX_URL: https://github.com/wixtoolset/wix3/releases/download/wix3141rtm/wix314-binaries.zip + WIX_SHA256: 6ac824e1642d6f7277d0ed7ea09411a508f6116ba6fae0aa5f2c7daa2ff43d31 + steps: + - uses: actions/checkout@v4 + + - name: Add MSBuild to PATH + uses: microsoft/setup-msbuild@v2 + with: + vs-version: "[17.0,18.0)" + + - name: Install WiX 3.14 + shell: pwsh + run: | + # Pinned binaries zip rather than whatever the runner image carries. + $zip = "$env:RUNNER_TEMP\wix.zip" + Invoke-WebRequest $env:WIX_URL -OutFile $zip + $hash = (Get-FileHash $zip -Algorithm SHA256).Hash + if ($hash -ne $env:WIX_SHA256) { throw "WiX zip hash mismatch: $hash" } + $wix = "$env:RUNNER_TEMP\wix314" + Expand-Archive $zip $wix + # SarInstallerActions.vcxproj expects $(WIX) with a trailing slash. + "WIX=$wix\" | Out-File $env:GITHUB_ENV -Append + + - name: Build custom actions + shell: pwsh + run: | + & msbuild SynchronousAudioRouter.sln /t:SarInstallerActions ` + /p:Configuration=Release /p:Platform=x64 /m /verbosity:minimal /nologo + if ($LASTEXITCODE -ne 0) { throw "msbuild failed (exit $LASTEXITCODE)" } + + - name: Download test-signed driver + uses: actions/download-artifact@v4 + with: + name: sar-driver-x64-testsigned + path: stage/driver + + - name: Download SarAsio (x64) + uses: actions/download-artifact@v4 + with: + name: sar-asio-x64 + path: stage/x64 + + - name: Download SarAsio (x86) + uses: actions/download-artifact@v4 + with: + name: sar-asio-x86 + path: stage/x86 + + - name: Build MSI + shell: pwsh + run: | + $stage = (Resolve-Path stage).Path + Copy-Item x64/Release/SarInstallerActions.dll "$stage/x64" -Force + # MSI versions compare major.minor.build only; the run number in the + # fourth field just labels the build (same-version upgrades are on). + $version = "0.13.99.$env:GITHUB_RUN_NUMBER" + # A response file keeps the semicolons in DefineConstants away from + # PowerShell's argument quoting; msbuild splits them into -d defines. + @( + "/p:Configuration=Release" + "/p:Platform=x64" + "/p:WixInstallPath=$env:WIX" + "/p:WixTargetsPath=$env:WIX\wix.targets" + "/p:DefineConstants=`"ProductVersion=$version;Bin64=$stage\x64;Bin32=$stage\x86;DriverDir=$stage\driver`"" + ) | Out-File "$env:RUNNER_TEMP\installer.rsp" -Encoding ascii + & msbuild SarInstaller/SarInstaller.wixproj "@$env:RUNNER_TEMP\installer.rsp" /verbosity:minimal /nologo + if ($LASTEXITCODE -ne 0) { throw "msbuild failed (exit $LASTEXITCODE)" } + + $out = 'artifacts/installer-x64-testsigned' + New-Item -ItemType Directory -Force -Path $out | Out-Null + Copy-Item SarInstaller/bin/x64/Release/SynchronousAudioRouter.msi "$out/SynchronousAudioRouter-$version-x64-testsigned.msi" + Copy-Item "$stage/driver/testsign.cer" $out + $lines = Get-ChildItem $out -File | Get-FileHash -Algorithm SHA256 | + ForEach-Object { '{0} {1}' -f $_.Hash, (Split-Path $_.Path -Leaf) } + $lines | Out-File "$out/SHA256SUMS.txt" -Encoding ascii + @" + TEST-SIGNED installer: the driver inside only loads with test signing on. + Not for distribution. On the test machine, as administrator: + + bcdedit /set testsigning on (then reboot; Secure Boot must be off) + certutil -addstore Root testsign.cer + certutil -addstore TrustedPublisher testsign.cer + msiexec /i SynchronousAudioRouter-$version-x64-testsigned.msi + + Every CI run signs with a fresh certificate, so trust this run's + testsign.cer before installing this run's MSI. + "@ | Out-File "$out/README.txt" -Encoding ascii + + - name: Upload installer + uses: actions/upload-artifact@v4 + with: + name: sar-installer-x64-testsigned + path: artifacts/installer-x64-testsigned/ + if-no-files-found: error diff --git a/BUILDING.md b/BUILDING.md index faa5b22..440b354 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -71,9 +71,11 @@ These are tracked here so nothing is silently missing: - **`DriverVer` in source.** The committed INF still has the placeholder `DriverVer=0.1`; the pipeline stamps a valid value at build time but the source value should eventually be fixed too. -- **Installer (MSI).** The WiX `SarInstaller` project is not built by this - pipeline yet (needs the WiX 3.x toolset on the runner). The driver package is - sufficient for signing; the MSI is a packaging convenience to add back later. +- **Release installer (MSI).** The `installer` job builds the WiX + `SarInstaller` project (pinned WiX 3.14 binaries) around the *test-signed* + driver, as artifact `sar-installer-x64-testsigned`, for manual testing on + machines with test signing on (see the README.txt inside). A redistributable + MSI needs the attestation-signed driver, so it has to be built after signing. - **EWDK alternative.** For maximum reproducibility you can swap the `Install-WdkBuildEnv.ps1` step for a pinned **Enterprise WDK** ISO (a fully self-contained, version-locked build environment). That removes the dependency diff --git a/SarInstaller/Product.wxs b/SarInstaller/Product.wxs index b94b36b..b128e29 100644 --- a/SarInstaller/Product.wxs +++ b/SarInstaller/Product.wxs @@ -1,11 +1,16 @@ + + + + + + SourceFile="$(var.Bin64)\SarInstallerActions.dll"/> @@ -98,7 +103,7 @@ Win64="yes"> diff --git a/SarInstaller/SarInstaller.wixproj b/SarInstaller/SarInstaller.wixproj index 5117d04..37900a7 100644 --- a/SarInstaller/SarInstaller.wixproj +++ b/SarInstaller/SarInstaller.wixproj @@ -55,13 +55,13 @@ - ..\..\..\..\..\Program Files (x86)\WiX Toolset v3.10\bin\difxapp_x64.wixlib + $(WixExtDir)\difxapp_x64.wixlib difxapp_x64 - ..\..\..\..\..\Program Files (x86)\WiX Toolset v3.10\bin\difxapp_x86.wixlib + $(WixExtDir)\difxapp_x86.wixlib difxapp_x86 diff --git a/SarInstallerActions/SarInstallerActions.vcxproj b/SarInstallerActions/SarInstallerActions.vcxproj index 8a4bb6b..09b01ab 100644 --- a/SarInstallerActions/SarInstallerActions.vcxproj +++ b/SarInstallerActions/SarInstallerActions.vcxproj @@ -93,7 +93,7 @@ Disabled - $(WIX)sdk\VS2015\inc;%(AdditionalIncludeDirectories) + $(WIX)sdk\inc;$(WIX)sdk\VS2015\inc;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;_USRDLL;CUSTOMACTIONTEST_EXPORTS;%(PreprocessorDefinitions) true EnableFastChecks @@ -114,7 +114,7 @@ Disabled - $(WIX)sdk\VS2015\inc;%(AdditionalIncludeDirectories) + $(WIX)sdk\inc;$(WIX)sdk\VS2015\inc;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;_USRDLL;CUSTOMACTIONTEST_EXPORTS;%(PreprocessorDefinitions) EnableFastChecks MultiThreadedDebug @@ -134,7 +134,7 @@ MaxSpeed true - $(WIX)sdk\VS2015\inc;%(AdditionalIncludeDirectories) + $(WIX)sdk\inc;$(WIX)sdk\VS2015\inc;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;_USRDLL;CUSTOMACTIONTEST_EXPORTS;%(PreprocessorDefinitions) MultiThreaded true @@ -157,7 +157,7 @@ MaxSpeed true - $(WIX)sdk\VS2015\inc;%(AdditionalIncludeDirectories) + $(WIX)sdk\inc;$(WIX)sdk\VS2015\inc;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;_USRDLL;CUSTOMACTIONTEST_EXPORTS;%(PreprocessorDefinitions) MultiThreaded true