diff --git a/bin/pm2 b/bin/pm2 index d7826ef15..c6f6c3ec6 100755 --- a/bin/pm2 +++ b/bin/pm2 @@ -76,6 +76,7 @@ commander.version(pkg.version) .option('--merge-logs', 'merge logs from different instances but keep error and out separated') .option('--watch [paths]', 'watch application folder for changes', function(v, m) { m.push(v); return m;}, []) .option('--ignore-watch ', 'List of paths to ignore (name or regex)') + .option('--watch-delay ', 'specify a restart delay after changing files (--watch-delay 4 (in sec) or 4000ms)') .option('--no-color', 'skip colors') .option('--no-vizion', 'start an app without vizion feature (versioning control)') .option('--no-autorestart', 'start an app without automatic restart') diff --git a/lib/API.js b/lib/API.js index ba25e4093..ebd2fc2df 100644 --- a/lib/API.js +++ b/lib/API.js @@ -25,7 +25,8 @@ var Modularizer = require('./API/Modules/Modularizer.js'); var path_structure = require('../paths.js'); var UX = require('./API/CliUx'); var pkg = require('../package.json'); -var hf = require('./API/Modules/flagExt.js') +var flagWatch = require("./API/Modules/flagWatch.js"); +var hf = require('./API/Modules/flagExt.js'); var IMMUTABLE_MSG = chalk.bold.blue('Use --update-env to update environment variables'); @@ -648,6 +649,8 @@ class API { var app_conf = Config.transCMDToConf(opts); var appConf = {}; + var ignoreFileArray = []; + if (!!opts.executeCommand) app_conf.exec_mode = 'fork'; else if (opts.instances !== undefined) @@ -674,11 +677,28 @@ class API { app_conf = appConf[0]; + + if (opts.ignoreWatch) { + flagWatch.handleFolders(opts.ignoreWatch, ignoreFileArray); + if (app_conf.ignore_watch) { + app_conf.ignore_watch = ignoreFileArray; + } + } + + if (opts.watchDelay) { + if (typeof opts.watchDelay === "string" && opts.watchDelay.indexOf("ms") !== -1) + app_conf.watch_delay = parseInt(opts.watchDelay); + else { + app_conf.watch_delay = parseFloat(opts.watchDelay) * 1000; + } + } + var mas = []; if(typeof opts.ext != 'undefined') hf.make_available_extension(opts, mas); // for -e flag mas.length > 0 ? app_conf.ignore_watch = mas : 0; + /** * If -w option, write configuration to configuration.json file */ diff --git a/lib/API/Modules/flagWatch.js b/lib/API/Modules/flagWatch.js new file mode 100644 index 000000000..8603c9a63 --- /dev/null +++ b/lib/API/Modules/flagWatch.js @@ -0,0 +1,29 @@ +var fs = require('fs'); + +function handleFolders(folder, mas) { + if (!folder || !mas || folder.indexOf("node_modules") !== -1) + return ; + + try { + fs.accessSync(folder, fs.constants.R_OK); + } catch (err) { + return ; + } + + if (fs.statSync(folder) && fs.statSync(folder).isDirectory()) { + fs.readdirSync(folder).forEach(file => { + if (fs.statSync(folder)["mode"] & 4 === 0) + return ; + if (fs.existsSync(folder + file + '/')) + handleFolders(folder + file + '/', mas); + else + mas.push(folder + file); + }); + } else { + if (fs.statSync(folder).isFile()) { + mas.push(folder); + } + } +} + +module.exports.handleFolders = handleFolders; diff --git a/lib/Watcher.js b/lib/Watcher.js index 2b09b6934..779bb38d8 100644 --- a/lib/Watcher.js +++ b/lib/Watcher.js @@ -63,16 +63,18 @@ module.exports = function ClusterMode(God) { console.error('Change detected on path %s for app %s - restarting', path, pm2_env.name); - God.restartProcessName(pm2_env.name, function(err, list) { - self.restarting = false; - - if (err) { - log('Error while restarting', err); - return false; - } - - return log('Process restarted'); - }); + setTimeout(function() { + God.restartProcessName(pm2_env.name, function(err, list) { + self.restarting = false; + + if (err) { + log('Error while restarting', err); + return false; + } + + return log('Process restarted'); + }); + }, (pm2_env.watch_delay || 0)); return false; }); diff --git a/test/programmatic/flagWatch.mocha.js b/test/programmatic/flagWatch.mocha.js new file mode 100644 index 000000000..572000bcc --- /dev/null +++ b/test/programmatic/flagWatch.mocha.js @@ -0,0 +1,55 @@ + +var should = require('should'); +var f_w = require('../../lib/API/Modules/flagWatch.js'); +var fs = require('fs'); + +describe('Flag --ignore-watch', function() { + + it('should return not empty result', function() { + var res = []; + f_w.handleFolders('./', res); + should(res).be.not.empty(); + }); + it('should not crash', function() { + var res = [] + f_w.handleFolders(); + f_w.handleFolders(res); + f_w.handleFolders(''); + f_w.handleFolders('lsdldmcsdf/amfkdmfk'); + }); + it('should give different results', function() { + var tmp_res = []; + var res = []; + f_w.handleFolders('./lib', res); + f_w.handleFolders('./examples', tmp_res); + should(res).not.equal(tmp_res); + }); + it('should not crash in case, when no access for file or directory by permissions', function() { + var fileStream; + + if (!fs.existsSync("noAccessDir")) + fs.mkdirSync("noAccessDir", 0777); + if (!fs.existsSync("noAccessDir/checkPermissions.txt")) { + fileStream = fs.createWriteStream("noAccessDir/checkPermissions.txt"); + fileStream.write("It's a temporary file for testing flag --ignore-watch in PM2"); + fileStream.end(); + } + fs.chmodSync('noAccessDir/checkPermissions.txt', 0000); + fs.chmodSync('noAccessDir', 0000); + + after(function () { + fs.chmodSync('noAccessDir', 0777); + fs.chmodSync('noAccessDir/checkPermissions.txt', 0777); + fs.unlinkSync('noAccessDir/checkPermissions.txt'); + fs.rmdirSync('noAccessDir/'); + }); + + f_w.handleFolders('noAccessDir/', []); + f_w.handleFolders('noAccessDir/checkPermissions.txt', []); + }); + it('should ignore node_modules folder', function() { + var res = []; + f_w.handleFolders('./node_modules', res); + should(res).be.empty(); + }); +}); diff --git a/test/programmatic/watcher.js b/test/programmatic/watcher.js index c0e172728..41d96e634 100644 --- a/test/programmatic/watcher.js +++ b/test/programmatic/watcher.js @@ -178,6 +178,16 @@ describe('Watcher', function() { }) }) + it('should work with watch_delay', function(cb) { + testPM2Env('server-watch:online')({watch: true, watch_delay: 4000}, cb); + pm2.start(extend(json, {watch: true, watch_delay: 4000}), errShouldBeNull); + }) + + it('should not crash with watch_delay without watch', function(cb) { + testPM2Env('server-watch:online')({watch_delay: 4000}, cb); + pm2.start(extend(json, {watch_delay: 4000}), errShouldBeNull); + }) + /** * Test #1668 */