From 814f3fdc4d06cbd83632d26e4c9555c9ca7dabee Mon Sep 17 00:00:00 2001
From: "@mazikiou" <mazikiou@takima.fr>
Date: Tue, 23 Aug 2022 11:33:11 +0200
Subject: [PATCH] fix: ssh setup

---
 .../deadlock-extension/.vscode/launch.json    |  4 +-
 .../deadlock-extension/package-lock.json      |  4 +-
 .../deadlock-extension/package.json           |  2 +-
 .../src/core/api.service.ts                   |  2 +-
 .../deadlock-extension/src/core/config.ts     | 10 ++--
 .../deadlock-extension/src/core/controller.ts | 24 ++++------
 .../src/core/extensionStore.ts                |  2 +-
 .../deadlock-extension/src/core/gitMission.ts | 46 +++++++------------
 .../keycloakOAuth2DeviceFlowConnection.ts     | 12 ++---
 .../src/core/mission/missionDevContainer.ts   | 13 ++----
 .../src/core/sshKeyManager.ts                 | 12 ++---
 .../deadlock-extension/src/core/utils/log.ts  | 15 ++++++
 .../deadlock-extension/src/extension.ts       |  5 +-
 .../src/recorder/recorder.ts                  | 16 +++----
 .../src/recorder/utils/gitea.ts               |  4 +-
 .../src/recorder/utils/log.ts                 | 43 -----------------
 .../src/recorder/utils/permission.ts          |  2 +-
 .../src/recorder/utils/workdir.ts             |  2 +-
 .../src/view/startedMissionsView.ts           |  6 +--
 19 files changed, 89 insertions(+), 135 deletions(-)
 create mode 100644 deadlock-plugins/deadlock-extension/src/core/utils/log.ts
 delete mode 100644 deadlock-plugins/deadlock-extension/src/recorder/utils/log.ts

diff --git a/deadlock-plugins/deadlock-extension/.vscode/launch.json b/deadlock-plugins/deadlock-extension/.vscode/launch.json
index 5804f984..fb0ffe18 100644
--- a/deadlock-plugins/deadlock-extension/.vscode/launch.json
+++ b/deadlock-plugins/deadlock-extension/.vscode/launch.json
@@ -22,7 +22,7 @@
       "env": {
         "DL_MOUNT_EXTENSION": "true",
         // TODO: SETTING: replace with your own extension path
-        "EXTENSION_PATH": "${workspaceFolder}/deadlock-coding-0.1.14.vsix"
+        "EXTENSION_PATH": "${workspaceFolder}/deadlock-coding-0.1.18.vsix"
       },
       "preLaunchTask": "${defaultBuildTask}"
     },
@@ -35,7 +35,7 @@
       "env": {
         "DL_MOUNT_EXTENSION": "true",
         // TODO: SETTING: replace with your own extension path
-        "EXTENSION_PATH": "${workspaceFolder}/deadlock-coding-0.1.14.vsix"
+        "EXTENSION_PATH": "${workspaceFolder}/deadlock-coding-0.1.18.vsix"
       },
       "preLaunchTask": "tasks: build and watch"
     },
diff --git a/deadlock-plugins/deadlock-extension/package-lock.json b/deadlock-plugins/deadlock-extension/package-lock.json
index 10f23c63..edd5752a 100644
--- a/deadlock-plugins/deadlock-extension/package-lock.json
+++ b/deadlock-plugins/deadlock-extension/package-lock.json
@@ -1,12 +1,12 @@
 {
   "name": "deadlock-coding",
-  "version": "0.1.17",
+  "version": "0.1.18",
   "lockfileVersion": 2,
   "requires": true,
   "packages": {
     "": {
       "name": "deadlock-coding",
-      "version": "0.1.17",
+      "version": "0.1.18",
       "dependencies": {
         "@types/tar": "^6.1.1",
         "@vscode/webview-ui-toolkit": "^1.0.0",
diff --git a/deadlock-plugins/deadlock-extension/package.json b/deadlock-plugins/deadlock-extension/package.json
index d6a8c271..468b9d21 100644
--- a/deadlock-plugins/deadlock-extension/package.json
+++ b/deadlock-plugins/deadlock-extension/package.json
@@ -1,7 +1,7 @@
 {
   "name": "deadlock-coding",
   "description": "Deadlock Coding",
-  "version": "0.1.17",
+  "version": "0.1.18",
   "publisher": "Deadlock",
   "icon": "media/logo.png",
   "engines": {
diff --git a/deadlock-plugins/deadlock-extension/src/core/api.service.ts b/deadlock-plugins/deadlock-extension/src/core/api.service.ts
index 703db95c..ed2bcf29 100644
--- a/deadlock-plugins/deadlock-extension/src/core/api.service.ts
+++ b/deadlock-plugins/deadlock-extension/src/core/api.service.ts
@@ -2,13 +2,13 @@ import axios, { AxiosError, AxiosInstance } from 'axios';
 import { API_QUERY_REFERER, API_URL } from '../config';
 import { GiteaPublicProperties } from '../model/giteaPublicProperties.model';
 import { SshKeyPair } from '../model/sshKeyPair.model';
-import { extensionError as error } from '../recorder/utils/log';
 import { Attempt } from '../model/attempt';
 import Controller from './controller';
 import ExtensionStore from './extensionStore';
 import KeycloakOAuth2DeviceFlowConnection from './keycloakOAuth2DeviceFlowConnection';
 import { MissionUser } from './mission/model/missionUser';
 import UserMission, { User } from './mission/model/userMission';
+import { error } from './utils/log';
 
 export default class ApiService {
   private axiosInstance: AxiosInstance;
diff --git a/deadlock-plugins/deadlock-extension/src/core/config.ts b/deadlock-plugins/deadlock-extension/src/core/config.ts
index 1c3a1580..ea506147 100644
--- a/deadlock-plugins/deadlock-extension/src/core/config.ts
+++ b/deadlock-plugins/deadlock-extension/src/core/config.ts
@@ -4,7 +4,11 @@ import isMissionStarted from './utils/mission.util';
 
 const deadlockConfigPath = join(homedir(), '.deadlock');
 
-export const userSshKeyFolderPath = join(deadlockConfigPath, '.ssh');
+export const deadlockPath = '/deadlock';
+
+export function getSshFolderPath() {
+  return isMissionStarted() ? join(deadlockPath, '.ssh') : join(deadlockConfigPath, '.ssh');
+}
 
 export const DOCS_PATH_IC = `${homedir()}/docs`;
 
@@ -14,9 +18,9 @@ export const GITEA_PATH_IC = '/project';
 
 export const MISSION_PATH_IC = `${homedir()}/mission`;
 
-export const MISSION_FILE_IC = join('/', 'deadlock', 'challenge.yaml');
+export const MISSION_FILE_IC = join(deadlockPath, 'challenge.yaml');
 
-export const DEADLOCK_WORKDIR_PATH = isMissionStarted() ? '/deadlock' : deadlockConfigPath;
+export const DEADLOCK_WORKDIR_PATH = isMissionStarted() ? deadlockPath : deadlockConfigPath;
 
 export const USER_MISSION_PATH = join('/home/config/user-challenge.json');
 
diff --git a/deadlock-plugins/deadlock-extension/src/core/controller.ts b/deadlock-plugins/deadlock-extension/src/core/controller.ts
index 70082bee..1a91725c 100644
--- a/deadlock-plugins/deadlock-extension/src/core/controller.ts
+++ b/deadlock-plugins/deadlock-extension/src/core/controller.ts
@@ -3,11 +3,10 @@ import { CommandHandler } from './commandHandler';
 import ExtensionStore from './extensionStore';
 import KeycloakOAuth2DeviceFlowConnection from './keycloakOAuth2DeviceFlowConnection';
 import { MissionDevContainer } from './mission/missionDevContainer';
-import { extensionError, extensionLog as log, extensionWarn } from '../recorder/utils/log';
 import { Command, commands, ExtensionContext, Uri, window } from 'vscode';
 import { createSshKeyFiles } from './sshKeyManager';
 import { emptyDirectories, removeFilesOrDirectories } from '../recorder/utils/workdir';
-import { missionWorkdir, userSshKeyFolderPath } from './config';
+import { missionWorkdir, getSshFolderPath } from './config';
 import { hasStatusNumber } from './utils/typeguards';
 import { existsSync, mkdirSync, readdirSync, renameSync, rmSync, writeFileSync } from 'fs';
 import { extract } from 'tar';
@@ -19,6 +18,7 @@ import Mission from '../model/mission';
 import ApiService from './api.service';
 import StartedMissionsView from '../view/startedMissionsView';
 import AuthenticationView from '../view/authenticationView';
+import { error, log, warn } from './utils/log';
 
 export default class Controller {
   private static _instance: Controller;
@@ -73,7 +73,7 @@ export default class Controller {
       });
     } catch (e) {
       window.showErrorMessage(`Une erreur est survenue lors de la récupération de la solution`);
-      extensionError(e);
+      error(e);
     }
   }
 
@@ -109,28 +109,24 @@ export default class Controller {
 
   public async disconnect() {
     try {
-      await removeFilesOrDirectories(`${userSshKeyFolderPath}/id_rsa`, `${userSshKeyFolderPath}/id_rsa.pub`);
+      await removeFilesOrDirectories(`${getSshFolderPath()}/id_rsa`, `${getSshFolderPath()}/id_rsa.pub`);
     } catch (e) {
-      extensionWarn('Could not remove ssh key files');
-      extensionWarn(e);
+      warn('Could not remove ssh key files', e);
     }
     try {
       await emptyDirectories(missionWorkdir);
     } catch (e) {
-      extensionWarn('Could not empty mission workdir');
-      extensionWarn(e);
+      warn('Could not empty mission workdir', e);
     }
     try {
       await ExtensionStore.instance.clear();
     } catch (e) {
-      extensionWarn('Could not clear extension store');
-      extensionWarn(e);
+      warn('Could not clear extension store', e);
     }
     try {
       await clearDevContainers();
     } catch (e) {
-      extensionWarn('Could not clear dev containers');
-      extensionWarn(e);
+      warn('Could not clear dev containers', e);
     }
     AuthenticationView.instance.isAlreadyConnected = false;
   }
@@ -208,7 +204,7 @@ export default class Controller {
         await this.getSolution(missionId, revieweeId);
       } catch (e) {
         window.showErrorMessage(`Une erreur est survenue lors de la récupération de la solution`);
-        extensionError(e);
+        error(e);
       }
     }
 
@@ -218,7 +214,7 @@ export default class Controller {
       await missionDevcontainer.open();
     } catch (e) {
       window.showErrorMessage('Impossible de lancer la mission');
-      extensionError(e);
+      error(e);
     }
   }
 }
diff --git a/deadlock-plugins/deadlock-extension/src/core/extensionStore.ts b/deadlock-plugins/deadlock-extension/src/core/extensionStore.ts
index ff52c9b6..b31e0262 100644
--- a/deadlock-plugins/deadlock-extension/src/core/extensionStore.ts
+++ b/deadlock-plugins/deadlock-extension/src/core/extensionStore.ts
@@ -1,5 +1,5 @@
 import { SecretStorage } from 'vscode';
-import { extensionLog as log } from '../recorder/utils/log';
+import { log } from './utils/log';
 import Controller from './controller';
 
 export default class ExtensionStore {
diff --git a/deadlock-plugins/deadlock-extension/src/core/gitMission.ts b/deadlock-plugins/deadlock-extension/src/core/gitMission.ts
index b7169e67..af69a29a 100644
--- a/deadlock-plugins/deadlock-extension/src/core/gitMission.ts
+++ b/deadlock-plugins/deadlock-extension/src/core/gitMission.ts
@@ -1,10 +1,10 @@
 import { exec as execCallback } from 'child_process';
 import simpleGit, { SimpleGit, SimpleGitOptions } from 'simple-git';
-import { GITEA_PATH_IC } from './config';
-import { log as customizableLog, error as customizableError } from '../recorder/utils/log';
+import { GITEA_PATH_IC, getSshFolderPath } from './config';
 import { promisify } from 'util';
 import ApiService from './api.service';
 import UserMission from './mission/model/userMission';
+import { error, warn } from './utils/log';
 
 const exec = promisify(execCallback);
 
@@ -21,48 +21,35 @@ export default class GitMission {
     return GitMission._instance;
   }
 
-  private prefix = 'DEADLOCK-RECORDER';
   private git: SimpleGit;
 
   private constructor() {
     const options: Partial<SimpleGitOptions> = {
       baseDir: GITEA_PATH_IC,
       binary: 'git',
-      maxConcurrentProcesses: 2,
+      maxConcurrentProcesses: 1,
     };
 
     this.git = simpleGit(options);
   }
 
-  log(message: any, ...args: any[]) {
-    customizableLog(this.prefix, message, ...args);
-  }
-
-  error(message: any, ...args: any[]) {
-    customizableError(this.prefix, message, ...args);
-  }
-
   async setupSshAgent() {
     try {
       const gitea = await ApiService.instance.getGiteaPublicProperties();
-      await exec(`ssh-add /deadlock/.ssh/id_rsa`);
       await exec(`eval "$(ssh-agent -s)" && ssh-keyscan -p ${gitea.sshPort} -H ${gitea.sshHost} >> ~/.ssh/known_hosts`);
+      await exec(`ssh-add ${getSshFolderPath()}/id_rsa`);
     } catch (err) {
-      this.log(err);
-      if (err instanceof Error) {
-        if (err) {
-          this.error(err.message);
-          throw err;
-        }
-      } else {
-        this.log(`Unhandled error: ${err}`);
-        throw err;
-      }
+      error('Could not add ssh keys', err);
+      throw err;
     }
   }
 
   async forgetSshKeys() {
-    await exec(`ssh-add -d /deadlock/.ssh/id_rsa`);
+    try {
+      await exec(`ssh-add -d ${getSshFolderPath()}/id_rsa`);
+    } catch (err) {
+      warn('Could not remove ssh keys', err);
+    }
   }
 
   async getAuthor(): Promise<string> {
@@ -95,8 +82,8 @@ export default class GitMission {
 
       return Promise.resolve(this);
     } catch (e) {
-      this.error(e);
-      return Promise.reject(e);
+      error('Could not init git', e);
+      throw e;
     }
   }
 
@@ -111,7 +98,8 @@ export default class GitMission {
     try {
       return ((await this.git.remote([])) || '').replace(/(\r\n|\n|\r)/gm, '');
     } catch (e) {
-      this.error(e);
+      error('Could not read remote', e);
+      throw e;
     }
     return '';
   }
@@ -140,8 +128,8 @@ export default class GitMission {
     return this.git.checkout(['-b', branch]);
   }
 
-  createRemoteBranch(branch: Branch = 'master') {
-    return this.git.push(['-u', defaultRemote, branch]);
+  async createRemoteBranch(branch: Branch = 'master') {
+    return await this.git.push(['-u', defaultRemote, branch]);
   }
 
   setUpstream(branch: Branch = 'master') {
diff --git a/deadlock-plugins/deadlock-extension/src/core/keycloakOAuth2DeviceFlowConnection.ts b/deadlock-plugins/deadlock-extension/src/core/keycloakOAuth2DeviceFlowConnection.ts
index dd3adef7..fcd520dc 100644
--- a/deadlock-plugins/deadlock-extension/src/core/keycloakOAuth2DeviceFlowConnection.ts
+++ b/deadlock-plugins/deadlock-extension/src/core/keycloakOAuth2DeviceFlowConnection.ts
@@ -9,12 +9,12 @@ import {
 } from '../config';
 import { HttpStatusCode } from '../customTypings/HttpStatusCode';
 import { TokenFetchErrorCode } from '../customTypings/KeycloakAPITypes';
-import { extensionError as err, extensionLog as log } from '../recorder/utils/log';
 import ExtensionStore from './extensionStore';
 import { commands, window } from 'vscode';
 import Controller from './controller';
 import isMissionStarted from './utils/mission.util';
 import { CommandHandler } from './commandHandler';
+import { error, log } from './utils/log';
 
 process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = REJECT_UNAUTHORIZED ? '1' : '0';
 
@@ -72,14 +72,14 @@ export default class KeycloakOAuth2DeviceFlowConnection {
       }
       case HttpStatusCode.BAD_REQUEST: {
         const badRequestResponse = (await tokenValidationRequestResponse.json()) as FailedAuthenticationResponse;
-        err(`${badRequestResponse.error!}: ${badRequestResponse.error_description}`);
+        error(`${badRequestResponse.error!}: ${badRequestResponse.error_description}`);
         throw new Error(`${badRequestResponse.error}: ${badRequestResponse.error_description}`);
       }
       case HttpStatusCode.UNAUTHORIZED: {
         return Promise.resolve(false);
       }
       default: {
-        err(`Unhandled HTTP status: ${tokenValidationRequestResponseCode}`);
+        error(`Unhandled HTTP status: ${tokenValidationRequestResponseCode}`);
         throw new Error(`Unhandled HTTP status: ${tokenValidationRequestResponseCode}`);
       }
     }
@@ -195,7 +195,7 @@ export default class KeycloakOAuth2DeviceFlowConnection {
         }
 
         default: {
-          err(`Unhandled HTTP status: ${userAuthenticationRequestResponseCode}`);
+          error(`Unhandled HTTP status: ${userAuthenticationRequestResponseCode}`);
           throw new Error(`Unhandled HTTP status: ${userAuthenticationRequestResponseCode}`);
         }
       }
@@ -222,7 +222,7 @@ export default class KeycloakOAuth2DeviceFlowConnection {
             });
           Controller.instance.disconnect();
         }
-        err(`${badRequestResponse.error!}: ${badRequestResponse.error_description}`);
+        error(`${badRequestResponse.error!}: ${badRequestResponse.error_description}`);
         throw new Error('createUserAuthentication: ' + errorCode);
       }
       case TokenFetchErrorCode.authorization_pending: {
@@ -235,7 +235,7 @@ export default class KeycloakOAuth2DeviceFlowConnection {
         break;
       }
       default: {
-        err(`${badRequestResponse.error!}: ${badRequestResponse.error_description}`);
+        error(`${badRequestResponse.error!}: ${badRequestResponse.error_description}`);
         throw new Error(
           `Unhandled error code [ ${badRequestResponse.error!}: ${badRequestResponse.error_description} ]`,
         );
diff --git a/deadlock-plugins/deadlock-extension/src/core/mission/missionDevContainer.ts b/deadlock-plugins/deadlock-extension/src/core/mission/missionDevContainer.ts
index e99c61a7..89167834 100644
--- a/deadlock-plugins/deadlock-extension/src/core/mission/missionDevContainer.ts
+++ b/deadlock-plugins/deadlock-extension/src/core/mission/missionDevContainer.ts
@@ -1,4 +1,4 @@
-import { missionWorkdir, userSshKeyFolderPath } from '../config';
+import { missionWorkdir, getSshFolderPath } from '../config';
 import { Base, DockerfileSpecific, LifecycleScripts, VSCodespecific } from './model/devContainer';
 import { writeFile } from 'fs/promises';
 import { commands, Uri, window } from 'vscode';
@@ -9,9 +9,9 @@ import UserMission from './model/userMission';
 import { REGISTRY_MISSION_URL } from '../../config';
 import { getReviewedStudentWorkdirPath } from '../utils/mission.util';
 import { homedir } from 'os';
-import { extensionError } from '../../recorder/utils/log';
 import { createDirectories } from '../../recorder/utils/workdir';
 import Controller from '../controller';
+import { error } from '../utils/log';
 
 const remoteUserHomeDir = '/home/deadlock';
 const remoteMissionDir = `${remoteUserHomeDir}/mission/`;
@@ -21,10 +21,7 @@ const defaultSettings = {
   'terminal.integrated.profiles.linux': {
     bash: {
       path: '/bin/bash',
-      "args": [
-        "--init-file",
-        "/deadlock/init_bash_env.sh"
-      ]
+      args: ['--init-file', '/deadlock/init_bash_env.sh'],
     },
   },
 };
@@ -92,7 +89,7 @@ export class MissionDevContainer {
     try {
       await UserMission.writeFile(this.missionId, this.missionVersion, this.revieweeId, this.storyId);
     } catch (e) {
-      extensionError(e);
+      error(e);
       window.showErrorMessage(`Une erreur est survenue lors de la création du fichier userChallenge.json`);
     }
   }
@@ -112,7 +109,7 @@ export class MissionDevContainer {
   private async setupMounts() {
     this.mounts.splice(0, this.mounts.length);
     this.mounts.push(
-      `source=${userSshKeyFolderPath},target=/deadlock/.ssh,type=bind,consistency=cached,readonly`,
+      `source=${getSshFolderPath()},target=/deadlock/.ssh,type=bind,consistency=cached,readonly`,
       `source=${UserMission.getMissionUserFolder(
         this.missionId,
         this.revieweeId,
diff --git a/deadlock-plugins/deadlock-extension/src/core/sshKeyManager.ts b/deadlock-plugins/deadlock-extension/src/core/sshKeyManager.ts
index 7526d29d..ba3f54c5 100644
--- a/deadlock-plugins/deadlock-extension/src/core/sshKeyManager.ts
+++ b/deadlock-plugins/deadlock-extension/src/core/sshKeyManager.ts
@@ -1,23 +1,23 @@
 import { existsSync, promises } from 'fs';
-import { userSshKeyFolderPath } from './config';
+import { getSshFolderPath } from './config';
 
 export function isSshKeyPairExist(): boolean {
   return isPrivateKeyExist() && isPublicKeyExist();
 }
 
 function isPublicKeyExist(): boolean {
-  return existsSync(`${userSshKeyFolderPath}/id_rsa.pub`);
+  return existsSync(`${getSshFolderPath()}/id_rsa.pub`);
 }
 
 function isPrivateKeyExist(): boolean {
-  return existsSync(`${userSshKeyFolderPath}/id_rsa`);
+  return existsSync(`${getSshFolderPath()}/id_rsa`);
 }
 
 export async function createSshKeyFiles(publicKey: string, privateKey: string) {
-  await createSshKeyFolderIfNotExist(userSshKeyFolderPath);
-  await promises.writeFile(`${userSshKeyFolderPath}/id_rsa.pub`, publicKey);
+  await createSshKeyFolderIfNotExist(getSshFolderPath());
+  await promises.writeFile(`${getSshFolderPath()}/id_rsa.pub`, publicKey);
 
-  await promises.writeFile(`${userSshKeyFolderPath}/id_rsa`, privateKey, { mode: 0o600 });
+  await promises.writeFile(`${getSshFolderPath()}/id_rsa`, privateKey, { mode: 0o600 });
 }
 
 async function createSshKeyFolderIfNotExist(sshKeyFolderPath) {
diff --git a/deadlock-plugins/deadlock-extension/src/core/utils/log.ts b/deadlock-plugins/deadlock-extension/src/core/utils/log.ts
new file mode 100644
index 00000000..233ed0cf
--- /dev/null
+++ b/deadlock-plugins/deadlock-extension/src/core/utils/log.ts
@@ -0,0 +1,15 @@
+const logTemplate = (logFunction: (_: any) => void, prefix: string, message: any, ...args: any[]) => {
+  console.log(`[${prefix}] ${message}`, ...args);
+};
+
+export function error(message: any, ...args: any[]) {
+  logTemplate(console.error, 'DEADLOCK-EXTENSION', message, ...args);
+}
+
+export function log(message: any, ...args: any[]) {
+  logTemplate(console.log, 'DEADLOCK-EXTENSION', message, ...args);
+}
+
+export function warn(message: any, ...args: any[]) {
+  logTemplate(console.warn, 'DEADLOCK-EXTENSION', message, ...args);
+}
diff --git a/deadlock-plugins/deadlock-extension/src/extension.ts b/deadlock-plugins/deadlock-extension/src/extension.ts
index 8d4d6480..f9399e57 100644
--- a/deadlock-plugins/deadlock-extension/src/extension.ts
+++ b/deadlock-plugins/deadlock-extension/src/extension.ts
@@ -1,12 +1,12 @@
 import { window, ExtensionContext, workspace, commands } from 'vscode';
 import Recorder from './recorder/recorder';
-import { extensionError as error } from './recorder/utils/log';
 import { DepNodeProvider } from './view/deadlockPanel';
 import { CommandTreeProvider } from './view/CommandTree';
 import Controller from './core/controller';
 import isMissionStarted from './core/utils/mission.util';
 import AuthenticationView from './view/authenticationView';
 import KeycloakOAuth2DeviceFlowConnection from './core/keycloakOAuth2DeviceFlowConnection';
+import { error } from './core/utils/log';
 
 export async function activate(context: ExtensionContext) {
   new Controller(context);
@@ -25,8 +25,7 @@ export async function activate(context: ExtensionContext) {
     try {
       Recorder.instance.run();
     } catch (e) {
-      error('Could not run recorder');
-      error(JSON.stringify(e));
+      error('Could not run recorder', e);
       window.showErrorMessage("Le recorder n'a pas pu être lancé");
     }
   } else {
diff --git a/deadlock-plugins/deadlock-extension/src/recorder/recorder.ts b/deadlock-plugins/deadlock-extension/src/recorder/recorder.ts
index 20b1cd8b..20337527 100644
--- a/deadlock-plugins/deadlock-extension/src/recorder/recorder.ts
+++ b/deadlock-plugins/deadlock-extension/src/recorder/recorder.ts
@@ -1,14 +1,14 @@
 import { ENABLE_AUTOMATIC_SAVE } from '../config';
 import GitMission from '../core/gitMission';
 import { MISSION_PATH_IC, GITEA_PATH_IC } from '../core/config';
-import { recorderError as error } from './utils/log';
 import AutomaticSave from './services/automatic-save';
 import { clearFilesExceptGit, copyProjectSources, renameTempToUserGitFiles } from './utils/workdir';
-import aquirePermissions from './utils/permission';
+import acquirePermissions from './utils/permission';
 import UserMission from '../core/mission/model/userMission';
 import ApiService from '../core/api.service';
 import { window } from 'vscode';
 import { pushOnCommitQueueIfNotReviewing } from './utils/gitea';
+import { error } from '../core/utils/log';
 
 export default class Recorder {
   private static _instance?: Recorder;
@@ -58,30 +58,28 @@ export default class Recorder {
 
   async run() {
     try {
-      await aquirePermissions();
+      await acquirePermissions();
       await GitMission.instance.init();
       const isStarted = await GitMission.instance.isRemoteRepoExist();
       await this.setupFromRemoteRepo(GitMission.instance);
       await this.setupProject(GitMission.instance);
       if (!isStarted) {
-        pushOnCommitQueueIfNotReviewing(GitMission.instance, 'Auto');
+        await pushOnCommitQueueIfNotReviewing(GitMission.instance, 'Auto');
       }
       if (!this.userMission.isReviewing()) {
         if (ENABLE_AUTOMATIC_SAVE) {
           new AutomaticSave(MISSION_PATH_IC, GitMission.instance);
         }
         try {
-          runTimer();
+          await runTimer();
         } catch (e) {
-          error('Error while setup automatic save');
-          error(e);
+          error('Error while setup automatic save', e);
         }
       } else {
         await GitMission.instance.forgetSshKeys();
       }
     } catch (e) {
-      error('Cannot setup user repo.');
-      error(e);
+      error('Cannot setup user repo.', e);
     }
   }
 }
diff --git a/deadlock-plugins/deadlock-extension/src/recorder/utils/gitea.ts b/deadlock-plugins/deadlock-extension/src/recorder/utils/gitea.ts
index d3aaa558..be3cdb51 100644
--- a/deadlock-plugins/deadlock-extension/src/recorder/utils/gitea.ts
+++ b/deadlock-plugins/deadlock-extension/src/recorder/utils/gitea.ts
@@ -6,10 +6,10 @@ import { existsSync, promises } from 'fs';
 import { join } from 'path';
 import { CommitResult, GitError } from 'simple-git';
 import { queue } from 'async';
-import { extensionWarn, recorderError as error, recorderLog as log } from './log';
 import { clearFilesExceptGit, copyGitUserFiles } from './workdir';
 import { copyFile } from 'fs/promises';
 import UserMission from '../../core/mission/model/userMission';
+import { error, log, warn } from '../../core/utils/log';
 
 const { readFile } = promises;
 
@@ -101,7 +101,7 @@ async function mergeMaster(gitMission: GitMission): Promise<CommitResult> {
 
 export async function getIgnorePatternFromIgnoreFile(ignoreFile: string): Promise<string[]> {
   if (!existsSync(ignoreFile)) {
-    extensionWarn(`${ignoreFile} not found`);
+    warn(`${ignoreFile} not found`);
     return ['.git'];
   }
 
diff --git a/deadlock-plugins/deadlock-extension/src/recorder/utils/log.ts b/deadlock-plugins/deadlock-extension/src/recorder/utils/log.ts
deleted file mode 100644
index e231a4d4..00000000
--- a/deadlock-plugins/deadlock-extension/src/recorder/utils/log.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-export const log = (prefix: string, message: any, ...args: any[]) => {
-  if (args) {
-    console.log(`[${prefix}] ${message}`, ...args);
-  } else {
-    console.log(`[${prefix}] ${message}`);
-  }
-};
-
-export const warn = (prefix: string, message: any, ...args: any[]) => {
-  if (args) {
-    console.warn(`[${prefix}] ${message}`, ...args);
-  } else {
-    console.warn(`[${prefix}] ${message}`);
-  }
-};
-
-export const error = (prefix: string, message: any, ...args: any[]) => {
-  if (args) {
-    console.error(`[${prefix}] ${message}`, ...args);
-  } else {
-    console.error(`[${prefix}] ${message}`);
-  }
-};
-
-export function recorderError(message: any, ...args: any[]) {
-  error('DEADLOCK-RECORDER', message, ...args);
-}
-
-export function recorderLog(message: any, ...args: any[]) {
-  log('DEADLOCK-RECORDER', message, ...args);
-}
-
-export function extensionError(message: any, ...args: any[]) {
-  error('DEADLOCK-EXTENSION', message, ...args);
-}
-
-export function extensionLog(message: any, ...args: any[]) {
-  log('DEADLOCK-EXTENSION', message, ...args);
-}
-
-export function extensionWarn(message: any, ...args: any[]) {
-  warn('DEADLOCK-EXTENSION', message, ...args);
-}
diff --git a/deadlock-plugins/deadlock-extension/src/recorder/utils/permission.ts b/deadlock-plugins/deadlock-extension/src/recorder/utils/permission.ts
index 6ad541f5..cce9f6a7 100644
--- a/deadlock-plugins/deadlock-extension/src/recorder/utils/permission.ts
+++ b/deadlock-plugins/deadlock-extension/src/recorder/utils/permission.ts
@@ -5,6 +5,6 @@ import { GITEA_PATH_IC } from '../../core/config';
 
 const exec = promisify(execCallback);
 
-export default async function aquirePermissions() {
+export default async function acquirePermissions() {
   await exec(`sudo chown -R ${userInfo().username} ${GITEA_PATH_IC}`);
 }
diff --git a/deadlock-plugins/deadlock-extension/src/recorder/utils/workdir.ts b/deadlock-plugins/deadlock-extension/src/recorder/utils/workdir.ts
index 5bac1dfb..d8e228f5 100644
--- a/deadlock-plugins/deadlock-extension/src/recorder/utils/workdir.ts
+++ b/deadlock-plugins/deadlock-extension/src/recorder/utils/workdir.ts
@@ -1,10 +1,10 @@
 import { exec as execCallback, execSync } from 'child_process';
 import { copyFileSync, existsSync, PathLike, readdirSync, renameSync } from 'fs';
 import { join } from 'path';
-import { log } from './log';
 import { promisify } from 'util';
 import { mkdir, rm } from 'fs/promises';
 import Controller from '../../core/controller';
+import { log } from '../../core/utils/log';
 
 const exec = promisify(execCallback);
 const PREFIX = '[DEADLOCK-RECORDER]';
diff --git a/deadlock-plugins/deadlock-extension/src/view/startedMissionsView.ts b/deadlock-plugins/deadlock-extension/src/view/startedMissionsView.ts
index 3277b2cb..fafea1b6 100644
--- a/deadlock-plugins/deadlock-extension/src/view/startedMissionsView.ts
+++ b/deadlock-plugins/deadlock-extension/src/view/startedMissionsView.ts
@@ -3,12 +3,12 @@ import { existsSync, readdirSync, readFileSync } from 'fs';
 import { join } from 'path';
 import { Webview, WebviewView, WebviewViewProvider } from 'vscode';
 import Controller from '../core/controller';
-import { extensionWarn } from '../recorder/utils/log';
 import { getUri } from './webViewBase';
 import { missionWorkdir } from '../core/config';
 import { watch } from 'chokidar';
 import UserMission from '../core/mission/model/userMission';
 import { getExtensionUri } from '../recorder/utils/workdir';
+import { warn } from '../core/utils/log';
 
 export default class StartedMissionsView implements WebviewViewProvider {
   constructor() {
@@ -77,10 +77,10 @@ export default class StartedMissionsView implements WebviewViewProvider {
             );
             missionsHtml += `<vscode-button onclick="openMission('${userChallenge.missionId}')" class="item" appearance="primary">${userChallenge.missionId}</vscode-button>`;
           } catch (e) {
-            extensionWarn(`Failed to read user-challenge.json for mission ${mission}`);
+            warn(`Failed to read user-challenge.json for mission ${mission}`);
           }
         } else {
-          extensionWarn(`Folder '${mission}' has no user-challenge.json file so it is ignored.`);
+          warn(`Folder '${mission}' has no user-challenge.json file so it is ignored.`);
         }
       });
 
-- 
GitLab