From cd789d8ce9f9c3b812dd6353d5a5f1a08b4f0b93 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 | 32 +++++++----------
 helpers/Dojo/SonarAnalyzer.ts       | 54 +++++++++++++++++++++++++++++
 2 files changed, 66 insertions(+), 20 deletions(-)
 create mode 100644 helpers/Dojo/SonarAnalyzer.ts

diff --git a/helpers/Dojo/AssignmentValidator.ts b/helpers/Dojo/AssignmentValidator.ts
index 9caa62f..a7c783b 100644
--- a/helpers/Dojo/AssignmentValidator.ts
+++ b/helpers/Dojo/AssignmentValidator.ts
@@ -13,8 +13,7 @@ import ExerciseDockerCompose         from './ExerciseDockerCompose';
 import util                          from 'util';
 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);
@@ -141,7 +140,7 @@ class AssignmentValidator {
         const resp = await ClientsSharedAssignmentHelper.getAssignmentByName(ClientsSharedConfig.assignment.name);
         if ( resp == undefined ) {
             this.emitError(`The assignment doesn't exist. An assignment must be created with "assignment create" before checking it.`, `Assignment doesn't exists`, AssignmentCheckerError.ASSIGNMENT_MISSING);
-            return;
+            throw new Error();
         } else {
             this.assignment = resp;
         }
@@ -293,37 +292,30 @@ class AssignmentValidator {
         if ( this.assignment.useSonar && this.runSonar ) {
             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;
+                throw new Error();
             }
 
-            if ( [ Language.c, Language.cpp, Language.objc ].includes(this.assignment.language) && this.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 ' + this.assignmentFile.buildLine, [], { shell: true });
-                if ( process.status !== 0 ) {
+            if ( SonarAnalyzer.mustRunBuild(this.assignment.language, this.assignmentFile.buildLine) ) {
+                const buildSuccess = SonarAnalyzer.runBuildStep(this.assignmentFile.buildLine as string);
+                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;
+                    throw new Error();
                 }
-                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=' + this.assignment.sonarKey, '-Dsonar.sources=.', '-Dsonar.host.url=' + SharedConfig.sonar.url, '-Dsonar.login=' + SharedConfig.sonar.token, ...additionalParams ]);
-            if ( process.status !== 0 ) {
+            const runSuccess = SonarAnalyzer.runAnalysis(this.assignment.sonarKey, this.assignment.language, this.assignmentFile.buildLine);
+            if ( runSuccess ) {
                 this.emitError(`Sonar gate failed`, 'Sonar analysis failure', AssignmentCheckerError.SONAR_ANALYSIS_FAILED);
-                return;
+                throw new Error();
             }
             this.endSubStep('Sonar gate passed', false);
 
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
-- 
GitLab