Skip to content

Commit 851b062

Browse files
committed
feat: add scafold enarx.toml, validate enarx.toml, codex pull features
1 parent af44288 commit 851b062

19 files changed

+687
-153
lines changed

package-lock.json

+358-50
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+24-7
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,25 @@
1010
"Other"
1111
],
1212
"activationEvents": [
13-
"onCommand:vscode-enarx.helloWorld"
13+
"onCommand:vscode-enarx.scafoldEnarxToml",
14+
"onCommand:vscode-enarx.codexPull",
15+
"workspaceContains:**/Enarx.toml"
1416
],
1517
"main": "./dist/extension.js",
1618
"contributes": {
1719
"commands": [
1820
{
19-
"command": "vscode-enarx.helloWorld",
20-
"title": "Hello World"
21+
"command": "vscode-enarx.scafoldEnarxToml",
22+
"title": "Enarx: Create Enarx.toml"
23+
},
24+
{
25+
"command": "vscode-enarx.enarxTomlValidation",
26+
"title": "Enarx: Validate Enarx Toml",
27+
"when": "workspaceContains:**/Enarx.toml"
28+
},
29+
{
30+
"command": "vscode-enarx.codexPull",
31+
"title": "Enarx: Pull Code from Codex"
2132
}
2233
]
2334
},
@@ -33,19 +44,25 @@
3344
"test": "node ./out/test/runTest.js"
3445
},
3546
"devDependencies": {
36-
"@types/vscode": "^1.70.0",
3747
"@types/glob": "^7.2.0",
3848
"@types/mocha": "^9.1.1",
3949
"@types/node": "16.x",
50+
"@types/vscode": "^1.70.0",
4051
"@typescript-eslint/eslint-plugin": "^5.31.0",
4152
"@typescript-eslint/parser": "^5.31.0",
53+
"@vscode/test-electron": "^2.1.5",
4254
"eslint": "^8.20.0",
4355
"glob": "^8.0.3",
4456
"mocha": "^10.0.0",
45-
"typescript": "^4.7.4",
4657
"ts-loader": "^9.3.1",
58+
"typescript": "^4.7.4",
4759
"webpack": "^5.74.0",
48-
"webpack-cli": "^4.10.0",
49-
"@vscode/test-electron": "^2.1.5"
60+
"webpack-cli": "^4.10.0"
61+
},
62+
"dependencies": {
63+
"@apideck/better-ajv-errors": "^0.3.6",
64+
"ajv": "^8",
65+
"axios": "^0.27.2",
66+
"toml": "^3.0.0"
5067
}
5168
}

src/CodexProvider/codexProvider.ts

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export interface ICodexProvider {
2+
getCodexRepos(): Promise<string[]>;
3+
}
+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { ICodexProvider } from "./codexProvider";
2+
import axios from 'axios';
3+
4+
interface Tree {
5+
path: string;
6+
mode: string;
7+
type: string;
8+
sha: string;
9+
url: string;
10+
size?: number;
11+
}
12+
13+
interface APIReponseStructure {
14+
sha: string;
15+
url: string;
16+
tree: Tree[];
17+
truncated: boolean;
18+
}
19+
20+
export class GithubCodexProvider implements ICodexProvider {
21+
async getCodexRepos(): Promise<string[]> {
22+
let result = await axios.get('https://api.github.com/repos/enarx/codex/git/trees/main?recursive=true&truncated=false');
23+
if (result.status!== 200){
24+
return [];
25+
} else {
26+
try {
27+
let resultData = result.data as APIReponseStructure;
28+
return resultData.tree.filter( v => (/^.*\/.*\/Enarx.toml$/).test(v.path)).map(v => v.path).map(v => v.replace("/Enarx.toml", ""));
29+
}
30+
catch(e) {
31+
return [];
32+
}
33+
}
34+
}
35+
}

src/EnarxTomlExample.ts

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
export let ENARX_TOML_EXAMPLE = `
2+
# Configuration for a WASI application in an Enarx Keep
3+
4+
# Arguments
5+
args = [
6+
"--argument1",
7+
"--argument2=foo"
8+
]
9+
10+
# Environment variables
11+
[env]
12+
VAR1 = "var1"
13+
VAR2 = "var2"
14+
15+
# Pre-opened file descriptors
16+
[[files]]
17+
kind = "null"
18+
19+
[[files]]
20+
kind = "stdout"
21+
22+
[[files]]
23+
kind = "stderr"
24+
25+
# A listen socket
26+
[[files]]
27+
name = "LISTEN"
28+
kind = "listen"
29+
prot = "tls" # or prot = "tcp"
30+
port = 12345
31+
32+
# An outgoing connected socket
33+
[[files]]
34+
name = "CONNECT"
35+
kind = "connect"
36+
prot = "tcp" # or prot = "tls"
37+
host = "127.0.0.1"
38+
port = 23456`;

src/EnarxTomlScema.ts

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
export let ENARX_TOML_SCHEMA_DEFINITION = {
2+
type: "object",
3+
properties: {
4+
steward: { type: "string", pattern: "(http|https)://[a-z\-A-Z0-9].[a-z\-A-Z0-9] .[a-zA-Z0-9]" },
5+
args: { type: "array", items: { type: "string" } },
6+
env: { type: "object", additionalProperties: { "type": "string" } },
7+
files: {
8+
type: "array",
9+
items: {
10+
type: "object",
11+
properties: {
12+
kind: {
13+
type: "string",
14+
enum: ["null", "stdin", "stdout", "stderr", "listen", "connect"]
15+
},
16+
name: { type: "string" },
17+
prot: {
18+
type: "string",
19+
enum: ["tcp", "tls"]
20+
},
21+
port: { type: "number", minimum: 1, maximum: 65535 },
22+
host: { type: "string" },
23+
addr: { type: "string" }
24+
},
25+
allOf: [
26+
{
27+
if: {
28+
properties: {
29+
kind: {
30+
enum: ["listen", "connect"]
31+
},
32+
},
33+
required: ["kind"]
34+
},
35+
then: {
36+
required: ["prot", "port"]
37+
},
38+
else: {
39+
required: []
40+
}
41+
},
42+
{
43+
if: {
44+
properties: {
45+
kind: {
46+
enum: ["connect"]
47+
},
48+
},
49+
required: ["kind"]
50+
},
51+
then: {
52+
required: ["host"]
53+
},
54+
else: {
55+
required: []
56+
}
57+
},
58+
{
59+
if: {
60+
properties: {
61+
kind: {
62+
enum: ["listen"]
63+
},
64+
},
65+
required: ["kind"]
66+
},
67+
then: {
68+
required: ["addr"]
69+
},
70+
else: {
71+
required: []
72+
}
73+
}
74+
],
75+
}
76+
}
77+
},
78+
additionalProperties: false,
79+
};

src/Parser/parser.ts

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export interface IParser {
2+
parseObject(content: string) : Object;
3+
}

src/Parser/tomlParser.ts

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { IParser } from "./parser";
2+
import * as toml from 'toml';
3+
export class TomlParser implements IParser {
4+
parseObject(content: string): Object {
5+
return toml.parse(content);
6+
}
7+
}

src/Reader/fileReader.ts

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { IReader } from "./reader";
2+
import * as fs from 'fs';
3+
4+
5+
export class FileReader implements IReader {
6+
public readText(path: string): string {
7+
if (fs.existsSync(path)) {
8+
let content = fs.readFileSync(path, 'utf-8')!;
9+
return content;
10+
} else {
11+
throw new Error(`error: path ${path} doesn't exist`)
12+
}
13+
}
14+
}

src/Reader/reader.ts

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
2+
export interface IReader {
3+
/**
4+
* read_text
5+
path: string : string */
6+
readText(path: string): string;
7+
}

src/extension.ts

+64-19
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,71 @@
1-
// The module 'vscode' contains the VS Code extensibility API
2-
// Import the module and reference it with the alias vscode in your code below
31
import * as vscode from 'vscode';
2+
import * as cp from 'child_process';
3+
import * as fs from 'fs';
4+
import { IValidationProvider } from './validationProvider/validationProvider';
5+
import { TomlValidationProvider } from './validationProvider/tomlValidationProvider';
6+
import { ICodexProvider } from './CodexProvider/codexProvider';
7+
import { GithubCodexProvider } from './CodexProvider/GithubCodexProvider';
8+
import { ENARX_TOML_EXAMPLE } from './EnarxTomlExample';
49

5-
// this method is called when your extension is activated
6-
// your extension is activated the very first time the command is executed
7-
export function activate(context: vscode.ExtensionContext) {
8-
9-
// Use the console to output diagnostic information (console.log) and errors (console.error)
10-
// This line of code will only be executed once when your extension is activated
11-
console.log('Congratulations, your extension "vscode-enarx" is now active!');
1210

13-
// The command has been defined in the package.json file
14-
// Now provide the implementation of the command with registerCommand
15-
// The commandId parameter must match the command field in package.json
16-
let disposable = vscode.commands.registerCommand('vscode-enarx.helloWorld', () => {
17-
// The code you place here will be executed every time your command is executed
18-
// Display a message box to the user
19-
vscode.window.showInformationMessage('Hello World from vscode-enarx!');
11+
export function activate(context: vscode.ExtensionContext) {
12+
let scafoldEnarxToml = vscode.commands.registerCommand('vscode-enarx.scafoldEnarxToml', () => {
13+
if (vscode.workspace.workspaceFolders) {
14+
try {
15+
fs.writeFileSync(`${vscode.workspace.workspaceFolders[0].uri.fsPath}/Enarx.toml`, ENARX_TOML_EXAMPLE);
16+
} catch (e) {
17+
vscode.window.showErrorMessage("We couldn't create an Enarx.toml file at the workspace location", "Ok");
18+
}
19+
} else {
20+
vscode.window.showErrorMessage('We couldn\'t create a Enarx.toml since you are working outside any workspace.');
21+
}
2022
});
21-
22-
context.subscriptions.push(disposable);
23+
let enarxTomlValidation = vscode.commands.registerCommand('vscode-enarx.enarxTomlValidation', () => {
24+
if (vscode.workspace.workspaceFolders) {
25+
let workspacePath = vscode.workspace.workspaceFolders[0].uri.path;
26+
let exarxTomlPath = `${workspacePath}/Enarx.toml`;
27+
let validationProvider: IValidationProvider = new TomlValidationProvider();
28+
let result = validationProvider.validate(exarxTomlPath);
29+
if (result[0] && result[1] === null) {
30+
vscode.window.showInformationMessage(`Enarx.toml is correct!`);
31+
} else {
32+
result[1]?.forEach(err => {
33+
vscode.window.showErrorMessage(`Enarx.toml Validation error: ${err}`);
34+
});
35+
}
36+
}
37+
});
38+
let enarxCodex = vscode.commands.registerCommand('vscode-enarx.codexPull', async () => {
39+
let codex: ICodexProvider = new GithubCodexProvider();
40+
let s = await codex.getCodexRepos();
41+
let selectedRepo = await vscode.window.showQuickPick(s.map(v => ({ label: v } as vscode.QuickPickItem)));
42+
if (selectedRepo) {
43+
if (vscode.workspace.workspaceFolders && (process.platform === 'linux' || process.platform === 'darwin')) {
44+
vscode.window.showInformationMessage("Pulling code for: " + selectedRepo.label);
45+
try {
46+
const commandToExtract = `curl https://codeload.github.com/enarx/codex/tar.gz/refs/heads/main | tar -zx --directory ${vscode.workspace.workspaceFolders[0].uri.fsPath} ./codex-main/${selectedRepo.label}`;
47+
cp.execSync(commandToExtract);
48+
cp.execSync(`mv ${vscode.workspace.workspaceFolders[0].uri.fsPath}/codex-main/${selectedRepo.label}/* ${vscode.workspace.workspaceFolders[0].uri.fsPath}`);
49+
cp.execSync(`rm -rf ${vscode.workspace.workspaceFolders[0].uri.fsPath}/codex-main`);
50+
vscode.window.showInformationMessage(`Workspace ready with ${selectedRepo.label}`);
51+
} catch (e) {
52+
vscode.window.showInformationMessage("Oops! we can't automatically setup the workspace for you.", "Configure Manually", "Do it later").then(select => {
53+
if (select === 'Configure Manually') {
54+
vscode.env.openExternal(vscode.Uri.parse(`https://github.com/enarx/codex/tree/main/${selectedRepo?.label}`));
55+
}
56+
});
57+
}
58+
} else {
59+
vscode.window.showInformationMessage("Oops! we can't automatically setup the workspace for you.", "Configure Manually", "Do it later").then(select => {
60+
if (select === 'Configure Manually') {
61+
vscode.env.openExternal(vscode.Uri.parse(`https://github.com/enarx/codex/tree/main/${selectedRepo?.label}`));
62+
}
63+
});
64+
}
65+
}
66+
});
67+
context.subscriptions.push(scafoldEnarxToml, enarxCodex, enarxTomlValidation);
2368
}
2469

2570
// this method is called when your extension is deactivated
26-
export function deactivate() {}
71+
export function deactivate() { }
+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import ajv from "ajv";
2+
import { ENARX_TOML_SCHEMA_DEFINITION } from "../EnarxTomlScema";
3+
import { IObjectValidator } from "./objectValidator";
4+
import {betterAjvErrors} from '@apideck/better-ajv-errors';
5+
import {JSONSchema6} from 'json-schema';
6+
7+
export class JSONObjectValidator implements IObjectValidator {
8+
validateObject(obj: Object): [Boolean, any] {
9+
let objectVal = new ajv();
10+
let validator = objectVal.compile(ENARX_TOML_SCHEMA_DEFINITION);
11+
if (validator(obj))
12+
{
13+
return [true, null];
14+
}
15+
else {
16+
console.log(validator.errors);
17+
const output = betterAjvErrors({schema: ENARX_TOML_SCHEMA_DEFINITION as JSONSchema6, data: obj, errors: validator.errors!});
18+
const messages = output.map(err => err.message);
19+
return [false, messages];
20+
}
21+
}
22+
}
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export interface IObjectValidator {
2+
validateObject(obj: Object): [Boolean, string[] | null];
3+
}

0 commit comments

Comments
 (0)