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,57 +1,77 @@
1
- import _ from 'lodash';
2
1
  import {
3
2
  addDependency,
4
3
  create as createApplication,
5
- get as getApplication,
6
4
  getAll as getAllApplications,
7
5
  getAllDependencies,
6
+ get as getApplication,
8
7
  redeploy as redeployApplication,
9
8
  remove as removeApplication,
10
9
  removeDependency,
11
10
  update as updateApplication,
12
11
  } from '@clevercloud/client/esm/api/v2/application.js';
13
- import cliparse from 'cliparse';
12
+ import { getAvailableInstances } from '@clevercloud/client/esm/api/v2/product.js';
14
13
  import { getSummary } from '@clevercloud/client/esm/api/v2/user.js';
15
- import colors from 'colors/safe.js';
16
-
17
- import * as AppConfiguration from './app_configuration.js';
14
+ import cliparse from 'cliparse';
15
+ import _ from 'lodash';
16
+ import { styleText } from 'node:util';
18
17
  import { confirmAnswer } from '../lib/prompts.js';
19
18
  import { Logger } from '../logger.js';
19
+ import * as AppConfiguration from './app_configuration.js';
20
+ import { resolveOwnerId } from './ids-resolver.js';
20
21
  import * as Organisation from './organisation.js';
22
+ import { sendToApi } from './send-to-api.js';
21
23
  import * as User from './user.js';
22
24
 
23
- import { sendToApi } from '../models/send-to-api.js';
24
- import { resolveOwnerId } from './ids-resolver.js';
25
- import { getAvailableInstances } from '@clevercloud/client/esm/api/v2/product.js';
26
-
27
- export function listAvailableTypes () {
28
- return cliparse.autocomplete.words(['docker', 'elixir', 'frankenphp', 'go', 'gradle', 'haskell', 'jar', 'linux', 'maven', 'meteor', 'node', 'php', 'play1', 'play2', 'python', 'ruby', 'rust', 'sbt', 'static', 'static-apache', 'v', 'war']);
29
- };
25
+ export function listAvailableTypes() {
26
+ return cliparse.autocomplete.words([
27
+ 'docker',
28
+ 'elixir',
29
+ 'frankenphp',
30
+ 'go',
31
+ 'gradle',
32
+ 'haskell',
33
+ 'jar',
34
+ 'linux',
35
+ 'maven',
36
+ 'meteor',
37
+ 'node',
38
+ 'php',
39
+ 'play1',
40
+ 'play2',
41
+ 'python',
42
+ 'ruby',
43
+ 'rust',
44
+ 'sbt',
45
+ 'static',
46
+ 'static-apache',
47
+ 'v',
48
+ 'war',
49
+ ]);
50
+ }
30
51
 
31
52
  export const AVAILABLE_ZONES = ['par', 'parhds', 'grahds', 'rbx', 'rbxhds', 'scw', 'ldn', 'mtl', 'sgp', 'syd', 'wsw'];
32
53
 
33
- export function listAvailableZones () {
54
+ export function listAvailableZones() {
34
55
  return cliparse.autocomplete.words(AVAILABLE_ZONES);
35
- };
56
+ }
36
57
 
37
- export function listAvailableAliases () {
58
+ export function listAvailableAliases() {
38
59
  return AppConfiguration.loadApplicationConf().then(({ apps }) => cliparse.autocomplete.words(_.map(apps, 'alias')));
39
- };
60
+ }
40
61
 
41
- export function listAvailableFlavors () {
62
+ export function listAvailableFlavors() {
42
63
  return ['pico', 'nano', 'XS', 'S', 'M', 'L', 'XL', '2XL', '3XL'];
43
- };
64
+ }
44
65
 
45
- async function getId (ownerId, dependency) {
66
+ async function getId(ownerId, dependency) {
46
67
  if (dependency.app_id) {
47
68
  return dependency.app_id;
48
69
  }
49
70
  const app = await getByName(ownerId, dependency.app_name);
50
71
  return app.id;
51
- };
52
-
53
- async function getInstanceType (type) {
72
+ }
54
73
 
74
+ async function getInstanceType(type) {
55
75
  // TODO: We should be able to use it without {}
56
76
  const types = await getAvailableInstances({}).then(sendToApi);
57
77
 
@@ -62,14 +82,12 @@ async function getInstanceType (type) {
62
82
  throw new Error(type + ' type does not exist.');
63
83
  }
64
84
  return instanceVariant;
65
- };
85
+ }
66
86
 
67
- export async function create (name, typeName, region, orgaIdOrName, github, isTask, envVars) {
87
+ export async function create(name, typeName, region, orgaIdOrName, github, isTask, envVars) {
68
88
  Logger.debug('Create the application…');
69
89
 
70
- const ownerId = (orgaIdOrName != null)
71
- ? await Organisation.getId(orgaIdOrName)
72
- : await User.getCurrentId();
90
+ const ownerId = orgaIdOrName != null ? await Organisation.getId(orgaIdOrName) : await User.getCurrentId();
73
91
 
74
92
  const instanceType = await getInstanceType(typeName);
75
93
 
@@ -95,24 +113,23 @@ export async function create (name, typeName, region, orgaIdOrName, github, isTa
95
113
  }
96
114
 
97
115
  return createApplication({ id: ownerId }, newApp).then(sendToApi);
98
- };
116
+ }
99
117
 
100
- export async function deleteApp (app, skipConfirmation) {
118
+ export async function deleteApp(app, skipConfirmation) {
101
119
  Logger.debug('Deleting app: ' + app.name + ' (' + app.id + ')');
102
120
 
103
121
  if (!skipConfirmation) {
104
122
  await confirmAnswer(
105
- `Deleting an application can't be undone, please type ${colors.green(app.name)} to confirm:`,
123
+ `Deleting an application can't be undone, please type ${styleText('green', app.name)} to confirm:`,
106
124
  'No confirmation, aborting application deletion',
107
125
  app.name,
108
126
  );
109
127
  }
110
128
 
111
129
  return removeApplication({ id: app.ownerId, appId: app.id }).then(sendToApi);
112
- };
113
-
114
- export async function getAllApps (ownerId) {
130
+ }
115
131
 
132
+ export async function getAllApps(ownerId) {
116
133
  const summary = await getSummary().then(sendToApi);
117
134
 
118
135
  const orgaWithApps = await Promise.all(
@@ -131,9 +148,9 @@ export async function getAllApps (ownerId) {
131
148
  );
132
149
 
133
150
  return orgaWithApps;
134
- };
151
+ }
135
152
 
136
- async function getApplicationsForOwner (ownerId) {
153
+ async function getApplicationsForOwner(ownerId) {
137
154
  const rawApplications = await getAllApplications({ id: ownerId }).then(sendToApi);
138
155
  return rawApplications.map((app) => {
139
156
  return {
@@ -149,52 +166,51 @@ async function getApplicationsForOwner (ownerId) {
149
166
  });
150
167
  }
151
168
 
152
- function getApplicationByName (apps, name) {
169
+ function getApplicationByName(apps, name) {
153
170
  const filteredApps = apps.filter((app) => app.name === name);
154
171
  if (filteredApps.length === 1) {
155
172
  return filteredApps[0];
156
- }
157
- else if (filteredApps.length === 0) {
173
+ } else if (filteredApps.length === 0) {
158
174
  throw new Error('Application not found');
159
175
  }
160
176
  throw new Error('Ambiguous application name');
161
- };
177
+ }
162
178
 
163
- async function getByName (ownerId, name) {
179
+ async function getByName(ownerId, name) {
164
180
  const apps = await getAllApplications({ id: ownerId }).then(sendToApi);
165
181
  return getApplicationByName(apps, name);
166
- };
182
+ }
167
183
 
168
- function addInstanceLifetime (app) {
184
+ function addInstanceLifetime(app) {
169
185
  // Patch to help config commands
170
186
  app.instanceLifetime = app.instance.lifetime;
171
187
  return app;
172
188
  }
173
189
 
174
- export function get (ownerId, appId) {
190
+ export function get(ownerId, appId) {
175
191
  Logger.debug(`Get information for the app: ${appId}`);
176
192
  return getApplication({ id: ownerId, appId }).then(sendToApi).then(addInstanceLifetime);
177
- };
193
+ }
178
194
 
179
- export function updateOptions (ownerId, appId, options) {
195
+ export function updateOptions(ownerId, appId, options) {
180
196
  Logger.debug(`Update app: ${appId}`);
181
197
  return updateApplication({ id: ownerId, appId }, options).then(sendToApi).then(addInstanceLifetime);
182
- };
198
+ }
183
199
 
184
- function getFromSelf (appId) {
200
+ function getFromSelf(appId) {
185
201
  Logger.debug(`Get information for the app: ${appId}`);
186
202
  // /self differs from /organisations only for this one:
187
203
  // it fallbacks to the organisations of which the user
188
204
  // is a member, if it doesn't belong to Personal Space.
189
205
  return getApplication({ appId }).then(sendToApi);
190
- };
206
+ }
191
207
 
192
208
  /**
193
209
  * @param {{app_id: string}|{app_name: string}} appIdOrName
194
210
  * @param {string} alias
195
211
  * @return {Promise<{appId: string, ownerId: string}>}
196
212
  */
197
- export async function resolveId (appIdOrName, alias) {
213
+ export async function resolveId(appIdOrName, alias) {
198
214
  if (appIdOrName != null && alias != null) {
199
215
  throw new Error('Only one of the `--app` or `--alias` options can be set at a time');
200
216
  }
@@ -245,32 +261,28 @@ export async function resolveId (appIdOrName, alias) {
245
261
  throw new Error('Ambiguous application name, use the `--app` option with one of the IDs above');
246
262
  }
247
263
 
248
- export async function linkRepo (app, orgaIdOrName, alias, ignoreParentConfig) {
264
+ export async function linkRepo(app, orgaIdOrName, alias, ignoreParentConfig) {
249
265
  Logger.debug(`Linking current repository to the app: ${app.app_id || app.app_name}`);
250
266
 
251
- const ownerId = (orgaIdOrName != null)
252
- ? await Organisation.getId(orgaIdOrName)
253
- : await User.getCurrentId();
267
+ const ownerId = orgaIdOrName != null ? await Organisation.getId(orgaIdOrName) : await User.getCurrentId();
254
268
 
255
- const appData = (app.app_id != null)
256
- ? await getFromSelf(app.app_id)
257
- : await getByName(ownerId, app.app_name);
269
+ const appData = app.app_id != null ? await getFromSelf(app.app_id) : await getByName(ownerId, app.app_name);
258
270
 
259
271
  return AppConfiguration.addLinkedApplication(appData, alias, ignoreParentConfig);
260
- };
272
+ }
261
273
 
262
- export function unlinkRepo (alias) {
274
+ export function unlinkRepo(alias) {
263
275
  Logger.debug(`Unlinking current repository from the app: ${alias}`);
264
276
  return AppConfiguration.removeLinkedApplication({ alias });
265
- };
277
+ }
266
278
 
267
- export function redeploy (ownerId, appId, commit, withoutCache) {
279
+ export function redeploy(ownerId, appId, commit, withoutCache) {
268
280
  Logger.debug(`Redeploying the app: ${appId}`);
269
- const useCache = (withoutCache) ? 'no' : null;
281
+ const useCache = withoutCache ? 'no' : null;
270
282
  return redeployApplication({ id: ownerId, appId, commit, useCache }).then(sendToApi);
271
- };
283
+ }
272
284
 
273
- export function mergeScalabilityParameters (scalabilityParameters, instance) {
285
+ export function mergeScalabilityParameters(scalabilityParameters, instance) {
274
286
  const flavors = listAvailableFlavors();
275
287
 
276
288
  if (scalabilityParameters.minFlavor) {
@@ -281,8 +293,10 @@ export function mergeScalabilityParameters (scalabilityParameters, instance) {
281
293
  }
282
294
  if (scalabilityParameters.maxFlavor) {
283
295
  instance.maxFlavor = scalabilityParameters.maxFlavor;
284
- if (flavors.indexOf(instance.minFlavor) > flavors.indexOf(instance.maxFlavor)
285
- && scalabilityParameters.minFlavor == null) {
296
+ if (
297
+ flavors.indexOf(instance.minFlavor) > flavors.indexOf(instance.maxFlavor) &&
298
+ scalabilityParameters.minFlavor == null
299
+ ) {
286
300
  instance.minFlavor = instance.maxFlavor;
287
301
  }
288
302
  }
@@ -300,9 +314,9 @@ export function mergeScalabilityParameters (scalabilityParameters, instance) {
300
314
  }
301
315
  }
302
316
  return instance;
303
- };
317
+ }
304
318
 
305
- export async function setScalability (appId, ownerId, scalabilityParameters, buildFlavor) {
319
+ export async function setScalability(appId, ownerId, scalabilityParameters, buildFlavor) {
306
320
  Logger.info('Scaling the app: ' + appId);
307
321
 
308
322
  const app = await getApplication({ id: ownerId, appId }).then(sendToApi);
@@ -314,19 +328,18 @@ export async function setScalability (appId, ownerId, scalabilityParameters, bui
314
328
  const newConfig = mergeScalabilityParameters(scalabilityParameters, instance);
315
329
 
316
330
  if (buildFlavor != null) {
317
- newConfig.separateBuild = (buildFlavor !== 'disabled');
331
+ newConfig.separateBuild = buildFlavor !== 'disabled';
318
332
  if (buildFlavor !== 'disabled') {
319
333
  newConfig.buildFlavor = buildFlavor;
320
- }
321
- else {
334
+ } else {
322
335
  Logger.info('No build size given, disabling dedicated build instance');
323
336
  }
324
337
  }
325
338
 
326
339
  return updateApplication({ id: ownerId, appId }, newConfig).then(sendToApi);
327
- };
340
+ }
328
341
 
329
- export async function listDependencies (ownerId, appId, showAll) {
342
+ export async function listDependencies(ownerId, appId, showAll) {
330
343
  const applicationDeps = await getAllDependencies({ id: ownerId, appId }).then(sendToApi);
331
344
 
332
345
  if (!showAll) {
@@ -342,12 +355,12 @@ export async function listDependencies (ownerId, appId, showAll) {
342
355
  });
343
356
  }
344
357
 
345
- export async function link (ownerId, appId, dependency) {
358
+ export async function link(ownerId, appId, dependency) {
346
359
  const dependencyId = await getId(ownerId, dependency);
347
360
  return addDependency({ id: ownerId, appId, dependencyId }).then(sendToApi);
348
- };
361
+ }
349
362
 
350
- export async function unlink (ownerId, appId, dependency) {
363
+ export async function unlink(ownerId, appId, dependency) {
351
364
  const dependencyId = await getId(ownerId, dependency);
352
365
  return removeDependency({ id: ownerId, appId, dependencyId }).then(sendToApi);
353
- };
366
+ }
@@ -1,6 +1,6 @@
1
1
  import cliparse from 'cliparse';
2
- import { Logger } from '../logger.js';
3
2
  import dedent from 'dedent';
3
+ import { Logger } from '../logger.js';
4
4
 
5
5
  const CONFIG_KEYS = [
6
6
  { id: 'name', name: 'name', displayName: 'Name', kind: 'string' },
@@ -12,17 +12,15 @@ const CONFIG_KEYS = [
12
12
  { id: 'task', name: 'instanceLifetime', displayName: 'Deploy an application as a Clever Task', kind: 'task' },
13
13
  ];
14
14
 
15
- export function listAvailableIds (asText = false) {
15
+ export function listAvailableIds(asText = false) {
16
16
  const ids = CONFIG_KEYS.map((config) => config.id);
17
17
  if (asText) {
18
- return new Intl
19
- .ListFormat('en', { style: 'short', type: 'disjunction' })
20
- .format(ids);
18
+ return new Intl.ListFormat('en', { style: 'short', type: 'disjunction' }).format(ids);
21
19
  }
22
20
  return ids;
23
21
  }
24
22
 
25
- export function getById (id) {
23
+ export function getById(id) {
26
24
  const config = CONFIG_KEYS.find((config) => config.id === id);
27
25
  if (config != null) {
28
26
  return config;
@@ -33,7 +31,7 @@ export function getById (id) {
33
31
  `);
34
32
  }
35
33
 
36
- export function formatValue (config, value) {
34
+ export function formatValue(config, value) {
37
35
  switch (config.kind) {
38
36
  case 'bool': {
39
37
  return value;
@@ -53,7 +51,7 @@ export function formatValue (config, value) {
53
51
  }
54
52
  }
55
53
 
56
- export function parse (config, value) {
54
+ export function parse(config, value) {
57
55
  switch (config.kind) {
58
56
  case 'bool':
59
57
  case 'inverted-bool':
@@ -63,16 +61,16 @@ export function parse (config, value) {
63
61
  throw new Error('Invalid configuration value, it must be a boolean (true or false)');
64
62
  }
65
63
  if (config.kind === 'bool') {
66
- return (value === 'true');
64
+ return value === 'true';
67
65
  }
68
66
  if (config.kind === 'inverted-bool') {
69
- return (value === 'false');
67
+ return value === 'false';
70
68
  }
71
69
  if (config.kind === 'force-https') {
72
- return (value === 'true') ? 'ENABLED' : 'DISABLED';
70
+ return value === 'true' ? 'ENABLED' : 'DISABLED';
73
71
  }
74
72
  if (config.kind === 'task') {
75
- return (value === 'false') ? 'REGULAR' : 'TASK';
73
+ return value === 'false' ? 'REGULAR' : 'TASK';
76
74
  }
77
75
  return;
78
76
  }
@@ -82,13 +80,11 @@ export function parse (config, value) {
82
80
  }
83
81
  }
84
82
 
85
- export function getUpdateOptions () {
86
- return CONFIG_KEYS
87
- .map((config) => getConfigOptions(config))
88
- .reduce((a, b) => [...a, ...b], []);
83
+ export function getUpdateOptions() {
84
+ return CONFIG_KEYS.map((config) => getConfigOptions(config)).reduce((a, b) => [...a, ...b], []);
89
85
  }
90
86
 
91
- function getConfigOptions (config) {
87
+ function getConfigOptions(config) {
92
88
  switch (config.kind) {
93
89
  case 'bool':
94
90
  case 'inverted-bool':
@@ -100,21 +96,17 @@ function getConfigOptions (config) {
100
96
  ];
101
97
  }
102
98
  default: {
103
- return [
104
- cliparse.option(`${config.id}`, { description: `Set ${config.id}` }),
105
- ];
99
+ return [cliparse.option(`${config.id}`, { description: `Set ${config.id}` })];
106
100
  }
107
101
  }
108
102
  }
109
103
 
110
- export function parseOptions (options) {
111
- const newOptions = CONFIG_KEYS
112
- .map((config) => parseConfigOption(config, options))
113
- .filter((a) => a != null);
104
+ export function parseOptions(options) {
105
+ const newOptions = CONFIG_KEYS.map((config) => parseConfigOption(config, options)).filter((a) => a != null);
114
106
  return Object.fromEntries(newOptions);
115
107
  }
116
108
 
117
- function parseConfigOption (config, options) {
109
+ function parseConfigOption(config, options) {
118
110
  switch (config.kind) {
119
111
  case 'bool':
120
112
  case 'inverted-bool':
@@ -144,12 +136,12 @@ function parseConfigOption (config, options) {
144
136
  }
145
137
  }
146
138
 
147
- export function printValue (app, id) {
139
+ export function printValue(app, id) {
148
140
  const config = getById(id);
149
141
  Logger.println(formatValue(config, app[config.name]));
150
142
  }
151
143
 
152
- export function printAllValues (app) {
144
+ export function printAllValues(app) {
153
145
  console.table(
154
146
  Object.fromEntries(
155
147
  CONFIG_KEYS.map((config) => {
@@ -1,12 +1,7 @@
1
1
  import { promises as fs } from 'node:fs';
2
2
  import path from 'node:path';
3
-
4
- import commonEnv from 'common-env';
5
- import mkdirp from 'mkdirp';
6
3
  import xdg from 'xdg';
7
-
8
4
  import { Logger } from '../logger.js';
9
- const env = commonEnv(Logger);
10
5
 
11
6
  const CONFIG_FILES = {
12
7
  MAIN: 'clever-tools.json',
@@ -14,22 +9,22 @@ const CONFIG_FILES = {
14
9
  EXPERIMENTAL_FEATURES_FILE: 'clever-tools-experimental-features.json',
15
10
  };
16
11
 
17
- function getConfigDir () {
18
- return (process.platform === 'win32')
12
+ function getConfigDir() {
13
+ return process.platform === 'win32'
19
14
  ? path.resolve(process.env.APPDATA, 'clever-cloud')
20
15
  : xdg.basedir.configPath('clever-cloud');
21
16
  }
22
17
 
23
- function getConfigPath (configFile) {
18
+ function getConfigPath(configFile) {
24
19
  return path.resolve(getConfigDir(), configFile);
25
20
  }
26
21
 
27
22
  // Every function which need 'clever-cloud' directory, need to call it before
28
- async function ensureConfigDirExists () {
29
- await mkdirp(getConfigDir(), { mode: 0o700 });
23
+ async function ensureConfigDirExists() {
24
+ await fs.mkdir(getConfigDir(), { mode: 0o700, recursive: true });
30
25
  }
31
26
 
32
- export async function loadOAuthConf () {
27
+ export async function loadOAuthConf() {
33
28
  Logger.debug('Load configuration from environment variables');
34
29
  if (process.env.CLEVER_TOKEN != null && process.env.CLEVER_SECRET != null) {
35
30
  return {
@@ -47,8 +42,7 @@ export async function loadOAuthConf () {
47
42
  token,
48
43
  secret,
49
44
  };
50
- }
51
- catch (error) {
45
+ } catch (error) {
52
46
  Logger.info(`Cannot load configuration from ${conf.CONFIGURATION_FILE}\n${error.message}`);
53
47
  return {
54
48
  source: 'none',
@@ -56,24 +50,22 @@ export async function loadOAuthConf () {
56
50
  }
57
51
  }
58
52
 
59
- export async function writeOAuthConf (oauthData) {
53
+ export async function writeOAuthConf(oauthData) {
60
54
  Logger.debug('Write the tokens in the configuration file…');
61
55
  try {
62
56
  await ensureConfigDirExists();
63
57
  await fs.writeFile(conf.CONFIGURATION_FILE, JSON.stringify(oauthData));
64
- }
65
- catch (error) {
58
+ } catch (error) {
66
59
  throw new Error(`Cannot write configuration to ${conf.CONFIGURATION_FILE}\n${error.message}`);
67
60
  }
68
61
  }
69
62
 
70
- export async function loadIdsCache () {
63
+ export async function loadIdsCache() {
71
64
  const cachePath = getConfigPath(CONFIG_FILES.IDS_CACHE);
72
65
  try {
73
66
  const rawFile = await fs.readFile(cachePath);
74
67
  return JSON.parse(rawFile);
75
- }
76
- catch (error) {
68
+ } catch (error) {
77
69
  Logger.info(`Cannot load IDs cache from ${cachePath}\n${error.message}`);
78
70
  return {
79
71
  owners: {},
@@ -82,25 +74,23 @@ export async function loadIdsCache () {
82
74
  }
83
75
  }
84
76
 
85
- export async function writeIdsCache (ids) {
77
+ export async function writeIdsCache(ids) {
86
78
  const cachePath = getConfigPath(CONFIG_FILES.IDS_CACHE);
87
79
  const idsJson = JSON.stringify(ids);
88
80
  try {
89
81
  await ensureConfigDirExists();
90
82
  await fs.writeFile(cachePath, idsJson);
91
- }
92
- catch (error) {
83
+ } catch (error) {
93
84
  throw new Error(`Cannot write IDs cache to ${cachePath}\n${error.message}`);
94
85
  }
95
86
  }
96
87
 
97
- export async function getFeatures () {
88
+ export async function getFeatures() {
98
89
  Logger.debug('Get features configuration from ' + conf.EXPERIMENTAL_FEATURES_FILE);
99
90
  try {
100
91
  const rawFile = await fs.readFile(conf.EXPERIMENTAL_FEATURES_FILE);
101
92
  return JSON.parse(rawFile);
102
- }
103
- catch (error) {
93
+ } catch (error) {
104
94
  if (error.code !== 'ENOENT') {
105
95
  throw new Error(`Cannot get experimental features configuration from ${conf.EXPERIMENTAL_FEATURES_FILE}`);
106
96
  }
@@ -108,20 +98,19 @@ export async function getFeatures () {
108
98
  }
109
99
  }
110
100
 
111
- export async function setFeature (feature, value) {
101
+ export async function setFeature(feature, value) {
112
102
  const currentFeatures = await getFeatures();
113
103
  const newFeatures = { ...currentFeatures, ...{ [feature]: value } };
114
104
 
115
105
  try {
116
106
  await ensureConfigDirExists();
117
107
  await fs.writeFile(conf.EXPERIMENTAL_FEATURES_FILE, JSON.stringify(newFeatures, null, 2));
118
- }
119
- catch (error) {
108
+ } catch {
120
109
  throw new Error(`Cannot write experimental features configuration to ${conf.EXPERIMENTAL_FEATURES_FILE}`);
121
110
  }
122
111
  }
123
112
 
124
- export const conf = env.getOrElseAll({
113
+ const defaultConf = {
125
114
  API_HOST: 'https://api.clever-cloud.com',
126
115
  AUTH_BRIDGE_HOST: 'https://api-bridge.clever-cloud.com',
127
116
  SSH_GATEWAY: 'ssh@sshgateway-clevercloud-customers.services.clever-cloud.com',
@@ -134,9 +123,18 @@ export const conf = env.getOrElseAll({
134
123
  CONFIGURATION_FILE: getConfigPath(CONFIG_FILES.MAIN),
135
124
  EXPERIMENTAL_FEATURES_FILE: getConfigPath(CONFIG_FILES.EXPERIMENTAL_FEATURES_FILE),
136
125
 
137
- API_DOC_URL: 'https://www.clever-cloud.com/developers/api',
138
- DOC_URL: 'https://www.clever-cloud.com/developers/doc',
126
+ API_DOC_URL: 'https://www.clever.cloud/developers/api',
127
+ DOC_URL: 'https://www.clever.cloud/developers/doc',
139
128
  CONSOLE_URL: 'https://console.clever-cloud.com',
140
129
  CONSOLE_TOKEN_URL: 'https://console.clever-cloud.com/cli-oauth',
141
130
  GOTO_URL: 'https://console.clever-cloud.com/goto',
142
- });
131
+ };
132
+
133
+ export const conf = Object.fromEntries(
134
+ Object.entries(defaultConf).map(([name, value]) => {
135
+ if (process.env[name] != null) {
136
+ return [name, process.env[name]];
137
+ }
138
+ return [name, value];
139
+ }),
140
+ );