From 41b3d88544eb46171acf36b4fd61332c33db5bf8 Mon Sep 17 00:00:00 2001
From: Joel von der Weid <joel.von-der-weid@hesge.ch>
Date: Mon, 17 Jun 2024 15:02:25 +0200
Subject: [PATCH] Create SonarAnalyzer

---
 helpers/Dojo/AssignmentValidator.ts | 24 +++++--------
 helpers/Dojo/SonarAnalyzer.ts       | 54 +++++++++++++++++++++++++++++
 types/Dojo/ApiRoute.ts              |  1 +
 3 files changed, 63 insertions(+), 16 deletions(-)
 create mode 100644 helpers/Dojo/SonarAnalyzer.ts

diff --git a/helpers/Dojo/AssignmentValidator.ts b/helpers/Dojo/AssignmentValidator.ts
index 4a16475..c06a4ea 100644
--- a/helpers/Dojo/AssignmentValidator.ts
+++ b/helpers/Dojo/AssignmentValidator.ts
@@ -15,6 +15,7 @@ import Assignment, { Language }      from '../../models/Assignment';
 import ClientsSharedAssignmentHelper from './ClientsSharedAssignmentHelper';
 import { spawnSync }                 from 'node:child_process';
 import SharedConfig                  from '../../../shared/config/SharedConfig';
+import SonarAnalyzer                 from './SonarAnalyzer';
 
 
 const execAsync = util.promisify(exec);
@@ -281,35 +282,27 @@ class AssignmentValidator {
             {
                 this.newStep('ASSIGNMENT_SONAR', 'Please wait while we are running Sonar analysis on the assignment...');
 
-                let additionalParams: string[] = [];
-
                 this.newSubStep('SONAR_BUILD', 'Build files');
 
-                const buildProcess = spawnSync('docker', ['build', '--tag', 'dojo-sonar-scanner', '/sonar']);
-                if ( buildProcess.status !== 0 ) {
+                const buildSuccess = SonarAnalyzer.buildDocker()
+                if ( !buildSuccess ) {
                     this.emitError(`Build sonar image failed`, 'Sonar analysis failure', AssignmentCheckerError.SONAR_ANALYSIS_FAILED);
-                    console.log(buildProcess.stdout.toString())
-                    console.log(buildProcess.stderr.toString())
                     return;
                 }
 
-                if ([Language.c, Language.cpp, Language.objc].includes(assignment.language) && assignmentFile.buildLine != undefined) {
-                    const process = spawnSync('docker run -v ./:/usr/src dojo-sonar-scanner /usr/local/bin/build-wrapper-linux-x86-64 --out-dir bw-output ' + assignmentFile.buildLine, [], { shell: true })
-                    if ( process.status !== 0 ) {
+                if (SonarAnalyzer.mustRunBuild(assignment.language, assignmentFile.buildLine)) {
+                    const buildSuccess = SonarAnalyzer.runBuildStep(assignmentFile.buildLine!);
+                    if ( !buildSuccess ) {
                         this.emitError(`Failed to build files using buildLine`, 'Sonar analysis failure', AssignmentCheckerError.SONAR_ANALYSIS_FAILED);
-                        console.log(process.stdout.toString())
-                        console.log(process.stderr.toString())
                         return;
                     }
-                    additionalParams = ['-Dsonar.cfamily.build-wrapper-output=/usr/src/bw-output'];
                 }
                 this.endSubStep('Sonar files build success', false);
 
                 this.newSubStep('SONAR_RUN', 'Run sonar analysis');
 
-
-                const process = spawnSync('docker', ['run', '-v', './:/usr/src', 'dojo-sonar-scanner' , 'sonar-scanner', '-Dsonar.qualitygate.wait=true', '-Dsonar.projectKey=' + assignment.sonarKey, '-Dsonar.sources=.', '-Dsonar.host.url=' + SharedConfig.sonar.url, '-Dsonar.login=' + SharedConfig.sonar.token, ...additionalParams])
-                if ( process.status !== 0 ) {
+                const runSuccess = SonarAnalyzer.runAnalysis(assignment.sonarKey, assignment.language, assignmentFile.buildLine);
+                if ( runSuccess ) {
                     this.emitError(`Sonar gate failed`, 'Sonar analysis failure', AssignmentCheckerError.SONAR_ANALYSIS_FAILED);
                     return;
                 }
@@ -326,7 +319,6 @@ class AssignmentValidator {
             {
                 this.newStep('ASSIGNMENT_RUN', 'Please wait while we are running the assignment...');
 
-
                 const exerciseDockerCompose = new ExerciseDockerCompose(ClientsSharedConfig.dockerCompose.projectName, assignmentFile, this.folderAssignment);
 
                 try {
diff --git a/helpers/Dojo/SonarAnalyzer.ts b/helpers/Dojo/SonarAnalyzer.ts
new file mode 100644
index 0000000..d763d32
--- /dev/null
+++ b/helpers/Dojo/SonarAnalyzer.ts
@@ -0,0 +1,54 @@
+import { spawnSync }          from 'node:child_process';
+import { Language }           from '../../models/Assignment';
+import SharedConfig           from '../../../shared/config/SharedConfig';
+
+const IMAGE_NAME = 'dojo-sonar-scanner'
+const OUT_DIR = 'bw-output';
+
+class SonarAnalyzer {
+    buildDocker = () => {
+        const buildProcess = spawnSync('docker', ['build', '--tag', IMAGE_NAME, '/sonar']);
+        if ( buildProcess.status !== 0 ) {
+            console.log(buildProcess.stdout.toString())
+            console.log(buildProcess.stderr.toString())
+            return false;
+        }
+        return true;
+    }
+
+    mustRunBuild = (language: Language, buildLine: string | undefined) => {
+        return [Language.c, Language.cpp, Language.objc].includes(language) && buildLine != undefined;
+    }
+
+    runBuildStep = (buildLine: string) => {
+        const process = spawnSync(`docker run -v ./:/usr/src ${IMAGE_NAME} /usr/local/bin/build-wrapper-linux-x86-64 --out-dir ${OUT_DIR} ` + buildLine, [], { shell: true })
+        if ( process.status !== 0 ) {
+            console.log(process.stdout.toString())
+            console.log(process.stderr.toString())
+            return false;
+        }
+        return true;
+    }
+
+    runAnalysis = (sonarKey: string, language: Language, buildLine: string | undefined) => {
+        let addParam: string[] = [];
+        if (this.mustRunBuild(language, buildLine)) {
+            addParam = [ `-Dsonar.cfamily.build-wrapper-output=/usr/src/${OUT_DIR}`];
+        }
+
+        const process = spawnSync(
+            'docker',
+            ['run', '-v', './:/usr/src',
+             IMAGE_NAME , 'sonar-scanner',
+             '-Dsonar.qualitygate.wait=true',
+             '-Dsonar.projectKey=' + sonarKey,
+             '-Dsonar.sources=.',
+             '-Dsonar.host.url=' + SharedConfig.sonar.url,
+             '-Dsonar.login=' + SharedConfig.sonar.token,
+             ...addParam])
+
+        return process.status === 0;
+    }
+}
+
+export default new SonarAnalyzer();
\ No newline at end of file
diff --git a/types/Dojo/ApiRoute.ts b/types/Dojo/ApiRoute.ts
index 0569734..6b10960 100644
--- a/types/Dojo/ApiRoute.ts
+++ b/types/Dojo/ApiRoute.ts
@@ -13,6 +13,7 @@ enum ApiRoute {
     ASSIGNMENT_CORRECTION_LINK   = '/assignments/{{assignmentNameOrUrl}}/corrections',
     ASSIGNMENT_CORRECTION_UPDATE = '/assignments/{{assignmentNameOrUrl}}/corrections/{{exerciseIdOrUrl}}',
     EXERCISE_CREATE              = '/assignments/{{nameOrUrl}}/exercises',
+    EXERCISE_GET                 = '/exercises/{{id}}',
     EXERCISE_ASSIGNMENT          = '/exercises/{{id}}/assignment',
     EXERCISE_RESULTS             = '/exercises/{{id}}/results'
 }
-- 
GitLab