mirror of
https://github.com/compiler-explorer/compiler-explorer.git
synced 2025-12-27 10:33:59 -05:00
Add more typings (#6082)
This commit is contained in:
80
lib/exec.ts
80
lib/exec.ts
@@ -36,18 +36,19 @@ import type {FilenameTransformFunc, UnprocessedExecResult} from '../types/execut
|
|||||||
import {logger} from './logger.js';
|
import {logger} from './logger.js';
|
||||||
import {propsFor} from './properties.js';
|
import {propsFor} from './properties.js';
|
||||||
import {Graceful} from './node-graceful.js';
|
import {Graceful} from './node-graceful.js';
|
||||||
import {unwrapString} from './assert.js';
|
import {unwrapString, unwrap, assert} from './assert.js';
|
||||||
import os from 'os';
|
import os from 'os';
|
||||||
|
import {Stream} from 'stream';
|
||||||
|
|
||||||
type NsJailOptions = {
|
type NsJailOptions = {
|
||||||
args: string[];
|
args: string[];
|
||||||
options: ExecutionOptions;
|
options: ExecutionOptions;
|
||||||
filenameTransform: FilenameTransformFunc;
|
filenameTransform: FilenameTransformFunc | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
const execProps = propsFor('execution');
|
const execProps = propsFor('execution');
|
||||||
|
|
||||||
function setupOnError(stream, name) {
|
function setupOnError(stream: Stream, name: string) {
|
||||||
if (stream === undefined) return;
|
if (stream === undefined) return;
|
||||||
stream.on('error', err => {
|
stream.on('error', err => {
|
||||||
logger.error(`Error with ${name} stream:`, err);
|
logger.error(`Error with ${name} stream:`, err);
|
||||||
@@ -116,7 +117,7 @@ export function executeDirect(
|
|||||||
streams.stderr += '\nKilled - processing time exceeded\n';
|
streams.stderr += '\nKilled - processing time exceeded\n';
|
||||||
}, timeoutMs);
|
}, timeoutMs);
|
||||||
|
|
||||||
function setupStream(stream, name) {
|
function setupStream(stream: Stream, name: string) {
|
||||||
if (stream === undefined) return;
|
if (stream === undefined) return;
|
||||||
stream.on('data', data => {
|
stream.on('data', data => {
|
||||||
if (streams.truncated) return;
|
if (streams.truncated) return;
|
||||||
@@ -222,7 +223,7 @@ export function getNsJailOptions(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const homeDir = '/app';
|
const homeDir = '/app';
|
||||||
let filenameTransform;
|
let filenameTransform: FilenameTransformFunc | undefined;
|
||||||
if (options.customCwd) {
|
if (options.customCwd) {
|
||||||
let replacement = options.customCwd;
|
let replacement = options.customCwd;
|
||||||
if (options.appHome) {
|
if (options.appHome) {
|
||||||
@@ -314,28 +315,28 @@ export function getExecuteCEWrapperOptions(command: string, args: string[], opti
|
|||||||
return getCeWrapperOptions('execute', command, args, options);
|
return getCeWrapperOptions('execute', command, args, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
function sandboxNsjail(command, args, options) {
|
function sandboxNsjail(command: string, args: string[], options: ExecutionOptions) {
|
||||||
logger.info('Sandbox execution via nsjail', {command, args});
|
logger.info('Sandbox execution via nsjail', {command, args});
|
||||||
const nsOpts = getSandboxNsjailOptions(command, args, options);
|
const nsOpts = getSandboxNsjailOptions(command, args, options);
|
||||||
return executeDirect(execProps<string>('nsjail'), nsOpts.args, nsOpts.options, nsOpts.filenameTransform);
|
return executeDirect(execProps<string>('nsjail'), nsOpts.args, nsOpts.options, nsOpts.filenameTransform);
|
||||||
}
|
}
|
||||||
|
|
||||||
function executeNsjail(command, args, options) {
|
function executeNsjail(command: string, args: string[], options: ExecutionOptions) {
|
||||||
const nsOpts = getNsJailOptions('execute', command, args, options);
|
const nsOpts = getNsJailOptions('execute', command, args, options);
|
||||||
return executeDirect(execProps<string>('nsjail'), nsOpts.args, nsOpts.options, nsOpts.filenameTransform);
|
return executeDirect(execProps<string>('nsjail'), nsOpts.args, nsOpts.options, nsOpts.filenameTransform);
|
||||||
}
|
}
|
||||||
|
|
||||||
function sandboxCEWrapper(command, args, options) {
|
function sandboxCEWrapper(command: string, args: string[], options: ExecutionOptions) {
|
||||||
const nsOpts = getSandboxCEWrapperOptions(command, args, options);
|
const nsOpts = getSandboxCEWrapperOptions(command, args, options);
|
||||||
return executeDirect(execProps<string>('cewrapper'), nsOpts.args, nsOpts.options, nsOpts.filenameTransform);
|
return executeDirect(execProps<string>('cewrapper'), nsOpts.args, nsOpts.options, nsOpts.filenameTransform);
|
||||||
}
|
}
|
||||||
|
|
||||||
function executeCEWrapper(command, args, options) {
|
function executeCEWrapper(command: string, args: string[], options: ExecutionOptions) {
|
||||||
const nsOpts = getExecuteCEWrapperOptions(command, args, options);
|
const nsOpts = getExecuteCEWrapperOptions(command, args, options);
|
||||||
return executeDirect(execProps<string>('cewrapper'), nsOpts.args, nsOpts.options, nsOpts.filenameTransform);
|
return executeDirect(execProps<string>('cewrapper'), nsOpts.args, nsOpts.options, nsOpts.filenameTransform);
|
||||||
}
|
}
|
||||||
|
|
||||||
function withFirejailTimeout(args: string[], options?) {
|
function withFirejailTimeout(args: string[], options?: ExecutionOptions) {
|
||||||
if (options && options.timeoutMs) {
|
if (options && options.timeoutMs) {
|
||||||
// const ExtraWallClockLeewayMs = 1000;
|
// const ExtraWallClockLeewayMs = 1000;
|
||||||
const ExtraCpuLeewayMs = 1500;
|
const ExtraCpuLeewayMs = 1500;
|
||||||
@@ -344,7 +345,7 @@ function withFirejailTimeout(args: string[], options?) {
|
|||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
function sandboxFirejail(command: string, args: string[], options) {
|
function sandboxFirejail(command: string, args: string[], options: ExecutionOptions) {
|
||||||
logger.info('Sandbox execution via firejail', {command, args});
|
logger.info('Sandbox execution via firejail', {command, args});
|
||||||
const execPath = path.dirname(command);
|
const execPath = path.dirname(command);
|
||||||
const execName = path.basename(command);
|
const execName = path.basename(command);
|
||||||
@@ -362,8 +363,9 @@ function sandboxFirejail(command: string, args: string[], options) {
|
|||||||
delete options.ldPath;
|
delete options.ldPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const key of Object.keys(options.env || {})) {
|
const env = options.env || {};
|
||||||
jailingOptions.push(`--env=${key}=${options.env[key]}`);
|
for (const key of Object.keys(env)) {
|
||||||
|
jailingOptions.push(`--env=${key}=${env[key]}`);
|
||||||
}
|
}
|
||||||
delete options.env;
|
delete options.env;
|
||||||
|
|
||||||
@@ -371,7 +373,7 @@ function sandboxFirejail(command: string, args: string[], options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const sandboxDispatchTable = {
|
const sandboxDispatchTable = {
|
||||||
none: (command, args, options) => {
|
none: (command: string, args: string[], options: ExecutionOptions) => {
|
||||||
logger.info('Sandbox execution (sandbox disabled)', {command, args});
|
logger.info('Sandbox execution (sandbox disabled)', {command, args});
|
||||||
if (needsWine(command)) {
|
if (needsWine(command)) {
|
||||||
return executeWineDirect(command, args, options);
|
return executeWineDirect(command, args, options);
|
||||||
@@ -412,7 +414,7 @@ export function startWineInit() {
|
|||||||
const executionType = execProps('executionType', 'none');
|
const executionType = execProps('executionType', 'none');
|
||||||
// We need to fire up a firejail wine server even in nsjail world (for now).
|
// We need to fire up a firejail wine server even in nsjail world (for now).
|
||||||
const firejail = executionType === 'firejail' || executionType === 'nsjail' ? execProps<string>('firejail') : null;
|
const firejail = executionType === 'firejail' || executionType === 'nsjail' ? execProps<string>('firejail') : null;
|
||||||
const env = applyWineEnv({PATH: process.env.PATH});
|
const env = applyWineEnv({PATH: unwrapString(process.env.PATH)});
|
||||||
const prefix = env.WINEPREFIX;
|
const prefix = env.WINEPREFIX;
|
||||||
|
|
||||||
logger.info(`Initialising WINE in ${prefix}`);
|
logger.info(`Initialising WINE in ${prefix}`);
|
||||||
@@ -433,7 +435,7 @@ export function startWineInit() {
|
|||||||
// We wait until the process has printed out some known good text, but don't wait
|
// We wait until the process has printed out some known good text, but don't wait
|
||||||
// for it to exit (it won't, on purpose).
|
// for it to exit (it won't, on purpose).
|
||||||
|
|
||||||
let wineServer;
|
let wineServer: child_process.ChildProcess | undefined;
|
||||||
if (firejail) {
|
if (firejail) {
|
||||||
logger.info(`Starting a new, firejailed, long-lived wineserver complex`);
|
logger.info(`Starting a new, firejailed, long-lived wineserver complex`);
|
||||||
wineServer = child_process.spawn(
|
wineServer = child_process.spawn(
|
||||||
@@ -462,7 +464,7 @@ export function startWineInit() {
|
|||||||
Graceful.on('exit', () => {
|
Graceful.on('exit', () => {
|
||||||
const waitingPromises: Promise<void>[] = [];
|
const waitingPromises: Promise<void>[] = [];
|
||||||
|
|
||||||
function waitForExit(process, name): Promise<void> {
|
function waitForExit(process: child_process.ChildProcess, name: string): Promise<void> {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
process.on('close', () => {
|
process.on('close', () => {
|
||||||
logger.info(`Process '${name}' closed`);
|
logger.info(`Process '${name}' closed`);
|
||||||
@@ -477,29 +479,33 @@ export function startWineInit() {
|
|||||||
if (wineServer.killed) {
|
if (wineServer.killed) {
|
||||||
waitingPromises.push(waitForExit(wineServer, 'WINE server'));
|
waitingPromises.push(waitForExit(wineServer, 'WINE server'));
|
||||||
}
|
}
|
||||||
wineServer = null;
|
wineServer = undefined;
|
||||||
}
|
}
|
||||||
return Promise.all(waitingPromises);
|
return Promise.all(waitingPromises);
|
||||||
});
|
});
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
setupOnError(wineServer.stdin, 'stdin');
|
assert(wineServer);
|
||||||
setupOnError(wineServer.stdout, 'stdout');
|
const [stdin, stdout, stderr] = [
|
||||||
setupOnError(wineServer.stderr, 'stderr');
|
unwrap(wineServer.stdin),
|
||||||
|
unwrap(wineServer.stdout),
|
||||||
|
unwrap(wineServer.stderr),
|
||||||
|
];
|
||||||
|
setupOnError(stdin, 'stdin');
|
||||||
|
setupOnError(stdout, 'stdout');
|
||||||
|
setupOnError(stderr, 'stderr');
|
||||||
const magicString = '!!EVERYTHING IS WORKING!!';
|
const magicString = '!!EVERYTHING IS WORKING!!';
|
||||||
wineServer.stdin.write(`echo ${magicString}`);
|
stdin.write(`echo ${magicString}`);
|
||||||
|
|
||||||
let output = '';
|
let output = '';
|
||||||
wineServer.stdout.on('data', data => {
|
stdout.on('data', data => {
|
||||||
logger.info(`Output from wine server complex: ${data.toString().trim()}`);
|
logger.info(`Output from wine server complex: ${data.toString().trim()}`);
|
||||||
output += data;
|
output += data;
|
||||||
if (output.includes(magicString)) {
|
if (output.includes(magicString)) {
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
wineServer.stderr.on('data', data =>
|
stderr.on('data', data => logger.info(`stderr output from wine server complex: ${data.toString().trim()}`));
|
||||||
logger.info(`stderr output from wine server complex: ${data.toString().trim()}`),
|
|
||||||
);
|
|
||||||
wineServer.on('error', e => {
|
wineServer.on('error', e => {
|
||||||
logger.error(`WINE server complex exited with error ${e}`);
|
logger.error(`WINE server complex exited with error ${e}`);
|
||||||
reject(e);
|
reject(e);
|
||||||
@@ -513,29 +519,29 @@ export function startWineInit() {
|
|||||||
wineInitPromise = asyncSetup();
|
wineInitPromise = asyncSetup();
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyWineEnv(env) {
|
function applyWineEnv(env: Record<string, string>): Record<string, string> {
|
||||||
return {
|
return {
|
||||||
...env,
|
...env,
|
||||||
// Force use of wine vcruntime (See change 45106c382)
|
// Force use of wine vcruntime (See change 45106c382)
|
||||||
WINEDLLOVERRIDES: 'vcruntime140=b',
|
WINEDLLOVERRIDES: 'vcruntime140=b',
|
||||||
WINEDEBUG: '-all',
|
WINEDEBUG: '-all',
|
||||||
WINEPREFIX: execProps('winePrefix'),
|
WINEPREFIX: execProps<string>('winePrefix'),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function needsWine(command) {
|
function needsWine(command: string) {
|
||||||
return command.match(/\.exe$/i) && process.platform === 'linux' && !process.env.wsl;
|
return command.match(/\.exe$/i) && process.platform === 'linux' && !process.env.wsl;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function executeWineDirect(command, args, options) {
|
async function executeWineDirect(command: string, args: string[], options: ExecutionOptions) {
|
||||||
options = _.clone(options) || {};
|
options = _.clone(options) || {};
|
||||||
options.env = applyWineEnv(options.env);
|
options.env = applyWineEnv(options.env || {});
|
||||||
args = [command, ...args];
|
args = [command, ...args];
|
||||||
await wineInitPromise;
|
await wineInitPromise;
|
||||||
return await executeDirect(unwrapString(execProps<string>('wine')), args, options);
|
return await executeDirect(unwrapString(execProps<string>('wine')), args, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function executeFirejail(command, args, options) {
|
async function executeFirejail(command: string, args: string[], options: ExecutionOptions) {
|
||||||
options = _.clone(options) || {};
|
options = _.clone(options) || {};
|
||||||
const firejail = execProps<string>('firejail');
|
const firejail = execProps<string>('firejail');
|
||||||
const baseOptions = withFirejailTimeout(
|
const baseOptions = withFirejailTimeout(
|
||||||
@@ -544,9 +550,9 @@ async function executeFirejail(command, args, options) {
|
|||||||
);
|
);
|
||||||
if (needsWine(command)) {
|
if (needsWine(command)) {
|
||||||
logger.debug('WINE execution via firejail', {command, args});
|
logger.debug('WINE execution via firejail', {command, args});
|
||||||
options.env = applyWineEnv(options.env);
|
options.env = applyWineEnv(options.env || {});
|
||||||
args = [command, ...args];
|
args = [command, ...args];
|
||||||
command = execProps('wine');
|
command = execProps<string>('wine');
|
||||||
baseOptions.push('--profile=' + getFirejailProfileFilePath('wine'), `--join=${wineSandboxName}`);
|
baseOptions.push('--profile=' + getFirejailProfileFilePath('wine'), `--join=${wineSandboxName}`);
|
||||||
delete options.customCwd;
|
delete options.customCwd;
|
||||||
baseOptions.push(command);
|
baseOptions.push(command);
|
||||||
@@ -562,7 +568,7 @@ async function executeFirejail(command, args, options) {
|
|||||||
delete options.ldPath;
|
delete options.ldPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
let filenameTransform;
|
let filenameTransform: FilenameTransformFunc | undefined;
|
||||||
if (options.customCwd) {
|
if (options.customCwd) {
|
||||||
baseOptions.push(`--private=${options.customCwd}`);
|
baseOptions.push(`--private=${options.customCwd}`);
|
||||||
const replacement = options.customCwd;
|
const replacement = options.customCwd;
|
||||||
@@ -579,7 +585,7 @@ async function executeFirejail(command, args, options) {
|
|||||||
return await executeDirect(firejail, baseOptions.concat(args), options, filenameTransform);
|
return await executeDirect(firejail, baseOptions.concat(args), options, filenameTransform);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function executeNone(command, args, options) {
|
async function executeNone(command: string, args: string[], options: ExecutionOptions) {
|
||||||
if (needsWine(command)) {
|
if (needsWine(command)) {
|
||||||
return await executeWineDirect(command, args, options);
|
return await executeWineDirect(command, args, options);
|
||||||
}
|
}
|
||||||
@@ -589,7 +595,7 @@ async function executeNone(command, args, options) {
|
|||||||
const executeDispatchTable = {
|
const executeDispatchTable = {
|
||||||
none: executeNone,
|
none: executeNone,
|
||||||
firejail: executeFirejail,
|
firejail: executeFirejail,
|
||||||
nsjail: (command, args, options) =>
|
nsjail: (command: string, args: string[], options: ExecutionOptions) =>
|
||||||
needsWine(command) ? executeFirejail(command, args, options) : executeNsjail(command, args, options),
|
needsWine(command) ? executeFirejail(command, args, options) : executeNsjail(command, args, options),
|
||||||
cewrapper: executeCEWrapper,
|
cewrapper: executeCEWrapper,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -183,7 +183,7 @@
|
|||||||
"format": "prettier --write .",
|
"format": "prettier --write .",
|
||||||
"format-files": "prettier --write --ignore-unknown",
|
"format-files": "prettier --write --ignore-unknown",
|
||||||
"ts-compile": "tsc",
|
"ts-compile": "tsc",
|
||||||
"ts-check": "tsc -p ./tsconfig.backend.json --noEmit && tsc -p ./tsconfig.frontend.json --noEmit",
|
"ts-check": "tsc -p ./tsconfig.backend.json --noEmit && tsc -p ./tsconfig.frontend.json --noEmit && tsc -p ./tsconfig.tests.json --noEmit",
|
||||||
"webpack": "node --no-warnings=ExperimentalWarning --loader ts-node/esm ./node_modules/webpack-cli/bin/cli.js --node-env=production --config webpack.config.esm.ts"
|
"webpack": "node --no-warnings=ExperimentalWarning --loader ts-node/esm ./node_modules/webpack-cli/bin/cli.js --node-env=production --config webpack.config.esm.ts"
|
||||||
},
|
},
|
||||||
"license": "BSD-2-Clause"
|
"license": "BSD-2-Clause"
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
|
||||||
|
import {assert} from '../lib/assert.js';
|
||||||
import * as exec from '../lib/exec.js';
|
import * as exec from '../lib/exec.js';
|
||||||
import * as props from '../lib/properties.js';
|
import * as props from '../lib/properties.js';
|
||||||
import {UnprocessedExecResult} from '../types/execution/execution.interfaces.js';
|
import {UnprocessedExecResult} from '../types/execution/execution.interfaces.js';
|
||||||
@@ -230,6 +231,7 @@ describe('Execution tests', () => {
|
|||||||
]);
|
]);
|
||||||
options.should.deep.equals({});
|
options.should.deep.equals({});
|
||||||
expect(filenameTransform).to.not.be.undefined;
|
expect(filenameTransform).to.not.be.undefined;
|
||||||
|
assert(filenameTransform);
|
||||||
filenameTransform('moo').should.equal('moo');
|
filenameTransform('moo').should.equal('moo');
|
||||||
filenameTransform('/some/custom/cwd/file').should.equal('/app/file');
|
filenameTransform('/some/custom/cwd/file').should.equal('/app/file');
|
||||||
});
|
});
|
||||||
|
|||||||
6
tsconfig.tests.json
Normal file
6
tsconfig.tests.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.backend.json",
|
||||||
|
"files": [],
|
||||||
|
"include": ["test/**/*.js", "test/**/*.ts"],
|
||||||
|
"exclude": []
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user