Skip to content
This repository
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

executable file 264 lines (237 sloc) 7.88 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263
#!/usr/bin/env node

var path = require('path'),
    fs = require('fs'),
    winston = require('winston'),
    util = require('util'),
    forever = require('./../lib/forever');

var action, accepts = [
  'config',
  'cleanlogs',
  'clear',
  'columns',
  'list',
  'start',
  'stop',
  'stopall',
  'set',
  'restart'
];

if (accepts.indexOf(process.argv[2]) !== -1) {
  action = process.argv.splice(2,1)[0];
}

var argv = require('optimist').boolean(['v', 'verbose', 'a', 'append', 's', 'silent', 'w', 'watch', 'plain']).argv;

var help = [
  'usage: forever [action] [options] SCRIPT [script-options]',
  '',
  'Monitors the script specified in the current process or as a daemon',
  '',
  'actions:',
  ' start Start SCRIPT as a daemon',
  ' stop Stop the daemon SCRIPT',
  ' stopall Stop all running forever scripts',
  ' restart Restart the daemon SCRIPT',
  ' list List all running forever scripts',
  ' config Lists all forever user configuration',
  ' set <key> <val> Sets the specified forever config <key>',
  ' clear <key> Clears the specified forever config <key>',
  ' columns add <col> Adds the specified column to the output in `forever list`',
  ' columns rm <col> Removed the specified column from the output in `forever list`',
  ' columns set <cols> Set all columns for the output in `forever list`',
  ' cleanlogs [CAREFUL] Deletes all historical forever log files',
  '',
  'options:',
  ' -m MAX Only run the specified script MAX times',
  ' -l LOGFILE Logs the forever output to LOGFILE',
  ' -o OUTFILE Logs stdout from child script to OUTFILE',
  ' -e ERRFILE Logs stderr from child script to ERRFILE',
  ' -p PATH Base path for all forever related files (pid files, etc.)',
  ' -c COMMAND COMMAND to execute (defaults to node)',
  ' -a, --append Append logs',
  ' --pidfile The pid file',
  ' --sourceDir The source directory for which SCRIPT is relative to',
  ' --minUptime Minimum uptime (millis) for a script to not be considered "spinning"',
  ' --spinSleepTime Time to wait (millis) between launches of a spinning script.',
  ' --plain Disable command line colors',
  ' -d, --debug Forces forever to log debug output',
  ' -v, --verbose Turns on the verbose messages from Forever',
  ' -s, --silent Run the child script silencing stdout and stderr',
  ' -w, --watch Watch for file changes',
  ' -h, --help You\'re staring at it',
  '',
  '[Long Running Process]',
  ' The forever process will continue to run outputting log messages to the console.',
  ' ex. forever -o out.log -e err.log my-script.js',
  '',
  '[Daemon]',
  ' The forever process will run as a daemon which will make the target process start',
  ' in the background. This is extremely useful for remote starting simple node.js scripts',
  ' without using nohup. It is recommended to run start with -o -l, & -e.',
  ' ex. forever start -l forever.log -o out.log -e err.log my-daemon.js',
  ' forever stop my-daemon.js',
  ''
].join('\n');

function isSimpleAction () {
  return ['config', 'list', 'stopall', 'cleanlogs'].indexOf(action) !== -1;
};

//
// Show help prompt if requested or if the
// incorrect usage options are supplied
//
if (argv.h || argv.help || (argv._.length === 0 && !isSimpleAction())
  && (!argv.c && !argv.command)) {
  return util.puts(help);
}

var mappings = {
  'c': 'command',
  'e': 'errFile',
  'l': 'logFile',
  'a': 'appendLog',
  'append': 'appendLog',
  'm': 'max',
  'o': 'outFile',
  'p': 'path',
  'pidfile': 'pidFile',
  's': 'silent',
  'silent': 'silent',
  'sourceDir': 'sourceDir',
  'minUptime': 'minUptime',
  'spinSleepTime': 'spinSleepTime',
  'plain': 'plain',
  'v': 'verbose',
  'verbose': 'verbose',
  'w': 'watch',
  'watch': 'watch',
  'd': 'debug',
  'debug': 'debug'
};

//
// If we are passed more than one non-hyphenated
// options, only use the first one. Assume the
// rest are pass-through for the child process
//
var file = argv._[0], options = {};

if (file) {
  //
  // Setup pass-thru options for child-process
  //
  options.options = process.argv.splice(process.argv.indexOf(file)).splice(1);
}
else if (argv.c || argv.command) {
  options.options = process.argv.splice(process.argv.indexOf(argv.c || argv.command) + 1);
}

//
// Now that we've removed the target script options
// reparse the options and configure the forever settings
//
argv = require('optimist')(process.argv).boolean(['v', 'verbose', 'a', 'append', 's', 'silent', 'watch', 'w', 'plain']).argv;
Object.keys(argv).forEach(function (key) {
  if (mappings[key] && argv[key]) {
    options[mappings[key]] = argv[key];
  }
});

if (typeof options['max'] === 'undefined') {
  //
  // If max isn't specified set it to run forever
  //
  options.forever = true;
}

if (typeof options['minUptime'] !== 'undefined') {
  options['minUptime'] = parseFloat(options['minUptime']);
}
if (typeof options['spinSleepTime'] !== 'undefined') {
  options['spinSleepTime'] = parseFloat(options['spinSleepTime']);
}

if (!options.sourceDir) {
  //
  // Set the sourceDir of the options for graceful
  // restarting outside of the main directory
  //
  options.sourceDir = file && file[0] !== '/' ? process.cwd() : '/';
}

var uid = forever.randomString(24);
options.uid = uid;
options.pidFile = options.pidFile || uid + '.pid';
options.logFile = argv.l || uid + '.log';

//
// Check for existing global config and set each
// key appropriately if it exists.
//
['append', 'silent', 'verbose', 'watch', 'plain'].forEach(function (key) {
  var target = mappings[key],
      value = forever.config.get(key);

  if (value) {
    options[target] = options[target] || value === 'true';
  }
});

//
// If options.plain is false, set colors.mode = 'none' to avoid colorful
// output (see https://github.com/indexzero/forever/issues/112 ).
// Modifying colors.mode turns off output coloring for all node modules
// which use colors because of node.js require caching.
//
if (options.plain) {
  require('colors').mode = 'none';
}

//
// Pass the source dir to spawn
//
options.spawnWith = {
  cwd: options.sourceDir
};

//
// Configure winston for forever based on the CLI options
//
if (options.verbose) {
  forever.log.transports.console.level = 'silly';
}

//
// Setup configurations for forever
//
var config = {
  root: argv.p
};

//
// Only call `forever.load()` if the root path is different than
// the default root exposed by forever.
//
if ((config.root && config.root !== forever.root)) {
  forever.log.silly('Loading forever with config: ', config);
  forever.load(config);
  forever.log.silly('Loaded forever successfully.');
}

//
// If there is an uncaught exception then log
// it to the default `forever._debug`.
//
process.on('uncaughtException', function (err) {
  forever._debug();
  forever.log.error('uncaughtException');
  forever.log.error(err.message);
  forever.log.error(err.stack);
});

//
// If we should debug this forever process then
// configure it to do so.
//
if (argv.d || argv.debug) {
  forever.log.warn('Forever debugging enabled');
  forever._debug();
}

if (action === 'set') {
  //
  // If this is a set action then get the first
  // value from the options and continue.
  //
  options = options.options[0];
}
else if (action === 'columns') {
  //
  // Otherwise if this is a `forever columns`
  // operation then update the action, options,
  // and file then continue.
  //
  action = [action, file];
  options = options.options;
  file = null;
}

forever.cli.exec(action, file, options);
Something went wrong with that request. Please try again.