diff --git a/hooks.js b/hooks.js index d5614f1..512d6c2 100644 --- a/hooks.js +++ b/hooks.js @@ -2,6 +2,7 @@ const fs = require('fs').promises; const ms = require('ms'); const os = require('os'); const path = require('path'); +const { spawn } = require('child_process'); const getHooksRoot = () => path.join(os.homedir(), '/.syncthing-hooks'); @@ -9,7 +10,8 @@ const readHooksRoot = async root => { try { const files = await fs.readdir(root); return files; - } catch { + } catch (error) { + console.error(error); return []; } }; @@ -33,6 +35,26 @@ const collectHooks = async () => { return parseHooks(root, hooks); }; +const waitForProcess = childProcess => + new Promise((resolve, reject) => { + childProcess.once('exit', code => + code === 0 + ? resolve(null) + : reject(new Error(`hook failed with code: ${code}`)) + ); + childProcess.once('error', error => reject(error)); + }); + +const runHook = hook => + waitForProcess( + spawn(hook.path, [], { + cwd: path.dirname(hook.path), + shell: true, + stdio: [process.stdin, process.stdout, process.stderr], + }) + ); + module.exports = { collectHooks, + runHook, }; diff --git a/index.js b/index.js old mode 100644 new mode 100755 index 00a1f65..e938049 --- a/index.js +++ b/index.js @@ -1,11 +1,12 @@ const { fetchNewEvents } = require('./api'); -const { collectHooks } = require('./hooks'); +const { collectHooks, runHook } = require('./hooks'); require('dotenv').config(); const state = { seenIds: null, mostRecentEventForFolder: null, + promisesForHooks: new Map(), }; const getMostRecentEvents = (events, monitoredFolders) => { @@ -50,13 +51,31 @@ setInterval(async () => { .filter(x => deltaForFolders[x.folder]) .forEach(hook => { const timeToWait = hook.time - deltaForFolders[hook.folder]; - console.log(timeToWait); + console.log(`scheduled hook "${hook.path}" to run in ${timeToWait}ms`); if (timeToWait < 0) { - delete state.mostRecentEventForFolder[hook.folder]; - console.log('running hook', hook); + const existingPromise = state.promisesForHooks[hook.path]; + if (existingPromise) { + console.log( + `hook "${hook.path}" was skipped because it is already running` + ); + } else { + delete state.mostRecentEventForFolder[hook.folder]; + console.log(`running hook "${hook.path}"`); + const promise = runHook(hook); + state.promisesForHooks[hook.path] = promise; + promise + .then(() => { + console.log(`successfully ran hook "${hook.path}"`); + }) + .catch(error => { + console.error(`failed to run hook "${hook.path}": ${error}`); + }) + .finally(() => { + delete state.promisesForHooks[hook.path]; + }); + } } }); - console.warn(deltaForFolders); state.seenIds = seenIds; -}, 2000); +}, 30000);