clever-tools 3.14.0 → 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.
Files changed (93) hide show
  1. package/README.md +130 -37
  2. package/bin/clever.js +1427 -718
  3. package/package.json +61 -77
  4. package/src/clever-client/auth-bridge.js +9 -12
  5. package/src/clever-client/ng.js +1 -1
  6. package/src/clever-client/operators.js +7 -7
  7. package/src/command-options.js +6 -6
  8. package/src/command-promise-handler.js +6 -6
  9. package/src/commands/accesslogs.js +38 -29
  10. package/src/commands/activity.js +62 -40
  11. package/src/commands/addon.js +65 -69
  12. package/src/commands/applications.js +33 -31
  13. package/src/commands/cancel-deploy.js +7 -7
  14. package/src/commands/config.js +9 -7
  15. package/src/commands/console.js +3 -3
  16. package/src/commands/create.js +32 -31
  17. package/src/commands/curl.js +14 -36
  18. package/src/commands/database.js +10 -30
  19. package/src/commands/delete.js +8 -8
  20. package/src/commands/deploy.js +60 -46
  21. package/src/commands/diag.js +27 -25
  22. package/src/commands/domain.js +89 -91
  23. package/src/commands/drain.js +26 -14
  24. package/src/commands/emails.js +24 -28
  25. package/src/commands/env.js +18 -18
  26. package/src/commands/features.js +12 -18
  27. package/src/commands/keycloak.js +11 -11
  28. package/src/commands/kv.js +13 -13
  29. package/src/commands/link.js +9 -8
  30. package/src/commands/login.js +19 -27
  31. package/src/commands/logout.js +1 -1
  32. package/src/commands/logs.js +20 -12
  33. package/src/commands/makeDefault.js +5 -5
  34. package/src/commands/matomo.js +7 -7
  35. package/src/commands/metabase.js +9 -9
  36. package/src/commands/ng.js +32 -32
  37. package/src/commands/notify-email.js +11 -15
  38. package/src/commands/open.js +1 -1
  39. package/src/commands/otoroshi.js +11 -11
  40. package/src/commands/profile.js +8 -8
  41. package/src/commands/published-config.js +11 -11
  42. package/src/commands/restart.js +15 -8
  43. package/src/commands/scale.js +25 -16
  44. package/src/commands/service.js +18 -9
  45. package/src/commands/ssh-keys.js +25 -29
  46. package/src/commands/ssh.js +1 -2
  47. package/src/commands/status.js +31 -22
  48. package/src/commands/stop.js +3 -3
  49. package/src/commands/tcp-redirs.js +27 -26
  50. package/src/commands/tokens.js +44 -43
  51. package/src/commands/unlink.js +7 -5
  52. package/src/commands/version.js +2 -4
  53. package/src/commands/webhooks.js +8 -10
  54. package/src/experimental-features.js +7 -5
  55. package/src/format-table.js +46 -15
  56. package/src/initial-setup.js +2 -32
  57. package/src/initial-update-notifier.js +18 -0
  58. package/src/lib/has-param.js +16 -0
  59. package/src/lib/ng-print.js +21 -24
  60. package/src/lib/operator-commands.js +58 -53
  61. package/src/lib/prompts.js +5 -5
  62. package/src/lib/slugify.js +1 -1
  63. package/src/logger.js +24 -22
  64. package/src/models/activity.js +3 -3
  65. package/src/models/addon.js +52 -53
  66. package/src/models/app_configuration.js +34 -32
  67. package/src/models/application.js +90 -77
  68. package/src/models/application_configuration.js +19 -27
  69. package/src/models/configuration.js +30 -32
  70. package/src/models/deployments.js +9 -19
  71. package/src/models/domain.js +10 -9
  72. package/src/models/drain.js +16 -15
  73. package/src/models/exit-strategy-option.js +15 -5
  74. package/src/models/fs-utils.js +4 -3
  75. package/src/models/git.js +26 -32
  76. package/src/models/ids-resolver.js +37 -41
  77. package/src/models/isomorphic-http-with-agent.js +3 -3
  78. package/src/models/json-array.js +5 -6
  79. package/src/models/log-v4.js +44 -52
  80. package/src/models/log.js +40 -45
  81. package/src/models/namespaces.js +4 -4
  82. package/src/models/ng-resources.js +49 -39
  83. package/src/models/ng.js +41 -40
  84. package/src/models/node-dns-resolver.js +3 -3
  85. package/src/models/notification.js +7 -10
  86. package/src/models/operator.js +8 -10
  87. package/src/models/organisation.js +5 -8
  88. package/src/models/send-to-api.js +18 -11
  89. package/src/models/user.js +6 -5
  90. package/src/models/utils.js +5 -8
  91. package/src/models/variables.js +18 -20
  92. package/src/parsers.js +34 -34
  93. package/src/load-package-json.cjs +0 -5
@@ -1,43 +1,58 @@
1
- import colors from 'colors/safe.js';
2
- import moment from 'moment';
1
+ import { styleText } from 'node:util';
3
2
 
4
- import * as Activity from '../models/activity.js';
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 { Deferred } from '../models/utils.js';
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 (state, isLast) {
27
+ function getColoredState(state, isLast) {
13
28
  if (state === 'OK') {
14
- return colors.bold.green(state);
29
+ return styleText(['bold', 'green'], state);
15
30
  }
16
31
  if (state === 'FAIL' || state === 'CANCELLED') {
17
- return colors.bold.red(state);
32
+ return styleText(['bold', 'red'], state);
18
33
  }
19
34
  if (state === 'WIP' && !isLast) {
20
- return colors.bold.red('FAIL');
35
+ return styleText(['bold', 'red'], 'FAIL');
21
36
  }
22
37
  if (state === 'WIP' && isLast) {
23
- return colors.bold.blue('IN PROGRESS');
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 formatActivityTable = formatTable([
31
- moment().format(),
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 (event) {
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 (event) {
52
- return formatActivityTable([
66
+ function formatActivityLine(event) {
67
+ return formatTable(
53
68
  [
54
- moment(event.date).format(),
55
- event.uuid,
56
- getColoredState(event.state, event.isLast),
57
- event.action,
58
- event?.commit?.substring(0, 8) ?? 'N/A',
59
- event.cause,
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 (ev) {
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 (previousEvent, event, handler) {
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 (previousEvent, newEvent, handler) {
90
- const { event, date, data: { uuid, state, action, commit, cause } } = newEvent;
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 (format, follow) {
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 (params) {
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
- .reverse()
141
- .map((event, index, all) => {
142
- const isLast = index === all.length - 1;
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((previousEvent, newEvent) => handleEvent(previousEvent, newEvent, handler), {});
167
+ let lastEvent = reversedArrayWithIndex.reduce(
168
+ (previousEvent, newEvent) => handleEvent(previousEvent, newEvent, handler),
169
+ {},
170
+ );
149
171
 
150
172
  if (!follow) {
151
173
  handler.end();
@@ -1,22 +1,20 @@
1
- import colors from 'colors/safe.js';
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 { formatTable as initFormatTable } from '../format-table.js';
6
- import { Logger } from '../logger.js';
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 { toNameEqualsValueString } from '@clevercloud/client/esm/utils/env-vars.js';
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 (params) {
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
- colors.bold.green(addon.name),
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 (params) {
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 = (orgaIdOrName != null)
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('The specified application does not belong to the specified organisation. Ignoring the `--org` option');
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(format, newAddon, providerName, `Add-on created and linked to application ${linkedAppAlias} successfully!`);
97
- }
98
- else {
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 (format, addon, providerName, message) {
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
- ${colors.yellow('You can easily use Materia KV with \'redis-cli\', with such commands:')}
136
- ${colors.blue(`source <(clever addon env ${addon.id} -F shell)`)}
137
- ${colors.blue('redis-cli -h $KV_HOST -p $KV_PORT --tls')}
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 += WIP_PROVIDERS[providerName].status === 'alpha'
177
- ? '. Don\'t store sensitive or production grade data.'
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((WIP_PROVIDERS[providerName] != null)
192
- ? { ...jsonAddon, availability: WIP_PROVIDERS[providerName].status, message: statusMessage }
193
- : jsonAddon);
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("An initial account has been created, you'll be invited to change the password at first login:");
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(` - Temporary password: ${addon.env.find((e) => e.name === 'CC_KEYCLOAK_ADMIN_DEFAULT_PASSWORD').value}`);
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('An initial account has been created, change the password at first login (Security -> Administrators -> Edit user):');
229
- Logger.println(` - Admin user name: ${addon.env.find((e) => e.name === 'CC_OTOROSHI_INITIAL_ADMIN_LOGIN').value}`);
230
- Logger.println(` - Initial password: ${addon.env.find((e) => e.name === 'CC_OTOROSHI_INITIAL_ADMIN_PASSWORD').value}`);
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(colors.yellow(`/!\\ ${statusMessage}`));
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 (params) {
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 (params) {
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 (params) {
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 (params) {
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(colors.bold(formattedProvider.id));
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 ${colors.bold(plan.slug)}`);
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(` Available versions: ${plan.versions.map(({ version, isDefault }) => isDefault ? `${version} (default)` : version).join(', ')}`);
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 (params) {
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 colors from 'colors/safe.js';
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 (params) {
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: ${colors.bold(app.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 (params) {
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 = (orgaIdOrName != null && orgaIdOrName.orga_name !== '')
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(colors.blue(`• Organization '${org.name}' (${org.id}) with ${org.applications.length} ${applicationsPlural}${punctuation}`));
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(formatTableWithColumnWidth([
102
- headers,
103
- ...org.applications.map((app) => [
104
- app.app_id,
105
- truncateWithEllipsis(appNameWidth, app.name),
106
- app.type,
107
- app.zone,
108
- app.alias,
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 (array, propertyName) {
121
+ function getPropertyMaxWidth(array, propertyName) {
120
122
  return Math.max(...array.map((o) => o[propertyName].length));
121
123
  }
@@ -1,19 +1,19 @@
1
- import * as Application from '../models/application.js';
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 { getAllDeployments, cancelDeployment } from '@clevercloud/client/esm/api/v2/application.js';
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 (params) {
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 || (deployments[0].action !== 'DEPLOY' || deployments[0].state !== 'WIP')) {
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 ${colors.bold.green(deployments[0].uuid)} successfully cancelled!`);
19
- };
18
+ Logger.printSuccess(`Deployment ${styleText(['bold', 'green'], deployments[0].uuid)} successfully cancelled!`);
19
+ }