Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 4e38240

Browse files
WesCossicknfischer
authored andcommitted
Add boolean fatal option to exec() function (#961)
This PR introduces a new boolean fatal option for the exec() function. Like the existing silent option, this new option allows you to override the global fatal configuration parameter on a per-command basis.
1 parent 57df38c commit 4e38240

File tree

4 files changed

+51
-6
lines changed

4 files changed

+51
-6
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,7 @@ Available options:
308308

309309
+ `async`: Asynchronous execution. If a callback is provided, it will be set to
310310
`true`, regardless of the passed value (default: `false`).
311+
+ `fatal`: Exit upon error (default: `false`).
311312
+ `silent`: Do not echo program output to console (default: `false`).
312313
+ `encoding`: Character encoding to use. Affects the values returned to stdout and stderr, and
313314
what is written to stdout and stderr when not in silent mode (default: `'utf8'`).

src/common.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ CommandError.prototype = Object.create(Error.prototype);
8888
CommandError.prototype.constructor = CommandError;
8989
exports.CommandError = CommandError; // visible for testing
9090

91-
// Shows error message. Throws if config.fatal is true
91+
// Shows error message. Throws if fatal is true (defaults to config.fatal, overridable with options.fatal)
9292
function error(msg, _code, options) {
9393
// Validate input
9494
if (typeof msg !== 'string') throw new Error('msg must be a string');
@@ -98,6 +98,7 @@ function error(msg, _code, options) {
9898
code: 1,
9999
prefix: state.currentCmd + ': ',
100100
silent: false,
101+
fatal: config.fatal,
101102
};
102103

103104
if (typeof _code === 'number' && isObject(options)) {
@@ -118,7 +119,7 @@ function error(msg, _code, options) {
118119
state.error += logEntry;
119120

120121
// Throw an error, or log the entry
121-
if (config.fatal) throw new Error(logEntry);
122+
if (options.fatal) throw new Error(logEntry);
122123
if (msg.length > 0 && !options.silent) log(logEntry);
123124

124125
if (!options.continue) {
@@ -424,7 +425,7 @@ function wrap(cmd, fn, options) {
424425
e.name = 'ShellJSInternalError';
425426
throw e;
426427
}
427-
if (config.fatal) throw e;
428+
if (config.fatal || options.handlesFatalDynamically) throw e;
428429
}
429430

430431
if (options.wrapOutput &&
@@ -450,6 +451,7 @@ var DEFAULT_WRAP_OPTIONS = {
450451
canReceivePipe: false,
451452
cmdOptions: null,
452453
globStart: 1,
454+
handlesFatalDynamically: false,
453455
pipeOnly: false,
454456
wrapOutput: true,
455457
unix: true,

src/exec.js

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,22 @@ common.register('exec', _exec, {
1212
unix: false,
1313
canReceivePipe: true,
1414
wrapOutput: false,
15+
handlesFatalDynamically: true,
1516
});
1617

1718
// We use this function to run `exec` synchronously while also providing realtime
1819
// output.
1920
function execSync(cmd, opts, pipe) {
2021
if (!common.config.execPath) {
21-
common.error('Unable to find a path to the node binary. Please manually set config.execPath');
22+
try {
23+
common.error('Unable to find a path to the node binary. Please manually set config.execPath');
24+
} catch (e) {
25+
if (opts.fatal) {
26+
throw e;
27+
}
28+
29+
return;
30+
}
2231
}
2332

2433
var tempDir = _tempDir();
@@ -28,6 +37,7 @@ function execSync(cmd, opts, pipe) {
2837

2938
opts = common.extend({
3039
silent: common.config.silent,
40+
fatal: common.config.fatal, // TODO(nfischer): this and the line above are probably unnecessary
3141
cwd: _pwd().toString(),
3242
env: process.env,
3343
maxBuffer: DEFAULT_MAXBUFFER_SIZE,
@@ -99,7 +109,7 @@ function execSync(cmd, opts, pipe) {
99109
// Note: `silent` should be unconditionally true to avoid double-printing
100110
// the command's stderr, and to avoid printing any stderr when the user has
101111
// set `shell.config.silent`.
102-
common.error(stderr, code, { continue: true, silent: true });
112+
common.error(stderr, code, { continue: true, silent: true, fatal: opts.fatal });
103113
}
104114
var obj = common.ShellString(stdout, stderr, code);
105115
return obj;
@@ -109,6 +119,7 @@ function execSync(cmd, opts, pipe) {
109119
function execAsync(cmd, opts, pipe, callback) {
110120
opts = common.extend({
111121
silent: common.config.silent,
122+
fatal: common.config.fatal, // TODO(nfischer): this and the line above are probably unnecessary
112123
cwd: _pwd().toString(),
113124
env: process.env,
114125
maxBuffer: DEFAULT_MAXBUFFER_SIZE,
@@ -146,6 +157,7 @@ function execAsync(cmd, opts, pipe, callback) {
146157
//@
147158
//@ + `async`: Asynchronous execution. If a callback is provided, it will be set to
148159
//@ `true`, regardless of the passed value (default: `false`).
160+
//@ + `fatal`: Exit upon error (default: `false`).
149161
//@ + `silent`: Do not echo program output to console (default: `false`).
150162
//@ + `encoding`: Character encoding to use. Affects the values returned to stdout and stderr, and
151163
//@ what is written to stdout and stderr when not in silent mode (default: `'utf8'`).
@@ -184,7 +196,6 @@ function execAsync(cmd, opts, pipe, callback) {
184196
//@ Guidelines](https://github.com/shelljs/shelljs/wiki/Security-guidelines).
185197
function _exec(command, options, callback) {
186198
options = options || {};
187-
if (!command) common.error('must specify command');
188199

189200
var pipe = common.readFromPipe();
190201

@@ -201,9 +212,22 @@ function _exec(command, options, callback) {
201212

202213
options = common.extend({
203214
silent: common.config.silent,
215+
fatal: common.config.fatal,
204216
async: false,
205217
}, options);
206218

219+
if (!command) {
220+
try {
221+
common.error('must specify command');
222+
} catch (e) {
223+
if (options.fatal) {
224+
throw e;
225+
}
226+
227+
return;
228+
}
229+
}
230+
207231
if (options.async) {
208232
return execAsync(command, options, pipe, callback);
209233
} else {

test/exec.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,15 @@ test('config.fatal and unknown command', t => {
4545
shell.config.fatal = oldFatal;
4646
});
4747

48+
test('options.fatal = true and unknown command', t => {
49+
const oldFatal = shell.config.fatal;
50+
shell.config.fatal = false;
51+
t.throws(() => {
52+
shell.exec('asdfasdf', { fatal: true }); // could not find command
53+
}, /asdfasdf/); // name of command should be in error message
54+
shell.config.fatal = oldFatal; // TODO(nfischer): this setting won't get reset if the assertion above fails
55+
});
56+
4857
test('exec exits gracefully if we cannot find the execPath', t => {
4958
shell.config.execPath = null;
5059
shell.exec('echo foo');
@@ -193,6 +202,15 @@ test('encoding option works', t => {
193202
t.is(result.stderr.toString(), '');
194203
});
195204

205+
test('options.fatal = false and unknown command', t => {
206+
const oldFatal = shell.config.fatal;
207+
shell.config.fatal = true;
208+
const result = shell.exec('asdfasdf', { fatal: false }); // could not find command
209+
shell.config.fatal = oldFatal;
210+
t.truthy(shell.error());
211+
t.truthy(result.code);
212+
});
213+
196214
//
197215
// async
198216
//

0 commit comments

Comments
 (0)