Files
nexu-io-open-design/e2e/lib/tools-dev/cli.ts
PerishFire 891981d460 [codex] Optimize CI runtime topology (#4450)
* Optimize e2e tools-dev runtime parallelism

* Remove visual networkidle wait

* Optimize e2e vitest parallelism

* Optimize Nix flake caching

* Test CI on Blacksmith runners

* Allow parallel manual CI runs

* Tier CI runner sizes

* Temporarily narrow CI debug scope

* Instrument watcher CI timeouts

* Instrument watcher event diagnostics

* Avoid default polling in watcher tests

* Skip runtime trace during watcher debug

* Probe ARM runner tiers for CI

* Focus CI runner probe on x64 browser

* Probe browser workers by runner size

* Probe Playwright file parallelism

* Probe Playwright worker scaling on 8v

* Reshape CI topology

* Fix split E2E Vitest CI lane

* Simplify daemon CI topology

* Optimize Windows payload CI setup

* Revert "Optimize Windows payload CI setup"

This reverts commit 5cbc48c0af.

* Cache better-sqlite3 Nix binding separately

* Revert "Cache better-sqlite3 Nix binding separately"

This reverts commit 0384e3787e.

* Remove unused Nix cache setup from CI

* Use Blacksmith ARM for lightweight CI jobs
2026-06-17 12:53:06 +08:00

63 lines
2.2 KiB
TypeScript

import { execFile } from 'node:child_process';
import { extname } from 'node:path';
import { promisify } from 'node:util';
import type { ToolsDevSuiteSpec } from './types.ts';
const execFileAsync = promisify(execFile);
const pnpmCommand = process.env.OD_E2E_PNPM_COMMAND ?? 'pnpm';
const pnpmExecPath = process.env.npm_execpath;
const nodeLoadablePackageManagerExtensions = new Set(['.js', '.cjs', '.mjs']);
export async function runToolsDevJson<T>(
workspaceRoot: string,
suite: ToolsDevSuiteSpec,
args: string[],
extraEnv: Record<string, string | undefined> = {},
): Promise<T> {
const useNpmExecPathWithNode = process.env.OD_E2E_PNPM_COMMAND == null
&& pnpmExecPath != null
&& nodeLoadablePackageManagerExtensions.has(extname(pnpmExecPath).toLowerCase());
const command = useNpmExecPathWithNode
? process.execPath
: (process.env.OD_E2E_PNPM_COMMAND == null && pnpmExecPath ? pnpmExecPath : pnpmCommand);
const commandArgs = useNpmExecPathWithNode
? [pnpmExecPath, 'tools-dev', ...args]
: ['tools-dev', ...args];
const { stdout } = await execFileAsync(command, commandArgs, {
cwd: workspaceRoot,
env: {
...process.env,
...extraEnv,
CODEX_HOME: suite.codexHomeDir,
OD_DATA_DIR: suite.dataDir,
OD_MEDIA_CONFIG_DIR: suite.dataDir,
},
maxBuffer: 20 * 1024 * 1024,
shell: process.platform === 'win32' && command !== process.execPath,
});
return parseJsonOutput<T>(stdout);
}
export function isToolsDevPortConflict(error: unknown): boolean {
const text = error instanceof Error
? `${error.message}\n${error.stack ?? ''}`
: String(error);
return text.includes('EADDRINUSE') ||
(text.includes('is already running in namespace') && text.includes('stop it or choose another namespace'));
}
function parseJsonOutput<T>(stdout: string): T {
const trimmed = stdout.trim();
if (trimmed.startsWith('{') || trimmed.startsWith('[')) {
return JSON.parse(trimmed) as T;
}
const objectStart = stdout.lastIndexOf('\n{');
const arrayStart = stdout.lastIndexOf('\n[');
const jsonStart = Math.max(objectStart, arrayStart);
if (jsonStart < 0) {
throw new Error(`Expected JSON output from tools-dev, got: ${stdout}`);
}
return JSON.parse(stdout.slice(jsonStart + 1)) as T;
}