mirror of
https://github.com/stablyai/orca.git
synced 2026-10-22 08:03:37 +00:00
Harden layout validation, watcher lifecycle, and connection robustness
- Throw instead of silently skipping when the packaged daemon-entry is missing, preventing layout regressions from passing build checks. - Terminate idle parcel-watcher processes to reclaim native handles and avoid crash-prone native node module teardowns on shutdown. - Bind the persisted WS fallback port first to prevent orphaning active mobile pairings when the preferred port becomes free again. - Cap concurrent disk reads for restored dirty tab verification at three to prevent startup connection bottlenecks on remote SSH workspaces.
This commit is contained in:
@@ -353,6 +353,15 @@ describe('electron-builder config', () => {
|
||||
const launcherPath = join(resourcesDir, 'bin', 'orca-ide')
|
||||
await mkdir(join(resourcesDir, 'bin'), { recursive: true })
|
||||
await mkdir(join(resourcesDir, 'node_modules', 'zod', 'src'), { recursive: true })
|
||||
// Why: afterPack now fails hard when the unpacked daemon entry is
|
||||
// missing, so the fixture must carry one like a real package layout.
|
||||
const unpackedMainDir = join(resourcesDir, 'app.asar.unpacked', 'out', 'main')
|
||||
await mkdir(unpackedMainDir, { recursive: true })
|
||||
await writeFile(
|
||||
join(unpackedMainDir, 'daemon-entry.js'),
|
||||
'console.error("Usage: daemon-entry <socket>"); process.exit(1)\n',
|
||||
'utf8'
|
||||
)
|
||||
await writeFile(launcherPath, '#!/usr/bin/env bash\n', { encoding: 'utf8', mode: 0o644 })
|
||||
|
||||
await electronBuilderConfig.afterPack({
|
||||
|
||||
@@ -2,6 +2,24 @@ const { existsSync } = require('node:fs')
|
||||
const { spawnSync } = require('node:child_process')
|
||||
const { join } = require('node:path')
|
||||
|
||||
// Why: `asarUnpack` in config/electron-builder.config.cjs lists
|
||||
// out/main/daemon-entry.js on every platform, and the packaged daemon fork
|
||||
// (src/main/daemon/daemon-init.ts) resolves exactly this unpacked path. A
|
||||
// missing entry means the package layout regressed, so the check throws
|
||||
// instead of skipping — a silent skip false-passed exactly the layout bug
|
||||
// this gate exists to catch.
|
||||
function assertPackagedDaemonEntryExists(resourcesDir) {
|
||||
const entryPath = join(resourcesDir, 'app.asar.unpacked', 'out', 'main', 'daemon-entry.js')
|
||||
if (!existsSync(entryPath)) {
|
||||
throw new Error(
|
||||
`[verify-packaged-daemon-entry] missing unpacked daemon entry at ${entryPath} — ` +
|
||||
`asarUnpack expects out/main/daemon-entry.js on every platform, so the packaged ` +
|
||||
`daemon cannot be forked from this layout`
|
||||
)
|
||||
}
|
||||
return entryPath
|
||||
}
|
||||
|
||||
// Why: v1.4.129-rc.1 shipped a terminal daemon that could not load (an electron
|
||||
// `require` leaked into its bundle) while every build check passed. This boots
|
||||
// the PACKAGED daemon-entry under plain Node against the asar-unpacked layout,
|
||||
@@ -14,13 +32,7 @@ const { join } = require('node:path')
|
||||
// <appOutDir>/resources elsewhere). execPath defaults to the packaging Node.
|
||||
function verifyPackagedDaemonEntryBoots(resourcesDir, options = {}) {
|
||||
const execPath = options.execPath || process.execPath
|
||||
const entryPath = join(resourcesDir, 'app.asar.unpacked', 'out', 'main', 'daemon-entry.js')
|
||||
if (!existsSync(entryPath)) {
|
||||
// Why: some targets/layouts do not unpack here; skip rather than fail so
|
||||
// the hook stays safe across platforms it has not verified.
|
||||
console.log(`[verify-packaged-daemon-entry] skipped — no unpacked entry at ${entryPath}`)
|
||||
return
|
||||
}
|
||||
const entryPath = assertPackagedDaemonEntryExists(resourcesDir)
|
||||
|
||||
const result = spawnSync(execPath, [entryPath], { encoding: 'utf8', timeout: 10_000 })
|
||||
if (result.error) {
|
||||
@@ -43,4 +55,4 @@ function verifyPackagedDaemonEntryBoots(resourcesDir, options = {}) {
|
||||
console.log('[verify-packaged-daemon-entry] OK — packaged daemon-entry loads under plain Node')
|
||||
}
|
||||
|
||||
module.exports = { verifyPackagedDaemonEntryBoots }
|
||||
module.exports = { assertPackagedDaemonEntryExists, verifyPackagedDaemonEntryBoots }
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
|
||||
import { createRequire } from 'node:module'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
|
||||
|
||||
const require = createRequire(import.meta.url)
|
||||
const {
|
||||
assertPackagedDaemonEntryExists,
|
||||
verifyPackagedDaemonEntryBoots
|
||||
} = require('./verify-packaged-daemon-entry.cjs')
|
||||
|
||||
describe('verify-packaged-daemon-entry', () => {
|
||||
let resourcesDir
|
||||
|
||||
beforeEach(() => {
|
||||
resourcesDir = mkdtempSync(join(tmpdir(), 'orca-daemon-entry-verify-'))
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
rmSync(resourcesDir, { recursive: true, force: true })
|
||||
})
|
||||
|
||||
function writePackagedEntry(source) {
|
||||
const entryDir = join(resourcesDir, 'app.asar.unpacked', 'out', 'main')
|
||||
mkdirSync(entryDir, { recursive: true })
|
||||
writeFileSync(join(entryDir, 'daemon-entry.js'), source)
|
||||
}
|
||||
|
||||
// Why: a silent skip on a missing entry false-passed exactly the packaged
|
||||
// layout regression this gate exists to catch (rc.1 daemon-load incident).
|
||||
it('throws when the unpacked daemon entry is missing', () => {
|
||||
expect(() => assertPackagedDaemonEntryExists(resourcesDir)).toThrow(
|
||||
/missing unpacked daemon entry/
|
||||
)
|
||||
expect(() => verifyPackagedDaemonEntryBoots(resourcesDir)).toThrow(
|
||||
/missing unpacked daemon entry/
|
||||
)
|
||||
})
|
||||
|
||||
it('passes when the packaged entry loads and reaches argv parsing', () => {
|
||||
writePackagedEntry('console.error("Usage: daemon-entry <socket>"); process.exit(1)\n')
|
||||
expect(() => verifyPackagedDaemonEntryBoots(resourcesDir)).not.toThrow()
|
||||
})
|
||||
|
||||
it('fails when the packaged entry cannot resolve its module graph', () => {
|
||||
writePackagedEntry('require("orca-module-that-does-not-exist")\n')
|
||||
expect(() => verifyPackagedDaemonEntryBoots(resourcesDir)).toThrow(
|
||||
/failed to load under plain Node/
|
||||
)
|
||||
})
|
||||
|
||||
it('fails when the packaged entry never reaches argv parsing', () => {
|
||||
writePackagedEntry('process.exit(0)\n')
|
||||
expect(() => verifyPackagedDaemonEntryBoots(resourcesDir)).toThrow(/did not reach argv parsing/)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user