Skip to content

Commit 7e6ad0c

Browse files
committed
CI: Handle Windows virtual audio setup locally
1 parent 6ad18e2 commit 7e6ad0c

3 files changed

Lines changed: 129 additions & 2 deletions

File tree

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
# Based on LABSN/sound-ci-helpers windows/setup_sound.ps1:
2+
# https://github.com/LABSN/sound-ci-helpers/blob/20a2b9bbd21fd005d8fe89933901506dba84ea4e/windows/setup_sound.ps1
3+
#
4+
# The original helper downloaded VB-CABLE Pack43, trusted the VB-Audio
5+
# certificate, and installed vbMmeCable64_win7.inf with devcon. This local
6+
# version keeps that behavior without cloning the helper repository at CI
7+
# runtime. The vendored devcon.exe is from:
8+
# https://github.com/LABSN/sound-ci-helpers/blob/20a2b9bbd21fd005d8fe89933901506dba84ea4e/windows/devcon.exe
9+
10+
$ErrorActionPreference = "Stop"
11+
Set-StrictMode -Version 2.0
12+
13+
$driverUrl = "https://download.vb-audio.com/Download_CABLE/VBCABLE_Driver_Pack43.zip"
14+
$expectedSha256 = "66fd0a4d9f4896ff41632b7e3d53892c085c4561f53e8ae8d0f0bc10eedd1cdd"
15+
$devconExpectedSha256 = "97cff42f8c0fe4fbdf991273159516bf78090625a933c3983ebd6f62284e329a"
16+
$hardwareId = "VBAudioVACWDM"
17+
$devcon = Join-Path $PSScriptRoot "..\tools\windows\devcon.exe"
18+
$runnerTemp = $env:RUNNER_TEMP
19+
if ([string]::IsNullOrEmpty($runnerTemp)) {
20+
$runnerTemp = [System.IO.Path]::GetTempPath()
21+
}
22+
$workDir = Join-Path $runnerTemp "vbcable"
23+
$cacheDir = $env:VBCABLE_CACHE_DIR
24+
if ([string]::IsNullOrEmpty($cacheDir)) {
25+
$cacheDir = Join-Path $runnerTemp "vbcable-cache"
26+
}
27+
$zipName = "VBCABLE_Driver_Pack43.zip"
28+
$cachedZipPath = Join-Path $cacheDir $zipName
29+
$zipPath = Join-Path $workDir $zipName
30+
$extractDir = Join-Path $workDir "driver"
31+
32+
function Test-ExpectedHash {
33+
param(
34+
[Parameter(Mandatory = $true)]
35+
[string] $Path,
36+
[Parameter(Mandatory = $true)]
37+
[string] $ExpectedSha256
38+
)
39+
40+
if (!(Test-Path $Path)) {
41+
return $false
42+
}
43+
44+
$actualSha256 = (Get-FileHash -Algorithm SHA256 -Path $Path).Hash.ToLowerInvariant()
45+
return $actualSha256 -eq $ExpectedSha256
46+
}
47+
48+
function Invoke-Checked {
49+
param(
50+
[Parameter(Mandatory = $true)]
51+
[string] $FilePath,
52+
[Parameter(ValueFromRemainingArguments = $true)]
53+
[string[]] $Arguments
54+
)
55+
56+
Write-Host "Running: $FilePath $($Arguments -join ' ')"
57+
& $FilePath @Arguments
58+
if ($LASTEXITCODE -ne 0) {
59+
throw "$FilePath failed with exit code $LASTEXITCODE."
60+
}
61+
}
62+
63+
if (!(Test-ExpectedHash -Path $devcon -ExpectedSha256 $devconExpectedSha256)) {
64+
$actualSha256 = if (Test-Path $devcon) {
65+
(Get-FileHash -Algorithm SHA256 -Path $devcon).Hash.ToLowerInvariant()
66+
} else {
67+
"<missing>"
68+
}
69+
throw "Unexpected devcon.exe hash. Expected $devconExpectedSha256, got $actualSha256."
70+
}
71+
72+
New-Item -ItemType Directory -Force -Path $workDir, $cacheDir | Out-Null
73+
if (Test-Path $extractDir) {
74+
Remove-Item -Recurse -Force $extractDir
75+
}
76+
77+
if (Test-ExpectedHash -Path $cachedZipPath -ExpectedSha256 $expectedSha256) {
78+
Write-Host "Using cached VB-CABLE package: $cachedZipPath"
79+
Copy-Item -Force $cachedZipPath $zipPath
80+
} else {
81+
if (Test-Path $cachedZipPath) {
82+
Write-Host "Ignoring cached VB-CABLE package with unexpected hash: $cachedZipPath"
83+
Remove-Item -Force $cachedZipPath
84+
}
85+
86+
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
87+
Write-Host "Downloading $driverUrl"
88+
Invoke-WebRequest -Uri $driverUrl -OutFile $zipPath -MaximumRetryCount 3 -RetryIntervalSec 2
89+
90+
if (!(Test-ExpectedHash -Path $zipPath -ExpectedSha256 $expectedSha256)) {
91+
$actualSha256 = (Get-FileHash -Algorithm SHA256 -Path $zipPath).Hash.ToLowerInvariant()
92+
throw "Unexpected VB-CABLE package hash. Expected $expectedSha256, got $actualSha256."
93+
}
94+
95+
Copy-Item -Force $zipPath $cachedZipPath
96+
}
97+
98+
Expand-Archive -LiteralPath $zipPath -DestinationPath $extractDir
99+
100+
$catalogPath = Join-Path $extractDir "vbaudio_cable64_win7.cat"
101+
$signature = Get-AuthenticodeSignature -FilePath $catalogPath
102+
if ($null -eq $signature.SignerCertificate) {
103+
throw "Could not read signer certificate from $catalogPath."
104+
}
105+
106+
$certPath = Join-Path $workDir "vbcable.cer"
107+
[System.IO.File]::WriteAllBytes(
108+
$certPath,
109+
$signature.SignerCertificate.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Cert)
110+
)
111+
112+
& certutil.exe -addstore -f "TrustedPublisher" $certPath
113+
if ($LASTEXITCODE -ne 0) {
114+
throw "certutil.exe failed with exit code $LASTEXITCODE."
115+
}
116+
117+
$infPath = Join-Path $extractDir "vbMmeCable64_win7.inf"
118+
Invoke-Checked $devcon "install" $infPath $hardwareId

.github/tools/windows/devcon.exe

79.5 KB
Binary file not shown.

.github/workflows/build.yml

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,18 @@ jobs:
2525
run: pulseaudio -D --start --exit-idle-time=-1
2626
if: matrix.os == 'ubuntu-24.04'
2727

28+
- name: Cache VB-CABLE package (Windows)
29+
uses: actions/cache@v4
30+
with:
31+
path: ${{ runner.temp }}\vbcable-cache
32+
key: vbcable-driver-pack43-${{ runner.os }}-66fd0a4d9f4896ff41632b7e3d53892c085c4561f53e8ae8d0f0bc10eedd1cdd
33+
if: ${{ matrix.os == 'windows-2025' }}
34+
2835
- name: Install virtual audio devices (Windows)
29-
run: git clone https://github.com/LABSN/sound-ci-helpers && powershell sound-ci-helpers/windows/setup_sound.ps1
36+
shell: pwsh
37+
env:
38+
VBCABLE_CACHE_DIR: ${{ runner.temp }}\vbcable-cache
39+
run: .\.github\scripts\setup_windows_audio.ps1
3040
if: ${{ matrix.os == 'windows-2025' }}
3141

3242
- name: Allow microphone access to all apps (Windows)
@@ -102,4 +112,3 @@ jobs:
102112
- name: Check format
103113
shell: bash
104114
run: cmake --build build --target clang-format-check
105-

0 commit comments

Comments
 (0)