Skip to content
Snippets Groups Projects
Commit 3c6b35f9 authored by Christian ZHENG's avatar Christian ZHENG
Browse files

Merge branch 'feat-local-dev' into develop

parents edf0d660 6d677cdd
No related branches found
No related tags found
3 merge requests!14feat: added mounted, .bashrc, .zshrc, added tests, added keycloak tests,!8feat(extension): login, open in devcontainer, automaticly save code, open briefing, publish extension,!3feat: devcontainer support
......@@ -64,7 +64,7 @@
{
"id": "help",
"name": "Help",
"visibility": "collapsed"
"visibility": "visible"
}
]
},
......
......@@ -2,4 +2,4 @@ export const KEYCLOAK_DEVICE_AUTH_URL =
'https://auth.deadlock.io/auth/realms/Deadlock/protocol/openid-connect/auth/device';
export const KEYCLOAK_TOKEN_CREATE_URL = 'https://auth.deadlock.io/auth/realms/Deadlock/protocol/openid-connect/token';
export const KEYCLOAK_USER_INFO_URL = 'https://auth.deadlock.io/auth/realms/Deadlock/protocol/openid-connect/userinfo';
export const REJECT_UNAUTHORIZED = true;
export const REGISTRY_MISSION_URL = 'registry.e-biz.fr/deadlock/deadlock-challenges';
......@@ -4,4 +4,4 @@ export const KEYCLOAK_TOKEN_CREATE_URL =
'https://auth.staging.deadlock.io/auth/realms/Deadlock/protocol/openid-connect/token';
export const KEYCLOAK_USER_INFO_URL =
'https://auth.staging.deadlock.io/auth/realms/Deadlock/protocol/openid-connect/userinfo';
export const REJECT_UNAUTHORIZED = true;
export const REGISTRY_MISSION_URL = 'registry.e-biz.fr/deadlock/deadlock-challenges';
......@@ -4,4 +4,4 @@ export const KEYCLOAK_TOKEN_CREATE_URL =
'https://auth.dev.deadlock.io/auth/realms/Deadlock/protocol/openid-connect/token';
export const KEYCLOAK_USER_INFO_URL =
'https://auth.dev.deadlock.io/auth/realms/Deadlock/protocol/openid-connect/userinfo';
export const REJECT_UNAUTHORIZED = false;
export const REGISTRY_MISSION_URL = 'registry.e-biz.fr/deadlock/deadlock-challenges';
import * as vscode from 'vscode';
import { KEYCLOAK_DEVICE_AUTH_URL, KEYCLOAK_TOKEN_CREATE_URL, KEYCLOAK_USER_INFO_URL } from '../config';
import { OPEN_QUICK_SETUP_COMMAND } from '../theia/command';
import {
KEYCLOAK_DEVICE_AUTH_URL,
KEYCLOAK_TOKEN_CREATE_URL,
KEYCLOAK_USER_INFO_URL,
REGISTRY_MISSION_URL,
} from '../config';
import { log } from '../recorder/utils';
import { OPEN_GETTING_STARTED_COMMAND } from '../theia/command';
import BriefingView from '../view/briefingView';
import QuickSetupView from '../view/quickSetupView';
import { CHOOSE_MISSION_WORKDIR_COMMAND, CommandHandler, OPEN_URL_IN_BROWSER_COMMAND } from './commandHandler';
import ExtensionStore from './extensionStore';
import KeycloakOAuth2DeviceFlowConnection from './keycloakOAuth2DeviceFlowConnection';
import Mission from './mission';
export default class Controller {
public connection: KeycloakOAuth2DeviceFlowConnection;
......@@ -33,10 +40,11 @@ export default class Controller {
handleUri(uri: vscode.Uri) {
const queryParams: URLSearchParams = new URLSearchParams(uri.query);
const action: string | null = queryParams.get('action');
log('Opening link', uri);
switch (action) {
case 'open-challenge':
that.launchMission(queryParams.get('missionId'));
that.launchMission(queryParams.get('missionId'), queryParams.get('missionVersion'));
break;
default:
......@@ -86,8 +94,8 @@ export default class Controller {
vscode.commands.executeCommand(OPEN_URL_IN_BROWSER_COMMAND.cmd, vscode.Uri.parse(url));
}
public async launchMission(missionId: string | null) {
if (missionId) {
public async launchMission(missionId: string | null, missionVersion: string | null) {
if (missionId && missionVersion) {
vscode.window.showInformationMessage(`vous lancez la mission ${missionId}`);
const hadMissionWorkdir = this.extensionStore.getMissionWorkdir() !== undefined;
if (!hadMissionWorkdir) {
......@@ -97,13 +105,25 @@ export default class Controller {
const hadBeenConnected = (await this.extensionStore.getAccessToken()) !== undefined;
if (!hadBeenConnected) {
this.authenticate();
await this.authenticate();
vscode.window.showInformationMessage('Nouvelle connexion validée');
} else {
vscode.window.showInformationMessage('Déjà connecté: session récupérée');
}
vscode.commands.executeCommand(OPEN_QUICK_SETUP_COMMAND.cmd);
// TODO Should I fetch GET api/missions/{missionId} one day instead of passing necessary parameters in vscode xdg-open link ?
const mission = new Mission({
registryBaseURL: REGISTRY_MISSION_URL,
missionId: missionId,
missionVersion: missionVersion,
});
vscode.window.showInformationMessage(
'opening inside folder ' + this.extensionStore.getMissionWorkdir()! + '/' + missionId,
);
await mission.setup({});
await mission.openEditorInFolder();
vscode.commands.executeCommand(OPEN_GETTING_STARTED_COMMAND.cmd);
}
}
}
import { exec as _exec } from 'child_process';
import * as fs from 'fs';
import * as util from 'util';
import * as vscode from 'vscode';
import { error as err, log } from '../recorder/utils';
import ExtensionStore from './extensionStore';
/**
* {@link https://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback}
*/
const exec = util.promisify(_exec);
export default class Mission {
private readonly hostBaseWorkDir: string;
private readonly dockerImageURL: string;
private readonly hostMissionDir: string;
private readonly hostMissionDevcontainerDir: string;
private readonly hostMissionDevcontainerFileDir: string;
private readonly hostMissionMountDir: string;
private readonly remoteUserHomeDir: string;
private readonly remoteMissionDir: any;
private readonly remoteGiteaWorkDir: string;
constructor(private params: { registryBaseURL: string; missionId: string; missionVersion: string }) {
const { registryBaseURL, missionId, missionVersion } = params;
this.hostBaseWorkDir = ExtensionStore.getInstance().getMissionWorkdir() ?? '';
this.hostMissionDir = `${this.hostBaseWorkDir}/${missionId}`;
this.hostMissionDevcontainerDir = `${this.hostMissionDir}/.devcontainer`;
this.hostMissionDevcontainerFileDir = `${this.hostMissionDevcontainerDir}/devcontainer.json`;
this.hostMissionMountDir = `${this.hostMissionDir}/mounted`;
this.dockerImageURL = getDockerImageURL(registryBaseURL, missionId, missionVersion);
this.remoteUserHomeDir = '/home/deadlock';
this.remoteMissionDir = `${this.remoteUserHomeDir}/mission`;
this.remoteGiteaWorkDir = `/project`;
}
static async pullImage(url) {
const { stdout, stderr } = await exec(`docker pull ${url}`);
log(stdout);
err(stderr);
}
public async setup(options?: Partial<DockerfileSpecific & Base & VSCodespecific & LifecycleScripts>) {
await fs.promises.mkdir(this.hostMissionDevcontainerDir, { recursive: true });
await fs.promises.mkdir(this.hostMissionMountDir, { recursive: true });
await fs.promises.writeFile(
this.hostMissionDevcontainerFileDir,
(() => {
const devcontainer: Partial<DockerfileSpecific & Base & VSCodespecific & LifecycleScripts> = {
name: `deadlock-${this.params.missionId}`,
image: this.dockerImageURL,
extensions: ['Deadlock.deadlock-coding'],
remoteUser: 'deadlock',
// mounts: [
// `source=${this.hostMissionDir}/workspace,target=${'/workspace'},type=${'volume'},consistency=${'cached'}`,
// ],
userEnvProbe: 'interactiveShell',
settings: {
'terminal.integrated.defaultProfile.linux': 'bash',
'terminal.integrated.profiles.linux': {
bash: {
path: '/bin/bash',
},
},
},
overrideCommand: false,
shutdownAction: 'stopContainer',
workspaceMount: `source=${this.hostMissionMountDir},target=${this.remoteMissionDir},type=bind`,
workspaceFolder: `${this.remoteMissionDir}`,
onCreateCommand: `cp -R ${this.remoteGiteaWorkDir} ${this.remoteMissionDir}`,
...options,
};
return JSON.stringify(devcontainer, null, 2);
})(),
);
}
public async openEditorInFolder(arbitraryPath?: string) {
if (arbitraryPath) {
return vscode.commands.executeCommand('remote-containers.openFolder', vscode.Uri.file(arbitraryPath));
}
if (!fs.existsSync(this.hostMissionDir)) {
log('WARN missing path ', this.hostMissionDir);
await fs.promises.mkdir(this.hostMissionDir, { recursive: true });
}
await vscode.commands.executeCommand('remote-containers.openFolder', vscode.Uri.file(this.hostMissionDir));
}
}
export function getDockerImageURL(base, missionId, missionVersion) {
return `${base}/${missionId}:${missionVersion}`;
}
interface DockerfileSpecific {
image?;
dockerFile?;
context?;
'build.args'?;
'build.target'?;
'build.cacheFrom'?;
containerEnv?;
containerUser?;
mounts?;
workspaceMount?;
workspaceFolder?;
runArgs?;
}
interface Base {
name?;
forwardPorts?;
portsAttributes?;
otherPortsAttributes?;
remoteEnv?;
remoteUser?;
updateRemoteUserUID?;
userEnvProbe?;
overrideCommand?;
features?;
shutdownAction?;
}
interface VSCodespecific {
extensions?;
settings?;
devPort?;
}
interface LifecycleScripts {
initializeCommand?;
onCreateCommand?;
updateContentCommand?;
postCreateCommand?;
postStartCommand?;
postAttachCommand?;
waitFor?;
}
......@@ -90,17 +90,12 @@ export abstract class WebviewBase implements Disposable {
async show(column: ViewColumn = ViewColumn.Beside): Promise<void> {
if (this.panel == null) {
this.panel = window.createWebviewPanel(
this.id,
this.title,
{ viewColumn: column, preserveFocus: false },
{
this.panel = window.createWebviewPanel(this.id, this.title, ViewColumn.Active, {
retainContextWhenHidden: true,
enableFindWidget: true,
enableCommandUris: true,
enableScripts: true,
},
);
});
this.disposablePanel = Disposable.from(
this.panel,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment