























@@ -24,6 +24,7 @@ export const ARTIFACT_TARBALL_SCAN_MAX_ENTRIES = 10_000;
2424const COMMAND_STDOUT_CAPTURE_MAX_CHARS = 8 * 1024 * 1024;
2525const COMMAND_STDERR_CAPTURE_MAX_CHARS = 128 * 1024;
2626const COMMAND_TIMEOUT_KILL_AFTER_MS = 5_000;
27+const COMMAND_PROCESS_TREE_EXIT_POLL_MS = 50;
2728const ACTIVE_CHILD_KILLERS = new Set();
2829const SIGNAL_EXIT_CODES = {
2930SIGHUP: 129,
@@ -195,7 +196,7 @@ function run(command, args, options = {}) {
195196});
196197let timedOut = false;
197198let killTimer;
198-let timeoutReject;
199+let forceKillAt;
199200const killChild = (signal) => {
200201if (useProcessGroup && child.pid) {
201202try {
@@ -209,11 +210,13 @@ function run(command, args, options = {}) {
209210};
210211const terminateChild = () => {
211212killChild("SIGTERM");
213+const killAfterMs = options.killAfterMs ?? COMMAND_TIMEOUT_KILL_AFTER_MS;
214+forceKillAt = Date.now() + killAfterMs;
212215killTimer = setTimeout(() => {
213216killTimer = undefined;
217+forceKillAt = undefined;
214218killChild("SIGKILL");
215-timeoutReject?.();
216-}, options.killAfterMs ?? COMMAND_TIMEOUT_KILL_AFTER_MS);
219+}, killAfterMs);
217220};
218221const timeout =
219222options.timeoutMs === undefined
@@ -244,6 +247,7 @@ function run(command, args, options = {}) {
244247}
245248if (killTimer && !timedOut) {
246249clearTimeout(killTimer);
250+forceKillAt = undefined;
247251}
248252ACTIVE_CHILD_KILLERS.delete(killChild);
249253if (
@@ -261,7 +265,13 @@ function run(command, args, options = {}) {
261265`${command} ${args.join(" ")} timed out after ${options.timeoutMs}ms`,
262266);
263267if (killTimer) {
264-timeoutReject = () => reject(timeoutError);
268+void finishTimedOutProcessTree(child, {
269+ forceKillAt,
270+ killChild,
271+ killTimer,
272+killAfterMs: options.killAfterMs ?? COMMAND_TIMEOUT_KILL_AFTER_MS,
273+ useProcessGroup,
274+}).then(() => reject(timeoutError), reject);
265275return;
266276}
267277reject(timeoutError);
@@ -286,6 +296,54 @@ function run(command, args, options = {}) {
286296});
287297}
288298299+async function finishTimedOutProcessTree(
300+child,
301+{ forceKillAt, killAfterMs, killChild, killTimer, useProcessGroup },
302+) {
303+const graceRemainingMs =
304+forceKillAt === undefined ? killAfterMs : Math.max(0, forceKillAt - Date.now());
305+if (graceRemainingMs > 0) {
306+await waitForProcessTreeExit(child, graceRemainingMs, useProcessGroup);
307+}
308+clearTimeout(killTimer);
309+if (processTreeIsAlive(child, useProcessGroup)) {
310+killChild("SIGKILL");
311+await waitForProcessTreeExit(child, killAfterMs, useProcessGroup);
312+}
313+}
314+315+function childHasExited(child) {
316+return child.exitCode !== null || child.signalCode !== null;
317+}
318+319+function processTreeIsAlive(child, useProcessGroup) {
320+if (!child || typeof child.pid !== "number") {
321+return false;
322+}
323+if (!useProcessGroup) {
324+return !childHasExited(child);
325+}
326+try {
327+process.kill(-child.pid, 0);
328+return true;
329+} catch (error) {
330+return error?.code === "EPERM";
331+}
332+}
333+334+async function waitForProcessTreeExit(child, timeoutMs, useProcessGroup) {
335+const deadlineAt = Date.now() + timeoutMs;
336+while (Date.now() < deadlineAt) {
337+if (!processTreeIsAlive(child, useProcessGroup)) {
338+return true;
339+}
340+await new Promise((resolvePoll) => {
341+setTimeout(resolvePoll, COMMAND_PROCESS_TREE_EXIT_POLL_MS);
342+});
343+}
344+return !processTreeIsAlive(child, useProcessGroup);
345+}
346+289347function appendBoundedCommandOutput(buffer, chunk, maxChars) {
290348const nextText = buffer.text + String(chunk);
291349if (nextText.length <= maxChars) {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。