clever-tools 3.13.1 → 4.0.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 +130 -37
- package/bin/clever.js +1427 -718
- package/package.json +62 -77
- package/src/clever-client/auth-bridge.js +9 -12
- package/src/clever-client/ng.js +1 -1
- package/src/clever-client/operators.js +7 -7
- package/src/command-options.js +6 -6
- package/src/command-promise-handler.js +6 -6
- package/src/commands/accesslogs.js +38 -29
- package/src/commands/activity.js +62 -40
- package/src/commands/addon.js +65 -69
- package/src/commands/applications.js +33 -31
- package/src/commands/cancel-deploy.js +7 -7
- package/src/commands/config.js +9 -7
- package/src/commands/console.js +3 -3
- package/src/commands/create.js +33 -32
- package/src/commands/curl.js +14 -36
- package/src/commands/database.js +10 -30
- package/src/commands/delete.js +8 -8
- package/src/commands/deploy.js +60 -46
- package/src/commands/diag.js +27 -25
- package/src/commands/domain.js +89 -91
- package/src/commands/drain.js +26 -14
- package/src/commands/emails.js +24 -28
- package/src/commands/env.js +18 -18
- package/src/commands/features.js +12 -18
- package/src/commands/keycloak.js +11 -11
- package/src/commands/kv.js +13 -13
- package/src/commands/link.js +9 -8
- package/src/commands/login.js +19 -27
- package/src/commands/logout.js +1 -1
- package/src/commands/logs.js +20 -12
- package/src/commands/makeDefault.js +5 -5
- package/src/commands/matomo.js +7 -7
- package/src/commands/metabase.js +9 -9
- package/src/commands/ng.js +32 -32
- package/src/commands/notify-email.js +11 -15
- package/src/commands/open.js +1 -1
- package/src/commands/otoroshi.js +11 -11
- package/src/commands/profile.js +8 -8
- package/src/commands/published-config.js +11 -11
- package/src/commands/restart.js +15 -8
- package/src/commands/scale.js +25 -16
- package/src/commands/service.js +28 -11
- package/src/commands/ssh-keys.js +25 -29
- package/src/commands/ssh.js +1 -2
- package/src/commands/status.js +31 -22
- package/src/commands/stop.js +3 -3
- package/src/commands/tcp-redirs.js +27 -26
- package/src/commands/tokens.js +44 -43
- package/src/commands/unlink.js +7 -5
- package/src/commands/version.js +2 -4
- package/src/commands/webhooks.js +8 -10
- package/src/experimental-features.js +7 -5
- package/src/format-table.js +46 -15
- package/src/initial-setup.js +2 -32
- package/src/initial-update-notifier.js +18 -0
- package/src/lib/has-param.js +16 -0
- package/src/lib/ng-print.js +21 -24
- package/src/lib/operator-commands.js +58 -53
- package/src/lib/prompts.js +5 -5
- package/src/lib/slugify.js +1 -1
- package/src/logger.js +26 -21
- package/src/models/activity.js +3 -3
- package/src/models/addon.js +54 -56
- package/src/models/app_configuration.js +34 -32
- package/src/models/application.js +90 -77
- package/src/models/application_configuration.js +19 -27
- package/src/models/configuration.js +30 -32
- package/src/models/deployments.js +9 -19
- package/src/models/domain.js +10 -9
- package/src/models/drain.js +16 -15
- package/src/models/exit-strategy-option.js +15 -5
- package/src/models/fs-utils.js +4 -3
- package/src/models/git.js +26 -32
- package/src/models/ids-resolver.js +37 -41
- package/src/models/isomorphic-http-with-agent.js +3 -3
- package/src/models/json-array.js +5 -6
- package/src/models/log-v4.js +44 -52
- package/src/models/log.js +40 -45
- package/src/models/namespaces.js +4 -4
- package/src/models/ng-resources.js +49 -39
- package/src/models/ng.js +41 -40
- package/src/models/node-dns-resolver.js +3 -3
- package/src/models/notification.js +7 -10
- package/src/models/operator.js +8 -10
- package/src/models/organisation.js +5 -8
- package/src/models/send-to-api.js +18 -11
- package/src/models/user.js +6 -5
- package/src/models/utils.js +5 -8
- package/src/models/variables.js +18 -20
- package/src/parsers.js +34 -34
- package/src/load-package-json.cjs +0 -5
package/src/commands/activity.js
CHANGED
|
@@ -1,43 +1,58 @@
|
|
|
1
|
-
import
|
|
2
|
-
import moment from 'moment';
|
|
1
|
+
import { styleText } from 'node:util';
|
|
3
2
|
|
|
4
|
-
import
|
|
3
|
+
import { EventsStream } from '@clevercloud/client/esm/streams/events.js';
|
|
5
4
|
import { formatTable } from '../format-table.js';
|
|
6
5
|
import { Logger } from '../logger.js';
|
|
7
|
-
import
|
|
8
|
-
import { EventsStream } from '@clevercloud/client/esm/streams/events.node.js';
|
|
9
|
-
import { getHostAndTokens } from '../models/send-to-api.js';
|
|
6
|
+
import * as Activity from '../models/activity.js';
|
|
10
7
|
import * as Application from '../models/application.js';
|
|
8
|
+
import { getHostAndTokens } from '../models/send-to-api.js';
|
|
9
|
+
import { Deferred } from '../models/utils.js';
|
|
10
|
+
|
|
11
|
+
const dtf = new Intl.DateTimeFormat('en', {
|
|
12
|
+
year: 'numeric',
|
|
13
|
+
month: '2-digit',
|
|
14
|
+
day: '2-digit',
|
|
15
|
+
hour: '2-digit',
|
|
16
|
+
minute: '2-digit',
|
|
17
|
+
second: '2-digit',
|
|
18
|
+
hour12: false,
|
|
19
|
+
timeZoneName: 'longOffset',
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
function formatDate(date) {
|
|
23
|
+
const d = Object.fromEntries(dtf.formatToParts(date).map((p) => [p.type, p.value]));
|
|
24
|
+
return `${d.year}-${d.month}-${d.day}T${d.hour}:${d.minute}:${d.second}${d.timeZoneName.replace('GMT', '')}`;
|
|
25
|
+
}
|
|
11
26
|
|
|
12
|
-
function getColoredState
|
|
27
|
+
function getColoredState(state, isLast) {
|
|
13
28
|
if (state === 'OK') {
|
|
14
|
-
return
|
|
29
|
+
return styleText(['bold', 'green'], state);
|
|
15
30
|
}
|
|
16
31
|
if (state === 'FAIL' || state === 'CANCELLED') {
|
|
17
|
-
return
|
|
32
|
+
return styleText(['bold', 'red'], state);
|
|
18
33
|
}
|
|
19
34
|
if (state === 'WIP' && !isLast) {
|
|
20
|
-
return
|
|
35
|
+
return styleText(['bold', 'red'], 'FAIL');
|
|
21
36
|
}
|
|
22
37
|
if (state === 'WIP' && isLast) {
|
|
23
|
-
return
|
|
38
|
+
return styleText(['bold', 'blue'], 'IN PROGRESS');
|
|
24
39
|
}
|
|
25
40
|
Logger.warn(`Unknown deployment state: ${state}`);
|
|
26
41
|
return 'UNKNOWN';
|
|
27
42
|
}
|
|
28
43
|
|
|
29
44
|
// We use examples of maximum width text to have a clean display
|
|
30
|
-
const
|
|
31
|
-
|
|
45
|
+
const ACTIVITY_TABLE_COLUMN_WIDTHS = [
|
|
46
|
+
formatDate(Date.now()),
|
|
32
47
|
47,
|
|
33
48
|
'IN PROGRESS',
|
|
34
49
|
'downscale',
|
|
35
50
|
// a git commit id is 8 chars long
|
|
36
51
|
8,
|
|
37
52
|
0,
|
|
38
|
-
]
|
|
53
|
+
];
|
|
39
54
|
|
|
40
|
-
function convertEventToJson
|
|
55
|
+
function convertEventToJson(event) {
|
|
41
56
|
return {
|
|
42
57
|
uuid: event.uuid,
|
|
43
58
|
date: event.date,
|
|
@@ -48,27 +63,30 @@ function convertEventToJson (event) {
|
|
|
48
63
|
};
|
|
49
64
|
}
|
|
50
65
|
|
|
51
|
-
function formatActivityLine
|
|
52
|
-
return
|
|
66
|
+
function formatActivityLine(event) {
|
|
67
|
+
return formatTable(
|
|
53
68
|
[
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
69
|
+
[
|
|
70
|
+
formatDate(event.date),
|
|
71
|
+
event.uuid,
|
|
72
|
+
getColoredState(event.state, event.isLast),
|
|
73
|
+
event.action,
|
|
74
|
+
event?.commit?.substring(0, 8) ?? 'N/A',
|
|
75
|
+
event.cause,
|
|
76
|
+
],
|
|
60
77
|
],
|
|
61
|
-
|
|
78
|
+
ACTIVITY_TABLE_COLUMN_WIDTHS,
|
|
79
|
+
);
|
|
62
80
|
}
|
|
63
81
|
|
|
64
|
-
function isTemporaryEvent
|
|
82
|
+
function isTemporaryEvent(ev) {
|
|
65
83
|
if (ev == null) {
|
|
66
84
|
return false;
|
|
67
85
|
}
|
|
68
86
|
return (ev.state === 'WIP' && ev.isLast) || ev.state === 'CANCELLED';
|
|
69
87
|
}
|
|
70
88
|
|
|
71
|
-
function clearPreviousLine
|
|
89
|
+
function clearPreviousLine() {
|
|
72
90
|
if (process.stdout.isTTY) {
|
|
73
91
|
process.stdout.moveCursor(0, -1);
|
|
74
92
|
process.stdout.cursorTo(0);
|
|
@@ -76,7 +94,7 @@ function clearPreviousLine () {
|
|
|
76
94
|
}
|
|
77
95
|
}
|
|
78
96
|
|
|
79
|
-
function handleEvent
|
|
97
|
+
function handleEvent(previousEvent, event, handler) {
|
|
80
98
|
if (isTemporaryEvent(previousEvent)) {
|
|
81
99
|
handler.pop();
|
|
82
100
|
}
|
|
@@ -86,21 +104,24 @@ function handleEvent (previousEvent, event, handler) {
|
|
|
86
104
|
return event;
|
|
87
105
|
}
|
|
88
106
|
|
|
89
|
-
function onEvent
|
|
90
|
-
const {
|
|
107
|
+
function onEvent(previousEvent, newEvent, handler) {
|
|
108
|
+
const {
|
|
109
|
+
event,
|
|
110
|
+
date,
|
|
111
|
+
data: { uuid, state, action, commit, cause },
|
|
112
|
+
} = newEvent;
|
|
91
113
|
if (event !== 'DEPLOYMENT_ACTION_BEGIN' && event !== 'DEPLOYMENT_ACTION_END') {
|
|
92
114
|
return previousEvent;
|
|
93
115
|
}
|
|
94
116
|
return handleEvent(previousEvent, { date, uuid, state, action, commit, cause, isLast: true }, handler);
|
|
95
117
|
}
|
|
96
118
|
|
|
97
|
-
function getEventHandler
|
|
119
|
+
function getEventHandler(format, follow) {
|
|
98
120
|
switch (format) {
|
|
99
121
|
case 'json': {
|
|
100
122
|
if (follow) {
|
|
101
123
|
throw new Error('The `follow` option and "json" format are not compatible. Use "json-stream" format instead.');
|
|
102
|
-
}
|
|
103
|
-
else {
|
|
124
|
+
} else {
|
|
104
125
|
const buf = [];
|
|
105
126
|
|
|
106
127
|
return {
|
|
@@ -132,20 +153,21 @@ function getEventHandler (format, follow) {
|
|
|
132
153
|
}
|
|
133
154
|
}
|
|
134
155
|
|
|
135
|
-
export async function activity
|
|
156
|
+
export async function activity(params) {
|
|
136
157
|
const { alias, app: appIdOrName, 'show-all': showAll, follow, format } = params.options;
|
|
137
158
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
138
159
|
const events = await Activity.list(ownerId, appId, showAll);
|
|
139
|
-
const reversedArrayWithIndex = events
|
|
140
|
-
.
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
return ({ ...event, isLast });
|
|
144
|
-
});
|
|
160
|
+
const reversedArrayWithIndex = events.reverse().map((event, index, all) => {
|
|
161
|
+
const isLast = index === all.length - 1;
|
|
162
|
+
return { ...event, isLast };
|
|
163
|
+
});
|
|
145
164
|
|
|
146
165
|
const handler = getEventHandler(format, follow);
|
|
147
166
|
|
|
148
|
-
let lastEvent = reversedArrayWithIndex.reduce(
|
|
167
|
+
let lastEvent = reversedArrayWithIndex.reduce(
|
|
168
|
+
(previousEvent, newEvent) => handleEvent(previousEvent, newEvent, handler),
|
|
169
|
+
{},
|
|
170
|
+
);
|
|
149
171
|
|
|
150
172
|
if (!follow) {
|
|
151
173
|
handler.end();
|
package/src/commands/addon.js
CHANGED
|
@@ -1,22 +1,20 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { styleText } from 'node:util';
|
|
2
2
|
|
|
3
|
+
import { getAllEnvVars } from '@clevercloud/client/esm/api/v2/addon.js';
|
|
4
|
+
import { toNameEqualsValueString } from '@clevercloud/client/esm/utils/env-vars.js';
|
|
5
|
+
import dedent from 'dedent';
|
|
6
|
+
import { formatTable } from '../format-table.js';
|
|
7
|
+
import { Logger } from '../logger.js';
|
|
3
8
|
import * as Addon from '../models/addon.js';
|
|
9
|
+
import { findOwnerId, parseAddonOptions } from '../models/addon.js';
|
|
4
10
|
import * as AppConfig from '../models/app_configuration.js';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
11
|
+
import { conf } from '../models/configuration.js';
|
|
12
|
+
import { resolveAddonId } from '../models/ids-resolver.js';
|
|
7
13
|
import * as Organisation from '../models/organisation.js';
|
|
8
|
-
import * as User from '../models/user.js';
|
|
9
|
-
import { parseAddonOptions, findOwnerId } from '../models/addon.js';
|
|
10
|
-
import { getAllEnvVars } from '@clevercloud/client/esm/api/v2/addon.js';
|
|
11
14
|
import { sendToApi } from '../models/send-to-api.js';
|
|
12
|
-
import
|
|
13
|
-
import { resolveAddonId } from '../models/ids-resolver.js';
|
|
14
|
-
import { conf } from '../models/configuration.js';
|
|
15
|
-
import dedent from 'dedent';
|
|
16
|
-
|
|
17
|
-
const formatTable = initFormatTable();
|
|
15
|
+
import * as User from '../models/user.js';
|
|
18
16
|
|
|
19
|
-
export async function list
|
|
17
|
+
export async function list(params) {
|
|
20
18
|
const { org: orgaIdOrName, format } = params.options;
|
|
21
19
|
|
|
22
20
|
const ownerId = await Organisation.getId(orgaIdOrName);
|
|
@@ -46,7 +44,7 @@ export async function list (params) {
|
|
|
46
44
|
return [
|
|
47
45
|
addon.plan.name + ' ' + addon.provider.name,
|
|
48
46
|
addon.region,
|
|
49
|
-
|
|
47
|
+
styleText(['bold', 'green'], addon.name),
|
|
50
48
|
addon.id,
|
|
51
49
|
];
|
|
52
50
|
});
|
|
@@ -55,7 +53,7 @@ export async function list (params) {
|
|
|
55
53
|
}
|
|
56
54
|
}
|
|
57
55
|
|
|
58
|
-
export async function create
|
|
56
|
+
export async function create(params) {
|
|
59
57
|
const [providerName, name] = params.args;
|
|
60
58
|
const {
|
|
61
59
|
link: linkedAppAlias,
|
|
@@ -68,9 +66,7 @@ export async function create (params) {
|
|
|
68
66
|
const version = params.options['addon-version'];
|
|
69
67
|
const addonOptions = parseAddonOptions(params.options.option);
|
|
70
68
|
|
|
71
|
-
const ownerId =
|
|
72
|
-
? await Organisation.getId(orgaIdOrName)
|
|
73
|
-
: await User.getCurrentId();
|
|
69
|
+
const ownerId = orgaIdOrName != null ? await Organisation.getId(orgaIdOrName) : await User.getCurrentId();
|
|
74
70
|
|
|
75
71
|
const addonToCreate = {
|
|
76
72
|
ownerId,
|
|
@@ -86,23 +82,28 @@ export async function create (params) {
|
|
|
86
82
|
if (linkedAppAlias != null) {
|
|
87
83
|
const linkedAppData = await AppConfig.getAppDetails({ alias: linkedAppAlias });
|
|
88
84
|
if (orgaIdOrName != null && linkedAppData.ownerId !== ownerId && format === 'human') {
|
|
89
|
-
Logger.warn(
|
|
85
|
+
Logger.warn(
|
|
86
|
+
'The specified application does not belong to the specified organisation. Ignoring the `--org` option',
|
|
87
|
+
);
|
|
90
88
|
}
|
|
91
89
|
const newAddon = await Addon.create({
|
|
92
90
|
...addonToCreate,
|
|
93
91
|
ownerId: linkedAppData.ownerId,
|
|
94
92
|
});
|
|
95
93
|
await Addon.link(linkedAppData.ownerId, linkedAppData.appId, { addon_id: newAddon.id });
|
|
96
|
-
displayAddon(
|
|
97
|
-
|
|
98
|
-
|
|
94
|
+
displayAddon(
|
|
95
|
+
format,
|
|
96
|
+
newAddon,
|
|
97
|
+
providerName,
|
|
98
|
+
`Add-on created and linked to application ${linkedAppAlias} successfully!`,
|
|
99
|
+
);
|
|
100
|
+
} else {
|
|
99
101
|
const newAddon = await Addon.create(addonToCreate);
|
|
100
102
|
displayAddon(format, newAddon, providerName, 'Add-on created successfully!');
|
|
101
103
|
}
|
|
102
104
|
}
|
|
103
105
|
|
|
104
|
-
function displayAddon
|
|
105
|
-
|
|
106
|
+
function displayAddon(format, addon, providerName, message) {
|
|
106
107
|
const PROVIDERS_WITH_URL = {
|
|
107
108
|
keycloak: {
|
|
108
109
|
name: 'Keycloak',
|
|
@@ -132,9 +133,9 @@ function displayAddon (format, addon, providerName, message) {
|
|
|
132
133
|
kv: {
|
|
133
134
|
status: 'alpha',
|
|
134
135
|
postCreateInstructions: dedent`
|
|
135
|
-
${
|
|
136
|
-
${
|
|
137
|
-
${
|
|
136
|
+
${styleText('yellow', "You can easily use Materia KV with 'redis-cli', with such commands:")}
|
|
137
|
+
${styleText('blue', `source <(clever addon env ${addon.id} -F shell)`)}
|
|
138
|
+
${styleText('blue', 'redis-cli -h $KV_HOST -p $KV_PORT --tls')}
|
|
138
139
|
Learn more about Materia KV on Clever Cloud: ${conf.DOC_URL}/addons/materia-kv/
|
|
139
140
|
`,
|
|
140
141
|
},
|
|
@@ -167,19 +168,14 @@ function displayAddon (format, addon, providerName, message) {
|
|
|
167
168
|
let providerNameToShow = '';
|
|
168
169
|
let statusMessage = '';
|
|
169
170
|
if (providerName in WIP_PROVIDERS && WIP_PROVIDERS[providerName].status !== '') {
|
|
170
|
-
|
|
171
|
-
providerNameToShow = providerName === 'kv'
|
|
172
|
-
? 'Materia KV'
|
|
173
|
-
: providerName;
|
|
171
|
+
providerNameToShow = providerName === 'kv' ? 'Materia KV' : providerName;
|
|
174
172
|
|
|
175
173
|
statusMessage = `The ${providerNameToShow} provider is in ${WIP_PROVIDERS[providerName].status} testing phase`;
|
|
176
|
-
statusMessage +=
|
|
177
|
-
?
|
|
178
|
-
: '';
|
|
174
|
+
statusMessage +=
|
|
175
|
+
WIP_PROVIDERS[providerName].status === 'alpha' ? ". Don't store sensitive or production grade data." : '';
|
|
179
176
|
}
|
|
180
177
|
|
|
181
178
|
switch (format) {
|
|
182
|
-
|
|
183
179
|
case 'json': {
|
|
184
180
|
const jsonAddon = {
|
|
185
181
|
id: addon.id,
|
|
@@ -188,20 +184,17 @@ function displayAddon (format, addon, providerName, message) {
|
|
|
188
184
|
env: addon.env,
|
|
189
185
|
};
|
|
190
186
|
|
|
191
|
-
Logger.printJson(
|
|
192
|
-
|
|
193
|
-
|
|
187
|
+
Logger.printJson(
|
|
188
|
+
WIP_PROVIDERS[providerName] != null
|
|
189
|
+
? { ...jsonAddon, availability: WIP_PROVIDERS[providerName].status, message: statusMessage }
|
|
190
|
+
: jsonAddon,
|
|
191
|
+
);
|
|
194
192
|
break;
|
|
195
193
|
}
|
|
196
194
|
|
|
197
195
|
case 'human':
|
|
198
196
|
default:
|
|
199
|
-
Logger.println([
|
|
200
|
-
message,
|
|
201
|
-
`ID: ${addon.id}`,
|
|
202
|
-
`Real ID: ${addon.realId}`,
|
|
203
|
-
`Name: ${addon.name}`,
|
|
204
|
-
].join('\n'));
|
|
197
|
+
Logger.println([message, `ID: ${addon.id}`, `Real ID: ${addon.realId}`, `Name: ${addon.name}`].join('\n'));
|
|
205
198
|
|
|
206
199
|
if (providerName in PROVIDERS_WITH_URL) {
|
|
207
200
|
const provider = PROVIDERS_WITH_URL[providerName];
|
|
@@ -218,25 +211,34 @@ function displayAddon (format, addon, providerName, message) {
|
|
|
218
211
|
|
|
219
212
|
if (providerName === 'keycloak') {
|
|
220
213
|
Logger.println();
|
|
221
|
-
Logger.println(
|
|
214
|
+
Logger.println(
|
|
215
|
+
"An initial account has been created, you'll be invited to change the password at first login:",
|
|
216
|
+
);
|
|
222
217
|
Logger.println(` - Admin user name: ${addon.env.find((e) => e.name === 'CC_KEYCLOAK_ADMIN').value}`);
|
|
223
|
-
Logger.println(
|
|
218
|
+
Logger.println(
|
|
219
|
+
` - Temporary password: ${addon.env.find((e) => e.name === 'CC_KEYCLOAK_ADMIN_DEFAULT_PASSWORD').value}`,
|
|
220
|
+
);
|
|
224
221
|
}
|
|
225
222
|
|
|
226
223
|
if (providerName === 'otoroshi') {
|
|
227
224
|
Logger.println();
|
|
228
|
-
Logger.println(
|
|
229
|
-
|
|
230
|
-
|
|
225
|
+
Logger.println(
|
|
226
|
+
'An initial account has been created, change the password at first login (Security -> Administrators -> Edit user):',
|
|
227
|
+
);
|
|
228
|
+
Logger.println(
|
|
229
|
+
` - Admin user name: ${addon.env.find((e) => e.name === 'CC_OTOROSHI_INITIAL_ADMIN_LOGIN').value}`,
|
|
230
|
+
);
|
|
231
|
+
Logger.println(
|
|
232
|
+
` - Initial password: ${addon.env.find((e) => e.name === 'CC_OTOROSHI_INITIAL_ADMIN_PASSWORD').value}`,
|
|
233
|
+
);
|
|
231
234
|
}
|
|
232
235
|
}
|
|
233
236
|
|
|
234
237
|
if (providerName in WIP_PROVIDERS) {
|
|
235
|
-
|
|
236
238
|
Logger.println();
|
|
237
239
|
|
|
238
240
|
if (statusMessage !== '') {
|
|
239
|
-
Logger.println(
|
|
241
|
+
Logger.println(styleText('yellow', `/!\\ ${statusMessage}`));
|
|
240
242
|
}
|
|
241
243
|
|
|
242
244
|
Logger.println(WIP_PROVIDERS[providerName].postCreateInstructions);
|
|
@@ -244,7 +246,7 @@ function displayAddon (format, addon, providerName, message) {
|
|
|
244
246
|
}
|
|
245
247
|
}
|
|
246
248
|
|
|
247
|
-
export async function deleteAddon
|
|
249
|
+
export async function deleteAddon(params) {
|
|
248
250
|
const { yes: skipConfirmation, org: orgaIdOrName } = params.options;
|
|
249
251
|
const [addon] = params.args;
|
|
250
252
|
|
|
@@ -262,7 +264,7 @@ export async function deleteAddon (params) {
|
|
|
262
264
|
Logger.println(`Addon ${addon.addon_id || addon.addon_name} successfully deleted`);
|
|
263
265
|
}
|
|
264
266
|
|
|
265
|
-
export async function rename
|
|
267
|
+
export async function rename(params) {
|
|
266
268
|
const [addon, newName] = params.args;
|
|
267
269
|
const { org: orgaIdOrName } = params.options;
|
|
268
270
|
|
|
@@ -272,7 +274,7 @@ export async function rename (params) {
|
|
|
272
274
|
Logger.println(`Addon ${addon.addon_id || addon.addon_name} successfully renamed to ${newName}`);
|
|
273
275
|
}
|
|
274
276
|
|
|
275
|
-
export async function listProviders
|
|
277
|
+
export async function listProviders(params) {
|
|
276
278
|
const { format } = params.options;
|
|
277
279
|
|
|
278
280
|
const providers = await Addon.listProviders();
|
|
@@ -296,18 +298,14 @@ export async function listProviders (params) {
|
|
|
296
298
|
case 'human':
|
|
297
299
|
default: {
|
|
298
300
|
const formattedProviders = providers.map((provider) => {
|
|
299
|
-
return [
|
|
300
|
-
colors.bold(provider.id),
|
|
301
|
-
provider.name,
|
|
302
|
-
provider.shortDesc || '',
|
|
303
|
-
];
|
|
301
|
+
return [styleText('bold', provider.id), provider.name, provider.shortDesc || ''];
|
|
304
302
|
});
|
|
305
303
|
Logger.println(formatTable(formattedProviders));
|
|
306
304
|
}
|
|
307
305
|
}
|
|
308
306
|
}
|
|
309
307
|
|
|
310
|
-
export async function showProvider
|
|
308
|
+
export async function showProvider(params) {
|
|
311
309
|
const [providerName] = params.args;
|
|
312
310
|
const { format } = params.options;
|
|
313
311
|
|
|
@@ -366,7 +364,7 @@ export async function showProvider (params) {
|
|
|
366
364
|
}
|
|
367
365
|
case 'human':
|
|
368
366
|
default: {
|
|
369
|
-
Logger.println(
|
|
367
|
+
Logger.println(styleText('bold', formattedProvider.id));
|
|
370
368
|
Logger.println(`${formattedProvider.name}: ${formattedProvider.shortDesc}`);
|
|
371
369
|
Logger.println();
|
|
372
370
|
Logger.println(`Available regions: ${formattedProvider.regions.join(', ')}`);
|
|
@@ -374,11 +372,13 @@ export async function showProvider (params) {
|
|
|
374
372
|
Logger.println('Available plans');
|
|
375
373
|
|
|
376
374
|
formattedProvider.plans.forEach((plan) => {
|
|
377
|
-
Logger.println(`Plan ${
|
|
375
|
+
Logger.println(`Plan ${styleText('bold', plan.slug)}`);
|
|
378
376
|
plan.features.forEach(({ name, value }) => Logger.println(` ${name}: ${value}`));
|
|
379
377
|
|
|
380
378
|
if (plan.versions != null) {
|
|
381
|
-
Logger.println(
|
|
379
|
+
Logger.println(
|
|
380
|
+
` Available versions: ${plan.versions.map(({ version, isDefault }) => (isDefault ? `${version} (default)` : version)).join(', ')}`,
|
|
381
|
+
);
|
|
382
382
|
plan.versions.forEach(({ version, options }) => {
|
|
383
383
|
Logger.println(` Options for version ${version}:`);
|
|
384
384
|
options.forEach(({ name, enabledByDefault }) => {
|
|
@@ -391,8 +391,7 @@ export async function showProvider (params) {
|
|
|
391
391
|
}
|
|
392
392
|
}
|
|
393
393
|
|
|
394
|
-
export async function env
|
|
395
|
-
|
|
394
|
+
export async function env(params) {
|
|
396
395
|
const { org, format } = params.options;
|
|
397
396
|
const [addonIdOrRealId] = params.args;
|
|
398
397
|
|
|
@@ -402,11 +401,8 @@ export async function env (params) {
|
|
|
402
401
|
const envFromAddon = await getAllEnvVars({ id: ownerId, addonId }).then(sendToApi);
|
|
403
402
|
|
|
404
403
|
switch (format) {
|
|
405
|
-
|
|
406
404
|
case 'json': {
|
|
407
|
-
const envFromAddonJson = Object.fromEntries(
|
|
408
|
-
envFromAddon.map(({ name, value }) => [name, value]),
|
|
409
|
-
);
|
|
405
|
+
const envFromAddonJson = Object.fromEntries(envFromAddon.map(({ name, value }) => [name, value]));
|
|
410
406
|
Logger.println(JSON.stringify(envFromAddonJson, null, 2));
|
|
411
407
|
break;
|
|
412
408
|
}
|
|
@@ -1,40 +1,37 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { styleText } from 'node:util';
|
|
2
2
|
|
|
3
|
+
import { formatTable } from '../format-table.js';
|
|
4
|
+
import { Logger } from '../logger.js';
|
|
3
5
|
import * as AppConfig from '../models/app_configuration.js';
|
|
4
6
|
import * as Application from '../models/application.js';
|
|
5
7
|
import * as Organisation from '../models/organisation.js';
|
|
6
|
-
import { Logger } from '../logger.js';
|
|
7
|
-
import { formatTable } from '../format-table.js';
|
|
8
8
|
import { truncateWithEllipsis } from '../models/utils.js';
|
|
9
9
|
|
|
10
|
-
export async function list
|
|
10
|
+
export async function list(params) {
|
|
11
11
|
const { 'only-aliases': onlyAliases, json } = params.options;
|
|
12
12
|
|
|
13
13
|
const { apps } = await AppConfig.loadApplicationConf();
|
|
14
14
|
|
|
15
15
|
const formattedApps = formatApps(apps, onlyAliases, json);
|
|
16
16
|
Logger.println(formattedApps);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function formatApps (apps, onlyAliases, json) {
|
|
17
|
+
}
|
|
20
18
|
|
|
19
|
+
function formatApps(apps, onlyAliases, json) {
|
|
21
20
|
if (json) {
|
|
22
21
|
if (onlyAliases) {
|
|
23
22
|
apps = apps.map((a) => a.alias);
|
|
24
23
|
}
|
|
25
24
|
return JSON.stringify(apps, null, 2);
|
|
26
|
-
}
|
|
27
|
-
else {
|
|
25
|
+
} else {
|
|
28
26
|
if (onlyAliases) {
|
|
29
27
|
return apps.map((a) => a.alias).join('\n');
|
|
30
|
-
}
|
|
31
|
-
else {
|
|
28
|
+
} else {
|
|
32
29
|
return apps
|
|
33
30
|
.map((app) => {
|
|
34
31
|
const sshUrl = app.git_ssh_url ?? app.deploy_url.replace('https://', 'git+ssh://git@');
|
|
35
32
|
return [
|
|
36
33
|
`Application ${app.name}`,
|
|
37
|
-
` alias: ${
|
|
34
|
+
` alias: ${styleText('bold', app.alias)}`,
|
|
38
35
|
` ID: ${app.app_id}`,
|
|
39
36
|
` deployment URL: ${app.deploy_url}`,
|
|
40
37
|
` git+ssh URL: ${sshUrl}`,
|
|
@@ -45,14 +42,12 @@ function formatApps (apps, onlyAliases, json) {
|
|
|
45
42
|
}
|
|
46
43
|
}
|
|
47
44
|
|
|
48
|
-
export async function listAll
|
|
45
|
+
export async function listAll(params) {
|
|
49
46
|
const { org: orgaIdOrName, format } = params.options;
|
|
50
47
|
|
|
51
48
|
const linkedApps = await AppConfig.loadApplicationConf().then((conf) => conf.apps);
|
|
52
49
|
|
|
53
|
-
const ownerId =
|
|
54
|
-
? await Organisation.getId(orgaIdOrName)
|
|
55
|
-
: null;
|
|
50
|
+
const ownerId = orgaIdOrName != null && orgaIdOrName.orga_name !== '' ? await Organisation.getId(orgaIdOrName) : null;
|
|
56
51
|
|
|
57
52
|
const allAppsPerOrg = await Application.getAllApps(ownerId);
|
|
58
53
|
|
|
@@ -72,7 +67,6 @@ export async function listAll (params) {
|
|
|
72
67
|
}
|
|
73
68
|
case 'human':
|
|
74
69
|
default: {
|
|
75
|
-
|
|
76
70
|
const headers = ['APPLICATION ID', 'NAME', 'TYPE', 'ZONE', 'LOCAL ALIAS'];
|
|
77
71
|
const appNameWidth = 42;
|
|
78
72
|
|
|
@@ -85,29 +79,37 @@ export async function listAll (params) {
|
|
|
85
79
|
getPropertyMaxWidth(allApps, 'zone'),
|
|
86
80
|
getPropertyMaxWidth(allApps, 'alias'),
|
|
87
81
|
];
|
|
88
|
-
const formatTableWithColumnWidth = formatTable(columnWidths);
|
|
89
82
|
|
|
90
83
|
allAppsWithAliasPerOrg.forEach((org) => {
|
|
91
|
-
|
|
92
84
|
Logger.println();
|
|
93
85
|
|
|
94
86
|
const applicationsPlural = org.applications.length !== 1 ? 'applications' : 'application';
|
|
95
87
|
const punctuation = org.applications.length > 0 ? ':' : '';
|
|
96
88
|
|
|
97
|
-
Logger.println(
|
|
89
|
+
Logger.println(
|
|
90
|
+
styleText(
|
|
91
|
+
'blue',
|
|
92
|
+
`• Organization '${org.name}' (${org.id}) with ${org.applications.length} ${applicationsPlural}${punctuation}`,
|
|
93
|
+
),
|
|
94
|
+
);
|
|
98
95
|
|
|
99
96
|
if (org.applications.length > 0) {
|
|
100
97
|
Logger.println();
|
|
101
|
-
Logger.println(
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
98
|
+
Logger.println(
|
|
99
|
+
formatTable(
|
|
100
|
+
[
|
|
101
|
+
headers,
|
|
102
|
+
...org.applications.map((app) => [
|
|
103
|
+
app.app_id,
|
|
104
|
+
truncateWithEllipsis(appNameWidth, app.name),
|
|
105
|
+
app.type,
|
|
106
|
+
app.zone,
|
|
107
|
+
app.alias,
|
|
108
|
+
]),
|
|
109
|
+
],
|
|
110
|
+
columnWidths,
|
|
111
|
+
),
|
|
112
|
+
);
|
|
111
113
|
}
|
|
112
114
|
});
|
|
113
115
|
|
|
@@ -116,6 +118,6 @@ export async function listAll (params) {
|
|
|
116
118
|
}
|
|
117
119
|
}
|
|
118
120
|
|
|
119
|
-
function getPropertyMaxWidth
|
|
121
|
+
function getPropertyMaxWidth(array, propertyName) {
|
|
120
122
|
return Math.max(...array.map((o) => o[propertyName].length));
|
|
121
123
|
}
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { cancelDeployment, getAllDeployments } from '@clevercloud/client/esm/api/v2/application.js';
|
|
2
|
+
import { styleText } from 'node:util';
|
|
2
3
|
import { Logger } from '../logger.js';
|
|
3
|
-
import
|
|
4
|
+
import * as Application from '../models/application.js';
|
|
4
5
|
import { sendToApi } from '../models/send-to-api.js';
|
|
5
|
-
import colors from 'colors/safe.js';
|
|
6
6
|
|
|
7
|
-
export async function cancelDeploy
|
|
7
|
+
export async function cancelDeploy(params) {
|
|
8
8
|
const { alias, app: appIdOrName } = params.options;
|
|
9
9
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
10
10
|
|
|
11
11
|
const deployments = await getAllDeployments({ id: ownerId, appId, limit: 1 }).then(sendToApi);
|
|
12
12
|
|
|
13
|
-
if (deployments.length === 0 ||
|
|
13
|
+
if (deployments.length === 0 || deployments[0].action !== 'DEPLOY' || deployments[0].state !== 'WIP') {
|
|
14
14
|
throw new Error('There is no ongoing deployment for this application');
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
await cancelDeployment({ id: ownerId, appId, deploymentId: deployments[0].id }).then(sendToApi);
|
|
18
|
-
Logger.printSuccess(`Deployment ${
|
|
19
|
-
}
|
|
18
|
+
Logger.printSuccess(`Deployment ${styleText(['bold', 'green'], deployments[0].uuid)} successfully cancelled!`);
|
|
19
|
+
}
|