UNPKG

@enspirit/emb

Version:

A replacement for our Makefile-for-monorepos

71 lines (70 loc) 2.23 kB
import { spawn } from 'node:child_process'; import * as z from 'zod'; import { ComposeExecError } from '../../../errors.js'; import { AbstractOperation } from '../../../operations/index.js'; const schema = z.object({ command: z.string().describe('The command to execute on the service'), env: z .object() .catchall(z.string()) .describe('Environment variables to pass to the execution') .optional(), service: z .string() .describe('The name of the compose service to exec a shell'), }); export class ComposeExecOperation extends AbstractOperation { out; constructor(out) { super(schema); this.out = out; } async _run(input) { const { monorepo } = this.context; const cmd = 'docker'; const args = ['compose', 'exec']; // Add any env vars Object.entries(input.env || {}).forEach(([key, value]) => { args.push('-e', `${key.trim()}=${value.trim()}`); }); // add component and script args.push(input.service, input.command); const child = spawn(cmd, args, { stdio: 'pipe', shell: true, cwd: monorepo.rootDir, }); if (this.out) { child.stderr.pipe(this.out); child.stdout.pipe(this.out); } const forward = (sig) => { try { child.kill(sig); } catch { } }; const signals = [ 'SIGINT', 'SIGTERM', 'SIGHUP', 'SIGQUIT', ]; signals.forEach((sig) => { process.on(sig, () => forward(sig)); }); return new Promise((resolve, reject) => { child.on('error', (err) => { reject(new Error(`Failed to execute docker compose: ${err.message}`)); }); child.on('exit', (code, signal) => { if (code !== null && code !== 0) { reject(new ComposeExecError(`The shell exited unexpectedly. ${code}`, code, signal)); } else { resolve(); } }); }); } }