Skip to content

Commit d495284

Browse files
yanickrochonYanick Rochon
andauthored
fix(npm-lifecycle): properly handle SIGINT from child process (#41)
When a child process sends the INT signal, pnpm should not assume child failed to terminate Closes pnpm/pnpm#7164 Co-authored-by: Yanick Rochon <[email protected]>
1 parent 99ac042 commit d495284

File tree

3 files changed

+66
-6
lines changed

3 files changed

+66
-6
lines changed

index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ function runCmd_ (cmd, pkg, env, wd, opts, stage, unsafe, uid, gid, cb_) {
278278
proc.on('close', (code, signal) => {
279279
opts.log.silly('lifecycle', logid(pkg, stage), 'Returned: code:', code, ' signal:', signal)
280280
let err
281-
if (signal) {
281+
if (signal && signal !== "SIGINT") {
282282
err = new PnpmError('CHILD_PROCESS_FAILED', `Command failed with signal "${signal}"`)
283283
process.kill(process.pid, signal)
284284
} else if (code) {
@@ -304,7 +304,7 @@ function runCmd_ (cmd, pkg, env, wd, opts, stage, unsafe, uid, gid, cb_) {
304304
if (er.code !== 'EPERM') {
305305
er.code = 'ELIFECYCLE'
306306
}
307-
fs.stat(opts.dir, (statError, d) => {
307+
fs.stat(opts.dir, (statError) => {
308308
if (statError && statError.code === 'ENOENT' && opts.dir.split(path.sep).slice(-1)[0] === 'node_modules') {
309309
opts.log.warn('', 'Local package.json exists, but node_modules missing, did you mean to install?')
310310
}

test/fixtures/count-to-10/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"version": "1.0.0",
44
"scripts": {
55
"postinstall": "node postinstall",
6-
"signal-exit": "echo 'signal-exit script' && kill -s ABRT $$"
6+
"signal-abrt": "echo 'signal-exit script' && kill -s ABRT $$",
7+
"signal-int": "echo 'signal-int script' && kill -s INT $$"
78
}
89
}

test/index.js

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -183,13 +183,72 @@ test('throw error signal kills child', async function (t) {
183183
const dir = fixture
184184
const pkg = require(path.join(fixture, 'package.json'))
185185

186-
t.rejects(async () => {
187-
await lifecycle(pkg, 'signal-exit', fixture, {
186+
await t.rejects(async () => {
187+
await lifecycle(pkg, 'signal-abrt', fixture, {
188188
stdio: 'pipe',
189189
log,
190190
dir,
191191
config: {}
192192
})
193-
stubProcessExit.restore()
194193
})
194+
195+
stubProcessExit.restore()
196+
})
197+
198+
test('no error on INT signal from child', async function (t) {
199+
const fixture = path.join(__dirname, 'fixtures', 'count-to-10')
200+
201+
const verbose = sinon.spy()
202+
const silly = sinon.spy()
203+
const info = sinon.spy()
204+
205+
const stubProcessExit = sinon.stub(process, 'kill').callsFake(noop)
206+
207+
const log = {
208+
level: 'silent',
209+
info,
210+
warn: noop,
211+
silly,
212+
verbose,
213+
pause: noop,
214+
resume: noop,
215+
clearProgress: noop,
216+
showProgress: noop
217+
}
218+
219+
const dir = fixture
220+
const pkg = require(path.join(fixture, 'package.json'))
221+
222+
await t.resolves(async () => {
223+
await lifecycle(pkg, 'signal-int', fixture, {
224+
stdio: 'pipe',
225+
log,
226+
dir,
227+
config: {}
228+
})
229+
})
230+
231+
stubProcessExit.restore()
232+
233+
t.ok(
234+
!info.calledWithMatch(
235+
'lifecycle',
236+
'undefined~signal-int:',
237+
'Failed to exec signal-int script'
238+
),
239+
'INT signal intercepted incorrectly'
240+
)
241+
242+
t.ok(
243+
silly.calledWithMatch(
244+
'lifecycle',
245+
'undefined~signal-int:',
246+
'Returned: code:',
247+
null,
248+
' signal:',
249+
'SIGINT'
250+
),
251+
'INT signal reported'
252+
)
253+
195254
})

0 commit comments

Comments
 (0)