From d58e123e7013208ff3f03d07d2c336ff2950b617 Mon Sep 17 00:00:00 2001
From: Guillaume WEBER <gweber@takima.fr>
Date: Wed, 29 Jun 2022 14:44:44 +0000
Subject: [PATCH] feat: make mission workdir constant

---
 .../deadlock-extension/package.json           | 16 +------
 .../src/core/commandHandler.ts                |  5 --
 .../deadlock-extension/src/core/config.ts     |  1 +
 .../deadlock-extension/src/core/controller.ts | 46 +++----------------
 .../src/core/extensionStore.ts                | 33 +------------
 .../src/core/mission/missionDevContainer.ts   |  5 +-
 .../src/recorder/utils/workdir.ts             | 21 +++++----
 .../src/view/quickSetupView.ts                | 16 +------
 .../src/view/startedMissionsView.ts           | 15 +++---
 9 files changed, 31 insertions(+), 127 deletions(-)

diff --git a/deadlock-plugins/deadlock-extension/package.json b/deadlock-plugins/deadlock-extension/package.json
index 162f9db1..5cacb477 100644
--- a/deadlock-plugins/deadlock-extension/package.json
+++ b/deadlock-plugins/deadlock-extension/package.json
@@ -30,11 +30,6 @@
         "title": "Open Deadlock quick setup page",
         "category": "Deadlock Coding"
       },
-      {
-        "command": "deadlock.chooseMissionWorkdir",
-        "title": "Choose mission workdir",
-        "category": "Deadlock Coding"
-      },
       {
         "command": "deadlock.disconnect",
         "title": "Clear cache",
@@ -83,16 +78,7 @@
         "view": "help",
         "contents": "[Quick Setup](command:deadlock.openQuickSetup)\n"
       }
-    ],
-    "menus": {
-      "view/title": [
-        {
-          "command": "deadlock.chooseMissionWorkdir",
-          "when": "view == deadlockPanel"
-        }
-      ],
-      "view/item/context": []
-    }
+    ]
   },
   "scripts": {
     "vsce-package": "vsce package --allow-missing-repository",
diff --git a/deadlock-plugins/deadlock-extension/src/core/commandHandler.ts b/deadlock-plugins/deadlock-extension/src/core/commandHandler.ts
index fe216cce..85a89d5c 100644
--- a/deadlock-plugins/deadlock-extension/src/core/commandHandler.ts
+++ b/deadlock-plugins/deadlock-extension/src/core/commandHandler.ts
@@ -7,16 +7,11 @@ export class CommandHandler {
   }
 
   initCommandHandler() {
-    commands.registerCommand(
-      chooseMissionWorkdirCommand.cmd,
-      this.controller.chooseMissionWorkdir.bind(this.controller),
-    );
     commands.registerCommand(disconnectCommand.cmd, this.controller.disconnect.bind(this.controller));
     commands.registerCommand(authenticateCommand.cmd, this.controller.authenticate.bind(this.controller));
   }
 }
 
-export const chooseMissionWorkdirCommand = new Command('Choose mission workdir', 'deadlock.chooseMissionWorkdir');
 export const disconnectCommand = new Command('Disconnect', 'deadlock.disconnect');
 export const authenticateCommand = new Command('Authenticate', 'deadlock.authenticate');
 export const openUrlInBrowserCommand = new Command('Open url in browser', 'vscode.open');
diff --git a/deadlock-plugins/deadlock-extension/src/core/config.ts b/deadlock-plugins/deadlock-extension/src/core/config.ts
index 9dbf60ba..93934ffa 100644
--- a/deadlock-plugins/deadlock-extension/src/core/config.ts
+++ b/deadlock-plugins/deadlock-extension/src/core/config.ts
@@ -10,6 +10,7 @@ const deadlockExtensionPath = path.join(homeDir, 'deadlock-extension');
 
 const deadlockConfigPath = path.join(homeDir, '.deadlock');
 export const userSshKeyFolderPath = path.join(deadlockConfigPath, '.ssh');
+export const missionWorkdir = path.join(deadlockConfigPath, 'workspace');
 
 export const PROJECT_SRC_PATH = onContainer ? '/project' : path.join(homeDir, 'deadlock-extension', '/project');
 
diff --git a/deadlock-plugins/deadlock-extension/src/core/controller.ts b/deadlock-plugins/deadlock-extension/src/core/controller.ts
index 70a7e01f..90076cca 100644
--- a/deadlock-plugins/deadlock-extension/src/core/controller.ts
+++ b/deadlock-plugins/deadlock-extension/src/core/controller.ts
@@ -1,7 +1,7 @@
 import { KEYCLOAK_DEVICE_AUTH_URL, KEYCLOAK_TOKEN_CREATE_URL, KEYCLOAK_USER_INFO_URL } from '../config';
 import BriefingView from '../view/briefingView';
 import QuickSetupView from '../view/quickSetupView';
-import { chooseMissionWorkdirCommand, CommandHandler, openUrlInBrowserCommand } from './commandHandler';
+import { CommandHandler, openUrlInBrowserCommand } from './commandHandler';
 import ExtensionStore from './extensionStore';
 import KeycloakOAuth2DeviceFlowConnection from './keycloakOAuth2DeviceFlowConnection';
 import ApiService from './api.service';
@@ -11,8 +11,8 @@ import { MissionDevContainer } from './mission/missionDevContainer';
 import { extensionLog as log } from '../recorder/utils/log';
 import { commands, ExtensionContext, Uri, window } from 'vscode';
 import { createSshKeyFiles } from './sshKeyManager';
-import { removeFiles } from '../recorder/utils/workdir';
-import { userSshKeyFolderPath } from './config';
+import { emptyDirectories, removeFilesOrDirectories } from '../recorder/utils/workdir';
+import { missionWorkdir, userSshKeyFolderPath } from './config';
 import { hasStatusNumber } from './utils/typeguards';
 import { User } from './mission/models/userChallenge';
 
@@ -79,28 +79,9 @@ export default class Controller {
     this.quickSetupView.isAlreadyConnected = (await this.extensionStore.getAccessToken()) !== undefined;
   }
 
-  async chooseMissionWorkdir() {
-    const actualMissionWorkDir = this.extensionStore.getMissionWorkdir();
-
-    const folderUri = await window.showOpenDialog({
-      defaultUri: actualMissionWorkDir ? Uri.file(actualMissionWorkDir) : undefined,
-      canSelectFolders: true,
-      canSelectFiles: false,
-      title: 'Choisis le dossier qui contiendra tes missions',
-    });
-
-    if (!folderUri) {
-      if (this.extensionStore.getMissionWorkdir()) {
-        return;
-      }
-      this.chooseMissionWorkdir();
-    } else {
-      this.extensionStore.setMissionWorkdir(folderUri[0].path);
-    }
-  }
-
   public async disconnect() {
-    await removeFiles(`${userSshKeyFolderPath}/id_rsa`, `${userSshKeyFolderPath}/id_rsa.pub`);
+    await removeFilesOrDirectories(`${userSshKeyFolderPath}/id_rsa`, `${userSshKeyFolderPath}/id_rsa.pub`);
+    await emptyDirectories(missionWorkdir);
     await this.extensionStore.clear();
     this.quickSetupView.isAlreadyConnected = false;
   }
@@ -125,10 +106,6 @@ export default class Controller {
 
   public async launchMission(missionId: string, missionVersion: string, userId?: string) {
     window.showInformationMessage(`vous lancez la mission ${missionId}`);
-    const hadMissionWorkdir = this.extensionStore.getMissionWorkdir() !== undefined;
-    if (!hadMissionWorkdir) {
-      await commands.executeCommand(chooseMissionWorkdirCommand.cmd);
-    }
 
     const hadBeenConnected = (await this.extensionStore.getAccessToken()) !== undefined;
 
@@ -139,8 +116,6 @@ export default class Controller {
 
     const mission = new Mission(missionId, missionVersion);
 
-    const missionsWorkdir = this.extensionStore.getMissionWorkdir() ?? '';
-
     const currentUser = await this.apiService.getCurrentUser();
 
     const user: User = userId ? await this.apiService.getUser(userId) : currentUser;
@@ -162,17 +137,8 @@ export default class Controller {
 
     const giteaPublicProperties: GiteaPublicProperties = await this.apiService.getGiteaPublicProperties();
 
-    const missionDevcontainer = new MissionDevContainer(
-      missionsWorkdir,
-      user,
-      currentUser,
-      mission,
-      giteaPublicProperties,
-    );
+    const missionDevcontainer = new MissionDevContainer(user, currentUser, mission, giteaPublicProperties);
 
-    window.showInformationMessage(
-      'opening inside folder ' + this.extensionStore.getMissionWorkdir()! + '/' + missionId,
-    );
     try {
       await missionDevcontainer.open();
     } catch {
diff --git a/deadlock-plugins/deadlock-extension/src/core/extensionStore.ts b/deadlock-plugins/deadlock-extension/src/core/extensionStore.ts
index 17cd1edf..2ccb8749 100644
--- a/deadlock-plugins/deadlock-extension/src/core/extensionStore.ts
+++ b/deadlock-plugins/deadlock-extension/src/core/extensionStore.ts
@@ -1,25 +1,15 @@
-import { ExtensionContext, Memento, SecretStorage, window } from 'vscode';
+import { ExtensionContext, SecretStorage } from 'vscode';
 import { extensionLog as log } from '../recorder/utils/log';
 
-type GlobalStorageType = Memento & { setKeysForSync(keys: readonly string[]): void };
-
 export default class ExtensionStore {
   private static instance: ExtensionStore;
-
-  private globalStorage: GlobalStorageType;
   private secretStorage: SecretStorage;
 
   private constructor(context: ExtensionContext) {
-    this.globalStorage = context.globalState;
     this.secretStorage = context.secrets;
   }
 
   public async clear() {
-    if (this.globalStorage.keys()) {
-      for (const key of this.globalStorage.keys()) {
-        this.globalStorage.update(key, undefined);
-      }
-    }
     if (await this.secretStorage.get(SecretStoreKey.AccessTokenKey))
       this.secretStorage.delete(SecretStoreKey.AccessTokenKey);
     if (await this.secretStorage.get(SecretStoreKey.RefreshTokenKey))
@@ -35,23 +25,6 @@ export default class ExtensionStore {
     return ExtensionStore.instance;
   }
 
-  public getMissionWorkdir(): string | undefined {
-    return this.readStringKey(GlobalStoreKey.MissionWorkdirKey);
-  }
-
-  public setMissionWorkdir(path: string) {
-    this.storeStringKey(GlobalStoreKey.MissionWorkdirKey, path);
-    window.showInformationMessage(`Nouveau dossier de stockage des missions: ${path}`);
-  }
-
-  private readStringKey(key: GlobalStoreKey): string | undefined {
-    return this.globalStorage.get<string>(key);
-  }
-
-  private storeStringKey(key: GlobalStoreKey, value: string) {
-    this.globalStorage.update(key, value);
-  }
-
   public getAccessToken(): Thenable<string | undefined> {
     return this.readSecret(SecretStoreKey.AccessTokenKey);
   }
@@ -89,7 +62,3 @@ enum SecretStoreKey {
   AccessTokenKey = 'access-token-key',
   RefreshTokenKey = 'refresh-token-key',
 }
-
-enum GlobalStoreKey {
-  MissionWorkdirKey = 'mission-workdir-key',
-}
diff --git a/deadlock-plugins/deadlock-extension/src/core/mission/missionDevContainer.ts b/deadlock-plugins/deadlock-extension/src/core/mission/missionDevContainer.ts
index 9b2e8a6c..cffd1d2b 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 { userSshKeyFolderPath } from '../config';
+import { missionWorkdir, userSshKeyFolderPath } from '../config';
 import { Base, DockerfileSpecific, LifecycleScripts, VSCodespecific } from './models/devContainer';
 import { mkdir, writeFile } from 'fs/promises';
 import { Mission } from './models/mission';
@@ -31,13 +31,12 @@ export class MissionDevContainer {
   private readonly dirs: { missionWorkdir: string; config: string; devcontainer: string; mounted: string };
 
   constructor(
-    private readonly missionsWorkdir: string,
     private readonly user: User,
     private readonly currentUser: User,
     private readonly mission: Mission,
     private readonly giteaProperties: GiteaPublicProperties,
   ) {
-    let prefix = `${this.missionsWorkdir}`;
+    let prefix = missionWorkdir;
     if (this.isReviewingStudent()) {
       prefix += `/students/${this.user.details.lastName}_${this.user.details.firstName}_${this.user.id}`;
     }
diff --git a/deadlock-plugins/deadlock-extension/src/recorder/utils/workdir.ts b/deadlock-plugins/deadlock-extension/src/recorder/utils/workdir.ts
index f528864d..c81a9abe 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, lstatSync, PathLike, readdirSync, renameSync, rmdirSync, promises } from 'fs';
+import { copyFileSync, existsSync, lstatSync, PathLike, readdirSync, renameSync, rmdirSync } from 'fs';
 import { join } from 'path';
 import { log } from './log';
 import { promisify } from 'util';
+import { mkdir, rmdir, unlink, rm } from 'fs/promises';
 
-const { unlink } = promises;
 const exec = promisify(execCallback);
 const PREFIX = '[DEADLOCK-RECORDER]';
 
@@ -82,10 +82,15 @@ export function copyGitUserFiles(srcPath: string, destPath: string, userId: stri
   copyFileIfExistsSync(join(srcPath, '.gitignore'), join(destPath, `user-gitignore-${userId}`));
 }
 
-export async function removeFiles(...paths: string[]) {
-  for (const path of paths) {
-    if (existsSync(path)) {
-      await unlink(path);
-    }
-  }
+export async function removeFilesOrDirectories(...paths: string[]) {
+  paths.filter(existsSync).forEach(async (path) => {
+    await rm(path, { recursive: true });
+  });
+}
+
+export async function emptyDirectories(...directoryPaths: string[]) {
+  directoryPaths.forEach(async (directoryPath) => {
+    await rmdir(directoryPath, { recursive: true });
+    await mkdir(directoryPath);
+  });
 }
diff --git a/deadlock-plugins/deadlock-extension/src/view/quickSetupView.ts b/deadlock-plugins/deadlock-extension/src/view/quickSetupView.ts
index 5fb0bc98..c64d69c1 100644
--- a/deadlock-plugins/deadlock-extension/src/view/quickSetupView.ts
+++ b/deadlock-plugins/deadlock-extension/src/view/quickSetupView.ts
@@ -1,5 +1,5 @@
 import { commands, Uri } from 'vscode';
-import { authenticateCommand, chooseMissionWorkdirCommand, disconnectCommand } from '../core/commandHandler';
+import { authenticateCommand, disconnectCommand } from '../core/commandHandler';
 import ExtensionStore from '../core/extensionStore';
 import { extensionLog as log } from '../recorder/utils/log';
 import { openQuickSetupCommand } from '../theia/command';
@@ -38,8 +38,6 @@ export default class QuickSetupView extends WebviewBase {
   }
 
   renderHtmlBody() {
-    const hadMissionWorkdir = this.extensionStore.getMissionWorkdir() !== undefined;
-
     return `
          <h1>Quick Setup</h1>
          <div class="deadlock-getting-started-card-container">
@@ -58,15 +56,6 @@ export default class QuickSetupView extends WebviewBase {
                   },
               this._isAlreadyConnected,
             )}
-            ${this.renderCardHtml(
-              'Dossier contenant tes exercices',
-              'Choisis le dossier qui contiendra tous les exercices Deadlock.',
-              {
-                name: 'Choisir un dossier',
-                onClickFunctionName: 'launchChooseMissionWorkdirAction',
-              },
-              hadMissionWorkdir,
-            )}
          </div>
          `;
   }
@@ -124,9 +113,6 @@ export default class QuickSetupView extends WebviewBase {
 
   onMessageReceive(message: any): void {
     switch (message.command) {
-      case 'launchChooseMissionWorkdirAction':
-        commands.executeCommand(chooseMissionWorkdirCommand.cmd).then(() => this.reload());
-        return;
       case 'openAuthenticationPageAction':
         commands.executeCommand(authenticateCommand.cmd);
         return;
diff --git a/deadlock-plugins/deadlock-extension/src/view/startedMissionsView.ts b/deadlock-plugins/deadlock-extension/src/view/startedMissionsView.ts
index 463d15df..75378b46 100644
--- a/deadlock-plugins/deadlock-extension/src/view/startedMissionsView.ts
+++ b/deadlock-plugins/deadlock-extension/src/view/startedMissionsView.ts
@@ -2,17 +2,15 @@ import { randomBytes } from 'crypto';
 import { existsSync, readdirSync, readFileSync } from 'fs';
 import { join } from 'path';
 import { ExtensionContext, Webview, WebviewView, WebviewViewProvider, window } from 'vscode';
+import { missionWorkdir } from '../core/config';
 import Controller from '../core/controller';
-import ExtensionStore from '../core/extensionStore';
 import UserChallenge from '../core/mission/models/userChallenge';
 import { extensionWarn } from '../recorder/utils/log';
 import { getUri } from './webviewBase';
 
 export default class StartedMissionsView implements WebviewViewProvider {
   private readonly controller: Controller;
-  private readonly missionsWorkdir: string;
   constructor(private context: ExtensionContext) {
-    this.missionsWorkdir = ExtensionStore.getInstance(this.context).getMissionWorkdir() ?? '';
     this.controller = Controller.getInstance(context);
   }
 
@@ -30,7 +28,7 @@ export default class StartedMissionsView implements WebviewViewProvider {
     switch (message.command) {
       case 'openMission': {
         window.showInformationMessage('click received: ' + message.mission);
-        const path = join(this.missionsWorkdir, message.mission, '.config', 'user-challenge.json');
+        const path = join(missionWorkdir, message.mission, '.config', 'user-challenge.json');
         const userChallenge: UserChallenge = JSON.parse(readFileSync(path, 'utf8'));
         this.controller.launchMission(userChallenge.missionId, userChallenge.missionVersion);
         return;
@@ -52,14 +50,13 @@ export default class StartedMissionsView implements WebviewViewProvider {
     const js = getUri(webview, this.context.extensionUri, ['resources', 'js', 'startedMissionsView.js']);
 
     // find all folders in mission directory
-    const path = this.missionsWorkdir;
     let missionsHtml = '';
-    existsSync(path) &&
-      readdirSync(path).forEach((mission) => {
-        if (existsSync(join(path, mission, '.config', 'user-challenge.json'))) {
+    existsSync(missionWorkdir) &&
+      readdirSync(missionWorkdir).forEach((mission) => {
+        if (existsSync(join(missionWorkdir, mission, '.config', 'user-challenge.json'))) {
           try {
             const userChallenge: UserChallenge = JSON.parse(
-              readFileSync(join(path, mission, '.config', 'user-challenge.json'), 'utf8'),
+              readFileSync(join(missionWorkdir, mission, '.config', 'user-challenge.json'), 'utf8'),
             );
             missionsHtml += `<vscode-button onclick="openMission('${userChallenge.missionId}')" class="item" appearance="primary">${userChallenge.missionId}</vscode-button>`;
           } catch (e) {
-- 
GitLab