Skip to content
Snippets Groups Projects
Commit 2b2b2376 authored by michael.minelli's avatar michael.minelli
Browse files

Sonar => Resolve issues

parent 4efff1c5
No related branches found
No related tags found
No related merge requests found
......@@ -17,6 +17,9 @@ const execAsync = util.promisify(exec);
class AssignmentValidator {
private readonly folderAssignment: string;
private readonly doDown: boolean;
readonly events: TypedEmitter<AssignmentValidatorEvents> = new TypedEmitter<AssignmentValidatorEvents>();
public displayableLogs: string = '';
......@@ -30,7 +33,13 @@ class AssignmentValidator {
private currentStep: string = 'NOT_RUNNING';
private currentSubStep: string = 'NOT_RUNNING';
constructor(private folderAssignment: string) {
private dockerComposeFile!: DojoDockerCompose;
private assignmentFile!: AssignmentFile;
constructor(folderAssignment: string, doDown: boolean = false) {
this.folderAssignment = folderAssignment;
this.doDown = doDown;
this.events.on('logs', (log: string, _error: boolean, displayable: boolean) => {
this.allLogs += log;
this.displayableLogs += displayable ? log : '';
......@@ -77,212 +86,224 @@ class AssignmentValidator {
this.finished(false, code);
}
run(doDown: boolean = false) {
(async () => {
let dockerComposeFile: DojoDockerCompose;
let assignmentFile: AssignmentFile;
/**
* Step 1: Check requirements
* - Check if Docker daemon is running
* - Check if required files exists
* @private
*/
private async checkRequirements() {
this.newStep('REQUIREMENTS_CHECKING', 'Please wait while we are checking requirements...');
/*
//////////////////////////////////////////////////////////////////////////////////////////////////////////// Step 1: Check requirements
- Check if Docker daemon is running
- Check if required files exists
*/
{
this.newStep('REQUIREMENTS_CHECKING', 'Please wait while we are checking requirements...');
// Check requirements
this.newSubStep('DOCKER_RUNNING', 'Checking if Docker daemon is running');
try {
await execAsync(`docker ps`);
} catch ( error ) {
this.emitError(`Docker daemon isn't running`, `Some requirements are not satisfied.`, AssignmentCheckerError.DOCKER_DAEMON_NOT_RUNNING);
throw new Error();
}
this.endSubStep('Docker daemon is running', false);
// Check requirements
this.newSubStep('DOCKER_RUNNING', 'Checking if Docker daemon is running');
try {
await execAsync(`docker ps`);
} catch ( error ) {
this.emitError(`Docker daemon isn't running`, `Some requirements are not satisfied.`, AssignmentCheckerError.DOCKER_DAEMON_NOT_RUNNING);
return;
}
this.endSubStep('Docker daemon is running', false);
// Check if required files exists
this.newSubStep('REQUIRED_FILES_EXISTS', 'Checking if required files exists');
const files = fs.readdirSync(this.folderAssignment);
const missingFiles = ClientsSharedConfig.assignment.neededFiles.map((file: string): [ string, boolean ] => [ file, files.includes(file) ]).filter((file: [ string, boolean ]) => !file[1]);
if ( missingFiles.length > 0 ) {
this.emitError(`The exercise folder is missing the following files: ${ missingFiles.map((file: [ string, boolean ]) => file[0]).join(', ') }`, 'Some requirements are not satisfied', AssignmentCheckerError.REQUIRED_FILES_MISSING);
throw new Error();
}
this.endSubStep('All required files exists', false);
// Check if required files exists
this.newSubStep('REQUIRED_FILES_EXISTS', 'Checking if required files exists');
const files = fs.readdirSync(this.folderAssignment);
const missingFiles = ClientsSharedConfig.assignment.neededFiles.map((file: string): [ string, boolean ] => [ file, files.includes(file) ]).filter((file: [ string, boolean ]) => !file[1]);
if ( missingFiles.length > 0 ) {
this.emitError(`The exercise folder is missing the following files: ${ missingFiles.map((file: [ string, boolean ]) => file[0]).join(', ') }`, 'Some requirements are not satisfied', AssignmentCheckerError.REQUIRED_FILES_MISSING);
return;
}
this.endSubStep('All required files exists', false);
this.endStep('All requirements are satisfied', false);
}
/**
* Step 2: dojo_assignment.json file validation
* - Structure validation
* - Immutable files validation (Check if exists and if the given type is correct)
* @private
*/
private dojoAssignmentFileValidation() {
this.newStep('ASSIGNMENT_FILE_VALIDATION', 'Please wait while we are validating dojo_assignment.json file...');
const assignmentFileValidationError = `${ ClientsSharedConfig.assignment.filename } file is invalid`;
// Structure validation
this.newSubStep('ASSIGNMENT_FILE_SCHEMA_VALIDATION', 'Validating dojo_assignment.json file schema');
const validationResults = SharedAssignmentHelper.validateDescriptionFile(path.join(this.folderAssignment, ClientsSharedConfig.assignment.filename));
if ( !validationResults.isValid ) {
this.emitError(`dojo_assignment.json file schema is invalid.\nHere are the errors:\n${ validationResults.error }`, assignmentFileValidationError, AssignmentCheckerError.ASSIGNMENT_FILE_SCHEMA_ERROR);
throw new Error();
}
this.assignmentFile = validationResults.content!;
this.endSubStep('dojo_assignment.json file schema is valid', false);
// Immutable files validation (Check if exists and if the given type is correct)
this.newSubStep('ASSIGNMENT_FILE_IMMUTABLES_VALIDATION', 'Validating immutable files');
for ( const immutable of validationResults.content!.immutable ) {
const immutablePath = path.join(this.folderAssignment, immutable.path);
if ( !fs.existsSync(immutablePath) ) {
this.emitError(`Immutable path not found: ${ immutable.path }`, assignmentFileValidationError, AssignmentCheckerError.IMMUTABLE_PATH_NOT_FOUND);
throw new Error();
}
this.endStep('All requirements are satisfied', false);
const isDirectory = fs.lstatSync(immutablePath).isDirectory();
if ( isDirectory && !immutable.isDirectory ) {
this.emitError(`Immutable (${ immutable.path }) is declared as a file but is a directory.`, assignmentFileValidationError, AssignmentCheckerError.IMMUTABLE_PATH_IS_NOT_DIRECTORY);
throw new Error();
} else if ( !isDirectory && immutable.isDirectory === true ) {
this.emitError(`Immutable (${ immutable.path }) is declared as a directory but is a file.`, assignmentFileValidationError, AssignmentCheckerError.IMMUTABLE_PATH_IS_DIRECTORY);
throw new Error();
}
}
this.endSubStep('Immutable files are valid', false);
/*
//////////////////////////////////////////////////////////////////////////////////////////////////////////// Step 2: dojo_assignment.json file validation
- Structure validation
- Immutable files validation (Check if exists and if the given type is correct)
*/
{
this.newStep('ASSIGNMENT_FILE_VALIDATION', 'Please wait while we are validating dojo_assignment.json file...');
// Structure validation
this.newSubStep('ASSIGNMENT_FILE_SCHEMA_VALIDATION', 'Validating dojo_assignment.json file schema');
const validationResults = SharedAssignmentHelper.validateDescriptionFile(path.join(this.folderAssignment, ClientsSharedConfig.assignment.filename));
if ( !validationResults.isValid ) {
this.emitError(`dojo_assignment.json file schema is invalid.\nHere are the errors:\n${ validationResults.error }`, 'dojo_assignment.json file is invalid', AssignmentCheckerError.ASSIGNMENT_FILE_SCHEMA_ERROR);
return;
}
assignmentFile = validationResults.content!;
this.endSubStep('dojo_assignment.json file schema is valid', false);
// Immutable files validation (Check if exists and if the given type is correct)
this.newSubStep('ASSIGNMENT_FILE_IMMUTABLES_VALIDATION', 'Validating immutable files');
for ( const immutable of validationResults.content!.immutable ) {
const immutablePath = path.join(this.folderAssignment, immutable.path);
if ( !fs.existsSync(immutablePath) ) {
this.emitError(`Immutable path not found: ${ immutable.path }`, 'dojo_assignment.json file is invalid', AssignmentCheckerError.IMMUTABLE_PATH_NOT_FOUND);
return;
}
const isDirectory = fs.lstatSync(immutablePath).isDirectory();
if ( isDirectory && !immutable.isDirectory ) {
this.emitError(`Immutable (${ immutable.path }) is declared as a file but is a directory.`, 'dojo_assignment.json file is invalid', AssignmentCheckerError.IMMUTABLE_PATH_IS_NOT_DIRECTORY);
return;
} else if ( !isDirectory && immutable.isDirectory === true ) {
this.emitError(`Immutable (${ immutable.path }) is declared as a directory but is a file.`, 'dojo_assignment.json file is invalid', AssignmentCheckerError.IMMUTABLE_PATH_IS_DIRECTORY);
return;
}
}
this.endSubStep('Immutable files are valid', false);
this.endStep('dojo_assignment.json file is valid', false);
}
this.endStep('dojo_assignment.json file is valid', false);
}
/**
* Step 3: Docker Compose file validation
* - Global validation
* - Validation of the containers and volumes named in dojo_assignment.json
* @private
*/
private async dockerComposeFileValidation() {
this.newStep('DOCKER_COMPOSE_VALIDATION', 'Please wait while we are validating docker compose file...');
const composeFileValidationError = `Docker compose file is invalid`;
// Global validation
this.newSubStep('DOCKER_COMPOSE_STRUCTURE_VALIDATION', 'Docker compose file structure validation');
try {
this.dockerComposeFile = YAML.parse(fs.readFileSync(path.join(this.folderAssignment, 'docker-compose.yml'), 'utf8')) as DojoDockerCompose;
} catch ( error ) {
this.emitError(`Docker compose file yaml structure is invalid.`, composeFileValidationError, AssignmentCheckerError.COMPOSE_FILE_YAML_ERROR);
throw new Error();
}
try {
await new Promise<void>((resolve, reject) => {
const dockerComposeValidation = spawn(`docker compose -f docker-compose.yml config --quiet`, {
cwd : this.folderAssignment,
shell: true
});
dockerComposeValidation.on('exit', code => {
code !== null && code === 0 ? resolve() : reject();
});
});
} catch ( error ) {
this.emitError(`Docker compose file structure is invalid.`, composeFileValidationError, AssignmentCheckerError.COMPOSE_FILE_SCHEMA_ERROR);
throw new Error();
}
this.endSubStep('Docker compose file structure is valid', false);
// Validation of the containers and volumes named in dojo_assignment.json
this.newSubStep('DOCKER_COMPOSE_CONTENT_VALIDATION', 'Docker compose file content validation');
if ( !(this.assignmentFile.result.container in this.dockerComposeFile.services) ) {
this.emitError(`Container specified in ${ ClientsSharedConfig.assignment.filename } is missing from compose file.`, composeFileValidationError, AssignmentCheckerError.COMPOSE_FILE_CONTAINER_MISSING);
throw new Error();
}
if ( this.assignmentFile.result.volume && (!this.dockerComposeFile.volumes || !(this.assignmentFile.result.volume in this.dockerComposeFile.volumes)) ) {
this.emitError(`Volume specified in ${ ClientsSharedConfig.assignment.filename } is missing from compose file.`, composeFileValidationError, AssignmentCheckerError.COMPOSE_FILE_VOLUME_MISSING);
throw new Error();
}
this.endSubStep('Docker compose file content is valid', false);
this.endStep('Docker compose file is valid', false);
}
/**
* Step 4: Dockerfiles validation
* - Check if file exists
* - TODO - Dockerfile structure linter - Issue #51 - https://github.com/hadolint/hadolint
* @private
*/
private dockerfilesValidation() {
this.newStep('DOCKERFILE_VALIDATION', 'Please wait while we are validating dockerfiles...');
/*
//////////////////////////////////////////////////////////////////////////////////////////////////////////// Step 3: Docker Compose file validation
- Global validation
- Validation of the containers and volumes named in dojo_assignment.json
*/
{
this.newStep('DOCKER_COMPOSE_VALIDATION', 'Please wait while we are validating docker compose file...');
// Global validation
this.newSubStep('DOCKER_COMPOSE_STRUCTURE_VALIDATION', 'Docker compose file structure validation');
try {
dockerComposeFile = YAML.parse(fs.readFileSync(path.join(this.folderAssignment, 'docker-compose.yml'), 'utf8')) as DojoDockerCompose;
} catch ( error ) {
this.emitError(`Docker compose file yaml structure is invalid.`, 'Docker compose file is invalid', AssignmentCheckerError.COMPOSE_FILE_YAML_ERROR);
return;
}
try {
await new Promise<void>((resolve, reject) => {
const dockerComposeValidation = spawn(`docker compose -f docker-compose.yml config --quiet`, {
cwd : this.folderAssignment,
shell: true
});
dockerComposeValidation.on('exit', (code) => {
code !== null && code == 0 ? resolve() : reject();
});
});
} catch ( error ) {
this.emitError(`Docker compose file structure is invalid.`, 'Docker compose file is invalid', AssignmentCheckerError.COMPOSE_FILE_SCHEMA_ERROR);
return;
}
this.endSubStep('Docker compose file structure is valid', false);
// Validation of the containers and volumes named in dojo_assignment.json
this.newSubStep('DOCKER_COMPOSE_CONTENT_VALIDATION', 'Docker compose file content validation');
if ( !(assignmentFile.result.container in dockerComposeFile!.services) ) {
this.emitError(`Container specified in ${ ClientsSharedConfig.assignment.filename } is missing from compose file.`, 'Docker compose file is invalid', AssignmentCheckerError.COMPOSE_FILE_CONTAINER_MISSING);
return;
}
if ( assignmentFile.result.volume && (!dockerComposeFile!.volumes || !(assignmentFile.result.volume in dockerComposeFile!.volumes)) ) {
this.emitError(`Volume specified in ${ ClientsSharedConfig.assignment.filename } is missing from compose file.`, 'Docker compose file is invalid', AssignmentCheckerError.COMPOSE_FILE_VOLUME_MISSING);
return;
}
this.endSubStep('Docker compose file content is valid', false);
this.endStep('Docker compose file is valid', false);
}
this.newSubStep('DOCKERFILE_EXIST', 'Docker compose file content validation');
const dockerfilesPaths = Object.values(this.dockerComposeFile.services).filter(service => service.build).map(service => path.join(this.folderAssignment, service.build!.context ?? '', service.build!.dockerfile!));
const filesNotFound = dockerfilesPaths.filter(dockerfilePath => !fs.existsSync(dockerfilePath));
if ( filesNotFound.length > 0 ) {
this.emitError(`Dockerfiles not found: ${ filesNotFound.join(', ') }`, 'Dockerfiles are invalid', AssignmentCheckerError.DOCKERFILE_NOT_FOUND);
throw new Error();
}
this.endSubStep('Docker compose file content is valid', false);
/*
//////////////////////////////////////////////////////////////////////////////////////////////////////////// Step 4: Dockerfiles validation
- Check if file exists
- TODO - Dockerfile structure linter - Issue #51 - https://github.com/hadolint/hadolint
*/
{
this.newStep('DOCKERFILE_VALIDATION', 'Please wait while we are validating dockerfiles...');
this.endStep('Dockerfiles are valid', false);
}
this.newSubStep('DOCKERFILE_EXIST', 'Docker compose file content validation');
const dockerfilesPaths = Object.values(dockerComposeFile!.services).filter((service) => service.build).map((service) => path.join(this.folderAssignment, service.build!.context ?? '', service.build!.dockerfile!));
const filesNotFound = dockerfilesPaths.filter((dockerfilePath) => !fs.existsSync(dockerfilePath));
if ( filesNotFound.length > 0 ) {
this.emitError(`Dockerfiles not found: ${ filesNotFound.join(', ') }`, 'Dockerfiles are invalid', AssignmentCheckerError.DOCKERFILE_NOT_FOUND);
return;
}
this.endSubStep('Docker compose file content is valid', false);
/**
* Step 5: 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
*/
private async runAssignment() {
this.newStep('ASSIGNMENT_RUN', 'Please wait while we are running the assignment...');
this.endStep('Dockerfiles are valid', false);
}
const exerciseDockerCompose = new ExerciseDockerCompose(ClientsSharedConfig.dockerCompose.projectName, this.assignmentFile, this.folderAssignment);
try {
await new Promise<void>((resolve, reject) => {
exerciseDockerCompose.events.on('logs', (log: string, error: boolean, displayable: boolean) => {
this.log(log, error, displayable);
});
/*
//////////////////////////////////////////////////////////////////////////////////////////////////////////// Step 5: 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)
*/
{
this.newStep('ASSIGNMENT_RUN', 'Please wait while we are running the assignment...');
exerciseDockerCompose.events.on('step', (name: string, message: string) => {
this.newSubStep(name, message);
});
exerciseDockerCompose.events.on('endStep', (_stepName: string, message: string, error: boolean) => {
this.endSubStep(message, error);
});
const exerciseDockerCompose = new ExerciseDockerCompose(ClientsSharedConfig.dockerCompose.projectName, assignmentFile, this.folderAssignment);
exerciseDockerCompose.events.on('finished', (_success: boolean, exitCode: number) => {
exitCode !== 0 ? resolve() : reject();
});
try {
await new Promise<void>((resolve, reject) => {
exerciseDockerCompose.events.on('logs', (log: string, error: boolean, displayable: boolean) => {
this.log(log, error, displayable);
});
exerciseDockerCompose.run(this.doDown);
});
} catch ( error ) {
this.fatalErrorMessage = 'Assignment is already solved';
this.endStep(this.fatalErrorMessage, true);
this.finished(false, AssignmentCheckerError.COMPOSE_RUN_SUCCESSFULLY);
throw new Error();
}
exerciseDockerCompose.events.on('step', (name: string, message: string) => {
this.newSubStep(name, message);
});
exerciseDockerCompose.events.on('endStep', (stepName: string, message: string, error: boolean) => {
this.endSubStep(message, error);
});
this.endStep('Assignment run successfully', false);
}
exerciseDockerCompose.events.on('finished', (success: boolean, exitCode: number) => {
exitCode != 0 ? resolve() : reject();
});
run() {
(async () => {
try {
await this.checkRequirements();
exerciseDockerCompose.run(doDown);
});
} catch ( error ) {
this.fatalErrorMessage = 'Assignment is already solved';
this.endStep(this.fatalErrorMessage, true);
this.finished(false, AssignmentCheckerError.COMPOSE_RUN_SUCCESSFULLY);
return;
}
this.dojoAssignmentFileValidation();
await this.dockerComposeFileValidation();
this.endStep('Assignment run successfully', false);
}
this.dockerfilesValidation();
await this.runAssignment();
this.finished(true, 0);
this.finished(true, 0);
} catch ( error ) {
return;
}
})();
}
}
......
......@@ -6,7 +6,8 @@ import AssignmentValidator from './AssignmentValidator';
class ClientsSharedAssignmentHelper {
displayExecutionResults(validator: AssignmentValidator, successMessage: string, Style: { INFO: chalk.Chalk, SUCCESS: chalk.Chalk, FAILURE: chalk.Chalk }) {
const finalLogGlobalResult = `${ Style.INFO('Global result') } : ${ validator.success ? Style.SUCCESS(`${ Icon.SUCCESS } Success`) : Style.FAILURE(`${ Icon.FAILURE } Failure`) }`;
const globalResult = validator.success ? Style.SUCCESS(`${ Icon.SUCCESS } Success`) : Style.FAILURE(`${ Icon.FAILURE } Failure`);
const finalLogGlobalResult = `${ Style.INFO('Global result') } : ${ globalResult }`;
const finalLogSuccessMessage = validator.success ? `${ successMessage }` : '';
const finalLogErrorMessage = !validator.success ? `${ Style.INFO('Error message') } :\n${ Style.FAILURE(validator.fatalErrorMessage) }` : '';
......
......@@ -5,27 +5,31 @@ import Icon from '../../../shared/types/Icon';
class ClientsSharedExerciseHelper {
displayExecutionResults(exerciseResults: ExerciseResultsFile, containerExitCode: number, Style: { INFO: chalk.Chalk, SUCCESS: chalk.Chalk, FAILURE: chalk.Chalk }, additionalText: string = '') {
const finalLogGlobalResult = `${ Style.INFO('Global result: ') }${ exerciseResults.success ? Style.SUCCESS(`${ Icon.SUCCESS } Success`) : Style.FAILURE(`${ Icon.FAILURE } Failure`) }`;
const finalLogExecutionExitCode = `${ Style.INFO('Execution exit code: ') }${ (containerExitCode == 0 ? Style.SUCCESS : Style.FAILURE)(containerExitCode) }`;
private getOtherInformations(exerciseResults: ExerciseResultsFile, Style: { INFO: chalk.Chalk, SUCCESS: chalk.Chalk, FAILURE: chalk.Chalk }) {
return exerciseResults.otherInformations ? [ '', ...exerciseResults.otherInformations.map(information => {
const informationTitle = Style.INFO(`${ information.icon && information.icon !== '' ? Icon[information.icon] + ' ' : '' }${ information.name }: `);
const informationItems = typeof information.itemsOrInformations == 'string' ? information.itemsOrInformations : information.itemsOrInformations.map(item => `- ${ item }`).join('\n');
const finalLogResultNumbers = exerciseResults.successfulTests || exerciseResults.failedTests ? `\n\n${ Style.INFO(Style.SUCCESS('Tests passed: ')) }${ exerciseResults.successfulTests ?? '--' }\n${ Style.INFO(Style.FAILURE('Tests failed: ')) }${ exerciseResults.failedTests ?? '--' }` : '';
return `${ informationTitle }\n${ informationItems }`;
}) ].join('\n\n') : '';
}
const finalLogSuccessResultDetails = (exerciseResults.successfulTestsList ?? []).map(testName => `- ${ Icon.SUCCESS } ${ testName }`).join('\n');
const finalLogFailedResultDetails = (exerciseResults.failedTestsList ?? []).map(testName => `- ${ Icon.FAILURE } ${ testName }`).join('\n');
const finalLogResultDetails = exerciseResults.successfulTestsList || exerciseResults.failedTestsList ? `\n\n${ Style.INFO('Tests: ') }${ finalLogSuccessResultDetails != '' ? '\n' + finalLogSuccessResultDetails : '' }${ finalLogFailedResultDetails != '' ? '\n' + finalLogFailedResultDetails : '' }` : '';
displayExecutionResults(exerciseResults: ExerciseResultsFile, containerExitCode: number, Style: { INFO: chalk.Chalk, SUCCESS: chalk.Chalk, FAILURE: chalk.Chalk }, additionalText: string = '') {
const globalResult = exerciseResults.success ? Style.SUCCESS(`${ Icon.SUCCESS } Success`) : Style.FAILURE(`${ Icon.FAILURE } Failure`);
const finalLogGlobalResult = `${ Style.INFO('Global result: ') }${ globalResult }`;
const finalLogExecutionExitCode = `${ Style.INFO('Execution exit code: ') }${ (containerExitCode === 0 ? Style.SUCCESS : Style.FAILURE)(containerExitCode) }`;
let finalLogInformations = '';
if ( exerciseResults.otherInformations ) {
finalLogInformations = [ '', ...exerciseResults.otherInformations.map(information => {
const informationTitle = Style.INFO(`${ information.icon && information.icon != '' ? Icon[information.icon] + ' ' : '' }${ information.name }: `);
const informationItems = typeof information.itemsOrInformations == 'string' ? information.itemsOrInformations : information.itemsOrInformations.map(item => `- ${ item }`).join('\n');
const finalLogResultNumbers = exerciseResults.successfulTests || exerciseResults.failedTests ? `\n\n${ Style.INFO(Style.SUCCESS('Tests passed: ')) }${ exerciseResults.successfulTests ?? '--' }\n${ Style.INFO(Style.FAILURE('Tests failed: ')) }${ exerciseResults.failedTests ?? '--' }` : '';
return `${ informationTitle }\n${ informationItems }`;
}) ].join('\n\n');
}
let finalLogSuccessResultDetails = (exerciseResults.successfulTestsList ?? []).map(testName => `- ${ Icon.SUCCESS } ${ testName }`).join('\n');
finalLogSuccessResultDetails = finalLogSuccessResultDetails !== '' ? '\n' + finalLogSuccessResultDetails : '';
let finalLogFailedResultDetails = (exerciseResults.failedTestsList ?? []).map(testName => `- ${ Icon.FAILURE } ${ testName }`).join('\n');
finalLogFailedResultDetails = finalLogFailedResultDetails !== '' ? '\n' + finalLogFailedResultDetails : '';
const finalLogResultDetails = exerciseResults.successfulTestsList || exerciseResults.failedTestsList ? `\n\n${ Style.INFO('Tests: ') }${ finalLogSuccessResultDetails }${ finalLogFailedResultDetails }` : '';
const finalLogInformations = this.getOtherInformations(exerciseResults, Style);
console.log(boxen(`${ finalLogGlobalResult }\n\n${ finalLogExecutionExitCode }${ finalLogResultNumbers }${ finalLogResultDetails }${ finalLogInformations }${ additionalText }`, {
title : 'Results',
......
......@@ -18,7 +18,17 @@ class ExerciseDockerCompose {
private currentStep: string = 'NOT_RUNNING';
constructor(private projectName: string, private assignmentFile: AssignmentFile, private executionFolder: string, private composeFileOverride: Array<string> = []) {
private readonly projectName: string;
private readonly assignmentFile: AssignmentFile;
private readonly executionFolder: string;
private readonly composeFileOverride: Array<string> = [];
constructor(projectName: string, assignmentFile: AssignmentFile, executionFolder: string, composeFileOverride: Array<string> = []) {
this.projectName = projectName;
this.assignmentFile = assignmentFile;
this.executionFolder = executionFolder;
this.composeFileOverride = composeFileOverride;
this.events.on('logs', (log: string, _error: boolean, displayable: boolean) => {
this.allLogs += log;
this.displayableLogs += displayable ? log : '';
......@@ -49,15 +59,15 @@ class ExerciseDockerCompose {
}
private registerChildProcess(childProcess: ChildProcessWithoutNullStreams, resolve: (value: (number | PromiseLike<number>)) => void, reject: (reason?: unknown) => void, displayable: boolean, rejectIfCodeIsNotZero: boolean) {
childProcess.stdout.on('data', (data) => {
childProcess.stdout.on('data', data => {
this.log(data.toString(), false, displayable);
});
childProcess.stderr.on('data', (data) => {
childProcess.stderr.on('data', data => {
this.log(data.toString(), true, displayable);
});
childProcess.on('exit', (code) => {
childProcess.on('exit', code => {
code === null || (rejectIfCodeIsNotZero && code !== 0) ? reject(code) : resolve(code);
});
}
......@@ -66,7 +76,8 @@ class ExerciseDockerCompose {
(async () => {
let containerExitCode: number = -1;
const dockerComposeCommand = `docker compose --project-name ${ this.projectName } --progress plain --file docker-compose.yml ${ this.composeFileOverride.map((file) => `--file "${ file }"`).join(' ') }`;
const filesOverrideArguments = this.composeFileOverride.map(file => `--file "${ file }"`).join(' ');
const dockerComposeCommand = `docker compose --project-name ${ this.projectName } --progress plain --file docker-compose.yml ${ filesOverrideArguments }`;
// Run the service
{
......
......@@ -17,7 +17,15 @@ class ExerciseResultsSanitizerAndValidator {
private resultsFilePath: string = '';
constructor(private folderResultsDojo: string, private folderResultsExercise: string, private containerExitCode: number) { }
private readonly folderResultsDojo: string;
private readonly folderResultsExercise: string;
private readonly containerExitCode: number;
constructor(folderResultsDojo: string, folderResultsExercise: string, containerExitCode: number) {
this.folderResultsDojo = folderResultsDojo;
this.folderResultsExercise = folderResultsExercise;
this.containerExitCode = containerExitCode;
}
private async resultsFileSanitization() {
this.events.emit('step', 'RESULTS_FILE_SANITIZATION', 'Sanitizing results file');
......@@ -33,11 +41,11 @@ class ExerciseResultsSanitizerAndValidator {
this.events.emit('endStep', 'RESULTS_FILE_SANITIZATION', 'Results file sanitized', false);
}
private async resultsFileProvided(path: string): Promise<boolean> {
private async resultsFileProvided(resultFilePath: string): Promise<boolean> {
// Results file schema validation
{
this.events.emit('step', 'VALIDATE_RESULTS_FILE', 'Validating results file schema');
const validationResults = Json5FileValidator.validateFile(ExerciseResultsFile, path);
const validationResults = Json5FileValidator.validateFile(ExerciseResultsFile, resultFilePath);
if ( !validationResults.isValid ) {
this.events.emit('endStep', 'VALIDATE_RESULTS_FILE', `Results file is not valid. Here are the errors :\n${ validationResults.error }`, true);
this.events.emit('finished', false, ExerciseCheckerError.EXERCISE_RESULTS_FILE_SCHEMA_NOT_VALID);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment