From e15ead9da29d80731cafba8d7a7149542ebb1cfe Mon Sep 17 00:00:00 2001 From: janssen <118828444+janssen-tiobe@users.noreply.github.com> Date: Fri, 27 Oct 2023 17:09:26 +0200 Subject: [PATCH 1/8] #33077: Always run upload artifact when analysis has run Removed all uses of logger.exit() and replaced them with proper error handling. --- dist/index.js | 193 ++++++++++++------------ package-lock.json | 113 +++++++------- package.json | 8 +- src/github/commits.ts | 2 +- src/github/pulls.ts | 6 +- src/helper/logger.ts | 11 -- src/interfaces.d.ts | 6 + src/main.ts | 168 +++++++++++---------- src/tics/api_helper.ts | 6 +- src/tics/fetcher.ts | 8 +- test/unit/github/annotations.test.ts | 4 +- test/unit/github/comments.test.ts | 41 ++++- test/unit/github/commits.test.ts | 11 +- test/unit/github/objects/annotations.ts | 24 +++ test/unit/github/pulls.test.ts | 11 +- test/unit/helper/logger.test.ts | 14 -- test/unit/main.test.ts | 101 +++++++++++-- test/unit/tics/fetcher.test.ts | 46 +++--- 18 files changed, 467 insertions(+), 306 deletions(-) create mode 100644 src/interfaces.d.ts diff --git a/dist/index.js b/dist/index.js index f281575d..0dd411c6 100644 --- a/dist/index.js +++ b/dist/index.js @@ -419,7 +419,7 @@ async function getChangedFilesOfCommit() { } catch (error) { const message = (0, error_1.handleOctokitError)(error); - logger_1.logger.exit(`Could not retrieve the changed files: ${message}`); + throw Error(`Could not retrieve the changed files: ${message}`); } return response; } @@ -476,7 +476,7 @@ async function getChangedFilesOfPullRequest() { } catch (error) { const message = (0, error_1.handleOctokitError)(error); - logger_1.logger.exit(`Could not retrieve the changed files: ${message}`); + throw Error(`Could not retrieve the changed files: ${message}`); } return response; } @@ -706,16 +706,6 @@ class Logger { core.setFailed(`\u001b[31m${error}`); this.called = 'error'; } - /** - * Uses core.setFailed to exit with error. - * @param error - */ - exit(error) { - error = this.maskSecrets(error); - this.addNewline('error'); - core.setFailed(`\u001b[31m${error}`); - process.exit(1); - } /** * Add newline above header, error and setFailed if the logger has been called before. * @param type the type of call to add a newline for. @@ -1200,9 +1190,9 @@ const configuration_1 = __nccwpck_require__(5527); * @param analysis the output of the TICS analysis run. */ function cliSummary(analysis) { - analysis.errorList.forEach(error => logger_1.logger.error(error)); + analysis?.errorList.forEach(error => logger_1.logger.error(error)); if (configuration_1.githubConfig.debugger) { - analysis.warningList.forEach(warning => logger_1.logger.warning(warning)); + analysis?.warningList.forEach(warning => logger_1.logger.warning(warning)); } } exports.cliSummary = cliSummary; @@ -1318,7 +1308,7 @@ async function getAnalyzedFiles(url) { let message = 'unknown error'; if (error instanceof Error) message = error.message; - logger_1.logger.exit(`There was an error retrieving the analyzed files: ${message}`); + throw Error(`There was an error retrieving the analyzed files: ${message}`); } return analyzedFiles; } @@ -1354,7 +1344,7 @@ async function getQualityGate(url) { let message = 'reason unknown'; if (error instanceof Error) message = error.message; - logger_1.logger.exit(`There was an error retrieving the quality gates: ${message}`); + throw Error(`There was an error retrieving the quality gates: ${message}`); } return response; } @@ -1409,7 +1399,7 @@ async function getAnnotations(apiLinks) { let message = 'reason unknown'; if (error instanceof Error) message = error.message; - logger_1.logger.exit(`An error occured when trying to retrieve annotations: ${message}`); + throw Error(`An error occured when trying to retrieve annotations: ${message}`); } return annotations; } @@ -1430,7 +1420,7 @@ async function getViewerVersion() { let message = 'reason unknown'; if (error instanceof Error) message = error.message; - logger_1.logger.exit(`There was an error retrieving the Viewer version: ${message}`); + throw Error(`There was an error retrieving the Viewer version: ${message}`); } return response; } @@ -84400,95 +84390,111 @@ main().catch((error) => { let message = 'TICS failed with unknown reason'; if (error instanceof Error) message = error.message; - logger_1.logger.exit(message); + logger_1.logger.setFailed(message); + process.exit(1); }); // exported for testing purposes async function main() { configure(); - const message = await meetsPrerequisites(); - if (message) { - return logger_1.logger.exit(message); - } - try { - await run(); - } - catch (error) { - throw error; - } -} -exports.main = main; -async function run() { + await meetsPrerequisites(); let analysis; if (configuration_1.ticsConfig.mode === 'diagnostic') { analysis = await diagnosticAnalysis(); } else { - let changedFilesFilePath = undefined; - let changedFiles = undefined; - if (configuration_1.ticsConfig.filelist) { - changedFilesFilePath = configuration_1.ticsConfig.filelist; - } - if (configuration_1.githubConfig.eventName === 'pull_request') { - changedFiles = await (0, pulls_1.getChangedFilesOfPullRequest)(); - } - else { - changedFiles = await (0, commits_1.getChangedFilesOfCommit)(); - } - if (!configuration_1.ticsConfig.filelist) { - if (changedFiles.length <= 0) { - return logger_1.logger.info('No changed files found to analyze.'); + const changedFiles = await getChangedFiles(); + if (changedFiles) { + analysis = await analyze(changedFiles); + if (analysis) { + try { + await processAnalysis(analysis, changedFiles); + } + catch (error) { + let message = 'Something went wrond: reason unknown'; + if (error instanceof Error) + message = error.message; + logger_1.logger.setFailed(message); + } } - changedFilesFilePath = (0, pulls_1.changedFilesToFile)(changedFiles); } - if (!changedFilesFilePath) - return logger_1.logger.error('No filepath for changedfiles list.'); - analysis = await (0, analyzer_1.runTicsAnalyzer)(changedFilesFilePath); - if (analysis.explorerUrls.length === 0) { - (0, comments_1.deletePreviousComments)(await (0, comments_1.getPostedComments)()); - if (!analysis.completed) { - await (0, comments_1.postErrorComment)(analysis); - logger_1.logger.setFailed('Failed to run TICS Github Action.'); - } - else if (analysis.warningList.find(w => w.includes('[WARNING 5057]'))) { - await postToConversation(false, 'No changed files applicable for TICS analysis quality gating.'); - } - else { - logger_1.logger.setFailed('Failed to run TICS Github Action.'); - analysis.errorList.push('Explorer URL not returned from TICS analysis.'); - await (0, comments_1.postErrorComment)(analysis); - } - (0, api_helper_1.cliSummary)(analysis); + } + if ((configuration_1.ticsConfig.tmpDir || configuration_1.githubConfig.debugger) && analysis) { + await (0, artifacts_1.uploadArtifact)(); + } + // Write the summary made to the action summary. + await core_1.summary.write({ overwrite: true }); + (0, api_helper_1.cliSummary)(analysis); +} +exports.main = main; +async function getChangedFiles() { + let changedFilesFilePath = undefined; + let changedFiles = undefined; + if (configuration_1.githubConfig.eventName === 'pull_request') { + changedFiles = await (0, pulls_1.getChangedFilesOfPullRequest)(); + } + else { + changedFiles = await (0, commits_1.getChangedFilesOfCommit)(); + } + if (configuration_1.ticsConfig.filelist) { + changedFilesFilePath = configuration_1.ticsConfig.filelist; + } + else { + if (changedFiles.length <= 0) { + logger_1.logger.info('No changed files found to analyze.'); return; } - const analysisResults = await (0, fetcher_1.getAnalysisResults)(analysis.explorerUrls, changedFiles); - if (analysisResults.missesQualityGate) - return logger_1.logger.exit('Some quality gates could not be retrieved'); - // If not run on a pull request no review comments have to be deleted - if (configuration_1.githubConfig.eventName === 'pull_request') { - const previousReviewComments = await (0, annotations_1.getPostedReviewComments)(); - if (previousReviewComments && previousReviewComments.length > 0) { - (0, annotations_1.deletePreviousReviewComments)(previousReviewComments); - } + changedFilesFilePath = (0, pulls_1.changedFilesToFile)(changedFiles); + } + return { + files: changedFiles, + path: changedFilesFilePath + }; +} +async function analyze(changedFiles) { + const analysis = await (0, analyzer_1.runTicsAnalyzer)(changedFiles.path); + if (analysis.explorerUrls.length === 0) { + (0, comments_1.deletePreviousComments)(await (0, comments_1.getPostedComments)()); + if (!analysis.completed) { + await (0, comments_1.postErrorComment)(analysis); + logger_1.logger.setFailed('Failed to run TICS Github Action.'); } - if (configuration_1.ticsConfig.postAnnotations) { - (0, annotations_1.postAnnotations)(analysisResults); + else if (analysis.warningList.find(w => w.includes('[WARNING 5057]'))) { + await postToConversation(false, 'No changed files applicable for TICS analysis quality gating.'); } - let reviewBody = (0, summary_1.createSummaryBody)(analysisResults); - // If not run on a pull request no comments have to be deleted - // and there is no conversation to post to. - if (configuration_1.githubConfig.eventName === 'pull_request') { - (0, comments_1.deletePreviousComments)(await (0, comments_1.getPostedComments)()); - await postToConversation(true, reviewBody, analysisResults.passed ? enums_1.Events.APPROVE : enums_1.Events.REQUEST_CHANGES); + else { + logger_1.logger.setFailed('Failed to run TICS Github Action.'); + analysis.errorList.push('Explorer URL not returned from TICS analysis.'); + await (0, comments_1.postErrorComment)(analysis); } - if (!analysisResults.passed) - logger_1.logger.setFailed(analysisResults.message); + (0, api_helper_1.cliSummary)(analysis); + return; } - if (configuration_1.ticsConfig.tmpDir || configuration_1.githubConfig.debugger) { - await (0, artifacts_1.uploadArtifact)(); + return analysis; +} +async function processAnalysis(analysis, changedFiles) { + const analysisResults = await (0, fetcher_1.getAnalysisResults)(analysis.explorerUrls, changedFiles.files); + if (analysisResults.missesQualityGate) { + throw Error('Some quality gates could not be retrieved'); } - // Write the summary made to the action summary. - await core_1.summary.write({ overwrite: true }); - (0, api_helper_1.cliSummary)(analysis); + // If not run on a pull request no review comments have to be deleted + if (configuration_1.githubConfig.eventName === 'pull_request') { + const previousReviewComments = await (0, annotations_1.getPostedReviewComments)(); + if (previousReviewComments && previousReviewComments.length > 0) { + (0, annotations_1.deletePreviousReviewComments)(previousReviewComments); + } + } + if (configuration_1.ticsConfig.postAnnotations) { + (0, annotations_1.postAnnotations)(analysisResults); + } + let reviewBody = (0, summary_1.createSummaryBody)(analysisResults); + // If not run on a pull request no comments have to be deleted + // and there is no conversation to post to. + if (configuration_1.githubConfig.eventName === 'pull_request') { + (0, comments_1.deletePreviousComments)(await (0, comments_1.getPostedComments)()); + await postToConversation(true, reviewBody, analysisResults.passed ? enums_1.Events.APPROVE : enums_1.Events.REQUEST_CHANGES); + } + if (!analysisResults.passed) + logger_1.logger.setFailed(analysisResults.message); } /** * Function for running the action in diagnostic mode. @@ -84563,25 +84569,22 @@ exports.configure = configure; /** * Checks if prerequisites are met to run the Github Plugin. * If any of these checks fail it returns a message. - * @returns Message containing why it failed the prerequisite. */ async function meetsPrerequisites() { - let message; const viewerVersion = await (0, fetcher_1.getViewerVersion)(); if (!viewerVersion || !(0, compare_versions_1.satisfies)(viewerVersion.version, '>=2022.4.0')) { const version = viewerVersion ? viewerVersion.version : 'unknown'; - message = `Minimum required TICS Viewer version is 2022.4. Found version ${version}.`; + throw Error(`Minimum required TICS Viewer version is 2022.4. Found version ${version}.`); } else if (configuration_1.ticsConfig.mode === 'diagnostic') { // No need for checked out repository. } else if (configuration_1.githubConfig.eventName !== 'pull_request' && !configuration_1.ticsConfig.filelist) { - message = 'If the the action is run outside a pull request it should be run with a filelist.'; + throw Error('If the the action is run outside a pull request it should be run with a filelist.'); } else if (!isCheckedOut()) { - message = 'No checkout found to analyze. Please perform a checkout before running the TICS Action.'; + throw Error('No checkout found to analyze. Please perform a checkout before running the TICS Action.'); } - return message; } /** * Checks if a .git directory exists to see if a checkout has been performed. diff --git a/package-lock.json b/package-lock.json index 8d8b335b..6756acfa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,12 +25,12 @@ }, "devDependencies": { "@types/jest": "^29.5.5", - "@types/node": "^20.8.6", - "@types/underscore": "^1.11.11", - "@typescript-eslint/parser": "^6.7.5", + "@types/node": "^20.8.9", + "@types/underscore": "^1.11.12", + "@typescript-eslint/parser": "^6.9.0", "@vercel/ncc": "^0.38.1", "async-listen": "^3.0.1", - "eslint": "^8.51.0", + "eslint": "^8.52.0", "eslint-config-prettier": "^9.0.0", "http-proxy": "^1.18.1", "jest": "^29.7.0", @@ -932,9 +932,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.51.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.51.0.tgz", - "integrity": "sha512-HxjQ8Qn+4SI3/AFv6sOrDB+g6PpUTDwSJiQqOrnneEk8L71161srI9gjzzZvYVbzHiVg/BvcH95+cK/zfIt4pg==", + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.52.0.tgz", + "integrity": "sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -949,12 +949,12 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.11", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz", - "integrity": "sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==", + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", + "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", + "@humanwhocodes/object-schema": "^2.0.1", "debug": "^4.1.1", "minimatch": "^3.0.5" }, @@ -976,9 +976,9 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", "dev": true }, "node_modules/@istanbuljs/load-nyc-config": { @@ -1834,11 +1834,11 @@ } }, "node_modules/@types/node": { - "version": "20.8.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.6.tgz", - "integrity": "sha512-eWO4K2Ji70QzKUqRy6oyJWUeB7+g2cRagT3T/nxYibYcT4y2BDL8lqolRXjTHmkZCdJfIPaY73KbJAZmcryxTQ==", + "version": "20.8.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.9.tgz", + "integrity": "sha512-UzykFsT3FhHb1h7yD4CA4YhBHq545JC0YnEz41xkipN88eKQtL6rSgocL5tbAP6Ola9Izm/Aw4Ora8He4x0BHg==", "dependencies": { - "undici-types": "~5.25.1" + "undici-types": "~5.26.4" } }, "node_modules/@types/node-fetch": { @@ -1857,9 +1857,9 @@ "dev": true }, "node_modules/@types/underscore": { - "version": "1.11.11", - "resolved": "https://registry.npmjs.org/@types/underscore/-/underscore-1.11.11.tgz", - "integrity": "sha512-J/ZgSP9Yv0S+wfUfeRh9ynktcCvycfW4S9NbzkFdiHLBth+Ctdy5nYg3ZAqUKq7v3gcJce6rXo41zJV6IqsXsQ==", + "version": "1.11.12", + "resolved": "https://registry.npmjs.org/@types/underscore/-/underscore-1.11.12.tgz", + "integrity": "sha512-beX81q12OQo809WJ/UYCvUDvJR3YQ4wtehYYQ6eNw5VLyd+KUNBRV4FgzZCHBmACbdPulH9F9ifhxzFFU9TWvQ==", "dev": true }, "node_modules/@types/yargs": { @@ -1878,15 +1878,15 @@ "dev": true }, "node_modules/@typescript-eslint/parser": { - "version": "6.7.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.5.tgz", - "integrity": "sha512-bIZVSGx2UME/lmhLcjdVc7ePBwn7CLqKarUBL4me1C5feOd663liTGjMBGVcGr+BhnSLeP4SgwdvNnnkbIdkCw==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.9.0.tgz", + "integrity": "sha512-GZmjMh4AJ/5gaH4XF2eXA8tMnHWP+Pm1mjQR2QN4Iz+j/zO04b9TOvJYOX2sCNIQHtRStKTxRY1FX7LhpJT4Gw==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.7.5", - "@typescript-eslint/types": "6.7.5", - "@typescript-eslint/typescript-estree": "6.7.5", - "@typescript-eslint/visitor-keys": "6.7.5", + "@typescript-eslint/scope-manager": "6.9.0", + "@typescript-eslint/types": "6.9.0", + "@typescript-eslint/typescript-estree": "6.9.0", + "@typescript-eslint/visitor-keys": "6.9.0", "debug": "^4.3.4" }, "engines": { @@ -1906,13 +1906,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.7.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.5.tgz", - "integrity": "sha512-GAlk3eQIwWOJeb9F7MKQ6Jbah/vx1zETSDw8likab/eFcqkjSD7BI75SDAeC5N2L0MmConMoPvTsmkrg71+B1A==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.9.0.tgz", + "integrity": "sha512-1R8A9Mc39n4pCCz9o79qRO31HGNDvC7UhPhv26TovDsWPBDx+Sg3rOZdCELIA3ZmNoWAuxaMOT7aWtGRSYkQxw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.7.5", - "@typescript-eslint/visitor-keys": "6.7.5" + "@typescript-eslint/types": "6.9.0", + "@typescript-eslint/visitor-keys": "6.9.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1923,9 +1923,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.7.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.5.tgz", - "integrity": "sha512-WboQBlOXtdj1tDFPyIthpKrUb+kZf2VroLZhxKa/VlwLlLyqv/PwUNgL30BlTVZV1Wu4Asu2mMYPqarSO4L5ZQ==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.9.0.tgz", + "integrity": "sha512-+KB0lbkpxBkBSiVCuQvduqMJy+I1FyDbdwSpM3IoBS7APl4Bu15lStPjgBIdykdRqQNYqYNMa8Kuidax6phaEw==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1936,13 +1936,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.7.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.5.tgz", - "integrity": "sha512-NhJiJ4KdtwBIxrKl0BqG1Ur+uw7FiOnOThcYx9DpOGJ/Abc9z2xNzLeirCG02Ig3vkvrc2qFLmYSSsaITbKjlg==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.9.0.tgz", + "integrity": "sha512-NJM2BnJFZBEAbCfBP00zONKXvMqihZCrmwCaik0UhLr0vAgb6oguXxLX1k00oQyD+vZZ+CJn3kocvv2yxm4awQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.7.5", - "@typescript-eslint/visitor-keys": "6.7.5", + "@typescript-eslint/types": "6.9.0", + "@typescript-eslint/visitor-keys": "6.9.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1963,12 +1963,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.7.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.5.tgz", - "integrity": "sha512-3MaWdDZtLlsexZzDSdQWsFQ9l9nL8B80Z4fImSpyllFC/KLqWQRdEcB+gGGO+N3Q2uL40EsG66wZLsohPxNXvg==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.9.0.tgz", + "integrity": "sha512-dGtAfqjV6RFOtIP8I0B4ZTBRrlTT8NHHlZZSchQx3qReaoDeXhYM++M4So2AgFK9ZB0emRPA6JI1HkafzA2Ibg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.7.5", + "@typescript-eslint/types": "6.9.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -1979,6 +1979,12 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, "node_modules/@vercel/ncc": { "version": "0.38.1", "resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.38.1.tgz", @@ -2839,18 +2845,19 @@ } }, "node_modules/eslint": { - "version": "8.51.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.51.0.tgz", - "integrity": "sha512-2WuxRZBrlwnXi+/vFSJyjMqrNjtJqiasMzehF0shoLaW7DzS3/9Yvrmq5JiT66+pNjiX4UBnLDiKHcWAr/OInA==", + "version": "8.52.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.52.0.tgz", + "integrity": "sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "8.51.0", - "@humanwhocodes/config-array": "^0.11.11", + "@eslint/js": "8.52.0", + "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -5558,9 +5565,9 @@ } }, "node_modules/undici-types": { - "version": "5.25.3", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.25.3.tgz", - "integrity": "sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==" + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, "node_modules/universal-user-agent": { "version": "6.0.0", diff --git a/package.json b/package.json index f2703a23..2714e9d1 100644 --- a/package.json +++ b/package.json @@ -45,12 +45,12 @@ }, "devDependencies": { "@types/jest": "^29.5.5", - "@types/node": "^20.8.6", - "@types/underscore": "^1.11.11", - "@typescript-eslint/parser": "^6.7.5", + "@types/node": "^20.8.9", + "@types/underscore": "^1.11.12", + "@typescript-eslint/parser": "^6.9.0", "@vercel/ncc": "^0.38.1", "async-listen": "^3.0.1", - "eslint": "^8.51.0", + "eslint": "^8.52.0", "eslint-config-prettier": "^9.0.0", "http-proxy": "^1.18.1", "jest": "^29.7.0", diff --git a/src/github/commits.ts b/src/github/commits.ts index c8a58e36..a269ca90 100644 --- a/src/github/commits.ts +++ b/src/github/commits.ts @@ -41,7 +41,7 @@ export async function getChangedFilesOfCommit(): Promise { logger.info('Retrieved changed files from commit.'); } catch (error: unknown) { const message = handleOctokitError(error); - logger.exit(`Could not retrieve the changed files: ${message}`); + throw Error(`Could not retrieve the changed files: ${message}`); } return response; } diff --git a/src/github/pulls.ts b/src/github/pulls.ts index 6fdac116..bb9162ab 100644 --- a/src/github/pulls.ts +++ b/src/github/pulls.ts @@ -3,7 +3,7 @@ import { normalize, resolve } from 'canonical-path'; import { logger } from '../helper/logger'; import { githubConfig, octokit, ticsConfig } from '../configuration'; import { ChangedFile } from './interfaces'; -import { handleOctokitError } from '../helper/error'; +import { handleOctokitError as getMessageFromOctokitError } from '../helper/error'; /** * Sends a request to retrieve the changed files for a given pull request to the GitHub API. @@ -40,8 +40,8 @@ export async function getChangedFilesOfPullRequest(): Promise { }); logger.info('Retrieved changed files from pull request.'); } catch (error: unknown) { - const message = handleOctokitError(error); - logger.exit(`Could not retrieve the changed files: ${message}`); + const message = getMessageFromOctokitError(error); + throw Error(`Could not retrieve the changed files: ${message}`); } return response; } diff --git a/src/helper/logger.ts b/src/helper/logger.ts index ecea73ad..b69dee6a 100644 --- a/src/helper/logger.ts +++ b/src/helper/logger.ts @@ -69,17 +69,6 @@ class Logger { this.called = 'error'; } - /** - * Uses core.setFailed to exit with error. - * @param error - */ - exit(error: string): void { - error = this.maskSecrets(error); - this.addNewline('error'); - core.setFailed(`\u001b[31m${error}`); - process.exit(1); - } - /** * Add newline above header, error and setFailed if the logger has been called before. * @param type the type of call to add a newline for. diff --git a/src/interfaces.d.ts b/src/interfaces.d.ts new file mode 100644 index 00000000..fdac70fc --- /dev/null +++ b/src/interfaces.d.ts @@ -0,0 +1,6 @@ +import { ChangedFile } from './github/interfaces'; + +export interface ChangedFiles { + files: ChangedFile[]; + path: string; +} diff --git a/src/main.ts b/src/main.ts index f508621b..902334bf 100644 --- a/src/main.ts +++ b/src/main.ts @@ -15,110 +15,129 @@ import { exportVariable, summary } from '@actions/core'; import { Analysis } from './helper/interfaces'; import { uploadArtifact } from './github/artifacts'; import { getChangedFilesOfCommit } from './github/commits'; +import { ChangedFiles } from './interfaces'; main().catch((error: unknown) => { let message = 'TICS failed with unknown reason'; if (error instanceof Error) message = error.message; - logger.exit(message); + logger.setFailed(message); + process.exit(1); }); // exported for testing purposes export async function main(): Promise { configure(); - const message = await meetsPrerequisites(); - if (message) { - return logger.exit(message); - } - - try { - await run(); - } catch (error: unknown) { - throw error; - } -} + await meetsPrerequisites(); -async function run() { let analysis: Analysis | undefined; - if (ticsConfig.mode === 'diagnostic') { analysis = await diagnosticAnalysis(); } else { - let changedFilesFilePath = undefined; - let changedFiles = undefined; - - if (ticsConfig.filelist) { - changedFilesFilePath = ticsConfig.filelist; + const changedFiles = await getChangedFiles(); + + if (changedFiles) { + analysis = await analyze(changedFiles); + + if (analysis) { + try { + await processAnalysis(analysis, changedFiles); + } catch (error: unknown) { + let message = 'Something went wrond: reason unknown'; + if (error instanceof Error) message = error.message; + logger.setFailed(message); + } + } } + } - if (githubConfig.eventName === 'pull_request') { - changedFiles = await getChangedFilesOfPullRequest(); - } else { - changedFiles = await getChangedFilesOfCommit(); - } + if ((ticsConfig.tmpDir || githubConfig.debugger) && analysis) { + await uploadArtifact(); + } - if (!ticsConfig.filelist) { - if (changedFiles.length <= 0) { - return logger.info('No changed files found to analyze.'); - } - changedFilesFilePath = changedFilesToFile(changedFiles); - } + // Write the summary made to the action summary. + await summary.write({ overwrite: true }); + cliSummary(analysis); +} - if (!changedFilesFilePath) return logger.error('No filepath for changedfiles list.'); - analysis = await runTicsAnalyzer(changedFilesFilePath); +async function getChangedFiles(): Promise { + let changedFilesFilePath = undefined; + let changedFiles = undefined; - if (analysis.explorerUrls.length === 0) { - deletePreviousComments(await getPostedComments()); - if (!analysis.completed) { - await postErrorComment(analysis); - logger.setFailed('Failed to run TICS Github Action.'); - } else if (analysis.warningList.find(w => w.includes('[WARNING 5057]'))) { - await postToConversation(false, 'No changed files applicable for TICS analysis quality gating.'); - } else { - logger.setFailed('Failed to run TICS Github Action.'); - analysis.errorList.push('Explorer URL not returned from TICS analysis.'); - await postErrorComment(analysis); - } - cliSummary(analysis); + if (githubConfig.eventName === 'pull_request') { + changedFiles = await getChangedFilesOfPullRequest(); + } else { + changedFiles = await getChangedFilesOfCommit(); + } + + if (ticsConfig.filelist) { + changedFilesFilePath = ticsConfig.filelist; + } else { + if (changedFiles.length <= 0) { + logger.info('No changed files found to analyze.'); return; } + changedFilesFilePath = changedFilesToFile(changedFiles); + } - const analysisResults = await getAnalysisResults(analysis.explorerUrls, changedFiles); + return { + files: changedFiles, + path: changedFilesFilePath + }; +} - if (analysisResults.missesQualityGate) return logger.exit('Some quality gates could not be retrieved'); +async function analyze(changedFiles: ChangedFiles): Promise { + const analysis = await runTicsAnalyzer(changedFiles.path); - // If not run on a pull request no review comments have to be deleted - if (githubConfig.eventName === 'pull_request') { - const previousReviewComments = await getPostedReviewComments(); - if (previousReviewComments && previousReviewComments.length > 0) { - deletePreviousReviewComments(previousReviewComments); - } + if (analysis.explorerUrls.length === 0) { + deletePreviousComments(await getPostedComments()); + if (!analysis.completed) { + await postErrorComment(analysis); + logger.setFailed('Failed to run TICS Github Action.'); + } else if (analysis.warningList.find(w => w.includes('[WARNING 5057]'))) { + await postToConversation(false, 'No changed files applicable for TICS analysis quality gating.'); + } else { + logger.setFailed('Failed to run TICS Github Action.'); + analysis.errorList.push('Explorer URL not returned from TICS analysis.'); + await postErrorComment(analysis); } + cliSummary(analysis); + return; + } - if (ticsConfig.postAnnotations) { - postAnnotations(analysisResults); - } + return analysis; +} - let reviewBody = createSummaryBody(analysisResults); +async function processAnalysis(analysis: Analysis, changedFiles: ChangedFiles) { + const analysisResults = await getAnalysisResults(analysis.explorerUrls, changedFiles.files); - // If not run on a pull request no comments have to be deleted - // and there is no conversation to post to. - if (githubConfig.eventName === 'pull_request') { - deletePreviousComments(await getPostedComments()); + if (analysisResults.missesQualityGate) { + throw Error('Some quality gates could not be retrieved'); + } - await postToConversation(true, reviewBody, analysisResults.passed ? Events.APPROVE : Events.REQUEST_CHANGES); + // If not run on a pull request no review comments have to be deleted + if (githubConfig.eventName === 'pull_request') { + const previousReviewComments = await getPostedReviewComments(); + if (previousReviewComments && previousReviewComments.length > 0) { + deletePreviousReviewComments(previousReviewComments); } + } - if (!analysisResults.passed) logger.setFailed(analysisResults.message); + if (ticsConfig.postAnnotations) { + postAnnotations(analysisResults); } - if (ticsConfig.tmpDir || githubConfig.debugger) { - await uploadArtifact(); + let reviewBody = createSummaryBody(analysisResults); + + // If not run on a pull request no comments have to be deleted + // and there is no conversation to post to. + if (githubConfig.eventName === 'pull_request') { + deletePreviousComments(await getPostedComments()); + + await postToConversation(true, reviewBody, analysisResults.passed ? Events.APPROVE : Events.REQUEST_CHANGES); } - // Write the summary made to the action summary. - await summary.write({ overwrite: true }); - cliSummary(analysis); + if (!analysisResults.passed) logger.setFailed(analysisResults.message); } /** @@ -199,25 +218,20 @@ export function configure(): void { /** * Checks if prerequisites are met to run the Github Plugin. * If any of these checks fail it returns a message. - * @returns Message containing why it failed the prerequisite. */ -async function meetsPrerequisites(): Promise { - let message; - +async function meetsPrerequisites(): Promise { const viewerVersion = await getViewerVersion(); if (!viewerVersion || !satisfies(viewerVersion.version, '>=2022.4.0')) { const version = viewerVersion ? viewerVersion.version : 'unknown'; - message = `Minimum required TICS Viewer version is 2022.4. Found version ${version}.`; + throw Error(`Minimum required TICS Viewer version is 2022.4. Found version ${version}.`); } else if (ticsConfig.mode === 'diagnostic') { // No need for checked out repository. } else if (githubConfig.eventName !== 'pull_request' && !ticsConfig.filelist) { - message = 'If the the action is run outside a pull request it should be run with a filelist.'; + throw Error('If the the action is run outside a pull request it should be run with a filelist.'); } else if (!isCheckedOut()) { - message = 'No checkout found to analyze. Please perform a checkout before running the TICS Action.'; + throw Error('No checkout found to analyze. Please perform a checkout before running the TICS Action.'); } - - return message; } /** diff --git a/src/tics/api_helper.ts b/src/tics/api_helper.ts index 8cfe4806..9a89e9cc 100644 --- a/src/tics/api_helper.ts +++ b/src/tics/api_helper.ts @@ -6,10 +6,10 @@ import { Analysis } from '../helper/interfaces'; * Creates a cli summary of all errors and bugs based on the logLevel. * @param analysis the output of the TICS analysis run. */ -export function cliSummary(analysis: Analysis): void { - analysis.errorList.forEach(error => logger.error(error)); +export function cliSummary(analysis?: Analysis): void { + analysis?.errorList.forEach(error => logger.error(error)); if (githubConfig.debugger) { - analysis.warningList.forEach(warning => logger.warning(warning)); + analysis?.warningList.forEach(warning => logger.warning(warning)); } } diff --git a/src/tics/fetcher.ts b/src/tics/fetcher.ts index 16febb60..c1f3ff39 100644 --- a/src/tics/fetcher.ts +++ b/src/tics/fetcher.ts @@ -96,7 +96,7 @@ export async function getAnalyzedFiles(url: string): Promise { } catch (error: unknown) { let message = 'unknown error'; if (error instanceof Error) message = error.message; - logger.exit(`There was an error retrieving the analyzed files: ${message}`); + throw Error(`There was an error retrieving the analyzed files: ${message}`); } return analyzedFiles; } @@ -135,7 +135,7 @@ export async function getQualityGate(url: string): Promise { } catch (error: unknown) { let message = 'reason unknown'; if (error instanceof Error) message = error.message; - logger.exit(`There was an error retrieving the Viewer version: ${message}`); + throw Error(`There was an error retrieving the Viewer version: ${message}`); } return response; } diff --git a/test/unit/github/annotations.test.ts b/test/unit/github/annotations.test.ts index cb6b2f09..53aa1e77 100644 --- a/test/unit/github/annotations.test.ts +++ b/test/unit/github/annotations.test.ts @@ -1,7 +1,7 @@ -import { deletePreviousReviewComments, getPostedReviewComments } from '../../../src/github/annotations'; +import { deletePreviousReviewComments, getPostedReviewComments, postAnnotations } from '../../../src/github/annotations'; import { octokit } from '../../../src/configuration'; import { logger } from '../../../src/helper/logger'; -import { emptyComment, warningComment } from './objects/annotations'; +import { analysisResults, emptyComment, warningComment } from './objects/annotations'; describe('getPostedReviewComments', () => { test('Should return single file on getPostedReviewComments', async () => { diff --git a/test/unit/github/comments.test.ts b/test/unit/github/comments.test.ts index e7438d4e..79440456 100644 --- a/test/unit/github/comments.test.ts +++ b/test/unit/github/comments.test.ts @@ -1,6 +1,6 @@ import { githubConfig, octokit } from '../../../src/configuration'; import { logger } from '../../../src/helper/logger'; -import { deletePreviousComments, postErrorComment, getPostedComments } from '../../../src/github/comments'; +import { deletePreviousComments, postErrorComment, getPostedComments, postNothingAnalyzedComment } from '../../../src/github/comments'; import { createErrorSummary } from '../../../src/helper/summary'; import { Comment } from '../../../src/github/interfaces'; @@ -98,11 +98,22 @@ describe('postErrorComment', () => { }); }); +describe('postNothingAnalyzedComment', () => { + test('Should call createComment once', async () => { + (createErrorSummary as any).mockReturnValueOnce('body'); + const spy = jest.spyOn(octokit.rest.issues, 'createComment'); + + const message = 'message'; + await postNothingAnalyzedComment(message); + expect(spy).toBeCalledTimes(1); + }); +}); + describe('deletePreviousComments', () => { test('Should call deleteComment once', async () => { const spy = jest.spyOn(octokit.rest.issues, 'deleteComment'); - deletePreviousComments([comment]); + deletePreviousComments([commentWithBody]); expect(spy).toBeCalledTimes(1); }); @@ -110,7 +121,7 @@ describe('deletePreviousComments', () => { (createErrorSummary as any).mockReturnValueOnce('body'); const spy = jest.spyOn(octokit.rest.issues, 'deleteComment'); - deletePreviousComments([comment]); + deletePreviousComments([commentWithBody]); const calledWith = { owner: githubConfig.owner, repo: githubConfig.reponame, @@ -119,6 +130,14 @@ describe('deletePreviousComments', () => { expect(spy).toBeCalledWith(calledWith); }); + test('Should call createComment with values', async () => { + (createErrorSummary as any).mockReturnValueOnce('body'); + const spy = jest.spyOn(octokit.rest.issues, 'deleteComment'); + + deletePreviousComments([commentWithoutBody]); + expect(spy).toBeCalledTimes(0); + }); + test('Should throw an error on postErrorComment', async () => { (createErrorSummary as any).mockReturnValueOnce('body'); jest.spyOn(octokit.rest.issues, 'deleteComment').mockImplementationOnce(() => { @@ -126,13 +145,13 @@ describe('deletePreviousComments', () => { }); const spy = jest.spyOn(logger, 'error'); - deletePreviousComments([comment]); + deletePreviousComments([commentWithBody]); expect(spy).toBeCalledTimes(1); }); }); -const comment: Comment = { +const commentWithBody: Comment = { url: '', html_url: '', issue_url: '', @@ -143,3 +162,15 @@ const comment: Comment = { updated_at: '', body: '

TICS Quality Gate

' }; + +const commentWithoutBody: Comment = { + url: '', + html_url: '', + issue_url: '', + id: 0, + node_id: '', + user: null, + created_at: '', + updated_at: '', + body: undefined +}; diff --git a/test/unit/github/commits.test.ts b/test/unit/github/commits.test.ts index 781125ce..c922dc82 100644 --- a/test/unit/github/commits.test.ts +++ b/test/unit/github/commits.test.ts @@ -89,9 +89,14 @@ describe('getChangedFilesOfCommit', () => { (octokit.paginate as any).mockImplementationOnce(() => { throw new Error(); }); - const spy = jest.spyOn(logger, 'exit'); - await getChangedFilesOfCommit(); - expect(spy).toHaveBeenCalledTimes(1); + let error: any; + try { + await getChangedFilesOfCommit(); + } catch (err) { + error = err; + } + + expect(error).toBeInstanceOf(Error); }); }); diff --git a/test/unit/github/objects/annotations.ts b/test/unit/github/objects/annotations.ts index d0d3e094..38339497 100644 --- a/test/unit/github/objects/annotations.ts +++ b/test/unit/github/objects/annotations.ts @@ -1,4 +1,5 @@ import { Links, ReviewComment, User } from '../../../../src/github/interfaces'; +import { AnalysisResults, ProjectResult, TicsReviewComment, TicsReviewComments } from '../../../../src/helper/interfaces'; export const user: User = { login: '', @@ -74,3 +75,26 @@ export const emptyComment: ReviewComment = { author_association: 'COLLABORATOR', _links: links }; + +export const analysisResults: AnalysisResults = { + passed: false, + message: '', + missesQualityGate: false, + projectResults: [ + { + project: '', + explorerUrl: '', + analyzedFiles: [], + reviewComments: { + postable: [ + { + title: '', + body: '', + line: 0 + } + ], + unpostable: [] + } + } + ] +}; diff --git a/test/unit/github/pulls.test.ts b/test/unit/github/pulls.test.ts index b31ecdfb..549adc02 100644 --- a/test/unit/github/pulls.test.ts +++ b/test/unit/github/pulls.test.ts @@ -70,10 +70,15 @@ describe('getChangedFilesOfPullRequest', () => { (octokit.paginate as any).mockImplementationOnce(() => { throw new Error(); }); - const spy = jest.spyOn(logger, 'exit'); - await getChangedFilesOfPullRequest(); - expect(spy).toHaveBeenCalledTimes(1); + let error: any; + try { + await getChangedFilesOfPullRequest(); + } catch (err) { + error = err; + } + + expect(error).toBeInstanceOf(Error); }); }); diff --git a/test/unit/helper/logger.test.ts b/test/unit/helper/logger.test.ts index e63832e7..8a583be9 100644 --- a/test/unit/helper/logger.test.ts +++ b/test/unit/helper/logger.test.ts @@ -87,17 +87,3 @@ describe('setFailed', () => { expect(logger.called).toEqual('error'); }); }); - -describe('exit', () => { - test('Should call core.setFailed on exit', () => { - const setFailed = jest.spyOn(core, 'setFailed'); - const addNewline = jest.spyOn(logger, 'addNewline'); - - logger.exit('error'); - - expect(setFailed).toHaveBeenCalledTimes(1); - expect(setFailed).toHaveBeenCalledWith(expect.stringContaining('error')); - expect(addNewline).toHaveBeenCalledTimes(1); - expect(addNewline).toHaveBeenCalledWith('error'); - }); -}); diff --git a/test/unit/main.test.ts b/test/unit/main.test.ts index 61418734..6696fc26 100644 --- a/test/unit/main.test.ts +++ b/test/unit/main.test.ts @@ -10,6 +10,8 @@ import * as fetcher from '../../src/tics/fetcher'; import * as review from '../../src/github/review'; import * as annotations from '../../src/github/annotations'; import * as comments from '../../src/github/comments'; +import * as artifacts from '../../src/github/artifacts'; +import * as api_helper from '../../src/tics/api_helper'; import { analysisFailedNoUrl, @@ -29,32 +31,47 @@ import { describe('pre checks', () => { test('Should call exit if viewer version is too low', async () => { jest.spyOn(fetcher, 'getViewerVersion').mockResolvedValue({ version: '2022.0.0' }); - const spyExit = jest.spyOn(logger, 'exit'); - await main.main(); + let error: any; + try { + await main.main(); + } catch (err) { + error = err; + } - expect(spyExit).toHaveBeenCalledWith(expect.stringContaining('Minimum required TICS Viewer version is 2022.4. Found version 2022.0.0.')); + expect(error).toBeInstanceOf(Error); + expect((error as Error).message).toEqual(expect.stringContaining('Minimum required TICS Viewer version is 2022.4. Found version 2022.0.0.')); }); test('Should call exit if event is not pull request and', async () => { jest.spyOn(fetcher, 'getViewerVersion').mockResolvedValue({ version: '2022.4.0' }); jest.spyOn(main, 'configure').mockImplementation(); - const spyExit = jest.spyOn(logger, 'exit'); - await main.main(); + let error: any; + try { + await main.main(); + } catch (err) { + error = err; + } - expect(spyExit).toHaveBeenCalledWith( + expect(error).toBeInstanceOf(Error); + expect((error as Error).message).toEqual( expect.stringContaining('If the the action is run outside a pull request it should be run with a filelist.') ); }); test('Should call exit if ".git" does not exist', async () => { githubConfig.eventName = 'pull_request'; - const spyExit = jest.spyOn(logger, 'exit'); - await main.main(); + let error: any; + try { + await main.main(); + } catch (err) { + error = err; + } - expect(spyExit).toHaveBeenCalledWith( + expect(error).toBeInstanceOf(Error); + expect((error as Error).message).toEqual( expect.stringContaining('No checkout found to analyze. Please perform a checkout before running the TICS Action.') ); }); @@ -102,7 +119,7 @@ describe('SetFailed checks', () => { expect(spyError).toHaveBeenCalledWith(expect.stringContaining('Explorer URL not returned from TICS analysis.')); }); - test('Should call exit if analysis passed and quality gate undefined', async () => { + test('Should call setFailed if analysis passed and quality gate undefined', async () => { (existsSync as any).mockReturnValueOnce(true); jest.spyOn(pulls, 'getChangedFilesOfPullRequest').mockResolvedValueOnce(singleChangedFiles); jest.spyOn(pulls, 'changedFilesToFile').mockReturnValueOnce('location/changedFiles.txt'); @@ -110,7 +127,7 @@ describe('SetFailed checks', () => { jest.spyOn(fetcher, 'getAnalysisResults').mockResolvedValueOnce(analysisResultsPassedNoUrl); jest.spyOn(review, 'postReview').mockImplementationOnce(() => Promise.resolve()); - const spySetFailed = jest.spyOn(logger, 'exit'); + const spySetFailed = jest.spyOn(logger, 'setFailed'); await main.main(); @@ -249,6 +266,68 @@ describe('PostReview checks', () => { }); }); +describe('Artifact upload tests', () => { + test('Should not call uploadArtifacts when tmpDir or isDebug are not set', async () => { + const spyArtifact = jest.spyOn(artifacts, 'uploadArtifact').mockImplementationOnce(() => Promise.resolve()); + const spySummary = jest.spyOn(api_helper, 'cliSummary'); + (existsSync as any).mockReturnValueOnce(true); + jest.spyOn(pulls, 'getChangedFilesOfPullRequest').mockResolvedValueOnce(singleChangedFiles); + jest.spyOn(pulls, 'changedFilesToFile').mockReturnValueOnce('location/changedFiles.txt'); + jest.spyOn(analyzer, 'runTicsAnalyzer').mockResolvedValueOnce(analysisPassed); + + await main.main(); + + expect(spyArtifact).toHaveBeenCalledTimes(0); + expect(spySummary).toHaveBeenCalledTimes(1); + }); + + test('Should not call uploadArtifacts when tmpDir is set but no analysis has been done', async () => { + const spyArtifact = jest.spyOn(artifacts, 'uploadArtifact').mockImplementationOnce(() => Promise.resolve()); + const spySummary = jest.spyOn(api_helper, 'cliSummary'); + (existsSync as any).mockReturnValueOnce(true); + jest.spyOn(pulls, 'getChangedFilesOfPullRequest').mockResolvedValueOnce([]); + + await main.main(); + + expect(spyArtifact).toHaveBeenCalledTimes(0); + expect(spySummary).toHaveBeenCalledTimes(1); + }); + + test('Should call uploadArtifacts when action is run with debug', async () => { + const spyArtifact = jest.spyOn(artifacts, 'uploadArtifact').mockImplementationOnce(() => Promise.resolve()); + const spySummary = jest.spyOn(api_helper, 'cliSummary'); + (existsSync as any).mockReturnValueOnce(true); + jest.spyOn(pulls, 'getChangedFilesOfPullRequest').mockResolvedValueOnce(singleChangedFiles); + jest.spyOn(pulls, 'changedFilesToFile').mockReturnValueOnce('location/changedFiles.txt'); + jest.spyOn(analyzer, 'runTicsAnalyzer').mockResolvedValueOnce(analysisPassed); + + githubConfig.debugger = true; + + await main.main(); + + expect(spyArtifact).toHaveBeenCalledTimes(1); + expect(spySummary).toHaveBeenCalledTimes(1); + + githubConfig.debugger = false; + }); + + test('Should call uploadArtifacts when tmpDir is set', async () => { + const spyArtifact = jest.spyOn(artifacts, 'uploadArtifact').mockImplementationOnce(() => Promise.resolve()); + const spySummary = jest.spyOn(api_helper, 'cliSummary'); + (existsSync as any).mockReturnValueOnce(true); + jest.spyOn(pulls, 'getChangedFilesOfPullRequest').mockResolvedValueOnce(singleChangedFiles); + jest.spyOn(pulls, 'changedFilesToFile').mockReturnValueOnce('location/changedFiles.txt'); + jest.spyOn(analyzer, 'runTicsAnalyzer').mockResolvedValueOnce(analysisPassed); + + ticsConfig.tmpDir = '/tmp'; + + await main.main(); + + expect(spyArtifact).toHaveBeenCalledTimes(1); + expect(spySummary).toHaveBeenCalledTimes(1); + }); +}); + describe('DeletePreviousReviewComments check', () => { test('Should call deletePreviousReviewComments when postAnnotations is true with one annotation and one previously posted review comment', async () => { (existsSync as any).mockReturnValueOnce(true); diff --git a/test/unit/tics/fetcher.test.ts b/test/unit/tics/fetcher.test.ts index 248f79f0..25cc2e13 100644 --- a/test/unit/tics/fetcher.test.ts +++ b/test/unit/tics/fetcher.test.ts @@ -39,11 +39,14 @@ describe('getAnalyzedFiles', () => { jest.spyOn(api_helper, 'getProjectName').mockReturnValueOnce('projectName'); jest.spyOn(httpClient, 'get').mockImplementationOnce((): Promise => Promise.reject(new Error())); - const spy = jest.spyOn(logger, 'exit'); - - await fetcher.getAnalyzedFiles('url'); - - expect(spy).toHaveBeenCalledTimes(1); + let error: any; + try { + await fetcher.getAnalyzedFiles('url'); + } catch (err) { + error = err; + } + + expect(error).toBeInstanceOf(Error); }); }); @@ -65,11 +68,14 @@ describe('getQualityGate', () => { jest.spyOn(api_helper, 'getProjectName').mockReturnValueOnce('projectName'); jest.spyOn(httpClient, 'get').mockImplementationOnce((): Promise => Promise.reject(new Error())); - const spy = jest.spyOn(logger, 'exit'); - - await fetcher.getQualityGate('url'); + let error: any; + try { + await fetcher.getQualityGate('url'); + } catch (err) { + error = err; + } - expect(spy).toHaveBeenCalledTimes(1); + expect(error).toBeInstanceOf(Error); }); }); @@ -94,11 +100,14 @@ describe('getAnnotations', () => { test('Should throw error on faulty get in getAnnotations', async () => { jest.spyOn(httpClient, 'get').mockImplementationOnce((): Promise => Promise.reject(new Error())); - const spy = jest.spyOn(logger, 'exit'); + let error: any; + try { + await fetcher.getAnnotations([{ url: 'url' }]); + } catch (err) { + error = err; + } - await fetcher.getAnnotations([{ url: 'url' }]); - - expect(spy).toHaveBeenCalledTimes(1); + expect(error).toBeInstanceOf(Error); }); }); @@ -114,11 +123,14 @@ describe('getViewerVersion', () => { test('Should throw error on faulty get in getViewerVersion', async () => { jest.spyOn(httpClient, 'get').mockImplementationOnce((): Promise => Promise.reject(new Error())); - const spy = jest.spyOn(logger, 'exit'); - - await fetcher.getViewerVersion(); + let error: any; + try { + await fetcher.getViewerVersion(); + } catch (err) { + error = err; + } - expect(spy).toHaveBeenCalledTimes(1); + expect(error).toBeInstanceOf(Error); }); }); From 0f315fc2f11ad3d69a4196028ba554803550034f Mon Sep 17 00:00:00 2001 From: janssen <118828444+janssen-tiobe@users.noreply.github.com> Date: Mon, 30 Oct 2023 12:03:39 +0100 Subject: [PATCH 2/8] #33077: Fixed coding standard issues --- dist/index.js | 42 +++++++++++++++++++++++++++++++++++------- package-lock.json | 7 +++++++ package.json | 1 + src/configuration.ts | 5 +++-- src/helper/error.ts | 2 +- src/helper/logger.ts | 2 +- src/tics/fetcher.ts | 12 ++++++++---- 7 files changed, 56 insertions(+), 15 deletions(-) diff --git a/dist/index.js b/dist/index.js index 0dd411c6..a9fcfdea 100644 --- a/dist/index.js +++ b/dist/index.js @@ -18,6 +18,7 @@ const http_client_1 = __importDefault(__nccwpck_require__(4824)); const proxy_agent_1 = __nccwpck_require__(8391); const os_1 = __nccwpck_require__(2037); const install_tics_1 = __nccwpck_require__(8569); +const crypto_1 = __nccwpck_require__(6113); exports.githubConfig = { baseUrl: process.env.GITHUB_API_URL ? process.env.GITHUB_API_URL : 'https://api.github.com', repo: process.env.GITHUB_REPOSITORY ? process.env.GITHUB_REPOSITORY : '', @@ -28,7 +29,7 @@ exports.githubConfig = { branchdir: process.env.GITHUB_WORKSPACE ? process.env.GITHUB_WORKSPACE : '', commitSha: process.env.GITHUB_SHA ? process.env.GITHUB_SHA : '', eventName: github_1.context.eventName, - id: `${github_1.context.runId.toString()}-${process.env.GITHUB_RUN_ATTEMPT}`, + id: `${github_1.context.runId.toString()}-${process.env.GITHUB_RUN_ATTEMPT || (0, crypto_1.randomBytes)(4).toString('hex')}`, pullRequestNumber: getPullRequestNumber(), debugger: (0, core_1.isDebug)() }; @@ -105,7 +106,7 @@ const octokitOptions = { } }; exports.octokit = (0, github_1.getOctokit)(exports.ticsConfig.githubToken, octokitOptions, plugin_retry_1.retry); -exports.baseUrl = (0, install_tics_1.getBaseUrl)(exports.ticsConfig.ticsConfiguration); +exports.baseUrl = (0, install_tics_1.getBaseUrl)(exports.ticsConfig.ticsConfiguration).href; exports.viewerUrl = exports.ticsConfig.viewerUrl ? exports.ticsConfig.viewerUrl.replace(/\/+$/, '') : exports.baseUrl; @@ -1227,16 +1228,40 @@ exports.getProjectName = getProjectName; /***/ }), /***/ 1559: -/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { "use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : 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; +}; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.getViewerVersion = exports.getAnnotations = exports.getQualityGate = exports.getAnalyzedFiles = exports.getAnalysisResults = void 0; const configuration_1 = __nccwpck_require__(5527); const logger_1 = __nccwpck_require__(6440); const summary_1 = __nccwpck_require__(1502); const api_helper_1 = __nccwpck_require__(3823); +const fetcher = __importStar(__nccwpck_require__(1559)); /** * Retrieve all analysis results from the viewer in one convenient object. * @param explorerUrls All the explorer urls gotten from the TICS analysis. @@ -1259,9 +1284,11 @@ async function getAnalysisResults(explorerUrls, changedFiles) { let analysisResult = { project: (0, api_helper_1.getProjectName)(url), explorerUrl: url, - analyzedFiles: await exports.getAnalyzedFiles(url) // export is used for testing + // import of itself is used for mocking the function in the same file + analyzedFiles: await fetcher.getAnalyzedFiles(url) }; - const qualityGate = await exports.getQualityGate(url); // export is used for testing + // import of itself is used for mocking the function in the same file + const qualityGate = await fetcher.getQualityGate(url); if (!qualityGate) { analysisResults.passed = false; analysisResults.missesQualityGate = true; @@ -1271,7 +1298,8 @@ async function getAnalysisResults(explorerUrls, changedFiles) { analysisResults.message += qualityGate.message + '; '; } if (qualityGate && configuration_1.ticsConfig.postAnnotations) { - const annotations = await exports.getAnnotations(qualityGate.annotationsApiV1Links); // export is used for testing + // import of itself is used for mocking the function in the same file + const annotations = await fetcher.getAnnotations(qualityGate.annotationsApiV1Links); if (annotations && annotations.length > 0) { analysisResult.reviewComments = (0, summary_1.createReviewComments)(annotations, changedFiles); } @@ -1379,7 +1407,7 @@ async function getAnnotations(apiLinks) { await Promise.all(apiLinks.map(async (link, index) => { const annotationsUrl = new URL(`${configuration_1.baseUrl}/${link.url}`); annotationsUrl.searchParams.append('fields', 'default,ruleHelp,synopsis,annotationName'); - logger_1.logger.debug(`From: ${annotationsUrl}`); + logger_1.logger.debug(`From: ${annotationsUrl.href}`); const response = await configuration_1.httpClient.get(annotationsUrl.href); if (response) { response.data.forEach((annotation) => { diff --git a/package-lock.json b/package-lock.json index 6756acfa..ca2b46b2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "@tiobe/install-tics": "^0.4.0", "canonical-path": "^1.0.0", "compare-versions": "^6.1.0", + "crypto": "^1.0.1", "proxy-agent": "^6.3.1", "semver": "^7.5.4", "underscore": "^1.13.6" @@ -2649,6 +2650,12 @@ "node": ">= 8" } }, + "node_modules/crypto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", + "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==", + "deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in." + }, "node_modules/data-uri-to-buffer": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.1.tgz", diff --git a/package.json b/package.json index 2714e9d1..db40538c 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "@tiobe/install-tics": "^0.4.0", "canonical-path": "^1.0.0", "compare-versions": "^6.1.0", + "crypto": "^1.0.1", "proxy-agent": "^6.3.1", "semver": "^7.5.4", "underscore": "^1.13.6" diff --git a/src/configuration.ts b/src/configuration.ts index 14f6cf03..d75b3438 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -6,6 +6,7 @@ import HttpClient from '@tiobe/http-client'; import { ProxyAgent } from 'proxy-agent'; import { EOL } from 'os'; import { getBaseUrl } from '@tiobe/install-tics'; +import { randomBytes } from 'crypto'; export const githubConfig = { baseUrl: process.env.GITHUB_API_URL ? process.env.GITHUB_API_URL : 'https://api.github.com', @@ -17,7 +18,7 @@ export const githubConfig = { branchdir: process.env.GITHUB_WORKSPACE ? process.env.GITHUB_WORKSPACE : '', commitSha: process.env.GITHUB_SHA ? process.env.GITHUB_SHA : '', eventName: context.eventName, - id: `${context.runId.toString()}-${process.env.GITHUB_RUN_ATTEMPT}`, + id: `${context.runId.toString()}-${process.env.GITHUB_RUN_ATTEMPT || randomBytes(4).toString('hex')}`, pullRequestNumber: getPullRequestNumber(), debugger: isDebug() }; @@ -105,5 +106,5 @@ const octokitOptions: OctokitOptions = { }; export const octokit = getOctokit(ticsConfig.githubToken, octokitOptions, retry); -export const baseUrl = getBaseUrl(ticsConfig.ticsConfiguration); +export const baseUrl = getBaseUrl(ticsConfig.ticsConfiguration).href; export const viewerUrl = ticsConfig.viewerUrl ? ticsConfig.viewerUrl.replace(/\/+$/, '') : baseUrl; diff --git a/src/helper/error.ts b/src/helper/error.ts index 84428088..0dc0c1af 100644 --- a/src/helper/error.ts +++ b/src/helper/error.ts @@ -4,7 +4,7 @@ export function handleOctokitError(error: unknown): string { let message = 'reason unkown'; if (error instanceof Error) { message = ''; - const retryCount = (error as RequestError).request?.request?.retryCount; + const retryCount = (error as RequestError).request?.request?.retryCount; if (retryCount) { message = `Retried ${retryCount} time(s), but got: `; } diff --git a/src/helper/logger.ts b/src/helper/logger.ts index b69dee6a..43026654 100644 --- a/src/helper/logger.ts +++ b/src/helper/logger.ts @@ -88,7 +88,7 @@ class Logger { */ maskSecrets(data: string): string { // Find secrets value and add them to this.matched - ticsConfig.secretsFilter.forEach((secret: string | RegExp) => { + ticsConfig.secretsFilter.forEach((secret: string) => { if (data.match(new RegExp(secret, 'gi'))) { const regex = new RegExp(`\\w*${secret}\\w*(?:[ \\t]*[:=>]*[ \\t]*)(.*)`, 'gi'); let match: RegExpExecArray | null = null; diff --git a/src/tics/fetcher.ts b/src/tics/fetcher.ts index c1f3ff39..687ccb18 100644 --- a/src/tics/fetcher.ts +++ b/src/tics/fetcher.ts @@ -15,6 +15,7 @@ import { import { logger } from '../helper/logger'; import { createReviewComments } from '../helper/summary'; import { getItemFromUrl, getProjectName } from './api_helper'; +import * as fetcher from './fetcher'; /** * Retrieve all analysis results from the viewer in one convenient object. @@ -40,10 +41,12 @@ export async function getAnalysisResults(explorerUrls: string[], changedFiles: C let analysisResult: ProjectResult = { project: getProjectName(url), explorerUrl: url, - analyzedFiles: await exports.getAnalyzedFiles(url) // export is used for testing + // import of itself is used for mocking the function in the same file + analyzedFiles: await fetcher.getAnalyzedFiles(url) }; - const qualityGate = await exports.getQualityGate(url); // export is used for testing + // import of itself is used for mocking the function in the same file + const qualityGate = await fetcher.getQualityGate(url); if (!qualityGate) { analysisResults.passed = false; @@ -56,7 +59,8 @@ export async function getAnalysisResults(explorerUrls: string[], changedFiles: C } if (qualityGate && ticsConfig.postAnnotations) { - const annotations = await exports.getAnnotations(qualityGate.annotationsApiV1Links); // export is used for testing + // import of itself is used for mocking the function in the same file + const annotations = await fetcher.getAnnotations(qualityGate.annotationsApiV1Links); if (annotations && annotations.length > 0) { analysisResult.reviewComments = createReviewComments(annotations, changedFiles); } @@ -179,7 +183,7 @@ export async function getAnnotations(apiLinks: AnnotationApiLink[]): Promise { const annotationsUrl = new URL(`${baseUrl}/${link.url}`); annotationsUrl.searchParams.append('fields', 'default,ruleHelp,synopsis,annotationName'); - logger.debug(`From: ${annotationsUrl}`); + logger.debug(`From: ${annotationsUrl.href}`); const response = await httpClient.get(annotationsUrl.href); if (response) { response.data.forEach((annotation: Annotation) => { From d4c46bef63d75d69bd79b4dd32ea9e68897f0058 Mon Sep 17 00:00:00 2001 From: janssen <118828444+janssen-tiobe@users.noreply.github.com> Date: Tue, 31 Oct 2023 11:57:04 +0100 Subject: [PATCH 3/8] #33077: Improved some logging, added logging for test --- dist/index.js | 8 ++++++-- src/github/annotations.ts | 3 ++- src/github/comments.ts | 3 ++- src/main.ts | 1 + src/tics/api_helper.ts | 1 + 5 files changed, 12 insertions(+), 4 deletions(-) diff --git a/dist/index.js b/dist/index.js index a9fcfdea..caf4c808 100644 --- a/dist/index.js +++ b/dist/index.js @@ -129,7 +129,7 @@ const error_1 = __nccwpck_require__(5922); async function getPostedReviewComments() { let response = []; try { - logger_1.logger.info('Retrieving posted review comments.'); + logger_1.logger.header('Retrieving posted review comments.'); const params = { owner: configuration_1.githubConfig.owner, repo: configuration_1.githubConfig.reponame, @@ -141,6 +141,7 @@ async function getPostedReviewComments() { const message = (0, error_1.handleOctokitError)(error); logger_1.logger.error(`Could not retrieve the review comments: ${message}`); } + logger_1.logger.info('Retrieve posted review comments.'); return response; } exports.getPostedReviewComments = getPostedReviewComments; @@ -280,7 +281,7 @@ const error_1 = __nccwpck_require__(5922); async function getPostedComments() { let response = []; try { - logger_1.logger.info('Retrieving posted review comments.'); + logger_1.logger.header('Retrieving posted comments.'); const params = { owner: configuration_1.githubConfig.owner, repo: configuration_1.githubConfig.reponame, @@ -292,6 +293,7 @@ async function getPostedComments() { const message = (0, error_1.handleOctokitError)(error); logger_1.logger.error(`Could not retrieve the comments: ${message}`); } + logger_1.logger.info('Retrieved posted comments.'); return response; } exports.getPostedComments = getPostedComments; @@ -1191,6 +1193,7 @@ const configuration_1 = __nccwpck_require__(5527); * @param analysis the output of the TICS analysis run. */ function cliSummary(analysis) { + logger_1.logger.info(''); //empty line for better readability analysis?.errorList.forEach(error => logger_1.logger.error(error)); if (configuration_1.githubConfig.debugger) { analysis?.warningList.forEach(warning => logger_1.logger.warning(warning)); @@ -84446,6 +84449,7 @@ async function main() { } } } + logger_1.logger.info(`tmpdir: ${configuration_1.ticsConfig.tmpDir}, debugger: ${configuration_1.githubConfig.debugger}, analysis: ${analysis}`); if ((configuration_1.ticsConfig.tmpDir || configuration_1.githubConfig.debugger) && analysis) { await (0, artifacts_1.uploadArtifact)(); } diff --git a/src/github/annotations.ts b/src/github/annotations.ts index bb4ffce2..cb546038 100644 --- a/src/github/annotations.ts +++ b/src/github/annotations.ts @@ -11,7 +11,7 @@ import { handleOctokitError } from '../helper/error'; export async function getPostedReviewComments(): Promise { let response: ReviewComment[] = []; try { - logger.info('Retrieving posted review comments.'); + logger.header('Retrieving posted review comments.'); const params = { owner: githubConfig.owner, repo: githubConfig.reponame, @@ -22,6 +22,7 @@ export async function getPostedReviewComments(): Promise { const message = handleOctokitError(error); logger.error(`Could not retrieve the review comments: ${message}`); } + logger.info('Retrieve posted review comments.'); return response; } diff --git a/src/github/comments.ts b/src/github/comments.ts index 87da8254..c91c948e 100644 --- a/src/github/comments.ts +++ b/src/github/comments.ts @@ -14,7 +14,7 @@ import { handleOctokitError } from '../helper/error'; export async function getPostedComments(): Promise { let response: Comment[] = []; try { - logger.info('Retrieving posted review comments.'); + logger.header('Retrieving posted comments.'); const params = { owner: githubConfig.owner, repo: githubConfig.reponame, @@ -25,6 +25,7 @@ export async function getPostedComments(): Promise { const message = handleOctokitError(error); logger.error(`Could not retrieve the comments: ${message}`); } + logger.info('Retrieved posted comments.'); return response; } diff --git a/src/main.ts b/src/main.ts index 902334bf..5decc99e 100644 --- a/src/main.ts +++ b/src/main.ts @@ -51,6 +51,7 @@ export async function main(): Promise { } } + logger.info(`tmpdir: ${ticsConfig.tmpDir}, debugger: ${githubConfig.debugger}, analysis: ${analysis}`); if ((ticsConfig.tmpDir || githubConfig.debugger) && analysis) { await uploadArtifact(); } diff --git a/src/tics/api_helper.ts b/src/tics/api_helper.ts index 9a89e9cc..155790e5 100644 --- a/src/tics/api_helper.ts +++ b/src/tics/api_helper.ts @@ -7,6 +7,7 @@ import { Analysis } from '../helper/interfaces'; * @param analysis the output of the TICS analysis run. */ export function cliSummary(analysis?: Analysis): void { + logger.info(''); //empty line for better readability analysis?.errorList.forEach(error => logger.error(error)); if (githubConfig.debugger) { analysis?.warningList.forEach(warning => logger.warning(warning)); From 947596aeffee36e58969fcd2eb8aeb18c38afd94 Mon Sep 17 00:00:00 2001 From: janssen <118828444+janssen-tiobe@users.noreply.github.com> Date: Tue, 31 Oct 2023 12:02:43 +0100 Subject: [PATCH 4/8] #33077: Added some logging for testing purposes --- dist/index.js | 3 ++- src/main.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/dist/index.js b/dist/index.js index caf4c808..fc613dce 100644 --- a/dist/index.js +++ b/dist/index.js @@ -84436,6 +84436,7 @@ async function main() { const changedFiles = await getChangedFiles(); if (changedFiles) { analysis = await analyze(changedFiles); + logger_1.logger.info(`analysis: ${analysis}`); if (analysis) { try { await processAnalysis(analysis, changedFiles); @@ -84449,7 +84450,7 @@ async function main() { } } } - logger_1.logger.info(`tmpdir: ${configuration_1.ticsConfig.tmpDir}, debugger: ${configuration_1.githubConfig.debugger}, analysis: ${analysis}`); + logger_1.logger.info(`analysis: ${analysis}`); if ((configuration_1.ticsConfig.tmpDir || configuration_1.githubConfig.debugger) && analysis) { await (0, artifacts_1.uploadArtifact)(); } diff --git a/src/main.ts b/src/main.ts index 5decc99e..4a0f05bb 100644 --- a/src/main.ts +++ b/src/main.ts @@ -38,6 +38,7 @@ export async function main(): Promise { if (changedFiles) { analysis = await analyze(changedFiles); + logger.info(`analysis: ${analysis}`); if (analysis) { try { @@ -51,7 +52,7 @@ export async function main(): Promise { } } - logger.info(`tmpdir: ${ticsConfig.tmpDir}, debugger: ${githubConfig.debugger}, analysis: ${analysis}`); + logger.info(`analysis: ${analysis}`); if ((ticsConfig.tmpDir || githubConfig.debugger) && analysis) { await uploadArtifact(); } From 24ab2fc2578c5e482770079c078225ccc3ca718c Mon Sep 17 00:00:00 2001 From: janssen <118828444+janssen-tiobe@users.noreply.github.com> Date: Tue, 31 Oct 2023 12:08:35 +0100 Subject: [PATCH 5/8] #33077: Removed check on analysis as it broke functionality --- dist/index.js | 4 +--- src/main.ts | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/dist/index.js b/dist/index.js index fc613dce..dbd46adb 100644 --- a/dist/index.js +++ b/dist/index.js @@ -84436,7 +84436,6 @@ async function main() { const changedFiles = await getChangedFiles(); if (changedFiles) { analysis = await analyze(changedFiles); - logger_1.logger.info(`analysis: ${analysis}`); if (analysis) { try { await processAnalysis(analysis, changedFiles); @@ -84450,8 +84449,7 @@ async function main() { } } } - logger_1.logger.info(`analysis: ${analysis}`); - if ((configuration_1.ticsConfig.tmpDir || configuration_1.githubConfig.debugger) && analysis) { + if (configuration_1.ticsConfig.tmpDir || configuration_1.githubConfig.debugger) { await (0, artifacts_1.uploadArtifact)(); } // Write the summary made to the action summary. diff --git a/src/main.ts b/src/main.ts index 4a0f05bb..7e7c06bf 100644 --- a/src/main.ts +++ b/src/main.ts @@ -38,7 +38,6 @@ export async function main(): Promise { if (changedFiles) { analysis = await analyze(changedFiles); - logger.info(`analysis: ${analysis}`); if (analysis) { try { @@ -52,8 +51,7 @@ export async function main(): Promise { } } - logger.info(`analysis: ${analysis}`); - if ((ticsConfig.tmpDir || githubConfig.debugger) && analysis) { + if (ticsConfig.tmpDir || githubConfig.debugger) { await uploadArtifact(); } From e0f3198743079ac55420bb5223be9cf6cb2f0e9b Mon Sep 17 00:00:00 2001 From: janssen <118828444+janssen-tiobe@users.noreply.github.com> Date: Tue, 31 Oct 2023 12:24:41 +0100 Subject: [PATCH 6/8] #33077: Improved processAnalysis, cliSummary now posted once --- dist/index.js | 22 ++++++++++++---------- src/github/artifacts.ts | 2 +- src/main.ts | 15 ++++++++------- src/tics/api_helper.ts | 10 ++++++---- test/unit/main.test.ts | 2 +- 5 files changed, 28 insertions(+), 23 deletions(-) diff --git a/dist/index.js b/dist/index.js index dbd46adb..4c987371 100644 --- a/dist/index.js +++ b/dist/index.js @@ -214,7 +214,7 @@ async function uploadArtifact() { try { logger_1.logger.header('Uploading artifact'); const tmpDir = getTmpDir() + '/ticstmpdir'; - logger_1.logger.info(`Logs written to ${tmpDir}`); + logger_1.logger.info(`Logs gotten from ${tmpDir}`); const response = await artifactClient.uploadArtifact('ticstmpdir', getFilesInFolder(tmpDir), tmpDir); if (response.failedItems.length > 0) { logger_1.logger.debug(`Failed to upload file(s): ${response.failedItems.join(', ')}`); @@ -1193,10 +1193,12 @@ const configuration_1 = __nccwpck_require__(5527); * @param analysis the output of the TICS analysis run. */ function cliSummary(analysis) { - logger_1.logger.info(''); //empty line for better readability - analysis?.errorList.forEach(error => logger_1.logger.error(error)); + if (analysis.errorList.length > 0 || (configuration_1.githubConfig.debugger && analysis.warningList.length > 0)) { + logger_1.logger.info(''); //empty line for better readability + } + analysis.errorList.forEach(error => logger_1.logger.error(error)); if (configuration_1.githubConfig.debugger) { - analysis?.warningList.forEach(warning => logger_1.logger.warning(warning)); + analysis.warningList.forEach(warning => logger_1.logger.warning(warning)); } } exports.cliSummary = cliSummary; @@ -84436,7 +84438,7 @@ async function main() { const changedFiles = await getChangedFiles(); if (changedFiles) { analysis = await analyze(changedFiles); - if (analysis) { + if (analysis.explorerUrls.length > 0) { try { await processAnalysis(analysis, changedFiles); } @@ -84449,12 +84451,14 @@ async function main() { } } } - if (configuration_1.ticsConfig.tmpDir || configuration_1.githubConfig.debugger) { - await (0, artifacts_1.uploadArtifact)(); + if (analysis) { + if (configuration_1.ticsConfig.tmpDir || configuration_1.githubConfig.debugger) { + await (0, artifacts_1.uploadArtifact)(); + } + (0, api_helper_1.cliSummary)(analysis); } // Write the summary made to the action summary. await core_1.summary.write({ overwrite: true }); - (0, api_helper_1.cliSummary)(analysis); } exports.main = main; async function getChangedFiles() { @@ -84497,8 +84501,6 @@ async function analyze(changedFiles) { analysis.errorList.push('Explorer URL not returned from TICS analysis.'); await (0, comments_1.postErrorComment)(analysis); } - (0, api_helper_1.cliSummary)(analysis); - return; } return analysis; } diff --git a/src/github/artifacts.ts b/src/github/artifacts.ts index ce49cfa7..37420e50 100644 --- a/src/github/artifacts.ts +++ b/src/github/artifacts.ts @@ -12,7 +12,7 @@ export async function uploadArtifact(): Promise { try { logger.header('Uploading artifact'); const tmpDir = getTmpDir() + '/ticstmpdir'; - logger.info(`Logs written to ${tmpDir}`); + logger.info(`Logs gotten from ${tmpDir}`); const response = await artifactClient.uploadArtifact('ticstmpdir', getFilesInFolder(tmpDir), tmpDir); if (response.failedItems.length > 0) { diff --git a/src/main.ts b/src/main.ts index 7e7c06bf..0f35262c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -39,7 +39,7 @@ export async function main(): Promise { if (changedFiles) { analysis = await analyze(changedFiles); - if (analysis) { + if (analysis.explorerUrls.length > 0) { try { await processAnalysis(analysis, changedFiles); } catch (error: unknown) { @@ -51,13 +51,16 @@ export async function main(): Promise { } } - if (ticsConfig.tmpDir || githubConfig.debugger) { - await uploadArtifact(); + if (analysis) { + if (ticsConfig.tmpDir || githubConfig.debugger) { + await uploadArtifact(); + } + + cliSummary(analysis); } // Write the summary made to the action summary. await summary.write({ overwrite: true }); - cliSummary(analysis); } async function getChangedFiles(): Promise { @@ -86,7 +89,7 @@ async function getChangedFiles(): Promise { }; } -async function analyze(changedFiles: ChangedFiles): Promise { +async function analyze(changedFiles: ChangedFiles): Promise { const analysis = await runTicsAnalyzer(changedFiles.path); if (analysis.explorerUrls.length === 0) { @@ -101,8 +104,6 @@ async function analyze(changedFiles: ChangedFiles): Promise logger.error(error)); +export function cliSummary(analysis: Analysis): void { + if (analysis.errorList.length > 0 || (githubConfig.debugger && analysis.warningList.length > 0)) { + logger.info(''); //empty line for better readability + } + analysis.errorList.forEach(error => logger.error(error)); if (githubConfig.debugger) { - analysis?.warningList.forEach(warning => logger.warning(warning)); + analysis.warningList.forEach(warning => logger.warning(warning)); } } diff --git a/test/unit/main.test.ts b/test/unit/main.test.ts index 6696fc26..cf9ecb0b 100644 --- a/test/unit/main.test.ts +++ b/test/unit/main.test.ts @@ -290,7 +290,7 @@ describe('Artifact upload tests', () => { await main.main(); expect(spyArtifact).toHaveBeenCalledTimes(0); - expect(spySummary).toHaveBeenCalledTimes(1); + expect(spySummary).toHaveBeenCalledTimes(0); }); test('Should call uploadArtifacts when action is run with debug', async () => { From 07c175044b6a14985f38d65a6d358865efd36ac5 Mon Sep 17 00:00:00 2001 From: janssen <118828444+janssen-tiobe@users.noreply.github.com> Date: Tue, 31 Oct 2023 13:20:36 +0100 Subject: [PATCH 7/8] #33077: Setfailed is now called one in code --- dist/index.js | 492 +++++++++++++++++++++-------------------- src/helper/logger.ts | 2 +- src/main.ts | 31 ++- test/unit/main.test.ts | 59 +++-- 4 files changed, 315 insertions(+), 269 deletions(-) diff --git a/dist/index.js b/dist/index.js index 4c987371..1ae69860 100644 --- a/dist/index.js +++ b/dist/index.js @@ -721,7 +721,7 @@ class Logger { } } /** - * Masks the secrets defined in ticsConfig secretsFilter from the console logging. + * Masks the secrets defined in ticsConfig secretsFilter from the * @param data string that is going to be logged to the console. * @returns the message with the secrets masked. */ @@ -1042,6 +1042,250 @@ function createUnpostableAnnotationsDetails(unpostableReviewComments) { exports.createUnpostableAnnotationsDetails = createUnpostableAnnotationsDetails; +/***/ }), + +/***/ 3109: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.configure = exports.main = exports.failedMessage = void 0; +const fs_1 = __nccwpck_require__(7147); +const comments_1 = __nccwpck_require__(6572); +const configuration_1 = __nccwpck_require__(5527); +const pulls_1 = __nccwpck_require__(8808); +const logger_1 = __nccwpck_require__(6440); +const analyzer_1 = __nccwpck_require__(9099); +const api_helper_1 = __nccwpck_require__(3823); +const fetcher_1 = __nccwpck_require__(1559); +const review_1 = __nccwpck_require__(2691); +const summary_1 = __nccwpck_require__(1502); +const annotations_1 = __nccwpck_require__(1058); +const enums_1 = __nccwpck_require__(1655); +const compare_versions_1 = __nccwpck_require__(4773); +const core_1 = __nccwpck_require__(2186); +const artifacts_1 = __nccwpck_require__(5734); +const commits_1 = __nccwpck_require__(5759); +// export for testing purposes +exports.failedMessage = undefined; +main().catch((error) => { + let message = 'TICS failed with unknown reason'; + if (error instanceof Error) + message = error.message; + logger_1.logger.setFailed(message); + process.exit(1); +}); +// exported for testing purposes +async function main() { + configure(); + await meetsPrerequisites(); + let analysis; + if (configuration_1.ticsConfig.mode === 'diagnostic') { + analysis = await diagnosticAnalysis(); + } + else { + const changedFiles = await getChangedFiles(); + if (changedFiles) { + analysis = await analyze(changedFiles); + if (analysis.explorerUrls.length > 0) { + try { + await processAnalysis(analysis, changedFiles); + } + catch (error) { + let message = 'Something went wrond: reason unknown'; + if (error instanceof Error) + message = error.message; + exports.failedMessage = message; + } + } + } + } + if (analysis && (configuration_1.ticsConfig.tmpDir || configuration_1.githubConfig.debugger)) { + await (0, artifacts_1.uploadArtifact)(); + } + if (exports.failedMessage) { + logger_1.logger.setFailed(exports.failedMessage); + } + if (analysis) { + (0, api_helper_1.cliSummary)(analysis); + } + // Write the summary made to the action summary. + await core_1.summary.write({ overwrite: true }); +} +exports.main = main; +async function getChangedFiles() { + let changedFilesFilePath = undefined; + let changedFiles = undefined; + if (configuration_1.githubConfig.eventName === 'pull_request') { + changedFiles = await (0, pulls_1.getChangedFilesOfPullRequest)(); + } + else { + changedFiles = await (0, commits_1.getChangedFilesOfCommit)(); + } + if (configuration_1.ticsConfig.filelist) { + changedFilesFilePath = configuration_1.ticsConfig.filelist; + } + else { + if (changedFiles.length <= 0) { + logger_1.logger.info('No changed files found to analyze.'); + return; + } + changedFilesFilePath = (0, pulls_1.changedFilesToFile)(changedFiles); + } + return { + files: changedFiles, + path: changedFilesFilePath + }; +} +async function analyze(changedFiles) { + const analysis = await (0, analyzer_1.runTicsAnalyzer)(changedFiles.path); + if (analysis.explorerUrls.length === 0) { + (0, comments_1.deletePreviousComments)(await (0, comments_1.getPostedComments)()); + if (!analysis.completed) { + exports.failedMessage = 'Failed to run TICS Github Action.'; + await (0, comments_1.postErrorComment)(analysis); + } + else if (analysis.warningList.find(w => w.includes('[WARNING 5057]'))) { + await postToConversation(false, 'No changed files applicable for TICS analysis quality gating.'); + } + else { + exports.failedMessage = 'Failed to run TICS Github Action.'; + analysis.errorList.push('Explorer URL not returned from TICS analysis.'); + await (0, comments_1.postErrorComment)(analysis); + } + } + return analysis; +} +async function processAnalysis(analysis, changedFiles) { + const analysisResults = await (0, fetcher_1.getAnalysisResults)(analysis.explorerUrls, changedFiles.files); + if (analysisResults.missesQualityGate) { + throw Error('Some quality gates could not be retrieved.'); + } + // If not run on a pull request no review comments have to be deleted + if (configuration_1.githubConfig.eventName === 'pull_request') { + const previousReviewComments = await (0, annotations_1.getPostedReviewComments)(); + if (previousReviewComments && previousReviewComments.length > 0) { + (0, annotations_1.deletePreviousReviewComments)(previousReviewComments); + } + } + if (configuration_1.ticsConfig.postAnnotations) { + (0, annotations_1.postAnnotations)(analysisResults); + } + let reviewBody = (0, summary_1.createSummaryBody)(analysisResults); + // If not run on a pull request no comments have to be deleted + // and there is no conversation to post to. + if (configuration_1.githubConfig.eventName === 'pull_request') { + (0, comments_1.deletePreviousComments)(await (0, comments_1.getPostedComments)()); + await postToConversation(true, reviewBody, analysisResults.passed ? enums_1.Events.APPROVE : enums_1.Events.REQUEST_CHANGES); + } + if (!analysisResults.passed) { + exports.failedMessage = analysisResults.message; + } +} +/** + * Function for running the action in diagnostic mode. + * @returns Analysis result from a diagnostic run. + */ +async function diagnosticAnalysis() { + logger_1.logger.header('Running action in diagnostic mode'); + let analysis = await (0, analyzer_1.runTicsAnalyzer)(''); + if (analysis.statusCode !== 0) { + exports.failedMessage = 'Diagnostic run has failed.'; + } + return analysis; +} +/** + * Function to combine the posting to conversation in a single location. + * @param isGate if posting is done on a quality gate result. + * @param body body of the summary to post. + * @param event in case of posting a review an event should be given. + */ +async function postToConversation(isGate, body, event = enums_1.Events.COMMENT) { + if (configuration_1.ticsConfig.postToConversation) { + if (isGate) { + if (configuration_1.ticsConfig.pullRequestApproval) { + await (0, review_1.postReview)(body, event); + } + else { + await (0, comments_1.postComment)(body); + } + } + else { + if (configuration_1.ticsConfig.pullRequestApproval) { + await (0, review_1.postNothingAnalyzedReview)(body); + } + else { + await (0, comments_1.postNothingAnalyzedComment)(body); + } + } + } +} +/** + * Configure the action before running the analysis. + */ +function configure() { + process.removeAllListeners('warning'); + process.on('warning', warning => { + if (configuration_1.githubConfig.debugger) + logger_1.logger.debug(warning.message.toString()); + }); + (0, core_1.exportVariable)('TICSIDE', 'GITHUB'); + // set ticsAuthToken + if (configuration_1.ticsConfig.ticsAuthToken) { + (0, core_1.exportVariable)('TICSAUTHTOKEN', configuration_1.ticsConfig.ticsAuthToken); + } + // set hostnameVerification + if (configuration_1.ticsConfig.hostnameVerification) { + (0, core_1.exportVariable)('TICSHOSTNAMEVERIFICATION', configuration_1.ticsConfig.hostnameVerification); + if (configuration_1.ticsConfig.hostnameVerification === '0' || configuration_1.ticsConfig.hostnameVerification === 'false') { + (0, core_1.exportVariable)('NODE_TLS_REJECT_UNAUTHORIZED', 0); + logger_1.logger.debug('Hostname Verification disabled'); + } + } + // set trustStrategy + if (configuration_1.ticsConfig.trustStrategy) { + (0, core_1.exportVariable)('TICSTRUSTSTRATEGY', configuration_1.ticsConfig.trustStrategy); + if (configuration_1.ticsConfig.trustStrategy === 'self-signed' || configuration_1.ticsConfig.trustStrategy === 'all') { + (0, core_1.exportVariable)('NODE_TLS_REJECT_UNAUTHORIZED', 0); + logger_1.logger.debug(`Trust strategy set to ${configuration_1.ticsConfig.trustStrategy}`); + } + } +} +exports.configure = configure; +/** + * Checks if prerequisites are met to run the Github Plugin. + * If any of these checks fail it returns a message. + */ +async function meetsPrerequisites() { + const viewerVersion = await (0, fetcher_1.getViewerVersion)(); + if (!viewerVersion || !(0, compare_versions_1.satisfies)(viewerVersion.version, '>=2022.4.0')) { + const version = viewerVersion ? viewerVersion.version : 'unknown'; + throw Error(`Minimum required TICS Viewer version is 2022.4. Found version ${version}.`); + } + else if (configuration_1.ticsConfig.mode === 'diagnostic') { + // No need for checked out repository. + } + else if (configuration_1.githubConfig.eventName !== 'pull_request' && !configuration_1.ticsConfig.filelist) { + throw Error('If the the action is run outside a pull request it should be run with a filelist.'); + } + else if (!isCheckedOut()) { + throw Error('No checkout found to analyze. Please perform a checkout before running the TICS Action.'); + } +} +/** + * Checks if a .git directory exists to see if a checkout has been performed. + * @returns Boolean value if the folder is found or not. + */ +function isCheckedOut() { + if (!(0, fs_1.existsSync)('.git')) { + logger_1.logger.error('No git checkout found'); + return false; + } + return true; +} + + /***/ }), /***/ 9099: @@ -84395,244 +84639,12 @@ module.exports = JSON.parse('[[[0,44],"disallowed_STD3_valid"],[[45,46],"valid"] /******/ if (typeof __nccwpck_require__ !== 'undefined') __nccwpck_require__.ab = __dirname + "/"; /******/ /************************************************************************/ -var __webpack_exports__ = {}; -// This entry need to be wrapped in an IIFE because it need to be in strict mode. -(() => { -"use strict"; -var exports = __webpack_exports__; - -Object.defineProperty(exports, "__esModule", ({ value: true })); -exports.configure = exports.main = void 0; -const fs_1 = __nccwpck_require__(7147); -const comments_1 = __nccwpck_require__(6572); -const configuration_1 = __nccwpck_require__(5527); -const pulls_1 = __nccwpck_require__(8808); -const logger_1 = __nccwpck_require__(6440); -const analyzer_1 = __nccwpck_require__(9099); -const api_helper_1 = __nccwpck_require__(3823); -const fetcher_1 = __nccwpck_require__(1559); -const review_1 = __nccwpck_require__(2691); -const summary_1 = __nccwpck_require__(1502); -const annotations_1 = __nccwpck_require__(1058); -const enums_1 = __nccwpck_require__(1655); -const compare_versions_1 = __nccwpck_require__(4773); -const core_1 = __nccwpck_require__(2186); -const artifacts_1 = __nccwpck_require__(5734); -const commits_1 = __nccwpck_require__(5759); -main().catch((error) => { - let message = 'TICS failed with unknown reason'; - if (error instanceof Error) - message = error.message; - logger_1.logger.setFailed(message); - process.exit(1); -}); -// exported for testing purposes -async function main() { - configure(); - await meetsPrerequisites(); - let analysis; - if (configuration_1.ticsConfig.mode === 'diagnostic') { - analysis = await diagnosticAnalysis(); - } - else { - const changedFiles = await getChangedFiles(); - if (changedFiles) { - analysis = await analyze(changedFiles); - if (analysis.explorerUrls.length > 0) { - try { - await processAnalysis(analysis, changedFiles); - } - catch (error) { - let message = 'Something went wrond: reason unknown'; - if (error instanceof Error) - message = error.message; - logger_1.logger.setFailed(message); - } - } - } - } - if (analysis) { - if (configuration_1.ticsConfig.tmpDir || configuration_1.githubConfig.debugger) { - await (0, artifacts_1.uploadArtifact)(); - } - (0, api_helper_1.cliSummary)(analysis); - } - // Write the summary made to the action summary. - await core_1.summary.write({ overwrite: true }); -} -exports.main = main; -async function getChangedFiles() { - let changedFilesFilePath = undefined; - let changedFiles = undefined; - if (configuration_1.githubConfig.eventName === 'pull_request') { - changedFiles = await (0, pulls_1.getChangedFilesOfPullRequest)(); - } - else { - changedFiles = await (0, commits_1.getChangedFilesOfCommit)(); - } - if (configuration_1.ticsConfig.filelist) { - changedFilesFilePath = configuration_1.ticsConfig.filelist; - } - else { - if (changedFiles.length <= 0) { - logger_1.logger.info('No changed files found to analyze.'); - return; - } - changedFilesFilePath = (0, pulls_1.changedFilesToFile)(changedFiles); - } - return { - files: changedFiles, - path: changedFilesFilePath - }; -} -async function analyze(changedFiles) { - const analysis = await (0, analyzer_1.runTicsAnalyzer)(changedFiles.path); - if (analysis.explorerUrls.length === 0) { - (0, comments_1.deletePreviousComments)(await (0, comments_1.getPostedComments)()); - if (!analysis.completed) { - await (0, comments_1.postErrorComment)(analysis); - logger_1.logger.setFailed('Failed to run TICS Github Action.'); - } - else if (analysis.warningList.find(w => w.includes('[WARNING 5057]'))) { - await postToConversation(false, 'No changed files applicable for TICS analysis quality gating.'); - } - else { - logger_1.logger.setFailed('Failed to run TICS Github Action.'); - analysis.errorList.push('Explorer URL not returned from TICS analysis.'); - await (0, comments_1.postErrorComment)(analysis); - } - } - return analysis; -} -async function processAnalysis(analysis, changedFiles) { - const analysisResults = await (0, fetcher_1.getAnalysisResults)(analysis.explorerUrls, changedFiles.files); - if (analysisResults.missesQualityGate) { - throw Error('Some quality gates could not be retrieved'); - } - // If not run on a pull request no review comments have to be deleted - if (configuration_1.githubConfig.eventName === 'pull_request') { - const previousReviewComments = await (0, annotations_1.getPostedReviewComments)(); - if (previousReviewComments && previousReviewComments.length > 0) { - (0, annotations_1.deletePreviousReviewComments)(previousReviewComments); - } - } - if (configuration_1.ticsConfig.postAnnotations) { - (0, annotations_1.postAnnotations)(analysisResults); - } - let reviewBody = (0, summary_1.createSummaryBody)(analysisResults); - // If not run on a pull request no comments have to be deleted - // and there is no conversation to post to. - if (configuration_1.githubConfig.eventName === 'pull_request') { - (0, comments_1.deletePreviousComments)(await (0, comments_1.getPostedComments)()); - await postToConversation(true, reviewBody, analysisResults.passed ? enums_1.Events.APPROVE : enums_1.Events.REQUEST_CHANGES); - } - if (!analysisResults.passed) - logger_1.logger.setFailed(analysisResults.message); -} -/** - * Function for running the action in diagnostic mode. - * @returns Analysis result from a diagnostic run. - */ -async function diagnosticAnalysis() { - logger_1.logger.header('Running action in diagnostic mode'); - let analysis = await (0, analyzer_1.runTicsAnalyzer)(''); - if (analysis.statusCode !== 0) { - logger_1.logger.setFailed('Diagnostic run has failed.'); - } - return analysis; -} -/** - * Function to combine the posting to conversation in a single location. - * @param isGate if posting is done on a quality gate result. - * @param body body of the summary to post. - * @param event in case of posting a review an event should be given. - */ -async function postToConversation(isGate, body, event = enums_1.Events.COMMENT) { - if (configuration_1.ticsConfig.postToConversation) { - if (isGate) { - if (configuration_1.ticsConfig.pullRequestApproval) { - await (0, review_1.postReview)(body, event); - } - else { - await (0, comments_1.postComment)(body); - } - } - else { - if (configuration_1.ticsConfig.pullRequestApproval) { - await (0, review_1.postNothingAnalyzedReview)(body); - } - else { - await (0, comments_1.postNothingAnalyzedComment)(body); - } - } - } -} -/** - * Configure the action before running the analysis. - */ -function configure() { - process.removeAllListeners('warning'); - process.on('warning', warning => { - if (configuration_1.githubConfig.debugger) - logger_1.logger.debug(warning.message.toString()); - }); - (0, core_1.exportVariable)('TICSIDE', 'GITHUB'); - // set ticsAuthToken - if (configuration_1.ticsConfig.ticsAuthToken) { - (0, core_1.exportVariable)('TICSAUTHTOKEN', configuration_1.ticsConfig.ticsAuthToken); - } - // set hostnameVerification - if (configuration_1.ticsConfig.hostnameVerification) { - (0, core_1.exportVariable)('TICSHOSTNAMEVERIFICATION', configuration_1.ticsConfig.hostnameVerification); - if (configuration_1.ticsConfig.hostnameVerification === '0' || configuration_1.ticsConfig.hostnameVerification === 'false') { - (0, core_1.exportVariable)('NODE_TLS_REJECT_UNAUTHORIZED', 0); - logger_1.logger.debug('Hostname Verification disabled'); - } - } - // set trustStrategy - if (configuration_1.ticsConfig.trustStrategy) { - (0, core_1.exportVariable)('TICSTRUSTSTRATEGY', configuration_1.ticsConfig.trustStrategy); - if (configuration_1.ticsConfig.trustStrategy === 'self-signed' || configuration_1.ticsConfig.trustStrategy === 'all') { - (0, core_1.exportVariable)('NODE_TLS_REJECT_UNAUTHORIZED', 0); - logger_1.logger.debug(`Trust strategy set to ${configuration_1.ticsConfig.trustStrategy}`); - } - } -} -exports.configure = configure; -/** - * Checks if prerequisites are met to run the Github Plugin. - * If any of these checks fail it returns a message. - */ -async function meetsPrerequisites() { - const viewerVersion = await (0, fetcher_1.getViewerVersion)(); - if (!viewerVersion || !(0, compare_versions_1.satisfies)(viewerVersion.version, '>=2022.4.0')) { - const version = viewerVersion ? viewerVersion.version : 'unknown'; - throw Error(`Minimum required TICS Viewer version is 2022.4. Found version ${version}.`); - } - else if (configuration_1.ticsConfig.mode === 'diagnostic') { - // No need for checked out repository. - } - else if (configuration_1.githubConfig.eventName !== 'pull_request' && !configuration_1.ticsConfig.filelist) { - throw Error('If the the action is run outside a pull request it should be run with a filelist.'); - } - else if (!isCheckedOut()) { - throw Error('No checkout found to analyze. Please perform a checkout before running the TICS Action.'); - } -} -/** - * Checks if a .git directory exists to see if a checkout has been performed. - * @returns Boolean value if the folder is found or not. - */ -function isCheckedOut() { - if (!(0, fs_1.existsSync)('.git')) { - logger_1.logger.error('No git checkout found'); - return false; - } - return true; -} - -})(); - -module.exports = __webpack_exports__; +/******/ +/******/ // startup +/******/ // Load entry module and return exports +/******/ // This entry module is referenced by other modules so it can't be inlined +/******/ var __webpack_exports__ = __nccwpck_require__(3109); +/******/ module.exports = __webpack_exports__; +/******/ /******/ })() ; \ No newline at end of file diff --git a/src/helper/logger.ts b/src/helper/logger.ts index 43026654..d37f00c2 100644 --- a/src/helper/logger.ts +++ b/src/helper/logger.ts @@ -82,7 +82,7 @@ class Logger { } /** - * Masks the secrets defined in ticsConfig secretsFilter from the console logging. + * Masks the secrets defined in ticsConfig secretsFilter from the * @param data string that is going to be logged to the console. * @returns the message with the secrets masked. */ diff --git a/src/main.ts b/src/main.ts index 0f35262c..09be3363 100644 --- a/src/main.ts +++ b/src/main.ts @@ -17,6 +17,9 @@ import { uploadArtifact } from './github/artifacts'; import { getChangedFilesOfCommit } from './github/commits'; import { ChangedFiles } from './interfaces'; +// export for testing purposes +export let failedMessage: string | undefined = undefined; + main().catch((error: unknown) => { let message = 'TICS failed with unknown reason'; if (error instanceof Error) message = error.message; @@ -45,17 +48,22 @@ export async function main(): Promise { } catch (error: unknown) { let message = 'Something went wrond: reason unknown'; if (error instanceof Error) message = error.message; - logger.setFailed(message); + + failedMessage = message; } } } } - if (analysis) { - if (ticsConfig.tmpDir || githubConfig.debugger) { - await uploadArtifact(); - } + if (analysis && (ticsConfig.tmpDir || githubConfig.debugger)) { + await uploadArtifact(); + } + if (failedMessage) { + logger.setFailed(failedMessage); + } + + if (analysis) { cliSummary(analysis); } @@ -95,12 +103,12 @@ async function analyze(changedFiles: ChangedFiles): Promise { if (analysis.explorerUrls.length === 0) { deletePreviousComments(await getPostedComments()); if (!analysis.completed) { + failedMessage = 'Failed to run TICS Github Action.'; await postErrorComment(analysis); - logger.setFailed('Failed to run TICS Github Action.'); } else if (analysis.warningList.find(w => w.includes('[WARNING 5057]'))) { await postToConversation(false, 'No changed files applicable for TICS analysis quality gating.'); } else { - logger.setFailed('Failed to run TICS Github Action.'); + failedMessage = 'Failed to run TICS Github Action.'; analysis.errorList.push('Explorer URL not returned from TICS analysis.'); await postErrorComment(analysis); } @@ -113,7 +121,7 @@ async function processAnalysis(analysis: Analysis, changedFiles: ChangedFiles) { const analysisResults = await getAnalysisResults(analysis.explorerUrls, changedFiles.files); if (analysisResults.missesQualityGate) { - throw Error('Some quality gates could not be retrieved'); + throw Error('Some quality gates could not be retrieved.'); } // If not run on a pull request no review comments have to be deleted @@ -138,7 +146,9 @@ async function processAnalysis(analysis: Analysis, changedFiles: ChangedFiles) { await postToConversation(true, reviewBody, analysisResults.passed ? Events.APPROVE : Events.REQUEST_CHANGES); } - if (!analysisResults.passed) logger.setFailed(analysisResults.message); + if (!analysisResults.passed) { + failedMessage = analysisResults.message; + } } /** @@ -148,8 +158,9 @@ async function processAnalysis(analysis: Analysis, changedFiles: ChangedFiles) { async function diagnosticAnalysis(): Promise { logger.header('Running action in diagnostic mode'); let analysis = await runTicsAnalyzer(''); + if (analysis.statusCode !== 0) { - logger.setFailed('Diagnostic run has failed.'); + failedMessage = 'Diagnostic run has failed.'; } return analysis; diff --git a/test/unit/main.test.ts b/test/unit/main.test.ts index cf9ecb0b..c5d3a514 100644 --- a/test/unit/main.test.ts +++ b/test/unit/main.test.ts @@ -28,6 +28,11 @@ import { singlePreviousReviewComments } from './main_helper'; +afterEach(() => { + jest.clearAllMocks(); + delete main.failedMessage; +}); + describe('pre checks', () => { test('Should call exit if viewer version is too low', async () => { jest.spyOn(fetcher, 'getViewerVersion').mockResolvedValue({ version: '2022.0.0' }); @@ -93,7 +98,7 @@ describe('SetFailed checks', () => { (existsSync as any).mockReturnValueOnce(true); jest.spyOn(pulls, 'getChangedFilesOfPullRequest').mockResolvedValueOnce(singleChangedFiles); jest.spyOn(pulls, 'changedFilesToFile').mockReturnValueOnce('location/changedFiles.txt'); - jest.spyOn(analyzer, 'runTicsAnalyzer').mockResolvedValueOnce(analysisFailedNoUrl); + const spyAnalyzer = jest.spyOn(analyzer, 'runTicsAnalyzer').mockResolvedValueOnce(analysisFailedNoUrl); jest.spyOn(comments, 'getPostedComments').mockResolvedValue([]); const spySetFailed = jest.spyOn(logger, 'setFailed'); @@ -101,13 +106,14 @@ describe('SetFailed checks', () => { await main.main(); expect(spySetFailed).toHaveBeenCalledWith(expect.stringContaining('Failed to run TICS Github Action.')); + expect(spyAnalyzer).toHaveBeenCalledTimes(1); }); test('Should call setFailed if no Explorer URL and analysis passed', async () => { (existsSync as any).mockReturnValueOnce(true); jest.spyOn(pulls, 'getChangedFilesOfPullRequest').mockResolvedValueOnce(singleChangedFiles); jest.spyOn(pulls, 'changedFilesToFile').mockReturnValueOnce('location/changedFiles.txt'); - jest.spyOn(analyzer, 'runTicsAnalyzer').mockResolvedValueOnce(analysisPassedNoUrl); + const spyAnalyzer = jest.spyOn(analyzer, 'runTicsAnalyzer').mockResolvedValueOnce(analysisPassedNoUrl); jest.spyOn(comments, 'getPostedComments').mockResolvedValue([]); const spySetFailed = jest.spyOn(logger, 'setFailed'); @@ -117,13 +123,14 @@ describe('SetFailed checks', () => { expect(spySetFailed).toHaveBeenCalledWith(expect.stringContaining('Failed to run TICS Github Action.')); expect(spyError).toHaveBeenCalledWith(expect.stringContaining('Explorer URL not returned from TICS analysis.')); + expect(spyAnalyzer).toHaveBeenCalledTimes(1); }); test('Should call setFailed if analysis passed and quality gate undefined', async () => { (existsSync as any).mockReturnValueOnce(true); jest.spyOn(pulls, 'getChangedFilesOfPullRequest').mockResolvedValueOnce(singleChangedFiles); jest.spyOn(pulls, 'changedFilesToFile').mockReturnValueOnce('location/changedFiles.txt'); - jest.spyOn(analyzer, 'runTicsAnalyzer').mockResolvedValueOnce(analysisPassed); + const spyAnalyzer = jest.spyOn(analyzer, 'runTicsAnalyzer').mockResolvedValueOnce(analysisPassed); jest.spyOn(fetcher, 'getAnalysisResults').mockResolvedValueOnce(analysisResultsPassedNoUrl); jest.spyOn(review, 'postReview').mockImplementationOnce(() => Promise.resolve()); @@ -131,14 +138,15 @@ describe('SetFailed checks', () => { await main.main(); - expect(spySetFailed).toHaveBeenCalledWith('Some quality gates could not be retrieved'); + expect(spySetFailed).toHaveBeenCalledWith('Some quality gates could not be retrieved.'); + expect(spyAnalyzer).toHaveBeenCalledTimes(1); }); test('Should call setFailed if analysis passed and quality gate failed', async () => { (existsSync as any).mockReturnValueOnce(true); jest.spyOn(pulls, 'getChangedFilesOfPullRequest').mockResolvedValueOnce(singleChangedFiles); jest.spyOn(pulls, 'changedFilesToFile').mockReturnValueOnce('location/changedFiles.txt'); - jest.spyOn(analyzer, 'runTicsAnalyzer').mockResolvedValueOnce(analysisPassed); + const spyAnalyzer = jest.spyOn(analyzer, 'runTicsAnalyzer').mockResolvedValueOnce(analysisPassed); jest.spyOn(fetcher, 'getAnalysisResults').mockResolvedValueOnce(analysisResultsSingleFileFailed); jest.spyOn(review, 'postReview').mockImplementationOnce(() => Promise.resolve()); jest.spyOn(comments, 'getPostedComments').mockResolvedValue([]); @@ -148,13 +156,14 @@ describe('SetFailed checks', () => { await main.main(); expect(spySetFailed).toHaveBeenCalledWith('Project failed 2 out of 2 quality gates'); + expect(spyAnalyzer).toHaveBeenCalledTimes(1); }); test('Should not call setFailed if analysis passed and quality gate passed', async () => { (existsSync as any).mockReturnValueOnce(true); jest.spyOn(pulls, 'getChangedFilesOfPullRequest').mockResolvedValueOnce(singleChangedFiles); jest.spyOn(pulls, 'changedFilesToFile').mockReturnValueOnce('location/changedFiles.txt'); - jest.spyOn(analyzer, 'runTicsAnalyzer').mockResolvedValueOnce(analysisPassed); + const spyAnalyzer = jest.spyOn(analyzer, 'runTicsAnalyzer').mockResolvedValueOnce(analysisPassed); jest.spyOn(fetcher, 'getAnalysisResults').mockResolvedValueOnce(analysisResultsSingleFilePassed); jest.spyOn(review, 'postReview').mockImplementationOnce(() => Promise.resolve()); jest.spyOn(comments, 'getPostedComments').mockResolvedValue([]); @@ -164,6 +173,7 @@ describe('SetFailed checks', () => { await main.main(); expect(spySetFailed).toHaveBeenCalledTimes(0); + expect(spyAnalyzer).toHaveBeenCalledTimes(1); }); }); @@ -172,7 +182,7 @@ describe('postNothingAnalyzed', () => { (existsSync as any).mockReturnValueOnce(true); jest.spyOn(pulls, 'getChangedFilesOfPullRequest').mockResolvedValueOnce(singleChangedFiles); jest.spyOn(pulls, 'changedFilesToFile').mockReturnValueOnce('location/changedFiles.txt'); - jest.spyOn(analyzer, 'runTicsAnalyzer').mockResolvedValueOnce(analysisPassedNoUrlWarning5057); + const spyAnalyzer = jest.spyOn(analyzer, 'runTicsAnalyzer').mockResolvedValueOnce(analysisPassedNoUrlWarning5057); const spyReview = jest.spyOn(review, 'postNothingAnalyzedReview').mockImplementationOnce(() => Promise.resolve()); @@ -180,13 +190,14 @@ describe('postNothingAnalyzed', () => { await main.main(); expect(spyReview).toHaveBeenCalledWith('No changed files applicable for TICS analysis quality gating.'); + expect(spyAnalyzer).toHaveBeenCalledTimes(1); }); test('Should call postNothingAnalyzedReview when Explorer URL given and analysis failed with warning 5057', async () => { (existsSync as any).mockReturnValueOnce(true); jest.spyOn(pulls, 'getChangedFilesOfPullRequest').mockResolvedValueOnce(singleChangedFiles); jest.spyOn(pulls, 'changedFilesToFile').mockReturnValueOnce('location/changedFiles.txt'); - jest.spyOn(analyzer, 'runTicsAnalyzer').mockResolvedValueOnce(analysisPassedNoUrlWarning5057); + const spyAnalyzer = jest.spyOn(analyzer, 'runTicsAnalyzer').mockResolvedValueOnce(analysisPassedNoUrlWarning5057); const spyReview = jest.spyOn(comments, 'postNothingAnalyzedComment').mockImplementationOnce(() => Promise.resolve()); @@ -194,6 +205,7 @@ describe('postNothingAnalyzed', () => { await main.main(); expect(spyReview).toHaveBeenCalledWith('No changed files applicable for TICS analysis quality gating.'); + expect(spyAnalyzer).toHaveBeenCalledTimes(1); }); }); @@ -203,7 +215,7 @@ describe('PostReview checks', () => { (existsSync as any).mockReturnValueOnce(true); jest.spyOn(pulls, 'getChangedFilesOfPullRequest').mockResolvedValueOnce(singleChangedFiles); jest.spyOn(pulls, 'changedFilesToFile').mockReturnValueOnce('location/changedFiles.txt'); - jest.spyOn(analyzer, 'runTicsAnalyzer').mockResolvedValueOnce(analysisPassed); + const spyAnalyzer = jest.spyOn(analyzer, 'runTicsAnalyzer').mockResolvedValueOnce(analysisPassed); jest.spyOn(fetcher, 'getAnalysisResults').mockResolvedValueOnce(analysisResultsSingleFileFailed); jest.spyOn(comments, 'getPostedComments').mockResolvedValue([]); @@ -212,13 +224,14 @@ describe('PostReview checks', () => { await main.main(); expect(spyReview).toHaveBeenCalledWith(expect.stringContaining('TICS Quality Gate'), Events.REQUEST_CHANGES); + expect(spyAnalyzer).toHaveBeenCalledTimes(1); }); test('Should call postReview with one file analyzed, qualitygate passed and no annotations', async () => { (existsSync as any).mockReturnValueOnce(true); jest.spyOn(pulls, 'getChangedFilesOfPullRequest').mockResolvedValueOnce(singleChangedFiles); jest.spyOn(pulls, 'changedFilesToFile').mockReturnValueOnce('location/changedFiles.txt'); - jest.spyOn(analyzer, 'runTicsAnalyzer').mockResolvedValueOnce(analysisPassed); + const spyAnalyzer = jest.spyOn(analyzer, 'runTicsAnalyzer').mockResolvedValueOnce(analysisPassed); jest.spyOn(fetcher, 'getAnalysisResults').mockResolvedValueOnce(analysisResultsSingleFilePassed); jest.spyOn(comments, 'getPostedComments').mockImplementationOnce(() => Promise.resolve([])); @@ -227,13 +240,14 @@ describe('PostReview checks', () => { await main.main(); expect(spyReview).toHaveBeenCalledWith(expect.stringContaining('TICS Quality Gate'), Events.APPROVE); + expect(spyAnalyzer).toHaveBeenCalledTimes(1); }); test('Should call postReview when postAnnotations is true with no annotations and no previously posted review comments', async () => { (existsSync as any).mockReturnValueOnce(true); jest.spyOn(pulls, 'getChangedFilesOfPullRequest').mockResolvedValueOnce(doubleChangedFiles); jest.spyOn(pulls, 'changedFilesToFile').mockReturnValueOnce('location/changedFiles.txt'); - jest.spyOn(analyzer, 'runTicsAnalyzer').mockResolvedValueOnce(analysisPassed); + const spyAnalyzer = jest.spyOn(analyzer, 'runTicsAnalyzer').mockResolvedValueOnce(analysisPassed); jest.spyOn(fetcher, 'getAnalysisResults').mockResolvedValueOnce(analysisResultsSingleFilePassed); jest.spyOn(fetcher, 'getAnnotations').mockResolvedValueOnce([]); jest.spyOn(annotations, 'getPostedReviewComments').mockResolvedValueOnce([]); @@ -245,13 +259,14 @@ describe('PostReview checks', () => { await main.main(); expect(spyReview).toHaveBeenCalledWith(expect.stringContaining('TICS Quality Gate'), Events.APPROVE); + expect(spyAnalyzer).toHaveBeenCalledTimes(1); }); test('Should call postReview when postAnnotations is true with one annotation and no previously posted review comment', async () => { (existsSync as any).mockReturnValueOnce(true); jest.spyOn(pulls, 'getChangedFilesOfPullRequest').mockResolvedValueOnce(doubleChangedFiles); jest.spyOn(pulls, 'changedFilesToFile').mockReturnValueOnce('location/changedFiles.txt'); - jest.spyOn(analyzer, 'runTicsAnalyzer').mockResolvedValueOnce(analysisPassed); + const spyAnalyzer = jest.spyOn(analyzer, 'runTicsAnalyzer').mockResolvedValueOnce(analysisPassed); jest.spyOn(fetcher, 'getAnalysisResults').mockResolvedValueOnce(analysisResultsDoubleFilePassed); jest.spyOn(fetcher, 'getAnnotations').mockResolvedValueOnce(singleAnnotations); jest.spyOn(annotations, 'getPostedReviewComments').mockResolvedValueOnce([]); @@ -263,6 +278,7 @@ describe('PostReview checks', () => { await main.main(); expect(spyReview).toHaveBeenCalledWith(expect.stringContaining('TICS Quality Gate'), Events.APPROVE); + expect(spyAnalyzer).toHaveBeenCalledTimes(1); }); }); @@ -273,12 +289,13 @@ describe('Artifact upload tests', () => { (existsSync as any).mockReturnValueOnce(true); jest.spyOn(pulls, 'getChangedFilesOfPullRequest').mockResolvedValueOnce(singleChangedFiles); jest.spyOn(pulls, 'changedFilesToFile').mockReturnValueOnce('location/changedFiles.txt'); - jest.spyOn(analyzer, 'runTicsAnalyzer').mockResolvedValueOnce(analysisPassed); + const spyAnalyzer = jest.spyOn(analyzer, 'runTicsAnalyzer').mockResolvedValueOnce(analysisPassed); await main.main(); expect(spyArtifact).toHaveBeenCalledTimes(0); expect(spySummary).toHaveBeenCalledTimes(1); + expect(spyAnalyzer).toHaveBeenCalledTimes(1); }); test('Should not call uploadArtifacts when tmpDir is set but no analysis has been done', async () => { @@ -299,7 +316,7 @@ describe('Artifact upload tests', () => { (existsSync as any).mockReturnValueOnce(true); jest.spyOn(pulls, 'getChangedFilesOfPullRequest').mockResolvedValueOnce(singleChangedFiles); jest.spyOn(pulls, 'changedFilesToFile').mockReturnValueOnce('location/changedFiles.txt'); - jest.spyOn(analyzer, 'runTicsAnalyzer').mockResolvedValueOnce(analysisPassed); + const spyAnalyzer = jest.spyOn(analyzer, 'runTicsAnalyzer').mockResolvedValueOnce(analysisPassed); githubConfig.debugger = true; @@ -307,6 +324,7 @@ describe('Artifact upload tests', () => { expect(spyArtifact).toHaveBeenCalledTimes(1); expect(spySummary).toHaveBeenCalledTimes(1); + expect(spyAnalyzer).toHaveBeenCalledTimes(1); githubConfig.debugger = false; }); @@ -317,7 +335,7 @@ describe('Artifact upload tests', () => { (existsSync as any).mockReturnValueOnce(true); jest.spyOn(pulls, 'getChangedFilesOfPullRequest').mockResolvedValueOnce(singleChangedFiles); jest.spyOn(pulls, 'changedFilesToFile').mockReturnValueOnce('location/changedFiles.txt'); - jest.spyOn(analyzer, 'runTicsAnalyzer').mockResolvedValueOnce(analysisPassed); + const spyAnalyzer = jest.spyOn(analyzer, 'runTicsAnalyzer').mockResolvedValueOnce(analysisPassed); ticsConfig.tmpDir = '/tmp'; @@ -325,6 +343,7 @@ describe('Artifact upload tests', () => { expect(spyArtifact).toHaveBeenCalledTimes(1); expect(spySummary).toHaveBeenCalledTimes(1); + expect(spyAnalyzer).toHaveBeenCalledTimes(1); }); }); @@ -333,7 +352,7 @@ describe('DeletePreviousReviewComments check', () => { (existsSync as any).mockReturnValueOnce(true); jest.spyOn(pulls, 'getChangedFilesOfPullRequest').mockResolvedValueOnce(doubleChangedFiles); jest.spyOn(pulls, 'changedFilesToFile').mockReturnValueOnce('location/changedFiles.txt'); - jest.spyOn(analyzer, 'runTicsAnalyzer').mockResolvedValueOnce(analysisPassed); + const spyAnalyzer = jest.spyOn(analyzer, 'runTicsAnalyzer').mockResolvedValueOnce(analysisPassed); jest.spyOn(fetcher, 'getAnalysisResults').mockResolvedValueOnce(analysisResultsDoubleFilePassed); jest.spyOn(fetcher, 'getAnnotations').mockResolvedValueOnce(singleAnnotations); jest.spyOn(annotations, 'getPostedReviewComments').mockImplementationOnce((): any => singlePreviousReviewComments); @@ -345,29 +364,33 @@ describe('DeletePreviousReviewComments check', () => { await main.main(); expect(spyDelete).toHaveBeenCalledWith(singlePreviousReviewComments); + expect(spyAnalyzer).toHaveBeenCalledTimes(1); }); }); describe('Diagnostic mode checks', () => { test('Diagnostic mode succeeds', async () => { - jest.spyOn(analyzer, 'runTicsAnalyzer').mockResolvedValueOnce(analysisPassed); + const spyAnalyzer = jest.spyOn(analyzer, 'runTicsAnalyzer').mockResolvedValueOnce(analysisPassed); ticsConfig.mode = 'diagnostic'; const spySetFailed = jest.spyOn(logger, 'setFailed'); + console.log('test'); await main.main(); + expect(spyAnalyzer).toHaveBeenCalledTimes(1); expect(spySetFailed).toHaveBeenCalledTimes(0); }); test('Diagnostic mode fails', async () => { - jest.spyOn(analyzer, 'runTicsAnalyzer').mockResolvedValueOnce(analysisFailedNoUrl); + const spyAnalyzer = jest.spyOn(analyzer, 'runTicsAnalyzer').mockResolvedValueOnce(analysisFailedNoUrl); ticsConfig.mode = 'diagnostic'; const spySetFailed = jest.spyOn(logger, 'setFailed'); await main.main(); + expect(spyAnalyzer).toHaveBeenCalledTimes(1); expect(spySetFailed).toHaveBeenCalledTimes(1); }); }); From 768f4692fff48ce422b84df91707aa723e524011 Mon Sep 17 00:00:00 2001 From: janssen <118828444+janssen-tiobe@users.noreply.github.com> Date: Tue, 31 Oct 2023 15:22:19 +0100 Subject: [PATCH 8/8] #33077: Small layout change --- dist/index.js | 3 --- src/tics/api_helper.ts | 3 --- 2 files changed, 6 deletions(-) diff --git a/dist/index.js b/dist/index.js index 1ae69860..d38e5f10 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1437,9 +1437,6 @@ const configuration_1 = __nccwpck_require__(5527); * @param analysis the output of the TICS analysis run. */ function cliSummary(analysis) { - if (analysis.errorList.length > 0 || (configuration_1.githubConfig.debugger && analysis.warningList.length > 0)) { - logger_1.logger.info(''); //empty line for better readability - } analysis.errorList.forEach(error => logger_1.logger.error(error)); if (configuration_1.githubConfig.debugger) { analysis.warningList.forEach(warning => logger_1.logger.warning(warning)); diff --git a/src/tics/api_helper.ts b/src/tics/api_helper.ts index 8ba6c19e..8cfe4806 100644 --- a/src/tics/api_helper.ts +++ b/src/tics/api_helper.ts @@ -7,9 +7,6 @@ import { Analysis } from '../helper/interfaces'; * @param analysis the output of the TICS analysis run. */ export function cliSummary(analysis: Analysis): void { - if (analysis.errorList.length > 0 || (githubConfig.debugger && analysis.warningList.length > 0)) { - logger.info(''); //empty line for better readability - } analysis.errorList.forEach(error => logger.error(error)); if (githubConfig.debugger) { analysis.warningList.forEach(warning => logger.warning(warning));