UNPKG

too

Version:

Combine multiple processes' Stdout/Stderr/SIGINT to keep them all foreground

114 lines (113 loc) 3.46 kB
import * as fs from 'fs'; import * as yaml from 'js-yaml'; import { createInterface } from 'readline'; import { ParallelExecutor, SequentialExecutor } from './executor.js'; import { VarGenerator } from './var.js'; import { DefaultLogger } from './logger.js'; export class Too { version = 0; env = {}; var = {}; include = { env_files: [] }; prep; main; post; logger = new DefaultLogger(); constructor(data) { this.version = data.version; this.env = data.env || {}; this.var = Object.fromEntries(Object.entries(data.var || {}).map(([k, v]) => [k, new VarGenerator(v.generate, v.collect)])); this.include.env_files = data.include?.env_files || []; this.prep = new SequentialExecutor("PREP", data.prep); this.main = new ParallelExecutor("MAIN", data.main); this.post = new SequentialExecutor("POST", data.post); } /** * Parse a file and return a Too instance. * Expecting a YAML file given as an argument. * @param fpath * @returns */ static async parse(fpath) { const data = await fs.promises.readFile(fpath); const obj = yaml.load(data.toString()); const too = new Too(obj); for (const k in too.var) { const v = await too.var[k].value(); // console.log(`${k}=${v}`); too.env[k] = v; } for (const i in too.include.env_files) { const data = await fs.promises.readFile(too.include.env_files[i]); for (const line of data.toString().split("\n")) { const [k, v] = line.split(/=(.*)/s); too.env[k] = v; } } too.prep.env = { ...too.env }; too.main.env = { ...too.env }; too.post.env = { ...too.env }; return too; } static async direct(cmds) { const data = { version: 1, main: { jobs: cmds.map((cmd) => ({ run: cmd })), }, }; return new Too(data); } static async interactive(io = { stdin: process.stdin, stdout: process.stdout, }) { return new Promise((resolve) => { const data = { version: 0, main: { jobs: [], } }; const r = createInterface({ input: io.stdin || process.stdin, output: io.stdout || process.stdout, prompt: "> ", }); r.prompt(); r.on("line", (line) => { if (line.trim().length === 0) { resolve(new Too(data)); r.close(); } else { data.main.jobs.push({ run: line }); r.prompt(); } }); }); } async run() { process.on("SIGINT", async () => { await this.main.cleanup("SIGINT"); await this.post.run(); process.exit(2); }); try { await this.prep.run(); } catch (e) { await this.post.run(); return e.code || 1; } try { await this.main.run(); } catch (e) { await this.post.run(); return e.code || 1; } try { await this.post.run(); } catch (e) { return e.code || 1; } return 0; } }