UNPKG

azure-pipelines-task-lib

Version:
939 lines (938 loc) 38.2 kB
"use strict"; 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;