Skip to content

Instantly share code, notes, and snippets.

@WoLfulus
Created June 13, 2021 06:45
Show Gist options
  • Save WoLfulus/31904e91aa027269941b5cf2a7a1ca70 to your computer and use it in GitHub Desktop.
Save WoLfulus/31904e91aa027269941b5cf2a7a1ca70 to your computer and use it in GitHub Desktop.
Watch and restart executables
const fs = require('fs');
const path = require('path');
const yargs = require('yargs');
const short = require('short-uuid')();
const chokidar = require("chokidar");
const log = require('pino')({
prettyPrint: true,
});
const args = yargs.argv;
const command = path.resolve(args.cmd);
const directory = args.wd || process.cwd();
if (!command) {
log.error('missing command');
process.exit(1);
}
if (!fs.existsSync(path.join(__dirname, '.temp'))) {
fs.mkdirSync(path.join(__dirname, '.temp'));
}
const { spawn } = require('child_process');
const context = {
/** @type {import('child_process').ChildProcess | null} */
instance: null,
temporaryLocation: null,
};
chokidar.watch(command).on("all", function (event) {
log.info({ arguments });
if (event == "add" || event == 'change') {
if (context.instance) {
log.info('Terminating process');
killInstance(context.instance, 'SIGTERM', () => {
log.info('Removing old executable');
if (fs.existsSync(context.temporaryLocation)) {
fs.unlinkSync(context.temporaryLocation);
}
log.info('Making a copy of the new executable');
context.temporaryLocation = path.join(__dirname, '.temp', short.new() + path.extname(command));
fs.copyFileSync(command, context.temporaryLocation);
log.info('Creating a new process instance');
context.instance = createInstance(context.temporaryLocation, args._, directory);
});
} else {
log.info('Making a copy of the new executable');
context.temporaryLocation = path.join(__dirname, '.temp', short.new() + path.extname(command));
fs.copyFileSync(command, context.temporaryLocation);
log.info('Starting child process');
context.instance = createInstance(context.temporaryLocation, args._, directory);
}
}
});
['SIGKILL', 'SIGINT', 'SIGTERM'].forEach(signal => {
log.info('Signal received, terminating child');
process.on(signal, () => {
if (context.instance) {
context.instance.kill(signal);
context.instance.once('close', (code) => process.exit(code));
context.instance.once('error', () => process.exit(1));
} else {
process.exit(0);
}
});
});
/**
* @param {string} command
* @param {string[]} args
* @param {string} directory
* @returns {import('child_process').ChildProcess}
*/
function createInstance(command, args, directory) {
var instance = spawn(command, [args._], {
stdio: 'inherit',
cwd: directory,
});
return instance;
}
/**
* @param {import('child_process').ChildProcess} instance
* @param {NodeJS.Signals} signal
* @param {Function} callback
*/
function killInstance(instance, signal, callback) {
log.info(`Killing instance ${instance.pid}`);
instance.once('close', callback || (() => { }));
instance.once('error', callback || (() => { }));
instance.kill(signal);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment