mirror of
https://github.com/Pandipipas/scoreko-electron-dev.git
synced 2026-06-06 05:32:06 +00:00
Add NodeCG preflight step and sanity script
This commit is contained in:
@@ -30,6 +30,7 @@ Desktop app (Electron + TypeScript) to run and package `scoreko-dev` (Electron h
|
|||||||
### Quality and diagnostics
|
### Quality and diagnostics
|
||||||
|
|
||||||
- `npm run test`: build and tests (`node:test`).
|
- `npm run test`: build and tests (`node:test`).
|
||||||
|
- `npm run sanity`: runs `typecheck`, `lint`, and `test` as a quick pre-release gate.
|
||||||
- `npm run doctor`: environment/configuration diagnostics.
|
- `npm run doctor`: environment/configuration diagnostics.
|
||||||
- `npm run lint`: lint with ESLint.
|
- `npm run lint`: lint with ESLint.
|
||||||
- `npm run lint:fix`: lint with auto-fix.
|
- `npm run lint:fix`: lint with auto-fix.
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
"rebuild:native": "node scripts/rebuild-nodecg-native.mjs",
|
"rebuild:native": "node scripts/rebuild-nodecg-native.mjs",
|
||||||
"rebuild:better-sqlite3": "electron-rebuild --version 40.6.1 --module-dir lib/scoreko-dev/workspaces/database-adapter-sqlite-legacy --only better-sqlite3 -f",
|
"rebuild:better-sqlite3": "electron-rebuild --version 40.6.1 --module-dir lib/scoreko-dev/workspaces/database-adapter-sqlite-legacy --only better-sqlite3 -f",
|
||||||
"test": "npm run build && node --test dist/tests/**/*.test.js",
|
"test": "npm run build && node --test dist/tests/**/*.test.js",
|
||||||
|
"sanity": "npm run typecheck && npm run lint && npm run test",
|
||||||
"doctor": "node scripts/doctor.mjs",
|
"doctor": "node scripts/doctor.mjs",
|
||||||
"lint": "eslint . --ext .ts,.js,.mjs",
|
"lint": "eslint . --ext .ts,.js,.mjs",
|
||||||
"lint:fix": "npm run lint -- --fix",
|
"lint:fix": "npm run lint -- --fix",
|
||||||
|
|||||||
@@ -56,6 +56,8 @@ function focusExistingWindow(): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function launchApplication(): Promise<void> {
|
async function launchApplication(): Promise<void> {
|
||||||
|
await nodecgManager.runPreflightChecks();
|
||||||
|
|
||||||
// We create both windows early so startup feels instant while NodeCG is booting in the background.
|
// We create both windows early so startup feels instant while NodeCG is booting in the background.
|
||||||
mainWindow = createMainWindow({ appConfig, rootPath, mainDashboardUrl });
|
mainWindow = createMainWindow({ appConfig, rootPath, mainDashboardUrl });
|
||||||
loadingWindow = createLoadingWindow({ appConfig, rootPath });
|
loadingWindow = createLoadingWindow({ appConfig, rootPath });
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ type NodecgProcessManagerDeps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type NodecgProcessManager = {
|
export type NodecgProcessManager = {
|
||||||
|
runPreflightChecks: () => Promise<void>;
|
||||||
startNodecgProcess: () => Promise<ChildProcess>;
|
startNodecgProcess: () => Promise<ChildProcess>;
|
||||||
waitForNodecgReady: (startTime: number) => Promise<void>;
|
waitForNodecgReady: (startTime: number) => Promise<void>;
|
||||||
stopNodecgProcessGracefully: () => Promise<void>;
|
stopNodecgProcessGracefully: () => Promise<void>;
|
||||||
@@ -51,7 +52,7 @@ export function createNodecgProcessManager({
|
|||||||
let lastExit: { code: number | null; signal: NodeJS.Signals | null } | null = null;
|
let lastExit: { code: number | null; signal: NodeJS.Signals | null } | null = null;
|
||||||
let lastStderrLine: string | null = null;
|
let lastStderrLine: string | null = null;
|
||||||
|
|
||||||
const startNodecgProcess = async (): Promise<ChildProcess> => {
|
const runPreflightChecks = async (): Promise<void> => {
|
||||||
validateNodecgInstall(
|
validateNodecgInstall(
|
||||||
nodecgRootPath,
|
nodecgRootPath,
|
||||||
resolvedDeps.platform,
|
resolvedDeps.platform,
|
||||||
@@ -66,6 +67,10 @@ export function createNodecgProcessManager({
|
|||||||
`Port ${appConfig.nodecgPort} is already in use. Stop the process using it or set NODECG_PORT before starting.`,
|
`Port ${appConfig.nodecgPort} is already in use. Stop the process using it or set NODECG_PORT before starting.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const startNodecgProcess = async (): Promise<ChildProcess> => {
|
||||||
|
await runPreflightChecks();
|
||||||
|
|
||||||
const command = resolvedDeps.platform === "win32" ? "npx.cmd" : "npx";
|
const command = resolvedDeps.platform === "win32" ? "npx.cmd" : "npx";
|
||||||
const child = resolvedDeps.spawnProcess(command, ["nodecg", "start"], {
|
const child = resolvedDeps.spawnProcess(command, ["nodecg", "start"], {
|
||||||
@@ -186,6 +191,7 @@ export function createNodecgProcessManager({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
runPreflightChecks,
|
||||||
startNodecgProcess,
|
startNodecgProcess,
|
||||||
waitForNodecgReady,
|
waitForNodecgReady,
|
||||||
stopNodecgProcessGracefully,
|
stopNodecgProcessGracefully,
|
||||||
|
|||||||
@@ -70,6 +70,32 @@ test("startNodeCG fails when there are no read/write permissions", async () => {
|
|||||||
}, /No read\/write permissions on scoreko app folder/);
|
}, /No read\/write permissions on scoreko app folder/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("runPreflightChecks validates install and port availability without starting NodeCG", async () => {
|
||||||
|
let spawnCalls = 0;
|
||||||
|
|
||||||
|
const manager = createNodecgProcessManager({
|
||||||
|
isDev: true,
|
||||||
|
nodecgRootPath: "/fake/scoreko-dev",
|
||||||
|
nodecgBaseUrl: "http://127.0.0.1:9090",
|
||||||
|
appConfig: getBaseConfig(),
|
||||||
|
log: () => undefined,
|
||||||
|
deps: {
|
||||||
|
pathExists: () => true,
|
||||||
|
hasReadWriteAccess: () => true,
|
||||||
|
probePortAvailable: async () => true,
|
||||||
|
spawnProcess: () => {
|
||||||
|
spawnCalls += 1;
|
||||||
|
throw new Error("should not spawn during preflight");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await assert.doesNotReject(async () => {
|
||||||
|
await manager.runPreflightChecks();
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(spawnCalls, 0);
|
||||||
|
});
|
||||||
test("waitForNodeCGReady resolves when endpoint returns 404", async () => {
|
test("waitForNodeCGReady resolves when endpoint returns 404", async () => {
|
||||||
const child = new MockChildProcess(4321);
|
const child = new MockChildProcess(4321);
|
||||||
const manager = createNodecgProcessManager({
|
const manager = createNodecgProcessManager({
|
||||||
|
|||||||
Reference in New Issue
Block a user