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/internal.js CHANGED
@@ -1,885 +1,885 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports._exposeCertSettings = exports._exposeProxySettings = exports._normalizeSeparators = exports._isRooted = exports._getDirectoryName = exports._ensureRooted = exports._isUncPath = exports._loadData = exports._ensurePatternRooted = exports._getFindInfoFromPattern = exports._cloneMatchOptions = exports._legacyFindFiles_convertPatternToRegExp = exports._which = exports._checkPath = exports._exist = exports._debug = exports._error = exports._warning = exports._command = exports._getVariableKey = exports._getVariable = exports._loc = exports._setResourcePath = exports._setErrStream = exports._setStdStream = exports._writeLine = exports._endsWith = exports._startsWith = exports._vault = exports._knownVariableMap = void 0;
4
- var fs = require("fs");
5
- var path = require("path");
6
- var os = require("os");
7
- var minimatch = require("minimatch");
8
- var util = require("util");
9
- var tcm = require("./taskcommand");
10
- var vm = require("./vault");
11
- var semver = require("semver");
12
- var crypto = require("crypto");
13
- /**
14
- * Hash table of known variable info. The formatted env var name is the lookup key.
15
- *
16
- * The purpose of this hash table is to keep track of known variables. The hash table
17
- * needs to be maintained for multiple reasons:
18
- * 1) to distinguish between env vars and job vars
19
- * 2) to distinguish between secret vars and public
20
- * 3) to know the real variable name and not just the formatted env var name.
21
- */
22
- exports._knownVariableMap = {};
23
- //-----------------------------------------------------
24
- // Validation Checks
25
- //-----------------------------------------------------
26
- // async await needs generators in node 4.x+
27
- if (semver.lt(process.versions.node, '4.2.0')) {
28
- _warning('Tasks require a new agent. Upgrade your agent or node to 4.2.0 or later');
29
- }
30
- //-----------------------------------------------------
31
- // String convenience
32
- //-----------------------------------------------------
33
- function _startsWith(str, start) {
34
- return str.slice(0, start.length) == start;
35
- }
36
- exports._startsWith = _startsWith;
37
- function _endsWith(str, end) {
38
- return str.slice(-end.length) == end;
39
- }
40
- exports._endsWith = _endsWith;
41
- //-----------------------------------------------------
42
- // General Helpers
43
- //-----------------------------------------------------
44
- var _outStream = process.stdout;
45
- var _errStream = process.stderr;
46
- function _writeLine(str) {
47
- _outStream.write(str + os.EOL);
48
- }
49
- exports._writeLine = _writeLine;
50
- function _setStdStream(stdStream) {
51
- _outStream = stdStream;
52
- }
53
- exports._setStdStream = _setStdStream;
54
- function _setErrStream(errStream) {
55
- _errStream = errStream;
56
- }
57
- exports._setErrStream = _setErrStream;
58
- //-----------------------------------------------------
59
- // Loc Helpers
60
- //-----------------------------------------------------
61
- var _locStringCache = {};
62
- var _resourceFiles = {};
63
- var _libResourceFileLoaded = false;
64
- var _resourceCulture = 'en-US';
65
- function _loadResJson(resjsonFile) {
66
- var resJson;
67
- if (_exist(resjsonFile)) {
68
- var resjsonContent = fs.readFileSync(resjsonFile, 'utf8').toString();
69
- // remove BOM
70
- if (resjsonContent.indexOf('\uFEFF') == 0) {
71
- resjsonContent = resjsonContent.slice(1);
72
- }
73
- try {
74
- resJson = JSON.parse(resjsonContent);
75
- }
76
- catch (err) {
77
- _debug('unable to parse resjson with err: ' + err.message);
78
- }
79
- }
80
- else {
81
- _debug('.resjson file not found: ' + resjsonFile);
82
- }
83
- return resJson;
84
- }
85
- function _loadLocStrings(resourceFile, culture) {
86
- var locStrings = {};
87
- if (_exist(resourceFile)) {
88
- var resourceJson = require(resourceFile);
89
- if (resourceJson && resourceJson.hasOwnProperty('messages')) {
90
- var locResourceJson;
91
- // load up resource resjson for different culture
92
- var localizedResourceFile = path.join(path.dirname(resourceFile), 'Strings', 'resources.resjson');
93
- var upperCulture = culture.toUpperCase();
94
- var cultures = [];
95
- try {
96
- cultures = fs.readdirSync(localizedResourceFile);
97
- }
98
- catch (ex) { }
99
- for (var i = 0; i < cultures.length; i++) {
100
- if (cultures[i].toUpperCase() == upperCulture) {
101
- localizedResourceFile = path.join(localizedResourceFile, cultures[i], 'resources.resjson');
102
- if (_exist(localizedResourceFile)) {
103
- locResourceJson = _loadResJson(localizedResourceFile);
104
- }
105
- break;
106
- }
107
- }
108
- for (var key in resourceJson.messages) {
109
- if (locResourceJson && locResourceJson.hasOwnProperty('loc.messages.' + key)) {
110
- locStrings[key] = locResourceJson['loc.messages.' + key];
111
- }
112
- else {
113
- locStrings[key] = resourceJson.messages[key];
114
- }
115
- }
116
- }
117
- }
118
- else {
119
- _warning('LIB_ResourceFile does not exist');
120
- }
121
- return locStrings;
122
- }
123
- /**
124
- * Sets the location of the resources json. This is typically the task.json file.
125
- * Call once at the beginning of the script before any calls to loc.
126
- * @param path Full path to the json.
127
- * @param ignoreWarnings Won't throw warnings if path already set.
128
- * @returns void
129
- */
130
- function _setResourcePath(path, ignoreWarnings) {
131
- if (ignoreWarnings === void 0) { ignoreWarnings = false; }
132
- if (process.env['TASKLIB_INPROC_UNITS']) {
133
- _resourceFiles = {};
134
- _libResourceFileLoaded = false;
135
- _locStringCache = {};
136
- _resourceCulture = 'en-US';
137
- }
138
- if (!_resourceFiles[path]) {
139
- _checkPath(path, 'resource file path');
140
- _resourceFiles[path] = path;
141
- _debug('adding resource file: ' + path);
142
- _resourceCulture = _getVariable('system.culture') || _resourceCulture;
143
- var locStrs = _loadLocStrings(path, _resourceCulture);
144
- for (var key in locStrs) {
145
- //cache loc string
146
- _locStringCache[key] = locStrs[key];
147
- }
148
- }
149
- else {
150
- if (ignoreWarnings) {
151
- _debug(_loc('LIB_ResourceFileAlreadySet', path));
152
- }
153
- else {
154
- _warning(_loc('LIB_ResourceFileAlreadySet', path));
155
- }
156
- }
157
- }
158
- exports._setResourcePath = _setResourcePath;
159
- /**
160
- * Gets the localized string from the json resource file. Optionally formats with additional params.
161
- *
162
- * @param key key of the resources string in the resource file
163
- * @param param additional params for formatting the string
164
- * @returns string
165
- */
166
- function _loc(key) {
167
- var param = [];
168
- for (var _i = 1; _i < arguments.length; _i++) {
169
- param[_i - 1] = arguments[_i];
170
- }
171
- if (!_libResourceFileLoaded) {
172
- // merge loc strings from azure-pipelines-task-lib.
173
- var libResourceFile = path.join(__dirname, 'lib.json');
174
- var libLocStrs = _loadLocStrings(libResourceFile, _resourceCulture);
175
- for (var libKey in libLocStrs) {
176
- //cache azure-pipelines-task-lib loc string
177
- _locStringCache[libKey] = libLocStrs[libKey];
178
- }
179
- _libResourceFileLoaded = true;
180
- }
181
- var locString;
182
- ;
183
- if (_locStringCache.hasOwnProperty(key)) {
184
- locString = _locStringCache[key];
185
- }
186
- else {
187
- if (Object.keys(_resourceFiles).length <= 0) {
188
- _warning("Resource file haven't been set, can't find loc string for key: " + key);
189
- }
190
- else {
191
- _warning("Can't find loc string for key: " + key);
192
- }
193
- locString = key;
194
- }
195
- if (param.length > 0) {
196
- return util.format.apply(this, [locString].concat(param));
197
- }
198
- else {
199
- return locString;
200
- }
201
- }
202
- exports._loc = _loc;
203
- //-----------------------------------------------------
204
- // Input Helpers
205
- //-----------------------------------------------------
206
- /**
207
- * Gets a variable value that is defined on the build/release definition or set at runtime.
208
- *
209
- * @param name name of the variable to get
210
- * @returns string
211
- */
212
- function _getVariable(name) {
213
- var varval;
214
- // get the metadata
215
- var info;
216
- var key = _getVariableKey(name);
217
- if (exports._knownVariableMap.hasOwnProperty(key)) {
218
- info = exports._knownVariableMap[key];
219
- }
220
- if (info && info.secret) {
221
- // get the secret value
222
- varval = exports._vault.retrieveSecret('SECRET_' + key);
223
- }
224
- else {
225
- // get the public value
226
- varval = process.env[key];
227
- // fallback for pre 2.104.1 agent
228
- if (!varval && name.toUpperCase() == 'AGENT.JOBSTATUS') {
229
- varval = process.env['agent.jobstatus'];
230
- }
231
- }
232
- _debug(name + '=' + varval);
233
- return varval;
234
- }
235
- exports._getVariable = _getVariable;
236
- function _getVariableKey(name) {
237
- if (!name) {
238
- throw new Error(_loc('LIB_ParameterIsRequired', 'name'));
239
- }
240
- return name.replace(/\./g, '_').replace(/ /g, '_').toUpperCase();
241
- }
242
- exports._getVariableKey = _getVariableKey;
243
- //-----------------------------------------------------
244
- // Cmd Helpers
245
- //-----------------------------------------------------
246
- function _command(command, properties, message) {
247
- var taskCmd = new tcm.TaskCommand(command, properties, message);
248
- _writeLine(taskCmd.toString());
249
- }
250
- exports._command = _command;
251
- function _warning(message) {
252
- _command('task.issue', { 'type': 'warning' }, message);
253
- }
254
- exports._warning = _warning;
255
- function _error(message) {
256
- _command('task.issue', { 'type': 'error' }, message);
257
- }
258
- exports._error = _error;
259
- function _debug(message) {
260
- _command('task.debug', null, message);
261
- }
262
- exports._debug = _debug;
263
- // //-----------------------------------------------------
264
- // // Disk Functions
265
- // //-----------------------------------------------------
266
- /**
267
- * Returns whether a path exists.
268
- *
269
- * @param path path to check
270
- * @returns boolean
271
- */
272
- function _exist(path) {
273
- var exist = false;
274
- try {
275
- exist = !!(path && fs.statSync(path) != null);
276
- }
277
- catch (err) {
278
- if (err && err.code === 'ENOENT') {
279
- exist = false;
280
- }
281
- else {
282
- throw err;
283
- }
284
- }
285
- return exist;
286
- }
287
- exports._exist = _exist;
288
- /**
289
- * Checks whether a path exists.
290
- * If the path does not exist, it will throw.
291
- *
292
- * @param p path to check
293
- * @param name name only used in error message to identify the path
294
- * @returns void
295
- */
296
- function _checkPath(p, name) {
297
- _debug('check path : ' + p);
298
- if (!_exist(p)) {
299
- throw new Error(_loc('LIB_PathNotFound', name, p));
300
- }
301
- }
302
- exports._checkPath = _checkPath;
303
- /**
304
- * Returns path of a tool had the tool actually been invoked. Resolves via paths.
305
- * If you check and the tool does not exist, it will throw.
306
- *
307
- * @param tool name of the tool
308
- * @param check whether to check if tool exists
309
- * @returns string
310
- */
311
- function _which(tool, check) {
312
- if (!tool) {
313
- throw new Error('parameter \'tool\' is required');
314
- }
315
- // recursive when check=true
316
- if (check) {
317
- var result = _which(tool, false);
318
- if (result) {
319
- return result;
320
- }
321
- else {
322
- if (process.platform == 'win32') {
323
- throw new Error(_loc('LIB_WhichNotFound_Win', tool));
324
- }
325
- else {
326
- throw new Error(_loc('LIB_WhichNotFound_Linux', tool));
327
- }
328
- }
329
- }
330
- _debug("which '" + tool + "'");
331
- try {
332
- // build the list of extensions to try
333
- var extensions = [];
334
- if (process.platform == 'win32' && process.env['PATHEXT']) {
335
- for (var _i = 0, _a = process.env['PATHEXT'].split(path.delimiter); _i < _a.length; _i++) {
336
- var extension = _a[_i];
337
- if (extension) {
338
- extensions.push(extension);
339
- }
340
- }
341
- }
342
- // if it's rooted, return it if exists. otherwise return empty.
343
- if (_isRooted(tool)) {
344
- var filePath = _tryGetExecutablePath(tool, extensions);
345
- if (filePath) {
346
- _debug("found: '" + filePath + "'");
347
- return filePath;
348
- }
349
- _debug('not found');
350
- return '';
351
- }
352
- // if any path separators, return empty
353
- if (tool.indexOf('/') >= 0 || (process.platform == 'win32' && tool.indexOf('\\') >= 0)) {
354
- _debug('not found');
355
- return '';
356
- }
357
- // build the list of directories
358
- //
359
- // Note, technically "where" checks the current directory on Windows. From a task lib perspective,
360
- // it feels like we should not do this. Checking the current directory seems like more of a use
361
- // case of a shell, and the which() function exposed by the task lib should strive for consistency
362
- // across platforms.
363
- var directories = [];
364
- if (process.env['PATH']) {
365
- for (var _b = 0, _c = process.env['PATH'].split(path.delimiter); _b < _c.length; _b++) {
366
- var p = _c[_b];
367
- if (p) {
368
- directories.push(p);
369
- }
370
- }
371
- }
372
- // return the first match
373
- for (var _d = 0, directories_1 = directories; _d < directories_1.length; _d++) {
374
- var directory = directories_1[_d];
375
- var filePath = _tryGetExecutablePath(directory + path.sep + tool, extensions);
376
- if (filePath) {
377
- _debug("found: '" + filePath + "'");
378
- return filePath;
379
- }
380
- }
381
- _debug('not found');
382
- return '';
383
- }
384
- catch (err) {
385
- throw new Error(_loc('LIB_OperationFailed', 'which', err.message));
386
- }
387
- }
388
- exports._which = _which;
389
- /**
390
- * Best effort attempt to determine whether a file exists and is executable.
391
- * @param filePath file path to check
392
- * @param extensions additional file extensions to try
393
- * @return if file exists and is executable, returns the file path. otherwise empty string.
394
- */
395
- function _tryGetExecutablePath(filePath, extensions) {
396
- try {
397
- // test file exists
398
- var stats = fs.statSync(filePath);
399
- if (stats.isFile()) {
400
- if (process.platform == 'win32') {
401
- // on Windows, test for valid extension
402
- var isExecutable = false;
403
- var fileName = path.basename(filePath);
404
- var dotIndex = fileName.lastIndexOf('.');
405
- if (dotIndex >= 0) {
406
- var upperExt_1 = fileName.substr(dotIndex).toUpperCase();
407
- if (extensions.some(function (validExt) { return validExt.toUpperCase() == upperExt_1; })) {
408
- return filePath;
409
- }
410
- }
411
- }
412
- else {
413
- if (isUnixExecutable(stats)) {
414
- return filePath;
415
- }
416
- }
417
- }
418
- }
419
- catch (err) {
420
- if (err.code != 'ENOENT') {
421
- _debug("Unexpected error attempting to determine if executable file exists '" + filePath + "': " + err);
422
- }
423
- }
424
- // try each extension
425
- var originalFilePath = filePath;
426
- for (var _i = 0, extensions_1 = extensions; _i < extensions_1.length; _i++) {
427
- var extension = extensions_1[_i];
428
- var found = false;
429
- var filePath_1 = originalFilePath + extension;
430
- try {
431
- var stats = fs.statSync(filePath_1);
432
- if (stats.isFile()) {
433
- if (process.platform == 'win32') {
434
- // preserve the case of the actual file (since an extension was appended)
435
- try {
436
- var directory = path.dirname(filePath_1);
437
- var upperName = path.basename(filePath_1).toUpperCase();
438
- for (var _a = 0, _b = fs.readdirSync(directory); _a < _b.length; _a++) {
439
- var actualName = _b[_a];
440
- if (upperName == actualName.toUpperCase()) {
441
- filePath_1 = path.join(directory, actualName);
442
- break;
443
- }
444
- }
445
- }
446
- catch (err) {
447
- _debug("Unexpected error attempting to determine the actual case of the file '" + filePath_1 + "': " + err);
448
- }
449
- return filePath_1;
450
- }
451
- else {
452
- if (isUnixExecutable(stats)) {
453
- return filePath_1;
454
- }
455
- }
456
- }
457
- }
458
- catch (err) {
459
- if (err.code != 'ENOENT') {
460
- _debug("Unexpected error attempting to determine if executable file exists '" + filePath_1 + "': " + err);
461
- }
462
- }
463
- }
464
- return '';
465
- }
466
- // on Mac/Linux, test the execute bit
467
- // R W X R W X R W X
468
- // 256 128 64 32 16 8 4 2 1
469
- function isUnixExecutable(stats) {
470
- return (stats.mode & 1) > 0 || ((stats.mode & 8) > 0 && stats.gid === process.getgid()) || ((stats.mode & 64) > 0 && stats.uid === process.getuid());
471
- }
472
- function _legacyFindFiles_convertPatternToRegExp(pattern) {
473
- pattern = (process.platform == 'win32' ? pattern.replace(/\\/g, '/') : pattern) // normalize separator on Windows
474
- .replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') // regex escape - from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript
475
- .replace(/\\\/\\\*\\\*\\\//g, '((\/.+/)|(\/))') // replace directory globstar, e.g. /hello/**/world
476
- .replace(/\\\*\\\*/g, '.*') // replace remaining globstars with a wildcard that can span directory separators, e.g. /hello/**dll
477
- .replace(/\\\*/g, '[^\/]*') // replace asterisks with a wildcard that cannot span directory separators, e.g. /hello/*.dll
478
- .replace(/\\\?/g, '[^\/]'); // replace single character wildcards, e.g. /hello/log?.dll
479
- pattern = "^" + pattern + "$";
480
- var flags = process.platform == 'win32' ? 'i' : '';
481
- return new RegExp(pattern, flags);
482
- }
483
- exports._legacyFindFiles_convertPatternToRegExp = _legacyFindFiles_convertPatternToRegExp;
484
- function _cloneMatchOptions(matchOptions) {
485
- return {
486
- debug: matchOptions.debug,
487
- nobrace: matchOptions.nobrace,
488
- noglobstar: matchOptions.noglobstar,
489
- dot: matchOptions.dot,
490
- noext: matchOptions.noext,
491
- nocase: matchOptions.nocase,
492
- nonull: matchOptions.nonull,
493
- matchBase: matchOptions.matchBase,
494
- nocomment: matchOptions.nocomment,
495
- nonegate: matchOptions.nonegate,
496
- flipNegate: matchOptions.flipNegate
497
- };
498
- }
499
- exports._cloneMatchOptions = _cloneMatchOptions;
500
- function _getFindInfoFromPattern(defaultRoot, pattern, matchOptions) {
501
- // parameter validation
502
- if (!defaultRoot) {
503
- throw new Error('getFindRootFromPattern() parameter defaultRoot cannot be empty');
504
- }
505
- if (!pattern) {
506
- throw new Error('getFindRootFromPattern() parameter pattern cannot be empty');
507
- }
508
- if (!matchOptions.nobrace) {
509
- throw new Error('getFindRootFromPattern() expected matchOptions.nobrace to be true');
510
- }
511
- // for the sake of determining the findPath, pretend nocase=false
512
- matchOptions = _cloneMatchOptions(matchOptions);
513
- matchOptions.nocase = false;
514
- // check if basename only and matchBase=true
515
- if (matchOptions.matchBase &&
516
- !_isRooted(pattern) &&
517
- (process.platform == 'win32' ? pattern.replace(/\\/g, '/') : pattern).indexOf('/') < 0) {
518
- return {
519
- adjustedPattern: pattern,
520
- findPath: defaultRoot,
521
- statOnly: false,
522
- };
523
- }
524
- // the technique applied by this function is to use the information on the Minimatch object determine
525
- // the findPath. Minimatch breaks the pattern into path segments, and exposes information about which
526
- // segments are literal vs patterns.
527
- //
528
- // note, the technique currently imposes a limitation for drive-relative paths with a glob in the
529
- // first segment, e.g. C:hello*/world. it's feasible to overcome this limitation, but is left unsolved
530
- // for now.
531
- var minimatchObj = new minimatch.Minimatch(pattern, matchOptions);
532
- // the "set" property is an array of arrays of parsed path segment info. the outer array should only
533
- // contain one item, otherwise something went wrong. brace expansion can result in multiple arrays,
534
- // but that should be turned off by the time this function is reached.
535
- if (minimatchObj.set.length != 1) {
536
- throw new Error('getFindRootFromPattern() expected Minimatch(...).set.length to be 1. Actual: ' + minimatchObj.set.length);
537
- }
538
- var literalSegments = [];
539
- for (var _i = 0, _a = minimatchObj.set[0]; _i < _a.length; _i++) {
540
- var parsedSegment = _a[_i];
541
- if (typeof parsedSegment == 'string') {
542
- // the item is a string when the original input for the path segment does not contain any
543
- // unescaped glob characters.
544
- //
545
- // note, the string here is already unescaped (i.e. glob escaping removed), so it is ready
546
- // to pass to find() as-is. for example, an input string 'hello\\*world' => 'hello*world'.
547
- literalSegments.push(parsedSegment);
548
- continue;
549
- }
550
- break;
551
- }
552
- // join the literal segments back together. Minimatch converts '\' to '/' on Windows, then squashes
553
- // consequetive slashes, and finally splits on slash. this means that UNC format is lost, but can
554
- // be detected from the original pattern.
555
- var joinedSegments = literalSegments.join('/');
556
- if (joinedSegments && process.platform == 'win32' && _startsWith(pattern.replace(/\\/g, '/'), '//')) {
557
- joinedSegments = '/' + joinedSegments; // restore UNC format
558
- }
559
- // determine the find path
560
- var findPath;
561
- if (_isRooted(pattern)) { // the pattern was rooted
562
- findPath = joinedSegments;
563
- }
564
- else if (joinedSegments) { // the pattern was not rooted, and literal segments were found
565
- findPath = _ensureRooted(defaultRoot, joinedSegments);
566
- }
567
- else { // the pattern was not rooted, and no literal segments were found
568
- findPath = defaultRoot;
569
- }
570
- // clean up the path
571
- if (findPath) {
572
- findPath = _getDirectoryName(_ensureRooted(findPath, '_')); // hack to remove unnecessary trailing slash
573
- findPath = _normalizeSeparators(findPath); // normalize slashes
574
- }
575
- return {
576
- adjustedPattern: _ensurePatternRooted(defaultRoot, pattern),
577
- findPath: findPath,
578
- statOnly: literalSegments.length == minimatchObj.set[0].length,
579
- };
580
- }
581
- exports._getFindInfoFromPattern = _getFindInfoFromPattern;
582
- function _ensurePatternRooted(root, p) {
583
- if (!root) {
584
- throw new Error('ensurePatternRooted() parameter "root" cannot be empty');
585
- }
586
- if (!p) {
587
- throw new Error('ensurePatternRooted() parameter "p" cannot be empty');
588
- }
589
- if (_isRooted(p)) {
590
- return p;
591
- }
592
- // normalize root
593
- root = _normalizeSeparators(root);
594
- // escape special glob characters
595
- root = (process.platform == 'win32' ? root : root.replace(/\\/g, '\\\\')) // escape '\' on OSX/Linux
596
- .replace(/(\[)(?=[^\/]+\])/g, '[[]') // escape '[' when ']' follows within the path segment
597
- .replace(/\?/g, '[?]') // escape '?'
598
- .replace(/\*/g, '[*]') // escape '*'
599
- .replace(/\+\(/g, '[+](') // escape '+('
600
- .replace(/@\(/g, '[@](') // escape '@('
601
- .replace(/!\(/g, '[!]('); // escape '!('
602
- return _ensureRooted(root, p);
603
- }
604
- exports._ensurePatternRooted = _ensurePatternRooted;
605
- //-------------------------------------------------------------------
606
- // Populate the vault with sensitive data. Inputs and Endpoints
607
- //-------------------------------------------------------------------
608
- function _loadData() {
609
- // in agent, prefer TempDirectory then workFolder.
610
- // In interactive dev mode, it won't be
611
- var keyPath = _getVariable("agent.TempDirectory") || _getVariable("agent.workFolder") || process.cwd();
612
- exports._vault = new vm.Vault(keyPath);
613
- exports._knownVariableMap = {};
614
- _debug('loading inputs and endpoints');
615
- var loaded = 0;
616
- for (var envvar in process.env) {
617
- if (_startsWith(envvar, 'INPUT_') ||
618
- _startsWith(envvar, 'ENDPOINT_AUTH_') ||
619
- _startsWith(envvar, 'SECUREFILE_TICKET_') ||
620
- _startsWith(envvar, 'SECRET_') ||
621
- _startsWith(envvar, 'VSTS_TASKVARIABLE_')) {
622
- // Record the secret variable metadata. This is required by getVariable to know whether
623
- // to retrieve the value from the vault. In a 2.104.1 agent or higher, this metadata will
624
- // be overwritten when the VSTS_SECRET_VARIABLES env var is processed below.
625
- if (_startsWith(envvar, 'SECRET_')) {
626
- var variableName = envvar.substring('SECRET_'.length);
627
- if (variableName) {
628
- // This is technically not the variable name (has underscores instead of dots),
629
- // but it's good enough to make getVariable work in a pre-2.104.1 agent where
630
- // the VSTS_SECRET_VARIABLES env var is not defined.
631
- exports._knownVariableMap[_getVariableKey(variableName)] = { name: variableName, secret: true };
632
- }
633
- }
634
- // store the secret
635
- var value = process.env[envvar];
636
- if (value) {
637
- ++loaded;
638
- _debug('loading ' + envvar);
639
- exports._vault.storeSecret(envvar, value);
640
- delete process.env[envvar];
641
- }
642
- }
643
- }
644
- _debug('loaded ' + loaded);
645
- // store public variable metadata
646
- var names;
647
- try {
648
- names = JSON.parse(process.env['VSTS_PUBLIC_VARIABLES'] || '[]');
649
- }
650
- catch (err) {
651
- throw new Error('Failed to parse VSTS_PUBLIC_VARIABLES as JSON. ' + err); // may occur during interactive testing
652
- }
653
- names.forEach(function (name) {
654
- exports._knownVariableMap[_getVariableKey(name)] = { name: name, secret: false };
655
- });
656
- delete process.env['VSTS_PUBLIC_VARIABLES'];
657
- // store secret variable metadata
658
- try {
659
- names = JSON.parse(process.env['VSTS_SECRET_VARIABLES'] || '[]');
660
- }
661
- catch (err) {
662
- throw new Error('Failed to parse VSTS_SECRET_VARIABLES as JSON. ' + err); // may occur during interactive testing
663
- }
664
- names.forEach(function (name) {
665
- exports._knownVariableMap[_getVariableKey(name)] = { name: name, secret: true };
666
- });
667
- delete process.env['VSTS_SECRET_VARIABLES'];
668
- // avoid loading twice (overwrites .taskkey)
669
- global['_vsts_task_lib_loaded'] = true;
670
- }
671
- exports._loadData = _loadData;
672
- //--------------------------------------------------------------------------------
673
- // Internal path helpers.
674
- //--------------------------------------------------------------------------------
675
- /**
676
- * Defines if path is unc-path.
677
- *
678
- * @param path a path to a file.
679
- * @returns true if path starts with double backslash, otherwise returns false.
680
- */
681
- function _isUncPath(path) {
682
- return /^\\\\[^\\]/.test(path);
683
- }
684
- exports._isUncPath = _isUncPath;
685
- function _ensureRooted(root, p) {
686
- if (!root) {
687
- throw new Error('ensureRooted() parameter "root" cannot be empty');
688
- }
689
- if (!p) {
690
- throw new Error('ensureRooted() parameter "p" cannot be empty');
691
- }
692
- if (_isRooted(p)) {
693
- return p;
694
- }
695
- if (process.platform == 'win32' && root.match(/^[A-Z]:$/i)) { // e.g. C:
696
- return root + p;
697
- }
698
- // ensure root ends with a separator
699
- if (_endsWith(root, '/') || (process.platform == 'win32' && _endsWith(root, '\\'))) {
700
- // root already ends with a separator
701
- }
702
- else {
703
- root += path.sep; // append separator
704
- }
705
- return root + p;
706
- }
707
- exports._ensureRooted = _ensureRooted;
708
- /**
709
- * Determines the parent path and trims trailing slashes (when safe). Path separators are normalized
710
- * in the result. This function works similar to the .NET System.IO.Path.GetDirectoryName() method.
711
- * For example, C:\hello\world\ returns C:\hello\world (trailing slash removed). Returns empty when
712
- * no higher directory can be determined.
713
- */
714
- function _getDirectoryName(p) {
715
- // short-circuit if empty
716
- if (!p) {
717
- return '';
718
- }
719
- // normalize separators
720
- p = _normalizeSeparators(p);
721
- // on Windows, the goal of this function is to match the behavior of
722
- // [System.IO.Path]::GetDirectoryName(), e.g.
723
- // C:/ =>
724
- // C:/hello => C:\
725
- // C:/hello/ => C:\hello
726
- // C:/hello/world => C:\hello
727
- // C:/hello/world/ => C:\hello\world
728
- // C: =>
729
- // C:hello => C:
730
- // C:hello/ => C:hello
731
- // / =>
732
- // /hello => \
733
- // /hello/ => \hello
734
- // //hello =>
735
- // //hello/ =>
736
- // //hello/world =>
737
- // //hello/world/ => \\hello\world
738
- //
739
- // unfortunately, path.dirname() can't simply be used. for example, on Windows
740
- // it yields different results from Path.GetDirectoryName:
741
- // C:/ => C:/
742
- // C:/hello => C:/
743
- // C:/hello/ => C:/
744
- // C:/hello/world => C:/hello
745
- // C:/hello/world/ => C:/hello
746
- // C: => C:
747
- // C:hello => C:
748
- // C:hello/ => C:
749
- // / => /
750
- // /hello => /
751
- // /hello/ => /
752
- // //hello => /
753
- // //hello/ => /
754
- // //hello/world => //hello/world
755
- // //hello/world/ => //hello/world/
756
- // //hello/world/again => //hello/world/
757
- // //hello/world/again/ => //hello/world/
758
- // //hello/world/again/again => //hello/world/again
759
- // //hello/world/again/again/ => //hello/world/again
760
- if (process.platform == 'win32') {
761
- if (/^[A-Z]:\\?[^\\]+$/i.test(p)) { // e.g. C:\hello or C:hello
762
- return p.charAt(2) == '\\' ? p.substring(0, 3) : p.substring(0, 2);
763
- }
764
- else if (/^[A-Z]:\\?$/i.test(p)) { // e.g. C:\ or C:
765
- return '';
766
- }
767
- var lastSlashIndex = p.lastIndexOf('\\');
768
- if (lastSlashIndex < 0) { // file name only
769
- return '';
770
- }
771
- else if (p == '\\') { // relative root
772
- return '';
773
- }
774
- else if (lastSlashIndex == 0) { // e.g. \\hello
775
- return '\\';
776
- }
777
- else if (/^\\\\[^\\]+(\\[^\\]*)?$/.test(p)) { // UNC root, e.g. \\hello or \\hello\ or \\hello\world
778
- return '';
779
- }
780
- return p.substring(0, lastSlashIndex); // e.g. hello\world => hello or hello\world\ => hello\world
781
- // note, this means trailing slashes for non-root directories
782
- // (i.e. not C:\, \, or \\unc\) will simply be removed.
783
- }
784
- // OSX/Linux
785
- if (p.indexOf('/') < 0) { // file name only
786
- return '';
787
- }
788
- else if (p == '/') {
789
- return '';
790
- }
791
- else if (_endsWith(p, '/')) {
792
- return p.substring(0, p.length - 1);
793
- }
794
- return path.dirname(p);
795
- }
796
- exports._getDirectoryName = _getDirectoryName;
797
- /**
798
- * On OSX/Linux, true if path starts with '/'. On Windows, true for paths like:
799
- * \, \hello, \\hello\share, C:, and C:\hello (and corresponding alternate separator cases).
800
- */
801
- function _isRooted(p) {
802
- p = _normalizeSeparators(p);
803
- if (!p) {
804
- throw new Error('isRooted() parameter "p" cannot be empty');
805
- }
806
- if (process.platform == 'win32') {
807
- return _startsWith(p, '\\') || // e.g. \ or \hello or \\hello
808
- /^[A-Z]:/i.test(p); // e.g. C: or C:\hello
809
- }
810
- return _startsWith(p, '/'); // e.g. /hello
811
- }
812
- exports._isRooted = _isRooted;
813
- function _normalizeSeparators(p) {
814
- p = p || '';
815
- if (process.platform == 'win32') {
816
- // convert slashes on Windows
817
- p = p.replace(/\//g, '\\');
818
- // remove redundant slashes
819
- var isUnc = /^\\\\+[^\\]/.test(p); // e.g. \\hello
820
- return (isUnc ? '\\' : '') + p.replace(/\\\\+/g, '\\'); // preserve leading // for UNC
821
- }
822
- // remove redundant slashes
823
- return p.replace(/\/\/+/g, '/');
824
- }
825
- exports._normalizeSeparators = _normalizeSeparators;
826
- //-----------------------------------------------------
827
- // Expose proxy information to vsts-node-api
828
- //-----------------------------------------------------
829
- function _exposeProxySettings() {
830
- var proxyUrl = _getVariable('Agent.ProxyUrl');
831
- if (proxyUrl && proxyUrl.length > 0) {
832
- var proxyUsername = _getVariable('Agent.ProxyUsername');
833
- var proxyPassword = _getVariable('Agent.ProxyPassword');
834
- var proxyBypassHostsJson = _getVariable('Agent.ProxyBypassList');
835
- global['_vsts_task_lib_proxy_url'] = proxyUrl;
836
- global['_vsts_task_lib_proxy_username'] = proxyUsername;
837
- global['_vsts_task_lib_proxy_bypass'] = proxyBypassHostsJson;
838
- global['_vsts_task_lib_proxy_password'] = _exposeTaskLibSecret('proxy', proxyPassword || '');
839
- _debug('expose agent proxy configuration.');
840
- global['_vsts_task_lib_proxy'] = true;
841
- }
842
- }
843
- exports._exposeProxySettings = _exposeProxySettings;
844
- //-----------------------------------------------------
845
- // Expose certificate information to vsts-node-api
846
- //-----------------------------------------------------
847
- function _exposeCertSettings() {
848
- var ca = _getVariable('Agent.CAInfo');
849
- if (ca) {
850
- global['_vsts_task_lib_cert_ca'] = ca;
851
- }
852
- var clientCert = _getVariable('Agent.ClientCert');
853
- if (clientCert) {
854
- var clientCertKey = _getVariable('Agent.ClientCertKey');
855
- var clientCertArchive = _getVariable('Agent.ClientCertArchive');
856
- var clientCertPassword = _getVariable('Agent.ClientCertPassword');
857
- global['_vsts_task_lib_cert_clientcert'] = clientCert;
858
- global['_vsts_task_lib_cert_key'] = clientCertKey;
859
- global['_vsts_task_lib_cert_archive'] = clientCertArchive;
860
- global['_vsts_task_lib_cert_passphrase'] = _exposeTaskLibSecret('cert', clientCertPassword || '');
861
- }
862
- if (ca || clientCert) {
863
- _debug('expose agent certificate configuration.');
864
- global['_vsts_task_lib_cert'] = true;
865
- }
866
- var skipCertValidation = _getVariable('Agent.SkipCertValidation') || 'false';
867
- if (skipCertValidation) {
868
- global['_vsts_task_lib_skip_cert_validation'] = skipCertValidation.toUpperCase() === 'TRUE';
869
- }
870
- }
871
- exports._exposeCertSettings = _exposeCertSettings;
872
- // We store the encryption key on disk and hold the encrypted content and key file in memory
873
- // return base64encoded<keyFilePath>:base64encoded<encryptedContent>
874
- // downstream vsts-node-api will retrieve the secret later
875
- function _exposeTaskLibSecret(keyFile, secret) {
876
- if (secret) {
877
- var encryptKey = crypto.randomBytes(256);
878
- var cipher = crypto.createCipher("aes-256-ctr", encryptKey);
879
- var encryptedContent = cipher.update(secret, "utf8", "hex");
880
- encryptedContent += cipher.final("hex");
881
- var storageFile = path.join(_getVariable('Agent.TempDirectory') || _getVariable("agent.workFolder") || process.cwd(), keyFile);
882
- fs.writeFileSync(storageFile, encryptKey.toString('base64'), { encoding: 'utf8' });
883
- return new Buffer(storageFile).toString('base64') + ':' + new Buffer(encryptedContent).toString('base64');
884
- }
885
- }
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports._exposeCertSettings = exports._exposeProxySettings = exports._normalizeSeparators = exports._isRooted = exports._getDirectoryName = exports._ensureRooted = exports._isUncPath = exports._loadData = exports._ensurePatternRooted = exports._getFindInfoFromPattern = exports._cloneMatchOptions = exports._legacyFindFiles_convertPatternToRegExp = exports._which = exports._checkPath = exports._exist = exports._debug = exports._error = exports._warning = exports._command = exports._getVariableKey = exports._getVariable = exports._loc = exports._setResourcePath = exports._setErrStream = exports._setStdStream = exports._writeLine = exports._endsWith = exports._startsWith = exports._vault = exports._knownVariableMap = void 0;
4
+ var fs = require("fs");
5
+ var path = require("path");
6
+ var os = require("os");
7
+ var minimatch = require("minimatch");
8
+ var util = require("util");
9
+ var tcm = require("./taskcommand");
10
+ var vm = require("./vault");
11
+ var semver = require("semver");
12
+ var crypto = require("crypto");
13
+ /**
14
+ * Hash table of known variable info. The formatted env var name is the lookup key.
15
+ *
16
+ * The purpose of this hash table is to keep track of known variables. The hash table
17
+ * needs to be maintained for multiple reasons:
18
+ * 1) to distinguish between env vars and job vars
19
+ * 2) to distinguish between secret vars and public
20
+ * 3) to know the real variable name and not just the formatted env var name.
21
+ */
22
+ exports._knownVariableMap = {};
23
+ //-----------------------------------------------------
24
+ // Validation Checks
25
+ //-----------------------------------------------------
26
+ // async await needs generators in node 4.x+
27
+ if (semver.lt(process.versions.node, '4.2.0')) {
28
+ _warning('Tasks require a new agent. Upgrade your agent or node to 4.2.0 or later');
29
+ }
30
+ //-----------------------------------------------------
31
+ // String convenience
32
+ //-----------------------------------------------------
33
+ function _startsWith(str, start) {
34
+ return str.slice(0, start.length) == start;
35
+ }
36
+ exports._startsWith = _startsWith;
37
+ function _endsWith(str, end) {
38
+ return str.slice(-end.length) == end;
39
+ }
40
+ exports._endsWith = _endsWith;
41
+ //-----------------------------------------------------
42
+ // General Helpers
43
+ //-----------------------------------------------------
44
+ var _outStream = process.stdout;
45
+ var _errStream = process.stderr;
46
+ function _writeLine(str) {
47
+ _outStream.write(str + os.EOL);
48
+ }
49
+ exports._writeLine = _writeLine;
50
+ function _setStdStream(stdStream) {
51
+ _outStream = stdStream;
52
+ }
53
+ exports._setStdStream = _setStdStream;
54
+ function _setErrStream(errStream) {
55
+ _errStream = errStream;
56
+ }
57
+ exports._setErrStream = _setErrStream;
58
+ //-----------------------------------------------------
59
+ // Loc Helpers
60
+ //-----------------------------------------------------
61
+ var _locStringCache = {};
62
+ var _resourceFiles = {};
63
+ var _libResourceFileLoaded = false;
64
+ var _resourceCulture = 'en-US';
65
+ function _loadResJson(resjsonFile) {
66
+ var resJson;
67
+ if (_exist(resjsonFile)) {
68
+ var resjsonContent = fs.readFileSync(resjsonFile, 'utf8').toString();
69
+ // remove BOM
70
+ if (resjsonContent.indexOf('\uFEFF') == 0) {
71
+ resjsonContent = resjsonContent.slice(1);
72
+ }
73
+ try {
74
+ resJson = JSON.parse(resjsonContent);
75
+ }
76
+ catch (err) {
77
+ _debug('unable to parse resjson with err: ' + err.message);
78
+ }
79
+ }
80
+ else {
81
+ _debug('.resjson file not found: ' + resjsonFile);
82
+ }
83
+ return resJson;
84
+ }
85
+ function _loadLocStrings(resourceFile, culture) {
86
+ var locStrings = {};
87
+ if (_exist(resourceFile)) {
88
+ var resourceJson = require(resourceFile);
89
+ if (resourceJson && resourceJson.hasOwnProperty('messages')) {
90
+ var locResourceJson;
91
+ // load up resource resjson for different culture
92
+ var localizedResourceFile = path.join(path.dirname(resourceFile), 'Strings', 'resources.resjson');
93
+ var upperCulture = culture.toUpperCase();
94
+ var cultures = [];
95
+ try {
96
+ cultures = fs.readdirSync(localizedResourceFile);
97
+ }
98
+ catch (ex) { }
99
+ for (var i = 0; i < cultures.length; i++) {
100
+ if (cultures[i].toUpperCase() == upperCulture) {
101
+ localizedResourceFile = path.join(localizedResourceFile, cultures[i], 'resources.resjson');
102
+ if (_exist(localizedResourceFile)) {
103
+ locResourceJson = _loadResJson(localizedResourceFile);
104
+ }
105
+ break;
106
+ }
107
+ }
108
+ for (var key in resourceJson.messages) {
109
+ if (locResourceJson && locResourceJson.hasOwnProperty('loc.messages.' + key)) {
110
+ locStrings[key] = locResourceJson['loc.messages.' + key];
111
+ }
112
+ else {
113
+ locStrings[key] = resourceJson.messages[key];
114
+ }
115
+ }
116
+ }
117
+ }
118
+ else {
119
+ _warning('LIB_ResourceFile does not exist');
120
+ }
121
+ return locStrings;
122
+ }
123
+ /**
124
+ * Sets the location of the resources json. This is typically the task.json file.
125
+ * Call once at the beginning of the script before any calls to loc.
126
+ * @param path Full path to the json.
127
+ * @param ignoreWarnings Won't throw warnings if path already set.
128
+ * @returns void
129
+ */
130
+ function _setResourcePath(path, ignoreWarnings) {
131
+ if (ignoreWarnings === void 0) { ignoreWarnings = false; }
132
+ if (process.env['TASKLIB_INPROC_UNITS']) {
133
+ _resourceFiles = {};
134
+ _libResourceFileLoaded = false;
135
+ _locStringCache = {};
136
+ _resourceCulture = 'en-US';
137
+ }
138
+ if (!_resourceFiles[path]) {
139
+ _checkPath(path, 'resource file path');
140
+ _resourceFiles[path] = path;
141
+ _debug('adding resource file: ' + path);
142
+ _resourceCulture = _getVariable('system.culture') || _resourceCulture;
143
+ var locStrs = _loadLocStrings(path, _resourceCulture);
144
+ for (var key in locStrs) {
145
+ //cache loc string
146
+ _locStringCache[key] = locStrs[key];
147
+ }
148
+ }
149
+ else {
150
+ if (ignoreWarnings) {
151
+ _debug(_loc('LIB_ResourceFileAlreadySet', path));
152
+ }
153
+ else {
154
+ _warning(_loc('LIB_ResourceFileAlreadySet', path));
155
+ }
156
+ }
157
+ }
158
+ exports._setResourcePath = _setResourcePath;
159
+ /**
160
+ * Gets the localized string from the json resource file. Optionally formats with additional params.
161
+ *
162
+ * @param key key of the resources string in the resource file
163
+ * @param param additional params for formatting the string
164
+ * @returns string
165
+ */
166
+ function _loc(key) {
167
+ var param = [];
168
+ for (var _i = 1; _i < arguments.length; _i++) {
169
+ param[_i - 1] = arguments[_i];
170
+ }
171
+ if (!_libResourceFileLoaded) {
172
+ // merge loc strings from azure-pipelines-task-lib.
173
+ var libResourceFile = path.join(__dirname, 'lib.json');
174
+ var libLocStrs = _loadLocStrings(libResourceFile, _resourceCulture);
175
+ for (var libKey in libLocStrs) {
176
+ //cache azure-pipelines-task-lib loc string
177
+ _locStringCache[libKey] = libLocStrs[libKey];
178
+ }
179
+ _libResourceFileLoaded = true;
180
+ }
181
+ var locString;
182
+ ;
183
+ if (_locStringCache.hasOwnProperty(key)) {
184
+ locString = _locStringCache[key];
185
+ }
186
+ else {
187
+ if (Object.keys(_resourceFiles).length <= 0) {
188
+ _warning("Resource file haven't been set, can't find loc string for key: " + key);
189
+ }
190
+ else {
191
+ _warning("Can't find loc string for key: " + key);
192
+ }
193
+ locString = key;
194
+ }
195
+ if (param.length > 0) {
196
+ return util.format.apply(this, [locString].concat(param));
197
+ }
198
+ else {
199
+ return locString;
200
+ }
201
+ }
202
+ exports._loc = _loc;
203
+ //-----------------------------------------------------
204
+ // Input Helpers
205
+ //-----------------------------------------------------
206
+ /**
207
+ * Gets a variable value that is defined on the build/release definition or set at runtime.
208
+ *
209
+ * @param name name of the variable to get
210
+ * @returns string
211
+ */
212
+ function _getVariable(name) {
213
+ var varval;
214
+ // get the metadata
215
+ var info;
216
+ var key = _getVariableKey(name);
217
+ if (exports._knownVariableMap.hasOwnProperty(key)) {
218
+ info = exports._knownVariableMap[key];
219
+ }
220
+ if (info && info.secret) {
221
+ // get the secret value
222
+ varval = exports._vault.retrieveSecret('SECRET_' + key);
223
+ }
224
+ else {
225
+ // get the public value
226
+ varval = process.env[key];
227
+ // fallback for pre 2.104.1 agent
228
+ if (!varval && name.toUpperCase() == 'AGENT.JOBSTATUS') {
229
+ varval = process.env['agent.jobstatus'];
230
+ }
231
+ }
232
+ _debug(name + '=' + varval);
233
+ return varval;
234
+ }
235
+ exports._getVariable = _getVariable;
236
+ function _getVariableKey(name) {
237
+ if (!name) {
238
+ throw new Error(_loc('LIB_ParameterIsRequired', 'name'));
239
+ }
240
+ return name.replace(/\./g, '_').replace(/ /g, '_').toUpperCase();
241
+ }
242
+ exports._getVariableKey = _getVariableKey;
243
+ //-----------------------------------------------------
244
+ // Cmd Helpers
245
+ //-----------------------------------------------------
246
+ function _command(command, properties, message) {
247
+ var taskCmd = new tcm.TaskCommand(command, properties, message);
248
+ _writeLine(taskCmd.toString());
249
+ }
250
+ exports._command = _command;
251
+ function _warning(message) {
252
+ _command('task.issue', { 'type': 'warning' }, message);
253
+ }
254
+ exports._warning = _warning;
255
+ function _error(message) {
256
+ _command('task.issue', { 'type': 'error' }, message);
257
+ }
258
+ exports._error = _error;
259
+ function _debug(message) {
260
+ _command('task.debug', null, message);
261
+ }
262
+ exports._debug = _debug;
263
+ // //-----------------------------------------------------
264
+ // // Disk Functions
265
+ // //-----------------------------------------------------
266
+ /**
267
+ * Returns whether a path exists.
268
+ *
269
+ * @param path path to check
270
+ * @returns boolean
271
+ */
272
+ function _exist(path) {
273
+ var exist = false;
274
+ try {
275
+ exist = !!(path && fs.statSync(path) != null);
276
+ }
277
+ catch (err) {
278
+ if (err && err.code === 'ENOENT') {
279
+ exist = false;
280
+ }
281
+ else {
282
+ throw err;
283
+ }
284
+ }
285
+ return exist;
286
+ }
287
+ exports._exist = _exist;
288
+ /**
289
+ * Checks whether a path exists.
290
+ * If the path does not exist, it will throw.
291
+ *
292
+ * @param p path to check
293
+ * @param name name only used in error message to identify the path
294
+ * @returns void
295
+ */
296
+ function _checkPath(p, name) {
297
+ _debug('check path : ' + p);
298
+ if (!_exist(p)) {
299
+ throw new Error(_loc('LIB_PathNotFound', name, p));
300
+ }
301
+ }
302
+ exports._checkPath = _checkPath;
303
+ /**
304
+ * Returns path of a tool had the tool actually been invoked. Resolves via paths.
305
+ * If you check and the tool does not exist, it will throw.
306
+ *
307
+ * @param tool name of the tool
308
+ * @param check whether to check if tool exists
309
+ * @returns string
310
+ */
311
+ function _which(tool, check) {
312
+ if (!tool) {
313
+ throw new Error('parameter \'tool\' is required');
314
+ }
315
+ // recursive when check=true
316
+ if (check) {
317
+ var result = _which(tool, false);
318
+ if (result) {
319
+ return result;
320
+ }
321
+ else {
322
+ if (process.platform == 'win32') {
323
+ throw new Error(_loc('LIB_WhichNotFound_Win', tool));
324
+ }
325
+ else {
326
+ throw new Error(_loc('LIB_WhichNotFound_Linux', tool));
327
+ }
328
+ }
329
+ }
330
+ _debug("which '" + tool + "'");
331
+ try {
332
+ // build the list of extensions to try
333
+ var extensions = [];
334
+ if (process.platform == 'win32' && process.env['PATHEXT']) {
335
+ for (var _i = 0, _a = process.env['PATHEXT'].split(path.delimiter); _i < _a.length; _i++) {
336
+ var extension = _a[_i];
337
+ if (extension) {
338
+ extensions.push(extension);
339
+ }
340
+ }
341
+ }
342
+ // if it's rooted, return it if exists. otherwise return empty.
343
+ if (_isRooted(tool)) {
344
+ var filePath = _tryGetExecutablePath(tool, extensions);
345
+ if (filePath) {
346
+ _debug("found: '" + filePath + "'");
347
+ return filePath;
348
+ }
349
+ _debug('not found');
350
+ return '';
351
+ }
352
+ // if any path separators, return empty
353
+ if (tool.indexOf('/') >= 0 || (process.platform == 'win32' && tool.indexOf('\\') >= 0)) {
354
+ _debug('not found');
355
+ return '';
356
+ }
357
+ // build the list of directories
358
+ //
359
+ // Note, technically "where" checks the current directory on Windows. From a task lib perspective,
360
+ // it feels like we should not do this. Checking the current directory seems like more of a use
361
+ // case of a shell, and the which() function exposed by the task lib should strive for consistency
362
+ // across platforms.
363
+ var directories = [];
364
+ if (process.env['PATH']) {
365
+ for (var _b = 0, _c = process.env['PATH'].split(path.delimiter); _b < _c.length; _b++) {
366
+ var p = _c[_b];
367
+ if (p) {
368
+ directories.push(p);
369
+ }
370
+ }
371
+ }
372
+ // return the first match
373
+ for (var _d = 0, directories_1 = directories; _d < directories_1.length; _d++) {
374
+ var directory = directories_1[_d];
375
+ var filePath = _tryGetExecutablePath(directory + path.sep + tool, extensions);
376
+ if (filePath) {
377
+ _debug("found: '" + filePath + "'");
378
+ return filePath;
379
+ }
380
+ }
381
+ _debug('not found');
382
+ return '';
383
+ }
384
+ catch (err) {
385
+ throw new Error(_loc('LIB_OperationFailed', 'which', err.message));
386
+ }
387
+ }
388
+ exports._which = _which;
389
+ /**
390
+ * Best effort attempt to determine whether a file exists and is executable.
391
+ * @param filePath file path to check
392
+ * @param extensions additional file extensions to try
393
+ * @return if file exists and is executable, returns the file path. otherwise empty string.
394
+ */
395
+ function _tryGetExecutablePath(filePath, extensions) {
396
+ try {
397
+ // test file exists
398
+ var stats = fs.statSync(filePath);
399
+ if (stats.isFile()) {
400
+ if (process.platform == 'win32') {
401
+ // on Windows, test for valid extension
402
+ var isExecutable = false;
403
+ var fileName = path.basename(filePath);
404
+ var dotIndex = fileName.lastIndexOf('.');
405
+ if (dotIndex >= 0) {
406
+ var upperExt_1 = fileName.substr(dotIndex).toUpperCase();
407
+ if (extensions.some(function (validExt) { return validExt.toUpperCase() == upperExt_1; })) {
408
+ return filePath;
409
+ }
410
+ }
411
+ }
412
+ else {
413
+ if (isUnixExecutable(stats)) {
414
+ return filePath;
415
+ }
416
+ }
417
+ }
418
+ }
419
+ catch (err) {
420
+ if (err.code != 'ENOENT') {
421
+ _debug("Unexpected error attempting to determine if executable file exists '" + filePath + "': " + err);
422
+ }
423
+ }
424
+ // try each extension
425
+ var originalFilePath = filePath;
426
+ for (var _i = 0, extensions_1 = extensions; _i < extensions_1.length; _i++) {
427
+ var extension = extensions_1[_i];
428
+ var found = false;
429
+ var filePath_1 = originalFilePath + extension;
430
+ try {
431
+ var stats = fs.statSync(filePath_1);
432
+ if (stats.isFile()) {
433
+ if (process.platform == 'win32') {
434
+ // preserve the case of the actual file (since an extension was appended)
435
+ try {
436
+ var directory = path.dirname(filePath_1);
437
+ var upperName = path.basename(filePath_1).toUpperCase();
438
+ for (var _a = 0, _b = fs.readdirSync(directory); _a < _b.length; _a++) {
439
+ var actualName = _b[_a];
440
+ if (upperName == actualName.toUpperCase()) {
441
+ filePath_1 = path.join(directory, actualName);
442
+ break;
443
+ }
444
+ }
445
+ }
446
+ catch (err) {
447
+ _debug("Unexpected error attempting to determine the actual case of the file '" + filePath_1 + "': " + err);
448
+ }
449
+ return filePath_1;
450
+ }
451
+ else {
452
+ if (isUnixExecutable(stats)) {
453
+ return filePath_1;
454
+ }
455
+ }
456
+ }
457
+ }
458
+ catch (err) {
459
+ if (err.code != 'ENOENT') {
460
+ _debug("Unexpected error attempting to determine if executable file exists '" + filePath_1 + "': " + err);
461
+ }
462
+ }
463
+ }
464
+ return '';
465
+ }
466
+ // on Mac/Linux, test the execute bit
467
+ // R W X R W X R W X
468
+ // 256 128 64 32 16 8 4 2 1
469
+ function isUnixExecutable(stats) {
470
+ return (stats.mode & 1) > 0 || ((stats.mode & 8) > 0 && stats.gid === process.getgid()) || ((stats.mode & 64) > 0 && stats.uid === process.getuid());
471
+ }
472
+ function _legacyFindFiles_convertPatternToRegExp(pattern) {
473
+ pattern = (process.platform == 'win32' ? pattern.replace(/\\/g, '/') : pattern) // normalize separator on Windows
474
+ .replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') // regex escape - from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript
475
+ .replace(/\\\/\\\*\\\*\\\//g, '((\/.+/)|(\/))') // replace directory globstar, e.g. /hello/**/world
476
+ .replace(/\\\*\\\*/g, '.*') // replace remaining globstars with a wildcard that can span directory separators, e.g. /hello/**dll
477
+ .replace(/\\\*/g, '[^\/]*') // replace asterisks with a wildcard that cannot span directory separators, e.g. /hello/*.dll
478
+ .replace(/\\\?/g, '[^\/]'); // replace single character wildcards, e.g. /hello/log?.dll
479
+ pattern = "^" + pattern + "$";
480
+ var flags = process.platform == 'win32' ? 'i' : '';
481
+ return new RegExp(pattern, flags);
482
+ }
483
+ exports._legacyFindFiles_convertPatternToRegExp = _legacyFindFiles_convertPatternToRegExp;
484
+ function _cloneMatchOptions(matchOptions) {
485
+ return {
486
+ debug: matchOptions.debug,
487
+ nobrace: matchOptions.nobrace,
488
+ noglobstar: matchOptions.noglobstar,
489
+ dot: matchOptions.dot,
490
+ noext: matchOptions.noext,
491
+ nocase: matchOptions.nocase,
492
+ nonull: matchOptions.nonull,
493
+ matchBase: matchOptions.matchBase,
494
+ nocomment: matchOptions.nocomment,
495
+ nonegate: matchOptions.nonegate,
496
+ flipNegate: matchOptions.flipNegate
497
+ };
498
+ }
499
+ exports._cloneMatchOptions = _cloneMatchOptions;
500
+ function _getFindInfoFromPattern(defaultRoot, pattern, matchOptions) {
501
+ // parameter validation
502
+ if (!defaultRoot) {
503
+ throw new Error('getFindRootFromPattern() parameter defaultRoot cannot be empty');
504
+ }
505
+ if (!pattern) {
506
+ throw new Error('getFindRootFromPattern() parameter pattern cannot be empty');
507
+ }
508
+ if (!matchOptions.nobrace) {
509
+ throw new Error('getFindRootFromPattern() expected matchOptions.nobrace to be true');
510
+ }
511
+ // for the sake of determining the findPath, pretend nocase=false
512
+ matchOptions = _cloneMatchOptions(matchOptions);
513
+ matchOptions.nocase = false;
514
+ // check if basename only and matchBase=true
515
+ if (matchOptions.matchBase &&
516
+ !_isRooted(pattern) &&
517
+ (process.platform == 'win32' ? pattern.replace(/\\/g, '/') : pattern).indexOf('/') < 0) {
518
+ return {
519
+ adjustedPattern: pattern,
520
+ findPath: defaultRoot,
521
+ statOnly: false,
522
+ };
523
+ }
524
+ // the technique applied by this function is to use the information on the Minimatch object determine
525
+ // the findPath. Minimatch breaks the pattern into path segments, and exposes information about which
526
+ // segments are literal vs patterns.
527
+ //
528
+ // note, the technique currently imposes a limitation for drive-relative paths with a glob in the
529
+ // first segment, e.g. C:hello*/world. it's feasible to overcome this limitation, but is left unsolved
530
+ // for now.
531
+ var minimatchObj = new minimatch.Minimatch(pattern, matchOptions);
532
+ // the "set" property is an array of arrays of parsed path segment info. the outer array should only
533
+ // contain one item, otherwise something went wrong. brace expansion can result in multiple arrays,
534
+ // but that should be turned off by the time this function is reached.
535
+ if (minimatchObj.set.length != 1) {
536
+ throw new Error('getFindRootFromPattern() expected Minimatch(...).set.length to be 1. Actual: ' + minimatchObj.set.length);
537
+ }
538
+ var literalSegments = [];
539
+ for (var _i = 0, _a = minimatchObj.set[0]; _i < _a.length; _i++) {
540
+ var parsedSegment = _a[_i];
541
+ if (typeof parsedSegment == 'string') {
542
+ // the item is a string when the original input for the path segment does not contain any
543
+ // unescaped glob characters.
544
+ //
545
+ // note, the string here is already unescaped (i.e. glob escaping removed), so it is ready
546
+ // to pass to find() as-is. for example, an input string 'hello\\*world' => 'hello*world'.
547
+ literalSegments.push(parsedSegment);
548
+ continue;
549
+ }
550
+ break;
551
+ }
552
+ // join the literal segments back together. Minimatch converts '\' to '/' on Windows, then squashes
553
+ // consequetive slashes, and finally splits on slash. this means that UNC format is lost, but can
554
+ // be detected from the original pattern.
555
+ var joinedSegments = literalSegments.join('/');
556
+ if (joinedSegments && process.platform == 'win32' && _startsWith(pattern.replace(/\\/g, '/'), '//')) {
557
+ joinedSegments = '/' + joinedSegments; // restore UNC format
558
+ }
559
+ // determine the find path
560
+ var findPath;
561
+ if (_isRooted(pattern)) { // the pattern was rooted
562
+ findPath = joinedSegments;
563
+ }
564
+ else if (joinedSegments) { // the pattern was not rooted, and literal segments were found
565
+ findPath = _ensureRooted(defaultRoot, joinedSegments);
566
+ }
567
+ else { // the pattern was not rooted, and no literal segments were found
568
+ findPath = defaultRoot;
569
+ }
570
+ // clean up the path
571
+ if (findPath) {
572
+ findPath = _getDirectoryName(_ensureRooted(findPath, '_')); // hack to remove unnecessary trailing slash
573
+ findPath = _normalizeSeparators(findPath); // normalize slashes
574
+ }
575
+ return {
576
+ adjustedPattern: _ensurePatternRooted(defaultRoot, pattern),
577
+ findPath: findPath,
578
+ statOnly: literalSegments.length == minimatchObj.set[0].length,
579
+ };
580
+ }
581
+ exports._getFindInfoFromPattern = _getFindInfoFromPattern;
582
+ function _ensurePatternRooted(root, p) {
583
+ if (!root) {
584
+ throw new Error('ensurePatternRooted() parameter "root" cannot be empty');
585
+ }
586
+ if (!p) {
587
+ throw new Error('ensurePatternRooted() parameter "p" cannot be empty');
588
+ }
589
+ if (_isRooted(p)) {
590
+ return p;
591
+ }
592
+ // normalize root
593
+ root = _normalizeSeparators(root);
594
+ // escape special glob characters
595
+ root = (process.platform == 'win32' ? root : root.replace(/\\/g, '\\\\')) // escape '\' on OSX/Linux
596
+ .replace(/(\[)(?=[^\/]+\])/g, '[[]') // escape '[' when ']' follows within the path segment
597
+ .replace(/\?/g, '[?]') // escape '?'
598
+ .replace(/\*/g, '[*]') // escape '*'
599
+ .replace(/\+\(/g, '[+](') // escape '+('
600
+ .replace(/@\(/g, '[@](') // escape '@('
601
+ .replace(/!\(/g, '[!]('); // escape '!('
602
+ return _ensureRooted(root, p);
603
+ }
604
+ exports._ensurePatternRooted = _ensurePatternRooted;
605
+ //-------------------------------------------------------------------
606
+ // Populate the vault with sensitive data. Inputs and Endpoints
607
+ //-------------------------------------------------------------------
608
+ function _loadData() {
609
+ // in agent, prefer TempDirectory then workFolder.
610
+ // In interactive dev mode, it won't be
611
+ var keyPath = _getVariable("agent.TempDirectory") || _getVariable("agent.workFolder") || process.cwd();
612
+ exports._vault = new vm.Vault(keyPath);
613
+ exports._knownVariableMap = {};
614
+ _debug('loading inputs and endpoints');
615
+ var loaded = 0;
616
+ for (var envvar in process.env) {
617
+ if (_startsWith(envvar, 'INPUT_') ||
618
+ _startsWith(envvar, 'ENDPOINT_AUTH_') ||
619
+ _startsWith(envvar, 'SECUREFILE_TICKET_') ||
620
+ _startsWith(envvar, 'SECRET_') ||
621
+ _startsWith(envvar, 'VSTS_TASKVARIABLE_')) {
622
+ // Record the secret variable metadata. This is required by getVariable to know whether
623
+ // to retrieve the value from the vault. In a 2.104.1 agent or higher, this metadata will
624
+ // be overwritten when the VSTS_SECRET_VARIABLES env var is processed below.
625
+ if (_startsWith(envvar, 'SECRET_')) {
626
+ var variableName = envvar.substring('SECRET_'.length);
627
+ if (variableName) {
628
+ // This is technically not the variable name (has underscores instead of dots),
629
+ // but it's good enough to make getVariable work in a pre-2.104.1 agent where
630
+ // the VSTS_SECRET_VARIABLES env var is not defined.
631
+ exports._knownVariableMap[_getVariableKey(variableName)] = { name: variableName, secret: true };
632
+ }
633
+ }
634
+ // store the secret
635
+ var value = process.env[envvar];
636
+ if (value) {
637
+ ++loaded;
638
+ _debug('loading ' + envvar);
639
+ exports._vault.storeSecret(envvar, value);
640
+ delete process.env[envvar];
641
+ }
642
+ }
643
+ }
644
+ _debug('loaded ' + loaded);
645
+ // store public variable metadata
646
+ var names;
647
+ try {
648
+ names = JSON.parse(process.env['VSTS_PUBLIC_VARIABLES'] || '[]');
649
+ }
650
+ catch (err) {
651
+ throw new Error('Failed to parse VSTS_PUBLIC_VARIABLES as JSON. ' + err); // may occur during interactive testing
652
+ }
653
+ names.forEach(function (name) {
654
+ exports._knownVariableMap[_getVariableKey(name)] = { name: name, secret: false };
655
+ });
656
+ delete process.env['VSTS_PUBLIC_VARIABLES'];
657
+ // store secret variable metadata
658
+ try {
659
+ names = JSON.parse(process.env['VSTS_SECRET_VARIABLES'] || '[]');
660
+ }
661
+ catch (err) {
662
+ throw new Error('Failed to parse VSTS_SECRET_VARIABLES as JSON. ' + err); // may occur during interactive testing
663
+ }
664
+ names.forEach(function (name) {
665
+ exports._knownVariableMap[_getVariableKey(name)] = { name: name, secret: true };
666
+ });
667
+ delete process.env['VSTS_SECRET_VARIABLES'];
668
+ // avoid loading twice (overwrites .taskkey)
669
+ global['_vsts_task_lib_loaded'] = true;
670
+ }
671
+ exports._loadData = _loadData;
672
+ //--------------------------------------------------------------------------------
673
+ // Internal path helpers.
674
+ //--------------------------------------------------------------------------------
675
+ /**
676
+ * Defines if path is unc-path.
677
+ *
678
+ * @param path a path to a file.
679
+ * @returns true if path starts with double backslash, otherwise returns false.
680
+ */
681
+ function _isUncPath(path) {
682
+ return /^\\\\[^\\]/.test(path);
683
+ }
684
+ exports._isUncPath = _isUncPath;
685
+ function _ensureRooted(root, p) {
686
+ if (!root) {
687
+ throw new Error('ensureRooted() parameter "root" cannot be empty');
688
+ }
689
+ if (!p) {
690
+ throw new Error('ensureRooted() parameter "p" cannot be empty');
691
+ }
692
+ if (_isRooted(p)) {
693
+ return p;
694
+ }
695
+ if (process.platform == 'win32' && root.match(/^[A-Z]:$/i)) { // e.g. C:
696
+ return root + p;
697
+ }
698
+ // ensure root ends with a separator
699
+ if (_endsWith(root, '/') || (process.platform == 'win32' && _endsWith(root, '\\'))) {
700
+ // root already ends with a separator
701
+ }
702
+ else {
703
+ root += path.sep; // append separator
704
+ }
705
+ return root + p;
706
+ }
707
+ exports._ensureRooted = _ensureRooted;
708
+ /**
709
+ * Determines the parent path and trims trailing slashes (when safe). Path separators are normalized
710
+ * in the result. This function works similar to the .NET System.IO.Path.GetDirectoryName() method.
711
+ * For example, C:\hello\world\ returns C:\hello\world (trailing slash removed). Returns empty when
712
+ * no higher directory can be determined.
713
+ */
714
+ function _getDirectoryName(p) {
715
+ // short-circuit if empty
716
+ if (!p) {
717
+ return '';
718
+ }
719
+ // normalize separators
720
+ p = _normalizeSeparators(p);
721
+ // on Windows, the goal of this function is to match the behavior of
722
+ // [System.IO.Path]::GetDirectoryName(), e.g.
723
+ // C:/ =>
724
+ // C:/hello => C:\
725
+ // C:/hello/ => C:\hello
726
+ // C:/hello/world => C:\hello
727
+ // C:/hello/world/ => C:\hello\world
728
+ // C: =>
729
+ // C:hello => C:
730
+ // C:hello/ => C:hello
731
+ // / =>
732
+ // /hello => \
733
+ // /hello/ => \hello
734
+ // //hello =>
735
+ // //hello/ =>
736
+ // //hello/world =>
737
+ // //hello/world/ => \\hello\world
738
+ //
739
+ // unfortunately, path.dirname() can't simply be used. for example, on Windows
740
+ // it yields different results from Path.GetDirectoryName:
741
+ // C:/ => C:/
742
+ // C:/hello => C:/
743
+ // C:/hello/ => C:/
744
+ // C:/hello/world => C:/hello
745
+ // C:/hello/world/ => C:/hello
746
+ // C: => C:
747
+ // C:hello => C:
748
+ // C:hello/ => C:
749
+ // / => /
750
+ // /hello => /
751
+ // /hello/ => /
752
+ // //hello => /
753
+ // //hello/ => /
754
+ // //hello/world => //hello/world
755
+ // //hello/world/ => //hello/world/
756
+ // //hello/world/again => //hello/world/
757
+ // //hello/world/again/ => //hello/world/
758
+ // //hello/world/again/again => //hello/world/again
759
+ // //hello/world/again/again/ => //hello/world/again
760
+ if (process.platform == 'win32') {
761
+ if (/^[A-Z]:\\?[^\\]+$/i.test(p)) { // e.g. C:\hello or C:hello
762
+ return p.charAt(2) == '\\' ? p.substring(0, 3) : p.substring(0, 2);
763
+ }
764
+ else if (/^[A-Z]:\\?$/i.test(p)) { // e.g. C:\ or C:
765
+ return '';
766
+ }
767
+ var lastSlashIndex = p.lastIndexOf('\\');
768
+ if (lastSlashIndex < 0) { // file name only
769
+ return '';
770
+ }
771
+ else if (p == '\\') { // relative root
772
+ return '';
773
+ }
774
+ else if (lastSlashIndex == 0) { // e.g. \\hello
775
+ return '\\';
776
+ }
777
+ else if (/^\\\\[^\\]+(\\[^\\]*)?$/.test(p)) { // UNC root, e.g. \\hello or \\hello\ or \\hello\world
778
+ return '';
779
+ }
780
+ return p.substring(0, lastSlashIndex); // e.g. hello\world => hello or hello\world\ => hello\world
781
+ // note, this means trailing slashes for non-root directories
782
+ // (i.e. not C:\, \, or \\unc\) will simply be removed.
783
+ }
784
+ // OSX/Linux
785
+ if (p.indexOf('/') < 0) { // file name only
786
+ return '';
787
+ }
788
+ else if (p == '/') {
789
+ return '';
790
+ }
791
+ else if (_endsWith(p, '/')) {
792
+ return p.substring(0, p.length - 1);
793
+ }
794
+ return path.dirname(p);
795
+ }
796
+ exports._getDirectoryName = _getDirectoryName;
797
+ /**
798
+ * On OSX/Linux, true if path starts with '/'. On Windows, true for paths like:
799
+ * \, \hello, \\hello\share, C:, and C:\hello (and corresponding alternate separator cases).
800
+ */
801
+ function _isRooted(p) {
802
+ p = _normalizeSeparators(p);
803
+ if (!p) {
804
+ throw new Error('isRooted() parameter "p" cannot be empty');
805
+ }
806
+ if (process.platform == 'win32') {
807
+ return _startsWith(p, '\\') || // e.g. \ or \hello or \\hello
808
+ /^[A-Z]:/i.test(p); // e.g. C: or C:\hello
809
+ }
810
+ return _startsWith(p, '/'); // e.g. /hello
811
+ }
812
+ exports._isRooted = _isRooted;
813
+ function _normalizeSeparators(p) {
814
+ p = p || '';
815
+ if (process.platform == 'win32') {
816
+ // convert slashes on Windows
817
+ p = p.replace(/\//g, '\\');
818
+ // remove redundant slashes
819
+ var isUnc = /^\\\\+[^\\]/.test(p); // e.g. \\hello
820
+ return (isUnc ? '\\' : '') + p.replace(/\\\\+/g, '\\'); // preserve leading // for UNC
821
+ }
822
+ // remove redundant slashes
823
+ return p.replace(/\/\/+/g, '/');
824
+ }
825
+ exports._normalizeSeparators = _normalizeSeparators;
826
+ //-----------------------------------------------------
827
+ // Expose proxy information to vsts-node-api
828
+ //-----------------------------------------------------
829
+ function _exposeProxySettings() {
830
+ var proxyUrl = _getVariable('Agent.ProxyUrl');
831
+ if (proxyUrl && proxyUrl.length > 0) {
832
+ var proxyUsername = _getVariable('Agent.ProxyUsername');
833
+ var proxyPassword = _getVariable('Agent.ProxyPassword');
834
+ var proxyBypassHostsJson = _getVariable('Agent.ProxyBypassList');
835
+ global['_vsts_task_lib_proxy_url'] = proxyUrl;
836
+ global['_vsts_task_lib_proxy_username'] = proxyUsername;
837
+ global['_vsts_task_lib_proxy_bypass'] = proxyBypassHostsJson;
838
+ global['_vsts_task_lib_proxy_password'] = _exposeTaskLibSecret('proxy', proxyPassword || '');
839
+ _debug('expose agent proxy configuration.');
840
+ global['_vsts_task_lib_proxy'] = true;
841
+ }
842
+ }
843
+ exports._exposeProxySettings = _exposeProxySettings;
844
+ //-----------------------------------------------------
845
+ // Expose certificate information to vsts-node-api
846
+ //-----------------------------------------------------
847
+ function _exposeCertSettings() {
848
+ var ca = _getVariable('Agent.CAInfo');
849
+ if (ca) {
850
+ global['_vsts_task_lib_cert_ca'] = ca;
851
+ }
852
+ var clientCert = _getVariable('Agent.ClientCert');
853
+ if (clientCert) {
854
+ var clientCertKey = _getVariable('Agent.ClientCertKey');
855
+ var clientCertArchive = _getVariable('Agent.ClientCertArchive');
856
+ var clientCertPassword = _getVariable('Agent.ClientCertPassword');
857
+ global['_vsts_task_lib_cert_clientcert'] = clientCert;
858
+ global['_vsts_task_lib_cert_key'] = clientCertKey;
859
+ global['_vsts_task_lib_cert_archive'] = clientCertArchive;
860
+ global['_vsts_task_lib_cert_passphrase'] = _exposeTaskLibSecret('cert', clientCertPassword || '');
861
+ }
862
+ if (ca || clientCert) {
863
+ _debug('expose agent certificate configuration.');
864
+ global['_vsts_task_lib_cert'] = true;
865
+ }
866
+ var skipCertValidation = _getVariable('Agent.SkipCertValidation') || 'false';
867
+ if (skipCertValidation) {
868
+ global['_vsts_task_lib_skip_cert_validation'] = skipCertValidation.toUpperCase() === 'TRUE';
869
+ }
870
+ }
871
+ exports._exposeCertSettings = _exposeCertSettings;
872
+ // We store the encryption key on disk and hold the encrypted content and key file in memory
873
+ // return base64encoded<keyFilePath>:base64encoded<encryptedContent>
874
+ // downstream vsts-node-api will retrieve the secret later
875
+ function _exposeTaskLibSecret(keyFile, secret) {
876
+ if (secret) {
877
+ var encryptKey = crypto.randomBytes(256);
878
+ var cipher = crypto.createCipher("aes-256-ctr", encryptKey);
879
+ var encryptedContent = cipher.update(secret, "utf8", "hex");
880
+ encryptedContent += cipher.final("hex");
881
+ var storageFile = path.join(_getVariable('Agent.TempDirectory') || _getVariable("agent.workFolder") || process.cwd(), keyFile);
882
+ fs.writeFileSync(storageFile, encryptKey.toString('base64'), { encoding: 'utf8' });
883
+ return new Buffer(storageFile).toString('base64') + ':' + new Buffer(encryptedContent).toString('base64');
884
+ }
885
+ }