From 08841a8e7b595363298e5c9719b22ff7eace4a28 Mon Sep 17 00:00:00 2001 From: Pandelis Zembashis Date: Mon, 26 Oct 2026 11:24:05 -0700 Subject: [PATCH] Retry deadline-killed Docker readiness probes regardless of shutdown signal --- builder.mjs | 3 ++- builder.test.mjs | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/builder.mjs b/builder.mjs index 85b1b18..356729b 100644 --- a/builder.mjs +++ b/builder.mjs @@ -37,7 +37,8 @@ export async function command(executable, args, env, timeout = 120_000) { return stdout; } catch (error) { // Never include argv, environment, or stdout (which may contain credentials). - const timedOut = error.killed && error.signal === 'SIGTERM' && error.code == null; + // A deadline kill can finish with SIGPIPE while Docker shuts down. + const timedOut = error.killed && error.code == null; const details = [timedOut ? `timed out after ${timeout / 1000}s` : 'command failed', `exit=${error.code ?? 'none'}`, `signal=${error.signal ?? 'none'}`, `killed=${Boolean(error.killed)}`]; const operation = args.includes('version') ? 'version' : args.slice(0, 2).filter(a => /^[a-z-]+$/.test(a)).join(' '); diff --git a/builder.test.mjs b/builder.test.mjs index 59aaada..79e1215 100644 --- a/builder.test.mjs +++ b/builder.test.mjs @@ -484,3 +484,21 @@ test('installation and CLI calls follow the selected hosted API', async t => { assert.equal(f.calls.find(c => c.args[1] === 'status').env.VEX_API_BASE_URL, api); } }); + +test('deadline kills remain retryable when the child shuts down with SIGPIPE', { skip: process.platform === 'win32' }, async () => { + await assert.rejects(command('/bin/sh', ['-c', + "trap 'kill -PIPE $$' TERM; while :; do sleep 0.05; done"], + {}, 300), error => { + assert.match(error.message, /timed out after 0.3s/); + assert.match(error.message, /signal=SIGPIPE, killed=true/); + return true; + }); +}); + +test('a child exiting on SIGPIPE without a deadline kill is not reported as a timeout', { skip: process.platform === 'win32' }, async () => { + await assert.rejects(command('/bin/sh', ['-c', 'kill -PIPE $$'], {}), error => { + assert.match(error.message, /command failed/); + assert.doesNotMatch(error.message, /timed out/); + return true; + }); +});