From b17b33f56df5fab4ffd0c0b4ecd676e1b081ff35 Mon Sep 17 00:00:00 2001 From: Danny Martini Date: Thu, 11 Jun 2026 15:43:08 +0200 Subject: [PATCH] fix(core): Prevent SSH tunnel cleanup timer from keeping the process alive (no-changelog) (#32146) --- .../__tests__/ssh-clients-manager.test.ts | 6 ++++++ packages/core/src/execution-engine/ssh-clients-manager.ts | 8 ++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/core/src/execution-engine/__tests__/ssh-clients-manager.test.ts b/packages/core/src/execution-engine/__tests__/ssh-clients-manager.test.ts index 453f4a432bc..f1e3304103a 100644 --- a/packages/core/src/execution-engine/__tests__/ssh-clients-manager.test.ts +++ b/packages/core/src/execution-engine/__tests__/ssh-clients-manager.test.ts @@ -40,6 +40,12 @@ afterEach(() => { sshClientsManager.onShutdown(); }); +it('should not keep the process alive for the stale-connection cleanup timer', () => { + const { cleanupTimer } = sshClientsManager as unknown as { cleanupTimer: NodeJS.Timeout }; + + expect(cleanupTimer.hasRef()).toBe(false); +}); + describe('getClient', () => { it('should create a new SSH client', async () => { const client = await sshClientsManager.getClient(credentials); diff --git a/packages/core/src/execution-engine/ssh-clients-manager.ts b/packages/core/src/execution-engine/ssh-clients-manager.ts index 7bec4f2b4a7..3da0f3a4274 100644 --- a/packages/core/src/execution-engine/ssh-clients-manager.ts +++ b/packages/core/src/execution-engine/ssh-clients-manager.ts @@ -65,8 +65,12 @@ export class SSHClientsManager { // Close all SSH connections when the process exits process.on('exit', () => this.onShutdown()); - // Regularly close stale SSH connections - this.cleanupTimer = setInterval(() => this.cleanupStaleConnections(), 60 * 1000); + // Regularly close stale SSH connections. Unref'd so this housekeeping + // timer never keeps the process alive on its own: the manager is created + // by every workflow execution, and a referenced interval would block + // process exit until SIGKILL (e.g. single-file integration test runs + // hang after the run completes). + this.cleanupTimer = setInterval(() => this.cleanupStaleConnections(), 60 * 1000).unref(); this.logger = logger.scoped('ssh-client'); }