Created
June 13, 2021 06:45
-
-
Save WoLfulus/31904e91aa027269941b5cf2a7a1ca70 to your computer and use it in GitHub Desktop.
Watch and restart executables
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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