fix(process): normalize macOS launcher identity
This commit is contained in:
parent
13f3372a8b
commit
9c3aec8131
|
|
@ -0,0 +1,24 @@
|
|||
import { basename } from "node:path";
|
||||
|
||||
/** Match process-table command text while handling macOS executable-name normalization. */
|
||||
export const matchesOwnedProcessCommand = (
|
||||
actual: string,
|
||||
expected: string,
|
||||
platform: NodeJS.Platform = process.platform,
|
||||
): boolean => {
|
||||
const actualCommand = actual.trim();
|
||||
const expectedCommand = expected.trim();
|
||||
if (
|
||||
actualCommand === expectedCommand ||
|
||||
actualCommand.startsWith(`${expectedCommand} `)
|
||||
)
|
||||
return true;
|
||||
if (platform !== "darwin") return false;
|
||||
const actualExecutable = actualCommand.split(/\s+/u, 1)[0];
|
||||
const expectedExecutable = expectedCommand.split(/\s+/u, 1)[0];
|
||||
return (
|
||||
actualExecutable !== undefined &&
|
||||
expectedExecutable !== undefined &&
|
||||
basename(actualExecutable) === basename(expectedExecutable)
|
||||
);
|
||||
};
|
||||
|
|
@ -2,6 +2,8 @@ import { execFile } from "node:child_process";
|
|||
import { readFile } from "node:fs/promises";
|
||||
import { promisify } from "node:util";
|
||||
|
||||
import { matchesOwnedProcessCommand } from "./ProcessCommandIdentity.js";
|
||||
|
||||
const execFileAsync = promisify(execFile);
|
||||
|
||||
/** Identity proof required before REA may signal an owned process group. */
|
||||
|
|
@ -318,7 +320,7 @@ const launcherIdentityFailure = (
|
|||
return "owned launcher parent identity did not match";
|
||||
if (
|
||||
ownership.expectedCommand !== undefined &&
|
||||
!commandMatches(launcher.command, ownership.expectedCommand)
|
||||
!matchesOwnedProcessCommand(launcher.command, ownership.expectedCommand)
|
||||
)
|
||||
return "owned launcher command identity did not match";
|
||||
return null;
|
||||
|
|
@ -471,7 +473,7 @@ export const observeOwnedProcessLineage = async (
|
|||
);
|
||||
if (
|
||||
ownership.expectedCommand !== undefined &&
|
||||
!commandMatches(launcher.command, ownership.expectedCommand)
|
||||
!matchesOwnedProcessCommand(launcher.command, ownership.expectedCommand)
|
||||
)
|
||||
return unavailableLineage(
|
||||
ownership,
|
||||
|
|
@ -553,10 +555,3 @@ const liveProcesses = (
|
|||
members: readonly ProcessTableEntry[],
|
||||
): readonly ProcessTableEntry[] =>
|
||||
members.filter(({ state }) => !state.startsWith("Z"));
|
||||
|
||||
const commandMatches = (actual: string, expected: string): boolean => {
|
||||
return (
|
||||
actual.trim() === expected.trim() ||
|
||||
actual.trim().startsWith(`${expected.trim()} `)
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import {
|
|||
type ProcessOwnershipHost,
|
||||
type WindowsProcessTreeHost,
|
||||
} from "../src/process/ProcessOwnership.js";
|
||||
import { matchesOwnedProcessCommand } from "../src/process/ProcessCommandIdentity.js";
|
||||
|
||||
const ownership = {
|
||||
runId: "run-token",
|
||||
|
|
@ -61,6 +62,30 @@ describe("owned process-group cleanup", () => {
|
|||
).toEqual({ REA_PROCESS_RUN_ID: "owned", EMPTY: "" });
|
||||
});
|
||||
|
||||
it("matches macOS executable names after process-table normalization", () => {
|
||||
expect(
|
||||
matchesOwnedProcessCommand(
|
||||
"node /tmp/fake-launcher.mjs",
|
||||
"/opt/node/bin/node",
|
||||
"darwin",
|
||||
),
|
||||
).toBe(true);
|
||||
expect(
|
||||
matchesOwnedProcessCommand(
|
||||
"node /tmp/fake-launcher.mjs",
|
||||
"/opt/python/bin/python3",
|
||||
"darwin",
|
||||
),
|
||||
).toBe(false);
|
||||
expect(
|
||||
matchesOwnedProcessCommand(
|
||||
"node /tmp/fake-launcher.mjs",
|
||||
"/opt/node/bin/node",
|
||||
"linux",
|
||||
),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("signals only a group whose every member carries the run token", async () => {
|
||||
const { adapter, signalGroup } = host({
|
||||
100: { REA_PROCESS_RUN_ID: "run-token" },
|
||||
|
|
|
|||
Loading…
Reference in New Issue