From d7ed71eba070bb5a0cda0843220392d0f0a50eb8 Mon Sep 17 00:00:00 2001 From: Richard Hansen Date: Tue, 16 Feb 2021 16:48:50 -0500 Subject: [PATCH] plugins: Fix "Error: spawn npm ENOENT" error on Windows On Windows, npm should be invoked as `npm.cmd`, not `npm`. Use a drop-in replacement for `child_process.spawn()` that does the right thing on Windows. --- src/node/utils/run_cmd.js | 10 +++++----- src/package-lock.json | 9 ++------- src/package.json | 1 + 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/node/utils/run_cmd.js b/src/node/utils/run_cmd.js index f0d7c92d6..4b30af1fe 100644 --- a/src/node/utils/run_cmd.js +++ b/src/node/utils/run_cmd.js @@ -1,6 +1,6 @@ 'use strict'; -const childProcess = require('child_process'); +const spawn = require('cross-spawn'); const log4js = require('log4js'); const path = require('path'); const settings = require('./Settings'); @@ -28,14 +28,14 @@ const logLines = (readable, logLineFn) => { }; /** - * Similar to `util.promisify(childProcess.exec)`, except: + * Similar to `util.promisify(child_rocess.exec)`, except: * - `cwd` defaults to the Etherpad root directory. * - PATH is prefixed with src/node_modules/.bin so that utilities from installed dependencies * (e.g., npm) are preferred over system utilities. * - Output is passed to logger callback functions by default. See below for details. * * @param args Array of command-line arguments, where `args[0]` is the command to run. - * @param opts Optional options that will be passed to `childProcess.spawn()` with two extensions: + * @param opts Optional options that will be passed to `child_process.spawn()` with two extensions: * - `stdoutLogger`: Callback that is called each time a line of text is written to stdout (utf8 * is assumed). The line (without trailing newline) is passed as the only argument. If null, * stdout is not logged. If unset, defaults to no-op. Ignored if stdout is not a pipe. @@ -48,7 +48,7 @@ module.exports = exports = (args, opts = {}) => { logger.debug(`Executing command: ${args.join(' ')}`); const {stdoutLogger = () => {}, stderrLogger = () => {}} = opts; - // Avoid confusing childProcess.spawn() with our extensions. + // Avoid confusing child_process.spawn() with our extensions. opts = {...opts}; // Make a copy to avoid mutating the caller's copy. delete opts.stdoutLogger; delete opts.stderrLogger; @@ -70,7 +70,7 @@ module.exports = exports = (args, opts = {}) => { // process's `exit` handler so that we get a useful stack trace. const procFailedErr = new Error(`Command exited non-zero: ${args.join(' ')}`); - const proc = childProcess.spawn(args[0], args.slice(1), {cwd: settings.root, ...opts, env}); + const proc = spawn(args[0], args.slice(1), {cwd: settings.root, ...opts, env}); if (proc.stdout != null && stdoutLogger != null) logLines(proc.stdout, stdoutLogger); if (proc.stderr != null && stderrLogger != null) logLines(proc.stderr, stderrLogger); const p = new Promise((resolve, reject) => { diff --git a/src/package-lock.json b/src/package-lock.json index a9d66b11e..5e64120cc 100644 --- a/src/package-lock.json +++ b/src/package-lock.json @@ -1281,7 +1281,6 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, "requires": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -1292,7 +1291,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "requires": { "isexe": "^2.0.0" } @@ -7097,8 +7095,7 @@ "path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" }, "path-parse": { "version": "1.0.6", @@ -7621,7 +7618,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "requires": { "shebang-regex": "^3.0.0" } @@ -7629,8 +7625,7 @@ "shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" }, "signal-exit": { "version": "3.0.3", diff --git a/src/package.json b/src/package.json index 8545a277c..b7822cc10 100644 --- a/src/package.json +++ b/src/package.json @@ -36,6 +36,7 @@ "cheerio": "0.22.0", "clean-css": "4.2.3", "cookie-parser": "1.4.5", + "cross-spawn": "^7.0.3", "ejs": "^3.1.6", "etherpad-require-kernel": "1.0.9", "etherpad-yajsml": "0.0.2",