From 9daed8299958f0a1d0ee36445e4e657c28d7b55e Mon Sep 17 00:00:00 2001 From: Alan Agius <17563226+alan-agius4@users.noreply.github.com> Date: Mon, 2 Mar 2026 08:22:33 +0000 Subject: [PATCH 1/2] refactor: remove `tree-kill` dependency and refactor `killAllProcesses` to use native `childProc.kill` with retries. Remove last `tree-kill` package usage. --- pnpm-lock.yaml | 9 ------- tests/e2e/utils/BUILD.bazel | 1 - tests/e2e/utils/process.ts | 49 ++++++++++++++++++++++--------------- tests/package.json | 3 +-- 4 files changed, 30 insertions(+), 32 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 929e00d801f8..310d3afd2352 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -848,9 +848,6 @@ importers: tar-stream: specifier: 3.1.7 version: 3.1.7 - tree-kill: - specifier: 1.2.2 - version: 1.2.2 packages: @@ -7768,10 +7765,6 @@ 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'} @@ -16418,8 +16411,6 @@ 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 d4493fb14492..dbbda05702cd 100644 --- a/tests/e2e/utils/BUILD.bazel +++ b/tests/e2e/utils/BUILD.bazel @@ -20,6 +20,5 @@ 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 91216843086a..5811f9b40346 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 treeKill from 'tree-kill'; +import { setTimeout as sleep } from 'node:timers/promises'; import { delimiter, join, resolve } from 'node:path'; import { stripVTControlCharacters, styleText } from 'node:util'; @@ -255,29 +255,38 @@ export async function waitForAnyProcessOutputToMatch( return matchingProcess; } -export async function killAllProcesses(signal = 'SIGTERM'): Promise { - const processesToKill: Promise[] = []; +/** + * Kills all tracked processes with a retry mechanism. + */ +export async function killAllProcesses(signal: NodeJS.Signals = 'SIGTERM'): Promise { + let attempts = 0; + const maxRetries = 3; + const processes = [..._processes]; + + 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]; - while (_processes.length) { - const childProc = _processes.pop(); - if (!childProc || childProc.pid === undefined) { - continue; + if (!childProc || childProc.killed) { + processes.splice(i, 1); + continue; + } + + const killed = childProc.kill(signal); + if (killed) { + processes.splice(i, 1); + continue; + } } - 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(); - }); - }), - ); + // If still have processes, wait a bit before the next retry (e.g., 100ms) + if (processes.length > 0 && attempts < maxRetries) { + await sleep(100); + } } - - await Promise.all(processesToKill); } export function exec(cmd: string, ...args: string[]) { diff --git a/tests/package.json b/tests/package.json index 17660ff2192e..9eb906b8cda5 100644 --- a/tests/package.json +++ b/tests/package.json @@ -2,7 +2,6 @@ "devDependencies": { "@types/tar-stream": "3.1.4", "@angular-devkit/schematics": "workspace:*", - "tar-stream": "3.1.7", - "tree-kill": "1.2.2" + "tar-stream": "3.1.7" } } From b999a6504897b756984904c9c0dd13a28452d185 Mon Sep 17 00:00:00 2001 From: Alan Agius <17563226+alan-agius4@users.noreply.github.com> Date: Mon, 2 Mar 2026 14:37:20 +0000 Subject: [PATCH 2/2] fixup! refactor: remove `tree-kill` dependency and refactor `killAllProcesses` to use native `childProc.kill` with retries. --- tests/e2e/utils/process.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/tests/e2e/utils/process.ts b/tests/e2e/utils/process.ts index 5811f9b40346..54bcf629a69e 100644 --- a/tests/e2e/utils/process.ts +++ b/tests/e2e/utils/process.ts @@ -261,29 +261,28 @@ export async function waitForAnyProcessOutputToMatch( export async function killAllProcesses(signal: NodeJS.Signals = 'SIGTERM'): Promise { let attempts = 0; const maxRetries = 3; - const processes = [..._processes]; - while (processes.length > 0 && attempts < maxRetries) { + 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]; + for (let i = _processes.length - 1; i >= 0; i--) { + const childProc = _processes[i]; if (!childProc || childProc.killed) { - processes.splice(i, 1); + _processes.splice(i, 1); continue; } const killed = childProc.kill(signal); if (killed) { - processes.splice(i, 1); + _processes.splice(i, 1); continue; } } // If still have processes, wait a bit before the next retry (e.g., 100ms) - if (processes.length > 0 && attempts < maxRetries) { + if (_processes.length > 0 && attempts < maxRetries) { await sleep(100); } }