Skip to content
Snippets Groups Projects
Commit 8ece6bfc authored by joel.vonderwe's avatar joel.vonderwe Committed by michael.minelli
Browse files

Implement sonar scan for assignments

parent d1764d09
No related branches found
No related tags found
No related merge requests found
...@@ -13,6 +13,8 @@ import ExerciseDockerCompose from './ExerciseDockerCompose'; ...@@ -13,6 +13,8 @@ import ExerciseDockerCompose from './ExerciseDockerCompose';
import util from 'util'; import util from 'util';
import Assignment, { Language } from '../../models/Assignment'; import Assignment, { Language } from '../../models/Assignment';
import ClientsSharedAssignmentHelper from './ClientsSharedAssignmentHelper'; import ClientsSharedAssignmentHelper from './ClientsSharedAssignmentHelper';
import { spawnSync } from 'node:child_process';
import SharedConfig from '../../../shared/config/SharedConfig';
const execAsync = util.promisify(exec); const execAsync = util.promisify(exec);
...@@ -21,6 +23,7 @@ const execAsync = util.promisify(exec); ...@@ -21,6 +23,7 @@ const execAsync = util.promisify(exec);
class AssignmentValidator { class AssignmentValidator {
private readonly folderAssignment: string; private readonly folderAssignment: string;
private readonly doDown: boolean; private readonly doDown: boolean;
private readonly runSonar: boolean;
readonly events: TypedEmitter<AssignmentValidatorEvents> = new TypedEmitter<AssignmentValidatorEvents>(); readonly events: TypedEmitter<AssignmentValidatorEvents> = new TypedEmitter<AssignmentValidatorEvents>();
...@@ -39,9 +42,10 @@ class AssignmentValidator { ...@@ -39,9 +42,10 @@ class AssignmentValidator {
private assignmentFile!: AssignmentFile; private assignmentFile!: AssignmentFile;
private assignment!: Assignment; private assignment!: Assignment;
constructor(folderAssignment: string, doDown: boolean = false) { constructor(folderAssignment: string, doDown: boolean = false, runSonar: boolean = false) {
this.folderAssignment = folderAssignment; this.folderAssignment = folderAssignment;
this.doDown = doDown; this.doDown = doDown;
this.runSonar = runSonar;
this.events.on('logs', (log: string, _error: boolean, displayable: boolean) => { this.events.on('logs', (log: string, _error: boolean, displayable: boolean) => {
this.allLogs += log; this.allLogs += log;
...@@ -133,17 +137,17 @@ class AssignmentValidator { ...@@ -133,17 +137,17 @@ class AssignmentValidator {
private async checkAssignment() { private async checkAssignment() {
this.newStep('ASSIGNMENT_CHECKING', 'Please wait while we are checking the assignment...'); this.newStep('ASSIGNMENT_CHECKING', 'Please wait while we are checking the assignment...');
this.newSubStep('ASSIGNMENT_EXISTS', 'Checking if the assignment exists');
const resp = await ClientsSharedAssignmentHelper.getAssignmentFromPath(this.folderAssignment); const resp = await ClientsSharedAssignmentHelper.getAssignmentByName(this.folderAssignment);
if ( resp == undefined ) { 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); 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; return;
} else { } else {
this.assignment = resp; this.assignment = resp;
} }
this.endSubStep('Assignment exists', false);
this.endStep('Assignment exists and is valid', false);
this.endStep('Assignment exists', false);
} }
/** /**
...@@ -281,7 +285,54 @@ class AssignmentValidator { ...@@ -281,7 +285,54 @@ class AssignmentValidator {
} }
/** /**
* Step 6: Run * Step 6: Sonar analysis
* - Analyse the project with SonarCube
* @private
*/
private async sonarAnalysis() {
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 ) {
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(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 ) {
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=' + this.assignment.sonarKey, '-Dsonar.sources=.', '-Dsonar.host.url=' + SharedConfig.sonar.url, '-Dsonar.login=' + SharedConfig.sonar.token, ...additionalParams ]);
if ( process.status !== 0 ) {
this.emitError(`Sonar gate failed`, 'Sonar analysis failure', AssignmentCheckerError.SONAR_ANALYSIS_FAILED);
return;
}
this.endSubStep('Sonar gate passed', false);
this.endStep('Sonar analysis success', false);
}
}
/**
* Step 7: Run
* - Make a run of the assignment (If the return code is 0, the assignment is not valid because it means that there no need of modification for succeed the exercise) * - Make a run of the assignment (If the return code is 0, the assignment is not valid because it means that there no need of modification for succeed the exercise)
* @private * @private
*/ */
...@@ -335,6 +386,8 @@ class AssignmentValidator { ...@@ -335,6 +386,8 @@ class AssignmentValidator {
this.dockerfilesValidation(); this.dockerfilesValidation();
await this.sonarAnalysis();
await this.runAssignment(); await this.runAssignment();
this.finished(true, 0); this.finished(true, 0);
......
...@@ -30,10 +30,11 @@ class ClientsSharedAssignmentHelper { ...@@ -30,10 +30,11 @@ class ClientsSharedAssignmentHelper {
})); }));
} }
private async getAssignment(url: string): Promise<Assignment | undefined> { private async getAssignment(nameOrUrl: string): Promise<Assignment | undefined> {
try { try {
return (await axios.get<DojoBackendResponse<Assignment>>(`${ ClientsSharedConfig.apiURL }${ ApiRoute.ASSIGNMENT_GET }`.replace('{{nameOrUrl}}', encodeURIComponent(url)))).data.data; return (await axios.get<DojoBackendResponse<Assignment>>(`${ ClientsSharedConfig.apiURL }${ ApiRoute.ASSIGNMENT_GET }`.replace('{{nameOrUrl}}', encodeURIComponent(nameOrUrl)))).data.data;
} catch ( error ) { } catch ( error ) {
console.log(error);
return undefined; return undefined;
} }
} }
...@@ -43,6 +44,10 @@ class ClientsSharedAssignmentHelper { ...@@ -43,6 +44,10 @@ class ClientsSharedAssignmentHelper {
return Array.from(content.matchAll(regexp), m => m[1])[0]; return Array.from(content.matchAll(regexp), m => m[1])[0];
} }
async getAssignmentByName(name: string): Promise<Assignment | undefined> {
return await this.getAssignment(name);
}
async getAssignmentFromPath(path: string): Promise<Assignment | undefined> { async getAssignmentFromPath(path: string): Promise<Assignment | undefined> {
const fullPath = join(path, "./.git/config"); const fullPath = join(path, "./.git/config");
if (!existsSync(fullPath)) { if (!existsSync(fullPath)) {
...@@ -50,7 +55,6 @@ class ClientsSharedAssignmentHelper { ...@@ -50,7 +55,6 @@ class ClientsSharedAssignmentHelper {
} }
const content = readFileSync(fullPath, 'utf-8'); const content = readFileSync(fullPath, 'utf-8');
const url = await this.extractOriginUrl(content); const url = await this.extractOriginUrl(content);
return await this.getAssignment(url); return await this.getAssignment(url);
} }
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment