Files
cinny-desktop/.gitea/workflows/windows-smoke.yml
T
Lotus CIandClaude Opus 5.5 852c67cc12 ci(windows-smoke): update over the running app, as the auto-updater does (#1)
After the smoke test, start the installed app, run the installer in
passive update mode (/P /UPDATE) while it runs, and only stop the app 3 s
later. Passes when the installer finishes by itself (no "Error opening
file for writing" dialog), exits 0, and has rewritten cinny.exe.

Refs #1, #19

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
2026-10-20 23:27:29 -04:00

171 lines
8.8 KiB
YAML

# cinny-desktop #19: smoke-test the Windows app on the `windows` runner.
#
# Installs the NSIS bundle silently, starts the installed app with WebView2's
# DevTools port open, and drives its real page with scripts/windows-smoke.mjs
# (playwright-core over CDP): boots to the login screen, local server address,
# microphone permission for the app and not for a foreign page, the call page's
# own origin + isolation, the Credential Manager round trip. Features a build
# doesn't have are reported "n/a". Then it updates the app while it is running,
# the way the auto-updater does (#1).
#
# Run it from the Actions tab (workflow_dispatch):
# - no `ref`: tests the published nightly installer (a couple of minutes);
# - `ref` = a branch (e.g. a PR branch): builds that branch, then tests it.
name: Windows smoke
on:
workflow_dispatch:
inputs:
ref:
description: 'Branch to build and test (empty: test the published nightly)'
required: false
default: ''
env:
GITEA_URL: ${{ github.server_url }}
REPO: ${{ github.repository }}
jobs:
smoke:
runs-on: windows
timeout-minutes: 90
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: .node-version
- name: Download the published nightly installer
if: ${{ inputs.ref == '' }}
shell: powershell
run: |
New-Item -ItemType Directory -Force -Path smoke-installer | Out-Null
Invoke-WebRequest -Uri "$env:GITEA_URL/$env:REPO/releases/download/latest/LotusChat-x86_64-setup.exe" -OutFile smoke-installer\setup.exe
Get-Item smoke-installer\setup.exe | Select-Object Name, Length
# A second actions/checkout in one job trips over the host runner's
# action cache on Windows ("Access is denied" on its pack files), so
# fetch the branch into a worktree with plain git instead.
- name: Check out the branch to build
if: ${{ inputs.ref != '' }}
shell: powershell
run: |
git fetch --depth=1 origin "${{ inputs.ref }}"
git worktree add --force build-src FETCH_HEAD
git -C build-src log --oneline -1
- name: Build the branch
if: ${{ inputs.ref != '' }}
shell: powershell
env:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ''
NODE_OPTIONS: '--max_old_space_size=4096'
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
CARGO_HTTP_MULTIPLEXING: 'false'
CARGO_NET_RETRY: '5'
run: |
cd build-src
git submodule update --init --depth=1
cd cinny; npm ci; cd ..
node scripts/sync-web-config.mjs
npm ci
$env:PATH = "$env:USERPROFILE\.cargo\bin;$env:PATH"
$toolchain = Get-ChildItem "$env:USERPROFILE\.rustup\toolchains" -Directory -ErrorAction SilentlyContinue |
Where-Object { $_.Name -match 'stable' } | Select-Object -First 1
if ($toolchain) { $env:PATH = "$($toolchain.FullName)\bin;$env:PATH" }
npm run tauri -- build --bundles nsis
New-Item -ItemType Directory -Force -Path ..\smoke-installer | Out-Null
$exe = Get-ChildItem src-tauri\target\release\bundle\nsis\*-setup.exe | Select-Object -First 1
Copy-Item $exe.FullName ..\smoke-installer\setup.exe
- name: Install silently
shell: powershell
run: |
Get-Process cinny -ErrorAction SilentlyContinue | Stop-Process -Force
Start-Process smoke-installer\setup.exe -ArgumentList '/S' -Wait
$app = Join-Path $env:LOCALAPPDATA 'Lotus Chat\cinny.exe'
if (-not (Test-Path $app)) { Write-Error "not installed at $app"; exit 1 }
(Get-Item $app).VersionInfo | Select-Object ProductVersion, FileVersion
- name: Start the app and smoke test it
shell: powershell
run: |
New-Item -ItemType Directory -Force -Path smoke-deps | Out-Null
Push-Location smoke-deps
npm init -y | Out-Null
npm install --no-audit --no-fund playwright-core@1 | Out-Null
Pop-Location
Copy-Item scripts\windows-smoke.mjs smoke-deps\windows-smoke.mjs
# Same step as the test: a process started in an earlier step may be
# cleaned up when that step ends.
# The app sets its own WebView2 arguments, so WebView2's env var doesn't
# apply; the app opens the DevTools port itself when asked (#19).
$env:LOTUS_WEBVIEW2_DEBUG_PORT = '9222'
$app = Join-Path $env:LOCALAPPDATA 'Lotus Chat\cinny.exe'
Start-Process $app
Start-Sleep -Seconds 15
Write-Host "--- processes"
Get-Process cinny, msedgewebview2 -ErrorAction SilentlyContinue | Select-Object Name, Id, SessionId | Format-Table | Out-String | Write-Host
Write-Host "--- listening ports 9222/44548"
Get-NetTCPConnection -State Listen -ErrorAction SilentlyContinue | Where-Object { $_.LocalPort -in 9222, 44548 } | Select-Object LocalAddress, LocalPort, OwningProcess | Format-Table | Out-String | Write-Host
Write-Host "--- WebView2 runtime"
Get-ItemProperty 'HKLM:\SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}' -ErrorAction SilentlyContinue | Select-Object pv | Out-String | Write-Host
Write-Host "--- whoami: $(whoami)"
Write-Host "--- webview2 command lines"
Get-CimInstance Win32_Process -Filter "Name='msedgewebview2.exe'" | Select-Object -First 2 | ForEach-Object { Write-Host $_.CommandLine.Substring(0, [Math]::Min(600, $_.CommandLine.Length)) }
Write-Host "--- netstat"
netstat -ano | Select-String "LISTENING" | Select-String ":9222 |:44548 " | ForEach-Object { Write-Host $_ }
Write-Host "--- http probes"
try { (Invoke-WebRequest -UseBasicParsing http://127.0.0.1:9222/json/version -TimeoutSec 5).Content | Write-Host } catch { Write-Host "9222: $($_.Exception.Message)" }
try { (Invoke-WebRequest -UseBasicParsing http://localhost:44548/ -TimeoutSec 5).StatusCode | Write-Host } catch { Write-Host "44548: $($_.Exception.Message)" }
node smoke-deps\windows-smoke.mjs smoke-out
# cinny-desktop #1: an auto-update over the RUNNING app. tauri-plugin-updater
# starts the installer in passive update mode (/P /UPDATE) and the app then
# exits; if it was slow to let go of cinny.exe, NSIS stopped on "Error opening
# file for writing". The installer's pre-install hook (src-tauri/nsis/
# hooks.nsh) now waits for the file to be writable. Here the app stays up 3 s
# after the installer starts, then goes. Pass = the installer finishes on its
# own (no dialog), exits 0, and has rewritten cinny.exe (its timestamp was set
# to 2000 first; NSIS writes the build's own timestamp back).
- name: Update over the running app (#1)
shell: powershell
run: |
Get-Process cinny -ErrorAction SilentlyContinue | Stop-Process -Force
$app = Join-Path $env:LOCALAPPDATA 'Lotus Chat\cinny.exe'
(Get-Item $app).LastWriteTime = Get-Date '2000-01-01'
Start-Process $app
Start-Sleep -Seconds 10
if (-not (Get-Process cinny -ErrorAction SilentlyContinue)) { Write-Error 'app did not start'; exit 1 }
$inst = Start-Process smoke-installer\setup.exe -ArgumentList '/P', '/UPDATE' -PassThru
Start-Sleep -Seconds 3
Get-Process cinny -ErrorAction SilentlyContinue | Stop-Process -Force
if (-not $inst.WaitForExit(90000)) {
Write-Host '--- windows with a title (an error dialog would be here)'
Get-Process | Where-Object { $_.MainWindowTitle } | Select-Object Name, MainWindowTitle | Format-Table | Out-String | Write-Host
$inst | Stop-Process -Force
Write-Error 'installer did not finish within 90 s (stuck on a dialog?)'
exit 1
}
$written = (Get-Item $app).LastWriteTime
Write-Host "installer exit code $($inst.ExitCode); cinny.exe timestamp now $written"
if ($inst.ExitCode -ne 0) { Write-Error "installer exit code $($inst.ExitCode)"; exit 1 }
if ($written -lt (Get-Date '2001-01-01')) { Write-Error 'cinny.exe was not rewritten'; exit 1 }
Get-Process cinny -ErrorAction SilentlyContinue | Stop-Process -Force
- name: Stop the app
if: ${{ always() }}
shell: powershell
run: |
Get-Process cinny -ErrorAction SilentlyContinue | Stop-Process -Force
if (Test-Path smoke-out\results.json) { Get-Content smoke-out\results.json }
- name: Upload results and screenshots
if: ${{ always() }}
uses: actions/upload-artifact@v3
with:
name: windows-smoke
path: smoke-out