From e98feaf3b57dfea34d9aef040f5447a7dc469d00 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Micha=C3=ABl=20Minelli?= <michael@minelli.me>
Date: Tue, 12 Sep 2023 19:19:38 +0200
Subject: [PATCH] Rename enonce and exercice to assignment and exercise

---
 .gitlab-ci.yml                                | 88 ++++++++++-------
 NodeApp/package.json                          |  2 +-
 NodeApp/src/commander/CommanderApp.ts         | 10 +-
 .../commander/assignment/AssignmentCommand.ts | 25 +++++
 .../AssignmentCreateCommand.ts}               | 32 +++----
 .../assignment/AssignmentPublishCommand.ts    | 10 ++
 .../AssignmentPublishUnpublishCommandBase.ts  | 83 ++++++++++++++++
 .../assignment/AssignmentUnpublishCommand.ts  | 10 ++
 NodeApp/src/commander/enonce/EnonceCommand.ts | 25 -----
 .../commander/enonce/EnoncePublishCommand.ts  | 10 --
 .../EnoncePublishUnpublishCommandBase.ts      | 83 ----------------
 .../enonce/EnonceUnpublishCommand.ts          | 10 --
 .../src/commander/exercice/ExerciceCommand.ts | 23 -----
 .../exercice/ExerciceCreateCommand.ts         | 91 ------------------
 .../src/commander/exercise/ExerciseCommand.ts | 23 +++++
 .../exercise/ExerciseCreateCommand.ts         | 91 ++++++++++++++++++
 .../ExerciseRunCommand.ts}                    | 94 +++++++++----------
 NodeApp/src/config/Config.ts                  | 16 ++--
 NodeApp/src/managers/DojoBackendManager.ts    | 54 +++++------
 19 files changed, 400 insertions(+), 380 deletions(-)
 create mode 100644 NodeApp/src/commander/assignment/AssignmentCommand.ts
 rename NodeApp/src/commander/{enonce/EnonceCreateCommand.ts => assignment/AssignmentCreateCommand.ts} (65%)
 create mode 100644 NodeApp/src/commander/assignment/AssignmentPublishCommand.ts
 create mode 100644 NodeApp/src/commander/assignment/AssignmentPublishUnpublishCommandBase.ts
 create mode 100644 NodeApp/src/commander/assignment/AssignmentUnpublishCommand.ts
 delete mode 100644 NodeApp/src/commander/enonce/EnonceCommand.ts
 delete mode 100644 NodeApp/src/commander/enonce/EnoncePublishCommand.ts
 delete mode 100644 NodeApp/src/commander/enonce/EnoncePublishUnpublishCommandBase.ts
 delete mode 100644 NodeApp/src/commander/enonce/EnonceUnpublishCommand.ts
 delete mode 100644 NodeApp/src/commander/exercice/ExerciceCommand.ts
 delete mode 100644 NodeApp/src/commander/exercice/ExerciceCreateCommand.ts
 create mode 100644 NodeApp/src/commander/exercise/ExerciseCommand.ts
 create mode 100644 NodeApp/src/commander/exercise/ExerciseCreateCommand.ts
 rename NodeApp/src/commander/{exercice/ExerciceRunCommand.ts => exercise/ExerciseRunCommand.ts} (66%)

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 1592254..4c0541b 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,62 +1,82 @@
 variables:
     BIN_FOLDER: /binary
     BIN_NAME: dojo
+    CLI_PROJECT_FOLDER: NodeApp
+    GIT_SUBMODULE_STRATEGY: recursive
+    GIT_SUBMODULE_FORCE_HTTPS: "true"
+    SECURE_FILES_DOWNLOAD_PATH: './'
 
-.macOS-sign_and_pkg:
+.build_cli:
+    script:
+        - cd NodeApp
+        # Download secure files
+        - curl --silent "https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/download-secure-files/-/raw/main/installer" | bash
+        - mv env_$VERSION .env
+        # Install dependencies
+        - npm install
+        - npm run build
+        # Build binaries
+        - npx pkg . -t node18-macos-arm64 --output $BIN_FOLDER/macOS/arm64/$BIN_NAME --no-bytecode --public-packages "*" --public
+        - npx pkg . -t node18-macos-x64 --output $BIN_FOLDER/macOS/x64/$BIN_NAME --no-bytecode --public-packages "*" --public
+        - npx pkg . -t node18-linuxstatic-arm64 --output $BIN_FOLDER/linux/arm64/$BIN_NAME --no-bytecode --public-packages "*" --public
+        - npx pkg . -t node18-linuxstatic-x64 --output $BIN_FOLDER/linux/x64/$BIN_NAME --no-bytecode --public-packages "*" --public
+        - npx pkg . -t node18-win-arm64 --output $BIN_FOLDER/Windows/arm64/$BIN_NAME --no-bytecode --public-packages "*" --public
+        - npx pkg . -t node18-win-x64 --output $BIN_FOLDER/Windows/x64/$BIN_NAME --no-bytecode --public-packages "*" --public
+
+.macos-sign_and_pkg:
     script:
         # Sign excecutable
-        - codesign --force --options=runtime --entitlements ../../../Resources/macApp/Signing/entitlements.plist --sign $SIGN_DEV_ID_APP --keychain $SIGN_LOGIN_KEYCHAIN_PATH --timestamp ${BIN_NAME} 
+        - codesign --force --options=runtime --entitlements ../../../Resources/macApp/Signing/entitlements.plist --sign $SIGN_DEV_ID_APP --keychain $SIGN_LOGIN_KEYCHAIN_PATH --timestamp ${BIN_NAME}
         # Package and notarize the app
         - xcrun notarytool store-credentials --apple-id $SIGN_APPLE_ID --team-id $SIGN_TEAM_ID --password $SIGN_APP_PASSWORD --keychain $SIGN_LOGIN_KEYCHAIN_PATH $SIGN_KEYCHAIN_PROFILE
         - ditto ${BIN_NAME} ${BIN_NAME}_pkg/usr/local/bin/
-        - productbuild --identifier $SIGN_IDENTIFIER --version $APP_VERSION --sign $SIGN_DEV_ID_INST --keychain $SIGN_LOGIN_KEYCHAIN_PATH --timestamp --root ${BIN_NAME}_pkg / ${BIN_NAME}.pkg 
+        - productbuild --identifier $SIGN_IDENTIFIER --version $APP_VERSION --sign $SIGN_DEV_ID_INST --keychain $SIGN_LOGIN_KEYCHAIN_PATH --timestamp --root ${BIN_NAME}_pkg / ${BIN_NAME}.pkg
         - xcrun notarytool submit ${BIN_NAME}.pkg --keychain $SIGN_LOGIN_KEYCHAIN_PATH --keychain-profile $SIGN_KEYCHAIN_PROFILE --wait
         - xcrun stapler staple ${BIN_NAME}.pkg
         #Clean folder
         - rm -Rf ${BIN_NAME}_pkg
 
+stages:
+    - build
+    - sign
 
-image: node:latest
-
-cache:
-    paths:
-        - NodeApp/node_modules/
+default:
+    image: node:latest
+    cache:
+        paths:
+            - $CLI_PROJECT_FOLDER/node_modules/
+    before_script:
+        - apt update
+        - apt install -y jq
+        - >
+            VERSION=$(jq -r .version $CLI_PROJECT_FOLDER/package.json)
 
-stages:
-    - Build_stage
-    - Sign_stage
+test:build:
+    stage: build
+    tags:
+        - build
+    script:
+        - !reference [.build_cli, script]
+    rules:
+        - if: '$CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH && $CI_COMMIT_BRANCH !~ /^v[0-9]+(\.[0-9]+)*$/'
 
-build:
-    stage: Build_stage
+deploy:build:
+    stage: build
     tags:
         - build
-    variables:
-        GIT_SUBMODULE_STRATEGY: recursive
-        GIT_SUBMODULE_FORCE_HTTPS: "true"
-        SECURE_FILES_DOWNLOAD_PATH: './'
     script:
-        - cd NodeApp
-        # Download secure files
-        - curl --silent "https://gitlab.com/gitlab-org/incubation-engineering/mobile-devops/download-secure-files/-/raw/main/installer" | bash
-        - mv env .env
-        # Install dependencies
-        - npm install
-        - npm run build
-        # Build binaries
-        - npx pkg . -t node18-macos-arm64 --output $BIN_FOLDER/macOS/arm64/$BIN_NAME --no-bytecode --public-packages "*" --public
-        - npx pkg . -t node18-macos-x64 --output $BIN_FOLDER/macOS/x64/$BIN_NAME --no-bytecode --public-packages "*" --public
-        - npx pkg . -t node18-linuxstatic-arm64 --output $BIN_FOLDER/linux/arm64/$BIN_NAME --no-bytecode --public-packages "*" --public
-        - npx pkg . -t node18-linuxstatic-x64 --output $BIN_FOLDER/linux/x64/$BIN_NAME --no-bytecode --public-packages "*" --public
-        - npx pkg . -t node18-win-arm64 --output $BIN_FOLDER/Windows/arm64/$BIN_NAME --no-bytecode --public-packages "*" --public
-        - npx pkg . -t node18-win-x64 --output $BIN_FOLDER/Windows/x64/$BIN_NAME --no-bytecode --public-packages "*" --public
+        - !reference [.build_cli, script]
     artifacts:
         untracked: true
         paths:
             - $BIN_FOLDER/*
         expire_in: 1 hour
+    rules:
+        - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH =~ /^v[0-9]+(\.[0-9]+)*$/'
 
 sign_macos:
-    stage: Sign_stage
+    stage: sign
+    needs: ["deploy:build"]
     tags:
         - macos_signing
     dependencies:
@@ -64,9 +84,9 @@ sign_macos:
     script:        
         - security unlock-keychain -p $SIGN_KEYCHAIN_PASSWORD $SIGN_LOGIN_KEYCHAIN_PATH
         - cd $BIN_FOLDER/macOS/arm64
-        - !reference [.macOS-sign_and_pkg, script]
+        - !reference [.macos-sign_and_pkg, script]
         - cd $BIN_FOLDER/macOS/x64
-        - !reference [.macOS-sign_and_pkg, script]
+        - !reference [.macos-sign_and_pkg, script]
     artifacts:
         paths:
             - $BIN_FOLDER/*
diff --git a/NodeApp/package.json b/NodeApp/package.json
index 6b739f9..eb09505 100644
--- a/NodeApp/package.json
+++ b/NodeApp/package.json
@@ -1,6 +1,6 @@
 {
     "name"           : "dojo_cli",
-    "version"        : "1.0.1",
+    "version"        : "2.0.0",
     "main"           : "dist/app.js",
     "bin"            : {
         "dojo": "./dist/app.js"
diff --git a/NodeApp/src/commander/CommanderApp.ts b/NodeApp/src/commander/CommanderApp.ts
index b374d70..a6002b0 100644
--- a/NodeApp/src/commander/CommanderApp.ts
+++ b/NodeApp/src/commander/CommanderApp.ts
@@ -1,8 +1,8 @@
 import { Command }         from 'commander';
-import EnonceCommand       from './enonce/EnonceCommand';
 import SessionCommand      from './session/SessionCommand';
-import ExerciceCommand     from './exercice/ExerciceCommand';
 import ClientsSharedConfig from '../sharedByClients/config/ClientsSharedConfig';
+import AssignmentCommand   from './assignment/AssignmentCommand';
+import ExerciseCommand     from './exercise/ExerciseCommand';
 
 
 class CommanderApp {
@@ -12,7 +12,7 @@ class CommanderApp {
         this.program
         .name('dojo')
         .description('CLI of the Dojo application')
-        .version('1.0.1')
+        .version('2.0.0')
         .showHelpAfterError()
         .configureHelp({
                            showGlobalOptions: true,
@@ -32,8 +32,8 @@ class CommanderApp {
 
     private registerCommands() {
         SessionCommand.registerOnCommand(this.program);
-        EnonceCommand.registerOnCommand(this.program);
-        ExerciceCommand.registerOnCommand(this.program);
+        AssignmentCommand.registerOnCommand(this.program);
+        ExerciseCommand.registerOnCommand(this.program);
     }
 }
 
diff --git a/NodeApp/src/commander/assignment/AssignmentCommand.ts b/NodeApp/src/commander/assignment/AssignmentCommand.ts
new file mode 100644
index 0000000..c629ddc
--- /dev/null
+++ b/NodeApp/src/commander/assignment/AssignmentCommand.ts
@@ -0,0 +1,25 @@
+import CommanderCommand           from '../CommanderCommand';
+import AssignmentCreateCommand    from './AssignmentCreateCommand';
+import AssignmentPublishCommand   from './AssignmentPublishCommand';
+import AssignmentUnpublishCommand from './AssignmentUnpublishCommand';
+
+
+class AssignmentCommand extends CommanderCommand {
+    protected commandName: string = 'assignment';
+
+    protected defineCommand() {
+        this.command
+        .description('manage an assignment');
+    }
+
+    protected defineSubCommands() {
+        AssignmentCreateCommand.registerOnCommand(this.command);
+        AssignmentPublishCommand.registerOnCommand(this.command);
+        AssignmentUnpublishCommand.registerOnCommand(this.command);
+    }
+
+    protected async commandAction(options: any): Promise<void> { }
+}
+
+
+export default new AssignmentCommand();
\ No newline at end of file
diff --git a/NodeApp/src/commander/enonce/EnonceCreateCommand.ts b/NodeApp/src/commander/assignment/AssignmentCreateCommand.ts
similarity index 65%
rename from NodeApp/src/commander/enonce/EnonceCreateCommand.ts
rename to NodeApp/src/commander/assignment/AssignmentCreateCommand.ts
index 6002d25..c2b384a 100644
--- a/NodeApp/src/commander/enonce/EnonceCreateCommand.ts
+++ b/NodeApp/src/commander/assignment/AssignmentCreateCommand.ts
@@ -4,18 +4,18 @@ import ora                from 'ora';
 import GitlabManager      from '../../managers/GitlabManager';
 import GitlabUser         from '../../shared/types/Gitlab/GitlabUser';
 import DojoBackendManager from '../../managers/DojoBackendManager';
-import Enonce             from '../../sharedByClients/models/Enonce';
 import Toolbox            from '../../shared/helpers/Toolbox';
 import AccessesHelper     from '../../helpers/AccessesHelper';
+import Assignment         from '../../sharedByClients/models/Assignment';
 
 
-class EnonceCreateCommand extends CommanderCommand {
+class AssignmentCreateCommand extends CommanderCommand {
     protected commandName: string = 'create';
 
     protected defineCommand() {
         this.command
-        .description('create a new repository for an enonce')
-        .requiredOption('-n, --name <name>', 'name of the enonce')
+        .description('create a new repository for an assignment')
+        .requiredOption('-n, --name <name>', 'name of the assignment')
         .option('-i, --members_id <ids...>', 'list of gitlab members ids (teaching staff) to add to the repository')
         .option('-u, --members_username <usernames...>', 'list of gitlab members username (teaching staff) to add to the repository')
         .option('-t, --template <string>', 'id or url of the template (http/s and ssh urls are possible)')
@@ -39,12 +39,12 @@ class EnonceCreateCommand extends CommanderCommand {
                 return;
             }
 
-            const enonceGetSpinner: ora.Ora = ora('Checking enonce name availability').start();
-            if ( await DojoBackendManager.getEnonce(options.name) ) {
-                enonceGetSpinner.fail(`Enonce name "${ options.name }" is already taken. Please choose another one.`);
+            const assignmentGetSpinner: ora.Ora = ora('Checking assignment name availability').start();
+            if ( await DojoBackendManager.getAssignment(options.name) ) {
+                assignmentGetSpinner.fail(`Assignment name "${ options.name }" is already taken. Please choose another one.`);
                 return;
             }
-            enonceGetSpinner.succeed(`Enonce name "${ options.name }" is available`);
+            assignmentGetSpinner.succeed(`Assignment name "${ options.name }" is available`);
 
             if ( options.template ) {
                 templateIdOrNamespace = options.template;
@@ -59,12 +59,12 @@ class EnonceCreateCommand extends CommanderCommand {
             }
         }
 
-        // Create the enonce
+        // Create the assignment
         {
-            console.log(chalk.cyan('Please wait while we are creating the enonce...'));
+            console.log(chalk.cyan('Please wait while we are creating the assignment...'));
 
             try {
-                const enonce: Enonce = await DojoBackendManager.createEnonce(options.name, members, templateIdOrNamespace);
+                const assignment: Assignment = await DojoBackendManager.createAssignment(options.name, members, templateIdOrNamespace);
 
                 const oraInfo = (message: string) => {
                     ora({
@@ -73,10 +73,10 @@ class EnonceCreateCommand extends CommanderCommand {
                         }).start().info();
                 };
 
-                oraInfo(`${ chalk.magenta('Name:') } ${ enonce.name }`);
-                oraInfo(`${ chalk.magenta('Web URL:') } ${ enonce.gitlabCreationInfo.web_url }`);
-                oraInfo(`${ chalk.magenta('HTTP Repo:') } ${ enonce.gitlabCreationInfo.http_url_to_repo }`);
-                oraInfo(`${ chalk.magenta('SSH Repo:') } ${ enonce.gitlabCreationInfo.ssh_url_to_repo }`);
+                oraInfo(`${ chalk.magenta('Name:') } ${ assignment.name }`);
+                oraInfo(`${ chalk.magenta('Web URL:') } ${ assignment.gitlabCreationInfo.web_url }`);
+                oraInfo(`${ chalk.magenta('HTTP Repo:') } ${ assignment.gitlabCreationInfo.http_url_to_repo }`);
+                oraInfo(`${ chalk.magenta('SSH Repo:') } ${ assignment.gitlabCreationInfo.ssh_url_to_repo }`);
             } catch ( error ) {
                 return;
             }
@@ -85,4 +85,4 @@ class EnonceCreateCommand extends CommanderCommand {
 }
 
 
-export default new EnonceCreateCommand();
\ No newline at end of file
+export default new AssignmentCreateCommand();
\ No newline at end of file
diff --git a/NodeApp/src/commander/assignment/AssignmentPublishCommand.ts b/NodeApp/src/commander/assignment/AssignmentPublishCommand.ts
new file mode 100644
index 0000000..6816aa3
--- /dev/null
+++ b/NodeApp/src/commander/assignment/AssignmentPublishCommand.ts
@@ -0,0 +1,10 @@
+import AssignmentPublishUnpublishCommandBase from './AssignmentPublishUnpublishCommandBase';
+
+
+class AssignmentPublishCommand extends AssignmentPublishUnpublishCommandBase {
+    protected commandName: string = 'publish';
+    protected publish: boolean = true;
+}
+
+
+export default new AssignmentPublishCommand();
\ No newline at end of file
diff --git a/NodeApp/src/commander/assignment/AssignmentPublishUnpublishCommandBase.ts b/NodeApp/src/commander/assignment/AssignmentPublishUnpublishCommandBase.ts
new file mode 100644
index 0000000..b5df0c9
--- /dev/null
+++ b/NodeApp/src/commander/assignment/AssignmentPublishUnpublishCommandBase.ts
@@ -0,0 +1,83 @@
+import CommanderCommand   from '../CommanderCommand';
+import inquirer           from 'inquirer';
+import chalk              from 'chalk';
+import SessionManager     from '../../managers/SessionManager';
+import ora                from 'ora';
+import DojoBackendManager from '../../managers/DojoBackendManager';
+import Assignment         from '../../sharedByClients/models/Assignment';
+
+
+abstract class AssignmentPublishUnpublishCommandBase extends CommanderCommand {
+    protected abstract publish: boolean;
+
+    protected defineCommand() {
+        this.command
+        .description('publish an assignment')
+        .argument('<name or url>', 'name or url (http/s or ssh) of the assignment')
+        .option('-f, --force', 'don\'t ask for confirmation')
+        .action(this.commandAction.bind(this));
+    }
+
+    protected async commandAction(assignmentNameOrUrl: string, options: { force: boolean }): Promise<void> {
+        if ( !options.force ) {
+            options.force = (await inquirer.prompt({
+                                                       type   : 'confirm',
+                                                       name   : 'force',
+                                                       message: this.publish ? 'Are you sure you want to publish this assignment?' : 'Are you sure you want to unpublish this assignment?'
+                                                   })).force;
+        }
+
+        if ( !options.force ) {
+            return;
+        }
+
+        let assignment!: Assignment | undefined;
+
+        {
+            console.log(chalk.cyan('Please wait while we verify and retrieve data...'));
+
+            if ( !await SessionManager.testSession(true, null) ) {
+                return;
+            }
+
+            ora('Checking assignment:').start().info();
+            ora({
+                    text  : assignmentNameOrUrl,
+                    indent: 4
+                }).start().info();
+            const assignmentGetSpinner: ora.Ora = ora({
+                                                          text  : 'Checking if assignment exists',
+                                                          indent: 8
+                                                      }).start();
+            assignment = await DojoBackendManager.getAssignment(assignmentNameOrUrl);
+            if ( !assignment ) {
+                assignmentGetSpinner.fail(`The assignment doesn't exists`);
+                return;
+            }
+            assignmentGetSpinner.succeed(`The assignment exists`);
+
+            const assignmentCheckAccessSpinner: ora.Ora = ora({
+                                                                  text  : 'Checking accesses',
+                                                                  indent: 8
+                                                              }).start();
+            if ( !assignment.staff ) {
+                assignmentCheckAccessSpinner.fail(`You are not in the staff of this assignment`);
+                return;
+            }
+            assignmentCheckAccessSpinner.succeed(`You are in the staff of this assignment`);
+        }
+
+        {
+            console.log(chalk.cyan(`Please wait while we ${ this.publish ? 'publish' : 'unpublish' } the assignment...`));
+
+            try {
+                await DojoBackendManager.changeAssignmentPublishedStatus(assignment, this.publish);
+            } catch ( error ) {
+                return;
+            }
+        }
+    }
+}
+
+
+export default AssignmentPublishUnpublishCommandBase;
\ No newline at end of file
diff --git a/NodeApp/src/commander/assignment/AssignmentUnpublishCommand.ts b/NodeApp/src/commander/assignment/AssignmentUnpublishCommand.ts
new file mode 100644
index 0000000..e329c48
--- /dev/null
+++ b/NodeApp/src/commander/assignment/AssignmentUnpublishCommand.ts
@@ -0,0 +1,10 @@
+import AssignmentPublishUnpublishCommandBase from './AssignmentPublishUnpublishCommandBase';
+
+
+class AssignmentUnpublishCommand extends AssignmentPublishUnpublishCommandBase {
+    protected commandName: string = 'unpublish';
+    protected publish: boolean = false;
+}
+
+
+export default new AssignmentUnpublishCommand();
\ No newline at end of file
diff --git a/NodeApp/src/commander/enonce/EnonceCommand.ts b/NodeApp/src/commander/enonce/EnonceCommand.ts
deleted file mode 100644
index dc3a5f3..0000000
--- a/NodeApp/src/commander/enonce/EnonceCommand.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import CommanderCommand       from '../CommanderCommand';
-import EnonceCreateCommand    from './EnonceCreateCommand';
-import EnoncePublishCommand   from './EnoncePublishCommand';
-import EnonceUnpublishCommand from './EnonceUnpublishCommand';
-
-
-class EnonceCommand extends CommanderCommand {
-    protected commandName: string = 'enonce';
-
-    protected defineCommand() {
-        this.command
-        .description('manage an enonce');
-    }
-
-    protected defineSubCommands() {
-        EnonceCreateCommand.registerOnCommand(this.command);
-        EnoncePublishCommand.registerOnCommand(this.command);
-        EnonceUnpublishCommand.registerOnCommand(this.command);
-    }
-
-    protected async commandAction(options: any): Promise<void> { }
-}
-
-
-export default new EnonceCommand();
\ No newline at end of file
diff --git a/NodeApp/src/commander/enonce/EnoncePublishCommand.ts b/NodeApp/src/commander/enonce/EnoncePublishCommand.ts
deleted file mode 100644
index 91900f6..0000000
--- a/NodeApp/src/commander/enonce/EnoncePublishCommand.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import EnoncePublishUnpublishCommandBase from './EnoncePublishUnpublishCommandBase';
-
-
-class EnoncePublishCommand extends EnoncePublishUnpublishCommandBase {
-    protected commandName: string = 'publish';
-    protected publish: boolean = true;
-}
-
-
-export default new EnoncePublishCommand();
\ No newline at end of file
diff --git a/NodeApp/src/commander/enonce/EnoncePublishUnpublishCommandBase.ts b/NodeApp/src/commander/enonce/EnoncePublishUnpublishCommandBase.ts
deleted file mode 100644
index c94c419..0000000
--- a/NodeApp/src/commander/enonce/EnoncePublishUnpublishCommandBase.ts
+++ /dev/null
@@ -1,83 +0,0 @@
-import CommanderCommand   from '../CommanderCommand';
-import inquirer           from 'inquirer';
-import Enonce             from '../../sharedByClients/models/Enonce';
-import chalk              from 'chalk';
-import SessionManager     from '../../managers/SessionManager';
-import ora                from 'ora';
-import DojoBackendManager from '../../managers/DojoBackendManager';
-
-
-abstract class EnoncePublishUnpublishCommandBase extends CommanderCommand {
-    protected abstract publish: boolean;
-
-    protected defineCommand() {
-        this.command
-        .description('publish an enonce')
-        .argument('<name or url>', 'name or url (http/s or ssh) of the enonce')
-        .option('-f, --force', 'don\'t ask for confirmation')
-        .action(this.commandAction.bind(this));
-    }
-
-    protected async commandAction(enonceNameOrUrl: string, options: { force: boolean }): Promise<void> {
-        if ( !options.force ) {
-            options.force = (await inquirer.prompt({
-                                                       type   : 'confirm',
-                                                       name   : 'force',
-                                                       message: this.publish ? 'Are you sure you want to publish this enonce?' : 'Are you sure you want to unpublish this enonce?'
-                                                   })).force;
-        }
-
-        if ( !options.force ) {
-            return;
-        }
-
-        let enonce!: Enonce | undefined;
-
-        {
-            console.log(chalk.cyan('Please wait while we verify and retrieve data...'));
-
-            if ( !await SessionManager.testSession(true, null) ) {
-                return;
-            }
-
-            ora('Checking enonce:').start().info();
-            ora({
-                    text  : enonceNameOrUrl,
-                    indent: 4
-                }).start().info();
-            const enonceGetSpinner: ora.Ora = ora({
-                                                      text  : 'Checking if enonce exists',
-                                                      indent: 8
-                                                  }).start();
-            enonce = await DojoBackendManager.getEnonce(enonceNameOrUrl);
-            if ( !enonce ) {
-                enonceGetSpinner.fail(`The enonce doesn't exists`);
-                return;
-            }
-            enonceGetSpinner.succeed(`The enonce exists`);
-
-            const enonceCheckAccessSpinner: ora.Ora = ora({
-                                                              text  : 'Checking accesses',
-                                                              indent: 8
-                                                          }).start();
-            if ( !enonce.staff ) {
-                enonceCheckAccessSpinner.fail(`You are not in the staff of this enonce`);
-                return;
-            }
-            enonceCheckAccessSpinner.succeed(`You are in the staff of this enonce`);
-        }
-
-        {
-            console.log(chalk.cyan(`Please wait while we ${ this.publish ? 'publish' : 'unpublish' } the enonce...`));
-
-            try {
-                await DojoBackendManager.changeEnoncePublishedStatus(enonce, this.publish);
-            } catch ( error ) {
-                return;
-            }
-        }
-    }
-}
-
-
-export default EnoncePublishUnpublishCommandBase;
\ No newline at end of file
diff --git a/NodeApp/src/commander/enonce/EnonceUnpublishCommand.ts b/NodeApp/src/commander/enonce/EnonceUnpublishCommand.ts
deleted file mode 100644
index 2a3e3d3..0000000
--- a/NodeApp/src/commander/enonce/EnonceUnpublishCommand.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import EnoncePublishUnpublishCommandBase from './EnoncePublishUnpublishCommandBase';
-
-
-class EnonceUnpublishCommand extends EnoncePublishUnpublishCommandBase {
-    protected commandName: string = 'unpublish';
-    protected publish: boolean = false;
-}
-
-
-export default new EnonceUnpublishCommand();
\ No newline at end of file
diff --git a/NodeApp/src/commander/exercice/ExerciceCommand.ts b/NodeApp/src/commander/exercice/ExerciceCommand.ts
deleted file mode 100644
index 27a7345..0000000
--- a/NodeApp/src/commander/exercice/ExerciceCommand.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import CommanderCommand      from '../CommanderCommand';
-import ExerciceCreateCommand from './ExerciceCreateCommand';
-import ExerciceRunCommand    from './ExerciceRunCommand';
-
-
-class ExerciceCommand extends CommanderCommand {
-    protected commandName: string = 'exercice';
-
-    protected defineCommand() {
-        this.command
-        .description('manage an exercice');
-    }
-
-    protected defineSubCommands() {
-        ExerciceCreateCommand.registerOnCommand(this.command);
-        ExerciceRunCommand.registerOnCommand(this.command);
-    }
-
-    protected async commandAction(options: any): Promise<void> { }
-}
-
-
-export default new ExerciceCommand();
\ No newline at end of file
diff --git a/NodeApp/src/commander/exercice/ExerciceCreateCommand.ts b/NodeApp/src/commander/exercice/ExerciceCreateCommand.ts
deleted file mode 100644
index ccd761f..0000000
--- a/NodeApp/src/commander/exercice/ExerciceCreateCommand.ts
+++ /dev/null
@@ -1,91 +0,0 @@
-import CommanderCommand   from '../CommanderCommand';
-import chalk              from 'chalk';
-import GitlabManager      from '../../managers/GitlabManager';
-import GitlabUser         from '../../shared/types/Gitlab/GitlabUser';
-import Enonce             from '../../sharedByClients/models/Enonce';
-import ora                from 'ora';
-import DojoBackendManager from '../../managers/DojoBackendManager';
-import Exercice           from '../../sharedByClients/models/Exercice';
-import AccessesHelper     from '../../helpers/AccessesHelper';
-
-
-class ExerciceCreateCommand extends CommanderCommand {
-    protected commandName: string = 'create';
-
-    protected defineCommand() {
-        this.command
-        .description('create a new exercice from an enonce')
-        .requiredOption('-e, --enonce <value>', 'enonce source (Dojo enonce ID, Dojo enonce name or Gitlab enonce URL)')
-        .option('-i, --members_id <ids...>', 'list of gitlab members ids (group\'s student) to add to the repository')
-        .option('-u, --members_username <usernames...>', 'list of gitlab members username (group\'s student) to add to the repository')
-        .action(this.commandAction.bind(this));
-    }
-
-    protected async commandAction(options: any): Promise<void> {
-        let members!: Array<GitlabUser> | false;
-        let enonce!: Enonce | undefined;
-
-        // Check access and retrieve data
-        {
-            console.log(chalk.cyan('Please wait while we verify and retrieve data...'));
-
-            if ( !await AccessesHelper.checkStudent() ) {
-                return;
-            }
-
-            members = await GitlabManager.fetchMembers(options);
-            if ( !members ) {
-                return;
-            }
-
-            ora('Checking enonce:').start().info();
-            const enonceGetSpinner: ora.Ora = ora({
-                                                      text  : 'Checking if enonce exists',
-                                                      indent: 4
-                                                  }).start();
-            enonce = await DojoBackendManager.getEnonce(options.enonce);
-            if ( !enonce ) {
-                enonceGetSpinner.fail(`Enonce "${ options.enonce }" doesn't exists`);
-                return;
-            }
-            enonceGetSpinner.succeed(`Enonce "${ options.enonce }" exists`);
-
-            const enoncePublishedSpinner: ora.Ora = ora({
-                                                            text  : 'Checking if enonce is published',
-                                                            indent: 4
-                                                        }).start();
-            if ( !enonce.published ) {
-                enoncePublishedSpinner.fail(`Enonce "${ enonce.name }" isn't published`);
-                return;
-            }
-            enoncePublishedSpinner.succeed(`Enonce "${ enonce.name }" is published`);
-        }
-
-        //Create the exercice
-        {
-            console.log(chalk.cyan('Please wait while we are creating the exercice...'));
-
-            try {
-                const exercice: Exercice = await DojoBackendManager.createExercice((enonce as Enonce).name, members);
-
-                const oraInfo = (message: string) => {
-                    ora({
-                            text  : message,
-                            indent: 4
-                        }).start().info();
-                };
-
-                oraInfo(`${ chalk.magenta('Id:') } ${ exercice.id }`);
-                oraInfo(`${ chalk.magenta('Name:') } ${ exercice.name }`);
-                oraInfo(`${ chalk.magenta('Web URL:') } ${ exercice.gitlabCreationInfo.web_url }`);
-                oraInfo(`${ chalk.magenta('HTTP Repo:') } ${ exercice.gitlabCreationInfo.http_url_to_repo }`);
-                oraInfo(`${ chalk.magenta('SSH Repo:') } ${ exercice.gitlabCreationInfo.ssh_url_to_repo }`);
-            } catch ( error ) {
-                return;
-            }
-        }
-    }
-}
-
-
-export default new ExerciceCreateCommand();
\ No newline at end of file
diff --git a/NodeApp/src/commander/exercise/ExerciseCommand.ts b/NodeApp/src/commander/exercise/ExerciseCommand.ts
new file mode 100644
index 0000000..ef2c3f2
--- /dev/null
+++ b/NodeApp/src/commander/exercise/ExerciseCommand.ts
@@ -0,0 +1,23 @@
+import CommanderCommand      from '../CommanderCommand';
+import ExerciseCreateCommand from './ExerciseCreateCommand';
+import ExerciseRunCommand    from './ExerciseRunCommand';
+
+
+class ExerciseCommand extends CommanderCommand {
+    protected commandName: string = 'exercise';
+
+    protected defineCommand() {
+        this.command
+        .description('manage an exercise');
+    }
+
+    protected defineSubCommands() {
+        ExerciseCreateCommand.registerOnCommand(this.command);
+        ExerciseRunCommand.registerOnCommand(this.command);
+    }
+
+    protected async commandAction(options: any): Promise<void> { }
+}
+
+
+export default new ExerciseCommand();
\ No newline at end of file
diff --git a/NodeApp/src/commander/exercise/ExerciseCreateCommand.ts b/NodeApp/src/commander/exercise/ExerciseCreateCommand.ts
new file mode 100644
index 0000000..9116ff3
--- /dev/null
+++ b/NodeApp/src/commander/exercise/ExerciseCreateCommand.ts
@@ -0,0 +1,91 @@
+import CommanderCommand   from '../CommanderCommand';
+import chalk              from 'chalk';
+import GitlabManager      from '../../managers/GitlabManager';
+import GitlabUser         from '../../shared/types/Gitlab/GitlabUser';
+import ora                from 'ora';
+import DojoBackendManager from '../../managers/DojoBackendManager';
+import AccessesHelper     from '../../helpers/AccessesHelper';
+import Assignment         from '../../sharedByClients/models/Assignment';
+import Exercise           from '../../sharedByClients/models/Exercise';
+
+
+class ExerciseCreateCommand extends CommanderCommand {
+    protected commandName: string = 'create';
+
+    protected defineCommand() {
+        this.command
+        .description('create a new exercise from an assignment')
+        .requiredOption('-a, --assignment <value>', 'assignment source (Dojo assignment ID, Dojo assignment name or Gitlab assignment URL)')
+        .option('-i, --members_id <ids...>', 'list of gitlab members ids (group\'s student) to add to the repository')
+        .option('-u, --members_username <usernames...>', 'list of gitlab members username (group\'s student) to add to the repository')
+        .action(this.commandAction.bind(this));
+    }
+
+    protected async commandAction(options: any): Promise<void> {
+        let members!: Array<GitlabUser> | false;
+        let assignment!: Assignment | undefined;
+
+        // Check access and retrieve data
+        {
+            console.log(chalk.cyan('Please wait while we verify and retrieve data...'));
+
+            if ( !await AccessesHelper.checkStudent() ) {
+                return;
+            }
+
+            members = await GitlabManager.fetchMembers(options);
+            if ( !members ) {
+                return;
+            }
+
+            ora('Checking assignment:').start().info();
+            const assignmentGetSpinner: ora.Ora = ora({
+                                                          text  : 'Checking if assignment exists',
+                                                          indent: 4
+                                                      }).start();
+            assignment = await DojoBackendManager.getAssignment(options.assignment);
+            if ( !assignment ) {
+                assignmentGetSpinner.fail(`Assignment "${ options.assignment }" doesn't exists`);
+                return;
+            }
+            assignmentGetSpinner.succeed(`Assignment "${ options.assignment }" exists`);
+
+            const assignmentPublishedSpinner: ora.Ora = ora({
+                                                                text  : 'Checking if assignment is published',
+                                                                indent: 4
+                                                            }).start();
+            if ( !assignment.published ) {
+                assignmentPublishedSpinner.fail(`Assignment "${ assignment.name }" isn't published`);
+                return;
+            }
+            assignmentPublishedSpinner.succeed(`Assignment "${ assignment.name }" is published`);
+        }
+
+        //Create the exercise
+        {
+            console.log(chalk.cyan('Please wait while we are creating the exercise...'));
+
+            try {
+                const exercise: Exercise = await DojoBackendManager.createExercise((assignment as Assignment).name, members);
+
+                const oraInfo = (message: string) => {
+                    ora({
+                            text  : message,
+                            indent: 4
+                        }).start().info();
+                };
+
+                oraInfo(`${ chalk.magenta('Id:') } ${ exercise.id }`);
+                oraInfo(`${ chalk.magenta('Name:') } ${ exercise.name }`);
+                oraInfo(`${ chalk.magenta('Web URL:') } ${ exercise.gitlabCreationInfo.web_url }`);
+                oraInfo(`${ chalk.magenta('HTTP Repo:') } ${ exercise.gitlabCreationInfo.http_url_to_repo }`);
+                oraInfo(`${ chalk.magenta('SSH Repo:') } ${ exercise.gitlabCreationInfo.ssh_url_to_repo }`);
+            } catch ( error ) {
+                return;
+            }
+        }
+    }
+}
+
+
+export default new ExerciseCreateCommand();
\ No newline at end of file
diff --git a/NodeApp/src/commander/exercice/ExerciceRunCommand.ts b/NodeApp/src/commander/exercise/ExerciseRunCommand.ts
similarity index 66%
rename from NodeApp/src/commander/exercice/ExerciceRunCommand.ts
rename to NodeApp/src/commander/exercise/ExerciseRunCommand.ts
index 4130206..7fdd21b 100644
--- a/NodeApp/src/commander/exercice/ExerciceRunCommand.ts
+++ b/NodeApp/src/commander/exercise/ExerciseRunCommand.ts
@@ -4,29 +4,29 @@ import fs                          from 'node:fs';
 import ora                         from 'ora';
 import util                        from 'util';
 import { exec }                    from 'child_process';
-import SharedEnonceHelper          from '../../shared/helpers/Dojo/SharedEnonceHelper';
-import EnonceFile                  from '../../shared/types/Dojo/EnonceFile';
 import chalk                       from 'chalk';
-import ExerciceDockerCompose       from '../../sharedByClients/helpers/Dojo/ExerciceDockerCompose';
 import * as os                     from 'os';
 import path                        from 'path';
-import ExerciceResultsValidation   from '../../sharedByClients/helpers/Dojo/ExerciceResultsValidation';
-import ExerciceCheckerError        from '../../shared/types/Dojo/ExerciceCheckerError';
-import ClientsSharedExerciceHelper from '../../sharedByClients/helpers/Dojo/ClientsSharedExerciceHelper';
 import ClientsSharedConfig         from '../../sharedByClients/config/ClientsSharedConfig';
+import AssignmentFile              from '../../shared/types/Dojo/AssignmentFile';
+import ExerciseDockerCompose       from '../../sharedByClients/helpers/Dojo/ExerciseDockerCompose';
+import ExerciseResultsValidation   from '../../sharedByClients/helpers/Dojo/ExerciseResultsValidation';
+import SharedAssignmentHelper      from '../../shared/helpers/Dojo/SharedAssignmentHelper';
+import ExerciseCheckerError        from '../../shared/types/Dojo/ExerciseCheckerError';
+import ClientsSharedExerciseHelper from '../../sharedByClients/helpers/Dojo/ClientsSharedExerciseHelper';
 
 
 const execAsync = util.promisify(exec);
 
 
-class ExerciceRunCommand extends CommanderCommand {
+class ExerciseRunCommand extends CommanderCommand {
     protected commandName: string = 'run';
 
     private readonly dateISOString: string = (new Date()).toISOString().replace(/:/g, '_').replace(/\./g, '_');
 
     private readonly folderResultsVolume: string = path.join(os.homedir(), 'DojoExecutions', `dojo_execLogs_${ this.dateISOString }`);
     private readonly folderResultsDojo: string = path.join(this.folderResultsVolume, `Dojo/`);
-    private readonly folderResultsExercice: string = path.join(this.folderResultsVolume, `Exercice/`);
+    private readonly folderResultsExercise: string = path.join(this.folderResultsVolume, `Exercise/`);
 
     private readonly projectName: string = `${ ClientsSharedConfig.dockerCompose.projectName }_${ this.dateISOString.toLowerCase() }`;
 
@@ -34,8 +34,8 @@ class ExerciceRunCommand extends CommanderCommand {
 
     protected defineCommand() {
         this.command
-        .description('locally run an exercice')
-        .option('-p, --path <value>', 'exercice path', Config.folders.defaultLocalExercice)
+        .description('locally run an exercise')
+        .option('-p, --path <value>', 'exercise path', Config.folders.defaultLocalExercise)
         .action(this.commandAction.bind(this));
     }
 
@@ -47,61 +47,61 @@ class ExerciceRunCommand extends CommanderCommand {
     }
 
     protected async commandAction(options: any): Promise<void> {
-        const localExercicePath = options.path ?? Config.folders.defaultLocalExercice;
+        const localExercisePath = options.path ?? Config.folders.defaultLocalExercise;
 
-        let enonceFile: EnonceFile;
-        let exerciceDockerCompose: ExerciceDockerCompose;
-        let exerciceResultsValidation: ExerciceResultsValidation;
+        let assignmentFile: AssignmentFile;
+        let exerciseDockerCompose: ExerciseDockerCompose;
+        let exerciseResultsValidation: ExerciseResultsValidation;
 
-        // Step 1: Check requirements (if it's an exercice folder and if Docker deamon is running)
+        // Step 1: Check requirements (if it's an exercise folder and if Docker deamon is running)
         {
             console.log(chalk.cyan('Please wait while we are checking and creating dependencies...'));
 
             // Create result temp folder
             fs.mkdirSync(this.folderResultsVolume, { recursive: true });
             fs.mkdirSync(this.folderResultsDojo, { recursive: true });
-            fs.mkdirSync(this.folderResultsExercice, { recursive: true });
+            fs.mkdirSync(this.folderResultsExercise, { recursive: true });
 
 
             ora({
-                    text  : `Checking exercice content:`,
+                    text  : `Checking exercise content:`,
                     indent: 4
                 }).start().info();
 
-            // Exercice folder
+            // Exercise folder
             {
                 const spinner: ora.Ora = ora({
-                                                 text  : `Checking exercice folder`,
+                                                 text  : `Checking exercise folder`,
                                                  indent: 8
                                              }).start();
 
                 const files = fs.readdirSync(options.path);
-                const missingFiles = Config.exercice.neededFiles.map((file: string): [ string, boolean ] => [ file, files.includes(file) ]).filter((file: [ string, boolean ]) => !file[1]);
+                const missingFiles = Config.exercise.neededFiles.map((file: string): [ string, boolean ] => [ file, files.includes(file) ]).filter((file: [ string, boolean ]) => !file[1]);
 
                 if ( missingFiles.length > 0 ) {
-                    spinner.fail(`The exercice folder is missing the following files: ${ missingFiles.map((file: [ string, boolean ]) => file[0]).join(', ') }`);
+                    spinner.fail(`The exercise folder is missing the following files: ${ missingFiles.map((file: [ string, boolean ]) => file[0]).join(', ') }`);
                     return;
                 }
 
-                spinner.succeed(`The exercice folder contains all the needed files`);
+                spinner.succeed(`The exercise folder contains all the needed files`);
             }
 
-            // dojo.enonce validity
+            // dojo.assignment validity
             {
                 const spinner: ora.Ora = ora({
-                                                 text  : `Checking ${ Config.enonce.filename } file`,
+                                                 text  : `Checking ${ Config.assignment.filename } file`,
                                                  indent: 8
                                              }).start();
 
-                const validationResults = SharedEnonceHelper.validateDescriptionFile(`${ options.path }/${ Config.enonce.filename }`);
+                const validationResults = SharedAssignmentHelper.validateDescriptionFile(`${ options.path }/${ Config.assignment.filename }`);
                 if ( !validationResults.isValid ) {
-                    spinner.fail(`The ${ Config.enonce.filename } file is invalid: ${ JSON.stringify(validationResults.errors) }`);
+                    spinner.fail(`The ${ Config.assignment.filename } file is invalid: ${ JSON.stringify(validationResults.errors) }`);
                     return;
                 } else {
-                    enonceFile = validationResults.results!;
+                    assignmentFile = validationResults.results!;
                 }
 
-                spinner.succeed(`The ${ Config.enonce.filename } file is valid`);
+                spinner.succeed(`The ${ Config.assignment.filename } file is valid`);
             }
 
             // Docker deamon
@@ -112,7 +112,7 @@ class ExerciceRunCommand extends CommanderCommand {
                                              }).start();
 
                 try {
-                    await execAsync(`cd "${ Config.folders.defaultLocalExercice }";docker ps`);
+                    await execAsync(`cd "${ Config.folders.defaultLocalExercise }";docker ps`);
                 } catch ( error ) {
                     spinner.fail(`The Docker deamon is not running`);
                     return;
@@ -125,27 +125,27 @@ class ExerciceRunCommand extends CommanderCommand {
 
         // Step 2: Run docker-compose file
         {
-            console.log(chalk.cyan('Please wait while we are running the exercice...'));
+            console.log(chalk.cyan('Please wait while we are running the exercise...'));
 
-            const composeOverridePath: string = path.join(localExercicePath, 'docker-compose-override.yml');
+            const composeOverridePath: string = path.join(localExercisePath, 'docker-compose-override.yml');
 
-            const composeOverride = fs.readFileSync(path.join(__dirname, '../../../assets/docker-compose-override.yml'), 'utf8').replace('{{VOLUME_NAME}}', enonceFile.result.volume).replace('{{MOUNT_PATH}}', this.folderResultsExercice);
+            const composeOverride = fs.readFileSync(path.join(__dirname, '../../../assets/docker-compose-override.yml'), 'utf8').replace('{{VOLUME_NAME}}', assignmentFile.result.volume).replace('{{MOUNT_PATH}}', this.folderResultsExercise);
             fs.writeFileSync(composeOverridePath, composeOverride);
 
-            exerciceDockerCompose = new ExerciceDockerCompose(this.projectName, enonceFile, localExercicePath, [ composeOverridePath ]);
+            exerciseDockerCompose = new ExerciseDockerCompose(this.projectName, assignmentFile, localExercisePath, [ composeOverridePath ]);
 
             try {
                 await new Promise<void>((resolve, reject) => {
                     let spinner: ora.Ora;
 
-                    exerciceDockerCompose.events.on('step', (name: string, message: string) => {
+                    exerciseDockerCompose.events.on('step', (name: string, message: string) => {
                         spinner = ora({
                                           text  : message,
                                           indent: 4
                                       }).start();
                     });
 
-                    exerciceDockerCompose.events.on('endStep', (stepName: string, message: string, error: boolean) => {
+                    exerciseDockerCompose.events.on('endStep', (stepName: string, message: string, error: boolean) => {
                         if ( error ) {
                             spinner.fail(message);
                         } else {
@@ -153,18 +153,18 @@ class ExerciceRunCommand extends CommanderCommand {
                         }
                     });
 
-                    exerciceDockerCompose.events.on('finished', (success: boolean, exitCode: number) => {
+                    exerciseDockerCompose.events.on('finished', (success: boolean, exitCode: number) => {
                         success ? resolve() : reject();
                     });
 
-                    exerciceDockerCompose.run(true);
+                    exerciseDockerCompose.run(true);
                 });
             } catch ( error ) { }
 
             fs.rmSync(composeOverridePath);
-            fs.writeFileSync(this.fileComposeLogs, exerciceDockerCompose.allLogs);
+            fs.writeFileSync(this.fileComposeLogs, exerciseDockerCompose.allLogs);
 
-            if ( !exerciceDockerCompose.success ) {
+            if ( !exerciseDockerCompose.success ) {
                 this.displayExecutionLogs();
                 return;
             }
@@ -175,20 +175,20 @@ class ExerciceRunCommand extends CommanderCommand {
         {
             console.log(chalk.cyan('Please wait while we are checking the results...'));
 
-            exerciceResultsValidation = new ExerciceResultsValidation(this.folderResultsDojo, this.folderResultsExercice);
+            exerciseResultsValidation = new ExerciseResultsValidation(this.folderResultsDojo, this.folderResultsExercise);
 
             try {
                 await new Promise<void>((resolve, reject) => {
                     let spinner: ora.Ora;
 
-                    exerciceResultsValidation.events.on('step', (name: string, message: string) => {
+                    exerciseResultsValidation.events.on('step', (name: string, message: string) => {
                         spinner = ora({
                                           text  : message,
                                           indent: 4
                                       }).start();
                     });
 
-                    exerciceResultsValidation.events.on('endStep', (stepName: string, message: string, error: boolean) => {
+                    exerciseResultsValidation.events.on('endStep', (stepName: string, message: string, error: boolean) => {
                         if ( error ) {
                             if ( stepName == 'CHECK_SIZE' ) {
                                 spinner.warn(message);
@@ -200,11 +200,11 @@ class ExerciceRunCommand extends CommanderCommand {
                         }
                     });
 
-                    exerciceResultsValidation.events.on('finished', (success: boolean, exitCode: number) => {
-                        success || exitCode == ExerciceCheckerError.EXERCICE_RESULTS_FOLDER_TOO_BIG ? resolve() : reject();
+                    exerciseResultsValidation.events.on('finished', (success: boolean, exitCode: number) => {
+                        success || exitCode == ExerciseCheckerError.EXERCISE_RESULTS_FOLDER_TOO_BIG ? resolve() : reject();
                     });
 
-                    exerciceResultsValidation.run();
+                    exerciseResultsValidation.run();
                 });
             } catch ( error ) {
                 this.displayExecutionLogs();
@@ -215,7 +215,7 @@ class ExerciceRunCommand extends CommanderCommand {
 
         // Step 4: Display results + Volume location
         {
-            ClientsSharedExerciceHelper.displayExecutionResults(exerciceResultsValidation.exerciceResults!, exerciceDockerCompose.exitCode, {
+            ClientsSharedExerciseHelper.displayExecutionResults(exerciseResultsValidation.exerciseResults!, exerciseDockerCompose.exitCode, {
                 INFO   : chalk.bold,
                 SUCCESS: chalk.green,
                 FAILURE: chalk.red
@@ -225,4 +225,4 @@ class ExerciceRunCommand extends CommanderCommand {
 }
 
 
-export default new ExerciceRunCommand();
\ No newline at end of file
+export default new ExerciseRunCommand();
\ No newline at end of file
diff --git a/NodeApp/src/config/Config.ts b/NodeApp/src/config/Config.ts
index c384a41..dca31f4 100644
--- a/NodeApp/src/config/Config.ts
+++ b/NodeApp/src/config/Config.ts
@@ -7,14 +7,14 @@ class Config {
     };
 
     public readonly folders: {
-        defaultLocalExercice: string
+        defaultLocalExercise: string
     };
 
-    public enonce: {
+    public assignment: {
         filename: string
     };
 
-    public readonly exercice: {
+    public readonly exercise: {
         neededFiles: Array<string>
     };
 
@@ -25,15 +25,15 @@ class Config {
         };
 
         this.folders = {
-            defaultLocalExercice: process.env.LOCAL_EXERCICE_DEFAULT_FOLDER || './'
+            defaultLocalExercise: process.env.LOCAL_EXERCISE_DEFAULT_FOLDER || './'
         };
 
-        this.enonce = {
-            filename: process.env.ENONCE_FILENAME || ''
+        this.assignment = {
+            filename: process.env.ASSIGNMENT_FILENAME || ''
         };
 
-        this.exercice = {
-            neededFiles: JSON.parse(process.env.EXERCICE_NEEDED_FILES || '[]')
+        this.exercise = {
+            neededFiles: JSON.parse(process.env.EXERCISE_NEEDED_FILES || '[]')
         };
     }
 }
diff --git a/NodeApp/src/managers/DojoBackendManager.ts b/NodeApp/src/managers/DojoBackendManager.ts
index d9283bb..64c56cb 100644
--- a/NodeApp/src/managers/DojoBackendManager.ts
+++ b/NodeApp/src/managers/DojoBackendManager.ts
@@ -2,11 +2,11 @@ import axios, { AxiosError } from 'axios';
 import ora                   from 'ora';
 import ApiRoute              from '../sharedByClients/types/Dojo/ApiRoute';
 import { StatusCodes }       from 'http-status-codes';
-import Enonce                from '../sharedByClients/models/Enonce';
 import GitlabUser            from '../shared/types/Gitlab/GitlabUser';
-import Exercice              from '../sharedByClients/models/Exercice';
-import DojoResponse          from '../shared/types/Dojo/DojoResponse';
 import ClientsSharedConfig   from '../sharedByClients/config/ClientsSharedConfig';
+import Assignment            from '../sharedByClients/models/Assignment';
+import DojoBackendResponse   from '../shared/types/Dojo/DojoBackendResponse';
+import Exercise              from '../sharedByClients/models/Exercise';
 
 
 class DojoBackendManager {
@@ -14,9 +14,9 @@ class DojoBackendManager {
         return `${ ClientsSharedConfig.apiURL }${ route }`;
     }
 
-    public async getEnonce(nameOrUrl: string): Promise<Enonce | undefined> {
+    public async getAssignment(nameOrUrl: string): Promise<Assignment | undefined> {
         try {
-            return (await axios.get<DojoResponse<Enonce>>(this.getApiUrl(ApiRoute.ENONCE_GET).replace('{{nameOrUrl}}', encodeURIComponent(nameOrUrl)))).data.data;
+            return (await axios.get<DojoBackendResponse<Assignment>>(this.getApiUrl(ApiRoute.ASSIGNMENT_GET).replace('{{nameOrUrl}}', encodeURIComponent(nameOrUrl)))).data.data;
         } catch ( error ) {
             return undefined;
         }
@@ -59,21 +59,21 @@ class DojoBackendManager {
         }
     }
 
-    public async createEnonce(name: string, members: Array<GitlabUser>, templateIdOrNamespace: string | null, verbose: boolean = true): Promise<Enonce> {
-        const spinner: ora.Ora = ora('Creating enonce...');
+    public async createAssignment(name: string, members: Array<GitlabUser>, templateIdOrNamespace: string | null, verbose: boolean = true): Promise<Assignment> {
+        const spinner: ora.Ora = ora('Creating assignment...');
 
         if ( verbose ) {
             spinner.start();
         }
 
         try {
-            const response = await axios.post<DojoResponse<Enonce>>(this.getApiUrl(ApiRoute.ENONCE_CREATE), Object.assign({
-                                                                                                                              name   : name,
-                                                                                                                              members: JSON.stringify(members)
-                                                                                                                          }, templateIdOrNamespace ? { template: templateIdOrNamespace } : {}));
+            const response = await axios.post<DojoBackendResponse<Assignment>>(this.getApiUrl(ApiRoute.ASSIGNMENT_CREATE), Object.assign({
+                                                                                                                                             name   : name,
+                                                                                                                                             members: JSON.stringify(members)
+                                                                                                                                         }, templateIdOrNamespace ? { template: templateIdOrNamespace } : {}));
 
             if ( verbose ) {
-                spinner.succeed(`Enonce successfully created`);
+                spinner.succeed(`Assignment successfully created`);
             }
 
             return response.data.data;
@@ -82,13 +82,13 @@ class DojoBackendManager {
                 if ( error instanceof AxiosError ) {
                     if ( error.response ) {
                         if ( error.response.status === StatusCodes.CONFLICT ) {
-                            spinner.fail(`The enonce name is already used. Please choose another one.`);
+                            spinner.fail(`The assignment name is already used. Please choose another one.`);
                         } else {
-                            spinner.fail(`Enonce creation error: ${ error.response.statusText }`);
+                            spinner.fail(`Assignment creation error: ${ error.response.statusText }`);
                         }
                     }
                 } else {
-                    spinner.fail(`Enonce creation error: unknown error`);
+                    spinner.fail(`Assignment creation error: unknown error`);
                 }
             }
 
@@ -96,18 +96,18 @@ class DojoBackendManager {
         }
     }
 
-    public async createExercice(enonceName: string, members: Array<GitlabUser>, verbose: boolean = true): Promise<Exercice> {
-        const spinner: ora.Ora = ora('Creating exercice...');
+    public async createExercise(assignmentName: string, members: Array<GitlabUser>, verbose: boolean = true): Promise<Exercise> {
+        const spinner: ora.Ora = ora('Creating exercise...');
 
         if ( verbose ) {
             spinner.start();
         }
 
         try {
-            const response = await axios.post<DojoResponse<Exercice>>(this.getApiUrl(ApiRoute.EXERCICE_CREATE).replace('{{nameOrUrl}}', encodeURIComponent(enonceName)), { members: JSON.stringify(members) });
+            const response = await axios.post<DojoBackendResponse<Exercise>>(this.getApiUrl(ApiRoute.EXERCISE_CREATE).replace('{{nameOrUrl}}', encodeURIComponent(assignmentName)), { members: JSON.stringify(members) });
 
             if ( verbose ) {
-                spinner.succeed(`Exercice successfully created`);
+                spinner.succeed(`Exercise successfully created`);
             }
 
             return response.data.data;
@@ -116,13 +116,13 @@ class DojoBackendManager {
                 if ( error instanceof AxiosError ) {
                     if ( error.response ) {
                         if ( error.response.status === StatusCodes.CONFLICT ) {
-                            spinner.fail(`You've reached the max number of exercice of this enonce.`);
+                            spinner.fail(`You've already reached the max number of exercise of this assignment.`);
                         } else {
-                            spinner.fail(`Exercice creation error: ${ error.response.statusText }`);
+                            spinner.fail(`Exercise creation error: ${ error.response.statusText }`);
                         }
                     }
                 } else {
-                    spinner.fail(`Exercice creation error: unknown error`);
+                    spinner.fail(`Exercise creation error: unknown error`);
                 }
             }
 
@@ -130,7 +130,7 @@ class DojoBackendManager {
         }
     }
 
-    public async changeEnoncePublishedStatus(enonce: Enonce, publish: boolean, verbose: boolean = true) {
+    public async changeAssignmentPublishedStatus(assignment: Assignment, publish: boolean, verbose: boolean = true) {
         const spinner: ora.Ora = ora('Changing published status...');
 
         if ( verbose ) {
@@ -138,19 +138,19 @@ class DojoBackendManager {
         }
 
         try {
-            await axios.patch<DojoResponse<null>>(this.getApiUrl(publish ? ApiRoute.ENONCE_PUBLISH : ApiRoute.ENONCE_UNPUBLISH).replace('{{nameOrUrl}}', encodeURIComponent(enonce.name)), {});
+            await axios.patch<DojoBackendResponse<null>>(this.getApiUrl(publish ? ApiRoute.ASSIGNMENT_PUBLISH : ApiRoute.ASSIGNMENT_UNPUBLISH).replace('{{nameOrUrl}}', encodeURIComponent(assignment.name)), {});
 
             if ( verbose ) {
-                spinner.succeed(`Enonce ${ enonce.name } successfully ${ publish ? 'published' : 'unpublished' }`);
+                spinner.succeed(`Assignment ${ assignment.name } successfully ${ publish ? 'published' : 'unpublished' }`);
             }
 
             return;
         } catch ( error ) {
             if ( verbose ) {
                 if ( error instanceof AxiosError && error.response ) {
-                    spinner.fail(`Enonce visibility change error: ${ error.response.statusText }`);
+                    spinner.fail(`Assignment visibility change error: ${ error.response.statusText }`);
                 } else {
-                    spinner.fail(`Enonce visibility change error: unknown error`);
+                    spinner.fail(`Assignment visibility change error: unknown error`);
                 }
             }
 
-- 
GitLab