Description
Current Behavior:
If a command in an npm script (e.g., postinstall or test) causes a sudo prompt to be shown, the prompt is not presented to the person and the script hangs.
For example, if I have a postinstall
script and I run npm install
and some command in the script (e.g., an execFileSync()
) requires sudo permissions, I will never the sudo prompt and the installation will hang.
Expected Behavior:
The sudo prompt should be shown to the person.
Steps To Reproduce:
- Create a folder called
my-module
- Run
npm init -y
to create yourpackage.json
file - Add the following
scripts
section to your package file:
"scripts": {
"postinstall": "node ./postinstall.js"
}
- Create the
postinstall.js
file with the following contents:
import childProcess from 'child_process'
childProcess.execFileSync('sudo', ['ls', '/root'])
- Run
npm i
(and note that you get prompted for the sudo password as expected and that installation succeeds once you enter it) - Create a different directory in the same parent directory as
my-module
calledthe-bug
- Run
npm init -y
from thethe-bug
directory - In the
the-bug
directory, typenpm i ../my-module
to install your module
What should happen
You should get prompted for your sudo password and be able to enter it and installation should succeed when you do.
What actually happens
You don’t see the sudo prompt and the installation hangs.
Workaround
Until this bug is fixed, you can use the sudo-prompt module to display a graphical sudo prompt in your lifecycle scripts.
The following snippet from the real-world use case where I encountered this bug (in my Auto Encrypt Localhost library that uses mkcert to provision local TLS certificates) demonstrates the workaround:
import sudoPrompt from 'sudo-prompt'
import { binaryPath as mkcertBinary } from '../lib/mkcert.js'
//…
await (() => {
return new Promise((resolve, reject) => {
sudoPrompt.exec(`${mkcertBinary} -install`, {name: 'Auto Encrypt Localhost'}, function(error, stdout, stderr) {
if (error) reject(error)
console.log(stdout)
resolve()
})
})
})()
(Above is not a true workaround as it does not behave exactly like sudo on Linux, causing file permission errors).
Other notes
- Note that if you have passwordless sudo set up on your system, you will not encounter this bug.
Update
Also just ran into this issue also when running tests, which actually makes it a much more important bug. So if your Node app at any point requires sudo privileges, this means that your test will fail also as they do not show the sudo password prompt.
As a workaround for that, do something that requires the sudo prompt before your test. e.g., in your package file:
"scripts": {
"test": "sudo echo 'Got sudo privileges.\n' && esm-tape-runner 'test/**/*.js' | tap-monkey"
}
That will unlock sudo and you should be able to run your tests before the timeout.
(Or, of course, use passwordless sudo on your dev machine but you cannot rely on everyone doing this. Most folks probably won’t and they’ll just think that your tests are hanging. So until this bug is fixed, your best bet is to use a workaround similar to the one above.)
Environment:
- OS: elementary OS 5.1.7 Hera x86_64 (based on Ubuntu 18.04)
- Node: 14.16.0
- npm: 7.6.0