Skip to content

Commit 47be79b

Browse files
committed
feat(ui): add custom listr renderer and move listr to ui class
closes #181 - add custom Listr renderer that works better with other UI methods e.g. log, prompt, run - refactor commands to use this.ui.listr instead of new Listr() - fix various command arg bugs
1 parent cf7bc54 commit 47be79b

File tree

10 files changed

+222
-148
lines changed

10 files changed

+222
-148
lines changed

lib/command.js

-4
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,6 @@ class Command {
115115
let context = {
116116
ui: ui,
117117
service: service,
118-
renderer: verbose ? 'verbose' : 'update',
119-
verbose: verbose,
120118
cliVersion: pkg.version
121119
};
122120

@@ -160,8 +158,6 @@ class Command {
160158
// Will refactor this out with the service refactors
161159
this.ui = context.ui;
162160
this.service = context.service;
163-
this.renderer = context.renderer;
164-
this.verbose = context.verbose;
165161
this.cliVersion = context.cliVersion;
166162
this.development = context.development;
167163
this.environment = context.environment;

lib/commands/doctor/checks/setup.js

+51-41
Original file line numberDiff line numberDiff line change
@@ -2,62 +2,72 @@
22
const os = require('os');
33
const chalk = require('chalk');
44
const execa = require('execa');
5-
const Listr = require('listr');
65

76
const errors = require('../../../errors');
87

98
module.exports = [{
109
title: 'System Stack',
1110
task: (context) => {
11+
let promise;
12+
1213
if (os.platform() !== 'linux') {
1314
context.linux = false;
14-
return Promise.reject(new errors.SystemError(chalk.yellow('Platform is not Linux')));
15-
}
15+
promise = Promise.reject(new errors.SystemError(chalk.yellow('Platform is not Linux')));
16+
} else {
17+
context.linux = true;
1618

17-
context.linux = true;
19+
promise = execa.shell('lsb_release -a').then((result) => {
20+
if (!result.stdout || !result.stdout.match(/Ubuntu 16/)) {
21+
context.ubuntu = false;
22+
return Promise.reject(new errors.SystemError(chalk.yellow('Linux version is not Ubuntu 16')));
23+
}
1824

19-
return execa.shell('lsb_release -a').then((result) => {
20-
if (!result.stdout || !result.stdout.match(/Ubuntu 16/)) {
21-
context.ubuntu = false;
22-
return Promise.reject(new errors.SystemError(chalk.yellow('Linux version is not Ubuntu 16')));
23-
}
25+
context.ubuntu = true;
26+
27+
return context.ui.listr([{
28+
title: 'Systemd',
29+
task: (ctx) => execa.shell('dpkg -l | grep systemd').then(() => {
30+
ctx.systemd = true;
31+
})
32+
}, {
33+
title: 'Nginx',
34+
task: (ctx) => execa.shell('dpkg -l | grep nginx').then(() => {
35+
ctx.nginx = true;
36+
})
37+
}], context, {concurrent: true, renderer: context.ui.verbose ? 'verbose' : 'silent', exitOnError: false})
38+
});
39+
}
2440

25-
context.ubuntu = true;
26-
27-
return new Listr([{
28-
title: 'Systemd',
29-
task: (ctx) => execa.shell('dpkg -l | grep systemd').then(() => {
30-
ctx.systemd = true;
31-
})
32-
}, {
33-
title: 'Nginx',
34-
task: (ctx) => execa.shell('dpkg -l | grep nginx').then(() => {
35-
ctx.nginx = true;
36-
})
37-
}], {concurrent: true, renderer: context.verbose ? context.renderer : 'silent', exitOnError: false})
38-
.run(context).catch(() => {
39-
let missing = [];
40-
41-
if (!context.systemd) {
42-
missing.push('systemd');
43-
}
44-
45-
if (!context.nginx) {
46-
missing.push('nginx');
47-
}
48-
49-
if (missing.length) {
50-
return Promise.reject(new errors.SystemError(chalk.yellow(`Missing package(s): ${missing.join(', ')}`)));
51-
}
52-
});
53-
}).catch((error) => {
41+
return promise.then(() => { return {continue: true}; }).catch((error) => {
5442
// If the error caught is not a SystemError, something went wrong with execa,
5543
// so throw a ProcessError instead
5644
if (!(error instanceof errors.SystemError)) {
57-
error = new errors.ProcessError(error);
45+
return Promise.reject(new errors.ProcessError(error));
46+
}
47+
48+
// This is a check so that when running as part of `ghost setup`, we can do things more cleanly
49+
// As part of `ghost doctor`, none of the below should run
50+
if (!context.setup) {
51+
return Promise.reject(error);
5852
}
5953

60-
return Promise.reject(error);
61-
});
54+
context.ui.log(
55+
`System Stack checks failed with message: '${error.message}'.${os.EOL}` +
56+
'Some features of Ghost-CLI may not work without additional configuration.',
57+
'yellow'
58+
);
59+
60+
return context.ui.prompt({
61+
type: 'confirm',
62+
name: 'continue',
63+
message: chalk.blue('Continue anyways?'),
64+
default: true
65+
});
66+
}).then(
67+
(answers) => answers.continue || Promise.reject(new errors.SystemError(
68+
`Setup was halted. Ghost is installed but not fully setup.${os.EOL}` +
69+
'Fix any errors shown and re-run `ghost setup`, or run `ghost setup --no-stack`.'
70+
))
71+
);
6272
}
6373
}];

lib/commands/doctor/index.js

+2-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
'use strict';
2-
const Listr = require('listr');
32
const Command = require('../../command');
43
const errors = require('../../errors');
54

@@ -19,9 +18,7 @@ class DoctorCommand extends Command {
1918
return Promise.reject(e);
2019
}
2120

22-
let tasks = new Listr(checks, {concurrent: true, renderer: this.renderer});
23-
24-
return tasks.run(this).then(() => {
21+
return this.ui.listr(checks, {ui: this.ui, system: this.system}, {concurrent: true}).then(() => {
2522
this.ui.success(`All ${category} checks passed`);
2623
}).catch((error) => {
2724
if (error instanceof errors.SystemError) {
@@ -36,7 +33,7 @@ class DoctorCommand extends Command {
3633
}
3734

3835
DoctorCommand.description = 'Check the system for any potential hiccups when installing/updating Ghost';
39-
DoctorCommand.params = '[name]';
36+
DoctorCommand.params = '[category]';
4037
DoctorCommand.global = true;
4138

4239
module.exports = DoctorCommand;

lib/commands/install.js

+13-14
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
const fs = require('fs-extra');
33
const path = require('path');
44
const every = require('lodash/every');
5-
const Listr = require('listr');
65
const Promise = require('bluebird');
76
const Command = require('../command');
87
const symlinkSync = require('symlink-or-copy').sync;
@@ -50,38 +49,38 @@ class InstallCommand extends Command {
5049
this.environment = 'development';
5150
}
5251

53-
return new Listr([{
52+
return this.ui.listr([{
5453
title: 'Checking for latest Ghost version',
5554
task: this.constructor.tasks.version
5655
}, {
5756
title: 'Running system checks',
58-
task: (ctx) => new Listr(installChecks, {concurrent: true, renderer: ctx.renderer})
57+
task: () => this.ui.listr(installChecks, false, {concurrent: true})
5958
}, {
6059
title: 'Setting up install directory',
6160
task: ensureStructure
6261
}, {
6362
title: 'Downloading and installing Ghost',
6463
task: (ctx, task) => {
6564
task.title = `Downloading and installing Ghost v${ctx.version}`;
66-
return yarnInstall(ctx.renderer);
65+
return yarnInstall(ctx.ui);
6766
}
6867
}, {
6968
title: 'Moving files',
70-
task: () => new Listr([{
69+
task: () => this.ui.listr([{
7170
title: 'Summoning Casper',
7271
task: this.constructor.tasks.casper
7372
}, {
7473
title: 'Linking things',
7574
task: this.constructor.tasks.link
76-
}], {concurrent: true})
77-
}], {renderer: this.renderer}).run({
75+
}], false, {concurrent: true})
76+
}], {
7877
version: version,
79-
cliVersion: this.cliVersion,
80-
renderer: this.renderer
78+
cliVersion: this.cliVersion
8179
}).then(() => {
82-
if (argv.noSetup) {
80+
if (!argv.setup) {
8381
return;
8482
}
83+
8584
argv.local = local;
8685

8786
let setup = new SetupCommand(this);
@@ -128,10 +127,10 @@ InstallCommand.options = {
128127
description: 'Folder to install Ghost in',
129128
type: 'string'
130129
},
131-
noSetup: {
132-
alias: 'N',
133-
description: 'Don\'t automatically run the setup command',
134-
type: 'boolean'
130+
setup: {
131+
description: 'Automatically run the setup command',
132+
type: 'boolean',
133+
default: true
135134
}
136135
};
137136

lib/commands/setup.js

+39-72
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
'use strict';
2-
const eol = require('os').EOL;
32
const path = require('path');
4-
const chalk = require('chalk');
5-
const Listr = require('listr');
63

74
const Config = require('../utils/config');
8-
const errors = require('../errors');
95
const setupChecks = require('./doctor/checks/setup');
106
const StartCommand = require('./start');
117
const ConfigCommand = require('./config');
@@ -19,105 +15,76 @@ class SetupCommand extends Command {
1915
}
2016

2117
run(argv) {
22-
let context = {
23-
renderer: this.renderer,
24-
verbose: this.verbose
25-
};
26-
2718
if (argv.local) {
2819
argv.url = argv.url || 'http://localhost:2368/';
2920
argv.pname = argv.pname || 'ghost-local';
3021
argv.process = 'local';
22+
argv.stack = false;
3123

3224
// If the user's already specified a db client, then we won't override it.
3325
if (!argv.db) {
3426
argv.db = argv.db || 'sqlite3';
3527
argv.dbpath = path.join(process.cwd(), 'content/data/ghost-local.db');
3628
}
3729

38-
context.start = true;
30+
argv.start = true;
3931

4032
// In the case that the user runs `ghost setup --local`, we want to make
4133
// sure we're set up in development mode
4234
this.development = true;
4335
process.env.NODE_ENV = this.environment = 'development';
44-
} else {
45-
context.start = argv.start || false;
4636
}
4737

48-
let configCommand = new ConfigCommand(this);
49-
return configCommand.run(argv).then((config) => {
50-
context.config = config;
51-
52-
if (!argv.local && argv.stack) {
53-
return new Listr(setupChecks, {concurrent: true, renderer: this.renderer}).run(context)
54-
.then((context) => {context.continue = true;})
55-
.catch((error) => {
56-
if (!(error instanceof errors.SystemError)) {
57-
return Promise.reject(error);
58-
}
59-
60-
this.ui.log(
61-
`System Stack checks failed with message: '${error.message}'.${eol}` +
62-
'Some features of Ghost-CLI may not work without additional configuration.',
63-
'yellow'
64-
);
65-
66-
return this.ui.prompt({
67-
type: 'confirm',
68-
name: 'continue',
69-
message: chalk.blue('Continue anyways?'),
70-
default: true
71-
}).then((answers) => {
72-
if (!answers.continue) {
73-
return Promise.reject(new Error(
74-
`Setup was halted. Ghost is installed but not fully setup.${eol}` +
75-
'Fix any errors shown and re-run `ghost setup`, or run `ghost setup --no-stack`.'
76-
));
77-
}
78-
});
79-
});
38+
return this.ui.listr([{
39+
title: 'Configuring Ghost',
40+
task: (ctx) => {
41+
let configCommand = new ConfigCommand(this);
42+
return configCommand.run(argv).then((config) => ctx.config = config);
8043
}
81-
}).then(() => {
82-
// De-duplicate process name before setting up the process manager
83-
dedupeProcessName(context.config);
84-
85-
this.service.setConfig(context.config);
86-
87-
return this.ui.run(this.service.callHook('setup', context), 'Finishing setup');
88-
}).then(() => {
89-
if (context.start) {
90-
return;
44+
}, {
45+
title: 'Running setup checks',
46+
skip: () => !argv.stack,
47+
task: () => this.ui.listr(setupChecks, false)
48+
}, {
49+
title: 'Finishing setup',
50+
task: (ctx) => {
51+
// De-duplicate process name before setting up the process manager
52+
dedupeProcessName(ctx.config);
53+
this.service.setConfig(ctx.config);
54+
return this.service.callHook('setup', ctx);
9155
}
92-
93-
return this.ui.prompt({
56+
}], {setup: true}).then((context) => {
57+
let promise = argv.start ? Promise.resolve({start: true}) : this.ui.prompt({
9458
type: 'confirm',
9559
name: 'start',
9660
message: 'Do you want to start Ghost?',
9761
default: true
9862
});
99-
}).then((answer) => {
100-
// Add config to system blog list
101-
let systemConfig = Config.load('system');
102-
let instances = systemConfig.get('instances', {});
103-
instances[context.config.get('pname')] = {
104-
cwd: process.cwd()
105-
};
106-
systemConfig.set('instances', instances).save();
10763

108-
if (context.start || answer.start) {
109-
let startCommand = new StartCommand(this);
110-
return startCommand.run(argv);
111-
}
64+
return promise.then((answer) => {
65+
// Add config to system blog list
66+
let systemConfig = Config.load('system');
67+
let instances = systemConfig.get('instances', {});
68+
instances[context.config.get('pname')] = {
69+
cwd: process.cwd()
70+
};
71+
systemConfig.set('instances', instances).save();
72+
73+
if (answer.start) {
74+
let startCommand = new StartCommand(this);
75+
return startCommand.run(argv);
76+
}
77+
});
11278
});
11379
}
11480
}
11581

11682
SetupCommand.description = 'Setup an installation of Ghost (after it is installed)';
11783
SetupCommand.options = {
118-
noStack: {
119-
description: 'Don\'t check the system stack on setup',
120-
type: 'boolean'
84+
stack: {
85+
description: 'Check the system stack on setup',
86+
type: 'boolean',
87+
default: true
12188
},
12289
local: {
12390
alias: 'l',
@@ -127,7 +94,7 @@ SetupCommand.options = {
12794
start: {
12895
name: 'start',
12996
description: 'Automatically start Ghost without prompting',
130-
flag: true
97+
type: 'boolean'
13198
}
13299
};
133100

0 commit comments

Comments
 (0)