azure-pipelines-task-lib
Version:
Azure Pipelines Task SDK
1,237 lines • 100 kB
JavaScript
"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