8080 - name : Test integration harness helpers
8181 run : npm run test:integration-harness
8282
83+ - name : Test workflow and CLI packaging guards
84+ run : npm run test:github-actions
85+
8386 - name : Typecheck client
8487 run : npm run --prefix packages/client typecheck
8588
@@ -289,12 +292,16 @@ jobs:
289292 SIMDECK_INTEGRATION_DEVICE_TYPE : iPhone SE (3rd generation)
290293
291294 integration-android :
292- name : Android emulator integration
293- runs-on : ubuntu-latest
294- timeout-minutes : 35
295+ name : Android emulator integration (${{ matrix.os }})
296+ runs-on : ${{ matrix.os }}
297+ timeout-minutes : 50
295298 needs :
296299 - client
297300 - packages
301+ strategy :
302+ fail-fast : false
303+ matrix :
304+ os : [ubuntu-latest, windows-latest]
298305
299306 steps :
300307 - uses : actions/checkout@v4
@@ -308,6 +315,7 @@ jobs:
308315 - uses : dtolnay/rust-toolchain@stable
309316
310317 - name : Enable KVM access
318+ if : runner.os == 'Linux'
311319 run : |
312320 echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
313321 sudo udevadm control --reload-rules
@@ -316,12 +324,13 @@ jobs:
316324 - name : Install root dependencies
317325 run : npm ci --ignore-scripts --force
318326
319- - name : Build Linux Android integration artifacts
327+ - name : Build Android integration artifacts
320328 run : |
321329 npm run build:cli
322330 npm run build:simdeck-test
323331
324- - name : Android emulator integration tests
332+ - name : Android emulator integration tests (Linux)
333+ if : runner.os == 'Linux'
325334 uses : reactivecircus/android-emulator-runner@v2
326335 with :
327336 api-level : 35
@@ -330,8 +339,99 @@ jobs:
330339 profile : pixel_6
331340 avd-name : SimDeck_Pixel_CI
332341 disable-animations : true
342+ emulator-options : -no-window -no-audio -no-boot-anim -no-snapshot -gpu swiftshader_indirect -grpc 8554
333343 script : npm run test:integration:android
334344 env :
335345 SIMDECK_INTEGRATION_ANDROID_AVD : SimDeck_Pixel_CI
336346 SIMDECK_INTEGRATION_REQUIRE_RUNNING_ANDROID : " 1"
337347 SIMDECK_INTEGRATION_VERBOSE : " 1"
348+
349+ - name : Create and boot Android emulator (Windows)
350+ if : runner.os == 'Windows'
351+ shell : pwsh
352+ run : |
353+ $ErrorActionPreference = "Stop"
354+ $sdk = $env:ANDROID_HOME
355+ if (-not $sdk) {
356+ $sdk = $env:ANDROID_SDK_ROOT
357+ }
358+ if (-not $sdk) {
359+ $sdk = Join-Path $env:LOCALAPPDATA "Android\Sdk"
360+ }
361+ $env:ANDROID_HOME = $sdk
362+ $env:ANDROID_SDK_ROOT = $sdk
363+ $cmdlineTools = Get-ChildItem -Path (Join-Path $sdk "cmdline-tools") -Directory -ErrorAction SilentlyContinue |
364+ Sort-Object Name -Descending |
365+ Select-Object -First 1
366+ $toolsBin = if (Test-Path (Join-Path $sdk "cmdline-tools\latest\bin")) {
367+ Join-Path $sdk "cmdline-tools\latest\bin"
368+ } elseif ($cmdlineTools) {
369+ Join-Path $cmdlineTools.FullName "bin"
370+ } else {
371+ Join-Path $sdk "tools\bin"
372+ }
373+ $sdkmanager = Join-Path $toolsBin "sdkmanager.bat"
374+ $avdmanager = Join-Path $toolsBin "avdmanager.bat"
375+ $adb = Join-Path $sdk "platform-tools\adb.exe"
376+ $emulator = Join-Path $sdk "emulator\emulator.exe"
377+
378+ & $sdkmanager --install "platform-tools" "emulator" "platforms;android-35" "system-images;android-35;google_apis;x86_64"
379+ 1..50 | ForEach-Object { "y" } | & $sdkmanager --licenses
380+ "no" | & $avdmanager create avd --force --name SimDeck_Pixel_CI --package "system-images;android-35;google_apis;x86_64" --device "pixel_6"
381+
382+ $args = @(
383+ "-avd", "SimDeck_Pixel_CI",
384+ "-no-window",
385+ "-no-audio",
386+ "-no-boot-anim",
387+ "-no-snapshot",
388+ "-gpu", "swiftshader_indirect",
389+ "-grpc", "8554"
390+ )
391+ $process = Start-Process -FilePath $emulator -ArgumentList $args -PassThru
392+ $process.Id | Out-File -FilePath emulator.pid -Encoding ascii
393+ & $adb wait-for-device
394+ $deadline = (Get-Date).AddMinutes(8)
395+ do {
396+ if ($process.HasExited) {
397+ throw "Android emulator exited early with code $($process.ExitCode)."
398+ }
399+ $booted = (& $adb shell getprop sys.boot_completed).Trim()
400+ if ($booted -eq "1") {
401+ break
402+ }
403+ Start-Sleep -Seconds 5
404+ } while ((Get-Date) -lt $deadline)
405+ if ($booted -ne "1") {
406+ & $adb devices
407+ throw "Android emulator did not boot before the timeout."
408+ }
409+ & $adb shell settings put global window_animation_scale 0
410+ & $adb shell settings put global transition_animation_scale 0
411+ & $adb shell settings put global animator_duration_scale 0
412+
413+ - name : Android emulator integration tests (Windows)
414+ if : runner.os == 'Windows'
415+ run : npm run test:integration:android
416+ env :
417+ SIMDECK_INTEGRATION_ANDROID_AVD : SimDeck_Pixel_CI
418+ SIMDECK_INTEGRATION_REQUIRE_RUNNING_ANDROID : " 1"
419+ SIMDECK_INTEGRATION_VERBOSE : " 1"
420+
421+ - name : Stop Android emulator (Windows)
422+ if : always() && runner.os == 'Windows'
423+ shell : pwsh
424+ run : |
425+ $sdk = $env:ANDROID_HOME
426+ if (-not $sdk) {
427+ $sdk = $env:ANDROID_SDK_ROOT
428+ }
429+ if ($sdk) {
430+ $adb = Join-Path $sdk "platform-tools\adb.exe"
431+ if (Test-Path $adb) {
432+ & $adb emu kill
433+ }
434+ }
435+ if (Test-Path emulator.pid) {
436+ Stop-Process -Id (Get-Content emulator.pid) -Force -ErrorAction SilentlyContinue
437+ }
0 commit comments