clever-tools 3.2.0 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/bin/clever.js +15 -5
- package/package.json +3 -3
- package/src/commands/addon.js +47 -21
- package/src/commands/create.js +17 -3
- package/src/commands/deploy.js +20 -3
- package/src/commands/logs.js +16 -7
- package/src/logger.js +5 -0
- package/src/models/application.js +1 -1
- package/src/models/git.js +14 -1
- package/src/models/log-v4.js +53 -3
package/README.md
CHANGED
|
@@ -88,6 +88,7 @@ Where `type` is one of:
|
|
|
88
88
|
Where region is one of:
|
|
89
89
|
|
|
90
90
|
- `par` (Paris, [Clever Cloud](https://www.clever-cloud.com/infrastructure/))
|
|
91
|
+
- `grahds` (Gravelines, HDS servers, OVHcloud)
|
|
91
92
|
- `rbx` (Roubaix, OVHcloud)
|
|
92
93
|
- `rbxhds` (Roubaix, HDS servers, OVHcloud)
|
|
93
94
|
- `scw` (Paris, [Scaleway DC5](https://www.clever-cloud.com/blog/press/2023/01/17/clever-cloud-and-scaleway-join-forces-to-unveil-a-sovereign-european-paas-offering/))
|
package/bin/clever.js
CHANGED
|
@@ -90,7 +90,10 @@ function run () {
|
|
|
90
90
|
description: 'Application ID (or name, if unambiguous)',
|
|
91
91
|
parser: Parsers.appIdOrName,
|
|
92
92
|
}),
|
|
93
|
-
appNameCreation: cliparse.argument('app-name', {
|
|
93
|
+
appNameCreation: cliparse.argument('app-name', {
|
|
94
|
+
description: 'Application name (optional, current directory name is used if not specified)',
|
|
95
|
+
default: '',
|
|
96
|
+
}),
|
|
94
97
|
backupId: cliparse.argument('backup-id', { description: 'A Database backup ID (format: UUID)' }),
|
|
95
98
|
databaseId: cliparse.argument('database-id', { description: 'Any database ID (format: addon_UUID, postgresql_UUID, mysql_UUID, ...)' }),
|
|
96
99
|
drainId: cliparse.argument('drain-id', { description: 'Drain ID' }),
|
|
@@ -132,6 +135,7 @@ function run () {
|
|
|
132
135
|
sourceableEnvVarsList: cliparse.flag('add-export', { description: 'Display sourceable env variables setting' }),
|
|
133
136
|
accesslogsFormat: getOutputFormatOption(['simple', 'extended', 'clf']),
|
|
134
137
|
addonEnvFormat: getOutputFormatOption(['shell']),
|
|
138
|
+
logsFormat: getOutputFormatOption(['json-stream']),
|
|
135
139
|
accesslogsFollow: cliparse.flag('follow', {
|
|
136
140
|
aliases: ['f'],
|
|
137
141
|
description: 'Display access logs continuously (ignores before/until, after/since)',
|
|
@@ -180,6 +184,12 @@ function run () {
|
|
|
180
184
|
metavar: 'commit id',
|
|
181
185
|
description: 'Restart the application with a specific commit ID',
|
|
182
186
|
}),
|
|
187
|
+
gitTag: cliparse.option('tag', {
|
|
188
|
+
aliases: ['t'],
|
|
189
|
+
default: '',
|
|
190
|
+
metavar: 'tag',
|
|
191
|
+
description: 'Tag to push (none by default)',
|
|
192
|
+
}),
|
|
183
193
|
databaseId: cliparse.option('database-id', {
|
|
184
194
|
metavar: 'database_id',
|
|
185
195
|
description: 'The Database ID (e.g.: postgresql_xxx)',
|
|
@@ -345,7 +355,7 @@ function run () {
|
|
|
345
355
|
parser: Parsers.commaSeparated,
|
|
346
356
|
}),
|
|
347
357
|
showAllActivity: cliparse.flag('show-all', { description: 'Show all activity' }),
|
|
348
|
-
showAll: cliparse.flag('show-all', { description: 'Show all available
|
|
358
|
+
showAll: cliparse.flag('show-all', { description: 'Show all available add-ons and applications' }),
|
|
349
359
|
loginToken: cliparse.option('token', {
|
|
350
360
|
metavar: 'token',
|
|
351
361
|
description: 'Directly give an existing token',
|
|
@@ -559,7 +569,7 @@ function run () {
|
|
|
559
569
|
const addonCreateCommand = cliparse.command('create', {
|
|
560
570
|
description: 'Create an addon',
|
|
561
571
|
args: [args.addonProvider, args.addonName],
|
|
562
|
-
options: [opts.linkAddon, opts.confirmAddonCreation, opts.addonPlan, opts.addonRegion, opts.addonVersion, opts.addonOptions],
|
|
572
|
+
options: [opts.linkAddon, opts.confirmAddonCreation, opts.addonPlan, opts.addonRegion, opts.addonVersion, opts.addonOptions, opts.humanJsonOutputFormat],
|
|
563
573
|
}, addon('create'));
|
|
564
574
|
const addonDeleteCommand = cliparse.command('delete', {
|
|
565
575
|
description: 'Delete an addon',
|
|
@@ -643,7 +653,7 @@ function run () {
|
|
|
643
653
|
const deploy = lazyRequirePromiseModule('../src/commands/deploy.js');
|
|
644
654
|
const deployCommand = cliparse.command('deploy', {
|
|
645
655
|
description: 'Deploy an application',
|
|
646
|
-
options: [opts.alias, opts.branch, opts.quiet, opts.forceDeploy, opts.followDeployLogs, opts.sameCommitPolicy],
|
|
656
|
+
options: [opts.alias, opts.branch, opts.gitTag, opts.quiet, opts.forceDeploy, opts.followDeployLogs, opts.sameCommitPolicy],
|
|
647
657
|
}, deploy('deploy'));
|
|
648
658
|
|
|
649
659
|
// DIAG COMMAND
|
|
@@ -754,7 +764,7 @@ function run () {
|
|
|
754
764
|
const logs = lazyRequirePromiseModule('../src/commands/logs.js');
|
|
755
765
|
const logsCommand = cliparse.command('logs', {
|
|
756
766
|
description: 'Fetch application logs, continuously',
|
|
757
|
-
options: [opts.alias, opts.before, opts.after, opts.search, opts.deploymentId, opts.addonId],
|
|
767
|
+
options: [opts.alias, opts.before, opts.after, opts.search, opts.deploymentId, opts.addonId, opts.logsFormat],
|
|
758
768
|
}, logs('appLogs'));
|
|
759
769
|
|
|
760
770
|
// MAKE DEFAULT COMMAND
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clever-tools",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0",
|
|
4
4
|
"description": "Command Line Interface for Clever Cloud.",
|
|
5
5
|
"main": "bin/clever.js",
|
|
6
6
|
"keywords": [
|
|
@@ -25,14 +25,14 @@
|
|
|
25
25
|
"scripts/*.sh"
|
|
26
26
|
],
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@clevercloud/client": "^8.1.
|
|
28
|
+
"@clevercloud/client": "^8.1.2",
|
|
29
29
|
"clf-date": "^0.2.0",
|
|
30
30
|
"cliparse": "^0.3.3",
|
|
31
31
|
"colors": "1.4.0",
|
|
32
32
|
"common-env": "^6.4.0",
|
|
33
33
|
"curlconverter": "^3.21.0",
|
|
34
34
|
"eventsource": "^1.1.0",
|
|
35
|
-
"isomorphic-git": "^1.25.
|
|
35
|
+
"isomorphic-git": "^1.25.3",
|
|
36
36
|
"linux-release-info": "^3.0.0",
|
|
37
37
|
"lodash": "^4.17.21",
|
|
38
38
|
"mkdirp": "^1.0.4",
|
package/src/commands/addon.js
CHANGED
|
@@ -34,7 +34,14 @@ async function list (params) {
|
|
|
34
34
|
|
|
35
35
|
async function create (params) {
|
|
36
36
|
const [providerName, name] = params.args;
|
|
37
|
-
const {
|
|
37
|
+
const {
|
|
38
|
+
link: linkedAppAlias,
|
|
39
|
+
plan: planName,
|
|
40
|
+
region,
|
|
41
|
+
yes: skipConfirmation,
|
|
42
|
+
org: orgaIdOrName,
|
|
43
|
+
format,
|
|
44
|
+
} = params.options;
|
|
38
45
|
const version = params.options['addon-version'];
|
|
39
46
|
const addonOptions = parseAddonOptions(params.options.option);
|
|
40
47
|
|
|
@@ -42,36 +49,55 @@ async function create (params) {
|
|
|
42
49
|
? await Organisation.getId(orgaIdOrName)
|
|
43
50
|
: await User.getCurrentId();
|
|
44
51
|
|
|
52
|
+
const addonToCreate = {
|
|
53
|
+
ownerId,
|
|
54
|
+
name,
|
|
55
|
+
providerName,
|
|
56
|
+
planName,
|
|
57
|
+
region,
|
|
58
|
+
skipConfirmation,
|
|
59
|
+
version,
|
|
60
|
+
addonOptions,
|
|
61
|
+
};
|
|
62
|
+
|
|
45
63
|
if (linkedAppAlias != null) {
|
|
46
64
|
const linkedAppData = await AppConfig.getAppDetails({ alias: linkedAppAlias });
|
|
47
|
-
if (orgaIdOrName != null && linkedAppData.ownerId !== ownerId) {
|
|
65
|
+
if (orgaIdOrName != null && linkedAppData.ownerId !== ownerId && format === 'human') {
|
|
48
66
|
Logger.warn('The specified application does not belong to the specified organisation. Ignoring the `--org` option');
|
|
49
67
|
}
|
|
50
68
|
const newAddon = await Addon.create({
|
|
69
|
+
...addonToCreate,
|
|
51
70
|
ownerId: linkedAppData.ownerId,
|
|
52
|
-
name,
|
|
53
|
-
providerName,
|
|
54
|
-
planName,
|
|
55
|
-
region,
|
|
56
|
-
skipConfirmation,
|
|
57
|
-
version,
|
|
58
|
-
addonOptions,
|
|
59
71
|
});
|
|
60
72
|
await Addon.link(linkedAppData.ownerId, linkedAppData.appId, { addon_id: newAddon.id });
|
|
61
|
-
|
|
73
|
+
displayAddon(format, newAddon, `Add-on created and linked to application ${linkedAppAlias} successfully!`);
|
|
62
74
|
}
|
|
63
75
|
else {
|
|
64
|
-
const newAddon = await Addon.create(
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
76
|
+
const newAddon = await Addon.create(addonToCreate);
|
|
77
|
+
displayAddon(format, newAddon, 'Add-on created successfully!');
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function displayAddon (format, addon, message) {
|
|
82
|
+
switch (format) {
|
|
83
|
+
|
|
84
|
+
case 'json': {
|
|
85
|
+
Logger.printJson({
|
|
86
|
+
id: addon.id,
|
|
87
|
+
realId: addon.realId,
|
|
88
|
+
name: addon.name,
|
|
89
|
+
});
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
case 'human':
|
|
94
|
+
default:
|
|
95
|
+
Logger.println([
|
|
96
|
+
message,
|
|
97
|
+
`ID: ${addon.id}`,
|
|
98
|
+
`Real ID: ${addon.realId}`,
|
|
99
|
+
`Name: ${addon.name}`,
|
|
100
|
+
].join('\n'));
|
|
75
101
|
}
|
|
76
102
|
}
|
|
77
103
|
|
package/src/commands/create.js
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const path = require('path');
|
|
3
4
|
const Application = require('../models/application.js');
|
|
4
5
|
const AppConfig = require('../models/app_configuration.js');
|
|
5
6
|
const Logger = require('../logger.js');
|
|
6
7
|
|
|
7
8
|
async function create (params) {
|
|
8
9
|
const { type: typeName } = params.options;
|
|
9
|
-
const [
|
|
10
|
+
const [rawName] = params.args;
|
|
10
11
|
const { org: orgaIdOrName, alias, region, github: githubOwnerRepo, format } = params.options;
|
|
11
12
|
const { apps } = await AppConfig.loadApplicationConf();
|
|
12
13
|
|
|
14
|
+
// Application name is optionnal, use current directory name if not specified (empty string)
|
|
15
|
+
const name = (rawName !== '') ? rawName : getCurrentDirectoryName();
|
|
16
|
+
|
|
13
17
|
AppConfig.checkAlreadyLinked(apps, name, alias);
|
|
14
18
|
|
|
15
19
|
const github = getGithubDetails(githubOwnerRepo);
|
|
@@ -19,13 +23,19 @@ async function create (params) {
|
|
|
19
23
|
switch (format) {
|
|
20
24
|
|
|
21
25
|
case 'json': {
|
|
22
|
-
|
|
26
|
+
Logger.printJson({
|
|
27
|
+
id: app.id,
|
|
28
|
+
name: app.name,
|
|
29
|
+
deployUrl: app.deployUrl,
|
|
30
|
+
});
|
|
23
31
|
break;
|
|
24
32
|
}
|
|
25
33
|
|
|
26
34
|
case 'human':
|
|
27
35
|
default:
|
|
28
|
-
Logger.println('
|
|
36
|
+
Logger.println('Application created successfully!');
|
|
37
|
+
Logger.println(`ID: ${app.id}`);
|
|
38
|
+
Logger.println(`Name: ${name}`);
|
|
29
39
|
}
|
|
30
40
|
};
|
|
31
41
|
|
|
@@ -36,4 +46,8 @@ function getGithubDetails (githubOwnerRepo) {
|
|
|
36
46
|
}
|
|
37
47
|
}
|
|
38
48
|
|
|
49
|
+
function getCurrentDirectoryName () {
|
|
50
|
+
return path.basename(process.cwd());
|
|
51
|
+
}
|
|
52
|
+
|
|
39
53
|
module.exports = { create };
|
package/src/commands/deploy.js
CHANGED
|
@@ -13,12 +13,12 @@ const { sendToApi } = require('../models/send-to-api.js');
|
|
|
13
13
|
// Once the API call to redeploy() has been triggered successfully,
|
|
14
14
|
// the rest (waiting for deployment state to evolve and displaying logs) is done with auto retry (resilient to network failures)
|
|
15
15
|
async function deploy (params) {
|
|
16
|
-
const { alias, branch: branchName, quiet, force, follow, 'same-commit-policy': sameCommitPolicy } = params.options;
|
|
16
|
+
const { alias, branch: branchName, tag: tagName, quiet, force, follow, 'same-commit-policy': sameCommitPolicy } = params.options;
|
|
17
17
|
|
|
18
18
|
const appData = await AppConfig.getAppDetails({ alias });
|
|
19
19
|
const { ownerId, appId } = appData;
|
|
20
|
-
const branchRefspec = await git.getFullBranch(branchName);
|
|
21
20
|
|
|
21
|
+
const branchRefspec = await getBranchToDeploy(branchName, tagName);
|
|
22
22
|
const commitIdToPush = await git.getBranchCommit(branchRefspec);
|
|
23
23
|
const remoteHeadCommitId = await git.getRemoteCommit(appData.deployUrl);
|
|
24
24
|
const deployedCommitId = await Application.get(ownerId, appId)
|
|
@@ -66,7 +66,8 @@ async function deploy (params) {
|
|
|
66
66
|
const knownDeployments = await getAllDeployments({ id: ownerId, appId, limit: 5 }).then(sendToApi);
|
|
67
67
|
|
|
68
68
|
Logger.println('Pushing source code to Clever Cloud…');
|
|
69
|
-
|
|
69
|
+
|
|
70
|
+
await git.push(appData.deployUrl, commitIdToPush, force)
|
|
70
71
|
.catch(async (e) => {
|
|
71
72
|
const isShallow = await git.isShallow();
|
|
72
73
|
if (isShallow) {
|
|
@@ -86,4 +87,20 @@ async function restartOnSameCommit (ownerId, appId, commitIdToPush, quiet, follo
|
|
|
86
87
|
return Log.watchDeploymentAndDisplayLogs({ ownerId, appId, deploymentId: restart.deploymentId, quiet, follow });
|
|
87
88
|
}
|
|
88
89
|
|
|
90
|
+
async function getBranchToDeploy (branchName, tagName) {
|
|
91
|
+
if (tagName) {
|
|
92
|
+
const useTag = await git.isExistingTag(tagName);
|
|
93
|
+
if (useTag) {
|
|
94
|
+
const tagRefspec = await git.getFullBranch(tagName);
|
|
95
|
+
return tagRefspec;
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
throw new Error(`Tag ${tagName} doesn't exist locally`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
return await git.getFullBranch(branchName);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
89
106
|
module.exports = { deploy };
|
package/src/commands/logs.js
CHANGED
|
@@ -5,26 +5,35 @@ const LogV2 = require('../models/log.js');
|
|
|
5
5
|
const Log = require('../models/log-v4.js');
|
|
6
6
|
const Logger = require('../logger.js');
|
|
7
7
|
const { Deferred } = require('../models/utils.js');
|
|
8
|
+
const colors = require('colors/safe');
|
|
8
9
|
|
|
9
10
|
async function appLogs (params) {
|
|
10
|
-
const { alias, after: since, before: until, search, 'deployment-id': deploymentId } = params.options;
|
|
11
|
+
const { alias, addon: addonId, after: since, before: until, search, 'deployment-id': deploymentId, format } = params.options;
|
|
11
12
|
|
|
12
13
|
// ignore --search ""
|
|
13
14
|
const filter = (search !== '') ? search : null;
|
|
14
15
|
|
|
15
16
|
const { appId, ownerId } = await AppConfig.getAppDetails({ alias });
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
const isForHuman = (format === 'human');
|
|
18
19
|
|
|
19
20
|
// TODO: drop when addons are migrated to the v4 API
|
|
20
|
-
if (
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
if (addonId) {
|
|
22
|
+
if (isForHuman) {
|
|
23
|
+
Logger.println(colors.blue('Waiting for addon logs…'));
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
throw new Error(`"${format}" format is not yet available for add-on logs`);
|
|
27
|
+
}
|
|
28
|
+
return LogV2.displayLogs({ appAddonId: addonId, since, until, filter, deploymentId });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (isForHuman) {
|
|
32
|
+
Logger.println(colors.blue('Waiting for application logs…'));
|
|
24
33
|
}
|
|
25
34
|
|
|
26
35
|
const deferred = new Deferred();
|
|
27
|
-
await Log.displayLogs({ ownerId, appId, since, until, filter, deploymentId, deferred });
|
|
36
|
+
await Log.displayLogs({ ownerId, appId, since, until, filter, deploymentId, format, deferred });
|
|
28
37
|
return deferred.promise;
|
|
29
38
|
}
|
|
30
39
|
|
package/src/logger.js
CHANGED
|
@@ -52,6 +52,11 @@ const Logger = _(['debug', 'info', 'warn', 'error'])
|
|
|
52
52
|
// No decoration for Logger.println
|
|
53
53
|
Logger.println = console.log;
|
|
54
54
|
|
|
55
|
+
// No decoration for Logger.println
|
|
56
|
+
Logger.printJson = (obj) => {
|
|
57
|
+
console.log(JSON.stringify(obj, null, 2));
|
|
58
|
+
};
|
|
59
|
+
|
|
55
60
|
// No decoration for Logger.printErrorLine
|
|
56
61
|
Logger.printErrorLine = console.error;
|
|
57
62
|
|
|
@@ -17,7 +17,7 @@ function listAvailableTypes () {
|
|
|
17
17
|
return autocomplete.words(['docker', 'elixir', 'go', 'gradle', 'haskell', 'jar', 'maven', 'meteor', 'node', 'php', 'play1', 'play2', 'python', 'ruby', 'rust', 'sbt', 'static-apache', 'war']);
|
|
18
18
|
};
|
|
19
19
|
|
|
20
|
-
const AVAILABLE_ZONES = ['par', 'rbx', 'rbxhds', 'scw', 'jed', 'mtl', 'sgp', 'syd', 'wsw'];
|
|
20
|
+
const AVAILABLE_ZONES = ['par', 'grahds', 'rbx', 'rbxhds', 'scw', 'jed', 'mtl', 'sgp', 'syd', 'wsw'];
|
|
21
21
|
|
|
22
22
|
function listAvailableZones () {
|
|
23
23
|
return autocomplete.words(AVAILABLE_ZONES);
|
package/src/models/git.js
CHANGED
|
@@ -78,7 +78,19 @@ async function getFullBranch (branchName) {
|
|
|
78
78
|
|
|
79
79
|
async function getBranchCommit (refspec) {
|
|
80
80
|
const repo = await getRepo();
|
|
81
|
-
|
|
81
|
+
const oid = await git.resolveRef({ ...repo, ref: refspec });
|
|
82
|
+
// When a refspec refers to an annotated tag, the OID ref represents the annotation and not the commit directly,
|
|
83
|
+
// that's why we need a call to `readCommit`.
|
|
84
|
+
const res = await git.readCommit({ ...repo, ref: refspec, oid });
|
|
85
|
+
return res.oid;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function isExistingTag (tag) {
|
|
89
|
+
const repo = await getRepo();
|
|
90
|
+
const tags = await git.listTags({
|
|
91
|
+
...repo,
|
|
92
|
+
});
|
|
93
|
+
return tags.includes(tag);
|
|
82
94
|
}
|
|
83
95
|
|
|
84
96
|
async function push (remoteUrl, branchRefspec, force) {
|
|
@@ -131,4 +143,5 @@ module.exports = {
|
|
|
131
143
|
push,
|
|
132
144
|
completeBranches,
|
|
133
145
|
isShallow,
|
|
146
|
+
isExistingTag,
|
|
134
147
|
};
|
package/src/models/log-v4.js
CHANGED
|
@@ -18,7 +18,11 @@ async function displayLogs (params) {
|
|
|
18
18
|
|
|
19
19
|
const deferred = params.deferred || new Deferred();
|
|
20
20
|
const { apiHost, tokens } = await getHostAndTokens();
|
|
21
|
-
const { ownerId, appId, filter, since, until, deploymentId } = params;
|
|
21
|
+
const { ownerId, appId, filter, since, until, deploymentId, format } = params;
|
|
22
|
+
|
|
23
|
+
if (format === 'json' && until == null) {
|
|
24
|
+
throw new Error('"json" format is only applicable with a limiting parameter such as `--until`');
|
|
25
|
+
}
|
|
22
26
|
|
|
23
27
|
const logStream = new ApplicationLogStream({
|
|
24
28
|
apiHost,
|
|
@@ -37,21 +41,40 @@ async function displayLogs (params) {
|
|
|
37
41
|
|
|
38
42
|
// Properly close the stream
|
|
39
43
|
process.once('SIGINT', (signal) => logStream.close(signal));
|
|
44
|
+
const jsonArray = new JsonArray();
|
|
40
45
|
|
|
41
46
|
logStream
|
|
42
47
|
.on('open', (event) => {
|
|
43
48
|
Logger.debug(colors.blue(`Logs stream (open) ${JSON.stringify({ appId, filter, deploymentId })}`));
|
|
49
|
+
if (format === 'json') {
|
|
50
|
+
jsonArray.open();
|
|
51
|
+
}
|
|
44
52
|
})
|
|
45
53
|
.on('error', (event) => {
|
|
46
54
|
Logger.debug(colors.red(`Logs stream (error) ${event.error.message}`));
|
|
47
55
|
})
|
|
48
56
|
.onLog((log) => {
|
|
49
|
-
|
|
57
|
+
switch (format) {
|
|
58
|
+
case 'json':
|
|
59
|
+
jsonArray.push(log);
|
|
60
|
+
return;
|
|
61
|
+
case 'json-stream':
|
|
62
|
+
Logger.printJson(log);
|
|
63
|
+
return;
|
|
64
|
+
case 'human':
|
|
65
|
+
default:
|
|
66
|
+
Logger.println(formatLogLine(log));
|
|
67
|
+
}
|
|
50
68
|
});
|
|
51
69
|
|
|
52
70
|
// start() is blocking until end of stream
|
|
53
71
|
logStream.start()
|
|
54
|
-
.then((reason) =>
|
|
72
|
+
.then((reason) => {
|
|
73
|
+
if (format === 'json') {
|
|
74
|
+
jsonArray.close();
|
|
75
|
+
}
|
|
76
|
+
return deferred.resolve();
|
|
77
|
+
})
|
|
55
78
|
.catch(processError)
|
|
56
79
|
.catch((error) => deferred.reject(error));
|
|
57
80
|
|
|
@@ -142,3 +165,30 @@ function isBuildSucessMessage (log) {
|
|
|
142
165
|
};
|
|
143
166
|
|
|
144
167
|
module.exports = { displayLogs, watchDeploymentAndDisplayLogs };
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Helper to print a real JSON array with starting `[` and ending `]`
|
|
171
|
+
*/
|
|
172
|
+
class JsonArray {
|
|
173
|
+
constructor () {
|
|
174
|
+
this._isFirst = true;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
open () {
|
|
178
|
+
process.stdout.write('[\n');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
push (log) {
|
|
182
|
+
if (this._isFirst) {
|
|
183
|
+
this._isFirst = false;
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
process.stdout.write(',\n');
|
|
187
|
+
}
|
|
188
|
+
process.stdout.write(` ${JSON.stringify(log)}`);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
close () {
|
|
192
|
+
process.stdout.write('\n]');
|
|
193
|
+
}
|
|
194
|
+
}
|