process-rerun
Version:
The purpose of this library is - build simple and flexible interface for parallel command execution with rerun (on fail) possibility
77 lines • 3.03 kB
JavaScript
import { spawn } from 'node:child_process';
import { isString, getType, isObject, isFunction } from 'sat-utils';
import { ProcessRerunError } from '../error';
/**
* Extracts every shared cache payload emitted by the child process on its stdout.
*
* A process can publish data back to the runner by printing one or more lines shaped like:
* shared-cache: "<json payload here>"
*
* Each json payload lives inside the double quotes with its own quotes escaped.
* Returns an array with one parsed payload per matched line (empty when none are present),
* preserving the order in which the process printed them.
*/
function getCache(output) {
if (!isString(output))
return [];
const entries = [];
for (const match of output.matchAll(/shared-cache:\s*"((?:\\.|[^"\\])*)"/g)) {
const raw = match[1];
try {
// `raw` is the body of a JSON string literal (inner quotes escaped),
// so decode it first, then parse the decoded json payload.
const decoded = JSON.parse(`"${raw}"`);
try {
entries.push(JSON.parse(decoded));
}
catch {
entries.push(decoded);
}
}
catch {
entries.push(raw);
}
}
return entries;
}
function execute({ cmd, logProcessResult, executionHolder, execOpts = {}, }) {
if (!isString(cmd)) {
throw new ProcessRerunError('Type', `cmd (first argument should be a string), current type is ${getType(cmd)}`);
}
if (!isObject(executionHolder)) {
throw new ProcessRerunError('Type', `executionHolder (third argument should be an object), current type is ${getType(executionHolder)}`);
}
if (!isFunction(logProcessResult)) {
throw new ProcessRerunError('Type', `executionHolder (second argument should be a function), current type is ${getType(logProcessResult)}`);
}
const startTime = +Date.now();
let stdout = '';
let stderr = '';
let settled = false;
const execProc = spawn(cmd, { ...execOpts, shell: true, detached: process.platform !== 'win32' });
execProc.stdout?.on('data', chunk => {
stdout += chunk.toString();
});
execProc.stderr?.on('data', chunk => {
stderr += chunk.toString();
});
const finalize = (error) => {
if (settled)
return;
settled = true;
logProcessResult(cmd, startTime, execProc, error, stdout, stderr);
executionHolder.stackTrace += `${stdout}${stderr}`;
// collect every shared-cache line the process printed over its whole lifetime
executionHolder.cache = getCache(stdout);
};
execProc.on('error', finalize);
execProc.on('close', (code, signal) => {
const error = code !== 0 && code !== null
? Object.assign(new Error(`Command failed: ${cmd}`), { code, signal })
: null;
finalize(error);
});
return execProc;
}
export { execute, getCache };
//# sourceMappingURL=index.js.map