keep-running/src/index.js

116 lines
3.2 KiB
JavaScript

const Yargs = require('yargs');
const {spawn} = require("child_process");
const yargs = Yargs(process.argv.splice(2))
.scriptName('kr')
.usage('kr [args] <process-to-run>')
.option('_rpm', {
default: 0,
description: 'The amount of Retries Per Minute, before we stop retrying',
type: 'number'
})
.option('_rph', {
default: 0,
description: 'The amount of Retries Per Hour, before we stop retrying',
type: 'number'
})
.option('_delay', {
default: 0,
description: 'Time in seconds we want to delay the restart with.',
type: 'number'
})
.conflicts('_rpm', '_rph')
.help();
const { _ = [], _rpm: rpm, _rph: rph, _delay: delay } = yargs.argv;
const [command, ...args ] = _;
if (!command) {
return yargs.showHelp();
}
const SECONDS_IN_A_MINUTE = 60;
const SECONDS_IN_A_HOUR = 60 * 60;
let historyMax = 4;
let seconds = SECONDS_IN_A_MINUTE;
let restartName = 'minute';
if (rpm) {
historyMax = rpm
} else if (rph) {
historyMax = rph
seconds = SECONDS_IN_A_HOUR
restartName = 'hour'
}
/** @type {Object<number, string>} */
const history = {};
const getNow = () => Math.ceil(Date.now() / 1000); // Time in seconds
/** @param {string} logs */
const pushHistory = (logs) => history[getNow() + seconds] = logs;
const updateHistory = () => {
const now = getNow();
const clearKeys = Object.keys(history)
.filter((time) => time <= now);
clearKeys.forEach((key) => delete history[key]);
}
const checkHistory = () => Object.keys(history).length <= historyMax;
const restart = () => setTimeout(() => runCommand(delay), delay * 1000);
const runCommand = () => {
const runner = spawn(command, args);
const commandLogs = [];
const pushLog = (logEntry) => commandLogs.push(logEntry);
const trimLog = () => {
if(commandLogs.length > 5000) commandLogs.shift()
};
const clearLogs = () => commandLogs.length = 0;
const getLogs = () => commandLogs.join('\n');
const handleOut = (type, message) => {
pushLog(`[${type}]\t${message}`);
trimLog();
};
const handleExit = (exitCode) => {
if (exitCode === 0) {
console.log(`Exit code: ${exitCode}`);
} else{
console.log(`[CRASH] exit code: ${exitCode}`)
pushHistory(getLogs());
clearLogs();
updateHistory();
if (checkHistory()) {
console.log('Restarting...');
restart();
} else {
console.error(`The process crashed more then ${historyMax} times in the past ${restartName}, stop retrying.`);
console.error(`See below the last crash log:`);
const keys = Object.keys(history);
const lastKey = keys[keys.length-1];
console.log(history[lastKey]);
}
}
}
runner.on('close', (exitCode) => handleExit(exitCode))
runner.stdout.on('data', (data) => console.log(data.toString().trim()))
runner.stdout.on('data', (data) => handleOut('LOG', data.toString().trim()))
runner.stderr.on('data', (data) => handleOut('ERR', data.toString().trim()))
}
// Leggo!~
runCommand();