diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5dba7004dae2..eb487bd2cb6c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -842,6 +842,9 @@ importers: tar-stream: specifier: 3.1.7 version: 3.1.7 + tree-kill: + specifier: 1.2.2 + version: 1.2.2 packages: @@ -7759,6 +7762,10 @@ packages: peerDependencies: tslib: '2' + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + ts-api-utils@2.4.0: resolution: {integrity: sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==} engines: {node: '>=18.12'} @@ -16405,6 +16412,8 @@ snapshots: dependencies: tslib: 2.8.1 + tree-kill@1.2.2: {} + ts-api-utils@2.4.0(typescript@5.9.3): dependencies: typescript: 5.9.3 diff --git a/tests/e2e/utils/BUILD.bazel b/tests/e2e/utils/BUILD.bazel index dbbda05702cd..d4493fb14492 100644 --- a/tests/e2e/utils/BUILD.bazel +++ b/tests/e2e/utils/BUILD.bazel @@ -20,5 +20,6 @@ ts_project( "//:node_modules/verdaccio-auth-memory", "//tests:node_modules/@types/tar-stream", "//tests:node_modules/tar-stream", + "//tests:node_modules/tree-kill", ], ) diff --git a/tests/e2e/utils/process.ts b/tests/e2e/utils/process.ts index 54bcf629a69e..91216843086a 100644 --- a/tests/e2e/utils/process.ts +++ b/tests/e2e/utils/process.ts @@ -1,7 +1,7 @@ import { spawn, SpawnOptions } from 'node:child_process'; import * as child_process from 'node:child_process'; import { getGlobalVariable, getGlobalVariablesEnv } from './env'; -import { setTimeout as sleep } from 'node:timers/promises'; +import treeKill from 'tree-kill'; import { delimiter, join, resolve } from 'node:path'; import { stripVTControlCharacters, styleText } from 'node:util'; @@ -255,37 +255,29 @@ export async function waitForAnyProcessOutputToMatch( return matchingProcess; } -/** - * Kills all tracked processes with a retry mechanism. - */ -export async function killAllProcesses(signal: NodeJS.Signals = 'SIGTERM'): Promise { - let attempts = 0; - const maxRetries = 3; - - while (_processes.length > 0 && attempts < maxRetries) { - attempts++; - - // Iterate backwards so we can remove elements while looping if needed. - for (let i = _processes.length - 1; i >= 0; i--) { - const childProc = _processes[i]; +export async function killAllProcesses(signal = 'SIGTERM'): Promise { + const processesToKill: Promise[] = []; - if (!childProc || childProc.killed) { - _processes.splice(i, 1); - continue; - } - - const killed = childProc.kill(signal); - if (killed) { - _processes.splice(i, 1); - continue; - } + while (_processes.length) { + const childProc = _processes.pop(); + if (!childProc || childProc.pid === undefined) { + continue; } - // If still have processes, wait a bit before the next retry (e.g., 100ms) - if (_processes.length > 0 && attempts < maxRetries) { - await sleep(100); - } + processesToKill.push( + new Promise((resolve) => { + treeKill(childProc.pid!, signal, () => { + // Ignore all errors. + // This is due to a race condition with the `waitForMatch` logic. + // where promises are resolved on matches and not when the process terminates. + // Also in some cases in windows we get `The operation attempted is not supported`. + resolve(); + }); + }), + ); } + + await Promise.all(processesToKill); } export function exec(cmd: string, ...args: string[]) { diff --git a/tests/package.json b/tests/package.json index 9eb906b8cda5..17660ff2192e 100644 --- a/tests/package.json +++ b/tests/package.json @@ -2,6 +2,7 @@ "devDependencies": { "@types/tar-stream": "3.1.4", "@angular-devkit/schematics": "workspace:*", - "tar-stream": "3.1.7" + "tar-stream": "3.1.7", + "tree-kill": "1.2.2" } }