azure-pipelines-task-lib
Version:
Azure Pipelines Task SDK
939 lines (938 loc) • 38.2 kB
JavaScript
var _a, _b;
Object.defineProperty(exports, "__esModule", { value: true });
exports.isSigPipeError = 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._truncateBeforeSensitiveKeyword = exports._endsWith = exports._startsWith = exports.IssueAuditAction = exports.IssueSource = exports._vault = exports._knownVariableMap = void 0;
var fs = require("fs");
var path = require("path");
var os = require("os");
var minimatch = require("minimatch");
var util = require("util");
var tcm = require("./taskcommand");
var vm = require("./vault");
var semver = require("semver");
var crypto = require("crypto");
/**
* Hash table of known variable info. The formatted env var name is the lookup key.
*
* The purpose of this hash table is to keep track of known variables. The hash table
* needs to be maintained for multiple reasons:
* 1) to distinguish between env vars and job vars
* 2) to distinguish between secret vars and public
* 3) to know the real variable name and not just the formatted env var name.
*/
exports._knownVariableMap = {};
var _commandCorrelationId;
//-----------------------------------------------------
// Enums
//-----------------------------------------------------
var IssueSource;
(function (IssueSource) {
IssueSource["CustomerScript"] = "CustomerScript";
IssueSource["TaskInternal"] = "TaskInternal";
})(IssueSource = exports.IssueSource || (exports.IssueSource = {}));
var IssueAuditAction;
(function (IssueAuditAction) {
IssueAuditAction[IssueAuditAction["Unknown"] = 0] = "Unknown";
IssueAuditAction[IssueAuditAction["ShellTasksValidation"] = 1] = "ShellTasksValidation";
})(IssueAuditAction = exports.IssueAuditAction || (exports.IssueAuditAction = {}));
//-----------------------------------------------------
// Validation Checks
//-----------------------------------------------------
// async await needs generators in node 4.x+
if (semver.lt(process.versions.node, '4.2.0')) {
_warning('Tasks require a new agent. Upgrade your agent or node to 4.2.0 or later', IssueSource.TaskInternal);
}
//-----------------------------------------------------
// String convenience
//-----------------------------------------------------
function _startsWith(str, start) {
return str.slice(0, start.length) == start;
}
exports._startsWith = _startsWith;
function _endsWith(str, end) {
return str.slice(-end.length) == end;
}
exports._endsWith = _endsWith;
function _truncateBeforeSensitiveKeyword(str, sensitiveKeywordsPattern) {
if (!str) {
return str;
}
var index = str.search(sensitiveKeywordsPattern);
if (index <= 0) {
return str;
}
return "".concat(str.substring(0, index), "...");
}
exports._truncateBeforeSensitiveKeyword = _truncateBeforeSensitiveKeyword;
//-----------------------------------------------------
// General Helpers
//-----------------------------------------------------
var _outStream = process.stdout;
var _errStream = process.stderr;
function _writeLine(str) {
_outStream.write(str + os.EOL);
}
exports._writeLine = _writeLine;
function _setStdStream(stdStream) {
_outStream = stdStream;
}
exports._setStdStream = _setStdStream;
function _setErrStream(errStream) {
_errStream = errStream;
}
exports._setErrStream = _setErrStream;
//-----------------------------------------------------
// Loc Helpers
//-----------------------------------------------------
var _locStringCache = {};
var _resourceFiles = {};
var _libResourceFileLoaded = false;
var _resourceCulture = 'en-US';
function _loadResJson(resjsonFile) {
var resJson;
if (_exist(resjsonFile)) {
var resjsonContent = fs.readFileSync(resjsonFile, 'utf8').toString();
// remove BOM
if (resjsonContent.indexOf('\uFEFF') == 0) {
resjsonContent = resjsonContent.slice(1);
}
try {
resJson = JSON.parse(resjsonContent);
}
catch (err) {
_debug('unable to parse resjson with err: ' + err.message);
}
}
else {
_debug('.resjson file not found: ' + resjsonFile);
}
return resJson;
}
function _loadLocStrings(resourceFile, culture) {
var locStrings = {};
if (_exist(resourceFile)) {
var resourceJson = require(resourceFile);
if (resourceJson && resourceJson.hasOwnProperty('messages')) {
var locResourceJson;
// load up resource resjson for different culture
var localizedResourceFile = path.join(path.dirname(resourceFile), 'Strings', 'resources.resjson');
var upperCulture = culture.toUpperCase();
var cultures = [];
try {
cultures = fs.readdirSync(localizedResourceFile);
}
catch (ex) { }
for (var i = 0; i < cultures.length; i++) {
if (cultures[i].toUpperCase() == upperCulture) {
localizedResourceFile = path.join(localizedResourceFile, cultures[i], 'resources.resjson');
if (_exist(localizedResourceFile)) {
locResourceJson = _loadResJson(localizedResourceFile);
}
break;
}
}
for (var key in resourceJson.messages) {
if (locResourceJson && locResourceJson.hasOwnProperty('loc.messages.' + key)) {
locStrings[key] = locResourceJson['loc.messages.' + key];
}
else {
locStrings[key] = resourceJson.messages[key];
}
}
}
}
else {
_warning('LIB_ResourceFile does not exist', IssueSource.TaskInternal);
}
return locStrings;
}
/**
* Sets the location of the resources json. This is typically the task.json file.
* Call once at the beginning of the script before any calls to loc.
* @param path Full path to the json.
* @param ignoreWarnings Won't throw warnings if path already set.
* @returns void
*/
function _setResourcePath(path, ignoreWarnings) {
if (ignoreWarnings === void 0) { ignoreWarnings = false; }
if (process.env['TASKLIB_INPROC_UNITS']) {
_resourceFiles = {};
_libResourceFileLoaded = false;
_locStringCache = {};
_resourceCulture = 'en-US';
}
if (!_resourceFiles[path]) {
_checkPath(path, 'resource file path');
_resourceFiles[path] = path;
_debug('adding resource file: ' + path);
_resourceCulture = _getVariable('system.culture') || _resourceCulture;
var locStrs = _loadLocStrings(path, _resourceCulture);
for (var key in locStrs) {
//cache loc string
_locStringCache[key] = locStrs[key];
}
}
else {
if (ignoreWarnings) {
}
else {
_warning(_loc('LIB_ResourceFileAlreadySet', path), IssueSource.TaskInternal);
}
}
}
exports._setResourcePath = _setResourcePath;
/**
* Gets the localized string from the json resource file. Optionally formats with additional params.
*
* @param key key of the resources string in the resource file
* @param param additional params for formatting the string
* @returns string
*/
function _loc(key) {
var param = [];
for (var _i = 1; _i < arguments.length; _i++) {
param[_i - 1] = arguments[_i];
}
if (!_libResourceFileLoaded) {
// merge loc strings from azure-pipelines-task-lib.
var libResourceFile = path.join(__dirname, 'lib.json');
var libLocStrs = _loadLocStrings(libResourceFile, _resourceCulture);
for (var libKey in libLocStrs) {
//cache azure-pipelines-task-lib loc string
_locStringCache[libKey] = libLocStrs[libKey];
}
_libResourceFileLoaded = true;
}
var locString;
;
if (_locStringCache.hasOwnProperty(key)) {
locString = _locStringCache[key];
}
else {
if (Object.keys(_resourceFiles).length <= 0) {
_warning("Resource file haven't been set, can't find loc string for key: ".concat(key), IssueSource.TaskInternal);
}
else {
_warning("Can't find loc string for key: ".concat(key));
}
locString = key;
}
if (param.length > 0) {
return util.format.apply(this, [locString].concat(param));
}
else {
return locString;
}
}
exports._loc = _loc;
//-----------------------------------------------------
// Input Helpers
//-----------------------------------------------------
/**
* Gets a variable value that is defined on the build/release definition or set at runtime.
*
* @param name name of the variable to get
* @returns string
*/
function _getVariable(name) {
var varval;
// get the metadata
var info;
var key = _getVariableKey(name);
if (exports._knownVariableMap.hasOwnProperty(key)) {
info = exports._knownVariableMap[key];
}
if (info && info.secret) {
// get the secret value
varval = exports._vault.retrieveSecret('SECRET_' + key);
}
else {
// get the public value
varval = process.env[key];
// fallback for pre 2.104.1 agent
if (!varval && name.toUpperCase() == 'AGENT.JOBSTATUS') {
varval = process.env['agent.jobstatus'];
}
}
_debug(name + '=' + varval);
return varval;
}
exports._getVariable = _getVariable;
function _getVariableKey(name) {
if (!name) {
throw new Error(_loc('LIB_ParameterIsRequired', 'name'));
}
return name.replace(/\./g, '_').replace(/ /g, '_').toUpperCase();
}
exports._getVariableKey = _getVariableKey;
//-----------------------------------------------------
// Cmd Helpers
//-----------------------------------------------------
function _command(command, properties, message) {
var taskCmd = new tcm.TaskCommand(command, properties, message);
_writeLine(taskCmd.toString());
}
exports._command = _command;
function _warning(message, source, auditAction) {
if (source === void 0) { source = IssueSource.TaskInternal; }
_command('task.issue', {
'type': 'warning',
'source': source,
'correlationId': _commandCorrelationId,
'auditAction': auditAction
}, message);
}
exports._warning = _warning;
function _error(message, source, auditAction) {
if (source === void 0) { source = IssueSource.TaskInternal; }
_command('task.issue', {
'type': 'error',
'source': source,
'correlationId': _commandCorrelationId,
'auditAction': auditAction
}, message);
}
exports._error = _error;
var debugMode = ((_a = _getVariable('system.debug')) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === 'true';
var shouldCheckDebugMode = ((_b = _getVariable('DistributedTask.Tasks.Node.SkipDebugLogsWhenDebugModeOff')) === null || _b === void 0 ? void 0 : _b.toLowerCase()) === 'true';
function _debug(message) {
if (!shouldCheckDebugMode
|| (shouldCheckDebugMode && debugMode)) {
_command('task.debug', null, message);
}
}
exports._debug = _debug;
// //-----------------------------------------------------
// // Disk Functions
// //-----------------------------------------------------
/**
* Returns whether a path exists.
*
* @param path path to check
* @returns boolean
*/
function _exist(path) {
var exist = false;
try {
exist = !!(path && fs.statSync(path) != null);
}
catch (err) {
if (err && err.code === 'ENOENT') {
exist = false;
}
else {
throw err;
}
}
return exist;
}
exports._exist = _exist;
/**
* Checks whether a path exists.
* If the path does not exist, it will throw.
*
* @param p path to check
* @param name name only used in error message to identify the path
* @returns void
*/
function _checkPath(p, name) {
_debug('check path : ' + p);
if (!_exist(p)) {
throw new Error(_loc('LIB_PathNotFound', name, p));
}
}
exports._checkPath = _checkPath;
/**
* Returns path of a tool had the tool actually been invoked. Resolves via paths.
* If you check and the tool does not exist, it will throw.
*
* @param tool name of the tool
* @param check whether to check if tool exists
* @returns string
*/
function _which(tool, check) {
if (!tool) {
throw new Error('parameter \'tool\' is required');
}
// recursive when check=true
if (check) {
var result = _which(tool, false);
if (result) {
return result;
}
else {
if (process.platform == 'win32') {
throw new Error(_loc('LIB_WhichNotFound_Win', tool));
}
else {
throw new Error(_loc('LIB_WhichNotFound_Linux', tool));
}
}
}
_debug("which '".concat(tool, "'"));
try {
// build the list of extensions to try
var extensions = [];
if (process.platform == 'win32' && process.env['PATHEXT']) {
for (var _i = 0, _a = process.env['PATHEXT'].split(path.delimiter); _i < _a.length; _i++) {
var extension = _a[_i];
if (extension) {
extensions.push(extension);
}
}
}
// if it's rooted, return it if exists. otherwise return empty.
if (_isRooted(tool)) {
var filePath = _tryGetExecutablePath(tool, extensions);
if (filePath) {
_debug("found: '".concat(filePath, "'"));
return filePath;
}
_debug('not found');
return '';
}
// if any path separators, return empty
if (tool.indexOf('/') >= 0 || (process.platform == 'win32' && tool.indexOf('\\') >= 0)) {
_debug('not found');
return '';
}
// build the list of directories
//
// Note, technically "where" checks the current directory on Windows. From a task lib perspective,
// it feels like we should not do this. Checking the current directory seems like more of a use
// case of a shell, and the which() function exposed by the task lib should strive for consistency
// across platforms.
var directories = [];
if (process.env['PATH']) {
for (var _b = 0, _c = process.env['PATH'].split(path.delimiter); _b < _c.length; _b++) {
var p = _c[_b];
if (p) {
directories.push(p);
}
}
}
// return the first match
for (var _d = 0, directories_1 = directories; _d < directories_1.length; _d++) {
var directory = directories_1[_d];
var filePath = _tryGetExecutablePath(directory + path.sep + tool, extensions);
if (filePath) {
_debug("found: '".concat(filePath, "'"));
return filePath;
}
}
_debug('not found');
return '';
}
catch (err) {
throw new Error(_loc('LIB_OperationFailed', 'which', err.message));
}
}
exports._which = _which;
/**
* Best effort attempt to determine whether a file exists and is executable.
* @param filePath file path to check
* @param extensions additional file extensions to try
* @return if file exists and is executable, returns the file path. otherwise empty string.
*/
function _tryGetExecutablePath(filePath, extensions) {
try {
// test file exists
var stats = fs.statSync(filePath);
if (stats.isFile()) {
if (process.platform == 'win32') {
// on Windows, test for valid extension
var isExecutable = false;
var fileName = path.basename(filePath);
var dotIndex = fileName.lastIndexOf('.');
if (dotIndex >= 0) {
var upperExt_1 = fileName.substr(dotIndex).toUpperCase();
if (extensions.some(function (validExt) { return validExt.toUpperCase() == upperExt_1; })) {
return filePath;
}
}
}
else {
if (isUnixExecutable(stats)) {
return filePath;
}
}
}
}
catch (err) {
if (err.code != 'ENOENT') {
_debug("Unexpected error attempting to determine if executable file exists '".concat(filePath, "': ").concat(err));
}
}
// try each extension
var originalFilePath = filePath;
for (var _i = 0, extensions_1 = extensions; _i < extensions_1.length; _i++) {
var extension = extensions_1[_i];
var found = false;
var filePath_1 = originalFilePath + extension;
try {
var stats = fs.statSync(filePath_1);
if (stats.isFile()) {
if (process.platform == 'win32') {
// preserve the case of the actual file (since an extension was appended)
try {
var directory = path.dirname(filePath_1);
var upperName = path.basename(filePath_1).toUpperCase();
for (var _a = 0, _b = fs.readdirSync(directory); _a < _b.length; _a++) {
var actualName = _b[_a];
if (upperName == actualName.toUpperCase()) {
filePath_1 = path.join(directory, actualName);
break;
}
}
}
catch (err) {
_debug("Unexpected error attempting to determine the actual case of the file '".concat(filePath_1, "': ").concat(err));
}
return filePath_1;
}
else {
if (isUnixExecutable(stats)) {
return filePath_1;
}
}
}
}
catch (err) {
if (err.code != 'ENOENT') {
_debug("Unexpected error attempting to determine if executable file exists '".concat(filePath_1, "': ").concat(err));
}
}
}
return '';
}
// on Mac/Linux, test the execute bit
// R W X R W X R W X
// 256 128 64 32 16 8 4 2 1
function isUnixExecutable(stats) {
return (stats.mode & 1) > 0 || ((stats.mode & 8) > 0 && stats.gid === process.getgid()) || ((stats.mode & 64) > 0 && stats.uid === process.getuid());
}
function _legacyFindFiles_convertPatternToRegExp(pattern) {
pattern = (process.platform == 'win32' ? pattern.replace(/\\/g, '/') : pattern) // normalize separator on Windows
.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') // regex escape - from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript
.replace(/\\\/\\\*\\\*\\\//g, '((\/.+/)|(\/))') // replace directory globstar, e.g. /hello/**/world
.replace(/\\\*\\\*/g, '.*') // replace remaining globstars with a wildcard that can span directory separators, e.g. /hello/**dll
.replace(/\\\*/g, '[^\/]*') // replace asterisks with a wildcard that cannot span directory separators, e.g. /hello/*.dll
.replace(/\\\?/g, '[^\/]'); // replace single character wildcards, e.g. /hello/log?.dll
pattern = "^".concat(pattern, "$");
var flags = process.platform == 'win32' ? 'i' : '';
return new RegExp(pattern, flags);
}
exports._legacyFindFiles_convertPatternToRegExp = _legacyFindFiles_convertPatternToRegExp;
function _cloneMatchOptions(matchOptions) {
return {
debug: matchOptions.debug,
nobrace: matchOptions.nobrace,
noglobstar: matchOptions.noglobstar,
dot: matchOptions.dot,
noext: matchOptions.noext,
nocase: matchOptions.nocase,
nonull: matchOptions.nonull,
matchBase: matchOptions.matchBase,
nocomment: matchOptions.nocomment,
nonegate: matchOptions.nonegate,
flipNegate: matchOptions.flipNegate
};
}
exports._cloneMatchOptions = _cloneMatchOptions;
function _getFindInfoFromPattern(defaultRoot, pattern, matchOptions) {
// parameter validation
if (!defaultRoot) {
throw new Error('getFindRootFromPattern() parameter defaultRoot cannot be empty');
}
if (!pattern) {
throw new Error('getFindRootFromPattern() parameter pattern cannot be empty');
}
if (!matchOptions.nobrace) {
throw new Error('getFindRootFromPattern() expected matchOptions.nobrace to be true');
}
// for the sake of determining the findPath, pretend nocase=false
matchOptions = _cloneMatchOptions(matchOptions);
matchOptions.nocase = false;
// check if basename only and matchBase=true
if (matchOptions.matchBase &&
!_isRooted(pattern) &&
(process.platform == 'win32' ? pattern.replace(/\\/g, '/') : pattern).indexOf('/') < 0) {
return {
adjustedPattern: pattern,
findPath: defaultRoot,
statOnly: false,
};
}
// the technique applied by this function is to use the information on the Minimatch object determine
// the findPath. Minimatch breaks the pattern into path segments, and exposes information about which
// segments are literal vs patterns.
//
// note, the technique currently imposes a limitation for drive-relative paths with a glob in the
// first segment, e.g. C:hello*/world. it's feasible to overcome this limitation, but is left unsolved
// for now.
var minimatchObj = new minimatch.Minimatch(pattern, matchOptions);
// the "set" property is an array of arrays of parsed path segment info. the outer array should only
// contain one item, otherwise something went wrong. brace expansion can result in multiple arrays,
// but that should be turned off by the time this function is reached.
if (minimatchObj.set.length != 1) {
throw new Error('getFindRootFromPattern() expected Minimatch(...).set.length to be 1. Actual: ' + minimatchObj.set.length);
}
var literalSegments = [];
for (var _i = 0, _a = minimatchObj.set[0]; _i < _a.length; _i++) {
var parsedSegment = _a[_i];
if (typeof parsedSegment == 'string') {
// the item is a string when the original input for the path segment does not contain any
// unescaped glob characters.
//
// note, the string here is already unescaped (i.e. glob escaping removed), so it is ready
// to pass to find() as-is. for example, an input string 'hello\\*world' => 'hello*world'.
literalSegments.push(parsedSegment);
continue;
}
break;
}
// join the literal segments back together. Minimatch converts '\' to '/' on Windows, then squashes
// consequetive slashes, and finally splits on slash. this means that UNC format is lost, but can
// be detected from the original pattern.
var joinedSegments = literalSegments.join('/');
if (joinedSegments && process.platform == 'win32' && _startsWith(pattern.replace(/\\/g, '/'), '//')) {
joinedSegments = '/' + joinedSegments; // restore UNC format
}
// determine the find path
var findPath;
if (_isRooted(pattern)) { // the pattern was rooted
findPath = joinedSegments;
}
else if (joinedSegments) { // the pattern was not rooted, and literal segments were found
findPath = _ensureRooted(defaultRoot, joinedSegments);
}
else { // the pattern was not rooted, and no literal segments were found
findPath = defaultRoot;
}
// clean up the path
if (findPath) {
findPath = _getDirectoryName(_ensureRooted(findPath, '_')); // hack to remove unnecessary trailing slash
findPath = _normalizeSeparators(findPath); // normalize slashes
}
return {
adjustedPattern: _ensurePatternRooted(defaultRoot, pattern),
findPath: findPath,
statOnly: literalSegments.length == minimatchObj.set[0].length,
};
}
exports._getFindInfoFromPattern = _getFindInfoFromPattern;
function _ensurePatternRooted(root, p) {
if (!root) {
throw new Error('ensurePatternRooted() parameter "root" cannot be empty');
}
if (!p) {
throw new Error('ensurePatternRooted() parameter "p" cannot be empty');
}
if (_isRooted(p)) {
return p;
}
// normalize root
root = _normalizeSeparators(root);
// escape special glob characters
root = (process.platform == 'win32' ? root : root.replace(/\\/g, '\\\\')) // escape '\' on OSX/Linux
.replace(/(\[)(?=[^\/]+\])/g, '[[]') // escape '[' when ']' follows within the path segment
.replace(/\?/g, '[?]') // escape '?'
.replace(/\*/g, '[*]') // escape '*'
.replace(/\+\(/g, '[+](') // escape '+('
.replace(/@\(/g, '[@](') // escape '@('
.replace(/!\(/g, '[!]('); // escape '!('
return _ensureRooted(root, p);
}
exports._ensurePatternRooted = _ensurePatternRooted;
//-------------------------------------------------------------------
// Populate the vault with sensitive data. Inputs and Endpoints
//-------------------------------------------------------------------
function _loadData() {
// in agent, prefer TempDirectory then workFolder.
// In interactive dev mode, it won't be
var keyPath = _getVariable("agent.TempDirectory") || _getVariable("agent.workFolder") || process.cwd();
exports._vault = new vm.Vault(keyPath);
exports._knownVariableMap = {};
_debug('loading inputs and endpoints');
var loaded = 0;
for (var envvar in process.env) {
if (_startsWith(envvar, 'INPUT_') ||
_startsWith(envvar, 'ENDPOINT_AUTH_') ||
_startsWith(envvar, 'SECUREFILE_TICKET_') ||
_startsWith(envvar, 'SECRET_') ||
_startsWith(envvar, 'VSTS_TASKVARIABLE_')) {
// Record the secret variable metadata. This is required by getVariable to know whether
// to retrieve the value from the vault. In a 2.104.1 agent or higher, this metadata will
// be overwritten when the VSTS_SECRET_VARIABLES env var is processed below.
if (_startsWith(envvar, 'SECRET_')) {
var variableName = envvar.substring('SECRET_'.length);
if (variableName) {
// This is technically not the variable name (has underscores instead of dots),
// but it's good enough to make getVariable work in a pre-2.104.1 agent where
// the VSTS_SECRET_VARIABLES env var is not defined.
exports._knownVariableMap[_getVariableKey(variableName)] = { name: variableName, secret: true };
}
}
// store the secret
var value = process.env[envvar];
if (value) {
++loaded;
_debug('loading ' + envvar);
exports._vault.storeSecret(envvar, value);
delete process.env[envvar];
}
}
}
_debug('loaded ' + loaded);
var correlationId = process.env["COMMAND_CORRELATION_ID"];
delete process.env["COMMAND_CORRELATION_ID"];
_commandCorrelationId = correlationId ? String(correlationId) : "";
// store public variable metadata
var names;
try {
names = JSON.parse(process.env['VSTS_PUBLIC_VARIABLES'] || '[]');
}
catch (err) {
throw new Error('Failed to parse VSTS_PUBLIC_VARIABLES as JSON. ' + err); // may occur during interactive testing
}
names.forEach(function (name) {
exports._knownVariableMap[_getVariableKey(name)] = { name: name, secret: false };
});
delete process.env['VSTS_PUBLIC_VARIABLES'];
// store secret variable metadata
try {
names = JSON.parse(process.env['VSTS_SECRET_VARIABLES'] || '[]');
}
catch (err) {
throw new Error('Failed to parse VSTS_SECRET_VARIABLES as JSON. ' + err); // may occur during interactive testing
}
names.forEach(function (name) {
exports._knownVariableMap[_getVariableKey(name)] = { name: name, secret: true };
});
delete process.env['VSTS_SECRET_VARIABLES'];
// avoid loading twice (overwrites .taskkey)
global['_vsts_task_lib_loaded'] = true;
}
exports._loadData = _loadData;
//--------------------------------------------------------------------------------
// Internal path helpers.
//--------------------------------------------------------------------------------
/**
* Defines if path is unc-path.
*
* @param path a path to a file.
* @returns true if path starts with double backslash, otherwise returns false.
*/
function _isUncPath(path) {
return /^\\\\[^\\]/.test(path);
}
exports._isUncPath = _isUncPath;
function _ensureRooted(root, p) {
if (!root) {
throw new Error('ensureRooted() parameter "root" cannot be empty');
}
if (!p) {
throw new Error('ensureRooted() parameter "p" cannot be empty');
}
if (_isRooted(p)) {
return p;
}
if (process.platform == 'win32' && root.match(/^[A-Z]:$/i)) { // e.g. C:
return root + p;
}
// ensure root ends with a separator
if (_endsWith(root, '/') || (process.platform == 'win32' && _endsWith(root, '\\'))) {
// root already ends with a separator
}
else {
root += path.sep; // append separator
}
return root + p;
}
exports._ensureRooted = _ensureRooted;
/**
* Determines the parent path and trims trailing slashes (when safe). Path separators are normalized
* in the result. This function works similar to the .NET System.IO.Path.GetDirectoryName() method.
* For example, C:\hello\world\ returns C:\hello\world (trailing slash removed). Returns empty when
* no higher directory can be determined.
*/
function _getDirectoryName(p) {
// short-circuit if empty
if (!p) {
return '';
}
// normalize separators
p = _normalizeSeparators(p);
// on Windows, the goal of this function is to match the behavior of
// [System.IO.Path]::GetDirectoryName(), e.g.
// C:/ =>
// C:/hello => C:\
// C:/hello/ => C:\hello
// C:/hello/world => C:\hello
// C:/hello/world/ => C:\hello\world
// C: =>
// C:hello => C:
// C:hello/ => C:hello
// / =>
// /hello => \
// /hello/ => \hello
// //hello =>
// //hello/ =>
// //hello/world =>
// //hello/world/ => \\hello\world
//
// unfortunately, path.dirname() can't simply be used. for example, on Windows
// it yields different results from Path.GetDirectoryName:
// C:/ => C:/
// C:/hello => C:/
// C:/hello/ => C:/
// C:/hello/world => C:/hello
// C:/hello/world/ => C:/hello
// C: => C:
// C:hello => C:
// C:hello/ => C:
// / => /
// /hello => /
// /hello/ => /
// //hello => /
// //hello/ => /
// //hello/world => //hello/world
// //hello/world/ => //hello/world/
// //hello/world/again => //hello/world/
// //hello/world/again/ => //hello/world/
// //hello/world/again/again => //hello/world/again
// //hello/world/again/again/ => //hello/world/again
if (process.platform == 'win32') {
if (/^[A-Z]:\\?[^\\]+$/i.test(p)) { // e.g. C:\hello or C:hello
return p.charAt(2) == '\\' ? p.substring(0, 3) : p.substring(0, 2);
}
else if (/^[A-Z]:\\?$/i.test(p)) { // e.g. C:\ or C:
return '';
}
var lastSlashIndex = p.lastIndexOf('\\');
if (lastSlashIndex < 0) { // file name only
return '';
}
else if (p == '\\') { // relative root
return '';
}
else if (lastSlashIndex == 0) { // e.g. \\hello
return '\\';
}
else if (/^\\\\[^\\]+(\\[^\\]*)?$/.test(p)) { // UNC root, e.g. \\hello or \\hello\ or \\hello\world
return '';
}
return p.substring(0, lastSlashIndex); // e.g. hello\world => hello or hello\world\ => hello\world
// note, this means trailing slashes for non-root directories
// (i.e. not C:\, \, or \\unc\) will simply be removed.
}
// OSX/Linux
if (p.indexOf('/') < 0) { // file name only
return '';
}
else if (p == '/') {
return '';
}
else if (_endsWith(p, '/')) {
return p.substring(0, p.length - 1);
}
return path.dirname(p);
}
exports._getDirectoryName = _getDirectoryName;
/**
* On OSX/Linux, true if path starts with '/'. On Windows, true for paths like:
* \, \hello, \\hello\share, C:, and C:\hello (and corresponding alternate separator cases).
*/
function _isRooted(p) {
p = _normalizeSeparators(p);
if (!p) {
throw new Error('isRooted() parameter "p" cannot be empty');
}
if (process.platform == 'win32') {
return _startsWith(p, '\\') || // e.g. \ or \hello or \\hello
/^[A-Z]:/i.test(p); // e.g. C: or C:\hello
}
return _startsWith(p, '/'); // e.g. /hello
}
exports._isRooted = _isRooted;
function _normalizeSeparators(p) {
p = p || '';
if (process.platform == 'win32') {
// convert slashes on Windows
p = p.replace(/\//g, '\\');
// remove redundant slashes
var isUnc = /^\\\\+[^\\]/.test(p); // e.g. \\hello
return (isUnc ? '\\' : '') + p.replace(/\\\\+/g, '\\'); // preserve leading // for UNC
}
// remove redundant slashes
return p.replace(/\/\/+/g, '/');
}
exports._normalizeSeparators = _normalizeSeparators;
//-----------------------------------------------------
// Expose proxy information to vsts-node-api
//-----------------------------------------------------
function _exposeProxySettings() {
var proxyUrl = _getVariable('Agent.ProxyUrl');
if (proxyUrl && proxyUrl.length > 0) {
var proxyUsername = _getVariable('Agent.ProxyUsername');
var proxyPassword = _getVariable('Agent.ProxyPassword');
var proxyBypassHostsJson = _getVariable('Agent.ProxyBypassList');
global['_vsts_task_lib_proxy_url'] = proxyUrl;
global['_vsts_task_lib_proxy_username'] = proxyUsername;
global['_vsts_task_lib_proxy_bypass'] = proxyBypassHostsJson;
global['_vsts_task_lib_proxy_password'] = _exposeTaskLibSecret('proxy', proxyPassword || '');
_debug('expose agent proxy configuration.');
global['_vsts_task_lib_proxy'] = true;
}
}
exports._exposeProxySettings = _exposeProxySettings;
//-----------------------------------------------------
// Expose certificate information to vsts-node-api
//-----------------------------------------------------
function _exposeCertSettings() {
var ca = _getVariable('Agent.CAInfo');
if (ca) {
global['_vsts_task_lib_cert_ca'] = ca;
}
var clientCert = _getVariable('Agent.ClientCert');
if (clientCert) {
var clientCertKey = _getVariable('Agent.ClientCertKey');
var clientCertArchive = _getVariable('Agent.ClientCertArchive');
var clientCertPassword = _getVariable('Agent.ClientCertPassword');
global['_vsts_task_lib_cert_clientcert'] = clientCert;
global['_vsts_task_lib_cert_key'] = clientCertKey;
global['_vsts_task_lib_cert_archive'] = clientCertArchive;
global['_vsts_task_lib_cert_passphrase'] = _exposeTaskLibSecret('cert', clientCertPassword || '');
}
if (ca || clientCert) {
_debug('expose agent certificate configuration.');
global['_vsts_task_lib_cert'] = true;
}
var skipCertValidation = _getVariable('Agent.SkipCertValidation') || 'false';
if (skipCertValidation) {
global['_vsts_task_lib_skip_cert_validation'] = skipCertValidation.toUpperCase() === 'TRUE';
}
}
exports._exposeCertSettings = _exposeCertSettings;
// We store the encryption key on disk and hold the encrypted content and key file in memory
// return base64encoded<keyFilePath>:base64encoded<encryptedContent>
// downstream vsts-node-api will retrieve the secret later
function _exposeTaskLibSecret(keyFile, secret) {
if (secret) {
var encryptKey = crypto.randomBytes(256);
var cipher = crypto.createCipher("aes-256-ctr", encryptKey);
var encryptedContent = cipher.update(secret, "utf8", "hex"); // CodeQL [SM01511] agent need to retrieve password later to connect to proxy server
encryptedContent += cipher.final("hex");
var storageFile = path.join(_getVariable('Agent.TempDirectory') || _getVariable("agent.workFolder") || process.cwd(), keyFile);
fs.writeFileSync(storageFile, encryptKey.toString('base64'), { encoding: 'utf8' });
return new Buffer(storageFile).toString('base64') + ':' + new Buffer(encryptedContent).toString('base64');
}
}
function isSigPipeError(e) {
var _a;
if (!e || typeof e !== 'object') {
return false;
}
return e.code === 'EPIPE' && ((_a = e.syscall) === null || _a === void 0 ? void 0 : _a.toUpperCase()) === 'WRITE';
}
exports.isSigPipeError = isSigPipeError;
;