clever-tools 3.7.0 → 3.8.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 -1
- package/bin/clever.js +99 -47
- package/package.json +47 -47
- package/src/command-options.js +19 -0
- package/src/commands/accesslogs.js +114 -34
- package/src/commands/activity.js +64 -12
- package/src/commands/addon.js +152 -57
- package/src/commands/applications.js +83 -1
- package/src/commands/cancel-deploy.js +3 -3
- package/src/commands/config.js +6 -7
- package/src/commands/console.js +16 -4
- package/src/commands/delete.js +16 -6
- package/src/commands/deploy.js +9 -6
- package/src/commands/diag.js +68 -28
- package/src/commands/domain.js +13 -13
- package/src/commands/drain.js +42 -27
- package/src/commands/env.js +53 -24
- package/src/commands/logs.js +3 -3
- package/src/commands/notify-email.js +33 -18
- package/src/commands/open.js +3 -3
- package/src/commands/profile.js +29 -9
- package/src/commands/published-config.js +26 -15
- package/src/commands/restart.js +12 -5
- package/src/commands/scale.js +2 -3
- package/src/commands/service.js +42 -15
- package/src/commands/ssh.js +3 -3
- package/src/commands/status.js +72 -46
- package/src/commands/stop.js +3 -3
- package/src/commands/tcp-redirs.js +54 -18
- package/src/commands/webhooks.js +28 -13
- package/src/logger.js +9 -6
- package/src/models/app_configuration.js +11 -3
- package/src/models/application.js +106 -7
- package/src/models/exit-strategy-option.js +24 -0
- package/src/models/json-array.js +28 -0
- package/src/models/log-v4.js +9 -29
- package/src/models/log.js +9 -2
- package/src/models/namespaces.js +20 -0
- package/src/models/organisation.js +0 -20
- package/src/models/utils.js +8 -1
- package/src/models/accesslogs.js +0 -54
|
@@ -2,32 +2,68 @@
|
|
|
2
2
|
|
|
3
3
|
const colors = require('colors/safe');
|
|
4
4
|
|
|
5
|
-
const
|
|
6
|
-
const Organisation = require('../models/organisation.js');
|
|
5
|
+
const Namespaces = require('../models/namespaces.js');
|
|
7
6
|
const { sendToApi } = require('../models/send-to-api.js');
|
|
8
7
|
const Interact = require('../models/interact.js');
|
|
9
8
|
const Logger = require('../logger.js');
|
|
10
9
|
const application = require('@clevercloud/client/cjs/api/v2/application.js');
|
|
10
|
+
const Application = require('../models/application.js');
|
|
11
11
|
|
|
12
12
|
async function listNamespaces (params) {
|
|
13
|
-
const
|
|
13
|
+
const { alias, app: appIdOrName, format } = params.options;
|
|
14
|
+
const { ownerId } = await Application.resolveId(appIdOrName, alias);
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
const namespaces = await Namespaces.getNamespaces(ownerId);
|
|
17
|
+
|
|
18
|
+
namespaces.sort((a, b) => a.namespace.localeCompare(b.namespace));
|
|
19
|
+
|
|
20
|
+
switch (format) {
|
|
21
|
+
case 'json': {
|
|
22
|
+
Logger.printJson(namespaces);
|
|
23
|
+
break;
|
|
24
|
+
}
|
|
25
|
+
case 'human':
|
|
26
|
+
default: {
|
|
27
|
+
Logger.println('Available namespaces:');
|
|
28
|
+
namespaces.forEach(({ namespace }) => {
|
|
29
|
+
switch (namespace) {
|
|
30
|
+
case 'cleverapps':
|
|
31
|
+
Logger.println(`- ${namespace}: for redirections used with 'cleverapps.io' domain`);
|
|
32
|
+
break;
|
|
33
|
+
case 'default':
|
|
34
|
+
Logger.println(`- ${namespace}: for redirections used with custom domains`);
|
|
35
|
+
break;
|
|
36
|
+
default:
|
|
37
|
+
Logger.println(`- ${namespace}`);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
17
44
|
|
|
18
45
|
async function list (params) {
|
|
19
|
-
const { alias } = params.options;
|
|
20
|
-
const { ownerId, appId } = await
|
|
46
|
+
const { alias, app: appIdOrName, format } = params.options;
|
|
47
|
+
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
21
48
|
|
|
22
49
|
const redirs = await application.getTcpRedirs({ id: ownerId, appId }).then(sendToApi);
|
|
23
50
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
51
|
+
switch (format) {
|
|
52
|
+
case 'json': {
|
|
53
|
+
Logger.printJson(redirs);
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
case 'human':
|
|
57
|
+
default: {
|
|
58
|
+
if (redirs.length === 0) {
|
|
59
|
+
Logger.println('No active TCP redirection for this application');
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
Logger.println('Enabled TCP redirections:');
|
|
63
|
+
for (const { namespace, port } of redirs) {
|
|
64
|
+
Logger.println(port + ' on ' + namespace);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
31
67
|
}
|
|
32
68
|
}
|
|
33
69
|
}
|
|
@@ -46,8 +82,8 @@ async function acceptPayment (result, skipConfirmation) {
|
|
|
46
82
|
}
|
|
47
83
|
|
|
48
84
|
async function add (params) {
|
|
49
|
-
const { alias, namespace, yes: skipConfirmation } = params.options;
|
|
50
|
-
const { ownerId, appId } = await
|
|
85
|
+
const { alias, app: appIdOrName, namespace, yes: skipConfirmation } = params.options;
|
|
86
|
+
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
51
87
|
|
|
52
88
|
const { port } = await application.addTcpRedir({ id: ownerId, appId }, { namespace }).then(sendToApi).catch((error) => {
|
|
53
89
|
if (error.status === 402) {
|
|
@@ -65,8 +101,8 @@ async function add (params) {
|
|
|
65
101
|
|
|
66
102
|
async function remove (params) {
|
|
67
103
|
const [port] = params.args;
|
|
68
|
-
const { alias, namespace } = params.options;
|
|
69
|
-
const { ownerId, appId } = await
|
|
104
|
+
const { alias, app: appIdOrName, namespace } = params.options;
|
|
105
|
+
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
70
106
|
|
|
71
107
|
await application.removeTcpRedir({ id: ownerId, appId, sourcePort: port, namespace }).then(sendToApi);
|
|
72
108
|
|
package/src/commands/webhooks.js
CHANGED
|
@@ -8,29 +8,44 @@ const { getOwnerAndApp, getOrgaIdOrUserId } = require('../models/notification.js
|
|
|
8
8
|
const { getWebhooks, createWebhook, deleteWebhook } = require('@clevercloud/client/cjs/api/v2/notification.js');
|
|
9
9
|
const { sendToApi } = require('../models/send-to-api.js');
|
|
10
10
|
|
|
11
|
-
function displayWebhook (hook) {
|
|
12
|
-
Logger.println((hook.name && colors.bold(hook.name)) || hook.id);
|
|
13
|
-
Logger.println(` id: ${hook.id}`);
|
|
14
|
-
Logger.println(` services: ${(hook.scope && hook.scope.join(', ')) || hook.ownerId}`);
|
|
15
|
-
Logger.println(` events: ${(hook.events && hook.events.join(', ')) || colors.bold('ALL')}`);
|
|
16
|
-
Logger.println(' hooks:');
|
|
17
|
-
hook.urls.forEach((url) => Logger.println(` ${url.url} (${url.format})`));
|
|
18
|
-
Logger.println();
|
|
19
|
-
}
|
|
20
|
-
|
|
21
11
|
async function list (params) {
|
|
22
|
-
const { org, 'list-all': listAll } = params.options;
|
|
12
|
+
const { org, 'list-all': listAll, format } = params.options;
|
|
23
13
|
|
|
24
14
|
// TODO: fix alias option
|
|
25
15
|
const { ownerId, appId } = await getOwnerAndApp(null, org, !listAll);
|
|
26
16
|
const hooks = await getWebhooks({ ownerId }).then(sendToApi);
|
|
27
17
|
|
|
28
|
-
hooks
|
|
18
|
+
const formattedHooks = hooks
|
|
29
19
|
.filter((hook) => {
|
|
30
20
|
const emptyScope = !hook.scope || hook.scope.length === 0;
|
|
31
21
|
return !appId || emptyScope || hook.scope.includes(appId);
|
|
32
22
|
})
|
|
33
|
-
.
|
|
23
|
+
.map((hook) => ({
|
|
24
|
+
id: hook.id,
|
|
25
|
+
name: hook.name,
|
|
26
|
+
ownerId: hook.ownerId,
|
|
27
|
+
services: hook.scope ?? [hook.ownerId],
|
|
28
|
+
events: hook.events ?? ['ALL'],
|
|
29
|
+
urls: hook.urls,
|
|
30
|
+
}));
|
|
31
|
+
|
|
32
|
+
switch (format) {
|
|
33
|
+
case 'json': {
|
|
34
|
+
Logger.printJson(formattedHooks);
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
case 'human':
|
|
38
|
+
default: {
|
|
39
|
+
formattedHooks.forEach((hook) => {
|
|
40
|
+
Logger.println(hook.name ? colors.bold(hook.name) : hook.id);
|
|
41
|
+
Logger.println(` id: ${hook.id}`);
|
|
42
|
+
Logger.println(` services: ${hook.services.join(', ')}`);
|
|
43
|
+
Logger.println(` events: ${hook.events.join(', ')}`);
|
|
44
|
+
Logger.println(' hooks:');
|
|
45
|
+
hook.urls.forEach((url) => Logger.println(` ${url.url} (${url.format})`));
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
34
49
|
}
|
|
35
50
|
|
|
36
51
|
async function add (params) {
|
package/src/logger.js
CHANGED
|
@@ -28,20 +28,24 @@ function formatLines (prefixLength, lines) {
|
|
|
28
28
|
.join('\n');
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
function consoleErrorWithoutColor (line) {
|
|
32
|
+
process.stderr.write(line + '\n');
|
|
33
|
+
}
|
|
34
|
+
|
|
31
35
|
const Logger = _(['debug', 'info', 'warn', 'error'])
|
|
32
36
|
.map((severity) => {
|
|
33
37
|
if (process.env.CLEVER_QUIET || (!process.env.CLEVER_VERBOSE && (severity === 'debug' || severity === 'info'))) {
|
|
34
38
|
return [severity, _.noop];
|
|
35
39
|
}
|
|
36
|
-
const consoleFn = (severity === 'error') ?
|
|
40
|
+
const consoleFn = (severity === 'error') ? consoleErrorWithoutColor : console.log;
|
|
37
41
|
const { prefix, prefixLength } = getPrefix(severity);
|
|
38
42
|
return [severity, (err) => {
|
|
39
43
|
const message = _.get(err, 'message', err);
|
|
40
44
|
const formattedMsg = formatLines(prefixLength, processApiError(message));
|
|
41
45
|
if (process.env.CLEVER_VERBOSE && severity === 'error') {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
consoleErrorWithoutColor('[STACKTRACE]');
|
|
47
|
+
consoleErrorWithoutColor(err);
|
|
48
|
+
consoleErrorWithoutColor('[/STACKTRACE]');
|
|
45
49
|
}
|
|
46
50
|
return consoleFn(`${prefix}${formattedMsg}`);
|
|
47
51
|
}];
|
|
@@ -57,8 +61,7 @@ Logger.printJson = (obj) => {
|
|
|
57
61
|
console.log(JSON.stringify(obj, null, 2));
|
|
58
62
|
};
|
|
59
63
|
|
|
60
|
-
|
|
61
|
-
Logger.printErrorLine = console.error;
|
|
64
|
+
Logger.printErrorLine = consoleErrorWithoutColor;
|
|
62
65
|
|
|
63
66
|
// Only exported for testing, shouldn't be used directly
|
|
64
67
|
Logger.processApiError = processApiError;
|
|
@@ -54,13 +54,21 @@ async function addLinkedApplication (appData, alias, ignoreParentConfig) {
|
|
|
54
54
|
return persistConfig(currentConfig);
|
|
55
55
|
};
|
|
56
56
|
|
|
57
|
-
async function removeLinkedApplication (alias) {
|
|
57
|
+
async function removeLinkedApplication ({ appId, alias }) {
|
|
58
58
|
const currentConfig = await loadApplicationConf();
|
|
59
59
|
const newConfig = {
|
|
60
60
|
...currentConfig,
|
|
61
|
-
apps: currentConfig.apps.filter((appEntry) =>
|
|
61
|
+
apps: currentConfig.apps.filter((appEntry) => {
|
|
62
|
+
return appEntry.app_id !== appId && appEntry.alias !== alias;
|
|
63
|
+
}),
|
|
62
64
|
};
|
|
63
|
-
|
|
65
|
+
|
|
66
|
+
if (currentConfig.apps.length !== newConfig.apps.length) {
|
|
67
|
+
await persistConfig(newConfig);
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return false;
|
|
64
72
|
};
|
|
65
73
|
|
|
66
74
|
function findApp (config, alias) {
|
|
@@ -4,6 +4,7 @@ const _ = require('lodash');
|
|
|
4
4
|
const application = require('@clevercloud/client/cjs/api/v2/application.js');
|
|
5
5
|
const autocomplete = require('cliparse').autocomplete;
|
|
6
6
|
const product = require('@clevercloud/client/cjs/api/v2/product.js');
|
|
7
|
+
const { getSummary } = require('@clevercloud/client/cjs/api/v2/user.js');
|
|
7
8
|
|
|
8
9
|
const AppConfiguration = require('./app_configuration.js');
|
|
9
10
|
const Interact = require('./interact.js');
|
|
@@ -12,6 +13,8 @@ const Organisation = require('./organisation.js');
|
|
|
12
13
|
const User = require('./user.js');
|
|
13
14
|
|
|
14
15
|
const { sendToApi } = require('../models/send-to-api.js');
|
|
16
|
+
const AppConfig = require('./app_configuration.js');
|
|
17
|
+
const { resolveOwnerId } = require('./ids-resolver.js');
|
|
15
18
|
|
|
16
19
|
function listAvailableTypes () {
|
|
17
20
|
return autocomplete.words(['docker', 'elixir', 'go', 'gradle', 'haskell', 'jar', 'maven', 'meteor', 'node', 'php', 'play1', 'play2', 'python', 'ruby', 'rust', 'sbt', 'static-apache', 'war']);
|
|
@@ -86,20 +89,58 @@ async function create (name, typeName, region, orgaIdOrName, github, isTask, env
|
|
|
86
89
|
return application.create({ id: ownerId }, newApp).then(sendToApi);
|
|
87
90
|
};
|
|
88
91
|
|
|
89
|
-
async function deleteApp (
|
|
90
|
-
Logger.debug('Deleting app: ' +
|
|
92
|
+
async function deleteApp (app, skipConfirmation) {
|
|
93
|
+
Logger.debug('Deleting app: ' + app.name + ' (' + app.id + ')');
|
|
91
94
|
|
|
92
95
|
if (!skipConfirmation) {
|
|
93
96
|
await Interact.confirm(
|
|
94
|
-
`Deleting the application ${
|
|
97
|
+
`Deleting the application ${app.name} can't be undone, please type '${app.name}' to confirm: `,
|
|
95
98
|
'No confirmation, aborting application deletion',
|
|
96
|
-
[
|
|
99
|
+
[app.name],
|
|
97
100
|
);
|
|
98
101
|
}
|
|
99
102
|
|
|
100
|
-
return application.remove({ id:
|
|
103
|
+
return application.remove({ id: app.ownerId, appId: app.id }).then(sendToApi);
|
|
101
104
|
};
|
|
102
105
|
|
|
106
|
+
async function getAllApps (ownerId) {
|
|
107
|
+
|
|
108
|
+
const summary = await getSummary().then(sendToApi);
|
|
109
|
+
|
|
110
|
+
const orgaWithApps = await Promise.all(
|
|
111
|
+
summary.organisations
|
|
112
|
+
// If owner ID is present, only keep the matching org
|
|
113
|
+
.filter((org) => ownerId == null || org.id === ownerId)
|
|
114
|
+
.sort((a, b) => a.name.localeCompare(b.name))
|
|
115
|
+
.map(async (org) => {
|
|
116
|
+
const applications = await getApplicationsForOwner(org.id);
|
|
117
|
+
return {
|
|
118
|
+
id: org.id,
|
|
119
|
+
name: org.name,
|
|
120
|
+
applications: applications.sort((a, b) => a.name.localeCompare(b.name)),
|
|
121
|
+
};
|
|
122
|
+
}),
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
return orgaWithApps;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
async function getApplicationsForOwner (ownerId) {
|
|
129
|
+
const rawApplications = await application.getAll({ id: ownerId }).then(sendToApi);
|
|
130
|
+
return rawApplications.map((app) => {
|
|
131
|
+
return {
|
|
132
|
+
app_id: app.id,
|
|
133
|
+
org_id: ownerId,
|
|
134
|
+
name: app.name,
|
|
135
|
+
zone: app.zone,
|
|
136
|
+
type: app.instance.variant.slug,
|
|
137
|
+
createdAt: new Date(app.creationDate).toISOString(),
|
|
138
|
+
deploy_url: app.deployment.httpUrl,
|
|
139
|
+
git_ssh_url: app.deployment.url,
|
|
140
|
+
};
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
103
144
|
function getApplicationByName (apps, name) {
|
|
104
145
|
const filteredApps = apps.filter((app) => app.name === name);
|
|
105
146
|
if (filteredApps.length === 1) {
|
|
@@ -129,6 +170,62 @@ function getFromSelf (appId) {
|
|
|
129
170
|
return application.get({ appId }).then(sendToApi);
|
|
130
171
|
};
|
|
131
172
|
|
|
173
|
+
/**
|
|
174
|
+
* @param {{app_id: string}|{app_name: string}} appIdOrName
|
|
175
|
+
* @param {string} alias
|
|
176
|
+
* @return {Promise<{appId: string, ownerId: string}>}
|
|
177
|
+
*/
|
|
178
|
+
async function resolveId (appIdOrName, alias) {
|
|
179
|
+
if (appIdOrName != null && alias != null) {
|
|
180
|
+
throw new Error('Only one of the `--app` or `--alias` options can be set at a time');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// -- resolve by linked app
|
|
184
|
+
|
|
185
|
+
if (appIdOrName == null) {
|
|
186
|
+
const appDetails = await AppConfig.getAppDetails({ alias });
|
|
187
|
+
return { appId: appDetails.appId, ownerId: appDetails.ownerId };
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// -- resolve by app id
|
|
191
|
+
|
|
192
|
+
if (appIdOrName.app_id != null) {
|
|
193
|
+
const ownerId = await resolveOwnerId(appIdOrName.app_id);
|
|
194
|
+
if (ownerId != null) {
|
|
195
|
+
return {
|
|
196
|
+
appId: appIdOrName.app_id,
|
|
197
|
+
ownerId,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
throw new Error('Application not found');
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// -- resolve by app name
|
|
205
|
+
|
|
206
|
+
const summary = await getSummary({}).then(sendToApi);
|
|
207
|
+
|
|
208
|
+
const candidates = [summary.user, ...summary.organisations]
|
|
209
|
+
.flatMap((owner) => owner.applications.map((app) => ({ app, owner })))
|
|
210
|
+
.filter((candidate) => candidate.app.name === appIdOrName.app_name);
|
|
211
|
+
|
|
212
|
+
if (candidates.length === 0) {
|
|
213
|
+
throw new Error('Application not found');
|
|
214
|
+
}
|
|
215
|
+
if (candidates.length === 1) {
|
|
216
|
+
return {
|
|
217
|
+
appId: candidates[0].app.id,
|
|
218
|
+
ownerId: candidates[0].owner.id,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
Logger.printErrorLine(`The name '${appIdOrName.app_name}' refers to multiple applications:`);
|
|
223
|
+
candidates.forEach((candidate) => {
|
|
224
|
+
Logger.printErrorLine(`- ${candidate.owner.name}: ${candidate.app.id} (${candidate.app.variantSlug})`);
|
|
225
|
+
});
|
|
226
|
+
throw new Error('Ambiguous application name, use the `--app` option with one of the IDs above');
|
|
227
|
+
}
|
|
228
|
+
|
|
132
229
|
async function linkRepo (app, orgaIdOrName, alias, ignoreParentConfig) {
|
|
133
230
|
Logger.debug(`Linking current repository to the app: ${app.app_id || app.app_name}`);
|
|
134
231
|
|
|
@@ -145,7 +242,7 @@ async function linkRepo (app, orgaIdOrName, alias, ignoreParentConfig) {
|
|
|
145
242
|
|
|
146
243
|
function unlinkRepo (alias) {
|
|
147
244
|
Logger.debug(`Unlinking current repository from the app: ${alias}`);
|
|
148
|
-
return AppConfiguration.removeLinkedApplication(alias);
|
|
245
|
+
return AppConfiguration.removeLinkedApplication({ alias });
|
|
149
246
|
};
|
|
150
247
|
|
|
151
248
|
function redeploy (ownerId, appId, commit, withoutCache) {
|
|
@@ -214,7 +311,7 @@ async function listDependencies (ownerId, appId, showAll) {
|
|
|
214
311
|
const applicationDeps = await application.getAllDependencies({ id: ownerId, appId }).then(sendToApi);
|
|
215
312
|
|
|
216
313
|
if (!showAll) {
|
|
217
|
-
return applicationDeps;
|
|
314
|
+
return applicationDeps.map((app) => ({ ...app, isLinked: true }));
|
|
218
315
|
}
|
|
219
316
|
|
|
220
317
|
const allApps = await application.getAll({ id: ownerId }).then(sendToApi);
|
|
@@ -237,9 +334,11 @@ async function unlink (ownerId, appId, dependency) {
|
|
|
237
334
|
};
|
|
238
335
|
|
|
239
336
|
module.exports = {
|
|
337
|
+
resolveId,
|
|
240
338
|
create,
|
|
241
339
|
deleteApp,
|
|
242
340
|
get,
|
|
341
|
+
getAllApps,
|
|
243
342
|
link,
|
|
244
343
|
linkRepo,
|
|
245
344
|
listAvailableAliases,
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const colors = require('colors/safe');
|
|
4
|
+
const Logger = require('../logger.js');
|
|
5
|
+
|
|
6
|
+
function get (follow, exitOnDeploy) {
|
|
7
|
+
if (follow) {
|
|
8
|
+
if (exitOnDeploy === 'deploy-start') {
|
|
9
|
+
throw new Error('The `follow` and `exit-on` set to "deploy-start" options are not compatible');
|
|
10
|
+
}
|
|
11
|
+
Logger.println(colors.yellow('The `follow` option is deprecated and will be removed in an upcoming major, use --exit-on set to "never" instead'));
|
|
12
|
+
return 'never';
|
|
13
|
+
}
|
|
14
|
+
return exitOnDeploy;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// plotQuietWarning: If in quiet mode and exitStrategy set to never plot a warning to indicate that the command will end
|
|
18
|
+
function plotQuietWarning (exitStrategy, quiet) {
|
|
19
|
+
if (exitStrategy === 'never' && quiet) {
|
|
20
|
+
Logger.println(colors.bold.yellow('The "never" exit-on strategy is not compatible with the "quiet" mode, it will exit once the deployment ends'));
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
module.exports = { get, plotQuietWarning };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper to print a real JSON array with starting `[` and ending `]`
|
|
3
|
+
*/
|
|
4
|
+
class JsonArray {
|
|
5
|
+
constructor () {
|
|
6
|
+
this._isFirst = true;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
open () {
|
|
10
|
+
process.stdout.write('[\n');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
push (log) {
|
|
14
|
+
if (this._isFirst) {
|
|
15
|
+
this._isFirst = false;
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
process.stdout.write(',\n');
|
|
19
|
+
}
|
|
20
|
+
process.stdout.write(` ${JSON.stringify(log)}`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
close () {
|
|
24
|
+
process.stdout.write('\n]');
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
module.exports = { JsonArray };
|
package/src/models/log-v4.js
CHANGED
|
@@ -4,6 +4,8 @@ const { Deferred } = require('./utils.js');
|
|
|
4
4
|
const Logger = require('../logger.js');
|
|
5
5
|
const { waitForDeploymentEnd, waitForDeploymentStart } = require('./deployments.js');
|
|
6
6
|
const { ApplicationLogStream } = require('@clevercloud/client/cjs/streams/application-logs.js');
|
|
7
|
+
const { JsonArray } = require('./json-array.js');
|
|
8
|
+
const ExitStrategy = require('../models/exit-strategy-option.js');
|
|
7
9
|
|
|
8
10
|
// 2000 logs per 100ms maximum
|
|
9
11
|
const THROTTLE_ELEMENTS = 2000;
|
|
@@ -90,15 +92,20 @@ async function watchDeploymentAndDisplayLogs (options) {
|
|
|
90
92
|
commitId,
|
|
91
93
|
knownDeployments,
|
|
92
94
|
quiet,
|
|
93
|
-
follow,
|
|
94
95
|
redeployDate,
|
|
96
|
+
exitStrategy,
|
|
95
97
|
} = options;
|
|
96
98
|
|
|
99
|
+
ExitStrategy.plotQuietWarning(exitStrategy, quiet);
|
|
97
100
|
// If in quiet mode, we only log start/finished deployment messages
|
|
98
101
|
!quiet && Logger.println('Waiting for deployment to start…');
|
|
99
102
|
const deployment = await waitForDeploymentStart({ ownerId, appId, deploymentId, commitId, knownDeployments });
|
|
100
103
|
Logger.println(colors.bold.blue(`Deployment started (${deployment.uuid})`));
|
|
101
104
|
|
|
105
|
+
if (exitStrategy === 'deploy-start') {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
102
109
|
const deferred = new Deferred();
|
|
103
110
|
let logsStream;
|
|
104
111
|
|
|
@@ -120,7 +127,7 @@ async function watchDeploymentAndDisplayLogs (options) {
|
|
|
120
127
|
deferred.promise,
|
|
121
128
|
]);
|
|
122
129
|
|
|
123
|
-
if (!quiet &&
|
|
130
|
+
if (!quiet && exitStrategy !== 'never') {
|
|
124
131
|
logsStream.close(quiet ? 'quiet' : 'follow');
|
|
125
132
|
}
|
|
126
133
|
|
|
@@ -166,30 +173,3 @@ function isBuildSucessMessage (log) {
|
|
|
166
173
|
};
|
|
167
174
|
|
|
168
175
|
module.exports = { displayLogs, watchDeploymentAndDisplayLogs };
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Helper to print a real JSON array with starting `[` and ending `]`
|
|
172
|
-
*/
|
|
173
|
-
class JsonArray {
|
|
174
|
-
constructor () {
|
|
175
|
-
this._isFirst = true;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
open () {
|
|
179
|
-
process.stdout.write('[\n');
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
push (log) {
|
|
183
|
-
if (this._isFirst) {
|
|
184
|
-
this._isFirst = false;
|
|
185
|
-
}
|
|
186
|
-
else {
|
|
187
|
-
process.stdout.write(',\n');
|
|
188
|
-
}
|
|
189
|
-
process.stdout.write(` ${JSON.stringify(log)}`);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
close () {
|
|
193
|
-
process.stdout.write('\n]');
|
|
194
|
-
}
|
|
195
|
-
}
|
package/src/models/log.js
CHANGED
|
@@ -9,6 +9,7 @@ const { getOldLogs } = require('@clevercloud/client/cjs/api/v2/log.js');
|
|
|
9
9
|
const { LogsStream } = require('@clevercloud/client/cjs/streams/logs.node.js');
|
|
10
10
|
const { sendToApi, getHostAndTokens } = require('./send-to-api.js');
|
|
11
11
|
const { waitForDeploymentEnd, waitForDeploymentStart } = require('./deployments.js');
|
|
12
|
+
const ExitStrategy = require('../models/exit-strategy-option.js');
|
|
12
13
|
|
|
13
14
|
function isCleverMessage (line) {
|
|
14
15
|
return line._source.syslog_program === '/home/bas/rubydeployer/deployer.rb';
|
|
@@ -100,12 +101,18 @@ async function displayLogs ({ appAddonId, until, since, filter, deploymentId })
|
|
|
100
101
|
return deferred.promise;
|
|
101
102
|
}
|
|
102
103
|
|
|
103
|
-
async function watchDeploymentAndDisplayLogs ({ ownerId, appId, deploymentId, commitId, knownDeployments, quiet,
|
|
104
|
+
async function watchDeploymentAndDisplayLogs ({ ownerId, appId, deploymentId, commitId, knownDeployments, quiet, exitStrategy }) {
|
|
104
105
|
|
|
106
|
+
ExitStrategy.plotQuietWarning(exitStrategy, quiet);
|
|
107
|
+
// If in quiet mode, we only log start/finished deployment messages
|
|
105
108
|
Logger.println('Waiting for deployment to start…');
|
|
106
109
|
const deployment = await waitForDeploymentStart({ ownerId, appId, deploymentId, commitId, knownDeployments });
|
|
107
110
|
Logger.println(colors.bold.blue(`Deployment started (${deployment.uuid})`));
|
|
108
111
|
|
|
112
|
+
if (exitStrategy === 'deploy-start') {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
|
|
109
116
|
const deferred = new Deferred();
|
|
110
117
|
let logsStream;
|
|
111
118
|
|
|
@@ -127,7 +134,7 @@ async function watchDeploymentAndDisplayLogs ({ ownerId, appId, deploymentId, co
|
|
|
127
134
|
deferred.promise,
|
|
128
135
|
]);
|
|
129
136
|
|
|
130
|
-
if (!quiet &&
|
|
137
|
+
if (!quiet && exitStrategy !== 'never') {
|
|
131
138
|
logsStream.close();
|
|
132
139
|
}
|
|
133
140
|
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const Application = require('./application.js');
|
|
2
|
+
const organisation = require('@clevercloud/client/cjs/api/v2/organisation.js');
|
|
3
|
+
const { sendToApi } = require('./send-to-api.js');
|
|
4
|
+
const { autocomplete } = require('cliparse');
|
|
5
|
+
|
|
6
|
+
async function getNamespaces (ownerId) {
|
|
7
|
+
return organisation.getNamespaces({ id: ownerId }).then(sendToApi);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async function completeNamespaces () {
|
|
11
|
+
// Sadly we do not have access to current params in complete as of now
|
|
12
|
+
const { ownerId } = await Application.resolveId(null, null);
|
|
13
|
+
|
|
14
|
+
return getNamespaces(ownerId).then(autocomplete.words);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
module.exports = {
|
|
18
|
+
getNamespaces,
|
|
19
|
+
completeNamespaces,
|
|
20
|
+
};
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const _ = require('lodash');
|
|
4
|
-
const autocomplete = require('cliparse').autocomplete;
|
|
5
4
|
|
|
6
|
-
const AppConfig = require('./app_configuration.js');
|
|
7
|
-
|
|
8
|
-
const organisation = require('@clevercloud/client/cjs/api/v2/organisation.js');
|
|
9
5
|
const { getSummary } = require('@clevercloud/client/cjs/api/v2/user.js');
|
|
10
6
|
const { sendToApi } = require('../models/send-to-api.js');
|
|
11
7
|
|
|
@@ -37,22 +33,6 @@ async function getByName (name) {
|
|
|
37
33
|
return filteredOrgs[0];
|
|
38
34
|
}
|
|
39
35
|
|
|
40
|
-
async function getNamespaces (params) {
|
|
41
|
-
const { alias } = params.options;
|
|
42
|
-
const { ownerId } = await AppConfig.getAppDetails({ alias });
|
|
43
|
-
|
|
44
|
-
return organisation.getNamespaces({ id: ownerId }).then(sendToApi);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function completeNamespaces () {
|
|
48
|
-
// Sadly we do not have access to current params in complete as of now
|
|
49
|
-
const params = { options: {} };
|
|
50
|
-
|
|
51
|
-
return getNamespaces(params).then(autocomplete.words);
|
|
52
|
-
};
|
|
53
|
-
|
|
54
36
|
module.exports = {
|
|
55
37
|
getId,
|
|
56
|
-
getNamespaces,
|
|
57
|
-
completeNamespaces,
|
|
58
38
|
};
|
package/src/models/utils.js
CHANGED
|
@@ -15,4 +15,11 @@ class Deferred {
|
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
function truncateWithEllipsis (length, string) {
|
|
19
|
+
if (string.length > length - 1) {
|
|
20
|
+
return string.substring(0, length - 1) + '…';
|
|
21
|
+
}
|
|
22
|
+
return string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
module.exports = { Deferred, truncateWithEllipsis };
|