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';
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';
const execAsync = util.promisify(exec);
......@@ -21,6 +23,7 @@ const execAsync = util.promisify(exec);
class AssignmentValidator {
private readonly folderAssignment: string;
private readonly doDown: boolean;
private readonly runSonar: boolean;
readonly events: TypedEmitter<AssignmentValidatorEvents> = new TypedEmitter<AssignmentValidatorEvents>();
......@@ -39,9 +42,10 @@ class AssignmentValidator {
private assignmentFile!: AssignmentFile;
private assignment!: Assignment;
constructor(folderAssignment: string, doDown: boolean = false) {
constructor(folderAssignment: string, doDown: boolean = false, runSonar: boolean = false) {
this.folderAssignment = folderAssignment;
this.doDown = doDown;
this.runSonar = runSonar;
this.events.on('logs', (log: string, _error: boolean, displayable: boolean) => {
this.allLogs += log;
......@@ -133,17 +137,17 @@ class AssignmentValidator {
private async checkAssignment() {
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 ) {
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;
} else {
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 {
}
/**
* 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)
* @private
*/
......@@ -335,6 +386,8 @@ class AssignmentValidator {
this.dockerfilesValidation();
await this.sonarAnalysis();
await this.runAssignment();
this.finished(true, 0);
......
......@@ -30,10 +30,11 @@ class ClientsSharedAssignmentHelper {
}));
}
private async getAssignment(url: string): Promise<Assignment | undefined> {
private async getAssignment(nameOrUrl: string): Promise<Assignment | undefined> {
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 ) {
console.log(error);
return undefined;
}
}
......@@ -43,6 +44,10 @@ class ClientsSharedAssignmentHelper {
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> {
const fullPath = join(path, "./.git/config");
if (!existsSync(fullPath)) {
......@@ -50,7 +55,6 @@ class ClientsSharedAssignmentHelper {
}
const content = readFileSync(fullPath, 'utf-8');
const url = await this.extractOriginUrl(content);
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