mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-11-06 05:17:15 +00:00
fix(release): Windows x64 MSIX smoke runs on a GitHub-hosted runner and reports why a launch dies
Every Windows x64 MSIX chat smoke since the Blacksmith migration (91fae5f8cb) failed: first on the GitHub-hosted guard, and since that guard was removed (272adc7b6f) with Playwright's "Process failed to launch!", on Electron 40 and 44 alike. The same builds' arm64 MSIX, on a GitHub-hosted runner, chats; the last green x64 smoke (Oct 14) ran on windows-latest-32-core. Move the x64 leg back there, asf1b290e773did for the install/update GUI legs. The failure itself carried no evidence: when the app exits before its debugger line, Playwright's Electron.launch leaves sibling stderr-watcher rejections unhandled, Node dies on them, and the smoke's catch never writes its receipt or Playwright's call log. The driver now logs those rejections (still failing the run), so the receipt records the app's exit code and stderr, and the Windows smoke saves Application-log errors from the run on failure.
This commit is contained in:
1 parent
907e3224c4
commit
3ba2b0787e
4 files changed
+57
-1
No files matched your search
@@ -245,7 +245,10 @@ jobs:
|
||||
name: Windows install and chat
|
||||
needs: validate
|
||||
if: inputs.platform == 'win32'
|
||||
runs-on: ${{ inputs.arch == 'arm64' && 'windows-latest-32-arm-core' || 'blacksmith-32vcpu-windows-2025' }}
|
||||
# GitHub-hosted for both arches: on blacksmith Windows runners the installed MSIX app exits
|
||||
# before Playwright attaches (every x64 smoke there failed; the same commit's arm64 MSIX on a
|
||||
# GitHub-hosted runner chats), as the install/update GUI legs did (f1b290e7734).
|
||||
runs-on: ${{ inputs.arch == 'arm64' && 'windows-latest-32-arm-core' || 'windows-latest-32-core' }}
|
||||
# windows-11-arm runners are much slower (npm ci alone runs 12+ minutes and
|
||||
# the MSIX leg exceeds 30 total); x64 legs complete in ~14.
|
||||
timeout-minutes: 45
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import { spawnSync } from 'node:child_process'
|
||||
import fs from 'node:fs'
|
||||
import os from 'node:os'
|
||||
import path from 'node:path'
|
||||
|
||||
import { expect, test } from 'vitest'
|
||||
|
||||
const driver = path.resolve(import.meta.dirname, '../../tests/install/e2e-assets/desktop-smoke.ts')
|
||||
|
||||
test('an app that dies before Playwright attaches leaves a failed receipt carrying its exit code and stderr', (): void => {
|
||||
const work = fs.mkdtempSync(path.join(os.tmpdir(), 'desktop-smoke-launch-'))
|
||||
|
||||
try {
|
||||
const dir = (name: string): string => {
|
||||
const target = path.join(work, name)
|
||||
fs.mkdirSync(target)
|
||||
|
||||
return target
|
||||
}
|
||||
|
||||
const out = path.join(work, 'out')
|
||||
|
||||
// Node as the "app": it rejects Playwright's Chromium switches and exits before any debugger line.
|
||||
const result = spawnSync(process.execPath, [driver, '--exe', process.execPath, '--root', dir('root'), '--origin', 'source',
|
||||
'--home', dir('home'), '--user-data', dir('user-data'), '--out', out, '--phase', 'installed',
|
||||
'--expect-commit', '0'.repeat(40)], { cwd: work, encoding: 'utf8', timeout: 60_000 })
|
||||
|
||||
expect(result.status).toBe(1)
|
||||
const receipt = JSON.parse(fs.readFileSync(path.join(out, 'desktop-chat-installed.json'), 'utf8'))
|
||||
expect(receipt.status).toBe('failed')
|
||||
expect(receipt.error).toContain('Process failed to launch!')
|
||||
expect(receipt.error).toMatch(/<process did exit: exitCode=[1-9]\d*/)
|
||||
expect(receipt.error).toMatch(/\]\[err\] .*bad option: --no-sandbox/)
|
||||
} finally {
|
||||
fs.rmSync(work, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
@@ -384,6 +384,16 @@ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href)
|
||||
'user-data': { type: 'string' }, out: { type: 'string' }, phase: { type: 'string' },
|
||||
'expect-commit': { type: 'string' }, 'launch-spec': { type: 'string' }, 'mock-url': { type: 'string' },
|
||||
} })
|
||||
|
||||
// When the app exits before printing its debugger line, Playwright's Electron.launch rejects
|
||||
// the awaited watcher and leaves its sibling stderr watchers' rejections unhandled. Without a
|
||||
// listener Node dies on those first, so the catch below never writes the failed receipt or the
|
||||
// launch call log (the app's exit code and stderr). Still fail the run.
|
||||
process.on('unhandledRejection', (reason: unknown): void => {
|
||||
console.error(`unhandled rejection: ${redact(String(reason))}`)
|
||||
process.exitCode = 1
|
||||
})
|
||||
|
||||
try {
|
||||
await runInstalledDesktopSmoke(optionsSchema.parse(values))
|
||||
} catch (error) {
|
||||
|
||||
@@ -42,6 +42,7 @@ Start-Transcript -Path (Join-Path $Out 'native-install.log') | Out-Null
|
||||
$attempted = $false
|
||||
$installedFullName = $null
|
||||
$failed = $false
|
||||
$smokeStart = Get-Date
|
||||
try {
|
||||
$beforeHash = (Get-FileHash -Algorithm SHA256 -LiteralPath $Artifact).Hash.ToLowerInvariant()
|
||||
@{ path=$Artifact; sha256=$beforeHash } | ConvertTo-Json | Set-Content -LiteralPath (Join-Path $Out 'artifact.json')
|
||||
@@ -95,6 +96,11 @@ try {
|
||||
} catch {
|
||||
$failed = $true
|
||||
$_ | Out-String | Set-Content -LiteralPath (Join-Path $Out 'native-install-error.txt')
|
||||
# An app that dies before Playwright attaches leaves its faulting module and exception code
|
||||
# (Application Error / Windows Error Reporting / SideBySide) only in the Application log.
|
||||
Get-WinEvent -FilterHashtable @{ LogName='Application'; Level=1,2; StartTime=$smokeStart } -ErrorAction SilentlyContinue |
|
||||
Format-List TimeCreated, Id, ProviderName, Message | Out-String -Width 4096 |
|
||||
Set-Content -LiteralPath (Join-Path $Out 'native-app-error-events.txt')
|
||||
Write-Error $_ -ErrorAction Continue
|
||||
} finally {
|
||||
try {
|
||||
|
||||
Reference in new issue
Block a user