mirror of
https://github.com/nimbold/Firelink.git
synced 2026-08-18 15:23:13 +00:00
ci(release): harden packaged app builds
This commit is contained in:
Regular → Executable
+116
-27
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
import { spawn } from 'node:child_process';
|
||||
import { execFileSync, spawn } from 'node:child_process';
|
||||
import path from 'node:path';
|
||||
|
||||
function argValue(name) {
|
||||
@@ -12,52 +12,127 @@ if (!executableArg) {
|
||||
console.error('Pass --executable <path>.');
|
||||
process.exit(1);
|
||||
}
|
||||
const executable = path.resolve(executableArg);
|
||||
|
||||
const executable = path.resolve(executableArg);
|
||||
const assertNoVisibleChildWindows = process.argv.includes('--assert-no-visible-child-windows');
|
||||
const child = spawn(executable, [], {
|
||||
cwd: process.env.RUNNER_TEMP || process.env.TMPDIR || process.cwd(),
|
||||
detached: process.platform !== 'win32',
|
||||
env: {
|
||||
...process.env,
|
||||
env: {
|
||||
...process.env,
|
||||
FIRELINK_SMOKE_TEST: '1',
|
||||
WEBKIT_DISABLE_COMPOSITING_MODE: '1',
|
||||
GDK_BACKEND: 'x11'
|
||||
GDK_BACKEND: 'x11',
|
||||
},
|
||||
stdio: ['ignore', 'pipe', 'pipe']
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
});
|
||||
|
||||
let stderr = '';
|
||||
let spawnError = null;
|
||||
let readyPort = null;
|
||||
|
||||
child.on('error', error => {
|
||||
spawnError = error;
|
||||
});
|
||||
|
||||
child.on('exit', (code, signal) => {
|
||||
if (readyPort === null) {
|
||||
console.error(`Child exited prematurely with code ${code} signal ${signal}`);
|
||||
}
|
||||
});
|
||||
|
||||
child.stderr.on('data', data => {
|
||||
stderr += data.toString();
|
||||
});
|
||||
|
||||
let readyPort = null;
|
||||
for (let attempt = 0; attempt < 200 && readyPort === null; attempt += 1) {
|
||||
for (let port = 6412; port <= 6422; port += 1) {
|
||||
try {
|
||||
const response = await fetch(`http://127.0.0.1:${port}/ping`);
|
||||
if (response.headers.get('x-firelink-server') === '1') {
|
||||
readyPort = port;
|
||||
break;
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
if (readyPort === null) {
|
||||
await new Promise(resolve => setTimeout(resolve, 250));
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
async function findReadyPort() {
|
||||
for (let attempt = 0; attempt < 200 && readyPort === null; attempt += 1) {
|
||||
if (spawnError) {
|
||||
break;
|
||||
}
|
||||
|
||||
for (let port = 6412; port <= 6422; port += 1) {
|
||||
try {
|
||||
const response = await fetch(`http://127.0.0.1:${port}/ping`);
|
||||
if (response.headers.get('x-firelink-server') === '1') {
|
||||
readyPort = port;
|
||||
break;
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
if (readyPort === null) {
|
||||
await sleep(250);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
spawn('taskkill', ['/pid', String(child.pid), '/t', '/f'], { stdio: 'ignore' });
|
||||
} else {
|
||||
function assertNoVisibleWindows(rootPid) {
|
||||
if (process.platform !== 'win32') {
|
||||
return;
|
||||
}
|
||||
|
||||
const script = `
|
||||
$root = ${rootPid}
|
||||
$all = Get-CimInstance Win32_Process
|
||||
$pending = @($root)
|
||||
$descendants = @()
|
||||
while ($pending.Count -gt 0) {
|
||||
$parent = $pending[0]
|
||||
if ($pending.Count -eq 1) {
|
||||
$pending = @()
|
||||
} else {
|
||||
$pending = $pending[1..($pending.Count - 1)]
|
||||
}
|
||||
|
||||
$children = @($all | Where-Object { $_.ParentProcessId -eq $parent })
|
||||
foreach ($child in $children) {
|
||||
$descendants += $child.ProcessId
|
||||
$pending += $child.ProcessId
|
||||
}
|
||||
}
|
||||
|
||||
$visible = foreach ($pid in $descendants) {
|
||||
$process = Get-Process -Id $pid -ErrorAction SilentlyContinue
|
||||
if ($process -and $process.MainWindowHandle -ne 0) {
|
||||
"$($process.ProcessName)($pid)"
|
||||
}
|
||||
}
|
||||
|
||||
if ($visible.Count -gt 0) {
|
||||
Write-Error "Visible child process windows detected: $($visible -join ', ')"
|
||||
exit 1
|
||||
}
|
||||
`;
|
||||
|
||||
try {
|
||||
execFileSync('powershell', ['-NoProfile', '-Command', script], {
|
||||
stdio: 'pipe',
|
||||
windowsHide: true,
|
||||
});
|
||||
} catch (error) {
|
||||
const detail = [error.stdout?.toString(), error.stderr?.toString()].filter(Boolean).join('\n').trim();
|
||||
throw new Error(detail || 'Visible child process window check failed.');
|
||||
}
|
||||
}
|
||||
|
||||
function terminateChild() {
|
||||
if (!child.pid) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
spawn('taskkill', ['/pid', String(child.pid), '/t', '/f'], {
|
||||
stdio: 'ignore',
|
||||
windowsHide: true,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
process.kill(-child.pid, 'SIGTERM');
|
||||
} catch {
|
||||
@@ -65,9 +140,23 @@ if (process.platform === 'win32') {
|
||||
}
|
||||
}
|
||||
|
||||
if (readyPort === null) {
|
||||
const detail = spawnError?.message || stderr.slice(-1000);
|
||||
console.error(`Packaged Firelink did not expose extension server. ${detail}`);
|
||||
process.exit(1);
|
||||
await findReadyPort();
|
||||
|
||||
try {
|
||||
if (readyPort === null) {
|
||||
if (spawnError) {
|
||||
console.error(`Packaged Firelink failed to start: ${spawnError.message}`);
|
||||
} else {
|
||||
console.error(`Packaged Firelink did not expose extension ping endpoint. Stderr:\n${stderr.slice(-1000)}`);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (assertNoVisibleChildWindows) {
|
||||
assertNoVisibleWindows(child.pid);
|
||||
}
|
||||
|
||||
console.log(`Packaged Firelink smoke passed on 127.0.0.1:${readyPort}`);
|
||||
} finally {
|
||||
terminateChild();
|
||||
}
|
||||
console.log(`Packaged Firelink smoke passed on 127.0.0.1:${readyPort}`);
|
||||
|
||||
Reference in New Issue
Block a user