From 109e66d2fb6afea2a20358deff7e198c8c1e6c03 Mon Sep 17 00:00:00 2001 From: mohitpavan Date: Wed, 1 Apr 2026 11:22:49 +0530 Subject: [PATCH 1/4] Adding missing commits to the GH from ADO. --- .github/scripts/generateE2ETestMatrix.sh | 4 +- .github/workflows/pr_check_load_test.yml | 2 +- action.yml | 4 + lib/Constants/InputConstants.js | 3 +- lib/RunnerFiles/CreateAndRunTest.js | 23 +++-- lib/Utils/CommonUtils.js | 123 ++++++++++++++--------- lib/Utils/CoreUtils.js | 11 +- lib/Utils/FetchUtils.js | 33 +++--- lib/Utils/LoadtestConfigUtil.js | 8 +- lib/main.js | 4 +- package.json | 1 - src/Constants/InputConstants.ts | 1 + src/RunnerFiles/CreateAndRunTest.ts | 25 +++-- src/Utils/CommonUtils.ts | 24 +++++ src/Utils/CoreUtils.ts | 8 ++ src/Utils/EngineUtil.ts | 4 +- src/Utils/FetchUtils.ts | 4 +- src/Utils/LoadtestConfigUtil.ts | 8 +- src/main.ts | 5 +- src/models/UtilModels.ts | 6 +- test/CommonUtils.test.ts | 48 +++++++++ test/createAndRunTest.test.ts | 42 ++++++++ 22 files changed, 297 insertions(+), 94 deletions(-) create mode 100644 test/CommonUtils.test.ts diff --git a/.github/scripts/generateE2ETestMatrix.sh b/.github/scripts/generateE2ETestMatrix.sh index f78cc727..365f8b84 100644 --- a/.github/scripts/generateE2ETestMatrix.sh +++ b/.github/scripts/generateE2ETestMatrix.sh @@ -1,4 +1,5 @@ os_options=('ubuntu-latest' 'windows-latest') +node_options=('20' '22') ScriptsRoot="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" E2ETestConfigFile="$ScriptsRoot/../config/e2eTestConfig.json" @@ -8,12 +9,13 @@ matrix_json="{\"include\":[" while read -r config; do rand_os=${os_options[$((RANDOM % 2))]} # Random OS for each test + rand_node=${node_options[$((RANDOM % 2))]} # Random Node version for each test configFile=$(echo "$config" | jq -r '.configFile') secrets=$(echo "$config" | jq -r '.secrets') env=$(echo "$config" | jq -r '.env') - matrix_json+="{\"configFile\":\"$configFile\",\"secrets\":\"$secrets\",\"env\":\"$env\",\"os\":\"$rand_os\"}," + matrix_json+="{\"configFile\":\"$configFile\",\"secrets\":\"$secrets\",\"env\":\"$env\",\"os\":\"$rand_os\",\"node\":\"$rand_node\"}," done <<< "$(echo -e "$configs")" matrix_json="${matrix_json::-1}]}" # Remove trailing comma diff --git a/.github/workflows/pr_check_load_test.yml b/.github/workflows/pr_check_load_test.yml index 52d62a74..2c687b9a 100644 --- a/.github/workflows/pr_check_load_test.yml +++ b/.github/workflows/pr_check_load_test.yml @@ -61,7 +61,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v2 with: - node-version: '20' + node-version: ${{ matrix.node }} - name: Installing dependencies and building latest changes if: github.ref == 'refs/heads/main' || (github.event_name == 'pull_request' && github.base_ref == 'main') diff --git a/action.yml b/action.yml index fdcaf7a6..d06f626e 100644 --- a/action.yml +++ b/action.yml @@ -28,6 +28,10 @@ inputs: outputVariableName: description: 'Name of the output variable that stores the test run ID for use in subsequent tasks.' required: false + waitForCompletion: + description: 'Wait for the load test run to complete. If false, the action will return after starting the test run. Default is true.' + required: false + default: 'true' branding: icon: 'extension-icon.svg' diff --git a/lib/Constants/InputConstants.js b/lib/Constants/InputConstants.js index 576991ba..3e768901 100644 --- a/lib/Constants/InputConstants.js +++ b/lib/Constants/InputConstants.js @@ -1,6 +1,6 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.loadTestConfigFileLabel = exports.loadTestResourceLabel = exports.resourceGroupLabel = exports.serviceConnectionNameLabel = exports.secretsLabel = exports.envVarsLabel = exports.outputVariableNameLabel = exports.overRideParametersLabel = exports.runDescriptionLabel = exports.testRunNameLabel = exports.loadTestConfigFile = exports.loadTestResource = exports.resourceGroup = exports.secrets = exports.envVars = exports.outputVariableName = exports.overRideParameters = exports.runDescription = exports.testRunName = void 0; +exports.loadTestConfigFileLabel = exports.loadTestResourceLabel = exports.resourceGroupLabel = exports.serviceConnectionNameLabel = exports.secretsLabel = exports.envVarsLabel = exports.outputVariableNameLabel = exports.overRideParametersLabel = exports.runDescriptionLabel = exports.testRunNameLabel = exports.waitForCompletion = exports.loadTestConfigFile = exports.loadTestResource = exports.resourceGroup = exports.secrets = exports.envVars = exports.outputVariableName = exports.overRideParameters = exports.runDescription = exports.testRunName = void 0; exports.testRunName = 'loadTestRunName'; exports.runDescription = 'loadTestRunDescription'; exports.overRideParameters = 'overrideParameters'; @@ -10,6 +10,7 @@ exports.secrets = 'secrets'; exports.resourceGroup = 'resourceGroup'; exports.loadTestResource = 'loadTestResource'; exports.loadTestConfigFile = 'loadTestConfigFile'; +exports.waitForCompletion = 'waitForCompletion'; // labels user visible strings exports.testRunNameLabel = 'Load Test Run Name'; exports.runDescriptionLabel = 'Load Test Run Description'; diff --git a/lib/RunnerFiles/CreateAndRunTest.js b/lib/RunnerFiles/CreateAndRunTest.js index bc894439..fdcb4a7e 100644 --- a/lib/RunnerFiles/CreateAndRunTest.js +++ b/lib/RunnerFiles/CreateAndRunTest.js @@ -52,7 +52,7 @@ class CreateAndRunTest { this.apiService = apiService; } createAndRunTest() { - return __awaiter(this, void 0, void 0, function* () { + return __awaiter(this, arguments, void 0, function* (waitForRunCompletionInput = true) { var _a; yield this.initialize(); let testPayload = yield this.getTestPayload(); @@ -83,12 +83,18 @@ class CreateAndRunTest { let testRunResult = yield this.apiService.createTestRun(testRunPayload); this.printPortalUrl(testRunResult); CoreUtils.exportVariable(UtilModels_1.PostTaskParameters.runId, testRunResult.testRunId); - yield this.awaitTerminationForTestRun(testRunResult.testRunId, this.apiService); - testRunResult = (_a = yield this.awaitResultsPopulation(testRunResult.testRunId, this.apiService)) !== null && _a !== void 0 ? _a : testRunResult; - CoreUtils.exportVariable(UtilModels_1.PostTaskParameters.isRunCompleted, 'true'); - this.printMetrics(testRunResult); - yield this.uploadResultsToPipeline(testRunResult); - this.setTaskResults(testRunResult); + if (waitForRunCompletionInput) { + yield this.awaitTerminationForTestRun(testRunResult.testRunId, this.apiService); + testRunResult = (_a = yield this.awaitResultsPopulation(testRunResult.testRunId, this.apiService)) !== null && _a !== void 0 ? _a : testRunResult; + CoreUtils.exportVariable(UtilModels_1.PostTaskParameters.isRunCompleted, 'true'); + this.printMetrics(testRunResult); + yield this.uploadResultsToPipeline(testRunResult); + this.setTaskResults(testRunResult); + } + else { + console.log("Test run started. Not waiting for completion as per input."); + this.setRunStarted(); + } this.setOutputVariable(testRunResult); }); } @@ -331,6 +337,9 @@ class CreateAndRunTest { CoreUtils.setFailed("TestStatus: " + testRunObj.status); } } + setRunStarted() { + console.log("Result: Test Run Started Successfully."); + } setOutputVariable(testRunObj) { let outputVarName = (0, CreateAndRunUtils_1.validateAndGetOutPutVarName)(); let outputVar = { diff --git a/lib/Utils/CommonUtils.js b/lib/Utils/CommonUtils.js index 6989e084..2f3c12d7 100644 --- a/lib/Utils/CommonUtils.js +++ b/lib/Utils/CommonUtils.js @@ -15,13 +15,23 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? ( }) : function(o, v) { o["default"] = v; }); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { @@ -32,7 +42,44 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.getAllFileErrors = exports.validateTestRunParamsFromPipeline = exports.getDefaultRunDescription = exports.getDefaultTestRunName = exports.getDefaultTestName = exports.inValidEngineInstances = exports.invalidName = exports.validateUrlcert = exports.validateUrl = exports.validateOutputParametervariableName = exports.validateOverRideParameters = exports.validateAndGetSegregatedManagedIdentities = exports.validateAutoStop = exports.getSubscriptionIdFromResourceId = exports.getResourceGroupFromResourceId = exports.getResourceNameFromResourceId = exports.getResourceTypeFromResourceId = exports.invalidDescription = exports.invalidDisplayName = exports.getFileName = exports.getResultObj = exports.isStatusFailed = exports.isTerminalFileStatusSucceeded = exports.isTerminalFileStatus = exports.isTerminalTestStatus = exports.removeUnits = exports.indexOfFirstDigit = exports.getReportFolder = exports.getResultFolder = exports.getUniqueId = exports.sleep = exports.printClientMetrics = exports.errorCorrection = exports.printCriteria = exports.printTestDuration = exports.checkFileTypes = exports.checkFileType = void 0; +exports.checkFileType = checkFileType; +exports.checkFileTypes = checkFileTypes; +exports.printTestDuration = printTestDuration; +exports.printCriteria = printCriteria; +exports.errorCorrection = errorCorrection; +exports.printClientMetrics = printClientMetrics; +exports.sleep = sleep; +exports.getUniqueId = getUniqueId; +exports.getResultFolder = getResultFolder; +exports.getReportFolder = getReportFolder; +exports.indexOfFirstDigit = indexOfFirstDigit; +exports.removeUnits = removeUnits; +exports.isTerminalTestStatus = isTerminalTestStatus; +exports.isTerminalFileStatus = isTerminalFileStatus; +exports.isTerminalFileStatusSucceeded = isTerminalFileStatusSucceeded; +exports.isStatusFailed = isStatusFailed; +exports.getResultObj = getResultObj; +exports.getFileName = getFileName; +exports.invalidDisplayName = invalidDisplayName; +exports.invalidDescription = invalidDescription; +exports.getResourceTypeFromResourceId = getResourceTypeFromResourceId; +exports.getResourceNameFromResourceId = getResourceNameFromResourceId; +exports.getResourceGroupFromResourceId = getResourceGroupFromResourceId; +exports.getSubscriptionIdFromResourceId = getSubscriptionIdFromResourceId; +exports.validateAutoStop = validateAutoStop; +exports.validateAndGetSegregatedManagedIdentities = validateAndGetSegregatedManagedIdentities; +exports.validateOverRideParameters = validateOverRideParameters; +exports.validateOutputParametervariableName = validateOutputParametervariableName; +exports.validateUrl = validateUrl; +exports.validateUrlcert = validateUrlcert; +exports.invalidName = invalidName; +exports.inValidEngineInstances = inValidEngineInstances; +exports.getDefaultTestName = getDefaultTestName; +exports.getDefaultTestRunName = getDefaultTestRunName; +exports.getDefaultRunDescription = getDefaultRunDescription; +exports.validateTestRunParamsFromPipeline = validateTestRunParamsFromPipeline; +exports.getAllFileErrors = getAllFileErrors; +exports.sanitisePipelineNameHeader = sanitisePipelineNameHeader; const { v4: uuidv4 } = require('uuid'); const util_1 = require("util"); const GeneralConstants_1 = require("../Constants/GeneralConstants"); @@ -47,7 +94,6 @@ function checkFileType(filePath, fileExtToValidate) { let split = filePath.split('.'); return split[split.length - 1].toLowerCase() == fileExtToValidate.toLowerCase(); } -exports.checkFileType = checkFileType; function checkFileTypes(filePath, fileExtsToValidate) { var _a; if ((0, util_1.isNullOrUndefined)(filePath)) { @@ -57,7 +103,6 @@ function checkFileTypes(filePath, fileExtsToValidate) { let fileExtsToValidateLower = fileExtsToValidate.map(ext => ext.toLowerCase()); return fileExtsToValidateLower.includes((_a = split[split.length - 1]) === null || _a === void 0 ? void 0 : _a.toLowerCase()); } -exports.checkFileTypes = checkFileTypes; function printTestDuration(testRunObj) { return __awaiter(this, void 0, void 0, function* () { var _a, _b; @@ -70,7 +115,6 @@ function printTestDuration(testRunObj) { return; }); } -exports.printTestDuration = printTestDuration; function printCriteria(criteria) { if (Object.keys(criteria).length == 0) return; @@ -99,11 +143,9 @@ function printCriteria(criteria) { } console.log("\n"); } -exports.printCriteria = printCriteria; function errorCorrection(result) { return "Unable to fetch the response. Please re-run or contact support if the issue persists. " + "Status code :" + result.message.statusCode; } -exports.errorCorrection = errorCorrection; function printTestResult(criteria) { var _a, _b; let pass = 0; @@ -142,7 +184,6 @@ function printClientMetrics(obj) { } }); } -exports.printClientMetrics = printClientMetrics; function getAbsVal(data) { if ((0, util_1.isNullOrUndefined)(data)) { return "undefined"; @@ -156,46 +197,39 @@ function sleep(ms) { setTimeout(resolve, ms); }); } -exports.sleep = sleep; function getUniqueId() { return uuidv4(); } -exports.getUniqueId = getUniqueId; function getResultFolder(testArtifacts) { if ((0, util_1.isNullOrUndefined)(testArtifacts) || (0, util_1.isNullOrUndefined)(testArtifacts.outputArtifacts)) return null; var outputurl = testArtifacts.outputArtifacts; return !(0, util_1.isNullOrUndefined)(outputurl.resultFileInfo) ? outputurl.resultFileInfo.url : null; } -exports.getResultFolder = getResultFolder; function getReportFolder(testArtifacts) { if ((0, util_1.isNullOrUndefined)(testArtifacts) || (0, util_1.isNullOrUndefined)(testArtifacts.outputArtifacts)) return null; var outputurl = testArtifacts.outputArtifacts; return !(0, util_1.isNullOrUndefined)(outputurl.reportFileInfo) ? outputurl.reportFileInfo.url : null; } -exports.getReportFolder = getReportFolder; function indexOfFirstDigit(input) { let i = 0; for (; input[i] < '0' || input[i] > '9'; i++) ; return i == input.length ? -1 : i; } -exports.indexOfFirstDigit = indexOfFirstDigit; function removeUnits(input) { let i = 0; for (; input[i] >= '0' && input[i] <= '9'; i++) ; return i == input.length ? input : input.substring(0, i); } -exports.removeUnits = removeUnits; function isTerminalTestStatus(testStatus) { if (testStatus == "DONE" || testStatus === "FAILED" || testStatus === "CANCELLED") { return true; } return false; } -exports.isTerminalTestStatus = isTerminalTestStatus; function isTerminalFileStatus(fileStatus) { let fileStatusEnum = fileStatus; if (fileStatusEnum == PayloadModels_1.FileStatus.VALIDATION_INITIATED) { @@ -203,7 +237,6 @@ function isTerminalFileStatus(fileStatus) { } return true; } -exports.isTerminalFileStatus = isTerminalFileStatus; function isTerminalFileStatusSucceeded(fileStatus) { let fileStatusEnum = fileStatus; if ((0, util_1.isNullOrUndefined)(fileStatusEnum) || fileStatusEnum == PayloadModels_1.FileStatus.VALIDATION_SUCCESS || fileStatusEnum == PayloadModels_1.FileStatus.NOT_VALIDATED) { @@ -211,14 +244,12 @@ function isTerminalFileStatusSucceeded(fileStatus) { } return false; } -exports.isTerminalFileStatusSucceeded = isTerminalFileStatusSucceeded; function isStatusFailed(testStatus) { if (testStatus === "FAILED" || testStatus === "CANCELLED") { return true; } return false; } -exports.isStatusFailed = isStatusFailed; function getResultObj(data) { return __awaiter(this, void 0, void 0, function* () { let dataString; @@ -233,40 +264,32 @@ function getResultObj(data) { } }); } -exports.getResultObj = getResultObj; function getFileName(filepath) { const filename = path.basename(filepath); return filename; } -exports.getFileName = getFileName; function invalidDisplayName(value) { if (value.length < 2 || value.length > 50) return true; return false; } -exports.invalidDisplayName = invalidDisplayName; function invalidDescription(value) { if (value.length > 100) return true; return false; } -exports.invalidDescription = invalidDescription; function getResourceTypeFromResourceId(resourceId) { return resourceId && resourceId.split("/").length > 7 ? resourceId.split("/")[6] + "/" + resourceId.split("/")[7] : null; } -exports.getResourceTypeFromResourceId = getResourceTypeFromResourceId; function getResourceNameFromResourceId(resourceId) { return resourceId && resourceId.split("/").length > 8 ? resourceId.split("/")[8] : null; } -exports.getResourceNameFromResourceId = getResourceNameFromResourceId; function getResourceGroupFromResourceId(resourceId) { return resourceId && resourceId.split("/").length > 4 ? resourceId.split("/")[4] : null; } -exports.getResourceGroupFromResourceId = getResourceGroupFromResourceId; function getSubscriptionIdFromResourceId(resourceId) { return resourceId && resourceId.split("/").length > 2 ? resourceId.split("/")[2] : null; } -exports.getSubscriptionIdFromResourceId = getSubscriptionIdFromResourceId; function validateAutoStop(autoStop, isPipelineParam = false) { if (typeof autoStop != 'string') { if ((0, util_1.isNullOrUndefined)(autoStop.errorPercentage) || isNaN(autoStop.errorPercentage) || autoStop.errorPercentage > 100 || autoStop.errorPercentage < 0) { @@ -290,7 +313,6 @@ function validateAutoStop(autoStop, isPipelineParam = false) { } return { valid: true, error: "" }; } -exports.validateAutoStop = validateAutoStop; function validateAndGetSegregatedManagedIdentities(referenceIdentities, keyVaultGivenOutOfReferenceIdentities = false) { let referenceIdentityValuesUAMIMap = { [UtilModels_1.ReferenceIdentityKinds.KeyVault]: [], @@ -337,7 +359,6 @@ function validateAndGetSegregatedManagedIdentities(referenceIdentities, keyVault } return { referenceIdentityValuesUAMIMap, referenceIdentiesSystemAssignedCount }; } -exports.validateAndGetSegregatedManagedIdentities = validateAndGetSegregatedManagedIdentities; function validateOverRideParameters(overRideParams) { try { if (!(0, util_1.isNullOrUndefined)(overRideParams)) { @@ -393,64 +414,54 @@ function validateOverRideParameters(overRideParams) { } return { valid: true, error: "" }; } -exports.validateOverRideParameters = validateOverRideParameters; function validateOutputParametervariableName(outputVarName) { if ((0, util_1.isNullOrUndefined)(outputVarName) || typeof outputVarName != 'string' || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(outputVarName)) { return { valid: false, error: `Invalid output variable name '${outputVarName}'. Use only letters, numbers, and underscores.` }; } return { valid: true, error: "" }; } -exports.validateOutputParametervariableName = validateOutputParametervariableName; function validateUrl(url) { var r = new RegExp(/(http|https):\/\/.*\/secrets\/.+$/); return r.test(url); } -exports.validateUrl = validateUrl; function validateUrlcert(url) { var r = new RegExp(/(http|https):\/\/.*\/certificates\/.+$/); return r.test(url); } -exports.validateUrlcert = validateUrlcert; function invalidName(value) { if (value.length < 2 || value.length > 50) return true; var r = new RegExp(/[^a-z0-9_-]+/); return r.test(value); } -exports.invalidName = invalidName; function inValidEngineInstances(engines) { if (engines > 400 || engines < 1) { return true; } return false; } -exports.inValidEngineInstances = inValidEngineInstances; function getDefaultTestName() { const a = (new Date(Date.now())).toLocaleString(); const b = a.split(", "); const c = a.split(" "); return "Test_" + b[0] + "_" + c[1] + c[2]; } -exports.getDefaultTestName = getDefaultTestName; function getDefaultTestRunName() { const a = (new Date(Date.now())).toLocaleString(); const b = a.split(", "); const c = a.split(" "); return "TestRun_" + b[0] + "_" + c[1] + c[2]; } -exports.getDefaultTestRunName = getDefaultTestRunName; function getDefaultRunDescription() { const pipelineName = process.env.GITHUB_WORKFLOW || "Unknown Pipeline"; return "Started using GH workflows" + (pipelineName ? "-" + pipelineName : ""); } -exports.getDefaultRunDescription = getDefaultRunDescription; function validateTestRunParamsFromPipeline(runTimeParams) { if (runTimeParams.runDisplayName && invalidDisplayName(runTimeParams.runDisplayName)) throw new Error("Invalid test run name. Test run name must be between 2 to 50 characters."); if (runTimeParams.runDescription && invalidDescription(runTimeParams.runDescription)) throw new Error("Invalid test run description. Test run description must be less than 100 characters."); } -exports.validateTestRunParamsFromPipeline = validateTestRunParamsFromPipeline; function getAllFileErrors(testObj) { var _a, _b, _c, _d, _e, _f; let allArtifacts = []; @@ -474,4 +485,26 @@ function getAllFileErrors(testObj) { } return fileErrors; } -exports.getAllFileErrors = getAllFileErrors; +/** + * This function returns the string with only ascii charaters, removing the non-ascii characters. + * @param pipelineName - original pipeline name + * @returns sanitised pipeline name with only ascii characters + */ +function sanitisePipelineNameHeader(pipelineName) { + if (!pipelineName) { + return pipelineName; + } + let result = ""; + for (const ch of pipelineName) { + const code = ch.codePointAt(0); + const allowed = (code >= 32 && code <= 126); // ASCII characters range, the only allowed characters in headers. + if (allowed) { + result += ch; + } + } + result = result.trim(); + if (result.length == 0) { + result = "-"; // this is what GH does when i try to give all non-ascii characters in the repo name. + } + return result; +} diff --git a/lib/Utils/CoreUtils.js b/lib/Utils/CoreUtils.js index d990719c..6d51dd88 100644 --- a/lib/Utils/CoreUtils.js +++ b/lib/Utils/CoreUtils.js @@ -23,7 +23,7 @@ var __importStar = (this && this.__importStar) || function (mod) { return result; }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.setOutput = exports.setFailed = exports.getInput = exports.debug = exports.exportVariable = void 0; +exports.setOutput = exports.setFailed = exports.getBoolInput = exports.getInput = exports.debug = exports.exportVariable = void 0; const core = __importStar(require("@actions/core")); function exportVariable(name, value) { core.exportVariable(name, value); @@ -41,6 +41,15 @@ function getInput(name) { return variable; } exports.getInput = getInput; +function getBoolInput(name, defaultValue = false) { + try { + return core.getBooleanInput(name, { required: false }); + } + catch (error) { + return defaultValue; + } +} +exports.getBoolInput = getBoolInput; function setFailed(message) { core.setFailed(message); } diff --git a/lib/Utils/FetchUtils.js b/lib/Utils/FetchUtils.js index b16ea1e4..153476f2 100644 --- a/lib/Utils/FetchUtils.js +++ b/lib/Utils/FetchUtils.js @@ -15,13 +15,23 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? ( }) : function(o, v) { o["default"] = v; }); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { @@ -32,7 +42,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }); }; Object.defineProperty(exports, "__esModule", { value: true }); -exports.httpClientRetries = void 0; +exports.httpClientRetries = httpClientRetries; const CommonUtils_1 = require("./CommonUtils"); const UtilModels_1 = require("./../models/UtilModels"); const httpc = __importStar(require("typed-rest-client/HttpClient")); @@ -47,8 +57,8 @@ const methodEnumToString = { [UtilModels_1.FetchCallType.patch]: "patch" }; // (note mohit): shift to the enum later. -function httpClientRetries(urlSuffix, header, method, retries = 1, data, isUploadCall = true, log = true) { - return __awaiter(this, void 0, void 0, function* () { +function httpClientRetries(urlSuffix_1, header_1, method_1) { + return __awaiter(this, arguments, void 0, function* (urlSuffix, header, method, retries = 1, data, isUploadCall = true, log = true) { let httpResponse; const retrriableCodes = [408, 429, 500, 502, 503, 504]; // 408 - Request Timeout, 429 - Too Many Requests, 500 - Internal Server Error, 502 - Bad Gateway, 503 - Service Unavailable, 504 - Gateway Timeout let backOffTimeForRetry = 5; // seconds @@ -74,7 +84,7 @@ function httpClientRetries(urlSuffix, header, method, retries = 1, data, isUploa const runId = process.env.GITHUB_RUN_ID; const pipelineName = process.env.GITHUB_WORKFLOW || "Unknown Pipeline"; const pipelineUri = `${githubBaseUrl}/${repository}/actions/runs/${runId}`; - header['x-ms-pipeline-name'] = pipelineName; // setting these for patch calls. + header['x-ms-pipeline-name'] = (0, CommonUtils_1.sanitisePipelineNameHeader)(pipelineName); // setting these for patch calls. header['x-ms-pipeline-uri'] = pipelineUri; httpResponse = yield httpClient.request(methodEnumToString[method], urlSuffix, data, header); } @@ -106,4 +116,3 @@ function httpClientRetries(urlSuffix, header, method, retries = 1, data, isUploa } }); } -exports.httpClientRetries = httpClientRetries; diff --git a/lib/Utils/LoadtestConfigUtil.js b/lib/Utils/LoadtestConfigUtil.js index 3075a914..369c4d1f 100644 --- a/lib/Utils/LoadtestConfigUtil.js +++ b/lib/Utils/LoadtestConfigUtil.js @@ -248,7 +248,10 @@ class LoadtestConfigUtil { } for (let serverComponent of metrics) { let key = (resourceId + '/' + ((_f = serverComponent.namespace) !== null && _f !== void 0 ? _f : Util.getResourceTypeFromResourceId(resourceId)) + '/' + serverComponent.name).toLowerCase(); - if (!loadtestConfig.serverMetricsConfig.hasOwnProperty(key) || (0, util_1.isNullOrUndefined)(loadtestConfig.serverMetricsConfig[key])) { + if (loadtestConfig.serverMetricsConfig.hasOwnProperty(key) && !(0, util_1.isNullOrUndefined)(loadtestConfig.serverMetricsConfig[key])) { + loadtestConfig.serverMetricsConfig[key].aggregation = loadtestConfig.serverMetricsConfig[key].aggregation + "," + serverComponent.aggregation; + } + else { loadtestConfig.serverMetricsConfig[key] = { name: serverComponent.name, aggregation: serverComponent.aggregation, @@ -258,9 +261,6 @@ class LoadtestConfigUtil { id: key }; } - else { - loadtestConfig.serverMetricsConfig[key].aggregation = loadtestConfig.serverMetricsConfig[key].aggregation + "," + serverComponent.aggregation; - } } } } diff --git a/lib/main.js b/lib/main.js index becb11d8..5d8f8d76 100644 --- a/lib/main.js +++ b/lib/main.js @@ -40,6 +40,7 @@ const AuthenticatorService_1 = require("./services/AuthenticatorService"); const CreateAndRunTest_1 = require("./RunnerFiles/CreateAndRunTest"); const APIService_1 = require("./services/APIService"); const TaskParametersUtil_1 = require("./Utils/TaskParametersUtil"); +const InputConstants = __importStar(require("./Constants/InputConstants")); function run() { return __awaiter(this, void 0, void 0, function* () { try { @@ -53,8 +54,9 @@ function run() { util.deleteFile(UtilModels_1.resultFolder); } fs.mkdirSync(UtilModels_1.resultFolder); + let waitForRunCompletionInput = CoreUtils.getBoolInput(InputConstants.waitForCompletion, true); let runner = new CreateAndRunTest_1.CreateAndRunTest(apiService); - runner.createAndRunTest(); + runner.createAndRunTest(waitForRunCompletionInput); } catch (err) { CoreUtils.setFailed(err.message); diff --git a/package.json b/package.json index 9dd60624..93e37e1c 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,6 @@ "dependencies": { "@actions/core": "^1.10.1", "@types/q": "^1.5.8", - "adal-node": "^0.2.4", "adm-zip": "^0.5.12", "extract-zip": "^2.0.1", "form-data": "^4.0.0", diff --git a/src/Constants/InputConstants.ts b/src/Constants/InputConstants.ts index f4be6ddc..5bfdfb8f 100644 --- a/src/Constants/InputConstants.ts +++ b/src/Constants/InputConstants.ts @@ -7,6 +7,7 @@ export const secrets = 'secrets'; export const resourceGroup = 'resourceGroup'; export const loadTestResource = 'loadTestResource'; export const loadTestConfigFile = 'loadTestConfigFile'; +export const waitForCompletion = 'waitForCompletion'; // labels user visible strings export const testRunNameLabel = 'Load Test Run Name'; diff --git a/src/RunnerFiles/CreateAndRunTest.ts b/src/RunnerFiles/CreateAndRunTest.ts index e3a14108..6cbca084 100644 --- a/src/RunnerFiles/CreateAndRunTest.ts +++ b/src/RunnerFiles/CreateAndRunTest.ts @@ -23,7 +23,7 @@ export class CreateAndRunTest { this.apiService = apiService; } - public async createAndRunTest() { + public async createAndRunTest(waitForRunCompletionInput: boolean = true) { await this.initialize(); let testPayload = await this.getTestPayload(); @@ -62,13 +62,18 @@ export class CreateAndRunTest { CoreUtils.exportVariable(PostTaskParameters.runId, testRunResult.testRunId); - await this.awaitTerminationForTestRun(testRunResult.testRunId, this.apiService); - testRunResult = await this.awaitResultsPopulation(testRunResult.testRunId, this.apiService) ?? testRunResult; - CoreUtils.exportVariable(PostTaskParameters.isRunCompleted, 'true'); - - this.printMetrics(testRunResult); - await this.uploadResultsToPipeline(testRunResult); - this.setTaskResults(testRunResult); + if(waitForRunCompletionInput) { + await this.awaitTerminationForTestRun(testRunResult.testRunId, this.apiService); + testRunResult = await this.awaitResultsPopulation(testRunResult.testRunId, this.apiService) ?? testRunResult; + CoreUtils.exportVariable(PostTaskParameters.isRunCompleted, 'true'); + + this.printMetrics(testRunResult); + await this.uploadResultsToPipeline(testRunResult); + this.setTaskResults(testRunResult); + } else { + console.log("Test run started. Not waiting for completion as per input."); + this.setRunStarted(); + } this.setOutputVariable(testRunResult); } @@ -312,6 +317,10 @@ export class CreateAndRunTest { CoreUtils.setFailed("TestStatus: " + testRunObj.status); } } + + private setRunStarted() : void { + console.log("Result: Test Run Started Successfully."); + } private setOutputVariable(testRunObj: TestRunModel) { let outputVarName = validateAndGetOutPutVarName(); diff --git a/src/Utils/CommonUtils.ts b/src/Utils/CommonUtils.ts index 377d0076..68f2d5eb 100644 --- a/src/Utils/CommonUtils.ts +++ b/src/Utils/CommonUtils.ts @@ -444,3 +444,27 @@ export function getAllFileErrors(testObj:TestModel | null): { [key: string]: str return fileErrors; } + +/** + * This function returns the string with only ascii charaters, removing the non-ascii characters. + * @param pipelineName - original pipeline name + * @returns sanitised pipeline name with only ascii characters + */ +export function sanitisePipelineNameHeader(pipelineName: string | null): string | null { + if(!pipelineName) { + return pipelineName; + } + let result = ""; + for (const ch of pipelineName) { + const code = ch.codePointAt(0)!; + const allowed = (code >= 32 && code <= 126); // ASCII characters range, the only allowed characters in headers. + if(allowed) { + result += ch; + } + } + result = result.trim(); + if(result.length == 0) { + result = "-"; // this is what GH does when i try to give all non-ascii characters in the repo name. + } + return result; +} \ No newline at end of file diff --git a/src/Utils/CoreUtils.ts b/src/Utils/CoreUtils.ts index dcf723d0..3678700d 100644 --- a/src/Utils/CoreUtils.ts +++ b/src/Utils/CoreUtils.ts @@ -16,6 +16,14 @@ export function getInput(name: string): string | undefined { return variable } +export function getBoolInput(name: string, defaultValue: boolean = false): boolean { + try { + return core.getBooleanInput(name, {required: false}); + } catch (error) { + return defaultValue; + } +} + export function setFailed(message: string) { core.setFailed(message); } diff --git a/src/Utils/EngineUtil.ts b/src/Utils/EngineUtil.ts index 30804543..3c812401 100644 --- a/src/Utils/EngineUtil.ts +++ b/src/Utils/EngineUtil.ts @@ -94,8 +94,8 @@ export function getLoadTestFrameworkModelFromKind(kind?: TestKind): BaseLoadTest return getLoadTestFrameworkModel(getLoadTestFrameworkFromKind(kind)); } -export module Resources { - export module Strings { +export namespace Resources { + export namespace Strings { export const allFrameworksFriendly = "URL, JMX and Locust"; } } diff --git a/src/Utils/FetchUtils.ts b/src/Utils/FetchUtils.ts index 41501724..9ed506b1 100644 --- a/src/Utils/FetchUtils.ts +++ b/src/Utils/FetchUtils.ts @@ -1,5 +1,5 @@ import { IHeaders, IHttpClientResponse } from 'typed-rest-client/Interfaces'; -import { errorCorrection, getResultObj, getUniqueId, sleep } from './CommonUtils'; +import { errorCorrection, getResultObj, getUniqueId, sanitisePipelineNameHeader, sleep } from './CommonUtils'; import { FetchCallType, correlationHeader } from './../models/UtilModels'; import * as httpc from 'typed-rest-client/HttpClient'; import { uploadFileData } from './FileUtils'; @@ -39,7 +39,7 @@ export async function httpClientRetries(urlSuffix : string, header : IHeaders, m const pipelineName = process.env.GITHUB_WORKFLOW || "Unknown Pipeline"; const pipelineUri = `${githubBaseUrl}/${repository}/actions/runs/${runId}`; - header['x-ms-pipeline-name'] = pipelineName; // setting these for patch calls. + header['x-ms-pipeline-name'] = sanitisePipelineNameHeader(pipelineName); // setting these for patch calls. header['x-ms-pipeline-uri'] = pipelineUri; httpResponse = await httpClient.request(methodEnumToString[method], urlSuffix, data, header); } diff --git a/src/Utils/LoadtestConfigUtil.ts b/src/Utils/LoadtestConfigUtil.ts index bbd9a67b..f7116c13 100644 --- a/src/Utils/LoadtestConfigUtil.ts +++ b/src/Utils/LoadtestConfigUtil.ts @@ -281,7 +281,10 @@ export class LoadtestConfigUtil { for(let serverComponent of metrics) { let key : string = (resourceId + '/' + (serverComponent.namespace ?? Util.getResourceTypeFromResourceId(resourceId)) + '/' + serverComponent.name).toLowerCase(); - if(!loadtestConfig.serverMetricsConfig.hasOwnProperty(key) || isNullOrUndefined(loadtestConfig.serverMetricsConfig[key])) { + if(loadtestConfig.serverMetricsConfig.hasOwnProperty(key) && !isNullOrUndefined(loadtestConfig.serverMetricsConfig[key])) { + loadtestConfig.serverMetricsConfig[key]!.aggregation = loadtestConfig.serverMetricsConfig[key]!.aggregation + "," + serverComponent.aggregation; + } + else { loadtestConfig.serverMetricsConfig[key] = { name: serverComponent.name, aggregation: serverComponent.aggregation, @@ -291,9 +294,6 @@ export class LoadtestConfigUtil { id: key } } - else { - loadtestConfig.serverMetricsConfig[key]!.aggregation = loadtestConfig.serverMetricsConfig[key]!.aggregation + "," + serverComponent.aggregation; - } } } } diff --git a/src/main.ts b/src/main.ts index 1fd235c7..660d0f3a 100644 --- a/src/main.ts +++ b/src/main.ts @@ -7,6 +7,7 @@ import { CreateAndRunTest } from "./RunnerFiles/CreateAndRunTest"; import { APIService } from './services/APIService'; import { TaskParameters } from './models/TaskParameters'; import { TaskParametersUtil } from './Utils/TaskParametersUtil'; +import * as InputConstants from './Constants/InputConstants'; async function run() { try { @@ -24,8 +25,10 @@ async function run() { } fs.mkdirSync(resultFolder); + let waitForRunCompletionInput : boolean = CoreUtils.getBoolInput(InputConstants.waitForCompletion, true); + let runner = new CreateAndRunTest(apiService); - runner.createAndRunTest(); + runner.createAndRunTest(waitForRunCompletionInput); } catch (err:any) { CoreUtils.setFailed(err.message); diff --git a/src/models/UtilModels.ts b/src/models/UtilModels.ts index e5571da8..9cf93fe5 100644 --- a/src/models/UtilModels.ts +++ b/src/models/UtilModels.ts @@ -53,7 +53,7 @@ export const reportZipFileName = 'report.zip'; export const resultZipFileName = 'results.zip'; export const correlationHeader = 'x-ms-correlation-request-id'; -export module ApiVersionConstants { +export namespace ApiVersionConstants { export const latestVersion = '2025-03-01-preview'; export const tm2022Version = '2022-11-01'; export const cp2022Version = '2022-12-01' @@ -100,13 +100,13 @@ export interface OutputVariableInterface { testRunId: string; } -export module PostTaskParameters { +export namespace PostTaskParameters { export const runId = 'LOADTEST_RUNID'; export const baseUri = 'LOADTEST_RESOURCE_URI'; export const isRunCompleted = 'LOADTEST_RUN_COMPLETED'; // this is set when the task is completed, to avoid get calls for the test again. } -export module OutPutVariablesConstants { +export namespace OutPutVariablesConstants { export const testRunId = 'testRunId'; } diff --git a/test/CommonUtils.test.ts b/test/CommonUtils.test.ts new file mode 100644 index 00000000..e9f7bde2 --- /dev/null +++ b/test/CommonUtils.test.ts @@ -0,0 +1,48 @@ +import { sanitisePipelineNameHeader } from "../src/Utils/CommonUtils"; +describe("CommonUtils tests", () => { + it.each([ + { + input: "Pipeline@2025#Release$!", + expected: "Pipeline@2025#Release$!" + }, + { + input: "Build_Definition-01 (Test) ", + expected: "Build_Definition-01 (Test)" + }, + { + input: "Normal Name", + expected: "Normal Name" + }, + { + input: "Special*&^%$#@!Characters", + expected: "Special*&^%$#@!Characters" + }, + { + input: "", + expected: "" + }, + { + input: " ", + expected: "-" + }, + { + input: "Name_with_underscores_and-dashes", + expected: "Name_with_underscores_and-dashes" + }, + { + input: null, + expected: null + }, + { + input: "🚀 Deploy", + expected: "Deploy" + }, + { + input: "流水线-test-𰻞", + expected: "-test-" + } + ])("sanitisePipelineNameHeader removes special characters", ({ input, expected }) => { + const result = sanitisePipelineNameHeader(input); + expect(result).toBe(expected); + }); +}); \ No newline at end of file diff --git a/test/createAndRunTest.test.ts b/test/createAndRunTest.test.ts index 36d3116d..5940d154 100644 --- a/test/createAndRunTest.test.ts +++ b/test/createAndRunTest.test.ts @@ -403,4 +403,46 @@ describe('create and run test', () => { expect(isRunCompleted).toBe("true"); }); + it("test run wont wait for completion when the wait is explicit false.", async () => { + TestSupport.createAndSetLoadTestConfigFile(testYamls.jmxComprehensiveYaml, coreMock, "createAndRunTest.yaml"); + + let getTestAPIStub = sinon.stub(APIService.prototype, "getTestAPI").resolves(null); + let getAppComponentsStub = sinon.stub(APIService.prototype, "getAppComponents").resolves(null); + let getServerMetricsConfigStub = sinon.stub(APIService.prototype, "getServerMetricsConfig").resolves(null); + let uploadFilesStub = sinon.stub(APIService.prototype, "uploadFile"); + let deleteFileAPIStub = sinon.stub(APIService.prototype, "deleteFileAPI"); + let createTestAPIStub = sinon.stub(APIService.prototype, "createTestAPI").resolves(TestPayloadConstants.createJmxTestExpectedPayload); + let patchAppComponentsStub = sinon.stub(APIService.prototype, "patchAppComponents"); + let patchServerMetricsConfigStub = sinon.stub(APIService.prototype, "patchServerMetricsConfig"); + let createTestRunStub = sinon.stub(APIService.prototype, "createTestRun").resolves(TestRunResponseConstants.testRunNonTerminalResponse); + let awaitTestTerminationsStub = sinon.stub(runner, "awaitTerminationForFileValidation").resolves(TestReponseConstants.testFileValidationCompletedResponse); + let awaitTestRunTerminationsStub = sinon.stub(runner, "awaitTerminationForTestRun").resolves(TestRunResponseConstants.testRunResultFailedResponse); + let awaitResultsPopulationStub = sinon.stub(runner, "awaitResultsPopulation").resolves(TestRunResponseConstants.testRunResultFailedResponse); + + await runner.createAndRunTest(false); + + expect(getTestAPIStub.calledOnce).toBe(true); + expect(getAppComponentsStub.notCalled).toBe(true); + expect(getServerMetricsConfigStub.notCalled).toBe(true); + expect(deleteFileAPIStub.notCalled).toBe(true); + expect(uploadFilesStub.calledWithMatch(sinon.match.any, FileType.TEST_SCRIPT)).toBe(true); + expect(uploadFilesStub.calledWithMatch(sinon.match.any, FileType.ADDITIONAL_ARTIFACTS)).toBe(true); + expect(uploadFilesStub.calledWithMatch(sinon.match.any, FileType.ZIPPED_ARTIFACTS)).toBe(true); + expect(uploadFilesStub.calledWithMatch(sinon.match.any, FileType.USER_PROPERTIES)).toBe(true); + expect(createTestAPIStub.calledOnce).toBe(true); + expect(patchAppComponentsStub.calledOnce).toBe(true); + expect(patchServerMetricsConfigStub.calledOnce).toBe(true); + expect(createTestRunStub.calledOnce).toBe(true); + expect(awaitTestTerminationsStub.calledOnce).toBe(true); + + expect(awaitTestRunTerminationsStub.callCount).toBe(0); + expect(awaitResultsPopulationStub.callCount).toBe(0); + + let testRunId = coreMock.getVariable(PostTaskParameters.runId); + let isRunCompleted = coreMock.getVariable(PostTaskParameters.isRunCompleted); + + expect(testRunId).toBe(TestRunResponseConstants.testRunNonTerminalResponse.testRunId); + expect(isRunCompleted).toBe(undefined); + }); + }) \ No newline at end of file From bc2a9e6bbc11d62d9d8714b91766630fb145af21 Mon Sep 17 00:00:00 2001 From: mohitpavan Date: Wed, 1 Apr 2026 11:25:24 +0530 Subject: [PATCH 2/4] Adding node 18 to the tests. --- .github/scripts/generateE2ETestMatrix.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/scripts/generateE2ETestMatrix.sh b/.github/scripts/generateE2ETestMatrix.sh index 365f8b84..33335921 100644 --- a/.github/scripts/generateE2ETestMatrix.sh +++ b/.github/scripts/generateE2ETestMatrix.sh @@ -1,5 +1,5 @@ os_options=('ubuntu-latest' 'windows-latest') -node_options=('20' '22') +node_options=('18' '20' '22') ScriptsRoot="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" E2ETestConfigFile="$ScriptsRoot/../config/e2eTestConfig.json" @@ -9,7 +9,7 @@ matrix_json="{\"include\":[" while read -r config; do rand_os=${os_options[$((RANDOM % 2))]} # Random OS for each test - rand_node=${node_options[$((RANDOM % 2))]} # Random Node version for each test + rand_node=${node_options[$((RANDOM % 3))]} # Random Node version for each test configFile=$(echo "$config" | jq -r '.configFile') secrets=$(echo "$config" | jq -r '.secrets') From 54d838a21f2a6e59b11afb51d84e3d0e7bb728cb Mon Sep 17 00:00:00 2001 From: mohitpavan Date: Wed, 1 Apr 2026 11:42:02 +0530 Subject: [PATCH 3/4] MJaking bool Input required. --- lib/Utils/CoreUtils.js | 2 +- src/Utils/CoreUtils.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Utils/CoreUtils.js b/lib/Utils/CoreUtils.js index 6d51dd88..bedb3092 100644 --- a/lib/Utils/CoreUtils.js +++ b/lib/Utils/CoreUtils.js @@ -43,7 +43,7 @@ function getInput(name) { exports.getInput = getInput; function getBoolInput(name, defaultValue = false) { try { - return core.getBooleanInput(name, { required: false }); + return core.getBooleanInput(name, { required: true }); } catch (error) { return defaultValue; diff --git a/src/Utils/CoreUtils.ts b/src/Utils/CoreUtils.ts index 3678700d..02816ccb 100644 --- a/src/Utils/CoreUtils.ts +++ b/src/Utils/CoreUtils.ts @@ -18,7 +18,7 @@ export function getInput(name: string): string | undefined { export function getBoolInput(name: string, defaultValue: boolean = false): boolean { try { - return core.getBooleanInput(name, {required: false}); + return core.getBooleanInput(name, {required: true}); } catch (error) { return defaultValue; } From 76237daeacbc659969dbb73890bf2fc5986bde43 Mon Sep 17 00:00:00 2001 From: mohitpavan Date: Wed, 1 Apr 2026 11:53:39 +0530 Subject: [PATCH 4/4] Trigggering the pipeline again. --- src/Utils/CoreUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Utils/CoreUtils.ts b/src/Utils/CoreUtils.ts index 02816ccb..7a0ba01d 100644 --- a/src/Utils/CoreUtils.ts +++ b/src/Utils/CoreUtils.ts @@ -30,4 +30,4 @@ export function setFailed(message: string) { export function setOutput(name: string, value: string) { core.setOutput(name, value); -} \ No newline at end of file +}