Select Git revision
app.ts 7.78 KiB
// Read from the .env file
// ATTENTION : This lines MUST be the first of this file (except for the path import)
const path = require('node:path');
require('dotenv').config({ path: path.join(__dirname, '../.env') });
require('./shared/helpers/TypeScriptExtensions'); // ATTENTION : This line MUST be the second of this file
import ClientsSharedConfig from './sharedByClients/config/ClientsSharedConfig';
import Styles from './types/Style';
import Icon from './sharedByClients/types/Icon';
import RecursiveFilesStats from './shared/helpers/recursiveFilesStats/RecursiveFilesStats';
import Toolbox from './shared/helpers/Toolbox';
import ExerciceCheckerError from './shared/types/Dojo/ExerciceCheckerError';
import { exec } from 'child_process';
import util from 'util';
import fs from 'fs-extra';
import HttpManager from './managers/HttpManager';
import DojoBackendManager from './managers/DojoBackendManager';
import Config from './config/Config';
import ArchiveHelper from './shared/helpers/ArchiveHelper';
import ExerciceDockerCompose from './sharedByClients/helpers/Dojo/ExerciceDockerCompose';
import ExerciceResultsValidation from './sharedByClients/helpers/Dojo/ExerciceResultsValidation';
import ExerciceEnonce from './sharedByClients/models/ExerciceEnonce';
import ClientsSharedExerciceHelper from './sharedByClients/helpers/Dojo/ClientsSharedExerciceHelper';
(async () => {
const execAsync = util.promisify(exec);
HttpManager.registerAxiosInterceptor();
console.log(Styles.APP_NAME(Config.appName));
let exerciceEnonce: ExerciceEnonce | undefined;
let exerciceDockerCompose: ExerciceDockerCompose;
let exerciceResultsValidation: ExerciceResultsValidation;
/*
//////////////////////////////////////////////////////////////////////////////////////////////////////////// Step 1:
- Read the dojo enonce file from the enonce repository
- Download immutables files (maybe throw or show an error if the files have been modified ?)
*/
{
console.log(Styles.INFO(`${ Icon.INFO }️ Checking the exercice's enonce and his immutable files`));
exerciceEnonce = await DojoBackendManager.getExerciceEnonce();
if ( !exerciceEnonce ) {
console.error(Styles.ERROR(`${ Icon.ERROR } Error while getting the exercice's enonce`));
process.exit(ExerciceCheckerError.EXERCICE_ENONCE_GET_ERROR);
}
exerciceEnonce.immutable.forEach(immutableFile => {
const filePath = path.join(Config.folders.project, immutableFile.file_path);
fs.mkdirSync(path.dirname(filePath), { recursive: true });
fs.writeFileSync(filePath, immutableFile.content, { encoding: 'base64' });
});
}
/*
//////////////////////////////////////////////////////////////////////////////////////////////////////////// Step 2:
- Get override of docker-compose file (for override the volume by a bind mount to the results folder shared between dind and the host)
- Run docker-compose file
- Get logs from linked services
*/
{
const composeOverridePath: string = path.join(Config.folders.project, 'docker-compose-override.yml');
const composeOverride = fs.readFileSync(path.join(__dirname, '../assets/docker-compose-override.yml'), 'utf8').replace('{{VOLUME_NAME}}', exerciceEnonce.enonceFile.result.volume).replace('{{MOUNT_PATH}}', Config.folders.resultsExercice);
fs.writeFileSync(composeOverridePath, composeOverride);
exerciceDockerCompose = new ExerciceDockerCompose(ClientsSharedConfig.dockerCompose.projectName, exerciceEnonce.enonceFile, Config.folders.project, [ composeOverridePath ]);
try {
await new Promise<void>((resolve, reject) => {
exerciceDockerCompose.events.on('step', (name: string, message: string) => {
console.log(Styles.INFO(`${ Icon.INFO } ${ message }`));
});
exerciceDockerCompose.events.on('endStep', (stepName: string, message: string, error: boolean) => {
if ( error ) {
console.error(Styles.ERROR(`${ Icon.ERROR } ${ message }`));
}
});
exerciceDockerCompose.events.on('finished', (success: boolean, exitCode: number) => {
success ? resolve() : reject();
});
exerciceDockerCompose.run();
});
} catch ( error ) { }
fs.rmSync(composeOverridePath);
fs.writeFileSync(path.join(Config.folders.resultsDojo, 'dockerComposeLogs.txt'), exerciceDockerCompose.allLogs);
if ( !exerciceDockerCompose.success ) {
console.error(Styles.ERROR(`${ Icon.ERROR } Execution logs are available in artifacts`));
process.exit(exerciceDockerCompose.exitCode);
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////// Step 3: Check content requirements and content size
{
exerciceResultsValidation = new ExerciceResultsValidation(Config.folders.resultsDojo, Config.folders.resultsExercice);
try {
await new Promise<void>((resolve) => {
exerciceResultsValidation.events.on('step', (name: string, message: string) => {
console.log(Styles.INFO(`${ Icon.INFO } ${ message }`));
});
exerciceResultsValidation.events.on('endStep', (stepName: string, message: string, error: boolean) => {
if ( error ) {
console.error(Styles.ERROR(`${ Icon.ERROR } ${ message }`));
}
});
exerciceResultsValidation.events.on('finished', (success: boolean, exitCode: number) => {
if ( !success ) {
process.exit(exitCode);
}
resolve();
});
exerciceResultsValidation.run();
});
} catch ( error ) { }
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////// Step 4: Upload results
{
try {
console.log(Styles.INFO(`${ Icon.INFO } Uploading results to the dojo server`));
const commit: any = {};
Toolbox.getKeysWithPrefix(process.env, 'CI_COMMIT_').forEach(key => {
commit[Toolbox.snakeToCamel(key.replace('CI_COMMIT_', ''))] = process.env[key];
});
const files = await RecursiveFilesStats.explore(Config.folders.resultsVolume, {
replacePathByRelativeOne: true,
liteStats : true
});
await DojoBackendManager.sendResults(exerciceDockerCompose.exitCode, commit, exerciceResultsValidation.exerciceResults!, files, await ArchiveHelper.getBase64(Config.folders.resultsVolume));
} catch ( error ) {
console.error(Styles.ERROR(`${ Icon.ERROR } Error while uploading the results`));
console.error(JSON.stringify(error));
process.exit(ExerciceCheckerError.UPLOAD);
}
}
/*
//////////////////////////////////////////////////////////////////////////////////////////////////////////// Step 5:
- Display results
- Exit with container exit code
*/
{
ClientsSharedExerciceHelper.displayExecutionResults(exerciceResultsValidation.exerciceResults!, exerciceDockerCompose.exitCode, Styles, `\n\n${ Icon.INFO }️ More detailed logs and resources may be available in artifacts`);
process.exit(exerciceDockerCompose.exitCode);
}
})();