aboutsummaryrefslogtreecommitdiff
path: root/lib/execution-env.ts
blob: 31f81d8959499c29eb8e28ab3c3da834681be0ce (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import os from 'os';
import path from 'path';

import fs from 'fs-extra';
import temp from 'temp';

import {ExecutionOptions, ExecutionParams} from '../types/compilation/compilation.interfaces.js';
import {UnprocessedExecResult} from '../types/execution/execution.interfaces.js';

import {unwrap} from './assert.js';
import * as exec from './exec.js';
import {logger} from './logger.js';
import {Packager} from './packager.js';
import * as utils from './utils.js';

export interface IExecutionEnvironment {
    downloadExecutablePackage(hash: string): Promise<void>;
    execute(params: ExecutionParams): Promise<UnprocessedExecResult>;
}

export class LocalExecutionEnvironment implements IExecutionEnvironment {
    protected packager: Packager;
    protected executableCache: any;
    protected dirPath: string;
    protected buildResult: any;

    constructor() {
        this.packager = new Packager();
        this.dirPath = 'not initialized';
    }

    protected async executableGet(key: string, destinationFolder: string) {
        const result = await this.executableCache.get(key);
        if (!result.hit) return null;
        const filepath = destinationFolder + '/' + key;
        await fs.writeFile(filepath, unwrap(result.data));
        return filepath;
    }

    protected async loadPackageWithExecutable(hash: string, dirPath: string) {
        const compilationResultFilename = 'compilation-result.json';
        try {
            const startTime = process.hrtime.bigint();
            const outputFilename = await this.executableGet(hash, dirPath);
            if (outputFilename) {
                logger.debug(`Using cached package ${outputFilename}`);
                await this.packager.unpack(outputFilename, dirPath);
                const buildResultsBuf = await fs.readFile(path.join(dirPath, compilationResultFilename));
                const buildResults = JSON.parse(buildResultsBuf.toString('utf8'));
                const endTime = process.hrtime.bigint();

                // todo: get inputFilename and executableFilename from somewhere else?

                // let inputFilename = path.join(dirPath, this.compileFilename);
                // if (buildResults.inputFilename) {
                //     inputFilename = path.join(dirPath, path.basename(buildResults.inputFilename));
                // }

                return Object.assign({}, buildResults, {
                    code: 0,
                    // inputFilename: inputFilename,
                    dirPath: dirPath,
                    // executableFilename: this.getExecutableFilename(dirPath, this.outputFilebase),
                    packageDownloadAndUnzipTime: ((endTime - startTime) / BigInt(1000000)).toString(),
                });
            }
            logger.debug('Tried to get executable from cache, but got a cache miss');
        } catch (err) {
            logger.error('Tried to get executable from cache, but got an error:', {err});
        }
        return false;
    }

    async downloadExecutablePackage(hash: string): Promise<void> {
        this.dirPath = await temp.mkdir({prefix: utils.ce_temp_prefix, dir: os.tmpdir()});

        this.buildResult = await this.loadPackageWithExecutable(hash, this.dirPath);
    }

    private getDefaultExecOptions(): ExecutionOptions & {env: Record<string, string>} {
        // const env = this.env.getEnv(this.compiler.needsMulti);
        const env: Record<string, string> = {};
        if (!env.PATH) env.PATH = '';
        // env.PATH = [...this.getExtraPaths(), env.PATH].filter(Boolean).join(path.delimiter);

        return {
            // timeoutMs: this.env.ceProps('compileTimeoutMs', 7500),
            // maxErrorOutput: this.env.ceProps('max-error-output', 5000),
            env,
            // wrapper: this.compilerWrapper,
        };
    }

    async execute(params: ExecutionParams): Promise<UnprocessedExecResult> {
        return await exec.execute(
            this.buildResult.executableFilename,
            typeof params.args === 'string' ? utils.splitArguments(params.args) : params.args || [],
            this.getDefaultExecOptions(),
        );
    }
}