Skip to content

Commit 29b4a0b

Browse files
acburdineaileen
authored andcommitted
feat(process-manager): allow process isRunning to return a promise (#669)
refs #668 - update instance.running() to handle process.isRunning promise - update everywhere that uses instance.running to handle the promise - fix tests
1 parent cb2e3d0 commit 29b4a0b

22 files changed

+503
-407
lines changed

lib/commands/doctor/checks/validate-config.js

+35-26
Original file line numberDiff line numberDiff line change
@@ -10,45 +10,54 @@ const advancedOptions = require('../../config/advanced');
1010

1111
const taskTitle = 'Validating config';
1212

13-
function validateConfig(ctx) {
14-
const config = Config.exists(path.join(process.cwd(), `config.${ctx.system.environment}.json`));
15-
16-
if (config === false) {
17-
return Promise.reject(new errors.ConfigError({
18-
environment: ctx.system.environment,
19-
message: 'Config file is not valid JSON',
20-
task: taskTitle
21-
}));
13+
function validateConfig(ctx, task) {
14+
if (!ctx.instance) {
15+
return task.skip('Instance not set');
2216
}
2317

24-
const configValidations = filter(advancedOptions, cfg => cfg.validate);
18+
return ctx.instance.running().then((isRunning) => {
19+
if (isRunning) {
20+
return task.skip('Instance is currently running');
21+
}
2522

26-
return Promise.each(configValidations, (configItem) => {
27-
const key = configItem.configPath || configItem.name
28-
const value = get(config, key);
23+
const config = Config.exists(path.join(process.cwd(), `config.${ctx.system.environment}.json`));
2924

30-
if (!value) {
31-
return;
25+
if (config === false) {
26+
return Promise.reject(new errors.ConfigError({
27+
environment: ctx.system.environment,
28+
message: 'Config file is not valid JSON',
29+
task: taskTitle
30+
}));
3231
}
3332

34-
return Promise.resolve(configItem.validate(value)).then((validated) => {
35-
if (validated !== true) {
36-
return Promise.reject(new errors.ConfigError({
37-
config: {
38-
[key]: value
39-
},
40-
message: validated,
41-
environment: ctx.system.environment,
42-
task: taskTitle
43-
}));
33+
const configValidations = filter(advancedOptions, cfg => cfg.validate);
34+
35+
return Promise.each(configValidations, (configItem) => {
36+
const key = configItem.configPath || configItem.name
37+
const value = get(config, key);
38+
39+
if (!value) {
40+
return;
4441
}
42+
43+
return Promise.resolve(configItem.validate(value)).then((validated) => {
44+
if (validated !== true) {
45+
return Promise.reject(new errors.ConfigError({
46+
config: {
47+
[key]: value
48+
},
49+
message: validated,
50+
environment: ctx.system.environment,
51+
task: taskTitle
52+
}));
53+
}
54+
});
4555
});
4656
});
4757
}
4858

4959
module.exports = {
5060
title: taskTitle,
5161
task: validateConfig,
52-
skip: (ctx) => ctx.instance && ctx.instance.process.isRunning(ctx.instance.dir),
5362
category: ['start']
5463
}

lib/commands/log.js

+38-38
Original file line numberDiff line numberDiff line change
@@ -22,53 +22,53 @@ class LogCommand extends Command {
2222
return Promise.reject(new errors.SystemError(`Ghost instance '${argv.name}' does not exist`));
2323
}
2424

25-
if (instance.running()) {
26-
instance.loadRunningEnvironment();
27-
} else {
28-
instance.checkEnvironment();
29-
}
30-
31-
// Check if logging file transport is set in config
32-
if (!includes(instance.config.get('logging.transports', []), 'file')) {
33-
// TODO: fallback to process manager log retrieval?
34-
return Promise.reject(new errors.ConfigError({
35-
config: {
36-
'logging.transports': instance.config.get('logging.transports').join(', ')
37-
},
38-
message: 'You have excluded file logging in your ghost config.' +
39-
'Please add it to your transport config to use this command.',
40-
environment: this.system.environment
41-
}));
42-
}
43-
44-
const logFileName = path.join(instance.dir, 'content/logs', `${instance.config.get('url').replace(/[^\w]/gi, '_')}_${this.system.environment}${argv.error ? '.error' : ''}.log`);
45-
const prettyStream = new PrettyStream();
25+
return instance.running().then((running) => {
26+
if (!running) {
27+
instance.checkEnvironment();
28+
}
4629

47-
if (!fs.existsSync(logFileName)) {
48-
if (argv.follow) {
49-
this.ui.log('Log file has not been created yet, `--follow` only works on existing files', 'yellow');
30+
// Check if logging file transport is set in config
31+
if (!includes(instance.config.get('logging.transports', []), 'file')) {
32+
// TODO: fallback to process manager log retrieval?
33+
return Promise.reject(new errors.ConfigError({
34+
config: {
35+
'logging.transports': instance.config.get('logging.transports').join(', ')
36+
},
37+
message: 'You have excluded file logging in your ghost config.' +
38+
'Please add it to your transport config to use this command.',
39+
environment: this.system.environment
40+
}));
5041
}
5142

52-
return Promise.resolve();
53-
}
43+
const logFileName = path.join(instance.dir, 'content/logs', `${instance.config.get('url').replace(/[^\w]/gi, '_')}_${this.system.environment}${argv.error ? '.error' : ''}.log`);
44+
const prettyStream = new PrettyStream();
45+
46+
if (!fs.existsSync(logFileName)) {
47+
if (argv.follow) {
48+
this.ui.log('Log file has not been created yet, `--follow` only works on existing files', 'yellow');
49+
}
5450

55-
prettyStream.on('error', (error) => {
56-
if (!(error instanceof SyntaxError)) {
57-
throw error;
51+
return Promise.resolve();
5852
}
59-
});
6053

61-
prettyStream.pipe(this.ui.stdout);
54+
prettyStream.on('error', (error) => {
55+
if (!(error instanceof SyntaxError)) {
56+
throw error;
57+
}
58+
});
6259

63-
return lastLines.read(logFileName, argv.number).then((lines) => {
64-
lines.trim().split('\n').forEach(line => prettyStream.write(line));
60+
prettyStream.pipe(this.ui.stdout);
6561

66-
if (argv.follow) {
67-
const Tail = require('tail').Tail;
62+
return lastLines.read(logFileName, argv.number).then((lines) => {
63+
lines.trim().split('\n').forEach(line => prettyStream.write(line));
6864

69-
const tail = new Tail(logFileName);
70-
tail.on('line', (line) => prettyStream.write(line, 'utf8'));
71-
}
65+
if (argv.follow) {
66+
const Tail = require('tail').Tail;
67+
68+
const tail = new Tail(logFileName);
69+
tail.on('line', (line) => prettyStream.write(line, 'utf8'));
70+
}
71+
});
7272
});
7373
}
7474
}

lib/commands/ls.js

+14-12
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,22 @@ const Command = require('../command');
44
class LsCommand extends Command {
55
run() {
66
const chalk = require('chalk');
7+
const Promise = require('bluebird');
78

8-
const instances = this.system.getAllInstances();
9-
const rows = instances.map((instance) => {
10-
const summary = instance.summary();
9+
return this.system.getAllInstances().then((instances) => {
10+
return Promise.map(instances, (instance) => {
11+
return instance.summary().then((summary) => {
12+
if (!summary.running) {
13+
return [summary.name, summary.dir, summary.version, chalk.red('stopped'), chalk.red('n/a'), chalk.red('n/a'), chalk.red('n/a')];
14+
}
1115

12-
if (!summary.running) {
13-
return [summary.name, summary.dir, summary.version, chalk.red('stopped'), chalk.red('n/a'), chalk.red('n/a'), chalk.red('n/a')];
14-
}
15-
16-
return [summary.name, summary.dir, summary.version, `${chalk.green('running')} (${summary.mode})`, summary.url, summary.port, summary.process];
17-
});
18-
19-
this.ui.table(['Name', 'Location', 'Version', 'Status', 'URL', 'Port', 'Process Manager'], rows, {
20-
style: {head: ['cyan']}
16+
return [summary.name, summary.dir, summary.version, `${chalk.green('running')} (${summary.mode})`, summary.url, summary.port, summary.process];
17+
});
18+
});
19+
}).then((rows) => {
20+
this.ui.table(['Name', 'Location', 'Version', 'Status', 'URL', 'Port', 'Process Manager'], rows, {
21+
style: {head: ['cyan']}
22+
});
2123
});
2224
}
2325
}

lib/commands/restart.js

+9-7
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,16 @@ class RestartCommand extends Command {
55
run() {
66
const instance = this.system.getInstance();
77

8-
if (!instance.running()) {
9-
const StartCommand = require('./start');
10-
this.ui.log('Ghost instance is not running! Starting...', 'yellow');
11-
return this.runCommand(StartCommand);
12-
}
8+
return instance.running().then((isRunning) => {
9+
if (!isRunning) {
10+
const StartCommand = require('./start');
11+
this.ui.log('Ghost instance is not running! Starting...', 'yellow');
12+
return this.runCommand(StartCommand);
13+
}
1314

14-
instance.loadRunningEnvironment(true);
15-
return this.ui.run(instance.process.restart(process.cwd(), this.system.environment), 'Restarting Ghost');
15+
instance.loadRunningEnvironment(true);
16+
return this.ui.run(instance.process.restart(process.cwd(), this.system.environment), 'Restarting Ghost');
17+
});
1618
}
1719
}
1820

lib/commands/start.js

+39-37
Original file line numberDiff line numberDiff line change
@@ -25,44 +25,46 @@ class StartCommand extends Command {
2525
const instance = this.system.getInstance();
2626
const runOptions = {quiet: argv.quiet};
2727

28-
if (instance.running()) {
29-
this.ui.log('Ghost is already running! Run `ghost ls` for more information', 'green');
30-
return Promise.resolve();
31-
}
32-
33-
instance.checkEnvironment();
34-
35-
return this.runCommand(DoctorCommand, Object.assign({
36-
categories: ['start'],
37-
quiet: true
38-
}, argv)).then(() => {
39-
const processInstance = instance.process;
40-
41-
const start = () => {
42-
return Promise.resolve(processInstance.start(process.cwd(), this.system.environment))
43-
.then(() => { instance.running(this.system.environment); });
44-
};
45-
46-
return this.ui.run(start, 'Starting Ghost', runOptions).then(() => {
47-
// If process manager doesn't support enable behavior OR it's already enabled, don't try to enable
48-
if (!ProcessManager.supportsEnableBehavior(processInstance) || processInstance.isEnabled()) {
49-
argv.enable = false;
50-
}
51-
52-
if (!argv.enable) {
53-
return Promise.resolve();
54-
}
55-
56-
return this.ui.run(processInstance.enable(), 'Enabling Ghost instance startup on server boot', runOptions);
57-
}).then(() => {
58-
if (!argv.quiet) {
59-
this.ui.log(`You can access your blog at ${instance.config.get('url')}`, 'cyan');
60-
61-
if (instance.config.get('mail.transport') === 'Direct') {
62-
this.ui.log('\nGhost uses direct mail by default', 'green');
63-
this.ui.log('To set up an alternative email method read our docs at https://docs.ghost.org/docs/mail-config', 'green');
28+
return instance.running().then((isRunning) => {
29+
if (isRunning) {
30+
this.ui.log('Ghost is already running! Run `ghost ls` for more information', 'green');
31+
return Promise.resolve();
32+
}
33+
34+
instance.checkEnvironment();
35+
36+
return this.runCommand(DoctorCommand, Object.assign({
37+
categories: ['start'],
38+
quiet: true
39+
}, argv)).then(() => {
40+
const processInstance = instance.process;
41+
42+
const start = () => {
43+
return Promise.resolve(processInstance.start(process.cwd(), this.system.environment))
44+
.then(() => { instance.running(this.system.environment); });
45+
};
46+
47+
return this.ui.run(start, 'Starting Ghost', runOptions).then(() => {
48+
// If process manager doesn't support enable behavior OR it's already enabled, don't try to enable
49+
if (!ProcessManager.supportsEnableBehavior(processInstance) || processInstance.isEnabled()) {
50+
argv.enable = false;
51+
}
52+
53+
if (!argv.enable) {
54+
return Promise.resolve();
55+
}
56+
57+
return this.ui.run(processInstance.enable(), 'Enabling Ghost instance startup on server boot', runOptions);
58+
}).then(() => {
59+
if (!argv.quiet) {
60+
this.ui.log(`You can access your blog at ${instance.config.get('url')}`, 'cyan');
61+
62+
if (instance.config.get('mail.transport') === 'Direct') {
63+
this.ui.log('\nGhost uses direct mail by default', 'green');
64+
this.ui.log('To set up an alternative email method read our docs at https://docs.ghost.org/docs/mail-config', 'green');
65+
}
6466
}
65-
}
67+
});
6668
});
6769
});
6870
}

lib/commands/stop.js

+10-10
Original file line numberDiff line numberDiff line change
@@ -41,18 +41,18 @@ class StopCommand extends Command {
4141

4242
checkValidInstall('stop');
4343

44-
if (!instance.running()) {
45-
this.ui.log('Ghost is already stopped! Nothing to do here.', 'green');
46-
return Promise.resolve();
47-
}
44+
return instance.running().then((isRunning) => {
45+
if (!isRunning) {
46+
this.ui.log('Ghost is already stopped! Nothing to do here.', 'green');
47+
return Promise.resolve();
48+
}
4849

49-
instance.loadRunningEnvironment();
50+
const stop = () => Promise.resolve(instance.process.stop(process.cwd())).then(() => {
51+
instance.running(null);
52+
});
5053

51-
const stop = () => Promise.resolve(instance.process.stop(process.cwd())).then(() => {
52-
instance.running(null);
53-
});
54-
55-
return this.ui.run(stop, 'Stopping Ghost', runOptions).then(() => {
54+
return this.ui.run(stop, 'Stopping Ghost', runOptions);
55+
}).then(() => {
5656
if (
5757
argv.disable &&
5858
ProcessManager.supportsEnableBehavior(instance.process) &&

lib/commands/uninstall.js

+11-6
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,17 @@ class UninstallCommand extends Command {
2323

2424
return this.ui.listr([{
2525
title: 'Stopping Ghost',
26-
skip: () => !instance.running(),
27-
task: () => {
28-
instance.loadRunningEnvironment(true);
29-
// If the instance is currently running we need to make sure
30-
// it gets stopped and disabled if possible
31-
return this.runCommand(StopCommand, {quiet: true, disable: true});
26+
task: (_, task) => {
27+
return instance.running().then((isRunning) => {
28+
if (!isRunning) {
29+
return task.skip('Instance is not running');
30+
}
31+
32+
instance.loadRunningEnvironment(true);
33+
// If the instance is currently running we need to make sure
34+
// it gets stopped and disabled if possible
35+
return this.runCommand(StopCommand, {quiet: true, disable: true});
36+
});
3237
}
3338
}, {
3439
title: 'Removing content folder',

0 commit comments

Comments
 (0)