UNPKG

azure-pipelines-task-lib

Version:
1,237 lines 100 kB
"use strict"; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.getPlatform = exports.osType = exports.writeFile = exports.exist = exports.stats = exports.debug = exports.error = exports.warning = exports.command = exports.setTaskVariable = exports.getTaskVariable = exports.getSecureFileTicket = exports.getSecureFileName = exports.getEndpointAuthorization = exports.getEndpointAuthorizationParameterRequired = exports.getEndpointAuthorizationParameter = exports.getEndpointAuthorizationSchemeRequired = exports.getEndpointAuthorizationScheme = exports.getEndpointDataParameterRequired = exports.getEndpointDataParameter = exports.getEndpointUrlRequired = exports.getEndpointUrl = exports.getPathInputRequired = exports.getPathInput = exports.filePathSupplied = exports.getDelimitedInput = exports.getPipelineFeature = exports.getBoolFeatureFlag = exports.getBoolInput = exports.getInputRequired = exports.getInput = exports.setSecret = exports.setVariable = exports.getVariables = exports.assertAgent = exports.getVariable = exports.loc = exports.setResourcePath = exports.setSanitizedResult = exports.setResult = exports.setErrStream = exports.setStdStream = exports.AgentHostedMode = exports.Platform = exports.IssueSource = exports.FieldType = exports.ArtifactType = exports.IssueType = exports.TaskState = exports.TaskResult = void 0; exports.updateReleaseName = exports.addBuildTag = exports.updateBuildNumber = exports.uploadBuildLog = exports.associateArtifact = exports.uploadArtifact = exports.logIssue = exports.logDetail = exports.setProgress = exports.setEndpoint = exports.addAttachment = exports.uploadSummary = exports.prependPath = exports.uploadFile = exports.CodeCoverageEnabler = exports.CodeCoveragePublisher = exports.TestPublisher = exports.getHttpCertConfiguration = exports.getHttpProxyConfiguration = exports.findMatch = exports.filter = exports.match = exports.tool = exports.execSync = exports.exec = exports.execAsync = exports.rmRF = exports.legacyFindFiles = exports.find = exports.retry = exports.mv = exports.cp = exports.ls = exports.which = exports.resolve = exports.mkdirP = exports.popd = exports.pushd = exports.cd = exports.checkPath = exports.cwd = exports.getAgentMode = exports.getNodeMajorVersion = void 0; var childProcess = require("child_process"); var fs = require("fs"); var path = require("path"); var os = require("os"); var minimatch = require("minimatch"); var im = require("./internal"); var tcm = require("./taskcommand"); var trm = require("./toolrunner"); var semver = require("semver"); var TaskResult; (function (TaskResult) { TaskResult[TaskResult["Succeeded"] = 0] = "Succeeded"; TaskResult[TaskResult["SucceededWithIssues"] = 1] = "SucceededWithIssues"; TaskResult[TaskResult["Failed"] = 2] = "Failed"; TaskResult[TaskResult["Cancelled"] = 3] = "Cancelled"; TaskResult[TaskResult["Skipped"] = 4] = "Skipped"; })(TaskResult = exports.TaskResult || (exports.TaskResult = {})); var TaskState; (function (TaskState) { TaskState[TaskState["Unknown"] = 0] = "Unknown"; TaskState[TaskState["Initialized"] = 1] = "Initialized"; TaskState[TaskState["InProgress"] = 2] = "InProgress"; TaskState[TaskState["Completed"] = 3] = "Completed"; })(TaskState = exports.TaskState || (exports.TaskState = {})); var IssueType; (function (IssueType) { IssueType[IssueType["Error"] = 0] = "Error"; IssueType[IssueType["Warning"] = 1] = "Warning"; })(IssueType = exports.IssueType || (exports.IssueType = {})); var ArtifactType; (function (ArtifactType) { ArtifactType[ArtifactType["Container"] = 0] = "Container"; ArtifactType[ArtifactType["FilePath"] = 1] = "FilePath"; ArtifactType[ArtifactType["VersionControl"] = 2] = "VersionControl"; ArtifactType[ArtifactType["GitRef"] = 3] = "GitRef"; ArtifactType[ArtifactType["TfvcLabel"] = 4] = "TfvcLabel"; })(ArtifactType = exports.ArtifactType || (exports.ArtifactType = {})); var FieldType; (function (FieldType) { FieldType[FieldType["AuthParameter"] = 0] = "AuthParameter"; FieldType[FieldType["DataParameter"] = 1] = "DataParameter"; FieldType[FieldType["Url"] = 2] = "Url"; })(FieldType = exports.FieldType || (exports.FieldType = {})); exports.IssueSource = im.IssueSource; /** Platforms supported by our build agent */ var Platform; (function (Platform) { Platform[Platform["Windows"] = 0] = "Windows"; Platform[Platform["MacOS"] = 1] = "MacOS"; Platform[Platform["Linux"] = 2] = "Linux"; })(Platform = exports.Platform || (exports.Platform = {})); var AgentHostedMode; (function (AgentHostedMode) { AgentHostedMode[AgentHostedMode["Unknown"] = 0] = "Unknown"; AgentHostedMode[AgentHostedMode["SelfHosted"] = 1] = "SelfHosted"; AgentHostedMode[AgentHostedMode["MsHosted"] = 2] = "MsHosted"; })(AgentHostedMode = exports.AgentHostedMode || (exports.AgentHostedMode = {})); //----------------------------------------------------- // General Helpers //----------------------------------------------------- exports.setStdStream = im._setStdStream; exports.setErrStream = im._setErrStream; function setResult(result, message, done) { (0, exports.debug)('task result: ' + TaskResult[result]); // add an error issue if (result == TaskResult.Failed && message) { (0, exports.error)(message, exports.IssueSource.TaskInternal); } else if (result == TaskResult.SucceededWithIssues && message) { (0, exports.warning)(message, exports.IssueSource.TaskInternal); } // task.complete var properties = { 'result': TaskResult[result] }; if (done) { properties['done'] = 'true'; } (0, exports.command)('task.complete', properties, message); } exports.setResult = setResult; /** * Sets the result of the task with sanitized message. * * @param result TaskResult enum of Succeeded, SucceededWithIssues, Failed, Cancelled or Skipped. * @param message A message which will be logged as an error issue if the result is Failed. Message will be truncated * before first occurence of wellknown sensitive keyword. * @param done Optional. Instructs the agent the task is done. This is helpful when child processes * may still be running and prevent node from fully exiting. This argument is supported * from agent version 2.142.0 or higher (otherwise will no-op). * @returns void */ function setSanitizedResult(result, message, done) { var pattern = /password|key|secret|bearer|authorization|token|pat/i; var sanitizedMessage = im._truncateBeforeSensitiveKeyword(message, pattern); setResult(result, sanitizedMessage, done); } exports.setSanitizedResult = setSanitizedResult; // // Catching all exceptions // process.on('uncaughtException', function (err) { if (!im.isSigPipeError(err)) { setResult(TaskResult.Failed, (0, exports.loc)('LIB_UnhandledEx', err.message)); (0, exports.error)(String(err.stack), im.IssueSource.TaskInternal); } }); // // Catching unhandled rejections from promises and rethrowing them as exceptions // For example, a promise that is rejected but not handled by a .catch() handler in node 10 // doesn't cause an uncaughtException but causes in Node 16. // For types definitions(Error | Any) see https://nodejs.org/docs/latest-v16.x/api/process.html#event-unhandledrejection // process.on('unhandledRejection', function (reason) { if (reason instanceof Error) { throw reason; } else { throw new Error(reason); } }); //----------------------------------------------------- // Loc Helpers //----------------------------------------------------- exports.setResourcePath = im._setResourcePath; exports.loc = im._loc; //----------------------------------------------------- // Input Helpers //----------------------------------------------------- exports.getVariable = im._getVariable; /** * Asserts the agent version is at least the specified minimum. * * @param minimum minimum version version - must be 2.104.1 or higher */ function assertAgent(minimum) { if (semver.lt(minimum, '2.104.1')) { throw new Error('assertAgent() requires the parameter to be 2.104.1 or higher'); } var agent = (0, exports.getVariable)('Agent.Version'); (0, exports.debug)('Detected Agent.Version=' + (agent ? agent : 'undefined')); if (agent && semver.lt(agent, minimum)) { throw new Error("Agent version ".concat(minimum, " or higher is required. Detected Agent version: ").concat(agent)); } } exports.assertAgent = assertAgent; /** * Gets a snapshot of the current state of all job variables available to the task. * Requires a 2.104.1 agent or higher for full functionality. * * Limitations on an agent prior to 2.104.1: * 1) The return value does not include all public variables. Only public variables * that have been added using setVariable are returned. * 2) The name returned for each secret variable is the formatted environment variable * name, not the actual variable name (unless it was set explicitly at runtime using * setVariable). * * @returns VariableInfo[] */ function getVariables() { return Object.keys(im._knownVariableMap) .map(function (key) { var info = im._knownVariableMap[key]; return { name: info.name, value: (0, exports.getVariable)(info.name), secret: info.secret }; }); } exports.getVariables = getVariables; /** * Sets a variable which will be available to subsequent tasks as well. * * @param name name of the variable to set * @param val value to set * @param secret whether variable is secret. Multi-line secrets are not allowed. Optional, defaults to false * @param isOutput whether variable is an output variable. Optional, defaults to false * @returns void */ function setVariable(name, val, secret, isOutput) { if (secret === void 0) { secret = false; } if (isOutput === void 0) { isOutput = false; } // once a secret always a secret var key = im._getVariableKey(name); if (im._knownVariableMap.hasOwnProperty(key)) { secret = secret || im._knownVariableMap[key].secret; } // store the value var varValue = val || ''; (0, exports.debug)('set ' + name + '=' + (secret && varValue ? '********' : varValue)); if (secret) { if (varValue && varValue.match(/\r|\n/) && "".concat(process.env['SYSTEM_UNSAFEALLOWMULTILINESECRET']).toUpperCase() != 'TRUE') { throw new Error((0, exports.loc)('LIB_MultilineSecret')); } im._vault.storeSecret('SECRET_' + key, varValue); delete process.env[key]; } else { process.env[key] = varValue; } // store the metadata im._knownVariableMap[key] = { name: name, secret: secret }; // write the setvariable command (0, exports.command)('task.setvariable', { 'variable': name || '', isOutput: (isOutput || false).toString(), 'issecret': (secret || false).toString() }, varValue); } exports.setVariable = setVariable; /** * Registers a value with the logger, so the value will be masked from the logs. Multi-line secrets are not allowed. * * @param val value to register */ function setSecret(val) { if (val) { if (val.match(/\r|\n/) && "".concat(process.env['SYSTEM_UNSAFEALLOWMULTILINESECRET']).toUpperCase() !== 'TRUE') { throw new Error((0, exports.loc)('LIB_MultilineSecret')); } (0, exports.command)('task.setsecret', {}, val); } } exports.setSecret = setSecret; /** * Gets the value of an input. * If required is true and the value is not set, it will throw. * * @param name name of the input to get * @param required whether input is required. optional, defaults to false * @returns string */ function getInput(name, required) { var inval = im._vault.retrieveSecret('INPUT_' + im._getVariableKey(name)); if (required && !inval) { throw new Error((0, exports.loc)('LIB_InputRequired', name)); } (0, exports.debug)(name + '=' + inval); return inval; } exports.getInput = getInput; /** * Gets the value of an input. * If the value is not set, it will throw. * * @param name name of the input to get * @returns string */ function getInputRequired(name) { return getInput(name, true); } exports.getInputRequired = getInputRequired; /** * Gets the value of an input and converts to a bool. Convenience. * If required is true and the value is not set, it will throw. * If required is false and the value is not set, returns false. * * @param name name of the bool input to get * @param required whether input is required. optional, defaults to false * @returns boolean */ function getBoolInput(name, required) { return (getInput(name, required) || '').toUpperCase() == "TRUE"; } exports.getBoolInput = getBoolInput; /** * Gets the value of an feature flag and converts to a bool. * @IMPORTANT This method is only for internal Microsoft development. Do not use it for external tasks. * @param name name of the feature flag to get. * @param defaultValue default value of the feature flag in case it's not found in env. (optional. Default value = false) * @returns boolean * @deprecated Don't use this for new development. Use getPipelineFeature instead. */ function getBoolFeatureFlag(ffName, defaultValue) { if (defaultValue === void 0) { defaultValue = false; } var ffValue = process.env[ffName]; if (!ffValue) { (0, exports.debug)("Feature flag ".concat(ffName, " not found. Returning ").concat(defaultValue, " as default.")); return defaultValue; } (0, exports.debug)("Feature flag ".concat(ffName, " = ").concat(ffValue)); return ffValue.toLowerCase() === "true"; } exports.getBoolFeatureFlag = getBoolFeatureFlag; /** * Gets the value of an task feature and converts to a bool. * @IMPORTANT This method is only for internal Microsoft development. Do not use it for external tasks. * @param name name of the feature to get. * @returns boolean */ function getPipelineFeature(featureName) { var variableName = im._getVariableKey("DistributedTask.Tasks.".concat(featureName)); var featureValue = process.env[variableName]; if (!featureValue) { (0, exports.debug)("Feature '".concat(featureName, "' not found. Returning false as default.")); return false; } var boolValue = featureValue.toLowerCase() === "true"; (0, exports.debug)("Feature '".concat(featureName, "' = '").concat(featureValue, "'. Processed as '").concat(boolValue, "'.")); return boolValue; } exports.getPipelineFeature = getPipelineFeature; /** * Gets the value of an input and splits the value using a delimiter (space, comma, etc). * Empty values are removed. This function is useful for splitting an input containing a simple * list of items - such as build targets. * IMPORTANT: Do not use this function for splitting additional args! Instead use argString(), which * follows normal argument splitting rules and handles values encapsulated by quotes. * If required is true and the value is not set, it will throw. * * @param name name of the input to get * @param delim delimiter to split on * @param required whether input is required. optional, defaults to false * @returns string[] */ function getDelimitedInput(name, delim, required) { var inputVal = getInput(name, required); if (!inputVal) { return []; } var result = []; inputVal.split(delim).forEach(function (x) { if (x) { result.push(x); } }); return result; } exports.getDelimitedInput = getDelimitedInput; /** * Checks whether a path inputs value was supplied by the user * File paths are relative with a picker, so an empty path is the root of the repo. * Useful if you need to condition work (like append an arg) if a value was supplied * * @param name name of the path input to check * @returns boolean */ function filePathSupplied(name) { // normalize paths var pathValue = this.resolve(this.getPathInput(name) || ''); var repoRoot = this.resolve((0, exports.getVariable)('build.sourcesDirectory') || (0, exports.getVariable)('system.defaultWorkingDirectory') || ''); var supplied = pathValue !== repoRoot; (0, exports.debug)(name + 'path supplied :' + supplied); return supplied; } exports.filePathSupplied = filePathSupplied; /** * Gets the value of a path input * It will be quoted for you if it isn't already and contains spaces * If required is true and the value is not set, it will throw. * If check is true and the path does not exist, it will throw. * * @param name name of the input to get * @param required whether input is required. optional, defaults to false * @param check whether path is checked. optional, defaults to false * @returns string */ function getPathInput(name, required, check) { var inval = getInput(name, required); if (inval) { if (check) { (0, exports.checkPath)(inval, name); } } return inval; } exports.getPathInput = getPathInput; /** * Gets the value of a path input * It will be quoted for you if it isn't already and contains spaces * If the value is not set, it will throw. * If check is true and the path does not exist, it will throw. * * @param name name of the input to get * @param check whether path is checked. optional, defaults to false * @returns string */ function getPathInputRequired(name, check) { return getPathInput(name, true, check); } exports.getPathInputRequired = getPathInputRequired; //----------------------------------------------------- // Endpoint Helpers //----------------------------------------------------- /** * Gets the url for a service endpoint * If the url was not set and is not optional, it will throw. * * @param id name of the service endpoint * @param optional whether the url is optional * @returns string */ function getEndpointUrl(id, optional) { var urlval = process.env['ENDPOINT_URL_' + id]; if (!optional && !urlval) { throw new Error((0, exports.loc)('LIB_EndpointNotExist', id)); } (0, exports.debug)(id + '=' + urlval); return urlval; } exports.getEndpointUrl = getEndpointUrl; /** * Gets the url for a service endpoint * If the url was not set, it will throw. * * @param id name of the service endpoint * @returns string */ function getEndpointUrlRequired(id) { return getEndpointUrl(id, false); } exports.getEndpointUrlRequired = getEndpointUrlRequired; /* * Gets the endpoint data parameter value with specified key for a service endpoint * If the endpoint data parameter was not set and is not optional, it will throw. * * @param id name of the service endpoint * @param key of the parameter * @param optional whether the endpoint data is optional * @returns {string} value of the endpoint data parameter */ function getEndpointDataParameter(id, key, optional) { var dataParamVal = process.env['ENDPOINT_DATA_' + id + '_' + key.toUpperCase()]; if (!optional && !dataParamVal) { throw new Error((0, exports.loc)('LIB_EndpointDataNotExist', id, key)); } (0, exports.debug)(id + ' data ' + key + ' = ' + dataParamVal); return dataParamVal; } exports.getEndpointDataParameter = getEndpointDataParameter; /* * Gets the endpoint data parameter value with specified key for a service endpoint * If the endpoint data parameter was not set, it will throw. * * @param id name of the service endpoint * @param key of the parameter * @returns {string} value of the endpoint data parameter */ function getEndpointDataParameterRequired(id, key) { return getEndpointDataParameter(id, key, false); } exports.getEndpointDataParameterRequired = getEndpointDataParameterRequired; /** * Gets the endpoint authorization scheme for a service endpoint * If the endpoint authorization scheme is not set and is not optional, it will throw. * * @param id name of the service endpoint * @param optional whether the endpoint authorization scheme is optional * @returns {string} value of the endpoint authorization scheme */ function getEndpointAuthorizationScheme(id, optional) { var authScheme = im._vault.retrieveSecret('ENDPOINT_AUTH_SCHEME_' + id); if (!optional && !authScheme) { throw new Error((0, exports.loc)('LIB_EndpointAuthNotExist', id)); } (0, exports.debug)(id + ' auth scheme = ' + authScheme); return authScheme; } exports.getEndpointAuthorizationScheme = getEndpointAuthorizationScheme; /** * Gets the endpoint authorization scheme for a service endpoint * If the endpoint authorization scheme is not set, it will throw. * * @param id name of the service endpoint * @returns {string} value of the endpoint authorization scheme */ function getEndpointAuthorizationSchemeRequired(id) { return getEndpointAuthorizationScheme(id, false); } exports.getEndpointAuthorizationSchemeRequired = getEndpointAuthorizationSchemeRequired; /** * Gets the endpoint authorization parameter value for a service endpoint with specified key * If the endpoint authorization parameter is not set and is not optional, it will throw. * * @param id name of the service endpoint * @param key key to find the endpoint authorization parameter * @param optional optional whether the endpoint authorization scheme is optional * @returns {string} value of the endpoint authorization parameter value */ function getEndpointAuthorizationParameter(id, key, optional) { var authParam = im._vault.retrieveSecret('ENDPOINT_AUTH_PARAMETER_' + id + '_' + key.toUpperCase()); if (!optional && !authParam) { throw new Error((0, exports.loc)('LIB_EndpointAuthNotExist', id)); } (0, exports.debug)(id + ' auth param ' + key + ' = ' + authParam); return authParam; } exports.getEndpointAuthorizationParameter = getEndpointAuthorizationParameter; /** * Gets the endpoint authorization parameter value for a service endpoint with specified key * If the endpoint authorization parameter is not set, it will throw. * * @param id name of the service endpoint * @param key key to find the endpoint authorization parameter * @returns {string} value of the endpoint authorization parameter value */ function getEndpointAuthorizationParameterRequired(id, key) { return getEndpointAuthorizationParameter(id, key, false); } exports.getEndpointAuthorizationParameterRequired = getEndpointAuthorizationParameterRequired; /** * Gets the authorization details for a service endpoint * If the authorization was not set and is not optional, it will set the task result to Failed. * * @param id name of the service endpoint * @param optional whether the url is optional * @returns string */ function getEndpointAuthorization(id, optional) { var aval = im._vault.retrieveSecret('ENDPOINT_AUTH_' + id); if (!optional && !aval) { setResult(TaskResult.Failed, (0, exports.loc)('LIB_EndpointAuthNotExist', id)); } (0, exports.debug)(id + ' exists ' + (!!aval)); var auth; try { if (aval) { auth = JSON.parse(aval); } } catch (err) { throw new Error((0, exports.loc)('LIB_InvalidEndpointAuth', aval)); } return auth; } exports.getEndpointAuthorization = getEndpointAuthorization; //----------------------------------------------------- // SecureFile Helpers //----------------------------------------------------- /** * Gets the name for a secure file * * @param id secure file id * @returns string */ function getSecureFileName(id) { var name = process.env['SECUREFILE_NAME_' + id]; (0, exports.debug)('secure file name for id ' + id + ' = ' + name); return name; } exports.getSecureFileName = getSecureFileName; /** * Gets the secure file ticket that can be used to download the secure file contents * * @param id name of the secure file * @returns {string} secure file ticket */ function getSecureFileTicket(id) { var ticket = im._vault.retrieveSecret('SECUREFILE_TICKET_' + id); (0, exports.debug)('secure file ticket for id ' + id + ' = ' + ticket); return ticket; } exports.getSecureFileTicket = getSecureFileTicket; //----------------------------------------------------- // Task Variable Helpers //----------------------------------------------------- /** * Gets a variable value that is set by previous step from the same wrapper task. * Requires a 2.115.0 agent or higher. * * @param name name of the variable to get * @returns string */ function getTaskVariable(name) { assertAgent('2.115.0'); var inval = im._vault.retrieveSecret('VSTS_TASKVARIABLE_' + im._getVariableKey(name)); if (inval) { inval = inval.trim(); } (0, exports.debug)('task variable: ' + name + '=' + inval); return inval; } exports.getTaskVariable = getTaskVariable; /** * Sets a task variable which will only be available to subsequent steps belong to the same wrapper task. * Requires a 2.115.0 agent or higher. * * @param name name of the variable to set * @param val value to set * @param secret whether variable is secret. optional, defaults to false * @returns void */ function setTaskVariable(name, val, secret) { if (secret === void 0) { secret = false; } assertAgent('2.115.0'); var key = im._getVariableKey(name); // store the value var varValue = val || ''; (0, exports.debug)('set task variable: ' + name + '=' + (secret && varValue ? '********' : varValue)); im._vault.storeSecret('VSTS_TASKVARIABLE_' + key, varValue); delete process.env[key]; // write the command (0, exports.command)('task.settaskvariable', { 'variable': name || '', 'issecret': (secret || false).toString() }, varValue); } exports.setTaskVariable = setTaskVariable; //----------------------------------------------------- // Cmd Helpers //----------------------------------------------------- exports.command = im._command; exports.warning = im._warning; exports.error = im._error; exports.debug = im._debug; //----------------------------------------------------- // Disk Functions //----------------------------------------------------- /** * Get's stat on a path. * Useful for checking whether a file or directory. Also getting created, modified and accessed time. * see [fs.stat](https://nodejs.org/api/fs.html#fs_class_fs_stats) * * @param path path to check * @returns fsStat */ function stats(path) { return fs.statSync(path); } exports.stats = stats; exports.exist = im._exist; function writeFile(file, data, options) { if (typeof (options) === 'string') { fs.writeFileSync(file, data, { encoding: options }); } else { fs.writeFileSync(file, data, options); } } exports.writeFile = writeFile; /** * @deprecated Use `getPlatform` * Useful for determining the host operating system. * see [os.type](https://nodejs.org/api/os.html#os_os_type) * * @return the name of the operating system */ function osType() { return os.type(); } exports.osType = osType; /** * Determine the operating system the build agent is running on. * @returns {Platform} * @throws {Error} Platform is not supported by our agent */ function getPlatform() { switch (process.platform) { case 'win32': return Platform.Windows; case 'darwin': return Platform.MacOS; case 'linux': return Platform.Linux; default: throw Error((0, exports.loc)('LIB_PlatformNotSupported', process.platform)); } } exports.getPlatform = getPlatform; /** * Resolves major version of Node.js engine used by the agent. * @returns {Number} Node's major version. */ function getNodeMajorVersion() { var _a; var version = (_a = process === null || process === void 0 ? void 0 : process.versions) === null || _a === void 0 ? void 0 : _a.node; if (!version) { throw new Error((0, exports.loc)('LIB_UndefinedNodeVersion')); } var parts = version.split('.').map(Number); if (parts.length < 1) { return NaN; } return parts[0]; } exports.getNodeMajorVersion = getNodeMajorVersion; /** * Return hosted type of Agent * @returns {AgentHostedMode} */ function getAgentMode() { var agentCloudId = (0, exports.getVariable)('Agent.CloudId'); if (agentCloudId === undefined) return AgentHostedMode.Unknown; if (agentCloudId) return AgentHostedMode.MsHosted; return AgentHostedMode.SelfHosted; } exports.getAgentMode = getAgentMode; /** * Returns the process's current working directory. * see [process.cwd](https://nodejs.org/api/process.html#process_process_cwd) * * @return the path to the current working directory of the process */ function cwd() { return process.cwd(); } exports.cwd = cwd; exports.checkPath = im._checkPath; /** * Change working directory. * * @param {string} path - New working directory path * @returns {void} */ function cd(path) { if (path === '-') { if (!process.env.OLDPWD) { throw new Error((0, exports.loc)('LIB_NotFoundPreviousDirectory')); } else { path = process.env.OLDPWD; } } if (path === '~') { path = os.homedir(); } if (!fs.existsSync(path)) { throw new Error((0, exports.loc)('LIB_PathNotFound', 'cd', path)); } if (!fs.statSync(path).isDirectory()) { throw new Error((0, exports.loc)('LIB_PathIsNotADirectory', path)); } try { var currentPath = process.cwd(); process.chdir(path); process.env.OLDPWD = currentPath; } catch (error) { (0, exports.debug)((0, exports.loc)('LIB_OperationFailed', 'cd', error)); } } exports.cd = cd; var dirStack = []; function getActualStack() { return [process.cwd()].concat(dirStack); } /** * Change working directory and push it on the stack * * @param {string} dir - New working directory path * @returns {void} */ function pushd(dir) { if (dir === void 0) { dir = ''; } var dirs = getActualStack(); var maybeIndex = parseInt(dir); if (dir === '+0') { return dirs; } else if (dir.length === 0) { if (dirs.length > 1) { dirs.splice.apply(dirs, __spreadArray([0, 0], dirs.splice(1, 1), false)); } else { throw new Error((0, exports.loc)('LIB_DirectoryStackEmpty')); } } else if (!isNaN(maybeIndex)) { if (maybeIndex < dirStack.length + 1) { maybeIndex = dir.charAt(0) === '-' ? maybeIndex - 1 : maybeIndex; } dirs.splice.apply(dirs, __spreadArray([0, dirs.length], dirs.slice(maybeIndex).concat(dirs.slice(0, maybeIndex)), false)); } else { dirs.unshift(dir); } var _path = path.resolve(dirs.shift()); try { cd(_path); } catch (error) { if (!fs.existsSync(_path)) { throw new Error((0, exports.loc)('Not found', 'pushd', _path)); } throw error; } dirStack.splice.apply(dirStack, __spreadArray([0, dirStack.length], dirs, false)); return getActualStack(); } exports.pushd = pushd; /** * Change working directory back to previously pushed directory * * @param {string} index - Index to remove from the stack * @returns {void} */ function popd(index) { if (index === void 0) { index = ''; } if (dirStack.length === 0) { throw new Error((0, exports.loc)('LIB_DirectoryStackEmpty')); } var maybeIndex = parseInt(index); if (isNaN(maybeIndex)) { maybeIndex = 0; } else if (maybeIndex < dirStack.length + 1) { maybeIndex = index.charAt(0) === '-' ? maybeIndex - 1 : maybeIndex; } if (maybeIndex > 0 || dirStack.length + maybeIndex === 0) { maybeIndex = maybeIndex > 0 ? maybeIndex - 1 : maybeIndex; dirStack.splice(maybeIndex, 1); } else { var _path = path.resolve(dirStack.shift()); cd(_path); } return getActualStack(); } exports.popd = popd; /** * Make a directory. Creates the full path with folders in between * Will throw if it fails * * @param {string} p - Path to create * @returns {void} */ function mkdirP(p) { if (!p) { throw new Error((0, exports.loc)('LIB_ParameterIsRequired', 'p')); } // build a stack of directories to create var stack = []; var testDir = p; while (true) { // validate the loop is not out of control if (stack.length >= Number(process.env['TASKLIB_TEST_MKDIRP_FAILSAFE'] || 1000)) { // let the framework throw (0, exports.debug)('loop is out of control'); fs.mkdirSync(p); return; } (0, exports.debug)("testing directory '".concat(testDir, "'")); var stats_1 = void 0; try { stats_1 = fs.statSync(testDir); } catch (err) { if (err.code == 'ENOENT') { // validate the directory is not the drive root var parentDir = path.dirname(testDir); if (testDir == parentDir) { throw new Error((0, exports.loc)('LIB_MkdirFailedInvalidDriveRoot', p, testDir)); // Unable to create directory '{p}'. Root directory does not exist: '{testDir}' } // push the dir and test the parent stack.push(testDir); testDir = parentDir; continue; } else if (err.code == 'UNKNOWN') { throw new Error((0, exports.loc)('LIB_MkdirFailedInvalidShare', p, testDir)); // Unable to create directory '{p}'. Unable to verify the directory exists: '{testDir}'. If directory is a file share, please verify the share name is correct, the share is online, and the current process has permission to access the share. } else { throw err; } } if (!stats_1.isDirectory()) { throw new Error((0, exports.loc)('LIB_MkdirFailedFileExists', p, testDir)); // Unable to create directory '{p}'. Conflicting file exists: '{testDir}' } // testDir exists break; } // create each directory while (stack.length) { var dir = stack.pop(); // non-null because `stack.length` was truthy (0, exports.debug)("mkdir '".concat(dir, "'")); try { fs.mkdirSync(dir); } catch (err) { throw new Error((0, exports.loc)('LIB_MkdirFailed', p, err.message)); // Unable to create directory '{p}'. {err.message} } } } exports.mkdirP = mkdirP; /** * Resolves a sequence of paths or path segments into an absolute path. * Calls node.js path.resolve() * Allows L0 testing with consistent path formats on Mac/Linux and Windows in the mock implementation * @param pathSegments * @returns {string} */ function resolve() { var pathSegments = []; for (var _i = 0; _i < arguments.length; _i++) { pathSegments[_i] = arguments[_i]; } var absolutePath = path.resolve.apply(this, pathSegments); (0, exports.debug)('Absolute path for pathSegments: ' + pathSegments + ' = ' + absolutePath); return absolutePath; } exports.resolve = resolve; exports.which = im._which; /** * Returns array of files in the given path, or in current directory if no path provided. * @param {unknown} optionsOrPaths - Available options: -R (recursive), -A (all files, include files beginning with ., except for . and ..) * @param {unknown[]} paths - Paths to search. * @return {string[]} - An array of files in the given path(s). */ function ls(optionsOrPaths) { var paths = []; for (var _i = 1; _i < arguments.length; _i++) { paths[_i - 1] = arguments[_i]; } var isRecursive = false; var includeHidden = false; if (typeof optionsOrPaths === 'string' && optionsOrPaths.startsWith('-')) { var options = String(optionsOrPaths).toLowerCase(); isRecursive = options.includes('r'); includeHidden = options.includes('a'); } if (Array.isArray(paths)) { paths = flattenArray(paths); } // If the first argument is not options, then it is a path if (typeof optionsOrPaths !== 'string' || !optionsOrPaths.startsWith('-')) { var pathsFromOptions = []; if (Array.isArray(optionsOrPaths)) { pathsFromOptions = optionsOrPaths; } else if (optionsOrPaths && typeof optionsOrPaths === 'string') { pathsFromOptions = [optionsOrPaths]; } if (paths === undefined || paths.length === 0) { paths = pathsFromOptions; } else { paths.push.apply(paths, pathsFromOptions); } } if (paths.length === 0) { paths.push(path.resolve('.')); } var pathsCopy = __spreadArray([], paths, true); var preparedPaths = []; var fileEntries = []; try { var remainingPaths = []; while (paths.length > 0) { var pathEntry = resolve(paths.shift()); if (pathEntry === null || pathEntry === void 0 ? void 0 : pathEntry.includes('*')) { remainingPaths.push(pathEntry); continue; } var stats_2 = fs.lstatSync(pathEntry); if (stats_2.isFile()) { var fileName = path.basename(pathEntry); fileEntries.push(fileName); } else { remainingPaths.push(pathEntry); } } paths.push.apply(paths, remainingPaths); var _loop_1 = function () { var pathEntry = resolve(paths.shift()); if (pathEntry === null || pathEntry === void 0 ? void 0 : pathEntry.includes('*')) { paths.push.apply(paths, findMatch(path.dirname(pathEntry), [path.basename(pathEntry)])); return "continue"; } if (fs.lstatSync(pathEntry).isDirectory()) { preparedPaths.push.apply(preparedPaths, fs.readdirSync(pathEntry).map(function (file) { return path.join(pathEntry, file); })); } else { preparedPaths.push(pathEntry); } }; while (paths.length > 0) { _loop_1(); } var entries = []; var _loop_2 = function () { var entry = preparedPaths.shift(); var entrybasename = path.basename(entry); if (entry === null || entry === void 0 ? void 0 : entry.includes('*')) { preparedPaths.push.apply(preparedPaths, findMatch(path.dirname(entry), [entrybasename])); return "continue"; } if (!includeHidden && entrybasename.startsWith('.') && entrybasename !== '.' && entrybasename !== '..') { return "continue"; } var baseDir = pathsCopy.find(function (p) { return entry.startsWith(path.resolve(p)); }) || path.resolve('.'); if (fs.lstatSync(entry).isDirectory() && isRecursive) { preparedPaths.push.apply(preparedPaths, fs.readdirSync(entry).map(function (x) { return path.join(entry, x); })); entries.push(path.relative(baseDir, entry)); } else { entries.push(path.relative(baseDir, entry)); } }; while (preparedPaths.length > 0) { _loop_2(); } var finalResults = __spreadArray(__spreadArray([], fileEntries, true), entries, true); return finalResults; } catch (error) { if (error.code === 'ENOENT') { throw new Error((0, exports.loc)('LIB_PathNotFound', 'ls', error.message)); } else { throw new Error((0, exports.loc)('LIB_OperationFailed', 'ls', error)); } } } exports.ls = ls; function flattenArray(arr) { return arr.reduce(function (flat, toFlatten) { return flat.concat(Array.isArray(toFlatten) ? flattenArray(toFlatten) : toFlatten); }, []); } /** * Copies a file or folder. * @param {string} sourceOrOptions - Either the source path or an option string '-r', '-f' , '-n' or '-rfn' for recursive, force and no-clobber. * @param {string} destinationOrSource - Destination path or the source path. * @param {string} [optionsOrDestination] - Options string or the destination path. * @param {boolean} [continueOnError=false] - Optional. Whether to continue on error. * @param {number} [retryCount=0] - Optional. Retry count to copy the file. It might help to resolve intermittent issues e.g. with UNC target paths on a remote host. * @returns {void} */ function cp(sourceOrOptions, destinationOrSource, optionsOrDestination, continueOnError, retryCount) { if (continueOnError === void 0) { continueOnError = false; } if (retryCount === void 0) { retryCount = 0; } retry(function () { var recursive = false; var force = true; var source = String(sourceOrOptions); var destination = destinationOrSource; var options = ''; if (typeof sourceOrOptions === 'string' && sourceOrOptions.startsWith('-')) { options = sourceOrOptions.toLowerCase(); recursive = options.includes('r'); force = !options.includes('n'); source = destinationOrSource; destination = String(optionsOrDestination); } else if (typeof optionsOrDestination === 'string' && optionsOrDestination && optionsOrDestination.startsWith('-')) { options = optionsOrDestination.toLowerCase(); recursive = options.includes('r'); force = !options.includes('n'); source = String(sourceOrOptions); destination = destinationOrSource; } if (!fs.existsSync(destination) && !force) { throw new Error((0, exports.loc)('LIB_PathNotFound', 'cp', destination)); } var lstatSource = fs.lstatSync(source); if (!force && fs.existsSync(destination)) { return; } try { if (lstatSource.isSymbolicLink()) { var symlinkTarget = fs.readlinkSync(source); source = path.resolve(path.dirname(source), symlinkTarget); lstatSource = fs.lstatSync(source); } if (lstatSource.isFile()) { if (fs.existsSync(destination) && fs.lstatSync(destination).isDirectory()) { destination = path.join(destination, path.basename(source)); } if (force) { fs.copyFileSync(source, destination); } else { fs.copyFileSync(source, destination, fs.constants.COPYFILE_EXCL); } } else { copyDirectoryWithResolvedSymlinks(source, path.join(destination, path.basename(source)), force); } } catch (error) { throw new Error((0, exports.loc)('LIB_OperationFailed', 'cp', error)); } }, [], { retryCount: retryCount, continueOnError: continueOnError }); } exports.cp = cp; var copyDirectoryWithResolvedSymlinks = function (src, dest, force) { var srcPath; var destPath; var entry; var entries = fs.readdirSync(src, { withFileTypes: true }); if (!fs.existsSync(dest)) { fs.mkdirSync(dest, { recursive: true }); } for (var _i = 0, entries_1 = entries; _i < entries_1.length; _i++) { entry = entries_1[_i]; srcPath = path.join(src, entry.name); destPath = path.join(dest, entry.name); if (entry.isSymbolicLink()) { // Resolve the symbolic link and copy the target var symlinkTarget = fs.readlinkSync(srcPath); var resolvedPath = path.resolve(path.dirname(srcPath), symlinkTarget); var stat = fs.lstatSync(resolvedPath); if (stat.isFile()) { // Use the actual target file's name instead of the symbolic link's name var targetFileName = path.basename(resolvedPath); var targetDestPath = path.join(dest, targetFileName); fs.copyFileSync(resolvedPath, targetDestPath); } else if (stat.isDirectory()) { copyDirectoryWithResolvedSymlinks(resolvedPath, destPath, force); } } else if (entry.isFile()) { fs.copyFileSync(srcPath, destPath); } else if (entry.isDirectory()) { copyDirectoryWithResolvedSymlinks(srcPath, destPath, force); } } }; /** * Moves a path. * * @param {string} source - Source path. * @param {string} dest - Destination path. * @param {MoveOptionsVariants} [options] - Option string -f or -n for force and no clobber. * @param {boolean} [continueOnError] - Optional. Whether to continue on error. * @returns {void} */ function mv(source, dest, options, continueOnError) { var force = false; if (options && typeof options === 'string' && options.startsWith('-')) { var lowercasedOptions = String(options).toLowerCase(); force = lowercasedOptions.includes('f') && !lowercasedOptions.includes('n'); } var sourceExists = fs.existsSync(source); var destExists = fs.existsSync(dest); var sources = []; try { if (!sourceExists) { if (source.includes('*')) { sources.push.apply(sources, findMatch(path.resolve(path.dirname(source)), [path.basename(source)])); } else { throw new Error((0, exports.loc)('LIB_PathNotFound', 'mv', source)); } } else { sources.push(source); } if (destExists && !force) { throw new Error("File already exists at ".concat(dest)); } for (var _i = 0, sources_1 = sources; _i < sources_1.length; _i++) { var source_1 = sources_1[_i]; fs.renameSync(source_1, dest); } } catch (error) { (0, exports.debug)('mv failed'); var errMsg = (0, exports.loc)('LIB_OperationFailed', 'mv', error); (0, exports.debug)(errMsg); if (!continueOnError) { throw new Error(errMsg); } } } exports.mv = mv; /** * Tries to execute a function a specified number of times. * * @param func a function to be executed. * @param args executed function arguments array. * @param retryOptions optional. Defaults to { continueOnError: false, retryCount: 0 }. * @returns the same as the usual function. */ function retry(func, args, retryOptions) { if (retryOptions === void 0) { retryOptions = { continueOnError: false, retryCount: 0 }; } while (retryOptions.retryCount >= 0) { try { return func.apply(void 0, args); } catch (e) { if (retryOptions.retryCount <= 0) { if (retryOptions.continueOnError) { (0, exports.warning)(e, exports.IssueSource.TaskInternal); break; } else { throw e; } } else { (0, exports.debug)("Attempt to execute function \"".concat(func === null || func === void 0 ? void 0 : func.name, "\" failed, retries left: ").concat(retryOptions.retryCount)); retryOptions.retryCount--; } } } } exports.retry = retry; /** * Gets info about item stats. * * @param path a path to the item to be processed. * @param followSymbolicLink indicates whether to traverse descendants of symbolic link directories. * @param allowBrokenSymbolicLinks when true, broken symbolic link will not cause an error. * @returns fs.Stats */ function _getStats(path, followSymbolicLink, allowBrokenSymbolicLinks) { // stat returns info about the target of a symlink (or symlink chain), // lstat returns info about a symlink itself var stats; if (followSymbolicLink) { try { // use stat (following symlinks) stats = fs.statSync(path); } catch (err) { if (err.code == 'ENOENT' && allowBrokenSymbolicLinks) { // fallback to lstat (broken symlinks allowed) stats = fs.lstatSync(path); (0, exports.debug)(" ".concat(path, " (broken symlink)")); } else { throw err; } } } else { // use lstat (not following symlinks) stats = fs.lstatSync(path); } return stats; } /** * Recursively finds all paths a given path. Returns an array of paths. * * @param findPath path to search * @param options optional. defaults to { followSymbolicLinks: true }. following soft links is generally appropriate unless deleting files. * @returns string[] */ function find(findPath, options) { if (!findPath) { (0, exports.debug)('no path specified'); return []; } // normalize the path, otherwise the first result is inconsistently formatted fro