mirror of
https://github.com/engineer-man/piston.git
synced 2025-06-08 11:16:27 +02:00
feat: tooling api
This commit is contained in:
parent
7e09a3f9cd
commit
452b9d91d3
7 changed files with 90 additions and 12 deletions
|
@ -59,6 +59,7 @@ const SIGNALS = [
|
||||||
function get_job(body: RequestBody): Promise<Job> {
|
function get_job(body: RequestBody): Promise<Job> {
|
||||||
let {
|
let {
|
||||||
language,
|
language,
|
||||||
|
tool,
|
||||||
version,
|
version,
|
||||||
args,
|
args,
|
||||||
stdin,
|
stdin,
|
||||||
|
@ -159,6 +160,7 @@ function get_job(body: RequestBody): Promise<Job> {
|
||||||
run: run_memory_limit,
|
run: run_memory_limit,
|
||||||
compile: compile_memory_limit,
|
compile: compile_memory_limit,
|
||||||
},
|
},
|
||||||
|
tool
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -291,6 +293,22 @@ router.post('/execute', async (req, res) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.post('/tooling',async (req, res) => {
|
||||||
|
try {
|
||||||
|
const job = await get_job(req.body);
|
||||||
|
|
||||||
|
await job.prime();
|
||||||
|
|
||||||
|
const result = await job.analysis();
|
||||||
|
|
||||||
|
await job.cleanup();
|
||||||
|
|
||||||
|
return res.status(200).send(result);
|
||||||
|
} catch (error) {
|
||||||
|
return res.status(400).json(error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
router.get('/runtimes', (req, res) => {
|
router.get('/runtimes', (req, res) => {
|
||||||
const runtimes = _runtimes.map(rt => {
|
const runtimes = _runtimes.map(rt => {
|
||||||
return {
|
return {
|
||||||
|
@ -298,6 +316,7 @@ router.get('/runtimes', (req, res) => {
|
||||||
version: rt.version.raw,
|
version: rt.version.raw,
|
||||||
aliases: rt.aliases,
|
aliases: rt.aliases,
|
||||||
runtime: rt.runtime,
|
runtime: rt.runtime,
|
||||||
|
tooling: rt.tooling
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { spawn } from 'child_process';
|
||||||
import { join, relative, dirname } from 'node:path';
|
import { join, relative, dirname } from 'node:path';
|
||||||
import config from './config.js';
|
import config from './config.js';
|
||||||
import * as globals from './globals.js';
|
import * as globals from './globals.js';
|
||||||
|
import { type Runtime } from './runtime.js';
|
||||||
import {
|
import {
|
||||||
mkdir,
|
mkdir,
|
||||||
chown,
|
chown,
|
||||||
|
@ -18,7 +19,7 @@ import { readdirSync, readFileSync } from 'node:fs';
|
||||||
import wait_pid from 'waitpid';
|
import wait_pid from 'waitpid';
|
||||||
import EventEmitter from 'events';
|
import EventEmitter from 'events';
|
||||||
|
|
||||||
import { File, ResponseBody } from './types.js';
|
import { File, LimitObject, ResponseBody } from './types.js';
|
||||||
|
|
||||||
const job_states = {
|
const job_states = {
|
||||||
READY: Symbol('Ready to be primed'),
|
READY: Symbol('Ready to be primed'),
|
||||||
|
@ -42,35 +43,40 @@ setInterval(() => {
|
||||||
export default class Job {
|
export default class Job {
|
||||||
uuid: string;
|
uuid: string;
|
||||||
logger: Logger;
|
logger: Logger;
|
||||||
runtime: any;
|
runtime: Runtime;
|
||||||
|
tool?: string;
|
||||||
files: File[];
|
files: File[];
|
||||||
args: string[];
|
args: string[];
|
||||||
stdin: string;
|
stdin: string;
|
||||||
timeouts: { compile: number; run: number };
|
timeouts: LimitObject;
|
||||||
memory_limits: { compile: number; run: number };
|
memory_limits: LimitObject;
|
||||||
uid: number;
|
uid: number;
|
||||||
gid: number;
|
gid: number;
|
||||||
state: symbol;
|
state: symbol;
|
||||||
dir: string;
|
dir: string;
|
||||||
constructor({
|
constructor({
|
||||||
runtime,
|
runtime,
|
||||||
|
tool,
|
||||||
files,
|
files,
|
||||||
args,
|
args,
|
||||||
stdin,
|
stdin,
|
||||||
timeouts,
|
timeouts,
|
||||||
memory_limits,
|
memory_limits,
|
||||||
}: {
|
}: {
|
||||||
runtime: unknown;
|
runtime: Runtime;
|
||||||
|
tool?: string;
|
||||||
files: File[];
|
files: File[];
|
||||||
args: string[];
|
args: string[];
|
||||||
stdin: string;
|
stdin: string;
|
||||||
timeouts: { compile: number; run: number };
|
timeouts: LimitObject;
|
||||||
memory_limits: { compile: number; run: number };
|
memory_limits: LimitObject;
|
||||||
}) {
|
}) {
|
||||||
this.uuid = uuidv4();
|
this.uuid = uuidv4();
|
||||||
|
|
||||||
this.logger = create(`job/${this.uuid}`, {});
|
this.logger = create(`job/${this.uuid}`, {});
|
||||||
|
|
||||||
|
this.tool = tool;
|
||||||
|
|
||||||
this.runtime = runtime;
|
this.runtime = runtime;
|
||||||
this.files = files.map((file: File, i: number) => ({
|
this.files = files.map((file: File, i: number) => ({
|
||||||
name: file.name || `file${i}.code`,
|
name: file.name || `file${i}.code`,
|
||||||
|
@ -80,6 +86,7 @@ export default class Job {
|
||||||
: 'utf8',
|
: 'utf8',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
this.args = args;
|
this.args = args;
|
||||||
this.stdin = stdin;
|
this.stdin = stdin;
|
||||||
|
|
||||||
|
@ -193,6 +200,7 @@ export default class Job {
|
||||||
env: {
|
env: {
|
||||||
...this.runtime.env_vars,
|
...this.runtime.env_vars,
|
||||||
PISTON_LANGUAGE: this.runtime.language,
|
PISTON_LANGUAGE: this.runtime.language,
|
||||||
|
PISTON_TOOLING: this.tool
|
||||||
},
|
},
|
||||||
stdio: 'pipe',
|
stdio: 'pipe',
|
||||||
cwd: this.dir,
|
cwd: this.dir,
|
||||||
|
@ -271,6 +279,45 @@ export default class Job {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async analysis() {
|
||||||
|
if (this.state !== job_states.PRIMED) {
|
||||||
|
throw new Error(
|
||||||
|
'Job must be in primed state, current state: ' +
|
||||||
|
this.state.toString()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.runtime.tooling.includes(this.tool)) {
|
||||||
|
throw new Error(
|
||||||
|
`Tool ${this.tool} does not exist on ${this.runtime.toString()}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.info(`Tooling job runtime=${this.runtime.toString()}; tooling=${this.tool}`);
|
||||||
|
|
||||||
|
const code_files =
|
||||||
|
(this.runtime.language === 'file' && this.files) ||
|
||||||
|
this.files.filter((file: File) => file.encoding == 'utf8');
|
||||||
|
|
||||||
|
|
||||||
|
const output = await this.safe_call(
|
||||||
|
join(this.runtime.pkgdir, 'tooling'),
|
||||||
|
code_files.map(x => x.name),
|
||||||
|
this.timeouts.run,
|
||||||
|
this.memory_limits.run
|
||||||
|
);
|
||||||
|
|
||||||
|
this.logger.debug('Analyzing')
|
||||||
|
|
||||||
|
|
||||||
|
return {
|
||||||
|
output,
|
||||||
|
language: this.runtime.language,
|
||||||
|
tool: this.tool,
|
||||||
|
version: this.runtime.version.raw
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async execute() {
|
async execute() {
|
||||||
if (this.state !== job_states.PRIMED) {
|
if (this.state !== job_states.PRIMED) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
|
|
@ -12,6 +12,7 @@ export const runtimes: Runtime[] = [];
|
||||||
|
|
||||||
export class Runtime {
|
export class Runtime {
|
||||||
language: string;
|
language: string;
|
||||||
|
tooling: string[];
|
||||||
version: SemVer;
|
version: SemVer;
|
||||||
aliases: string[];
|
aliases: string[];
|
||||||
pkgdir: string;
|
pkgdir: string;
|
||||||
|
@ -26,10 +27,11 @@ export class Runtime {
|
||||||
_env_vars?: Record<string, any>;
|
_env_vars?: Record<string, any>;
|
||||||
constructor(o: {
|
constructor(o: {
|
||||||
language: string;
|
language: string;
|
||||||
|
tooling?: string[];
|
||||||
version: SemVer;
|
version: SemVer;
|
||||||
aliases: string[];
|
aliases: string[];
|
||||||
pkgdir: string;
|
pkgdir: string;
|
||||||
runtime?: any;
|
runtime?: string;
|
||||||
timeouts: { run: number; compile: number };
|
timeouts: { run: number; compile: number };
|
||||||
memory_limits: { run: number; compile: number };
|
memory_limits: { run: number; compile: number };
|
||||||
max_process_count: number;
|
max_process_count: number;
|
||||||
|
@ -38,6 +40,7 @@ export class Runtime {
|
||||||
output_max_size: number;
|
output_max_size: number;
|
||||||
}) {
|
}) {
|
||||||
this.language = o.language;
|
this.language = o.language;
|
||||||
|
this.tooling = o.tooling;
|
||||||
this.version = o.version;
|
this.version = o.version;
|
||||||
this.aliases = o.aliases || [];
|
this.aliases = o.aliases || [];
|
||||||
this.pkgdir = o.pkgdir;
|
this.pkgdir = o.pkgdir;
|
||||||
|
@ -128,6 +131,7 @@ export class Runtime {
|
||||||
aliases,
|
aliases,
|
||||||
provides,
|
provides,
|
||||||
limit_overrides,
|
limit_overrides,
|
||||||
|
tooling
|
||||||
} = info;
|
} = info;
|
||||||
const version = parse(_version);
|
const version = parse(_version);
|
||||||
|
|
||||||
|
@ -145,6 +149,7 @@ export class Runtime {
|
||||||
new Runtime({
|
new Runtime({
|
||||||
language: lang.language,
|
language: lang.language,
|
||||||
aliases: lang.aliases,
|
aliases: lang.aliases,
|
||||||
|
tooling,
|
||||||
version,
|
version,
|
||||||
pkgdir: package_dir,
|
pkgdir: package_dir,
|
||||||
runtime: language,
|
runtime: language,
|
||||||
|
@ -160,6 +165,7 @@ export class Runtime {
|
||||||
new Runtime({
|
new Runtime({
|
||||||
language,
|
language,
|
||||||
version,
|
version,
|
||||||
|
tooling,
|
||||||
aliases,
|
aliases,
|
||||||
pkgdir: package_dir,
|
pkgdir: package_dir,
|
||||||
...Runtime.compute_all_limits(language, limit_overrides),
|
...Runtime.compute_all_limits(language, limit_overrides),
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
export interface Metadata {
|
export interface Metadata {
|
||||||
language: string;
|
language: string;
|
||||||
version: string;
|
version: string;
|
||||||
|
tooling?: string[];
|
||||||
aliases?: string[];
|
aliases?: string[];
|
||||||
dependencies?: Record<string, string>;
|
dependencies?: Record<string, string>;
|
||||||
provides: {
|
provides: {
|
||||||
|
@ -23,6 +24,8 @@ export type Limit =
|
||||||
|
|
||||||
export type Limits = Record<Limit, number>;
|
export type Limits = Record<Limit, number>;
|
||||||
|
|
||||||
|
export type LimitObject = { compile: number; run: number; };
|
||||||
|
|
||||||
export type LanguageMetadata = {
|
export type LanguageMetadata = {
|
||||||
language: string;
|
language: string;
|
||||||
version: string;
|
version: string;
|
||||||
|
@ -37,6 +40,7 @@ export type File = {
|
||||||
};
|
};
|
||||||
export type RequestBody = {
|
export type RequestBody = {
|
||||||
language: string;
|
language: string;
|
||||||
|
tool?: string;
|
||||||
version: string;
|
version: string;
|
||||||
files: Array<File>;
|
files: Array<File>;
|
||||||
stdin?: string;
|
stdin?: string;
|
||||||
|
|
3
packages/.gitignore
vendored
3
packages/.gitignore
vendored
|
@ -5,4 +5,5 @@
|
||||||
!*/*/environment
|
!*/*/environment
|
||||||
!*/*/run
|
!*/*/run
|
||||||
!*/*/compile
|
!*/*/compile
|
||||||
!*/*/test.*
|
!*/*/test.*
|
||||||
|
!*/*/tooling.*
|
||||||
|
|
2
packages/python/3.10.0/build.sh
vendored
2
packages/python/3.10.0/build.sh
vendored
|
@ -18,4 +18,4 @@ cd ..
|
||||||
|
|
||||||
rm -rf build
|
rm -rf build
|
||||||
|
|
||||||
bin/pip3 install numpy scipy pandas pycrypto whoosh bcrypt passlib sympy
|
bin/pip3 install numpy scipy pandas pycrypto whoosh bcrypt passlib sympy flake8 mypy ruff
|
||||||
|
|
3
packages/python/3.10.0/metadata.json
vendored
3
packages/python/3.10.0/metadata.json
vendored
|
@ -1,5 +1,6 @@
|
||||||
{
|
{
|
||||||
"language": "python",
|
"language": "python",
|
||||||
"version": "3.10.0",
|
"version": "3.10.0",
|
||||||
"aliases": ["py", "py3", "python3", "python3.10"]
|
"aliases": ["py", "py3", "python3", "python3.10"],
|
||||||
|
"tooling": ["flake8", "mypy", "ruff"]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue