mirror of
https://github.com/moltbot/moltbot.git
synced 2026-05-13 15:47:28 +00:00
fix(plugins): abort sibling boundary prep steps
This commit is contained in:
@@ -33,11 +33,19 @@ export function createPrefixedOutputWriter(label, target) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function runNodeStep(label, args, timeoutMs) {
|
function abortSiblingSteps(abortController) {
|
||||||
|
if (abortController && !abortController.signal.aborted) {
|
||||||
|
abortController.abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function runNodeStep(label, args, timeoutMs, params = {}) {
|
||||||
|
const abortController = params.abortController;
|
||||||
return new Promise((resolvePromise, rejectPromise) => {
|
return new Promise((resolvePromise, rejectPromise) => {
|
||||||
const child = spawn(process.execPath, args, {
|
const child = spawn(process.execPath, args, {
|
||||||
cwd: repoRoot,
|
cwd: repoRoot,
|
||||||
env: process.env,
|
env: process.env,
|
||||||
|
signal: abortController?.signal,
|
||||||
stdio: ["ignore", "pipe", "pipe"],
|
stdio: ["ignore", "pipe", "pipe"],
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -52,6 +60,7 @@ function runNodeStep(label, args, timeoutMs) {
|
|||||||
settled = true;
|
settled = true;
|
||||||
stdoutWriter.flush();
|
stdoutWriter.flush();
|
||||||
stderrWriter.flush();
|
stderrWriter.flush();
|
||||||
|
abortSiblingSteps(abortController);
|
||||||
rejectPromise(new Error(`${label} timed out after ${timeoutMs}ms`));
|
rejectPromise(new Error(`${label} timed out after ${timeoutMs}ms`));
|
||||||
}, timeoutMs);
|
}, timeoutMs);
|
||||||
|
|
||||||
@@ -71,6 +80,11 @@ function runNodeStep(label, args, timeoutMs) {
|
|||||||
settled = true;
|
settled = true;
|
||||||
stdoutWriter.flush();
|
stdoutWriter.flush();
|
||||||
stderrWriter.flush();
|
stderrWriter.flush();
|
||||||
|
if (error.name === "AbortError" && abortController?.signal.aborted) {
|
||||||
|
rejectPromise(new Error(`${label} canceled after sibling failure`));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
abortSiblingSteps(abortController);
|
||||||
rejectPromise(new Error(`${label} failed to start: ${error.message}`));
|
rejectPromise(new Error(`${label} failed to start: ${error.message}`));
|
||||||
});
|
});
|
||||||
child.on("close", (code) => {
|
child.on("close", (code) => {
|
||||||
@@ -85,24 +99,36 @@ function runNodeStep(label, args, timeoutMs) {
|
|||||||
resolvePromise();
|
resolvePromise();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
abortSiblingSteps(abortController);
|
||||||
rejectPromise(new Error(`${label} failed with exit code ${code ?? 1}`));
|
rejectPromise(new Error(`${label} failed with exit code ${code ?? 1}`));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function runNodeStepsInParallel(steps) {
|
||||||
|
const abortController = new AbortController();
|
||||||
|
const results = await Promise.allSettled(
|
||||||
|
steps.map((step) => runNodeStep(step.label, step.args, step.timeoutMs, { abortController })),
|
||||||
|
);
|
||||||
|
const firstFailure = results.find((result) => result.status === "rejected");
|
||||||
|
if (firstFailure) {
|
||||||
|
throw firstFailure.reason;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function main() {
|
export async function main() {
|
||||||
try {
|
try {
|
||||||
await Promise.all([
|
await runNodeStepsInParallel([
|
||||||
runNodeStep(
|
{
|
||||||
"plugin-sdk boundary dts",
|
label: "plugin-sdk boundary dts",
|
||||||
[tscBin, "-p", "tsconfig.plugin-sdk.dts.json"],
|
args: [tscBin, "-p", "tsconfig.plugin-sdk.dts.json"],
|
||||||
300_000,
|
timeoutMs: 300_000,
|
||||||
),
|
},
|
||||||
runNodeStep(
|
{
|
||||||
"plugin-sdk package boundary dts",
|
label: "plugin-sdk package boundary dts",
|
||||||
[tscBin, "-p", "packages/plugin-sdk/tsconfig.json"],
|
args: [tscBin, "-p", "packages/plugin-sdk/tsconfig.json"],
|
||||||
300_000,
|
timeoutMs: 300_000,
|
||||||
),
|
},
|
||||||
]);
|
]);
|
||||||
await runNodeStep(
|
await runNodeStep(
|
||||||
"plugin-sdk boundary root shims",
|
"plugin-sdk boundary root shims",
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { createPrefixedOutputWriter } from "../../scripts/prepare-extension-package-boundary-artifacts.mjs";
|
import {
|
||||||
|
createPrefixedOutputWriter,
|
||||||
|
runNodeStepsInParallel,
|
||||||
|
} from "../../scripts/prepare-extension-package-boundary-artifacts.mjs";
|
||||||
|
|
||||||
describe("prepare-extension-package-boundary-artifacts", () => {
|
describe("prepare-extension-package-boundary-artifacts", () => {
|
||||||
it("prefixes each completed line and flushes the trailing partial line", () => {
|
it("prefixes each completed line and flushes the trailing partial line", () => {
|
||||||
@@ -16,4 +19,25 @@ describe("prepare-extension-package-boundary-artifacts", () => {
|
|||||||
|
|
||||||
expect(output).toBe("[boundary] first line\n[boundary] second line\n[boundary] third");
|
expect(output).toBe("[boundary] first line\n[boundary] second line\n[boundary] third");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("aborts sibling steps after the first failure", async () => {
|
||||||
|
const startedAt = Date.now();
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
runNodeStepsInParallel([
|
||||||
|
{
|
||||||
|
label: "fail-fast",
|
||||||
|
args: ["--eval", "setTimeout(() => process.exit(2), 10)"],
|
||||||
|
timeoutMs: 5_000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "slow-step",
|
||||||
|
args: ["--eval", "setTimeout(() => {}, 10_000)"],
|
||||||
|
timeoutMs: 5_000,
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
).rejects.toThrow("fail-fast failed with exit code 2");
|
||||||
|
|
||||||
|
expect(Date.now() - startedAt).toBeLessThan(2_000);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user