UNPKG

@enspirit/emb

Version:

A replacement for our Makefile-for-monorepos

120 lines (119 loc) 5.13 kB
import { getContext } from '../../../index.js'; import { input } from '@inquirer/prompts'; import { ListrInquirerPromptAdapter } from '@listr2/prompt-adapter-inquirer'; import { PassThrough } from 'node:stream'; import { ContainerExecOperation } from '../../../docker/index.js'; import { EMBCollection, findRunOrder } from '../../index.js'; import { ExecuteLocalCommandOperation } from '../index.js'; export var ExecutorType; (function (ExecutorType) { ExecutorType["container"] = "container"; ExecutorType["local"] = "local"; })(ExecutorType || (ExecutorType = {})); export class RunTasksOperation { async run(params) { const { monorepo } = getContext(); // First ensure the selection is valid (user can use task IDs or names) const collection = new EMBCollection(monorepo.tasks, { idField: 'id', depField: 'pre', }); const ordered = findRunOrder(params.tasks, collection, { onAmbiguous: params.allMatching ? 'runAll' : 'error', }); const hasInteractiveTasks = ordered.find((t) => t.interactive === true); if (hasInteractiveTasks) { monorepo.setTaskRenderer('silent'); } const manager = monorepo.taskManager(); await manager.run(ordered.map((task) => { return { rendererOptions: { persistentOutput: true, }, task: async (context, listrTask) => { if (!task.script) { return; } const vars = await monorepo.expand(task.vars || {}); const executor = params.executor ?? (await this.defaultExecutorFor(task)); await this.ensureExecutorValid(executor, task); // Handle tasks that require confirmation if (task.confirm) { const expected = await monorepo.expand(task.confirm.expect || 'yes', vars); const message = await monorepo.expand(task.confirm.message, vars); const res = await listrTask .prompt(ListrInquirerPromptAdapter) .run(input, { message: `${message} (type '${expected}' to continue)`, }); if (res !== expected) { throw new Error('Task canceled'); } } const tee = new PassThrough(); const logFile = await monorepo.store.createWriteStream(`logs/tasks/${task.id}.logs`); tee.pipe(listrTask.stdout()); tee.pipe(logFile); switch (executor) { case ExecutorType.container: { return this.runDocker(task, tee); } case ExecutorType.local: { return this.runLocal(task, tee); } default: { throw new Error(`Unsuported executor type: ${executor}`); } } }, title: `Running ${task.id}`, }; })); return ordered; } async runDocker(task, out) { const { monorepo, compose } = getContext(); const containerID = await compose.getContainer(task.component); return monorepo.run(new ContainerExecOperation(task.interactive ? undefined : out), { container: containerID, script: task.script, interactive: task.interactive || false, env: await monorepo.expand(task.vars || {}), }); } async runLocal(task, _out) { const { monorepo } = getContext(); const cwd = task.component ? monorepo.join(monorepo.component(task.component).rootDir) : monorepo.rootDir; return monorepo.run(new ExecuteLocalCommandOperation(), { script: task.script, workingDir: cwd, interactive: task.interactive, env: await monorepo.expand(task.vars || {}), }); } async defaultExecutorFor(task) { const available = await this.availableExecutorsFor(task); if (available.length === 0) { throw new Error('No available executor found for task'); } return available[0]; } async ensureExecutorValid(executor, task) { const available = await this.availableExecutorsFor(task); if (!available.includes(executor)) { throw new Error(`Unsuported executor type: ${executor}`); } } async availableExecutorsFor(task) { const { compose } = getContext(); if (task.executors) { return task.executors; } return task.component && (await compose.isService(task.component)) ? [ExecutorType.container, ExecutorType.local] : [ExecutorType.local]; } }