Skip to content

Commit

Permalink
Use typescript for judges
Browse files Browse the repository at this point in the history
  • Loading branch information
mousetail committed Nov 2, 2024
1 parent 01dce14 commit 5c6c63f
Show file tree
Hide file tree
Showing 18 changed files with 438 additions and 138 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ postgres-data
.sessions
node_modules
static/target
scripts/build
8 changes: 8 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.PHONY: ts-build-runner
ts-build-runner:
npx tsc scripts/runner-lib.ts --target es2022 --moduleResolution bundler --declaration --outDir scripts/build

.PHONY: restart-runner
restart-runner:
cargo build --package lang-runner
docker container kill --signal USR1 yet-to-be-named-golfing-site-yq-runner-1
13 changes: 13 additions & 0 deletions common/src/langs.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use serde::Serialize;

#[derive(Serialize)]
#[serde(rename_all(serialize = "camelCase"))]
pub struct Lang {
pub name: &'static str,
pub compile_command: &'static [&'static str],
Expand All @@ -21,6 +22,18 @@ pub const LANGS: &[Lang] = &[
install_env: &[],
latest_version: "22.9.0",
},
Lang {
name: "deno",
compile_command: &[],
run_command: &["${LANG_LOCATION}/bin/deno", "--allow-write=/tmp", "--allow-run", "--allow-read", "${FILE_LOCATION}"],
//run_command: &["/usr/bin/env"],
plugin: "https://github.com/asdf-community/asdf-deno.git",
env: &[
("RUST_BACKTRACE", "1")
],
install_env: &[],
latest_version: "2.0.4",
},
Lang {
name: "python",
compile_command: &[],
Expand Down
3 changes: 3 additions & 0 deletions common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ pub mod langs;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct JudgeResult {
pub pass: bool,
pub test_cases: Vec<TestCase>,
}

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RunLangOutput {
pub tests: JudgeResult,
pub stderr: String,
Expand All @@ -28,6 +30,7 @@ pub enum TestPassState {
}

#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TestCase {
pub name: Option<String>,
pub pass: TestPassState,
Expand Down
52 changes: 49 additions & 3 deletions js/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
import { basicSetup, EditorView, minimalSetup } from 'codemirror';
import {
createDefaultMapFromCDN,
createSystem,
createVirtualTypeScriptEnvironment,
VirtualTypeScriptEnvironment,
} from "@typescript/vfs";
import ts from "typescript";
import { tsSync, tsFacet, tsAutocomplete, tsLinter, tsHover, tsFacetWorker, tsSyncWorker, tsLinterWorker, tsAutocompleteWorker, tsHoverWorker } from "@valtown/codemirror-ts";
import { javascript } from '@codemirror/lang-javascript';
import { autocompletion } from "@codemirror/autocomplete";
import { WorkerShape } from '@valtown/codemirror-ts/worker';
import * as Comlink from "comlink";

function editorFromTextArea(textarea: HTMLTextAreaElement, extensions: typeof minimalSetup) {
let view = new EditorView({ doc: textarea.value, extensions })
Expand All @@ -9,14 +21,48 @@ function editorFromTextArea(textarea: HTMLTextAreaElement, extensions: typeof mi
textarea.value = view.state.doc.toString()
});
}

return view
}

window.addEventListener('load', () => {
let typeScriptEnvironment: WorkerShape | undefined = undefined;

async function initTypescriptForCodebox(): Promise<typeof minimalSetup> {
if (typeScriptEnvironment === undefined) {
const innerWorker = new Worker(new URL("./typescript_worker.ts", import.meta.url), {
type: "module",
});
const worker = Comlink.wrap(innerWorker) as WorkerShape;
await worker.initialize();
typeScriptEnvironment = worker;
}
const path = '/src/index.ts'

return [
basicSetup,
javascript({
typescript: true,
jsx: true,
}),
tsFacetWorker.of({ worker: typeScriptEnvironment, path }),
tsSyncWorker(),
tsLinterWorker(),
autocompletion({
override: [tsAutocompleteWorker()],
}),
tsHoverWorker()
]
}

window.addEventListener('load', async () => {
for (const textarea of document.querySelectorAll<HTMLTextAreaElement>('textarea.codemirror')) {

let plugins = basicSetup;
if (textarea.classList.contains('lang-typescript')) {
plugins = await initTypescriptForCodebox()
}
console.log("Replacing textarea with codemirror");
editorFromTextArea(textarea, basicSetup);
editorFromTextArea(textarea, plugins);
}
});


44 changes: 44 additions & 0 deletions js/typescript_worker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {
createDefaultMapFromCDN,
createSystem,
createVirtualTypeScriptEnvironment,
} from "@typescript/vfs";
import ts, { CompilerOptions } from "typescript";
import * as Comlink from "comlink";
import { createWorker } from "@valtown/codemirror-ts/worker";

const fetchDeclarations = async (): Promise<string> => {
const response = await fetch(new URL('/ts/runner-lib.d.ts', globalThis.origin));
if (!response.ok) {
throw new Error(`Failed to fetch type declarations: ${response.status}`)
}

return await response.text();
}

export default Comlink.expose(
createWorker(async function () {
const compilerOpts: CompilerOptions = {
typeRoots: ['/src/types'],

};
const [declarations, fsMap] = await Promise.all([
fetchDeclarations(),
createDefaultMapFromCDN(
compilerOpts,
"5.6.3",
false,
ts,
)
]);

// fsMap.set('/lib.d.ts',
// fsMap.get('/lib.d.ts') + '\n' + `/// <reference lib="global" />` +
// declarations
// )
// console.log(fsMap.get('/lib.d.ts'))
fsMap.set('/src/types/global.d.ts', declarations)
const system = createSystem(fsMap);
return createVirtualTypeScriptEnvironment(system, ['/src/types/global.d.ts'], ts, compilerOpts);
}),
);
4 changes: 3 additions & 1 deletion lang-runner/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ RUN apt-get update -y && apt-get install -y \
libncurses5-dev \
libffi-dev \
libreadline-dev \
libssl-dev
libssl-dev \
zip \
unzip

RUN adduser yq

Expand Down
19 changes: 15 additions & 4 deletions lang-runner/src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ async fn run_lang(
// "--clearenv",
// "--hostname",
// "yq",
"--proc",
"/proc",
"--ro-bind",
"/bin",
"/bin",
Expand All @@ -125,10 +127,18 @@ async fn run_lang(
"/lib",
"/lib",
"--ro-bind",
"/etc",
"/etc",
"--ro-bind",
"/etc/alternatives",
"/etc/alternatives",
"--tmpfs",
"/tmp",
"--tmpfs",
"/home/yq",
"--setenv",
"HOME",
"/home/yq",
])
.args(["--ro-bind"])
.arg(code_lang_folder)
Expand All @@ -150,12 +160,13 @@ async fn run_lang(
.iter()
.map(|k| {
k.replace("${LANG_LOCATION}", "/judge")
.replace("${FILE_LOCATION}", "/scripts/runner.js")
.replace("${FILE_LOCATION}", "/scripts/runner.ts")
})
.collect::<Vec<_>>(),
)
.stdout(Stdio::piped())
.stdin(Stdio::piped());

// .args([&format!("/lang/{}", lang.bin_location), code as &str, judge]);

let mut child = command.spawn()?;
Expand Down Expand Up @@ -223,7 +234,7 @@ pub async fn process_message(
lang_versions: &CacheMap<String, CacheMap<String, ()>>,
) -> Result<RunLangOutput, RunLangError> {
// Runner Lang
install_lang("nodejs".to_owned(), "22.9.0", lang_versions)
install_lang("deno".to_owned(), "2.0.4", lang_versions)
.await
.map_err(RunLangError::PluginInstallFailure)?;

Expand All @@ -235,8 +246,8 @@ pub async fn process_message(
&message.version,
&message.code,
&message.judge,
"nodejs",
"22.9.0",
"deno",
"2.0.4",
)
.await
.map_err(RunLangError::RunLangError)?;
Expand Down
6 changes: 5 additions & 1 deletion main-server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use file_session_storage::FileSessionStorage;
use sqlx::postgres::PgPoolOptions;
use std::env;
use tokio::signal;
use tower_http::services::ServeDir;
use tower_http::services::{ServeDir, ServeFile};
use tower_sessions::{cookie::time::Duration, Expiry, SessionManagerLayer};

#[tokio::main]
Expand Down Expand Up @@ -54,6 +54,10 @@ async fn main() -> anyhow::Result<()> {

let app = Router::new()
.route("/", get(all_challenges))
.nest_service(
"/ts/runner-lib.d.ts",
ServeFile::new("scripts/build/runner-lib.d.ts"),
)
.route("/challenge", get(compose_challenge).post(new_challenge))
.route("/challenge/:id", get(compose_challenge).post(new_challenge))
.route("/login/github", get(github_login))
Expand Down
3 changes: 3 additions & 0 deletions main-server/src/test_solution.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::time::Duration;

use common::RunLangOutput;
use serde::Serialize;

Expand Down Expand Up @@ -26,6 +28,7 @@ pub async fn test_solution(
code,
judge,
})
.timeout(Duration::from_secs(60))
.send()
.await
.map_err(|_e| Error::RunLangError("Failed to connect to the lang runner".to_string()))?;
Expand Down
Loading

0 comments on commit 5c6c63f

Please sign in to comment.