Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,9 @@ function run (argv) {
} else {
// Keep the event loop alive until the re-raised signal terminates us.
// Otherwise Node can finish normally before delivery and report exit 0.
setInterval(() => {}, 1000)
// Node ignores some signals (SIGPIPE), so fall back to the shell's
// 128 + signal number status instead of waiting forever.
setTimeout(() => process.exit(128 + os.constants.signals[signal]), 1000)
process.kill(process.pid, signal)
}
})
Expand Down
13 changes: 13 additions & 0 deletions tests/test-cli-signals.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,19 @@ t.test('POSIX signal forwarding', { skip: process.platform === 'win32' }, async
st.same(await command.exited, { code: null, signal: 'SIGTERM' })
})

await ct.test('exits when the command dies from a signal Node ignores', async st => {
// Node ignores SIGPIPE, so re-raising it does not stop dotenv (`dotenv run -- cmd | head`).
const result = cp.spawnSync(process.execPath, ['dist/index.cjs', 'run', '-q', '--', 'sh', '-c', 'kill -PIPE $$'], {
cwd: path.resolve(__dirname, '..'),
encoding: 'utf8',
timeout: 5000,
env: { ...process.env, NODE_OPTIONS: '' }
})
st.equal(result.error, undefined)
st.equal(result.signal, null)
st.equal(result.status, 128 + os.constants.signals.SIGPIPE)
})

await ct.test('forwards termination to subprocesses too', async st => {
const grandchild = "process.once('SIGTERM', () => { console.log('grandchild stopped'); process.exit(0) }); setInterval(() => {}, 1000); process.send('ready')"
const command = await launch(st, `
Expand Down
Loading