mirror of
https://github.com/phantomptr/ps5upload.git
synced 2026-10-19 02:20:02 -05:00
fix(scripts): make run-client stops only the engine on the app's port
kill-stale-client did `pkill -f ps5upload-engine`, which SIGTERMed every engine on the machine — a standalone one on another port died 70 minutes into a package build. It now stops only an engine listening on 19113 (an engine whose app is gone exits by itself). The decision is a small pure module with node:test cases, which scripts:check now runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
ce76e9dec5
commit
0bba2cbab8
4 files changed
+73
-5
No files matched your search
@@ -149,6 +149,9 @@ const psFiles = files.filter((f) => /\.ps1$/i.test(f));
|
||||
let ok = true;
|
||||
ok = checkSdkPin() && ok;
|
||||
for (const f of nodeFiles) ok = run(path.relative(repoRoot, f), "node", ["--check", f]) && ok;
|
||||
// Scripts with logic worth pinning carry a node:test file beside them.
|
||||
const nodeTests = nodeFiles.filter((f) => f.endsWith(".test.mjs"));
|
||||
if (nodeTests.length) ok = run("node --test", "node", ["--test", ...nodeTests]) && ok;
|
||||
if (hasCommand("bash")) {
|
||||
for (const f of shellFiles) ok = run(path.relative(repoRoot, f), "bash", ["-n", f]) && ok;
|
||||
} else if (shellFiles.length > 0) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
// Best-effort cleanup of stale ps5upload-desktop / ps5upload-engine processes
|
||||
// plus any Vite dev server holding :1420. Invoked as a `make run-client`
|
||||
// Best-effort cleanup of a stale ps5upload-desktop, an engine still holding the
|
||||
// app's port (19113), and a Vite dev server holding :1420. Engines elsewhere are
|
||||
// left alone (see stale-client-lib.mjs). Invoked as a `make run-client`
|
||||
// dependency so a previously mis-terminated session doesn't fail the next
|
||||
// launch with "Port 1420 already in use" or two engines fighting for ports.
|
||||
//
|
||||
@@ -15,6 +16,8 @@
|
||||
|
||||
import { execSync } from 'node:child_process';
|
||||
|
||||
import { APP_ENGINE_PORT, staleEnginePids } from './stale-client-lib.mjs';
|
||||
|
||||
const isWin = process.platform === 'win32';
|
||||
const PORT = 1420;
|
||||
|
||||
@@ -26,12 +29,12 @@ function silent(cmd) {
|
||||
try { execSync(cmd, { stdio: 'ignore' }); } catch { /* expected: no match */ }
|
||||
}
|
||||
|
||||
// The dev app itself. Its engine exits with it (the engine's parent watch); an
|
||||
// engine is NOT killed by name here — see stale-client-lib.mjs.
|
||||
if (isWin) {
|
||||
silent('taskkill /IM ps5upload-desktop.exe /F');
|
||||
silent('taskkill /IM ps5upload-engine.exe /F');
|
||||
} else {
|
||||
silent("pkill -f '[p]s5upload-desktop'");
|
||||
silent("pkill -f '[p]s5upload-engine'");
|
||||
}
|
||||
|
||||
function pidsOnPort(port) {
|
||||
@@ -46,7 +49,7 @@ function pidsOnPort(port) {
|
||||
}
|
||||
return [...pids];
|
||||
}
|
||||
const out = run(`lsof -ti :${port}`);
|
||||
const out = run(`lsof -ti tcp:${port} -sTCP:LISTEN`);
|
||||
return out.trim().split(/\s+/).filter(Boolean);
|
||||
} catch {
|
||||
return [];
|
||||
@@ -68,6 +71,26 @@ function looksLikeOurVite(pid) {
|
||||
}
|
||||
}
|
||||
|
||||
function commandOf(pid) {
|
||||
try {
|
||||
if (isWin) {
|
||||
return run(
|
||||
`powershell -NoProfile -Command "(Get-CimInstance Win32_Process -Filter 'ProcessId=${pid}').CommandLine"`,
|
||||
).trim();
|
||||
}
|
||||
return run(`ps -o command= -p ${pid}`).trim();
|
||||
} catch {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
// Only an engine still holding the app's port (an orphan the app can't bind past).
|
||||
const listeners = pidsOnPort(APP_ENGINE_PORT).map((pid) => ({ pid, command: commandOf(pid) }));
|
||||
for (const pid of staleEnginePids(listeners)) {
|
||||
silent(isWin ? `taskkill /PID ${pid} /F` : `kill ${pid}`);
|
||||
console.log(`✓ stopped the stale engine on :${APP_ENGINE_PORT} (pid ${pid})`);
|
||||
}
|
||||
|
||||
for (const pid of pidsOnPort(PORT)) {
|
||||
if (looksLikeOurVite(pid)) {
|
||||
silent(isWin ? `taskkill /PID ${pid} /F` : `kill -9 ${pid}`);
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
// What `make run-client` may clean up before starting the dev app (see kill-stale-client.mjs).
|
||||
//
|
||||
// Only an engine holding the desktop app's own port is stale. A blanket
|
||||
// `pkill -f ps5upload-engine` also killed engines that had nothing to do with the
|
||||
// dev app — a standalone one on another port running a 70-minute conversion
|
||||
// died mid-build. An engine whose app is gone exits by itself (its parent watch),
|
||||
// so the port holder is the only one that can be in the way.
|
||||
|
||||
/** The engine's port the desktop app binds first. */
|
||||
export const APP_ENGINE_PORT = 19113;
|
||||
|
||||
/** The executable is the engine itself, not a command that merely mentions it. */
|
||||
function isEngine(command) {
|
||||
const exe = command.trim().split(/\s+(?=-)/)[0];
|
||||
return /(^|[\\/])ps5upload-engine(\.exe)?$/i.test(exe);
|
||||
}
|
||||
|
||||
/** Of the processes listening on the app's port, the pids that are a ps5upload engine. */
|
||||
export function staleEnginePids(listeners) {
|
||||
return listeners.filter((l) => isEngine(l.command)).map((l) => l.pid);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { test } from "node:test";
|
||||
|
||||
import { staleEnginePids } from "./stale-client-lib.mjs";
|
||||
|
||||
test("only the engine holding the app's port is stale", () => {
|
||||
const listeners = [
|
||||
{ pid: 10, command: "/Users/me/Library/Application Support/com.phantomptr.ps5upload/engine/ps5upload-engine" },
|
||||
{ pid: 11, command: "/usr/bin/python3 -m http.server 19113" },
|
||||
];
|
||||
assert.deepEqual(staleEnginePids(listeners), [10]);
|
||||
});
|
||||
|
||||
test("an engine elsewhere — a conversion on another port — is never in the list to kill", () => {
|
||||
// The caller passes only what listens on the app's port; nothing else can come back.
|
||||
assert.deepEqual(staleEnginePids([]), []);
|
||||
});
|
||||
|
||||
test("a name that merely contains the words is not the engine", () => {
|
||||
assert.deepEqual(staleEnginePids([{ pid: 12, command: "vim notes-about-ps5upload-engine.txt" }]), []);
|
||||
});
|
||||
Reference in new issue
Block a user