azure-pipelines-task-lib 3.3.1 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/toolrunner.js CHANGED
@@ -1,967 +1,967 @@
1
- "use strict";
2
- var __extends = (this && this.__extends) || (function () {
3
- var extendStatics = function (d, b) {
4
- extendStatics = Object.setPrototypeOf ||
5
- ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
6
- function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
7
- return extendStatics(d, b);
8
- };
9
- return function (d, b) {
10
- extendStatics(d, b);
11
- function __() { this.constructor = d; }
12
- d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
13
- };
14
- })();
15
- Object.defineProperty(exports, "__esModule", { value: true });
16
- exports.ToolRunner = void 0;
17
- var Q = require("q");
18
- var os = require("os");
19
- var events = require("events");
20
- var child = require("child_process");
21
- var im = require("./internal");
22
- var fs = require("fs");
23
- var ToolRunner = /** @class */ (function (_super) {
24
- __extends(ToolRunner, _super);
25
- function ToolRunner(toolPath) {
26
- var _this = _super.call(this) || this;
27
- _this.cmdSpecialChars = [' ', '\t', '&', '(', ')', '[', ']', '{', '}', '^', '=', ';', '!', '\'', '+', ',', '`', '~', '|', '<', '>', '"'];
28
- if (!toolPath) {
29
- throw new Error('Parameter \'toolPath\' cannot be null or empty.');
30
- }
31
- _this.toolPath = im._which(toolPath, true);
32
- _this.args = [];
33
- _this._debug('toolRunner toolPath: ' + toolPath);
34
- return _this;
35
- }
36
- ToolRunner.prototype._debug = function (message) {
37
- this.emit('debug', message);
38
- };
39
- ToolRunner.prototype._argStringToArray = function (argString) {
40
- var args = [];
41
- var inQuotes = false;
42
- var escaped = false;
43
- var lastCharWasSpace = true;
44
- var arg = '';
45
- var append = function (c) {
46
- // we only escape double quotes.
47
- if (escaped) {
48
- if (c !== '"') {
49
- arg += '\\';
50
- }
51
- else {
52
- arg.slice(0, -1);
53
- }
54
- }
55
- arg += c;
56
- escaped = false;
57
- };
58
- for (var i = 0; i < argString.length; i++) {
59
- var c = argString.charAt(i);
60
- if (c === ' ' && !inQuotes) {
61
- if (!lastCharWasSpace) {
62
- args.push(arg);
63
- arg = '';
64
- }
65
- lastCharWasSpace = true;
66
- continue;
67
- }
68
- else {
69
- lastCharWasSpace = false;
70
- }
71
- if (c === '"') {
72
- if (!escaped) {
73
- inQuotes = !inQuotes;
74
- }
75
- else {
76
- append(c);
77
- }
78
- continue;
79
- }
80
- if (c === "\\" && escaped) {
81
- append(c);
82
- continue;
83
- }
84
- if (c === "\\" && inQuotes) {
85
- escaped = true;
86
- continue;
87
- }
88
- append(c);
89
- lastCharWasSpace = false;
90
- }
91
- if (!lastCharWasSpace) {
92
- args.push(arg.trim());
93
- }
94
- return args;
95
- };
96
- ToolRunner.prototype._getCommandString = function (options, noPrefix) {
97
- var _this = this;
98
- var toolPath = this._getSpawnFileName();
99
- var args = this._getSpawnArgs(options);
100
- var cmd = noPrefix ? '' : '[command]'; // omit prefix when piped to a second tool
101
- var commandParts = [];
102
- if (process.platform == 'win32') {
103
- // Windows + cmd file
104
- if (this._isCmdFile()) {
105
- commandParts.push(toolPath);
106
- commandParts = commandParts.concat(args);
107
- }
108
- // Windows + verbatim
109
- else if (options.windowsVerbatimArguments) {
110
- commandParts.push("\"" + toolPath + "\"");
111
- commandParts = commandParts.concat(args);
112
- }
113
- else if (options.shell) {
114
- commandParts.push(this._windowsQuoteCmdArg(toolPath));
115
- commandParts = commandParts.concat(args);
116
- }
117
- // Windows (regular)
118
- else {
119
- commandParts.push(this._windowsQuoteCmdArg(toolPath));
120
- commandParts = commandParts.concat(args.map(function (arg) { return _this._windowsQuoteCmdArg(arg); }));
121
- }
122
- }
123
- else {
124
- // OSX/Linux - this can likely be improved with some form of quoting.
125
- // creating processes on Unix is fundamentally different than Windows.
126
- // on Unix, execvp() takes an arg array.
127
- commandParts.push(toolPath);
128
- commandParts = commandParts.concat(args);
129
- }
130
- cmd += commandParts.join(' ');
131
- // append second tool
132
- if (this.pipeOutputToTool) {
133
- cmd += ' | ' + this.pipeOutputToTool._getCommandString(options, /*noPrefix:*/ true);
134
- }
135
- return cmd;
136
- };
137
- ToolRunner.prototype._processLineBuffer = function (data, strBuffer, onLine) {
138
- try {
139
- var s = strBuffer + data.toString();
140
- var n = s.indexOf(os.EOL);
141
- while (n > -1) {
142
- var line = s.substring(0, n);
143
- onLine(line);
144
- // the rest of the string ...
145
- s = s.substring(n + os.EOL.length);
146
- n = s.indexOf(os.EOL);
147
- }
148
- strBuffer = s;
149
- }
150
- catch (err) {
151
- // streaming lines to console is best effort. Don't fail a build.
152
- this._debug('error processing line');
153
- }
154
- };
155
- /**
156
- * Wraps an arg string with specified char if it's not already wrapped
157
- * @returns {string} Arg wrapped with specified char
158
- * @param {string} arg Input argument string
159
- * @param {string} wrapChar A char input string should be wrapped with
160
- */
161
- ToolRunner.prototype._wrapArg = function (arg, wrapChar) {
162
- if (!this._isWrapped(arg, wrapChar)) {
163
- return "" + wrapChar + arg + wrapChar;
164
- }
165
- return arg;
166
- };
167
- /**
168
- * Unwraps an arg string wrapped with specified char
169
- * @param arg Arg wrapped with specified char
170
- * @param wrapChar A char to be removed
171
- */
172
- ToolRunner.prototype._unwrapArg = function (arg, wrapChar) {
173
- if (this._isWrapped(arg, wrapChar)) {
174
- var pattern = new RegExp("(^\\\\?" + wrapChar + ")|(\\\\?" + wrapChar + "$)", 'g');
175
- return arg.trim().replace(pattern, '');
176
- }
177
- return arg;
178
- };
179
- /**
180
- * Determine if arg string is wrapped with specified char
181
- * @param arg Input arg string
182
- */
183
- ToolRunner.prototype._isWrapped = function (arg, wrapChar) {
184
- var pattern = new RegExp("^\\\\?" + wrapChar + ".+\\\\?" + wrapChar + "$");
185
- return pattern.test(arg.trim());
186
- };
187
- ToolRunner.prototype._getSpawnFileName = function (options) {
188
- if (process.platform == 'win32') {
189
- if (this._isCmdFile()) {
190
- return process.env['COMSPEC'] || 'cmd.exe';
191
- }
192
- }
193
- if (options && options.shell) {
194
- return this._wrapArg(this.toolPath, '"');
195
- }
196
- return this.toolPath;
197
- };
198
- ToolRunner.prototype._getSpawnArgs = function (options) {
199
- var _this = this;
200
- if (process.platform == 'win32') {
201
- if (this._isCmdFile()) {
202
- var argline = "/D /S /C \"" + this._windowsQuoteCmdArg(this.toolPath);
203
- for (var i = 0; i < this.args.length; i++) {
204
- argline += ' ';
205
- argline += options.windowsVerbatimArguments ? this.args[i] : this._windowsQuoteCmdArg(this.args[i]);
206
- }
207
- argline += '"';
208
- return [argline];
209
- }
210
- if (options.windowsVerbatimArguments) {
211
- // note, in Node 6.x options.argv0 can be used instead of overriding args.slice and args.unshift.
212
- // for more details, refer to https://github.com/nodejs/node/blob/v6.x/lib/child_process.js
213
- var args_1 = this.args.slice(0); // copy the array
214
- // override slice to prevent Node from creating a copy of the arg array.
215
- // we need Node to use the "unshift" override below.
216
- args_1.slice = function () {
217
- if (arguments.length != 1 || arguments[0] != 0) {
218
- throw new Error('Unexpected arguments passed to args.slice when windowsVerbatimArguments flag is set.');
219
- }
220
- return args_1;
221
- };
222
- // override unshift
223
- //
224
- // when using the windowsVerbatimArguments option, Node does not quote the tool path when building
225
- // the cmdline parameter for the win32 function CreateProcess(). an unquoted space in the tool path
226
- // causes problems for tools when attempting to parse their own command line args. tools typically
227
- // assume their arguments begin after arg 0.
228
- //
229
- // by hijacking unshift, we can quote the tool path when it pushed onto the args array. Node builds
230
- // the cmdline parameter from the args array.
231
- //
232
- // note, we can't simply pass a quoted tool path to Node for multiple reasons:
233
- // 1) Node verifies the file exists (calls win32 function GetFileAttributesW) and the check returns
234
- // false if the path is quoted.
235
- // 2) Node passes the tool path as the application parameter to CreateProcess, which expects the
236
- // path to be unquoted.
237
- //
238
- // also note, in addition to the tool path being embedded within the cmdline parameter, Node also
239
- // passes the tool path to CreateProcess via the application parameter (optional parameter). when
240
- // present, Windows uses the application parameter to determine which file to run, instead of
241
- // interpreting the file from the cmdline parameter.
242
- args_1.unshift = function () {
243
- if (arguments.length != 1) {
244
- throw new Error('Unexpected arguments passed to args.unshift when windowsVerbatimArguments flag is set.');
245
- }
246
- return Array.prototype.unshift.call(args_1, "\"" + arguments[0] + "\""); // quote the file name
247
- };
248
- return args_1;
249
- }
250
- else if (options.shell) {
251
- var args = [];
252
- for (var _i = 0, _a = this.args; _i < _a.length; _i++) {
253
- var arg = _a[_i];
254
- if (this._needQuotesForCmd(arg, '%')) {
255
- args.push(this._wrapArg(arg, '"'));
256
- }
257
- else {
258
- args.push(arg);
259
- }
260
- }
261
- return args;
262
- }
263
- }
264
- else if (options.shell) {
265
- return this.args.map(function (arg) {
266
- if (_this._isWrapped(arg, "'")) {
267
- return arg;
268
- }
269
- // remove wrapping double quotes to avoid escaping
270
- arg = _this._unwrapArg(arg, '"');
271
- arg = _this._escapeChar(arg, '"');
272
- return _this._wrapArg(arg, '"');
273
- });
274
- }
275
- return this.args;
276
- };
277
- /**
278
- * Escape specified character.
279
- * @param arg String to escape char in
280
- * @param charToEscape Char should be escaped
281
- */
282
- ToolRunner.prototype._escapeChar = function (arg, charToEscape) {
283
- var escChar = "\\";
284
- var output = '';
285
- var charIsEscaped = false;
286
- for (var _i = 0, arg_1 = arg; _i < arg_1.length; _i++) {
287
- var char = arg_1[_i];
288
- if (char === charToEscape && !charIsEscaped) {
289
- output += escChar + char;
290
- }
291
- else {
292
- output += char;
293
- }
294
- charIsEscaped = char === escChar && !charIsEscaped;
295
- }
296
- return output;
297
- };
298
- ToolRunner.prototype._isCmdFile = function () {
299
- var upperToolPath = this.toolPath.toUpperCase();
300
- return im._endsWith(upperToolPath, '.CMD') || im._endsWith(upperToolPath, '.BAT');
301
- };
302
- /**
303
- * Determine whether the cmd arg needs to be quoted. Returns true if arg contains any of special chars array.
304
- * @param arg The cmd command arg.
305
- * @param additionalChars Additional chars which should be also checked.
306
- */
307
- ToolRunner.prototype._needQuotesForCmd = function (arg, additionalChars) {
308
- var specialChars = this.cmdSpecialChars;
309
- if (additionalChars) {
310
- specialChars = this.cmdSpecialChars.concat(additionalChars);
311
- }
312
- var _loop_1 = function (char) {
313
- if (specialChars.some(function (x) { return x === char; })) {
314
- return { value: true };
315
- }
316
- };
317
- for (var _i = 0, arg_2 = arg; _i < arg_2.length; _i++) {
318
- var char = arg_2[_i];
319
- var state_1 = _loop_1(char);
320
- if (typeof state_1 === "object")
321
- return state_1.value;
322
- }
323
- return false;
324
- };
325
- ToolRunner.prototype._windowsQuoteCmdArg = function (arg) {
326
- // for .exe, apply the normal quoting rules that libuv applies
327
- if (!this._isCmdFile()) {
328
- return this._uv_quote_cmd_arg(arg);
329
- }
330
- // otherwise apply quoting rules specific to the cmd.exe command line parser.
331
- // the libuv rules are generic and are not designed specifically for cmd.exe
332
- // command line parser.
333
- //
334
- // for a detailed description of the cmd.exe command line parser, refer to
335
- // http://stackoverflow.com/questions/4094699/how-does-the-windows-command-interpreter-cmd-exe-parse-scripts/7970912#7970912
336
- // need quotes for empty arg
337
- if (!arg) {
338
- return '""';
339
- }
340
- // determine whether the arg needs to be quoted
341
- var needsQuotes = this._needQuotesForCmd(arg);
342
- // short-circuit if quotes not needed
343
- if (!needsQuotes) {
344
- return arg;
345
- }
346
- // the following quoting rules are very similar to the rules that by libuv applies.
347
- //
348
- // 1) wrap the string in quotes
349
- //
350
- // 2) double-up quotes - i.e. " => ""
351
- //
352
- // this is different from the libuv quoting rules. libuv replaces " with \", which unfortunately
353
- // doesn't work well with a cmd.exe command line.
354
- //
355
- // note, replacing " with "" also works well if the arg is passed to a downstream .NET console app.
356
- // for example, the command line:
357
- // foo.exe "myarg:""my val"""
358
- // is parsed by a .NET console app into an arg array:
359
- // [ "myarg:\"my val\"" ]
360
- // which is the same end result when applying libuv quoting rules. although the actual
361
- // command line from libuv quoting rules would look like:
362
- // foo.exe "myarg:\"my val\""
363
- //
364
- // 3) double-up slashes that preceed a quote,
365
- // e.g. hello \world => "hello \world"
366
- // hello\"world => "hello\\""world"
367
- // hello\\"world => "hello\\\\""world"
368
- // hello world\ => "hello world\\"
369
- //
370
- // technically this is not required for a cmd.exe command line, or the batch argument parser.
371
- // the reasons for including this as a .cmd quoting rule are:
372
- //
373
- // a) this is optimized for the scenario where the argument is passed from the .cmd file to an
374
- // external program. many programs (e.g. .NET console apps) rely on the slash-doubling rule.
375
- //
376
- // b) it's what we've been doing previously (by deferring to node default behavior) and we
377
- // haven't heard any complaints about that aspect.
378
- //
379
- // note, a weakness of the quoting rules chosen here, is that % is not escaped. in fact, % cannot be
380
- // escaped when used on the command line directly - even though within a .cmd file % can be escaped
381
- // by using %%.
382
- //
383
- // the saving grace is, on the command line, %var% is left as-is if var is not defined. this contrasts
384
- // the line parsing rules within a .cmd file, where if var is not defined it is replaced with nothing.
385
- //
386
- // one option that was explored was replacing % with ^% - i.e. %var% => ^%var^%. this hack would
387
- // often work, since it is unlikely that var^ would exist, and the ^ character is removed when the
388
- // variable is used. the problem, however, is that ^ is not removed when %* is used to pass the args
389
- // to an external program.
390
- //
391
- // an unexplored potential solution for the % escaping problem, is to create a wrapper .cmd file.
392
- // % can be escaped within a .cmd file.
393
- var reverse = '"';
394
- var quote_hit = true;
395
- for (var i = arg.length; i > 0; i--) { // walk the string in reverse
396
- reverse += arg[i - 1];
397
- if (quote_hit && arg[i - 1] == '\\') {
398
- reverse += '\\'; // double the slash
399
- }
400
- else if (arg[i - 1] == '"') {
401
- quote_hit = true;
402
- reverse += '"'; // double the quote
403
- }
404
- else {
405
- quote_hit = false;
406
- }
407
- }
408
- reverse += '"';
409
- return reverse.split('').reverse().join('');
410
- };
411
- ToolRunner.prototype._uv_quote_cmd_arg = function (arg) {
412
- // Tool runner wraps child_process.spawn() and needs to apply the same quoting as
413
- // Node in certain cases where the undocumented spawn option windowsVerbatimArguments
414
- // is used.
415
- //
416
- // Since this function is a port of quote_cmd_arg from Node 4.x (technically, lib UV,
417
- // see https://github.com/nodejs/node/blob/v4.x/deps/uv/src/win/process.c for details),
418
- // pasting copyright notice from Node within this function:
419
- //
420
- // Copyright Joyent, Inc. and other Node contributors. All rights reserved.
421
- //
422
- // Permission is hereby granted, free of charge, to any person obtaining a copy
423
- // of this software and associated documentation files (the "Software"), to
424
- // deal in the Software without restriction, including without limitation the
425
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
426
- // sell copies of the Software, and to permit persons to whom the Software is
427
- // furnished to do so, subject to the following conditions:
428
- //
429
- // The above copyright notice and this permission notice shall be included in
430
- // all copies or substantial portions of the Software.
431
- //
432
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
433
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
434
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
435
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
436
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
437
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
438
- // IN THE SOFTWARE.
439
- if (!arg) {
440
- // Need double quotation for empty argument
441
- return '""';
442
- }
443
- if (arg.indexOf(' ') < 0 && arg.indexOf('\t') < 0 && arg.indexOf('"') < 0) {
444
- // No quotation needed
445
- return arg;
446
- }
447
- if (arg.indexOf('"') < 0 && arg.indexOf('\\') < 0) {
448
- // No embedded double quotes or backslashes, so I can just wrap
449
- // quote marks around the whole thing.
450
- return "\"" + arg + "\"";
451
- }
452
- // Expected input/output:
453
- // input : hello"world
454
- // output: "hello\"world"
455
- // input : hello""world
456
- // output: "hello\"\"world"
457
- // input : hello\world
458
- // output: hello\world
459
- // input : hello\\world
460
- // output: hello\\world
461
- // input : hello\"world
462
- // output: "hello\\\"world"
463
- // input : hello\\"world
464
- // output: "hello\\\\\"world"
465
- // input : hello world\
466
- // output: "hello world\\" - note the comment in libuv actually reads "hello world\"
467
- // but it appears the comment is wrong, it should be "hello world\\"
468
- var reverse = '"';
469
- var quote_hit = true;
470
- for (var i = arg.length; i > 0; i--) { // walk the string in reverse
471
- reverse += arg[i - 1];
472
- if (quote_hit && arg[i - 1] == '\\') {
473
- reverse += '\\';
474
- }
475
- else if (arg[i - 1] == '"') {
476
- quote_hit = true;
477
- reverse += '\\';
478
- }
479
- else {
480
- quote_hit = false;
481
- }
482
- }
483
- reverse += '"';
484
- return reverse.split('').reverse().join('');
485
- };
486
- ToolRunner.prototype._cloneExecOptions = function (options) {
487
- options = options || {};
488
- var result = {
489
- cwd: options.cwd || process.cwd(),
490
- env: options.env || process.env,
491
- silent: options.silent || false,
492
- failOnStdErr: options.failOnStdErr || false,
493
- ignoreReturnCode: options.ignoreReturnCode || false,
494
- windowsVerbatimArguments: options.windowsVerbatimArguments || false,
495
- shell: options.shell || false
496
- };
497
- result.outStream = options.outStream || process.stdout;
498
- result.errStream = options.errStream || process.stderr;
499
- return result;
500
- };
501
- ToolRunner.prototype._getSpawnOptions = function (options) {
502
- options = options || {};
503
- var result = {};
504
- result.cwd = options.cwd;
505
- result.env = options.env;
506
- result.shell = options.shell;
507
- result['windowsVerbatimArguments'] = options.windowsVerbatimArguments || this._isCmdFile();
508
- return result;
509
- };
510
- ToolRunner.prototype._getSpawnSyncOptions = function (options) {
511
- var result = {};
512
- result.cwd = options.cwd;
513
- result.env = options.env;
514
- result.shell = options.shell;
515
- result['windowsVerbatimArguments'] = options.windowsVerbatimArguments || this._isCmdFile();
516
- return result;
517
- };
518
- ToolRunner.prototype.execWithPiping = function (pipeOutputToTool, options) {
519
- var _this = this;
520
- var _a, _b, _c, _d;
521
- var defer = Q.defer();
522
- this._debug('exec tool: ' + this.toolPath);
523
- this._debug('arguments:');
524
- this.args.forEach(function (arg) {
525
- _this._debug(' ' + arg);
526
- });
527
- var success = true;
528
- var optionsNonNull = this._cloneExecOptions(options);
529
- if (!optionsNonNull.silent) {
530
- optionsNonNull.outStream.write(this._getCommandString(optionsNonNull) + os.EOL);
531
- }
532
- var cp;
533
- var toolPath = pipeOutputToTool.toolPath;
534
- var toolPathFirst;
535
- var successFirst = true;
536
- var returnCodeFirst;
537
- var fileStream;
538
- var waitingEvents = 0; // number of process or stream events we are waiting on to complete
539
- var returnCode = 0;
540
- var error;
541
- toolPathFirst = this.toolPath;
542
- // Following node documentation example from this link on how to pipe output of one process to another
543
- // https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options
544
- //start the child process for both tools
545
- waitingEvents++;
546
- var cpFirst = child.spawn(this._getSpawnFileName(optionsNonNull), this._getSpawnArgs(optionsNonNull), this._getSpawnOptions(optionsNonNull));
547
- waitingEvents++;
548
- cp = child.spawn(pipeOutputToTool._getSpawnFileName(optionsNonNull), pipeOutputToTool._getSpawnArgs(optionsNonNull), pipeOutputToTool._getSpawnOptions(optionsNonNull));
549
- fileStream = this.pipeOutputToFile ? fs.createWriteStream(this.pipeOutputToFile) : null;
550
- if (fileStream) {
551
- waitingEvents++;
552
- fileStream.on('finish', function () {
553
- waitingEvents--; //file write is complete
554
- fileStream = null;
555
- if (waitingEvents == 0) {
556
- if (error) {
557
- defer.reject(error);
558
- }
559
- else {
560
- defer.resolve(returnCode);
561
- }
562
- }
563
- });
564
- fileStream.on('error', function (err) {
565
- waitingEvents--; //there were errors writing to the file, write is done
566
- _this._debug("Failed to pipe output of " + toolPathFirst + " to file " + _this.pipeOutputToFile + ". Error = " + err);
567
- fileStream = null;
568
- if (waitingEvents == 0) {
569
- if (error) {
570
- defer.reject(error);
571
- }
572
- else {
573
- defer.resolve(returnCode);
574
- }
575
- }
576
- });
577
- }
578
- //pipe stdout of first tool to stdin of second tool
579
- (_a = cpFirst.stdout) === null || _a === void 0 ? void 0 : _a.on('data', function (data) {
580
- var _a;
581
- try {
582
- if (fileStream) {
583
- fileStream.write(data);
584
- }
585
- (_a = cp.stdin) === null || _a === void 0 ? void 0 : _a.write(data);
586
- }
587
- catch (err) {
588
- _this._debug('Failed to pipe output of ' + toolPathFirst + ' to ' + toolPath);
589
- _this._debug(toolPath + ' might have exited due to errors prematurely. Verify the arguments passed are valid.');
590
- }
591
- });
592
- (_b = cpFirst.stderr) === null || _b === void 0 ? void 0 : _b.on('data', function (data) {
593
- if (fileStream) {
594
- fileStream.write(data);
595
- }
596
- successFirst = !optionsNonNull.failOnStdErr;
597
- if (!optionsNonNull.silent) {
598
- var s = optionsNonNull.failOnStdErr ? optionsNonNull.errStream : optionsNonNull.outStream;
599
- s.write(data);
600
- }
601
- });
602
- cpFirst.on('error', function (err) {
603
- var _a;
604
- waitingEvents--; //first process is complete with errors
605
- if (fileStream) {
606
- fileStream.end();
607
- }
608
- (_a = cp.stdin) === null || _a === void 0 ? void 0 : _a.end();
609
- error = new Error(toolPathFirst + ' failed. ' + err.message);
610
- if (waitingEvents == 0) {
611
- defer.reject(error);
612
- }
613
- });
614
- cpFirst.on('close', function (code, signal) {
615
- var _a;
616
- waitingEvents--; //first process is complete
617
- if (code != 0 && !optionsNonNull.ignoreReturnCode) {
618
- successFirst = false;
619
- returnCodeFirst = code;
620
- returnCode = returnCodeFirst;
621
- }
622
- _this._debug('success of first tool:' + successFirst);
623
- if (fileStream) {
624
- fileStream.end();
625
- }
626
- (_a = cp.stdin) === null || _a === void 0 ? void 0 : _a.end();
627
- if (waitingEvents == 0) {
628
- if (error) {
629
- defer.reject(error);
630
- }
631
- else {
632
- defer.resolve(returnCode);
633
- }
634
- }
635
- });
636
- var stdbuffer = '';
637
- (_c = cp.stdout) === null || _c === void 0 ? void 0 : _c.on('data', function (data) {
638
- _this.emit('stdout', data);
639
- if (!optionsNonNull.silent) {
640
- optionsNonNull.outStream.write(data);
641
- }
642
- _this._processLineBuffer(data, stdbuffer, function (line) {
643
- _this.emit('stdline', line);
644
- });
645
- });
646
- var errbuffer = '';
647
- (_d = cp.stderr) === null || _d === void 0 ? void 0 : _d.on('data', function (data) {
648
- _this.emit('stderr', data);
649
- success = !optionsNonNull.failOnStdErr;
650
- if (!optionsNonNull.silent) {
651
- var s = optionsNonNull.failOnStdErr ? optionsNonNull.errStream : optionsNonNull.outStream;
652
- s.write(data);
653
- }
654
- _this._processLineBuffer(data, errbuffer, function (line) {
655
- _this.emit('errline', line);
656
- });
657
- });
658
- cp.on('error', function (err) {
659
- waitingEvents--; //process is done with errors
660
- error = new Error(toolPath + ' failed. ' + err.message);
661
- if (waitingEvents == 0) {
662
- defer.reject(error);
663
- }
664
- });
665
- cp.on('close', function (code, signal) {
666
- waitingEvents--; //process is complete
667
- _this._debug('rc:' + code);
668
- returnCode = code;
669
- if (stdbuffer.length > 0) {
670
- _this.emit('stdline', stdbuffer);
671
- }
672
- if (errbuffer.length > 0) {
673
- _this.emit('errline', errbuffer);
674
- }
675
- if (code != 0 && !optionsNonNull.ignoreReturnCode) {
676
- success = false;
677
- }
678
- _this._debug('success:' + success);
679
- if (!successFirst) { //in the case output is piped to another tool, check exit code of both tools
680
- error = new Error(toolPathFirst + ' failed with return code: ' + returnCodeFirst);
681
- }
682
- else if (!success) {
683
- error = new Error(toolPath + ' failed with return code: ' + code);
684
- }
685
- if (waitingEvents == 0) {
686
- if (error) {
687
- defer.reject(error);
688
- }
689
- else {
690
- defer.resolve(returnCode);
691
- }
692
- }
693
- });
694
- return defer.promise;
695
- };
696
- /**
697
- * Add argument
698
- * Append an argument or an array of arguments
699
- * returns ToolRunner for chaining
700
- *
701
- * @param val string cmdline or array of strings
702
- * @returns ToolRunner
703
- */
704
- ToolRunner.prototype.arg = function (val) {
705
- if (!val) {
706
- return this;
707
- }
708
- if (val instanceof Array) {
709
- this._debug(this.toolPath + ' arg: ' + JSON.stringify(val));
710
- this.args = this.args.concat(val);
711
- }
712
- else if (typeof (val) === 'string') {
713
- this._debug(this.toolPath + ' arg: ' + val);
714
- this.args = this.args.concat(val.trim());
715
- }
716
- return this;
717
- };
718
- /**
719
- * Parses an argument line into one or more arguments
720
- * e.g. .line('"arg one" two -z') is equivalent to .arg(['arg one', 'two', '-z'])
721
- * returns ToolRunner for chaining
722
- *
723
- * @param val string argument line
724
- * @returns ToolRunner
725
- */
726
- ToolRunner.prototype.line = function (val) {
727
- if (!val) {
728
- return this;
729
- }
730
- this._debug(this.toolPath + ' arg: ' + val);
731
- this.args = this.args.concat(this._argStringToArray(val));
732
- return this;
733
- };
734
- /**
735
- * Add argument(s) if a condition is met
736
- * Wraps arg(). See arg for details
737
- * returns ToolRunner for chaining
738
- *
739
- * @param condition boolean condition
740
- * @param val string cmdline or array of strings
741
- * @returns ToolRunner
742
- */
743
- ToolRunner.prototype.argIf = function (condition, val) {
744
- if (condition) {
745
- this.arg(val);
746
- }
747
- return this;
748
- };
749
- /**
750
- * Pipe output of exec() to another tool
751
- * @param tool
752
- * @param file optional filename to additionally stream the output to.
753
- * @returns {ToolRunner}
754
- */
755
- ToolRunner.prototype.pipeExecOutputToTool = function (tool, file) {
756
- this.pipeOutputToTool = tool;
757
- this.pipeOutputToFile = file;
758
- return this;
759
- };
760
- /**
761
- * Exec a tool.
762
- * Output will be streamed to the live console.
763
- * Returns promise with return code
764
- *
765
- * @param tool path to tool to exec
766
- * @param options optional exec options. See IExecOptions
767
- * @returns number
768
- */
769
- ToolRunner.prototype.exec = function (options) {
770
- var _this = this;
771
- var _a, _b, _c;
772
- if (this.pipeOutputToTool) {
773
- return this.execWithPiping(this.pipeOutputToTool, options);
774
- }
775
- var defer = Q.defer();
776
- this._debug('exec tool: ' + this.toolPath);
777
- this._debug('arguments:');
778
- this.args.forEach(function (arg) {
779
- _this._debug(' ' + arg);
780
- });
781
- var optionsNonNull = this._cloneExecOptions(options);
782
- if (!optionsNonNull.silent) {
783
- optionsNonNull.outStream.write(this._getCommandString(optionsNonNull) + os.EOL);
784
- }
785
- var state = new ExecState(optionsNonNull, this.toolPath);
786
- state.on('debug', function (message) {
787
- _this._debug(message);
788
- });
789
- var cp = child.spawn(this._getSpawnFileName(options), this._getSpawnArgs(optionsNonNull), this._getSpawnOptions(options));
790
- this.childProcess = cp;
791
- // it is possible for the child process to end its last line without a new line.
792
- // because stdout is buffered, this causes the last line to not get sent to the parent
793
- // stream. Adding this event forces a flush before the child streams are closed.
794
- (_a = cp.stdout) === null || _a === void 0 ? void 0 : _a.on('finish', function () {
795
- if (!optionsNonNull.silent) {
796
- optionsNonNull.outStream.write(os.EOL);
797
- }
798
- });
799
- var stdbuffer = '';
800
- (_b = cp.stdout) === null || _b === void 0 ? void 0 : _b.on('data', function (data) {
801
- _this.emit('stdout', data);
802
- if (!optionsNonNull.silent) {
803
- optionsNonNull.outStream.write(data);
804
- }
805
- _this._processLineBuffer(data, stdbuffer, function (line) {
806
- _this.emit('stdline', line);
807
- });
808
- });
809
- var errbuffer = '';
810
- (_c = cp.stderr) === null || _c === void 0 ? void 0 : _c.on('data', function (data) {
811
- state.processStderr = true;
812
- _this.emit('stderr', data);
813
- if (!optionsNonNull.silent) {
814
- var s = optionsNonNull.failOnStdErr ? optionsNonNull.errStream : optionsNonNull.outStream;
815
- s.write(data);
816
- }
817
- _this._processLineBuffer(data, errbuffer, function (line) {
818
- _this.emit('errline', line);
819
- });
820
- });
821
- cp.on('error', function (err) {
822
- state.processError = err.message;
823
- state.processExited = true;
824
- state.processClosed = true;
825
- state.CheckComplete();
826
- });
827
- cp.on('exit', function (code, signal) {
828
- state.processExitCode = code;
829
- state.processExited = true;
830
- _this._debug("Exit code " + code + " received from tool '" + _this.toolPath + "'");
831
- state.CheckComplete();
832
- });
833
- cp.on('close', function (code, signal) {
834
- state.processExitCode = code;
835
- state.processExited = true;
836
- state.processClosed = true;
837
- _this._debug("STDIO streams have closed for tool '" + _this.toolPath + "'");
838
- state.CheckComplete();
839
- });
840
- state.on('done', function (error, exitCode) {
841
- if (stdbuffer.length > 0) {
842
- _this.emit('stdline', stdbuffer);
843
- }
844
- if (errbuffer.length > 0) {
845
- _this.emit('errline', errbuffer);
846
- }
847
- cp.removeAllListeners();
848
- if (error) {
849
- defer.reject(error);
850
- }
851
- else {
852
- defer.resolve(exitCode);
853
- }
854
- });
855
- return defer.promise;
856
- };
857
- /**
858
- * Exec a tool synchronously.
859
- * Output will be *not* be streamed to the live console. It will be returned after execution is complete.
860
- * Appropriate for short running tools
861
- * Returns IExecSyncResult with output and return code
862
- *
863
- * @param tool path to tool to exec
864
- * @param options optional exec options. See IExecSyncOptions
865
- * @returns IExecSyncResult
866
- */
867
- ToolRunner.prototype.execSync = function (options) {
868
- var _this = this;
869
- this._debug('exec tool: ' + this.toolPath);
870
- this._debug('arguments:');
871
- this.args.forEach(function (arg) {
872
- _this._debug(' ' + arg);
873
- });
874
- var success = true;
875
- options = this._cloneExecOptions(options);
876
- if (!options.silent) {
877
- options.outStream.write(this._getCommandString(options) + os.EOL);
878
- }
879
- var r = child.spawnSync(this._getSpawnFileName(options), this._getSpawnArgs(options), this._getSpawnSyncOptions(options));
880
- if (!options.silent && r.stdout && r.stdout.length > 0) {
881
- options.outStream.write(r.stdout);
882
- }
883
- if (!options.silent && r.stderr && r.stderr.length > 0) {
884
- options.errStream.write(r.stderr);
885
- }
886
- var res = { code: r.status, error: r.error };
887
- res.stdout = (r.stdout) ? r.stdout.toString() : '';
888
- res.stderr = (r.stderr) ? r.stderr.toString() : '';
889
- return res;
890
- };
891
- /**
892
- * Used to close child process by sending SIGNINT signal.
893
- * It allows executed script to have some additional logic on SIGINT, before exiting.
894
- */
895
- ToolRunner.prototype.killChildProcess = function () {
896
- if (this.childProcess) {
897
- this.childProcess.kill();
898
- }
899
- };
900
- return ToolRunner;
901
- }(events.EventEmitter));
902
- exports.ToolRunner = ToolRunner;
903
- var ExecState = /** @class */ (function (_super) {
904
- __extends(ExecState, _super);
905
- function ExecState(options, toolPath) {
906
- var _this = _super.call(this) || this;
907
- _this.delay = 10000; // 10 seconds
908
- _this.timeout = null;
909
- if (!toolPath) {
910
- throw new Error('toolPath must not be empty');
911
- }
912
- _this.options = options;
913
- _this.toolPath = toolPath;
914
- var delay = process.env['TASKLIB_TEST_TOOLRUNNER_EXITDELAY'];
915
- if (delay) {
916
- _this.delay = parseInt(delay);
917
- }
918
- return _this;
919
- }
920
- ExecState.prototype.CheckComplete = function () {
921
- if (this.done) {
922
- return;
923
- }
924
- if (this.processClosed) {
925
- this._setResult();
926
- }
927
- else if (this.processExited) {
928
- this.timeout = setTimeout(ExecState.HandleTimeout, this.delay, this);
929
- }
930
- };
931
- ExecState.prototype._debug = function (message) {
932
- this.emit('debug', message);
933
- };
934
- ExecState.prototype._setResult = function () {
935
- // determine whether there is an error
936
- var error;
937
- if (this.processExited) {
938
- if (this.processError) {
939
- error = new Error(im._loc('LIB_ProcessError', this.toolPath, this.processError));
940
- }
941
- else if (this.processExitCode != 0 && !this.options.ignoreReturnCode) {
942
- error = new Error(im._loc('LIB_ProcessExitCode', this.toolPath, this.processExitCode));
943
- }
944
- else if (this.processStderr && this.options.failOnStdErr) {
945
- error = new Error(im._loc('LIB_ProcessStderr', this.toolPath));
946
- }
947
- }
948
- // clear the timeout
949
- if (this.timeout) {
950
- clearTimeout(this.timeout);
951
- this.timeout = null;
952
- }
953
- this.done = true;
954
- this.emit('done', error, this.processExitCode);
955
- };
956
- ExecState.HandleTimeout = function (state) {
957
- if (state.done) {
958
- return;
959
- }
960
- if (!state.processClosed && state.processExited) {
961
- console.log(im._loc('LIB_StdioNotClosed', state.delay / 1000, state.toolPath));
962
- state._debug(im._loc('LIB_StdioNotClosed', state.delay / 1000, state.toolPath));
963
- }
964
- state._setResult();
965
- };
966
- return ExecState;
967
- }(events.EventEmitter));
1
+ "use strict";
2
+ var __extends = (this && this.__extends) || (function () {
3
+ var extendStatics = function (d, b) {
4
+ extendStatics = Object.setPrototypeOf ||
5
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
6
+ function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
7
+ return extendStatics(d, b);
8
+ };
9
+ return function (d, b) {
10
+ extendStatics(d, b);
11
+ function __() { this.constructor = d; }
12
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
13
+ };
14
+ })();
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.ToolRunner = void 0;
17
+ var Q = require("q");
18
+ var os = require("os");
19
+ var events = require("events");
20
+ var child = require("child_process");
21
+ var im = require("./internal");
22
+ var fs = require("fs");
23
+ var ToolRunner = /** @class */ (function (_super) {
24
+ __extends(ToolRunner, _super);
25
+ function ToolRunner(toolPath) {
26
+ var _this = _super.call(this) || this;
27
+ _this.cmdSpecialChars = [' ', '\t', '&', '(', ')', '[', ']', '{', '}', '^', '=', ';', '!', '\'', '+', ',', '`', '~', '|', '<', '>', '"'];
28
+ if (!toolPath) {
29
+ throw new Error('Parameter \'toolPath\' cannot be null or empty.');
30
+ }
31
+ _this.toolPath = im._which(toolPath, true);
32
+ _this.args = [];
33
+ _this._debug('toolRunner toolPath: ' + toolPath);
34
+ return _this;
35
+ }
36
+ ToolRunner.prototype._debug = function (message) {
37
+ this.emit('debug', message);
38
+ };
39
+ ToolRunner.prototype._argStringToArray = function (argString) {
40
+ var args = [];
41
+ var inQuotes = false;
42
+ var escaped = false;
43
+ var lastCharWasSpace = true;
44
+ var arg = '';
45
+ var append = function (c) {
46
+ // we only escape double quotes.
47
+ if (escaped) {
48
+ if (c !== '"') {
49
+ arg += '\\';
50
+ }
51
+ else {
52
+ arg.slice(0, -1);
53
+ }
54
+ }
55
+ arg += c;
56
+ escaped = false;
57
+ };
58
+ for (var i = 0; i < argString.length; i++) {
59
+ var c = argString.charAt(i);
60
+ if (c === ' ' && !inQuotes) {
61
+ if (!lastCharWasSpace) {
62
+ args.push(arg);
63
+ arg = '';
64
+ }
65
+ lastCharWasSpace = true;
66
+ continue;
67
+ }
68
+ else {
69
+ lastCharWasSpace = false;
70
+ }
71
+ if (c === '"') {
72
+ if (!escaped) {
73
+ inQuotes = !inQuotes;
74
+ }
75
+ else {
76
+ append(c);
77
+ }
78
+ continue;
79
+ }
80
+ if (c === "\\" && escaped) {
81
+ append(c);
82
+ continue;
83
+ }
84
+ if (c === "\\" && inQuotes) {
85
+ escaped = true;
86
+ continue;
87
+ }
88
+ append(c);
89
+ lastCharWasSpace = false;
90
+ }
91
+ if (!lastCharWasSpace) {
92
+ args.push(arg.trim());
93
+ }
94
+ return args;
95
+ };
96
+ ToolRunner.prototype._getCommandString = function (options, noPrefix) {
97
+ var _this = this;
98
+ var toolPath = this._getSpawnFileName();
99
+ var args = this._getSpawnArgs(options);
100
+ var cmd = noPrefix ? '' : '[command]'; // omit prefix when piped to a second tool
101
+ var commandParts = [];
102
+ if (process.platform == 'win32') {
103
+ // Windows + cmd file
104
+ if (this._isCmdFile()) {
105
+ commandParts.push(toolPath);
106
+ commandParts = commandParts.concat(args);
107
+ }
108
+ // Windows + verbatim
109
+ else if (options.windowsVerbatimArguments) {
110
+ commandParts.push("\"" + toolPath + "\"");
111
+ commandParts = commandParts.concat(args);
112
+ }
113
+ else if (options.shell) {
114
+ commandParts.push(this._windowsQuoteCmdArg(toolPath));
115
+ commandParts = commandParts.concat(args);
116
+ }
117
+ // Windows (regular)
118
+ else {
119
+ commandParts.push(this._windowsQuoteCmdArg(toolPath));
120
+ commandParts = commandParts.concat(args.map(function (arg) { return _this._windowsQuoteCmdArg(arg); }));
121
+ }
122
+ }
123
+ else {
124
+ // OSX/Linux - this can likely be improved with some form of quoting.
125
+ // creating processes on Unix is fundamentally different than Windows.
126
+ // on Unix, execvp() takes an arg array.
127
+ commandParts.push(toolPath);
128
+ commandParts = commandParts.concat(args);
129
+ }
130
+ cmd += commandParts.join(' ');
131
+ // append second tool
132
+ if (this.pipeOutputToTool) {
133
+ cmd += ' | ' + this.pipeOutputToTool._getCommandString(options, /*noPrefix:*/ true);
134
+ }
135
+ return cmd;
136
+ };
137
+ ToolRunner.prototype._processLineBuffer = function (data, strBuffer, onLine) {
138
+ try {
139
+ var s = strBuffer + data.toString();
140
+ var n = s.indexOf(os.EOL);
141
+ while (n > -1) {
142
+ var line = s.substring(0, n);
143
+ onLine(line);
144
+ // the rest of the string ...
145
+ s = s.substring(n + os.EOL.length);
146
+ n = s.indexOf(os.EOL);
147
+ }
148
+ strBuffer = s;
149
+ }
150
+ catch (err) {
151
+ // streaming lines to console is best effort. Don't fail a build.
152
+ this._debug('error processing line');
153
+ }
154
+ };
155
+ /**
156
+ * Wraps an arg string with specified char if it's not already wrapped
157
+ * @returns {string} Arg wrapped with specified char
158
+ * @param {string} arg Input argument string
159
+ * @param {string} wrapChar A char input string should be wrapped with
160
+ */
161
+ ToolRunner.prototype._wrapArg = function (arg, wrapChar) {
162
+ if (!this._isWrapped(arg, wrapChar)) {
163
+ return "" + wrapChar + arg + wrapChar;
164
+ }
165
+ return arg;
166
+ };
167
+ /**
168
+ * Unwraps an arg string wrapped with specified char
169
+ * @param arg Arg wrapped with specified char
170
+ * @param wrapChar A char to be removed
171
+ */
172
+ ToolRunner.prototype._unwrapArg = function (arg, wrapChar) {
173
+ if (this._isWrapped(arg, wrapChar)) {
174
+ var pattern = new RegExp("(^\\\\?" + wrapChar + ")|(\\\\?" + wrapChar + "$)", 'g');
175
+ return arg.trim().replace(pattern, '');
176
+ }
177
+ return arg;
178
+ };
179
+ /**
180
+ * Determine if arg string is wrapped with specified char
181
+ * @param arg Input arg string
182
+ */
183
+ ToolRunner.prototype._isWrapped = function (arg, wrapChar) {
184
+ var pattern = new RegExp("^\\\\?" + wrapChar + ".+\\\\?" + wrapChar + "$");
185
+ return pattern.test(arg.trim());
186
+ };
187
+ ToolRunner.prototype._getSpawnFileName = function (options) {
188
+ if (process.platform == 'win32') {
189
+ if (this._isCmdFile()) {
190
+ return process.env['COMSPEC'] || 'cmd.exe';
191
+ }
192
+ }
193
+ if (options && options.shell) {
194
+ return this._wrapArg(this.toolPath, '"');
195
+ }
196
+ return this.toolPath;
197
+ };
198
+ ToolRunner.prototype._getSpawnArgs = function (options) {
199
+ var _this = this;
200
+ if (process.platform == 'win32') {
201
+ if (this._isCmdFile()) {
202
+ var argline = "/D /S /C \"" + this._windowsQuoteCmdArg(this.toolPath);
203
+ for (var i = 0; i < this.args.length; i++) {
204
+ argline += ' ';
205
+ argline += options.windowsVerbatimArguments ? this.args[i] : this._windowsQuoteCmdArg(this.args[i]);
206
+ }
207
+ argline += '"';
208
+ return [argline];
209
+ }
210
+ if (options.windowsVerbatimArguments) {
211
+ // note, in Node 6.x options.argv0 can be used instead of overriding args.slice and args.unshift.
212
+ // for more details, refer to https://github.com/nodejs/node/blob/v6.x/lib/child_process.js
213
+ var args_1 = this.args.slice(0); // copy the array
214
+ // override slice to prevent Node from creating a copy of the arg array.
215
+ // we need Node to use the "unshift" override below.
216
+ args_1.slice = function () {
217
+ if (arguments.length != 1 || arguments[0] != 0) {
218
+ throw new Error('Unexpected arguments passed to args.slice when windowsVerbatimArguments flag is set.');
219
+ }
220
+ return args_1;
221
+ };
222
+ // override unshift
223
+ //
224
+ // when using the windowsVerbatimArguments option, Node does not quote the tool path when building
225
+ // the cmdline parameter for the win32 function CreateProcess(). an unquoted space in the tool path
226
+ // causes problems for tools when attempting to parse their own command line args. tools typically
227
+ // assume their arguments begin after arg 0.
228
+ //
229
+ // by hijacking unshift, we can quote the tool path when it pushed onto the args array. Node builds
230
+ // the cmdline parameter from the args array.
231
+ //
232
+ // note, we can't simply pass a quoted tool path to Node for multiple reasons:
233
+ // 1) Node verifies the file exists (calls win32 function GetFileAttributesW) and the check returns
234
+ // false if the path is quoted.
235
+ // 2) Node passes the tool path as the application parameter to CreateProcess, which expects the
236
+ // path to be unquoted.
237
+ //
238
+ // also note, in addition to the tool path being embedded within the cmdline parameter, Node also
239
+ // passes the tool path to CreateProcess via the application parameter (optional parameter). when
240
+ // present, Windows uses the application parameter to determine which file to run, instead of
241
+ // interpreting the file from the cmdline parameter.
242
+ args_1.unshift = function () {
243
+ if (arguments.length != 1) {
244
+ throw new Error('Unexpected arguments passed to args.unshift when windowsVerbatimArguments flag is set.');
245
+ }
246
+ return Array.prototype.unshift.call(args_1, "\"" + arguments[0] + "\""); // quote the file name
247
+ };
248
+ return args_1;
249
+ }
250
+ else if (options.shell) {
251
+ var args = [];
252
+ for (var _i = 0, _a = this.args; _i < _a.length; _i++) {
253
+ var arg = _a[_i];
254
+ if (this._needQuotesForCmd(arg, '%')) {
255
+ args.push(this._wrapArg(arg, '"'));
256
+ }
257
+ else {
258
+ args.push(arg);
259
+ }
260
+ }
261
+ return args;
262
+ }
263
+ }
264
+ else if (options.shell) {
265
+ return this.args.map(function (arg) {
266
+ if (_this._isWrapped(arg, "'")) {
267
+ return arg;
268
+ }
269
+ // remove wrapping double quotes to avoid escaping
270
+ arg = _this._unwrapArg(arg, '"');
271
+ arg = _this._escapeChar(arg, '"');
272
+ return _this._wrapArg(arg, '"');
273
+ });
274
+ }
275
+ return this.args;
276
+ };
277
+ /**
278
+ * Escape specified character.
279
+ * @param arg String to escape char in
280
+ * @param charToEscape Char should be escaped
281
+ */
282
+ ToolRunner.prototype._escapeChar = function (arg, charToEscape) {
283
+ var escChar = "\\";
284
+ var output = '';
285
+ var charIsEscaped = false;
286
+ for (var _i = 0, arg_1 = arg; _i < arg_1.length; _i++) {
287
+ var char = arg_1[_i];
288
+ if (char === charToEscape && !charIsEscaped) {
289
+ output += escChar + char;
290
+ }
291
+ else {
292
+ output += char;
293
+ }
294
+ charIsEscaped = char === escChar && !charIsEscaped;
295
+ }
296
+ return output;
297
+ };
298
+ ToolRunner.prototype._isCmdFile = function () {
299
+ var upperToolPath = this.toolPath.toUpperCase();
300
+ return im._endsWith(upperToolPath, '.CMD') || im._endsWith(upperToolPath, '.BAT');
301
+ };
302
+ /**
303
+ * Determine whether the cmd arg needs to be quoted. Returns true if arg contains any of special chars array.
304
+ * @param arg The cmd command arg.
305
+ * @param additionalChars Additional chars which should be also checked.
306
+ */
307
+ ToolRunner.prototype._needQuotesForCmd = function (arg, additionalChars) {
308
+ var specialChars = this.cmdSpecialChars;
309
+ if (additionalChars) {
310
+ specialChars = this.cmdSpecialChars.concat(additionalChars);
311
+ }
312
+ var _loop_1 = function (char) {
313
+ if (specialChars.some(function (x) { return x === char; })) {
314
+ return { value: true };
315
+ }
316
+ };
317
+ for (var _i = 0, arg_2 = arg; _i < arg_2.length; _i++) {
318
+ var char = arg_2[_i];
319
+ var state_1 = _loop_1(char);
320
+ if (typeof state_1 === "object")
321
+ return state_1.value;
322
+ }
323
+ return false;
324
+ };
325
+ ToolRunner.prototype._windowsQuoteCmdArg = function (arg) {
326
+ // for .exe, apply the normal quoting rules that libuv applies
327
+ if (!this._isCmdFile()) {
328
+ return this._uv_quote_cmd_arg(arg);
329
+ }
330
+ // otherwise apply quoting rules specific to the cmd.exe command line parser.
331
+ // the libuv rules are generic and are not designed specifically for cmd.exe
332
+ // command line parser.
333
+ //
334
+ // for a detailed description of the cmd.exe command line parser, refer to
335
+ // http://stackoverflow.com/questions/4094699/how-does-the-windows-command-interpreter-cmd-exe-parse-scripts/7970912#7970912
336
+ // need quotes for empty arg
337
+ if (!arg) {
338
+ return '""';
339
+ }
340
+ // determine whether the arg needs to be quoted
341
+ var needsQuotes = this._needQuotesForCmd(arg);
342
+ // short-circuit if quotes not needed
343
+ if (!needsQuotes) {
344
+ return arg;
345
+ }
346
+ // the following quoting rules are very similar to the rules that by libuv applies.
347
+ //
348
+ // 1) wrap the string in quotes
349
+ //
350
+ // 2) double-up quotes - i.e. " => ""
351
+ //
352
+ // this is different from the libuv quoting rules. libuv replaces " with \", which unfortunately
353
+ // doesn't work well with a cmd.exe command line.
354
+ //
355
+ // note, replacing " with "" also works well if the arg is passed to a downstream .NET console app.
356
+ // for example, the command line:
357
+ // foo.exe "myarg:""my val"""
358
+ // is parsed by a .NET console app into an arg array:
359
+ // [ "myarg:\"my val\"" ]
360
+ // which is the same end result when applying libuv quoting rules. although the actual
361
+ // command line from libuv quoting rules would look like:
362
+ // foo.exe "myarg:\"my val\""
363
+ //
364
+ // 3) double-up slashes that preceed a quote,
365
+ // e.g. hello \world => "hello \world"
366
+ // hello\"world => "hello\\""world"
367
+ // hello\\"world => "hello\\\\""world"
368
+ // hello world\ => "hello world\\"
369
+ //
370
+ // technically this is not required for a cmd.exe command line, or the batch argument parser.
371
+ // the reasons for including this as a .cmd quoting rule are:
372
+ //
373
+ // a) this is optimized for the scenario where the argument is passed from the .cmd file to an
374
+ // external program. many programs (e.g. .NET console apps) rely on the slash-doubling rule.
375
+ //
376
+ // b) it's what we've been doing previously (by deferring to node default behavior) and we
377
+ // haven't heard any complaints about that aspect.
378
+ //
379
+ // note, a weakness of the quoting rules chosen here, is that % is not escaped. in fact, % cannot be
380
+ // escaped when used on the command line directly - even though within a .cmd file % can be escaped
381
+ // by using %%.
382
+ //
383
+ // the saving grace is, on the command line, %var% is left as-is if var is not defined. this contrasts
384
+ // the line parsing rules within a .cmd file, where if var is not defined it is replaced with nothing.
385
+ //
386
+ // one option that was explored was replacing % with ^% - i.e. %var% => ^%var^%. this hack would
387
+ // often work, since it is unlikely that var^ would exist, and the ^ character is removed when the
388
+ // variable is used. the problem, however, is that ^ is not removed when %* is used to pass the args
389
+ // to an external program.
390
+ //
391
+ // an unexplored potential solution for the % escaping problem, is to create a wrapper .cmd file.
392
+ // % can be escaped within a .cmd file.
393
+ var reverse = '"';
394
+ var quote_hit = true;
395
+ for (var i = arg.length; i > 0; i--) { // walk the string in reverse
396
+ reverse += arg[i - 1];
397
+ if (quote_hit && arg[i - 1] == '\\') {
398
+ reverse += '\\'; // double the slash
399
+ }
400
+ else if (arg[i - 1] == '"') {
401
+ quote_hit = true;
402
+ reverse += '"'; // double the quote
403
+ }
404
+ else {
405
+ quote_hit = false;
406
+ }
407
+ }
408
+ reverse += '"';
409
+ return reverse.split('').reverse().join('');
410
+ };
411
+ ToolRunner.prototype._uv_quote_cmd_arg = function (arg) {
412
+ // Tool runner wraps child_process.spawn() and needs to apply the same quoting as
413
+ // Node in certain cases where the undocumented spawn option windowsVerbatimArguments
414
+ // is used.
415
+ //
416
+ // Since this function is a port of quote_cmd_arg from Node 4.x (technically, lib UV,
417
+ // see https://github.com/nodejs/node/blob/v4.x/deps/uv/src/win/process.c for details),
418
+ // pasting copyright notice from Node within this function:
419
+ //
420
+ // Copyright Joyent, Inc. and other Node contributors. All rights reserved.
421
+ //
422
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
423
+ // of this software and associated documentation files (the "Software"), to
424
+ // deal in the Software without restriction, including without limitation the
425
+ // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
426
+ // sell copies of the Software, and to permit persons to whom the Software is
427
+ // furnished to do so, subject to the following conditions:
428
+ //
429
+ // The above copyright notice and this permission notice shall be included in
430
+ // all copies or substantial portions of the Software.
431
+ //
432
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
433
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
434
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
435
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
436
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
437
+ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
438
+ // IN THE SOFTWARE.
439
+ if (!arg) {
440
+ // Need double quotation for empty argument
441
+ return '""';
442
+ }
443
+ if (arg.indexOf(' ') < 0 && arg.indexOf('\t') < 0 && arg.indexOf('"') < 0) {
444
+ // No quotation needed
445
+ return arg;
446
+ }
447
+ if (arg.indexOf('"') < 0 && arg.indexOf('\\') < 0) {
448
+ // No embedded double quotes or backslashes, so I can just wrap
449
+ // quote marks around the whole thing.
450
+ return "\"" + arg + "\"";
451
+ }
452
+ // Expected input/output:
453
+ // input : hello"world
454
+ // output: "hello\"world"
455
+ // input : hello""world
456
+ // output: "hello\"\"world"
457
+ // input : hello\world
458
+ // output: hello\world
459
+ // input : hello\\world
460
+ // output: hello\\world
461
+ // input : hello\"world
462
+ // output: "hello\\\"world"
463
+ // input : hello\\"world
464
+ // output: "hello\\\\\"world"
465
+ // input : hello world\
466
+ // output: "hello world\\" - note the comment in libuv actually reads "hello world\"
467
+ // but it appears the comment is wrong, it should be "hello world\\"
468
+ var reverse = '"';
469
+ var quote_hit = true;
470
+ for (var i = arg.length; i > 0; i--) { // walk the string in reverse
471
+ reverse += arg[i - 1];
472
+ if (quote_hit && arg[i - 1] == '\\') {
473
+ reverse += '\\';
474
+ }
475
+ else if (arg[i - 1] == '"') {
476
+ quote_hit = true;
477
+ reverse += '\\';
478
+ }
479
+ else {
480
+ quote_hit = false;
481
+ }
482
+ }
483
+ reverse += '"';
484
+ return reverse.split('').reverse().join('');
485
+ };
486
+ ToolRunner.prototype._cloneExecOptions = function (options) {
487
+ options = options || {};
488
+ var result = {
489
+ cwd: options.cwd || process.cwd(),
490
+ env: options.env || process.env,
491
+ silent: options.silent || false,
492
+ failOnStdErr: options.failOnStdErr || false,
493
+ ignoreReturnCode: options.ignoreReturnCode || false,
494
+ windowsVerbatimArguments: options.windowsVerbatimArguments || false,
495
+ shell: options.shell || false
496
+ };
497
+ result.outStream = options.outStream || process.stdout;
498
+ result.errStream = options.errStream || process.stderr;
499
+ return result;
500
+ };
501
+ ToolRunner.prototype._getSpawnOptions = function (options) {
502
+ options = options || {};
503
+ var result = {};
504
+ result.cwd = options.cwd;
505
+ result.env = options.env;
506
+ result.shell = options.shell;
507
+ result['windowsVerbatimArguments'] = options.windowsVerbatimArguments || this._isCmdFile();
508
+ return result;
509
+ };
510
+ ToolRunner.prototype._getSpawnSyncOptions = function (options) {
511
+ var result = {};
512
+ result.cwd = options.cwd;
513
+ result.env = options.env;
514
+ result.shell = options.shell;
515
+ result['windowsVerbatimArguments'] = options.windowsVerbatimArguments || this._isCmdFile();
516
+ return result;
517
+ };
518
+ ToolRunner.prototype.execWithPiping = function (pipeOutputToTool, options) {
519
+ var _this = this;
520
+ var _a, _b, _c, _d;
521
+ var defer = Q.defer();
522
+ this._debug('exec tool: ' + this.toolPath);
523
+ this._debug('arguments:');
524
+ this.args.forEach(function (arg) {
525
+ _this._debug(' ' + arg);
526
+ });
527
+ var success = true;
528
+ var optionsNonNull = this._cloneExecOptions(options);
529
+ if (!optionsNonNull.silent) {
530
+ optionsNonNull.outStream.write(this._getCommandString(optionsNonNull) + os.EOL);
531
+ }
532
+ var cp;
533
+ var toolPath = pipeOutputToTool.toolPath;
534
+ var toolPathFirst;
535
+ var successFirst = true;
536
+ var returnCodeFirst;
537
+ var fileStream;
538
+ var waitingEvents = 0; // number of process or stream events we are waiting on to complete
539
+ var returnCode = 0;
540
+ var error;
541
+ toolPathFirst = this.toolPath;
542
+ // Following node documentation example from this link on how to pipe output of one process to another
543
+ // https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options
544
+ //start the child process for both tools
545
+ waitingEvents++;
546
+ var cpFirst = child.spawn(this._getSpawnFileName(optionsNonNull), this._getSpawnArgs(optionsNonNull), this._getSpawnOptions(optionsNonNull));
547
+ waitingEvents++;
548
+ cp = child.spawn(pipeOutputToTool._getSpawnFileName(optionsNonNull), pipeOutputToTool._getSpawnArgs(optionsNonNull), pipeOutputToTool._getSpawnOptions(optionsNonNull));
549
+ fileStream = this.pipeOutputToFile ? fs.createWriteStream(this.pipeOutputToFile) : null;
550
+ if (fileStream) {
551
+ waitingEvents++;
552
+ fileStream.on('finish', function () {
553
+ waitingEvents--; //file write is complete
554
+ fileStream = null;
555
+ if (waitingEvents == 0) {
556
+ if (error) {
557
+ defer.reject(error);
558
+ }
559
+ else {
560
+ defer.resolve(returnCode);
561
+ }
562
+ }
563
+ });
564
+ fileStream.on('error', function (err) {
565
+ waitingEvents--; //there were errors writing to the file, write is done
566
+ _this._debug("Failed to pipe output of " + toolPathFirst + " to file " + _this.pipeOutputToFile + ". Error = " + err);
567
+ fileStream = null;
568
+ if (waitingEvents == 0) {
569
+ if (error) {
570
+ defer.reject(error);
571
+ }
572
+ else {
573
+ defer.resolve(returnCode);
574
+ }
575
+ }
576
+ });
577
+ }
578
+ //pipe stdout of first tool to stdin of second tool
579
+ (_a = cpFirst.stdout) === null || _a === void 0 ? void 0 : _a.on('data', function (data) {
580
+ var _a;
581
+ try {
582
+ if (fileStream) {
583
+ fileStream.write(data);
584
+ }
585
+ (_a = cp.stdin) === null || _a === void 0 ? void 0 : _a.write(data);
586
+ }
587
+ catch (err) {
588
+ _this._debug('Failed to pipe output of ' + toolPathFirst + ' to ' + toolPath);
589
+ _this._debug(toolPath + ' might have exited due to errors prematurely. Verify the arguments passed are valid.');
590
+ }
591
+ });
592
+ (_b = cpFirst.stderr) === null || _b === void 0 ? void 0 : _b.on('data', function (data) {
593
+ if (fileStream) {
594
+ fileStream.write(data);
595
+ }
596
+ successFirst = !optionsNonNull.failOnStdErr;
597
+ if (!optionsNonNull.silent) {
598
+ var s = optionsNonNull.failOnStdErr ? optionsNonNull.errStream : optionsNonNull.outStream;
599
+ s.write(data);
600
+ }
601
+ });
602
+ cpFirst.on('error', function (err) {
603
+ var _a;
604
+ waitingEvents--; //first process is complete with errors
605
+ if (fileStream) {
606
+ fileStream.end();
607
+ }
608
+ (_a = cp.stdin) === null || _a === void 0 ? void 0 : _a.end();
609
+ error = new Error(toolPathFirst + ' failed. ' + err.message);
610
+ if (waitingEvents == 0) {
611
+ defer.reject(error);
612
+ }
613
+ });
614
+ cpFirst.on('close', function (code, signal) {
615
+ var _a;
616
+ waitingEvents--; //first process is complete
617
+ if (code != 0 && !optionsNonNull.ignoreReturnCode) {
618
+ successFirst = false;
619
+ returnCodeFirst = code;
620
+ returnCode = returnCodeFirst;
621
+ }
622
+ _this._debug('success of first tool:' + successFirst);
623
+ if (fileStream) {
624
+ fileStream.end();
625
+ }
626
+ (_a = cp.stdin) === null || _a === void 0 ? void 0 : _a.end();
627
+ if (waitingEvents == 0) {
628
+ if (error) {
629
+ defer.reject(error);
630
+ }
631
+ else {
632
+ defer.resolve(returnCode);
633
+ }
634
+ }
635
+ });
636
+ var stdbuffer = '';
637
+ (_c = cp.stdout) === null || _c === void 0 ? void 0 : _c.on('data', function (data) {
638
+ _this.emit('stdout', data);
639
+ if (!optionsNonNull.silent) {
640
+ optionsNonNull.outStream.write(data);
641
+ }
642
+ _this._processLineBuffer(data, stdbuffer, function (line) {
643
+ _this.emit('stdline', line);
644
+ });
645
+ });
646
+ var errbuffer = '';
647
+ (_d = cp.stderr) === null || _d === void 0 ? void 0 : _d.on('data', function (data) {
648
+ _this.emit('stderr', data);
649
+ success = !optionsNonNull.failOnStdErr;
650
+ if (!optionsNonNull.silent) {
651
+ var s = optionsNonNull.failOnStdErr ? optionsNonNull.errStream : optionsNonNull.outStream;
652
+ s.write(data);
653
+ }
654
+ _this._processLineBuffer(data, errbuffer, function (line) {
655
+ _this.emit('errline', line);
656
+ });
657
+ });
658
+ cp.on('error', function (err) {
659
+ waitingEvents--; //process is done with errors
660
+ error = new Error(toolPath + ' failed. ' + err.message);
661
+ if (waitingEvents == 0) {
662
+ defer.reject(error);
663
+ }
664
+ });
665
+ cp.on('close', function (code, signal) {
666
+ waitingEvents--; //process is complete
667
+ _this._debug('rc:' + code);
668
+ returnCode = code;
669
+ if (stdbuffer.length > 0) {
670
+ _this.emit('stdline', stdbuffer);
671
+ }
672
+ if (errbuffer.length > 0) {
673
+ _this.emit('errline', errbuffer);
674
+ }
675
+ if (code != 0 && !optionsNonNull.ignoreReturnCode) {
676
+ success = false;
677
+ }
678
+ _this._debug('success:' + success);
679
+ if (!successFirst) { //in the case output is piped to another tool, check exit code of both tools
680
+ error = new Error(toolPathFirst + ' failed with return code: ' + returnCodeFirst);
681
+ }
682
+ else if (!success) {
683
+ error = new Error(toolPath + ' failed with return code: ' + code);
684
+ }
685
+ if (waitingEvents == 0) {
686
+ if (error) {
687
+ defer.reject(error);
688
+ }
689
+ else {
690
+ defer.resolve(returnCode);
691
+ }
692
+ }
693
+ });
694
+ return defer.promise;
695
+ };
696
+ /**
697
+ * Add argument
698
+ * Append an argument or an array of arguments
699
+ * returns ToolRunner for chaining
700
+ *
701
+ * @param val string cmdline or array of strings
702
+ * @returns ToolRunner
703
+ */
704
+ ToolRunner.prototype.arg = function (val) {
705
+ if (!val) {
706
+ return this;
707
+ }
708
+ if (val instanceof Array) {
709
+ this._debug(this.toolPath + ' arg: ' + JSON.stringify(val));
710
+ this.args = this.args.concat(val);
711
+ }
712
+ else if (typeof (val) === 'string') {
713
+ this._debug(this.toolPath + ' arg: ' + val);
714
+ this.args = this.args.concat(val.trim());
715
+ }
716
+ return this;
717
+ };
718
+ /**
719
+ * Parses an argument line into one or more arguments
720
+ * e.g. .line('"arg one" two -z') is equivalent to .arg(['arg one', 'two', '-z'])
721
+ * returns ToolRunner for chaining
722
+ *
723
+ * @param val string argument line
724
+ * @returns ToolRunner
725
+ */
726
+ ToolRunner.prototype.line = function (val) {
727
+ if (!val) {
728
+ return this;
729
+ }
730
+ this._debug(this.toolPath + ' arg: ' + val);
731
+ this.args = this.args.concat(this._argStringToArray(val));
732
+ return this;
733
+ };
734
+ /**
735
+ * Add argument(s) if a condition is met
736
+ * Wraps arg(). See arg for details
737
+ * returns ToolRunner for chaining
738
+ *
739
+ * @param condition boolean condition
740
+ * @param val string cmdline or array of strings
741
+ * @returns ToolRunner
742
+ */
743
+ ToolRunner.prototype.argIf = function (condition, val) {
744
+ if (condition) {
745
+ this.arg(val);
746
+ }
747
+ return this;
748
+ };
749
+ /**
750
+ * Pipe output of exec() to another tool
751
+ * @param tool
752
+ * @param file optional filename to additionally stream the output to.
753
+ * @returns {ToolRunner}
754
+ */
755
+ ToolRunner.prototype.pipeExecOutputToTool = function (tool, file) {
756
+ this.pipeOutputToTool = tool;
757
+ this.pipeOutputToFile = file;
758
+ return this;
759
+ };
760
+ /**
761
+ * Exec a tool.
762
+ * Output will be streamed to the live console.
763
+ * Returns promise with return code
764
+ *
765
+ * @param tool path to tool to exec
766
+ * @param options optional exec options. See IExecOptions
767
+ * @returns number
768
+ */
769
+ ToolRunner.prototype.exec = function (options) {
770
+ var _this = this;
771
+ var _a, _b, _c;
772
+ if (this.pipeOutputToTool) {
773
+ return this.execWithPiping(this.pipeOutputToTool, options);
774
+ }
775
+ var defer = Q.defer();
776
+ this._debug('exec tool: ' + this.toolPath);
777
+ this._debug('arguments:');
778
+ this.args.forEach(function (arg) {
779
+ _this._debug(' ' + arg);
780
+ });
781
+ var optionsNonNull = this._cloneExecOptions(options);
782
+ if (!optionsNonNull.silent) {
783
+ optionsNonNull.outStream.write(this._getCommandString(optionsNonNull) + os.EOL);
784
+ }
785
+ var state = new ExecState(optionsNonNull, this.toolPath);
786
+ state.on('debug', function (message) {
787
+ _this._debug(message);
788
+ });
789
+ var cp = child.spawn(this._getSpawnFileName(options), this._getSpawnArgs(optionsNonNull), this._getSpawnOptions(options));
790
+ this.childProcess = cp;
791
+ // it is possible for the child process to end its last line without a new line.
792
+ // because stdout is buffered, this causes the last line to not get sent to the parent
793
+ // stream. Adding this event forces a flush before the child streams are closed.
794
+ (_a = cp.stdout) === null || _a === void 0 ? void 0 : _a.on('finish', function () {
795
+ if (!optionsNonNull.silent) {
796
+ optionsNonNull.outStream.write(os.EOL);
797
+ }
798
+ });
799
+ var stdbuffer = '';
800
+ (_b = cp.stdout) === null || _b === void 0 ? void 0 : _b.on('data', function (data) {
801
+ _this.emit('stdout', data);
802
+ if (!optionsNonNull.silent) {
803
+ optionsNonNull.outStream.write(data);
804
+ }
805
+ _this._processLineBuffer(data, stdbuffer, function (line) {
806
+ _this.emit('stdline', line);
807
+ });
808
+ });
809
+ var errbuffer = '';
810
+ (_c = cp.stderr) === null || _c === void 0 ? void 0 : _c.on('data', function (data) {
811
+ state.processStderr = true;
812
+ _this.emit('stderr', data);
813
+ if (!optionsNonNull.silent) {
814
+ var s = optionsNonNull.failOnStdErr ? optionsNonNull.errStream : optionsNonNull.outStream;
815
+ s.write(data);
816
+ }
817
+ _this._processLineBuffer(data, errbuffer, function (line) {
818
+ _this.emit('errline', line);
819
+ });
820
+ });
821
+ cp.on('error', function (err) {
822
+ state.processError = err.message;
823
+ state.processExited = true;
824
+ state.processClosed = true;
825
+ state.CheckComplete();
826
+ });
827
+ cp.on('exit', function (code, signal) {
828
+ state.processExitCode = code;
829
+ state.processExited = true;
830
+ _this._debug("Exit code " + code + " received from tool '" + _this.toolPath + "'");
831
+ state.CheckComplete();
832
+ });
833
+ cp.on('close', function (code, signal) {
834
+ state.processExitCode = code;
835
+ state.processExited = true;
836
+ state.processClosed = true;
837
+ _this._debug("STDIO streams have closed for tool '" + _this.toolPath + "'");
838
+ state.CheckComplete();
839
+ });
840
+ state.on('done', function (error, exitCode) {
841
+ if (stdbuffer.length > 0) {
842
+ _this.emit('stdline', stdbuffer);
843
+ }
844
+ if (errbuffer.length > 0) {
845
+ _this.emit('errline', errbuffer);
846
+ }
847
+ cp.removeAllListeners();
848
+ if (error) {
849
+ defer.reject(error);
850
+ }
851
+ else {
852
+ defer.resolve(exitCode);
853
+ }
854
+ });
855
+ return defer.promise;
856
+ };
857
+ /**
858
+ * Exec a tool synchronously.
859
+ * Output will be *not* be streamed to the live console. It will be returned after execution is complete.
860
+ * Appropriate for short running tools
861
+ * Returns IExecSyncResult with output and return code
862
+ *
863
+ * @param tool path to tool to exec
864
+ * @param options optional exec options. See IExecSyncOptions
865
+ * @returns IExecSyncResult
866
+ */
867
+ ToolRunner.prototype.execSync = function (options) {
868
+ var _this = this;
869
+ this._debug('exec tool: ' + this.toolPath);
870
+ this._debug('arguments:');
871
+ this.args.forEach(function (arg) {
872
+ _this._debug(' ' + arg);
873
+ });
874
+ var success = true;
875
+ options = this._cloneExecOptions(options);
876
+ if (!options.silent) {
877
+ options.outStream.write(this._getCommandString(options) + os.EOL);
878
+ }
879
+ var r = child.spawnSync(this._getSpawnFileName(options), this._getSpawnArgs(options), this._getSpawnSyncOptions(options));
880
+ if (!options.silent && r.stdout && r.stdout.length > 0) {
881
+ options.outStream.write(r.stdout);
882
+ }
883
+ if (!options.silent && r.stderr && r.stderr.length > 0) {
884
+ options.errStream.write(r.stderr);
885
+ }
886
+ var res = { code: r.status, error: r.error };
887
+ res.stdout = (r.stdout) ? r.stdout.toString() : '';
888
+ res.stderr = (r.stderr) ? r.stderr.toString() : '';
889
+ return res;
890
+ };
891
+ /**
892
+ * Used to close child process by sending SIGNINT signal.
893
+ * It allows executed script to have some additional logic on SIGINT, before exiting.
894
+ */
895
+ ToolRunner.prototype.killChildProcess = function () {
896
+ if (this.childProcess) {
897
+ this.childProcess.kill();
898
+ }
899
+ };
900
+ return ToolRunner;
901
+ }(events.EventEmitter));
902
+ exports.ToolRunner = ToolRunner;
903
+ var ExecState = /** @class */ (function (_super) {
904
+ __extends(ExecState, _super);
905
+ function ExecState(options, toolPath) {
906
+ var _this = _super.call(this) || this;
907
+ _this.delay = 10000; // 10 seconds
908
+ _this.timeout = null;
909
+ if (!toolPath) {
910
+ throw new Error('toolPath must not be empty');
911
+ }
912
+ _this.options = options;
913
+ _this.toolPath = toolPath;
914
+ var delay = process.env['TASKLIB_TEST_TOOLRUNNER_EXITDELAY'];
915
+ if (delay) {
916
+ _this.delay = parseInt(delay);
917
+ }
918
+ return _this;
919
+ }
920
+ ExecState.prototype.CheckComplete = function () {
921
+ if (this.done) {
922
+ return;
923
+ }
924
+ if (this.processClosed) {
925
+ this._setResult();
926
+ }
927
+ else if (this.processExited) {
928
+ this.timeout = setTimeout(ExecState.HandleTimeout, this.delay, this);
929
+ }
930
+ };
931
+ ExecState.prototype._debug = function (message) {
932
+ this.emit('debug', message);
933
+ };
934
+ ExecState.prototype._setResult = function () {
935
+ // determine whether there is an error
936
+ var error;
937
+ if (this.processExited) {
938
+ if (this.processError) {
939
+ error = new Error(im._loc('LIB_ProcessError', this.toolPath, this.processError));
940
+ }
941
+ else if (this.processExitCode != 0 && !this.options.ignoreReturnCode) {
942
+ error = new Error(im._loc('LIB_ProcessExitCode', this.toolPath, this.processExitCode));
943
+ }
944
+ else if (this.processStderr && this.options.failOnStdErr) {
945
+ error = new Error(im._loc('LIB_ProcessStderr', this.toolPath));
946
+ }
947
+ }
948
+ // clear the timeout
949
+ if (this.timeout) {
950
+ clearTimeout(this.timeout);
951
+ this.timeout = null;
952
+ }
953
+ this.done = true;
954
+ this.emit('done', error, this.processExitCode);
955
+ };
956
+ ExecState.HandleTimeout = function (state) {
957
+ if (state.done) {
958
+ return;
959
+ }
960
+ if (!state.processClosed && state.processExited) {
961
+ console.log(im._loc('LIB_StdioNotClosed', state.delay / 1000, state.toolPath));
962
+ state._debug(im._loc('LIB_StdioNotClosed', state.delay / 1000, state.toolPath));
963
+ }
964
+ state._setResult();
965
+ };
966
+ return ExecState;
967
+ }(events.EventEmitter));