Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Avatar for KuCalc : devops.
Download
50654 views
1
/*
2
* cli.js: Handlers for the forever CLI commands.
3
*
4
* (C) 2010 Nodejitsu Inc.
5
* MIT LICENCE
6
*
7
*/
8
9
var fs = require('fs'),
10
util = require('util'),
11
path = require('path'),
12
flatiron = require('flatiron'),
13
cliff = require('cliff'),
14
forever = require('../forever');
15
16
var cli = exports;
17
18
var help = [
19
'usage: forever [action] [options] SCRIPT [script-options]',
20
'',
21
'Monitors the script specified in the current process or as a daemon',
22
'',
23
'actions:',
24
' start Start SCRIPT as a daemon',
25
' stop Stop the daemon SCRIPT',
26
' stopall Stop all running forever scripts',
27
' restart Restart the daemon SCRIPT',
28
' restartall Restart all running forever scripts',
29
' list List all running forever scripts',
30
' config Lists all forever user configuration',
31
' set <key> <val> Sets the specified forever config <key>',
32
' clear <key> Clears the specified forever config <key>',
33
' logs Lists log files for all forever processes',
34
' logs <script|index> Tails the logs for <script|index>',
35
' columns add <col> Adds the specified column to the output in `forever list`',
36
' columns rm <col> Removed the specified column from the output in `forever list`',
37
' columns set <cols> Set all columns for the output in `forever list`',
38
' cleanlogs [CAREFUL] Deletes all historical forever log files',
39
'',
40
'options:',
41
' -m MAX Only run the specified script MAX times',
42
' -l LOGFILE Logs the forever output to LOGFILE',
43
' -o OUTFILE Logs stdout from child script to OUTFILE',
44
' -e ERRFILE Logs stderr from child script to ERRFILE',
45
' -p PATH Base path for all forever related filesĀ (pid files, etc.)',
46
' -c COMMAND COMMAND to execute (defaults to node)',
47
' -a, --append Append logs',
48
' --pidFile The pid file',
49
' --sourceDir The source directory for which SCRIPT is relative to',
50
' --minUptime Minimum uptime (millis) for a script to not be considered "spinning"',
51
' --spinSleepTime Time to wait (millis) between launches of a spinning script.',
52
' --plain Disable command line colors',
53
' -d, --debug Forces forever to log debug output',
54
' -v, --verbose Turns on the verbose messages from Forever',
55
' -s, --silent Run the child script silencing stdout and stderr',
56
' -w, --watch Watch for file changes',
57
' --watchDirectory Top-level directory to watch from',
58
' -h, --help You\'re staring at it',
59
'',
60
'[Long Running Process]',
61
' The forever process will continue to run outputting log messages to the console.',
62
' ex. forever -o out.log -e err.log my-script.js',
63
'',
64
'[Daemon]',
65
' The forever process will run as a daemon which will make the target process start',
66
' in the background. This is extremely useful for remote starting simple node.js scripts',
67
' without using nohup. It is recommended to run start with -o -l, & -e.',
68
' ex. forever start -l forever.log -o out.log -e err.log my-daemon.js',
69
' forever stop my-daemon.js',
70
''
71
];
72
73
var app = flatiron.app;
74
75
var actions = [
76
'start',
77
'stop',
78
'stopall',
79
'restart',
80
'restartall',
81
'list',
82
'config',
83
'set',
84
'clear',
85
'logs',
86
'columns',
87
'cleanlogs'
88
];
89
90
var argvOptions = cli.argvOptions = {
91
'command': {alias: 'c'},
92
'errFile': {alias: 'e'},
93
'logFile': {alias: 'l'},
94
'append': {alias: 'a', boolean: true},
95
'max': {alias: 'm'},
96
'outFile': {alias: 'o'},
97
'path': {alias: 'p'},
98
'help': {alias: 'h'},
99
'silent': {alias: 's', boolean: true},
100
'verbose': {alias: 'v', boolean: true},
101
'watch': {alias: 'w', boolean: true},
102
'debug': {alias: 'd', boolean: true},
103
'plain': {boolean: true}
104
};
105
106
app.use(flatiron.plugins.cli, {
107
argv: argvOptions,
108
usage: help
109
});
110
111
var reserved = ['root', 'pidPath'];
112
113
//
114
// ### @private function (file, options, callback)
115
// #### @file {string} Target script to start
116
// #### @options {Object} Options to start the script with
117
// #### @callback {function} Continuation to respond to when complete.
118
// Helper function that sets up the pathing for the specified `file`
119
// then stats the appropriate files and responds.
120
//
121
function tryStart(file, options, callback) {
122
var fullLog, fullScript;
123
124
fullLog = forever.logFilePath(options.logFile, options.uid);
125
fullScript = path.join(options.sourceDir, file);
126
127
forever.stat(fullLog, fullScript, options.append, function (err) {
128
if (err) {
129
forever.log.error('Cannot start forever');
130
forever.log.error(err.message);
131
process.exit(-1);
132
}
133
134
callback();
135
});
136
}
137
138
//
139
// ### @private function updateConfig (updater)
140
// #### @updater {function} Function which updates the forever config
141
// Helper which runs the specified `updater` and then saves the forever
142
// config to `forever.config.get('root')`.
143
//
144
function updateConfig(updater) {
145
updater();
146
forever.config.save(function (err) {
147
if (err) {
148
return forever.log.error('Error saving config: ' + err.message);
149
}
150
151
cli.config();
152
var configFile = path.join(forever.config.get('root'), 'config.json');
153
forever.log.info('Forever config saved: ' + configFile.yellow);
154
});
155
}
156
157
//
158
// ### @private function checkColumn (name)
159
// #### @name {string} Column to check
160
// Checks if column `name` exists
161
//
162
function checkColumn(name) {
163
if (!forever.columns[name]) {
164
forever.log.error('Unknown column: ' + name.magenta);
165
return false;
166
}
167
return true;
168
}
169
170
//
171
// ### function getOptions (file)
172
// #### @file {string} File to run. **Optional**
173
// Returns `options` object for use with `forever.start` and
174
// `forever.startDaemon`
175
//
176
var getOptions = cli.getOptions = function (file) {
177
var options = {};
178
//
179
// First isolate options which should be passed to file
180
//
181
options.options = process.argv.splice(process.argv.indexOf(file) + 1);
182
183
//
184
// Now we have to force optimist to reparse command line options because
185
// we've removed some before.
186
//
187
app.config.stores.argv.store = {};
188
app.config.use('argv', argvOptions);
189
190
[
191
'pidFile', 'logFile', 'errFile', 'watch', 'minUptime', 'append',
192
'silent', 'outFile', 'max', 'command', 'path', 'spinSleepTime',
193
'sourceDir', 'uid', 'watchDirectory'
194
].forEach(function (key) {
195
options[key] = app.config.get(key);
196
});
197
198
options.sourceDir = options.sourceDir || (file && file[0] !== '/' ? process.cwd() : '/');
199
if (options.sourceDir) {
200
options.spawnWith = {cwd: options.sourceDir};
201
}
202
203
return options;
204
}
205
206
//
207
// ### function cleanLogs
208
// Deletes all historical forever log files
209
//
210
app.cmd('cleanlogs', cli.cleanLogs = function () {
211
forever.log.silly('Tidying ' + forever.config.get('root'));
212
forever.cleanUp(true).on('cleanUp', function () {
213
forever.log.silly(forever.config.get('root') + ' tidied.');
214
});
215
});
216
217
//
218
// ### function start (file)
219
// #### @file {string} Location of the script to spawn with forever
220
// Starts a forever process for the script located at `file` as daemon
221
// process.
222
//
223
app.cmd(/start (.+)/, cli.startDaemon = function () {
224
var file = app.argv._[1],
225
options = getOptions(file);
226
227
forever.log.info('Forever processing file: ' + file.grey);
228
tryStart(file, options, function () {
229
forever.startDaemon(file, options);
230
});
231
});
232
233
//
234
// ### function stop (file)
235
// #### @file {string} Target forever process to stop
236
// Stops the forever process specified by `file`.
237
//
238
app.cmd(/stop (.+)/, cli.stop = function (file) {
239
var runner = forever.stop(file, true);
240
241
runner.on('stop', function (process) {
242
forever.log.info('Forever stopped process:');
243
forever.log.data(process);
244
});
245
246
runner.on('error', function (err) {
247
forever.log.error('Forever cannot find process with index: ' + file);
248
});
249
});
250
251
//
252
// ### function stopall ()
253
// Stops all currently running forever processes.
254
//
255
app.cmd('stopall', cli.stopall = function () {
256
var runner = forever.stopAll(true);
257
runner.on('stopAll', function (processes) {
258
if (processes) {
259
forever.log.info('Forever stopped processes:');
260
processes.split('\n').forEach(function (line) {
261
forever.log.data(line);
262
});
263
}
264
else {
265
forever.log.info('No forever processes running');
266
}
267
});
268
269
runner.on('error', function () {
270
forever.log.info('No forever processes running');
271
});
272
});
273
274
//
275
// ### function stopall ()
276
// Stops all currently running forever processes.
277
//
278
app.cmd('restartall', cli.restartAll = function () {
279
var runner = forever.restartAll(true);
280
runner.on('restartAll', function (processes) {
281
if (processes) {
282
forever.log.info('Forever restarted processes:');
283
processes.split('\n').forEach(function (line) {
284
forever.log.data(line);
285
});
286
}
287
else {
288
forever.log.info('No forever processes running');
289
}
290
});
291
});
292
293
//
294
// ### function restart (file)
295
// #### @file {string} Target process to restart
296
// Restarts the forever process specified by `file`.
297
//
298
app.cmd(/restart (.+)/, cli.restart = function (file) {
299
var runner = forever.restart(file, true);
300
runner.on('restart', function (processes) {
301
if (processes) {
302
forever.log.info('Forever restarted process(es):');
303
processes.split('\n').forEach(function (line) {
304
forever.log.data(line);
305
});
306
}
307
else {
308
forever.log.info('No forever processes running');
309
}
310
});
311
312
runner.on('error', function (err) {
313
forever.log.error('Error restarting process: ' + file.grey);
314
forever.log.error(err.message);
315
});
316
});
317
318
//
319
// ### function list ()
320
// Lists all currently running forever processes.
321
//
322
app.cmd('list', cli.list = function () {
323
forever.list(true, function (err, processes) {
324
if (processes) {
325
forever.log.info('Forever processes running');
326
processes.split('\n').forEach(function (line) {
327
forever.log.data(line);
328
});
329
}
330
else {
331
forever.log.info('No forever processes running');
332
}
333
});
334
});
335
336
//
337
// ### function config ()
338
// Lists all of the configuration in `~/.forever/config.json`.
339
//
340
app.cmd('config', cli.config = function () {
341
var keys = Object.keys(forever.config.store),
342
conf = cliff.inspect(forever.config.store);
343
344
if (keys.length <= 2) {
345
conf = conf.replace(/\{\s/, '{ \n')
346
.replace(/\}/, '\n}')
347
.replace('\\033[90m', ' \\033[90m')
348
.replace(/, /ig, ',\n ');
349
}
350
else {
351
conf = conf.replace(/\n\s{4}/ig, '\n ');
352
}
353
354
conf.split('\n').forEach(function (line) {
355
forever.log.data(line);
356
});
357
});
358
359
//
360
// ### function set (key, value)
361
// #### @key {string} Key to set in forever config
362
// #### @value {string} Value to set for `key`
363
// Sets the specified `key` / `value` pair in the
364
// forever user config.
365
//
366
app.cmd(/set ([\w-_]+) (.+)/, cli.set = function (key, value) {
367
updateConfig(function () {
368
forever.log.info('Setting forever config: ' + key.grey);
369
forever.config.set(key, value);
370
});
371
});
372
373
//
374
// ### function clear (key)
375
// #### @key {string} Key to remove from `~/.forever/config.json`
376
// Removes the specified `key` from the forever user config.
377
//
378
app.cmd('clear :key', cli.clear = function (key) {
379
if (reserved.indexOf(key) !== -1) {
380
forever.log.warn('Cannot clear reserved config: ' + key.grey);
381
forever.log.warn('Use `forever set ' + key + '` instead');
382
return;
383
}
384
385
updateConfig(function () {
386
forever.log.info('Clearing forever config: ' + key.grey);
387
forever.config.clear(key);
388
});
389
});
390
391
//
392
// ### function logs (target)
393
// #### @target {string} Target script or index to list logs for
394
// Displays the logs using `tail` for the specified `target`.
395
//
396
app.cmd('logs :index', cli.logs = function (index) {
397
forever.tail(index, function (err, logs) {
398
if (err) {
399
return forever.log.error(err.message);
400
}
401
402
logs.forEach(function (proc) {
403
forever.log.info('Showing logs for ' + proc.file.magenta);
404
proc.logs.forEach(function (line) {
405
forever.log.data(line);
406
});
407
});
408
});
409
});
410
411
//
412
// ### function logFiles ()
413
// Display log files for all running forever processes.
414
//
415
app.cmd('logs', cli.logFiles = function (index) {
416
if (typeof index !== 'undefined') {
417
return;
418
}
419
420
var rows = [[' ', 'script', 'logfile']];
421
index = 0;
422
423
forever.list(false, function (err, processes) {
424
if (!processes) {
425
return forever.log.warn('No forever logfiles in ' + forever.config.get('root').magenta);
426
}
427
428
forever.log.info('Logs for running Forever processes');
429
rows = rows.concat(processes.map(function (proc) {
430
return ['[' + index++ + ']', proc.file.grey, proc.logFile.magenta];
431
}));
432
433
cliff.putRows('data', rows, ['white', 'grey', 'magenta']);
434
});
435
});
436
437
438
app.cmd('columns add :name', cli.addColumn = function (name) {
439
if (checkColumn(name)) {
440
var columns = forever.config.get('columns');
441
442
if (~columns.indexOf(name)) {
443
return forever.log.warn(name.magenta + ' already exists in forever');
444
}
445
446
forever.log.info('Adding column: ' + name.magenta);
447
columns.push(name);
448
449
forever.config.set('columns', columns);
450
forever.config.saveSync();
451
}
452
});
453
454
app.cmd('columns rm :name', cli.rmColumn = function (name) {
455
if (checkColumn(name)) {
456
var columns = forever.config.get('columns');
457
458
if (!~columns.indexOf(name)) {
459
return forever.log.warn(name.magenta + ' doesn\'t exist in forever');
460
}
461
462
forever.log.info('Removing column: ' + name.magenta);
463
columns.splice(columns.indexOf(name), 1);
464
465
forever.config.set('columns', columns);
466
forever.config.saveSync();
467
}
468
});
469
470
app.cmd(/columns set (.*)/, cli.setColumns = function (columns) {
471
forever.log.info('Setting columns: ' + columns.magenta);
472
473
forever.config.set('columns', columns.split(' '));
474
forever.config.saveSync();
475
});
476
477
//
478
// ### function help ()
479
// Shows help
480
//
481
app.cmd('help', cli.help = function () {
482
util.puts(help.join('\n'));
483
});
484
485
//
486
// ### function start (file)
487
// #### @file {string} Location of the script to spawn with forever
488
// Starts a forever process for the script located at `file` as non-daemon
489
// process.
490
//
491
// Remark: this regex matches everything. It has to be added at the end to
492
// make executing other commands possible.
493
//
494
cli.run = function () {
495
var file = app.argv._[0],
496
options = getOptions(file);
497
498
tryStart(file, options, function () {
499
var monitor = forever.start(file, options);
500
monitor.on('start', function () {
501
forever.startServer(monitor);
502
});
503
});
504
};
505
506
cli.start = function () {
507
if (app.config.get('help')) {
508
return util.puts(help.join('\n'));
509
}
510
511
app.init(function () {
512
if (app.argv._.length && actions.indexOf(app.argv._[0]) === -1) {
513
return cli.run();
514
}
515
516
app.start();
517
});
518
};
519
520
521