clever-tools 4.4.1 → 4.5.1

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 (291) hide show
  1. package/bin/clever.js +208 -2125
  2. package/package.json +33 -25
  3. package/src/commands/README.md +65 -0
  4. package/src/commands/accesslogs/accesslogs.command.js +158 -0
  5. package/src/commands/accesslogs/accesslogs.docs.md +20 -0
  6. package/src/commands/{activity.js → activity/activity.command.js} +74 -44
  7. package/src/commands/activity/activity.docs.md +19 -0
  8. package/src/commands/addon/addon.args.js +14 -0
  9. package/src/commands/addon/addon.command.js +7 -0
  10. package/src/commands/addon/addon.create.command.js +236 -0
  11. package/src/commands/addon/addon.delete.command.js +32 -0
  12. package/src/commands/addon/addon.docs.md +156 -0
  13. package/src/commands/addon/addon.env.command.js +50 -0
  14. package/src/commands/addon/addon.list.command.js +55 -0
  15. package/src/commands/addon/addon.providers.command.js +45 -0
  16. package/src/commands/addon/addon.providers.show.command.js +99 -0
  17. package/src/commands/addon/addon.rename.command.js +24 -0
  18. package/src/commands/applications/applications.command.js +59 -0
  19. package/src/commands/applications/applications.docs.md +31 -0
  20. package/src/commands/applications/applications.list.command.js +100 -0
  21. package/src/commands/cancel-deploy/cancel-deploy.command.js +30 -0
  22. package/src/commands/cancel-deploy/cancel-deploy.docs.md +16 -0
  23. package/src/commands/config/config.args.js +10 -0
  24. package/src/commands/config/config.command.js +20 -0
  25. package/src/commands/config/config.docs.md +86 -0
  26. package/src/commands/config/config.get.command.js +21 -0
  27. package/src/commands/config/config.set.command.js +38 -0
  28. package/src/commands/config/config.update.command.js +88 -0
  29. package/src/commands/console/console.command.js +31 -0
  30. package/src/commands/console/console.docs.md +16 -0
  31. package/src/commands/create/create.command.js +173 -0
  32. package/src/commands/create/create.docs.md +27 -0
  33. package/src/commands/{curl.js → curl/curl.command.js} +10 -3
  34. package/src/commands/curl/curl.docs.md +9 -0
  35. package/src/commands/database/database.args.js +8 -0
  36. package/src/commands/database/database.backups.command.js +60 -0
  37. package/src/commands/database/database.backups.download.command.js +55 -0
  38. package/src/commands/database/database.command.js +10 -0
  39. package/src/commands/database/database.docs.md +52 -0
  40. package/src/commands/delete/delete.command.js +43 -0
  41. package/src/commands/delete/delete.docs.md +17 -0
  42. package/src/commands/deploy/deploy.command.js +166 -0
  43. package/src/commands/deploy/deploy.docs.md +22 -0
  44. package/src/commands/diag/diag.command.js +136 -0
  45. package/src/commands/diag/diag.docs.md +15 -0
  46. package/src/commands/domain/domain.add.command.js +25 -0
  47. package/src/commands/domain/domain.args.js +8 -0
  48. package/src/commands/domain/domain.command.js +38 -0
  49. package/src/commands/domain/domain.diag.command.js +204 -0
  50. package/src/commands/domain/domain.docs.md +143 -0
  51. package/src/commands/domain/domain.favourite.command.js +35 -0
  52. package/src/commands/domain/domain.favourite.set.command.js +24 -0
  53. package/src/commands/domain/domain.favourite.unset.command.js +23 -0
  54. package/src/commands/domain/domain.overview.command.js +157 -0
  55. package/src/commands/domain/domain.rm.command.js +25 -0
  56. package/src/commands/drain/drain.args.js +8 -0
  57. package/src/commands/drain/drain.command.js +56 -0
  58. package/src/commands/drain/drain.create.command.js +128 -0
  59. package/src/commands/drain/drain.disable.command.js +27 -0
  60. package/src/commands/drain/drain.docs.md +129 -0
  61. package/src/commands/drain/drain.enable.command.js +27 -0
  62. package/src/commands/drain/drain.get.command.js +38 -0
  63. package/src/commands/drain/drain.remove.command.js +26 -0
  64. package/src/commands/emails/emails.add.command.js +32 -0
  65. package/src/commands/emails/emails.args.js +8 -0
  66. package/src/commands/emails/emails.command.js +35 -0
  67. package/src/commands/emails/emails.docs.md +79 -0
  68. package/src/commands/emails/emails.open.command.js +15 -0
  69. package/src/commands/emails/emails.primary.command.js +29 -0
  70. package/src/commands/emails/emails.remove-all.command.js +54 -0
  71. package/src/commands/emails/emails.remove.command.js +25 -0
  72. package/src/commands/env/env.command.js +77 -0
  73. package/src/commands/env/env.docs.md +99 -0
  74. package/src/commands/env/env.import-vars.command.js +45 -0
  75. package/src/commands/env/env.import.command.js +29 -0
  76. package/src/commands/env/env.rm.command.js +25 -0
  77. package/src/commands/env/env.set.command.js +32 -0
  78. package/src/commands/features/features.args.js +9 -0
  79. package/src/commands/features/features.command.js +7 -0
  80. package/src/commands/features/features.disable.command.js +25 -0
  81. package/src/commands/features/features.docs.md +71 -0
  82. package/src/commands/features/features.enable.command.js +32 -0
  83. package/src/commands/features/features.info.command.js +27 -0
  84. package/src/commands/features/features.list.command.js +49 -0
  85. package/src/commands/global.args.js +45 -0
  86. package/src/commands/global.commands.js +461 -0
  87. package/src/commands/global.options.js +186 -0
  88. package/src/commands/help/help.command.js +9 -0
  89. package/src/commands/help/help.docs.md +9 -0
  90. package/src/commands/k8s/k8s.add-persistent-storage.command.js +39 -0
  91. package/src/commands/k8s/k8s.args.js +9 -0
  92. package/src/commands/k8s/k8s.command.js +12 -0
  93. package/src/commands/k8s/k8s.create.command.js +86 -0
  94. package/src/commands/k8s/k8s.delete.command.js +40 -0
  95. package/src/commands/k8s/k8s.docs.md +131 -0
  96. package/src/commands/k8s/k8s.get-kubeconfig.command.js +32 -0
  97. package/src/commands/k8s/k8s.get.command.js +43 -0
  98. package/src/commands/k8s/k8s.list.command.js +38 -0
  99. package/src/commands/keycloak/keycloak.command.js +17 -0
  100. package/src/commands/keycloak/keycloak.disable-ng.command.js +13 -0
  101. package/src/commands/keycloak/keycloak.docs.md +197 -0
  102. package/src/commands/keycloak/keycloak.enable-ng.command.js +13 -0
  103. package/src/commands/keycloak/keycloak.get.command.js +17 -0
  104. package/src/commands/keycloak/keycloak.open.command.js +13 -0
  105. package/src/commands/keycloak/keycloak.open.logs.command.js +13 -0
  106. package/src/commands/keycloak/keycloak.open.webui.command.js +13 -0
  107. package/src/commands/keycloak/keycloak.rebuild.command.js +13 -0
  108. package/src/commands/keycloak/keycloak.restart.command.js +13 -0
  109. package/src/commands/keycloak/keycloak.version.check.command.js +17 -0
  110. package/src/commands/keycloak/keycloak.version.command.js +17 -0
  111. package/src/commands/keycloak/keycloak.version.update.command.js +17 -0
  112. package/src/commands/kv/kv.command.js +99 -0
  113. package/src/commands/kv/kv.docs.md +27 -0
  114. package/src/commands/link/link.command.js +31 -0
  115. package/src/commands/link/link.docs.md +22 -0
  116. package/src/commands/{login.js → login/login.command.js} +44 -22
  117. package/src/commands/login/login.docs.md +16 -0
  118. package/src/commands/logout/logout.command.js +15 -0
  119. package/src/commands/logout/logout.docs.md +9 -0
  120. package/src/commands/logs/logs.command.js +81 -0
  121. package/src/commands/logs/logs.docs.md +22 -0
  122. package/src/commands/make-default/make-default.command.js +17 -0
  123. package/src/commands/make-default/make-default.docs.md +15 -0
  124. package/src/commands/matomo/matomo.command.js +17 -0
  125. package/src/commands/matomo/matomo.docs.md +109 -0
  126. package/src/commands/matomo/matomo.get.command.js +17 -0
  127. package/src/commands/matomo/matomo.open.command.js +13 -0
  128. package/src/commands/matomo/matomo.open.logs.command.js +13 -0
  129. package/src/commands/matomo/matomo.open.webui.command.js +13 -0
  130. package/src/commands/matomo/matomo.rebuild.command.js +13 -0
  131. package/src/commands/matomo/matomo.restart.command.js +13 -0
  132. package/src/commands/metabase/metabase.command.js +17 -0
  133. package/src/commands/metabase/metabase.docs.md +169 -0
  134. package/src/commands/metabase/metabase.get.command.js +17 -0
  135. package/src/commands/metabase/metabase.open.command.js +13 -0
  136. package/src/commands/metabase/metabase.open.logs.command.js +13 -0
  137. package/src/commands/metabase/metabase.open.webui.command.js +13 -0
  138. package/src/commands/metabase/metabase.rebuild.command.js +13 -0
  139. package/src/commands/metabase/metabase.restart.command.js +13 -0
  140. package/src/commands/metabase/metabase.version.check.command.js +17 -0
  141. package/src/commands/metabase/metabase.version.command.js +17 -0
  142. package/src/commands/metabase/metabase.version.update.command.js +17 -0
  143. package/src/commands/ng/ng.args.js +27 -0
  144. package/src/commands/ng/ng.command.js +45 -0
  145. package/src/commands/ng/ng.create.command.js +60 -0
  146. package/src/commands/ng/ng.create.external.command.js +44 -0
  147. package/src/commands/ng/ng.delete.command.js +22 -0
  148. package/src/commands/ng/ng.delete.external.command.js +25 -0
  149. package/src/commands/ng/ng.docs.md +214 -0
  150. package/src/commands/ng/ng.get-config.command.js +32 -0
  151. package/src/commands/ng/ng.get.command.js +22 -0
  152. package/src/commands/ng/ng.link.command.js +25 -0
  153. package/src/commands/ng/ng.options.js +9 -0
  154. package/src/commands/ng/ng.search.command.js +22 -0
  155. package/src/commands/ng/ng.unlink.command.js +25 -0
  156. package/src/commands/notify-email/notify-email.add.command.js +65 -0
  157. package/src/commands/notify-email/notify-email.command.js +61 -0
  158. package/src/commands/notify-email/notify-email.docs.md +60 -0
  159. package/src/commands/notify-email/notify-email.remove.command.js +24 -0
  160. package/src/commands/open/open.command.js +24 -0
  161. package/src/commands/open/open.docs.md +16 -0
  162. package/src/commands/otoroshi/otoroshi.command.js +17 -0
  163. package/src/commands/otoroshi/otoroshi.disable-ng.command.js +13 -0
  164. package/src/commands/otoroshi/otoroshi.docs.md +211 -0
  165. package/src/commands/otoroshi/otoroshi.enable-ng.command.js +13 -0
  166. package/src/commands/otoroshi/otoroshi.get-config.command.js +13 -0
  167. package/src/commands/otoroshi/otoroshi.get.command.js +17 -0
  168. package/src/commands/otoroshi/otoroshi.open.command.js +13 -0
  169. package/src/commands/otoroshi/otoroshi.open.logs.command.js +13 -0
  170. package/src/commands/otoroshi/otoroshi.open.webui.command.js +13 -0
  171. package/src/commands/otoroshi/otoroshi.rebuild.command.js +13 -0
  172. package/src/commands/otoroshi/otoroshi.restart.command.js +13 -0
  173. package/src/commands/otoroshi/otoroshi.version.check.command.js +17 -0
  174. package/src/commands/otoroshi/otoroshi.version.command.js +17 -0
  175. package/src/commands/otoroshi/otoroshi.version.update.command.js +17 -0
  176. package/src/commands/profile/profile.command.js +60 -0
  177. package/src/commands/profile/profile.docs.md +23 -0
  178. package/src/commands/profile/profile.open.command.js +12 -0
  179. package/src/commands/published-config/published-config.command.js +40 -0
  180. package/src/commands/published-config/published-config.docs.md +77 -0
  181. package/src/commands/published-config/published-config.import.command.js +29 -0
  182. package/src/commands/published-config/published-config.rm.command.js +27 -0
  183. package/src/commands/published-config/published-config.set.command.js +34 -0
  184. package/src/commands/restart/restart.command.js +77 -0
  185. package/src/commands/restart/restart.docs.md +21 -0
  186. package/src/commands/scale/scale.command.js +121 -0
  187. package/src/commands/scale/scale.docs.md +23 -0
  188. package/src/commands/service/service.command.js +91 -0
  189. package/src/commands/service/service.docs.md +104 -0
  190. package/src/commands/service/service.link-addon.command.js +24 -0
  191. package/src/commands/service/service.link-app.command.js +23 -0
  192. package/src/commands/service/service.unlink-addon.command.js +24 -0
  193. package/src/commands/service/service.unlink-app.command.js +23 -0
  194. package/src/commands/ssh/ssh.command.js +40 -0
  195. package/src/commands/ssh/ssh.docs.md +17 -0
  196. package/src/commands/ssh-keys/ssh-keys.add.command.js +41 -0
  197. package/src/commands/ssh-keys/ssh-keys.args.js +8 -0
  198. package/src/commands/ssh-keys/ssh-keys.command.js +50 -0
  199. package/src/commands/ssh-keys/ssh-keys.docs.md +66 -0
  200. package/src/commands/ssh-keys/ssh-keys.open.command.js +12 -0
  201. package/src/commands/ssh-keys/ssh-keys.remove-all.command.js +54 -0
  202. package/src/commands/ssh-keys/ssh-keys.remove.command.js +26 -0
  203. package/src/commands/status/status.command.js +122 -0
  204. package/src/commands/status/status.docs.md +17 -0
  205. package/src/commands/stop/stop.command.js +23 -0
  206. package/src/commands/stop/stop.docs.md +16 -0
  207. package/src/commands/tcp-redirs/tcp-redirs.add.command.js +24 -0
  208. package/src/commands/tcp-redirs/tcp-redirs.command.js +41 -0
  209. package/src/commands/tcp-redirs/tcp-redirs.docs.md +71 -0
  210. package/src/commands/tcp-redirs/tcp-redirs.list-namespaces.command.js +48 -0
  211. package/src/commands/tcp-redirs/tcp-redirs.options.js +11 -0
  212. package/src/commands/tcp-redirs/tcp-redirs.remove.command.js +34 -0
  213. package/src/commands/tokens/tokens.command.js +43 -0
  214. package/src/commands/tokens/tokens.create.command.js +108 -0
  215. package/src/commands/tokens/tokens.docs.md +50 -0
  216. package/src/commands/tokens/tokens.revoke.command.js +25 -0
  217. package/src/commands/unlink/unlink.command.js +21 -0
  218. package/src/commands/unlink/unlink.docs.md +15 -0
  219. package/src/commands/version/version.command.js +13 -0
  220. package/src/commands/version/version.docs.md +9 -0
  221. package/src/commands/webhooks/webhooks.add.command.js +51 -0
  222. package/src/commands/webhooks/webhooks.command.js +57 -0
  223. package/src/commands/webhooks/webhooks.docs.md +61 -0
  224. package/src/commands/webhooks/webhooks.remove.command.js +24 -0
  225. package/src/lib/cliparse-patched.js +192 -0
  226. package/src/lib/define-argument.js +7 -0
  227. package/src/lib/define-argument.types.d.ts +23 -0
  228. package/src/lib/define-command.js +7 -0
  229. package/src/lib/define-command.types.d.ts +85 -0
  230. package/src/lib/define-common.types.d.ts +18 -0
  231. package/src/lib/define-option.js +7 -0
  232. package/src/lib/define-option.types.d.ts +29 -0
  233. package/src/lib/get-command-info.js +86 -0
  234. package/src/lib/get-command-info.types.d.ts +42 -0
  235. package/src/lib/zod-utils.js +66 -0
  236. package/src/models/addon.js +2 -3
  237. package/src/models/application.js +4 -5
  238. package/src/models/application_configuration.js +5 -23
  239. package/src/models/domain.js +34 -2
  240. package/src/models/exit-strategy-option.js +0 -9
  241. package/src/models/git.js +1 -4
  242. package/src/models/log-v4.js +5 -0
  243. package/src/models/log.js +5 -0
  244. package/src/models/namespaces.js +1 -2
  245. package/src/models/notification.js +1 -7
  246. package/src/parsers.js +33 -92
  247. package/src/command-options.js +0 -55
  248. package/src/command-promise-handler.js +0 -16
  249. package/src/commands/accesslogs.js +0 -138
  250. package/src/commands/addon.js +0 -391
  251. package/src/commands/applications.js +0 -123
  252. package/src/commands/cancel-deploy.js +0 -19
  253. package/src/commands/config.js +0 -47
  254. package/src/commands/console.js +0 -20
  255. package/src/commands/create.js +0 -129
  256. package/src/commands/database.js +0 -73
  257. package/src/commands/delete.js +0 -24
  258. package/src/commands/deploy.js +0 -133
  259. package/src/commands/diag.js +0 -126
  260. package/src/commands/domain.js +0 -445
  261. package/src/commands/drain.js +0 -164
  262. package/src/commands/emails.js +0 -156
  263. package/src/commands/env.js +0 -124
  264. package/src/commands/features.js +0 -87
  265. package/src/commands/k8s.js +0 -193
  266. package/src/commands/keycloak.js +0 -138
  267. package/src/commands/kv.js +0 -93
  268. package/src/commands/link.js +0 -20
  269. package/src/commands/logout.js +0 -8
  270. package/src/commands/logs.js +0 -45
  271. package/src/commands/makeDefault.js +0 -11
  272. package/src/commands/matomo.js +0 -85
  273. package/src/commands/metabase.js +0 -112
  274. package/src/commands/ng.js +0 -202
  275. package/src/commands/notify-email.js +0 -99
  276. package/src/commands/open.js +0 -13
  277. package/src/commands/otoroshi.js +0 -149
  278. package/src/commands/profile.js +0 -55
  279. package/src/commands/published-config.js +0 -70
  280. package/src/commands/restart.js +0 -52
  281. package/src/commands/scale.js +0 -66
  282. package/src/commands/service.js +0 -105
  283. package/src/commands/ssh-keys.js +0 -144
  284. package/src/commands/ssh.js +0 -20
  285. package/src/commands/status.js +0 -115
  286. package/src/commands/stop.js +0 -12
  287. package/src/commands/tcp-redirs.js +0 -80
  288. package/src/commands/tokens.js +0 -144
  289. package/src/commands/unlink.js +0 -14
  290. package/src/commands/version.js +0 -6
  291. package/src/commands/webhooks.js +0 -74
package/bin/clever.js CHANGED
@@ -4,70 +4,33 @@
4
4
  import '../src/initial-setup.js';
5
5
  import '../src/initial-update-notifier.js';
6
6
  // Other imports
7
- import cliparse from 'cliparse';
8
- import cliparseCommands from 'cliparse/src/command.js';
9
- import _sortBy from 'lodash/sortBy.js';
10
7
  import pkg from '../package.json' with { type: 'json' };
11
- import { getExitOnOption, getOutputFormatOption, getSameCommitPolicyOption } from '../src/command-options.js';
12
- import { handleCommandPromise } from '../src/command-promise-handler.js';
13
- import * as accesslogsModule from '../src/commands/accesslogs.js';
14
- import * as activity from '../src/commands/activity.js';
15
- import * as addon from '../src/commands/addon.js';
16
- import * as applications from '../src/commands/applications.js';
17
- import * as cancelDeploy from '../src/commands/cancel-deploy.js';
18
- import * as config from '../src/commands/config.js';
19
- import * as consoleModule from '../src/commands/console.js';
20
- import * as create from '../src/commands/create.js';
21
- import { curl } from '../src/commands/curl.js';
22
- import * as database from '../src/commands/database.js';
23
- import * as deleteCommandModule from '../src/commands/delete.js';
24
- import * as deploy from '../src/commands/deploy.js';
25
- import * as diag from '../src/commands/diag.js';
26
- import * as domain from '../src/commands/domain.js';
27
- import * as drain from '../src/commands/drain.js';
28
- import * as emails from '../src/commands/emails.js';
29
- import * as env from '../src/commands/env.js';
30
- import * as features from '../src/commands/features.js';
31
- import * as k8s from '../src/commands/k8s.js';
32
- import * as keycloak from '../src/commands/keycloak.js';
33
- import * as kv from '../src/commands/kv.js';
34
- import * as link from '../src/commands/link.js';
35
- import * as login from '../src/commands/login.js';
36
- import * as logout from '../src/commands/logout.js';
37
- import * as logs from '../src/commands/logs.js';
38
- import * as makeDefault from '../src/commands/makeDefault.js';
39
- import * as matomo from '../src/commands/matomo.js';
40
- import * as metabase from '../src/commands/metabase.js';
41
- import * as ng from '../src/commands/ng.js';
42
- import * as notifyEmail from '../src/commands/notify-email.js';
43
- import * as open from '../src/commands/open.js';
44
- import * as otoroshi from '../src/commands/otoroshi.js';
45
- import * as profile from '../src/commands/profile.js';
46
- import * as publishedConfig from '../src/commands/published-config.js';
47
- import * as restart from '../src/commands/restart.js';
48
- import * as scale from '../src/commands/scale.js';
49
- import * as service from '../src/commands/service.js';
50
- import * as sshKeys from '../src/commands/ssh-keys.js';
51
- import * as ssh from '../src/commands/ssh.js';
52
- import * as status from '../src/commands/status.js';
53
- import * as stop from '../src/commands/stop.js';
54
- import * as tcpRedirs from '../src/commands/tcp-redirs.js';
55
- import * as tokens from '../src/commands/tokens.js';
56
- import * as unlink from '../src/commands/unlink.js';
57
- import * as version from '../src/commands/version.js';
58
- import * as webhooks from '../src/commands/webhooks.js';
8
+ import { curl } from '../src/commands/curl/curl.command.js';
9
+ import { globalCommands } from '../src/commands/global.commands.js';
10
+ import {
11
+ colorOption,
12
+ helpOption,
13
+ updateNotifierOption,
14
+ verboseOption,
15
+ versionOption,
16
+ } from '../src/commands/global.options.js';
59
17
  import { EXPERIMENTAL_FEATURES } from '../src/experimental-features.js';
18
+ import { cliparse } from '../src/lib/cliparse-patched.js';
60
19
  import { styleText } from '../src/lib/style-text.js';
61
- import * as Addon from '../src/models/addon.js';
62
- import * as Application from '../src/models/application.js';
63
- import { AVAILABLE_ZONES } from '../src/models/application.js';
64
- import * as ApplicationConfiguration from '../src/models/application_configuration.js';
65
- import { conf, getFeatures } from '../src/models/configuration.js';
66
- import * as Drain from '../src/models/drain.js';
67
- import * as git from '../src/models/git.js';
68
- import * as Namespaces from '../src/models/namespaces.js';
69
- import * as Notification from '../src/models/notification.js';
70
- import * as Parsers from '../src/parsers.js';
20
+ import { getDefault, isBoolean, isRequired } from '../src/lib/zod-utils.js';
21
+ import { getFeatures } from '../src/models/configuration.js';
22
+
23
+ /**
24
+ * @typedef {import('../src/lib/define-command.types.js').CommandDefinition} CommandDefinition
25
+ * @typedef {import('../src/lib/define-option.types.js').OptionDefinition} OptionDefinition
26
+ * @typedef {import('../src/lib/define-argument.types.js').ArgumentDefinition} ArgumentDefinition
27
+ */
28
+
29
+ /**
30
+ * A command entry in the globalCommands structure.
31
+ * Can be either a command definition or a tuple of [command, subcommands].
32
+ * @typedef {CommandDefinition | [CommandDefinition, Record<string, CommandEntry>]} CommandEntry
33
+ */
71
34
 
72
35
  // Exit cleanly if the program we pipe to exits abruptly
73
36
  process.stdout.on('error', (error) => {
@@ -76,2105 +39,225 @@ process.stdout.on('error', (error) => {
76
39
  }
77
40
  });
78
41
 
79
- // Patch cliparse.command so we can catch errors
80
- const cliparseCommand = cliparse.command;
81
-
82
- cliparse.command = function (name, options, commandFunction) {
83
- if (commandFunction == null) {
84
- return cliparseCommand(name, options);
85
- }
86
-
87
- return cliparseCommand(name, options, (...args) => {
88
- const promise = commandFunction(...args);
89
- handleCommandPromise(promise);
90
- });
91
- };
92
-
93
- // Add a yellow color and status tag to the description of an experimental command
94
- function colorizeExperimentalCommand(command, id) {
95
- const status = EXPERIMENTAL_FEATURES[id].status;
96
- command.description = styleText('yellow', command.description + ' [' + status.toUpperCase() + ']');
97
- return command;
42
+ // Right now, this is the only way to do this properly
43
+ // cliparse doesn't allow unknown options/arguments
44
+ if (process.argv[2] === 'curl') {
45
+ curl().catch(() => process.exit(1));
46
+ } else {
47
+ run().catch(() => process.exit(1));
98
48
  }
99
49
 
100
50
  async function run() {
101
- // ARGUMENTS
102
- const args = {
103
- k8sClusterName: cliparse.argument('cluster-name', { description: 'Kubernetes cluster name' }),
104
- k8sIdOrName: cliparse.argument('id-or-name', {
105
- description: 'Kubernetes cluster ID or name',
106
- parser: Parsers.addonIdOrName,
107
- }),
108
- kvRawCommand: cliparse.argument('command', {
109
- description: 'The raw command to send to the Materia KV or Redis® add-on',
110
- }),
111
- kvIdOrName: cliparse.argument('kv-id', {
112
- description: 'Add-on/Real ID (or name, if unambiguous) of a Materia KV or Redis® add-on',
113
- }),
114
- apiTokenId: cliparse.argument('api-token-id', { description: 'API token ID' }),
115
- apiTokenName: cliparse.argument('api-token-name', { description: 'API token name' }),
116
- ngLabel: cliparse.argument('ng-label', {
117
- description: 'Network Group label',
118
- parser: Parsers.ngResourceType,
119
- }),
120
- ngIdOrLabel: cliparse.argument('ng-id-or-label', {
121
- description: 'Network Group ID or label',
122
- parser: Parsers.ngResourceType,
123
- }),
124
- ngExternalPeerLabel: cliparse.argument('external-peer-label', {
125
- description: 'External peer label',
126
- parser: Parsers.ngResourceType,
127
- }),
128
- ngExternalIdOrLabel: cliparse.argument('external-peer-id-or-label', {
129
- description: 'External peer ID or label',
130
- parser: Parsers.ngResourceType,
131
- }),
132
- ngAnyIdOrLabel: cliparse.argument('id-or-label', {
133
- description: 'ID or Label of a Network Group, a member or an (external) peer',
134
- parser: Parsers.ngResourceType,
135
- }),
136
- ngResourceId: cliparse.argument('id', {
137
- description: 'ID of a resource to (un)link to a Network Group',
138
- parser: Parsers.ngResourceType,
139
- }),
140
- wgPublicKey: cliparse.argument('public-key', {
141
- metavar: 'public_key',
142
- description: 'WireGuard public key of the external peer to link to a Network Group',
143
- }),
144
- email: cliparse.argument('email', {
145
- description: 'Email address',
146
- parser: Parsers.email,
147
- }),
148
- sshKeyName: cliparse.argument('ssh-key-name', { description: 'SSH key name' }),
149
- sshKeyPath: cliparse.argument('ssh-key-path', { description: 'SSH public key path (.pub)' }),
150
- addonIdOrName: cliparse.argument('addon-id', {
151
- description: 'Add-on ID (or name, if unambiguous)',
152
- parser: Parsers.addonIdOrName,
153
- }),
154
- addonName: cliparse.argument('addon-name', { description: 'Add-on name' }),
155
- addonProvider: cliparse.argument('addon-provider', { description: 'Add-on provider' }),
156
- alias: cliparse.argument('app-alias', { description: 'Application alias' }),
157
- appIdOrName: cliparse.argument('app-id', {
158
- description: 'Application ID (or name, if unambiguous)',
159
- parser: Parsers.appIdOrName,
160
- }),
161
- appNameCreation: cliparse.argument('app-name', {
162
- description: 'Application name (optional, current directory name is used if not specified)',
163
- default: '',
164
- }),
165
- backupId: cliparse.argument('backup-id', { description: 'A Database backup ID (format: UUID)' }),
166
- databaseId: cliparse.argument('database-id', {
167
- description: 'Any database ID (format: addon_UUID, postgresql_UUID, mysql_UUID, ...)',
168
- }),
169
- drainId: cliparse.argument('drain-id', { description: 'Drain ID' }),
170
- drainType: cliparse.argument('drain-type', {
171
- description: Drain.DRAIN_TYPE_CLI_CODES.join(', '),
172
- complete: Drain.DRAIN_TYPE_CLI_CODES,
173
- }),
174
- drainUrl: cliparse.argument('drain-url', { description: 'Drain URL' }),
175
- fqdn: cliparse.argument('fqdn', { description: 'Domain name of the application' }),
176
- features: cliparse.argument('features', {
177
- description: 'Comma-separated list of experimental features to manage',
178
- parser: Parsers.commaSeparated,
179
- }),
180
- featureId: cliparse.argument('feature', { description: 'Experimental feature to manage' }),
181
- notificationName: cliparse.argument('name', { description: 'Notification name' }),
182
- notificationId: cliparse.argument('notification-id', { description: 'Notification ID' }),
183
- webhookUrl: cliparse.argument('url', { description: 'Webhook URL' }),
184
- envVariableName: cliparse.argument('variable-name', { description: 'Name of the environment variable' }),
185
- envVariableNames: cliparse.argument('variable-names', {
186
- description: 'Comma separated list of names of the environment variables',
187
- parser: Parsers.commaSeparated,
188
- }),
189
- envVariableValue: cliparse.argument('variable-value', { description: 'Value of the environment variable' }),
190
- port: cliparse.argument('port', {
191
- description: 'port identifying the TCP redirection',
192
- parser: Parsers.integer,
193
- }),
194
- configurationName: cliparse.argument('configuration-name', {
195
- description: `Configuration to manage: ${ApplicationConfiguration.listAvailableIds(true)}`,
196
- complete() {
197
- return cliparse.autocomplete.words(ApplicationConfiguration.listAvailableIds());
198
- },
199
- }),
200
- configurationValue: cliparse.argument('configuration-value', { description: 'The new value of the configuration' }),
201
- };
202
-
203
- // OPTIONS
204
- const opts = {
205
- k8sDeployWatch: cliparse.flag('watch', {
206
- aliases: ['w'],
207
- description: 'Watch the deployment until the cluster is deployed',
208
- }),
209
- targetVersion: cliparse.option('target', {
210
- metavar: 'version',
211
- description: 'Target version to upgrade to (e.g.: 24, 2.4, 2.4.1)',
212
- }),
213
- apiTokenExpiration: cliparse.option('expiration', {
214
- aliases: ['e'],
215
- metavar: 'expiration',
216
- parser: Parsers.futureDateOrDuration,
217
- description: 'Duration until API token expiration (e.g.: 1h, 4d, 2w, 6M), default 1y',
218
- }),
219
- // Network Groups options
220
- ngDescription: cliparse.option('description', {
221
- metavar: 'description',
222
- description: 'Network Group description',
223
- }),
224
- ngMembersIdsToLink: cliparse.option('link', {
225
- metavar: 'members_ids',
226
- description:
227
- 'Comma separated list of members IDs to link to a Network Group (app_xxx, external_xxx, mysql_xxx, postgresql_xxx, redis_xxx, etc.)',
228
- parser: Parsers.commaSeparated,
229
- }),
230
- ngResourceType: cliparse.option('type', {
231
- metavar: 'type',
232
- description: 'Type of resource to look for (NetworkGroup, Member, CleverPeer, ExternalPeer)',
233
- parser: Parsers.ngValidType,
234
- }),
235
- sourceableEnvVarsList: cliparse.flag('add-export', { description: 'Display sourceable env variables setting' }),
236
- logsFormat: getOutputFormatOption(['json-stream']),
237
- activityFormat: getOutputFormatOption(['json-stream']),
238
- envFormat: getOutputFormatOption(['shell']),
239
- importAsJson: cliparse.flag('json', {
240
- description: 'Import variables as JSON (an array of { "name": "THE_NAME", "value": "THE_VALUE" } objects)',
241
- }),
242
- addonId: cliparse.option('addon', { metavar: 'addon_id', description: 'Add-on ID' }),
243
- after: cliparse.option('after', {
244
- metavar: 'after',
245
- aliases: ['since'],
246
- parser: Parsers.date,
247
- description: 'Fetch logs after this date/time (ISO8601 date, positive number in seconds or duration, e.g.: 1h)',
248
- }),
249
- aliasCreation: cliparse.option('alias', {
250
- aliases: ['a'],
251
- metavar: 'alias',
252
- description: 'Short name for the application',
253
- }),
254
- alias: cliparse.option('alias', {
255
- aliases: ['a'],
256
- metavar: 'alias',
257
- description: 'Short name for the application',
258
- complete: Application.listAvailableAliases,
259
- }),
260
- domain: cliparse.option('filter', {
261
- default: '',
262
- metavar: 'TEXT',
263
- description: 'Check only domains containing the provided text',
264
- }),
265
- domainOverviewFilter: cliparse.option('filter', {
266
- default: '',
267
- metavar: 'TEXT',
268
- description: 'Get only domains containing the provided text',
269
- }),
270
- before: cliparse.option('before', {
271
- metavar: 'before',
272
- aliases: ['until'],
273
- parser: Parsers.date,
274
- description: 'Fetch logs before this date/time (ISO8601 date, positive number in seconds or duration, e.g.: 1h)',
275
- }),
276
- branch: cliparse.option('branch', {
277
- aliases: ['b'],
278
- default: '',
279
- metavar: 'branch',
280
- description: 'Branch to push (current branch by default)',
281
- complete() {
282
- return git.completeBranches();
283
- },
284
- }),
285
- commit: cliparse.option('commit', {
286
- metavar: 'commit id',
287
- description: 'Restart the application with a specific commit ID',
288
- }),
289
- gitTag: cliparse.option('tag', {
290
- aliases: ['t'],
291
- default: '',
292
- metavar: 'tag',
293
- description: 'Tag to push (none by default)',
294
- }),
295
- deploymentId: cliparse.option('deployment-id', {
296
- metavar: 'deployment_id',
297
- description: 'Fetch logs for a given deployment',
298
- }),
299
- namespace: cliparse.option('namespace', {
300
- metavar: 'namespace',
301
- description: 'Namespace in which the TCP redirection should be',
302
- required: true,
303
- complete: Namespaces.completeNamespaces,
304
- }),
305
- notificationEventType: cliparse.option('event', {
306
- metavar: 'type',
307
- description: 'Restrict notifications to specific event types',
308
- complete: Notification.listMetaEvents,
309
- parser: Parsers.commaSeparated,
310
- }),
311
- flavor: cliparse.option('flavor', {
312
- metavar: 'flavor',
313
- parser: Parsers.flavor,
314
- description: 'The instance size of your application',
315
- complete() {
316
- return cliparse.autocomplete.words(Application.listAvailableFlavors());
317
- },
318
- }),
319
- follow: cliparse.flag('follow', {
320
- aliases: ['f'],
321
- description: 'Track new deployments in activity list',
322
- }),
323
- forceDeploy: cliparse.flag('force', {
324
- aliases: ['f'],
325
- description: "Force deploy even if it's not fast-forwardable",
326
- }),
327
- exitOnDeploy: getExitOnOption(),
328
- sameCommitPolicy: getSameCommitPolicyOption(),
329
- webhookFormat: cliparse.option('format', {
330
- metavar: 'format',
331
- default: 'raw',
332
- description: "Format of the body sent to the webhook ('raw', 'slack', 'gitter', or 'flowdock')",
333
- }),
334
- github: cliparse.option('github', {
335
- metavar: 'OWNER/REPO',
336
- description: 'GitHub application to use for deployments',
337
- }),
338
- sshIdentityFile: cliparse.option('identity-file', {
339
- aliases: ['i'],
340
- metavar: 'identity-file',
341
- description: 'SSH identity file',
342
- }),
343
- instances: cliparse.option('instances', {
344
- metavar: 'instances',
345
- parser: Parsers.instances,
346
- description: 'The number of parallel instances',
347
- }),
348
- linkAddon: cliparse.option('link', {
349
- aliases: ['l'],
350
- metavar: 'alias',
351
- description: 'Link the created add-on to the app with the specified alias',
352
- complete: Application.listAvailableAliases,
353
- }),
354
- listAllNotifications: cliparse.flag('list-all', {
355
- description: "List all notifications for your user or for an organisation with the '--org' option",
356
- }),
357
- maxFlavor: cliparse.option('max-flavor', {
358
- metavar: 'maxflavor',
359
- parser: Parsers.flavor,
360
- description: 'The maximum instance size of your application',
361
- complete() {
362
- return cliparse.autocomplete.words(Application.listAvailableFlavors());
363
- },
364
- }),
365
- buildFlavor: cliparse.option('build-flavor', {
366
- metavar: 'buildflavor',
367
- parser: Parsers.buildFlavor,
368
- description: "The size of the build instance, or 'disabled' if you want to disable dedicated build instances",
369
- }),
370
- maxInstances: cliparse.option('max-instances', {
371
- metavar: 'maxinstances',
372
- parser: Parsers.instances,
373
- description: 'The maximum number of parallel instances',
374
- }),
375
- minFlavor: cliparse.option('min-flavor', {
376
- metavar: 'minflavor',
377
- parser: Parsers.flavor,
378
- description: 'The minimum scale size of your application',
379
- complete() {
380
- return cliparse.autocomplete.words(Application.listAvailableFlavors());
381
- },
382
- }),
383
- minInstances: cliparse.option('min-instances', {
384
- metavar: 'mininstances',
385
- parser: Parsers.instances,
386
- description: 'The minimum number of parallel instances',
387
- }),
388
- updateNotifier: cliparse.flag('update-notifier', {
389
- description: 'Choose whether to use update notifier or not. You can also use --no-update-notifier',
390
- default: true,
391
- }),
392
- emailNotificationTarget: cliparse.option('notify', {
393
- metavar: '<email_address>|<user_id>|"organisation"',
394
- description:
395
- 'Notify a user, a specific email address or the whole organisation (multiple values allowed, comma separated)',
396
- required: true,
397
- parser: Parsers.commaSeparated,
398
- }),
399
- onlyAddons: cliparse.flag('only-addons', { description: 'Only show add-on dependencies' }),
400
- onlyAliases: cliparse.flag('only-aliases', { description: 'List only application aliases' }),
401
- onlyApps: cliparse.flag('only-apps', { description: 'Only show app dependencies' }),
402
- appIdOrName: cliparse.option('app', {
403
- metavar: 'ID_OR_NAME',
404
- description: 'Application to manage by its ID (or name, if unambiguous)',
405
- parser: Parsers.appIdOrName,
406
- }),
407
- orgaIdOrName: cliparse.option('org', {
408
- metavar: 'ID_OR_NAME',
409
- aliases: ['o', 'owner'],
410
- description: 'Organisation to target by its ID (or name, if unambiguous)',
411
- parser: Parsers.orgaIdOrName,
412
- }),
413
- output: cliparse.option('output', {
414
- aliases: ['out'],
415
- description: 'Redirect the output of the command in a file',
416
- }),
417
- drainPassword: cliparse.option('password', {
418
- aliases: ['p'],
419
- metavar: 'password',
420
- description: 'Basic auth password (for elasticsearch or raw-http)',
421
- }),
422
- addonPlan: cliparse.option('plan', {
423
- aliases: ['p'],
424
- default: '',
425
- metavar: 'plan',
426
- description: 'Add-on plan, depends on the provider',
427
- complete: Addon.completePlan,
428
- }),
429
- quiet: cliparse.flag('quiet', { aliases: ['q'], description: "Don't show logs during deployment" }),
430
- followDeployLogs: cliparse.flag('follow', {
431
- description: 'Continue to follow logs after deployment has ended',
432
- }),
433
- addonRegion: cliparse.option('region', {
434
- aliases: ['r'],
435
- default: 'par',
436
- metavar: 'region',
437
- description: 'Region to provision the add-on in, depends on the provider',
438
- complete: Addon.completeRegion,
439
- }),
440
- addonVersion: cliparse.option('addon-version', {
441
- metavar: 'addon-version',
442
- description: 'The version to use for the add-on',
443
- }),
444
- addonOptions: cliparse.option('option', {
445
- metavar: 'option',
446
- parser: Parsers.addonOptions,
447
- description:
448
- 'Option to enable for the add-on. Multiple --option argument can be passed to enable multiple options',
449
- }),
450
- region: cliparse.option('region', {
451
- aliases: ['r'],
452
- default: 'par',
453
- metavar: 'zone',
454
- description: `Region, can be ${AVAILABLE_ZONES.map((name) => `'${name}'`).join(', ')}`,
455
- complete: Application.listAvailableZones,
456
- }),
457
- search: cliparse.option('search', {
458
- metavar: 'search',
459
- description: 'Fetch logs matching this pattern',
460
- }),
461
- loginSecret: cliparse.option('secret', {
462
- metavar: 'secret',
463
- description: 'Directly give an existing secret',
464
- }),
465
- notificationScope: cliparse.option('service', {
466
- metavar: 'service_id',
467
- description: 'Restrict notifications to specific applications and add-ons',
468
- parser: Parsers.commaSeparated,
469
- }),
470
- showAllActivity: cliparse.flag('show-all', { description: 'Show all activity' }),
471
- showAll: cliparse.flag('show-all', { description: 'Show all available add-ons and applications' }),
472
- loginToken: cliparse.option('token', {
473
- metavar: 'token',
474
- description: 'Directly give an existing token',
475
- }),
476
- taskCommand: cliparse.option('task', {
477
- description: 'The application launch as a task executing the given command, then stopped',
478
- aliases: ['T'],
479
- parser: Parsers.nonEmptyString,
480
- metavar: 'command',
481
- }),
482
- instanceType: cliparse.option('type', {
483
- aliases: ['t'],
484
- required: true,
485
- metavar: 'type',
486
- description: 'Instance type',
487
- complete: Application.listAvailableTypes,
488
- }),
489
- drainUsername: cliparse.option('username', {
490
- aliases: ['u'],
491
- metavar: 'username',
492
- description: 'Basic auth username (for elasticsearch or raw-http)',
493
- }),
494
- drainApiKey: cliparse.option('api-key', {
495
- aliases: ['k'],
496
- metavar: 'api_key',
497
- description: 'API key (for newrelic)',
498
- }),
499
- drainIndexPrefix: cliparse.option('index-prefix', {
500
- aliases: ['i'],
501
- metavar: 'index_prefix',
502
- description: 'Optional index prefix (for elasticsearch), `logstash` value is used if not set',
503
- }),
504
- drainSdParameters: cliparse.option('sd-params', {
505
- aliases: ['s'],
506
- metavar: 'sd_params',
507
- description: 'RFC5424 structured data parameters (for ovh-tcp), e.g.: `X-OVH-TOKEN=\\"REDACTED\\"`',
508
- }),
509
- verbose: cliparse.flag('verbose', { aliases: ['v'], description: 'Verbose output' }),
510
- color: cliparse.flag('color', {
511
- description: 'Choose whether to print colors or not. You can also use --no-color',
512
- default: true,
513
- }),
514
- withoutCache: cliparse.flag('without-cache', { description: 'Restart the application without using cache' }),
515
- confirmAddonCreation: cliparse.flag('yes', {
516
- aliases: ['y'],
517
- description: 'Skip confirmation even if the add-on is not free',
518
- }),
519
- confirmAddonDeletion: cliparse.flag('yes', {
520
- aliases: ['y'],
521
- description: 'Skip confirmation and delete the add-on directly',
522
- }),
523
- confirmApplicationDeletion: cliparse.flag('yes', {
524
- aliases: ['y'],
525
- description: 'Skip confirmation and delete the application directly',
526
- }),
527
- confirmSshKeyClean: cliparse.flag('yes', {
528
- aliases: ['y'],
529
- description: 'Skip confirmation and remove all SSH keys directly',
530
- }),
531
- yes: cliparse.flag('yes', {
532
- aliases: ['y'],
533
- description: 'Skip confirmation',
534
- }),
535
- jsonFormat: cliparse.flag('json', { aliases: ['j'], description: 'Show result in JSON format' }),
536
- humanJsonOutputFormat: getOutputFormatOption(),
537
- optTags: cliparse.option('tags', {
538
- metavar: 'tags',
539
- description: 'List of tags, separated by a comma',
540
- parser: Parsers.tags,
541
- }),
542
- };
543
-
544
- // ACCESSLOGS COMMAND
545
- const accesslogsCommand = cliparse.command(
546
- 'accesslogs',
547
- {
548
- description: 'Fetch access logs',
549
- options: [opts.alias, opts.appIdOrName, opts.logsFormat, opts.before, opts.after, opts.addonId],
550
- },
551
- accesslogsModule.accessLogs,
552
- );
553
-
554
- // ACTIVITY COMMAND
555
- const activityCommand = cliparse.command(
556
- 'activity',
557
- {
558
- description: 'Show last deployments of an application',
559
- options: [opts.alias, opts.appIdOrName, opts.follow, opts.showAllActivity, opts.activityFormat],
560
- },
561
- activity.activity,
562
- );
563
-
564
- // ADDON COMMANDS
565
- const addonCreateCommand = cliparse.command(
566
- 'create',
567
- {
568
- description: 'Create an add-on',
569
- args: [args.addonProvider, args.addonName],
570
- options: [
571
- opts.linkAddon,
572
- opts.confirmAddonCreation,
573
- opts.addonPlan,
574
- opts.addonRegion,
575
- opts.addonVersion,
576
- opts.addonOptions,
577
- opts.humanJsonOutputFormat,
578
- ],
579
- },
580
- addon.create,
581
- );
582
- const addonDeleteCommand = cliparse.command(
583
- 'delete',
584
- {
585
- description: 'Delete an add-on',
586
- args: [args.addonIdOrName],
587
- options: [opts.confirmAddonDeletion],
588
- },
589
- addon.deleteAddon,
590
- );
591
- const addonRenameCommand = cliparse.command(
592
- 'rename',
593
- {
594
- description: 'Rename an add-on',
595
- args: [args.addonIdOrName, args.addonName],
596
- },
597
- addon.rename,
598
- );
599
- const addonShowProviderCommand = cliparse.command(
600
- 'show',
601
- {
602
- description: 'Show information about an add-on provider',
603
- args: [args.addonProvider],
604
- },
605
- addon.showProvider,
606
- );
607
- const addonProvidersCommand = cliparse.command(
608
- 'providers',
609
- {
610
- description: 'List available add-on providers',
611
- commands: [addonShowProviderCommand],
612
- options: [opts.humanJsonOutputFormat],
613
- },
614
- addon.listProviders,
615
- );
616
- const addonEnvCommand = cliparse.command(
617
- 'env',
618
- {
619
- description: 'List environment variables for an add-on',
620
- options: [opts.envFormat],
621
- args: [opts.addonId],
622
- },
623
- addon.env,
624
- );
625
- const addonListCommand = cliparse.command(
626
- 'list',
627
- {
628
- description: 'List available add-ons',
629
- options: [opts.humanJsonOutputFormat],
630
- },
631
- addon.list,
632
- );
633
-
634
- const addonCommands = cliparse.command(
635
- 'addon',
636
- {
637
- description: 'Manage add-ons',
638
- options: [opts.orgaIdOrName],
639
- privateOptions: [opts.humanJsonOutputFormat],
640
- commands: [
641
- addonCreateCommand,
642
- addonDeleteCommand,
643
- addonRenameCommand,
644
- addonListCommand,
645
- addonProvidersCommand,
646
- addonEnvCommand,
647
- ],
648
- },
649
- addon.list,
650
- );
651
-
652
- // APPLICATIONS COMMAND
653
- const applicationsListRemoteCommand = cliparse.command(
654
- 'list',
655
- {
656
- description: 'List all applications',
657
- options: [opts.orgaIdOrName, opts.humanJsonOutputFormat],
658
- },
659
- applications.listAll,
660
- );
661
- const applicationsCommand = cliparse.command(
662
- 'applications',
663
- {
664
- description: 'List linked applications',
665
- privateOptions: [opts.onlyAliases, opts.jsonFormat],
666
- commands: [applicationsListRemoteCommand],
667
- },
668
- applications.list,
669
- );
670
-
671
- // CANCEL DEPLOY COMMAND
672
- const cancelDeployCommand = cliparse.command(
673
- 'cancel-deploy',
674
- {
675
- description: 'Cancel an ongoing deployment',
676
- options: [opts.alias, opts.appIdOrName],
677
- },
678
- cancelDeploy.cancelDeploy,
679
- );
680
-
681
- // CONFIG COMMAND
682
- const configGetCommand = cliparse.command(
683
- 'get',
684
- {
685
- description: 'Display the current configuration',
686
- args: [args.configurationName],
687
- },
688
- config.get,
689
- );
690
- const configSetCommand = cliparse.command(
691
- 'set',
692
- {
693
- description: 'Edit one configuration setting',
694
- args: [args.configurationName, args.configurationValue],
695
- },
696
- config.set,
697
- );
698
- const configUpdateCommand = cliparse.command(
699
- 'update',
700
- {
701
- description: 'Edit multiple configuration settings at once',
702
- options: ApplicationConfiguration.getUpdateOptions(),
703
- },
704
- config.update,
705
- );
706
- const configCommands = cliparse.command(
707
- 'config',
708
- {
709
- description: 'Display or edit the configuration of your application',
710
- options: [opts.alias, opts.appIdOrName],
711
- commands: [configGetCommand, configSetCommand, configUpdateCommand],
712
- },
713
- config.list,
714
- );
715
-
716
- // CREATE COMMAND
717
- const appCreateCommand = cliparse.command(
718
- 'create',
719
- {
720
- description: 'Create an application',
721
- args: [args.appNameCreation],
722
- options: [
723
- opts.instanceType,
724
- opts.orgaIdOrName,
725
- opts.aliasCreation,
726
- opts.region,
727
- opts.github,
728
- opts.taskCommand,
729
- opts.humanJsonOutputFormat,
730
- ],
731
- },
732
- create.create,
733
- );
734
-
735
- // CURL COMMAND
736
- // NOTE: it's just here for documentation purposes, look at the bottom of the file for the real "clever curl" command
737
- const curlCommand = cliparse.command(
738
- 'curl',
739
- {
740
- description: "Query Clever Cloud's API using Clever Tools credentials",
741
- },
742
- () => null,
743
- );
744
-
745
- // DELETE COMMAND
746
- const deleteCommand = cliparse.command(
747
- 'delete',
748
- {
749
- description: 'Delete an application',
750
- options: [opts.alias, opts.appIdOrName, opts.confirmApplicationDeletion],
751
- },
752
- deleteCommandModule.deleteApp,
753
- );
754
-
755
- // DEPLOY COMMAND
756
- const deployCommand = cliparse.command(
757
- 'deploy',
758
- {
759
- description: 'Deploy an application',
760
- options: [
761
- opts.alias,
762
- opts.branch,
763
- opts.gitTag,
764
- opts.quiet,
765
- opts.forceDeploy,
766
- opts.followDeployLogs,
767
- opts.sameCommitPolicy,
768
- opts.exitOnDeploy,
769
- ],
770
- },
771
- deploy.deploy,
772
- );
773
-
774
- // DIAG COMMAND
775
- const diagCommand = cliparse.command(
776
- 'diag',
777
- {
778
- description: 'Diagnose the current installation (prints various informations for support)',
779
- args: [],
780
- options: [opts.humanJsonOutputFormat],
781
- },
782
- diag.diag,
783
- );
784
-
785
- // DOMAIN COMMANDS
786
- const domainCreateCommand = cliparse.command(
787
- 'add',
788
- {
789
- description: 'Add a domain name to an application',
790
- args: [args.fqdn],
791
- options: [opts.alias, opts.appIdOrName],
792
- },
793
- domain.add,
794
- );
795
- const domainRemoveCommand = cliparse.command(
796
- 'rm',
797
- {
798
- description: 'Remove a domain name from an application',
799
- args: [args.fqdn],
800
- options: [opts.alias, opts.appIdOrName],
801
- },
802
- domain.rm,
803
- );
804
- const domainSetFavouriteCommand = cliparse.command(
805
- 'set',
806
- {
807
- description: 'Set the favourite domain for an application',
808
- args: [args.fqdn],
809
- },
810
- domain.setFavourite,
811
- );
812
- const domainUnsetFavouriteCommand = cliparse.command(
813
- 'unset',
814
- {
815
- description: 'Unset the favourite domain for an application',
816
- },
817
- domain.unsetFavourite,
818
- );
819
- const domainFavouriteCommands = cliparse.command(
820
- 'favourite',
821
- {
822
- description: 'Manage the favourite domain name for an application',
823
- options: [opts.alias, opts.appIdOrName],
824
- privateOptions: [opts.humanJsonOutputFormat],
825
- commands: [domainSetFavouriteCommand, domainUnsetFavouriteCommand],
826
- },
827
- domain.getFavourite,
828
- );
829
- const domainDiagApplicationCommand = cliparse.command(
830
- 'diag',
831
- {
832
- description: 'Check if domains associated to a specific app are properly configured',
833
- options: [opts.alias, opts.appIdOrName, opts.humanJsonOutputFormat, opts.domain],
834
- },
835
- domain.diagApplication,
836
- );
837
- const domainOverviewCommand = cliparse.command(
838
- 'overview',
839
- {
840
- description: 'Get an overview of all your domains (all orgas, all apps)',
841
- options: [opts.humanJsonOutputFormat, opts.domainOverviewFilter],
842
- },
843
- domain.overview,
844
- );
845
- const domainCommands = cliparse.command(
846
- 'domain',
847
- {
848
- description: 'Manage domain names for an application',
849
- privateOptions: [opts.alias, opts.appIdOrName, opts.humanJsonOutputFormat],
850
- commands: [
851
- domainCreateCommand,
852
- domainFavouriteCommands,
853
- domainRemoveCommand,
854
- domainDiagApplicationCommand,
855
- domainOverviewCommand,
856
- ],
857
- },
858
- domain.list,
859
- );
860
-
861
- // DRAIN COMMANDS
862
- const drainCreateCommand = cliparse.command(
863
- 'create',
864
- {
865
- description: 'Create a drain',
866
- args: [args.drainType, args.drainUrl],
867
- options: [
868
- opts.drainUsername,
869
- opts.drainPassword,
870
- opts.drainApiKey,
871
- opts.drainIndexPrefix,
872
- opts.drainSdParameters,
873
- ],
874
- },
875
- drain.create,
876
- );
877
- const drainGetCommand = cliparse.command(
878
- 'get',
879
- {
880
- description: 'Get drain info',
881
- args: [args.drainId],
882
- privateOptions: [opts.humanJsonOutputFormat],
883
- },
884
- drain.get,
885
- );
886
- const drainRemoveCommand = cliparse.command(
887
- 'remove',
888
- {
889
- description: 'Remove a drain',
890
- args: [args.drainId],
891
- },
892
- drain.remove,
893
- );
894
- const drainEnableCommand = cliparse.command(
895
- 'enable',
896
- {
897
- description: 'Enable a drain',
898
- args: [args.drainId],
899
- },
900
- drain.enable,
901
- );
902
- const drainDisableCommand = cliparse.command(
903
- 'disable',
904
- {
905
- description: 'Disable a drain',
906
- args: [args.drainId],
907
- },
908
- drain.disable,
909
- );
910
- const drainCommands = cliparse.command(
911
- 'drain',
912
- {
913
- description: 'Manage drains',
914
- options: [opts.alias, opts.appIdOrName],
915
- privateOptions: [opts.humanJsonOutputFormat],
916
- commands: [drainCreateCommand, drainGetCommand, drainRemoveCommand, drainEnableCommand, drainDisableCommand],
917
- },
918
- drain.list,
919
- );
920
-
921
- // EMAILS COMMANDS
922
- const emailsAddCommand = cliparse.command(
923
- 'add',
924
- {
925
- description: 'Add a new secondary email address to the current user',
926
- args: [args.email],
927
- },
928
- emails.addSecondary,
929
- );
930
- const emailsPrimaryCommand = cliparse.command(
931
- 'primary',
932
- {
933
- description: 'Set the primary email address of the current user',
934
- args: [args.email],
935
- },
936
- emails.setPrimary,
937
- );
938
- const emailsRemoveCommand = cliparse.command(
939
- 'remove',
940
- {
941
- description: 'Remove a secondary email address from the current user',
942
- args: [args.email],
943
- },
944
- emails.removeSecondary,
945
- );
946
- const emailsClearCommand = cliparse.command(
947
- 'remove-all',
948
- {
949
- description: 'Remove all secondary email addresses from the current user',
950
- options: [opts.yes],
951
- },
952
- emails.removeAllSecondary,
953
- );
954
- const emailsOpenConsoleCommand = cliparse.command(
955
- 'open',
956
- {
957
- description: 'Open the email addresses management page in the Console',
958
- },
959
- emails.openConsole,
960
- );
961
- const emailsCommands = cliparse.command(
962
- 'emails',
963
- {
964
- description: 'Manage email addresses of the current user',
965
- privateOptions: [opts.humanJsonOutputFormat],
966
- commands: [
967
- emailsAddCommand,
968
- emailsPrimaryCommand,
969
- emailsRemoveCommand,
970
- emailsClearCommand,
971
- emailsOpenConsoleCommand,
972
- ],
973
- },
974
- emails.list,
975
- );
51
+ // Get enabled experimental features
52
+ /** @type {Record<string, boolean>} */
53
+ const featuresFromConf = await getFeatures();
976
54
 
977
- // ENV COMMANDS
978
- const envSetCommand = cliparse.command(
979
- 'set',
980
- {
981
- description: 'Add or update an environment variable named <variable-name> with the value <variable-value>',
982
- args: [args.envVariableName, args.envVariableValue],
983
- },
984
- env.set,
985
- );
986
- const envRemoveCommand = cliparse.command(
987
- 'rm',
988
- {
989
- description: 'Remove an environment variable from an application',
990
- args: [args.envVariableName],
991
- },
992
- env.rm,
993
- );
994
- const envImportCommand = cliparse.command(
995
- 'import',
996
- {
997
- description:
998
- 'Load environment variables from STDIN\n(WARNING: this deletes all current variables and replace them with the new list loaded from STDIN)',
999
- options: [opts.importAsJson],
1000
- },
1001
- env.importEnv,
1002
- );
1003
- const envImportVarsFromLocalEnvCommand = cliparse.command(
1004
- 'import-vars',
1005
- {
1006
- description:
1007
- 'Add or update environment variables named <variable-names> (comma-separated), taking their values from the current environment',
1008
- args: [args.envVariableNames],
1009
- },
1010
- env.importVarsFromLocalEnv,
1011
- );
1012
- const envCommands = cliparse.command(
1013
- 'env',
1014
- {
1015
- description: 'Manage environment variables of an application',
1016
- options: [opts.alias, opts.appIdOrName, opts.sourceableEnvVarsList],
1017
- privateOptions: [opts.envFormat],
1018
- commands: [envSetCommand, envRemoveCommand, envImportCommand, envImportVarsFromLocalEnvCommand],
1019
- },
1020
- env.list,
1021
- );
55
+ // Build all commands from globalCommands
56
+ const commands = [];
57
+ for (const [name, entry] of /** @type {[string, CommandEntry][]} */ (Object.entries(globalCommands))) {
58
+ const command = buildCommand(name, entry, featuresFromConf);
59
+ if (command != null) {
60
+ commands.push(command);
61
+ }
62
+ }
1022
63
 
1023
- // EXPERIMENTAL FEATURES COMMAND
1024
- const listFeaturesCommand = cliparse.command(
1025
- 'list',
1026
- {
1027
- description: 'List available experimental features',
1028
- options: [opts.humanJsonOutputFormat],
1029
- },
1030
- features.list,
1031
- );
1032
- const infoFeaturesCommand = cliparse.command(
1033
- 'info',
1034
- {
1035
- description: 'Display info about an experimental feature',
1036
- args: [args.featureId],
1037
- },
1038
- features.info,
1039
- );
1040
- const enableFeatureCommand = cliparse.command(
1041
- 'enable',
1042
- {
1043
- description: 'Enable experimental features',
1044
- args: [args.features],
1045
- },
1046
- features.enable,
1047
- );
1048
- const disableFeatureCommand = cliparse.command(
1049
- 'disable',
1050
- {
1051
- description: 'Disable experimental features',
1052
- args: [args.features],
1053
- },
1054
- features.disable,
1055
- );
1056
- const featuresCommands = cliparse.command(
1057
- 'features',
1058
- {
1059
- description: 'Manage Clever Tools experimental features',
1060
- commands: [enableFeatureCommand, disableFeatureCommand, listFeaturesCommand, infoFeaturesCommand],
64
+ const rootCommandDefinition = {
65
+ description: "CLI tool to manage Clever Cloud's data and products",
66
+ options: {
67
+ help: helpOption,
68
+ version: versionOption,
69
+ verbose: verboseOption,
70
+ color: colorOption,
71
+ 'update-notifier': updateNotifierOption,
1061
72
  },
1062
- features.list,
1063
- );
73
+ };
1064
74
 
1065
- // K8S COMMAND
1066
- const k8sListCommand = cliparse.command(
1067
- 'list',
1068
- {
1069
- description: 'List Kubernetes clusters',
1070
- options: [opts.orgaIdOrName, opts.humanJsonOutputFormat],
1071
- },
1072
- k8s.list,
1073
- );
1074
- const k8sCreateCommand = cliparse.command(
1075
- 'create',
1076
- {
1077
- description: 'Create a Kubernetes cluster',
1078
- args: [args.k8sClusterName],
1079
- options: [opts.orgaIdOrName, opts.k8sDeployWatch],
1080
- },
1081
- k8s.create,
1082
- );
1083
- const k8sDeleteCommand = cliparse.command(
1084
- 'delete',
1085
- {
1086
- description: 'Delete a Kubernetes cluster',
1087
- options: [opts.orgaIdOrName, opts.confirmAddonDeletion],
1088
- args: [args.k8sIdOrName],
1089
- },
1090
- k8s.del,
1091
- );
1092
- const k8sGetCommand = cliparse.command(
1093
- 'get',
1094
- {
1095
- description: 'Get information about a Kubernetes cluster',
1096
- args: [args.k8sIdOrName],
1097
- options: [opts.orgaIdOrName, opts.humanJsonOutputFormat],
1098
- },
1099
- k8s.get,
1100
- );
1101
- const k8sGetConfigCommand = cliparse.command(
1102
- 'get-kubeconfig',
1103
- {
1104
- description: 'Get configuration of a Kubernetes cluster',
1105
- args: [args.k8sIdOrName],
1106
- options: [opts.orgaIdOrName],
1107
- },
1108
- k8s.getConfig,
1109
- );
1110
- const k8sAddPersistentStorageCommand = cliparse.command(
1111
- 'add-persistent-storage',
1112
- {
1113
- description: 'Activate persistent storage to a deployed Kubernetes cluster',
1114
- args: [args.k8sIdOrName],
1115
- options: [opts.orgaIdOrName],
1116
- },
1117
- k8s.addPersistentStorage,
1118
- );
75
+ // Add help command and sort all commands
76
+ const sortedCommands = commands.sort((a, b) => a.name.localeCompare(b.name));
1119
77
 
1120
- const k8sCommand = cliparse.command('k8s', {
1121
- description: 'Manage Kubernetes clusters',
1122
- commands: [
1123
- k8sListCommand,
1124
- k8sCreateCommand,
1125
- k8sAddPersistentStorageCommand,
1126
- k8sDeleteCommand,
1127
- k8sGetCommand,
1128
- k8sGetConfigCommand,
1129
- ],
78
+ const cliParser = cliparse.cli({
79
+ name: 'clever',
80
+ description: rootCommandDefinition.description,
81
+ version: pkg.version,
82
+ options: [convertOption(colorOption), convertOption(updateNotifierOption), convertOption(verboseOption)],
83
+ helpCommand: false,
84
+ commands: sortedCommands,
1130
85
  });
1131
86
 
1132
- // KEYCLOAK COMMAND
1133
- const keycloakGetCommand = cliparse.command(
1134
- 'get',
1135
- {
1136
- description: 'Get information about a deployed Keycloak',
1137
- args: [args.addonIdOrName],
1138
- options: [opts.humanJsonOutputFormat],
1139
- },
1140
- keycloak.get,
1141
- );
1142
- const keycloakEnableNgCommand = cliparse.command(
1143
- 'enable-ng',
1144
- {
1145
- description: 'Link Keycloak to a Network Group, used for multi-instances secure communication',
1146
- args: [args.addonIdOrName],
1147
- },
1148
- keycloak.ngEnable,
1149
- );
1150
- const keycloakDisableNgCommand = cliparse.command(
1151
- 'disable-ng',
1152
- {
1153
- description: 'Unlink Keycloak from its Network Group',
1154
- args: [args.addonIdOrName],
1155
- },
1156
- keycloak.ngDisable,
1157
- );
1158
- const keycloakOpenLogsCommand = cliparse.command(
1159
- 'logs',
1160
- {
1161
- description: 'Open the Keycloak application logs in Clever Cloud Console',
1162
- args: [args.addonIdOrName],
1163
- },
1164
- keycloak.openLogs,
1165
- );
1166
- const keycloakOpenWebUiCommand = cliparse.command(
1167
- 'webui',
1168
- {
1169
- description: 'Open the Keycloak admin console in your browser',
1170
- args: [args.addonIdOrName],
1171
- },
1172
- keycloak.openWebUi,
1173
- );
1174
- const keycloakOpenCommand = cliparse.command(
1175
- 'open',
1176
- {
1177
- description: 'Open the Keycloak dashboard in Clever Cloud Console',
1178
- args: [args.addonIdOrName],
1179
- commands: [keycloakOpenLogsCommand, keycloakOpenWebUiCommand],
1180
- },
1181
- keycloak.open,
1182
- );
1183
- const keycloakRestartCommand = cliparse.command(
1184
- 'restart',
1185
- {
1186
- description: 'Restart Keycloak',
1187
- args: [args.addonIdOrName],
1188
- },
1189
- keycloak.reboot,
1190
- );
1191
- const keycloakRebuildCommand = cliparse.command(
1192
- 'rebuild',
1193
- {
1194
- description: 'Rebuild Keycloak',
1195
- args: [args.addonIdOrName],
1196
- },
1197
- keycloak.rebuild,
1198
- );
1199
- const keycloakVersionCheckCommand = cliparse.command(
1200
- 'check',
1201
- {
1202
- description: 'Check Keycloak deployed version',
1203
- args: [args.addonIdOrName],
1204
- options: [opts.humanJsonOutputFormat],
1205
- },
1206
- keycloak.checkVersion,
1207
- );
1208
- const keycloakVersionUpdateCommand = cliparse.command(
1209
- 'update',
1210
- {
1211
- description: 'Update Keycloak deployed version',
1212
- args: [args.addonIdOrName],
1213
- options: [opts.targetVersion],
1214
- },
1215
- keycloak.updateVersion,
1216
- );
1217
- const keycloakVersionsCommands = cliparse.command(
1218
- 'version',
1219
- {
1220
- description: 'Check Keycloak deployed version',
1221
- args: [args.addonIdOrName],
1222
- privateOptions: [opts.humanJsonOutputFormat],
1223
- commands: [keycloakVersionCheckCommand, keycloakVersionUpdateCommand],
1224
- },
1225
- keycloak.checkVersion,
1226
- );
1227
- const keycloakCommand = cliparse.command(
1228
- 'keycloak',
1229
- {
1230
- description: 'Manage Clever Cloud Keycloak services',
1231
- privateOptions: [opts.humanJsonOutputFormat],
1232
- commands: [
1233
- keycloakGetCommand,
1234
- keycloakEnableNgCommand,
1235
- keycloakDisableNgCommand,
1236
- keycloakOpenCommand,
1237
- keycloakRestartCommand,
1238
- keycloakRebuildCommand,
1239
- keycloakVersionsCommands,
1240
- ],
1241
- },
1242
- keycloak.list,
1243
- );
1244
-
1245
- // KV COMMAND
1246
- const kvRawCommand = cliparse.command(
1247
- 'kv',
1248
- {
1249
- description: 'Send a raw command to a Materia KV or Redis® add-on',
1250
- args: [args.kvIdOrName, args.kvRawCommand],
1251
- options: [opts.orgaIdOrName, opts.humanJsonOutputFormat],
1252
- },
1253
- kv.sendRawCommand,
1254
- );
1255
-
1256
- // LINK COMMAND
1257
- const appLinkCommand = cliparse.command(
1258
- 'link',
1259
- {
1260
- description: 'Link this repo to an existing application',
1261
- args: [args.appIdOrName],
1262
- options: [opts.aliasCreation, opts.orgaIdOrName],
1263
- },
1264
- link.link,
1265
- );
1266
-
1267
- // LOGIN COMMAND
1268
- const loginCommand = cliparse.command(
1269
- 'login',
1270
- {
1271
- description: 'Login to Clever Cloud',
1272
- options: [opts.loginToken, opts.loginSecret],
1273
- },
1274
- login.login,
1275
- );
1276
-
1277
- // LOGOUT COMMAND
1278
- const logoutCommand = cliparse.command(
1279
- 'logout',
1280
- {
1281
- description: 'Logout from Clever Cloud',
1282
- },
1283
- logout.logout,
1284
- );
1285
-
1286
- // LOGS COMMAND
1287
- const logsCommand = cliparse.command(
1288
- 'logs',
1289
- {
1290
- description: 'Fetch application logs, continuously',
1291
- options: [
1292
- opts.alias,
1293
- opts.appIdOrName,
1294
- opts.before,
1295
- opts.after,
1296
- opts.search,
1297
- opts.deploymentId,
1298
- opts.addonId,
1299
- opts.logsFormat,
1300
- ],
1301
- },
1302
- logs.appLogs,
1303
- );
1304
-
1305
- // MAKE DEFAULT COMMAND
1306
- const makeDefaultCommand = cliparse.command(
1307
- 'make-default',
1308
- {
1309
- description: 'Make a linked application the default one',
1310
- args: [args.alias],
1311
- },
1312
- makeDefault.makeDefault,
1313
- );
1314
-
1315
- // MATOMO COMMAND
1316
- const matomoGetCommand = cliparse.command(
1317
- 'get',
1318
- {
1319
- description: 'Get information about a deployed Matomo',
1320
- args: [args.addonIdOrName],
1321
- options: [opts.humanJsonOutputFormat],
1322
- },
1323
- matomo.get,
1324
- );
1325
- const matomoOpenLogsCommand = cliparse.command(
1326
- 'logs',
1327
- {
1328
- description: 'Open the Matomo application logs in Clever Cloud Console',
1329
- args: [args.addonIdOrName],
1330
- },
1331
- matomo.openLogs,
1332
- );
1333
- const matomoOpenWebUiCommand = cliparse.command(
1334
- 'webui',
1335
- {
1336
- description: 'Open the Matomo admin console in your browser',
1337
- args: [args.addonIdOrName],
1338
- },
1339
- matomo.openWebUi,
1340
- );
1341
- const matomoOpenCommand = cliparse.command(
1342
- 'open',
1343
- {
1344
- description: 'Open the Matomo dashboard in Clever Cloud Console',
1345
- args: [args.addonIdOrName],
1346
- commands: [matomoOpenLogsCommand, matomoOpenWebUiCommand],
1347
- },
1348
- matomo.open,
1349
- );
1350
- const matomoRestartCommand = cliparse.command(
1351
- 'restart',
1352
- {
1353
- description: 'Restart Matomo',
1354
- args: [args.addonIdOrName],
1355
- },
1356
- matomo.reboot,
1357
- );
1358
- const matomoRebuildCommand = cliparse.command(
1359
- 'rebuild',
1360
- {
1361
- description: 'Rebuild Matomo',
1362
- args: [args.addonIdOrName],
1363
- },
1364
- matomo.rebuild,
1365
- );
1366
- const matomoCommand = cliparse.command(
1367
- 'matomo',
1368
- {
1369
- description: 'Manage Clever Cloud Matomo services',
1370
- privateOptions: [opts.humanJsonOutputFormat],
1371
- commands: [matomoGetCommand, matomoOpenCommand, matomoRestartCommand, matomoRebuildCommand],
1372
- },
1373
- matomo.list,
1374
- );
87
+ // Attach root definition so cliparse-patched.js can use it for --help display
88
+ cliParser._definition = rootCommandDefinition;
1375
89
 
1376
- // METABASE COMMAND
1377
- const metabaseGetCommand = cliparse.command(
1378
- 'get',
1379
- {
1380
- description: 'Get information about a deployed Metabase',
1381
- args: [args.addonIdOrName],
1382
- options: [opts.humanJsonOutputFormat],
1383
- },
1384
- metabase.get,
1385
- );
1386
- const metabaseOpenLogsCommand = cliparse.command(
1387
- 'logs',
1388
- {
1389
- description: 'Open the Metabase application logs in Clever Cloud Console',
1390
- args: [args.addonIdOrName],
1391
- },
1392
- metabase.openLogs,
1393
- );
1394
- const metabaseOpenWebUiCommand = cliparse.command(
1395
- 'webui',
1396
- {
1397
- description: 'Open the Metabase admin console in your browser',
1398
- args: [args.addonIdOrName],
1399
- },
1400
- metabase.openWebUi,
1401
- );
1402
- const metabaseOpenCommand = cliparse.command(
1403
- 'open',
1404
- {
1405
- description: 'Open the Metabase dashboard in Clever Cloud Console',
1406
- args: [args.addonIdOrName],
1407
- commands: [metabaseOpenLogsCommand, metabaseOpenWebUiCommand],
1408
- },
1409
- metabase.open,
1410
- );
1411
- const metabaseRestartCommand = cliparse.command(
1412
- 'restart',
1413
- {
1414
- description: 'Restart Metabase',
1415
- args: [args.addonIdOrName],
1416
- },
1417
- metabase.reboot,
1418
- );
1419
- const metabaseRebuildCommand = cliparse.command(
1420
- 'rebuild',
1421
- {
1422
- description: 'Rebuild Metabase',
1423
- args: [args.addonIdOrName],
1424
- },
1425
- metabase.rebuild,
1426
- );
1427
- const metabaseVersionCheckCommand = cliparse.command(
1428
- 'check',
1429
- {
1430
- description: 'Check Metabase deployed version',
1431
- args: [args.addonIdOrName],
1432
- options: [opts.humanJsonOutputFormat],
1433
- },
1434
- metabase.checkVersion,
1435
- );
1436
- const metabaseVersionUpdateCommand = cliparse.command(
1437
- 'update',
1438
- {
1439
- description: 'Update Metabase deployed version',
1440
- args: [args.addonIdOrName],
1441
- options: [opts.targetVersion],
1442
- },
1443
- metabase.updateVersion,
1444
- );
1445
- const metabaseVersionsCommands = cliparse.command(
1446
- 'version',
1447
- {
1448
- description: 'Manage Metabase deployed version',
1449
- args: [args.addonIdOrName],
1450
- privateOptions: [opts.humanJsonOutputFormat],
1451
- commands: [metabaseVersionCheckCommand, metabaseVersionUpdateCommand],
1452
- },
1453
- metabase.checkVersion,
1454
- );
1455
- const metabaseCommand = cliparse.command(
1456
- 'metabase',
1457
- {
1458
- description: 'Manage Clever Cloud Metabase services',
1459
- privateOptions: [opts.humanJsonOutputFormat],
1460
- commands: [
1461
- metabaseGetCommand,
1462
- metabaseOpenCommand,
1463
- metabaseRestartCommand,
1464
- metabaseRebuildCommand,
1465
- metabaseVersionsCommands,
1466
- ],
1467
- },
1468
- metabase.list,
1469
- );
1470
-
1471
- // NETWORK GROUP COMMANDS
1472
- const ngCreateExternalPeerCommand = cliparse.command(
1473
- 'external',
1474
- {
1475
- description: 'Create an external peer in a Network Group',
1476
- args: [args.ngExternalPeerLabel, args.ngIdOrLabel, args.wgPublicKey],
1477
- },
1478
- ng.createExternalPeer,
1479
- );
1480
- const ngDeleteExternalPeerCommand = cliparse.command(
1481
- 'external',
1482
- {
1483
- description: 'Delete an external peer from a Network Group',
1484
- args: [args.ngExternalIdOrLabel, args.ngIdOrLabel],
1485
- },
1486
- ng.deleteExternalPeer,
1487
- );
1488
- const ngCreateCommand = cliparse.command(
1489
- 'create',
1490
- {
1491
- description: 'Create a Network Group',
1492
- args: [args.ngLabel],
1493
- privateOptions: [opts.ngMembersIdsToLink, opts.ngDescription, opts.optTags],
1494
- commands: [ngCreateExternalPeerCommand],
1495
- },
1496
- ng.createNg,
1497
- );
1498
- const ngDeleteCommand = cliparse.command(
1499
- 'delete',
1500
- {
1501
- description: 'Delete a Network Group',
1502
- args: [args.ngIdOrLabel],
1503
- commands: [ngDeleteExternalPeerCommand],
1504
- },
1505
- ng.deleteNg,
1506
- );
1507
- const ngLinkCommand = cliparse.command(
1508
- 'link',
1509
- {
1510
- description:
1511
- 'Link a resource by its ID (app_xxx, external_xxx, mysql_xxx, postgresql_xxx, redis_xxx, etc.) to a Network Group',
1512
- args: [args.ngResourceId, args.ngIdOrLabel],
1513
- },
1514
- ng.linkToNg,
1515
- );
1516
- const ngUnlinkCommand = cliparse.command(
1517
- 'unlink',
1518
- {
1519
- description:
1520
- 'Unlink a resource by its ID (app_xxx, external_xxx, mysql_xxx, postgresql_xxx, redis_xxx, etc.) from a Network Group',
1521
- args: [args.ngResourceId, args.ngIdOrLabel],
1522
- },
1523
- ng.unlinkFromNg,
1524
- );
1525
- const ngGetCommand = cliparse.command(
1526
- 'get',
1527
- {
1528
- description: 'Get details about a Network Group, a member or a peer',
1529
- args: [args.ngAnyIdOrLabel],
1530
- options: [opts.ngResourceType, opts.humanJsonOutputFormat],
1531
- },
1532
- ng.get,
1533
- );
1534
- const ngGetConfigCommand = cliparse.command(
1535
- 'get-config',
1536
- {
1537
- description: 'Get the WireGuard configuration of a peer in a Network Group',
1538
- args: [args.ngExternalIdOrLabel, args.ngIdOrLabel],
1539
- options: [opts.humanJsonOutputFormat],
1540
- },
1541
- ng.getPeerConfig,
1542
- );
1543
- const ngSearchCommand = cliparse.command(
1544
- 'search',
1545
- {
1546
- description: 'Search Network Groups, members or peers and get their details',
1547
- args: [args.ngAnyIdOrLabel],
1548
- options: [opts.ngResourceType, opts.humanJsonOutputFormat],
1549
- },
1550
- ng.search,
1551
- );
1552
- const networkGroupsCommand = cliparse.command(
1553
- 'ng',
1554
- {
1555
- description: 'List Network Groups',
1556
- options: [opts.orgaIdOrName],
1557
- privateOptions: [opts.humanJsonOutputFormat],
1558
- commands: [
1559
- ngCreateCommand,
1560
- ngDeleteCommand,
1561
- ngLinkCommand,
1562
- ngUnlinkCommand,
1563
- ngGetCommand,
1564
- ngGetConfigCommand,
1565
- ngSearchCommand,
1566
- ],
1567
- },
1568
- ng.listNg,
1569
- );
1570
-
1571
- // NOTIFY-EMAIL COMMAND
1572
- const addEmailNotificationCommand = cliparse.command(
1573
- 'add',
1574
- {
1575
- description: 'Add a new email notification',
1576
- options: [opts.notificationEventType, opts.notificationScope, opts.emailNotificationTarget],
1577
- args: [args.notificationName],
1578
- },
1579
- notifyEmail.add,
1580
- );
1581
- const removeEmailNotificationCommand = cliparse.command(
1582
- 'remove',
1583
- {
1584
- description: 'Remove an existing email notification',
1585
- args: [args.notificationId],
1586
- },
1587
- notifyEmail.remove,
1588
- );
1589
- const emailNotificationsCommand = cliparse.command(
1590
- 'notify-email',
1591
- {
1592
- description: 'Manage email notifications',
1593
- options: [opts.orgaIdOrName, opts.listAllNotifications],
1594
- privateOptions: [opts.humanJsonOutputFormat],
1595
- commands: [addEmailNotificationCommand, removeEmailNotificationCommand],
1596
- },
1597
- notifyEmail.list,
1598
- );
1599
-
1600
- // OPEN COMMAND
1601
- const openCommand = cliparse.command(
1602
- 'open',
1603
- {
1604
- description: 'Open an application in the Console',
1605
- options: [opts.alias, opts.appIdOrName],
1606
- },
1607
- open.open,
1608
- );
1609
-
1610
- // OTOROSHI COMMAND
1611
- const otoroshiGetCommand = cliparse.command(
1612
- 'get',
1613
- {
1614
- description: 'Get information about a deployed Otoroshi',
1615
- args: [args.addonIdOrName],
1616
- options: [opts.humanJsonOutputFormat],
1617
- },
1618
- otoroshi.get,
1619
- );
1620
- const otoroshiGetConfigCommand = cliparse.command(
1621
- 'get-config',
1622
- {
1623
- description: 'Get configuration of a deployed Otoroshi in otoroshictl format',
1624
- args: [args.addonIdOrName],
1625
- },
1626
- otoroshi.getConfig,
1627
- );
1628
- const otoroshiEnableNgCommand = cliparse.command(
1629
- 'enable-ng',
1630
- {
1631
- description: 'Link Otoroshi to a Network Group',
1632
- args: [args.addonIdOrName],
1633
- },
1634
- otoroshi.ngEnable,
1635
- );
1636
- const otoroshiDisableNgCommand = cliparse.command(
1637
- 'disable-ng',
1638
- {
1639
- description: 'Unlink Otoroshi from its Network Group',
1640
- args: [args.addonIdOrName],
1641
- },
1642
- otoroshi.ngDisable,
1643
- );
1644
- const otoroshiOpenLogsCommand = cliparse.command(
1645
- 'logs',
1646
- {
1647
- description: 'Open the Otoroshi application logs in Clever Cloud Console',
1648
- args: [args.addonIdOrName],
1649
- },
1650
- otoroshi.openLogs,
1651
- );
1652
- const otoroshiOpenWebUiCommand = cliparse.command(
1653
- 'webui',
1654
- {
1655
- description: 'Open the Otoroshi admin console in your browser',
1656
- args: [args.addonIdOrName],
1657
- },
1658
- otoroshi.openWebUi,
1659
- );
1660
- const otoroshiOpenCommand = cliparse.command(
1661
- 'open',
1662
- {
1663
- description: 'Open the Otoroshi dashboard in Clever Cloud Console',
1664
- args: [args.addonIdOrName],
1665
- commands: [otoroshiOpenLogsCommand, otoroshiOpenWebUiCommand],
1666
- },
1667
- otoroshi.open,
1668
- );
1669
- const otoroshiRestartCommand = cliparse.command(
1670
- 'restart',
1671
- {
1672
- description: 'Restart Otoroshi',
1673
- args: [args.addonIdOrName],
1674
- },
1675
- otoroshi.reboot,
1676
- );
1677
- const otoroshiRebuildCommand = cliparse.command(
1678
- 'rebuild',
1679
- {
1680
- description: 'Rebuild Otoroshi',
1681
- args: [args.addonIdOrName],
1682
- },
1683
- otoroshi.rebuild,
1684
- );
1685
- const otoroshiVersionCheckCommand = cliparse.command(
1686
- 'check',
1687
- {
1688
- description: 'Check Otoroshi deployed version',
1689
- args: [args.addonIdOrName],
1690
- options: [opts.humanJsonOutputFormat],
1691
- },
1692
- otoroshi.checkVersion,
1693
- );
1694
- const otoroshiVersionUpdateCommand = cliparse.command(
1695
- 'update',
1696
- {
1697
- description: 'Update Otoroshi deployed version',
1698
- args: [args.addonIdOrName],
1699
- options: [opts.targetVersion],
1700
- },
1701
- otoroshi.updateVersion,
1702
- );
1703
- const otoroshiVersionsCommands = cliparse.command(
1704
- 'version',
1705
- {
1706
- description: 'Manage Otoroshi deployed version',
1707
- args: [args.addonIdOrName],
1708
- privateOptions: [opts.humanJsonOutputFormat],
1709
- commands: [otoroshiVersionCheckCommand, otoroshiVersionUpdateCommand],
1710
- },
1711
- otoroshi.checkVersion,
1712
- );
1713
- const otoroshiCommand = cliparse.command(
1714
- 'otoroshi',
1715
- {
1716
- description: 'Manage Clever Cloud Otoroshi services',
1717
- privateOptions: [opts.humanJsonOutputFormat],
1718
- commands: [
1719
- otoroshiGetCommand,
1720
- otoroshiGetConfigCommand,
1721
- otoroshiEnableNgCommand,
1722
- otoroshiDisableNgCommand,
1723
- otoroshiOpenCommand,
1724
- otoroshiRestartCommand,
1725
- otoroshiRebuildCommand,
1726
- otoroshiVersionsCommands,
1727
- ],
1728
- },
1729
- otoroshi.list,
1730
- );
1731
-
1732
- // CONSOLE COMMAND
1733
- const consoleCommand = cliparse.command(
1734
- 'console',
1735
- {
1736
- description: 'Open an application in the Console',
1737
- options: [opts.alias, opts.appIdOrName],
1738
- },
1739
- consoleModule.openConsole,
1740
- );
1741
-
1742
- // PROFILE COMMAND
1743
- const profileOpenCommand = cliparse.command(
1744
- 'open',
1745
- {
1746
- description: 'Open your profile in the Console',
1747
- },
1748
- profile.openProfile,
1749
- );
1750
- const profileCommand = cliparse.command(
1751
- 'profile',
1752
- {
1753
- description: 'Display the profile of the current user',
1754
- options: [opts.humanJsonOutputFormat],
1755
- commands: [profileOpenCommand],
1756
- },
1757
- profile.profile,
1758
- );
1759
-
1760
- // PUBLISHED CONFIG COMMANDS
1761
- const publishedConfigSetCommand = cliparse.command(
1762
- 'set',
1763
- {
1764
- description: 'Add or update a published configuration item named <variable-name> with the value <variable-value>',
1765
- args: [args.envVariableName, args.envVariableValue],
1766
- },
1767
- publishedConfig.set,
1768
- );
1769
- const publishedConfigRemoveCommand = cliparse.command(
1770
- 'rm',
1771
- {
1772
- description: 'Remove a published configuration variable from an application',
1773
- args: [args.envVariableName],
1774
- },
1775
- publishedConfig.rm,
1776
- );
1777
- const publishedConfigImportCommand = cliparse.command(
1778
- 'import',
1779
- {
1780
- description:
1781
- 'Load published configuration from STDIN\n(WARNING: this deletes all current variables and replace them with the new list loaded from STDIN)',
1782
- options: [opts.importAsJson],
1783
- },
1784
- publishedConfig.importEnv,
1785
- );
1786
- const publishedConfigCommands = cliparse.command(
1787
- 'published-config',
1788
- {
1789
- description: 'Manage the configuration made available to other applications by this application',
1790
- options: [opts.alias, opts.appIdOrName],
1791
- privateOptions: [opts.envFormat],
1792
- commands: [publishedConfigSetCommand, publishedConfigRemoveCommand, publishedConfigImportCommand],
1793
- },
1794
- publishedConfig.list,
1795
- );
90
+ // Make sure argv[0] is always "node"
91
+ const cliArgs = process.argv;
92
+ cliArgs[0] = 'node';
93
+ cliparse.parse(cliParser, cliArgs);
94
+ }
1796
95
 
1797
- // RESTART COMMAND
1798
- const restartCommand = cliparse.command(
1799
- 'restart',
1800
- {
1801
- description: 'Start or restart an application',
1802
- options: [
1803
- opts.alias,
1804
- opts.appIdOrName,
1805
- opts.commit,
1806
- opts.withoutCache,
1807
- opts.quiet,
1808
- opts.followDeployLogs,
1809
- opts.exitOnDeploy,
1810
- ],
1811
- },
1812
- restart.restart,
1813
- );
96
+ /**
97
+ * Recursively build commands from the global commands structure
98
+ * @param {string} name - Command name
99
+ * @param {CommandEntry} commandEntry - Command entry (either a command object or [command, subcommands])
100
+ * @param {Record<string, boolean>} featuresFromConf - Enabled features configuration
101
+ * @returns {Object|null} cliparse command or null if filtered out
102
+ */
103
+ function buildCommand(name, commandEntry, featuresFromConf) {
104
+ /** @type {CommandDefinition} */
105
+ let commandDef;
106
+ /** @type {Record<string, CommandEntry>} */
107
+ let subcommandsMap = {};
108
+
109
+ // Handle both formats: plain object or [command, {subcommands}]
110
+ if (Array.isArray(commandEntry)) {
111
+ [commandDef, subcommandsMap] = commandEntry;
112
+ } else {
113
+ commandDef = commandEntry;
114
+ }
1814
115
 
1815
- // SCALE COMMAND
1816
- const scaleCommand = cliparse.command(
1817
- 'scale',
1818
- {
1819
- description: 'Change scalability of an application',
1820
- options: [
1821
- opts.alias,
1822
- opts.appIdOrName,
1823
- opts.flavor,
1824
- opts.minFlavor,
1825
- opts.maxFlavor,
1826
- opts.instances,
1827
- opts.minInstances,
1828
- opts.maxInstances,
1829
- opts.buildFlavor,
1830
- ],
1831
- },
1832
- scale.scale,
1833
- );
116
+ // Check if this is an experimental feature that needs to be enabled
117
+ if (commandDef.featureFlag && !featuresFromConf[commandDef.featureFlag]) {
118
+ return null;
119
+ }
1834
120
 
1835
- // SERVICE COMMANDS
1836
- const serviceLinkAppCommand = cliparse.command(
1837
- 'link-app',
1838
- {
1839
- description: 'Add an existing app as a dependency',
1840
- args: [args.appIdOrName],
1841
- },
1842
- service.linkApp,
1843
- );
1844
- const serviceUnlinkAppCommand = cliparse.command(
1845
- 'unlink-app',
1846
- {
1847
- description: 'Remove an app from the dependencies',
1848
- args: [args.appIdOrName],
1849
- },
1850
- service.unlinkApp,
1851
- );
1852
- const serviceLinkAddonCommand = cliparse.command(
1853
- 'link-addon',
1854
- {
1855
- description: 'Link an existing add-on to this application',
1856
- args: [args.addonIdOrName],
1857
- },
1858
- service.linkAddon,
1859
- );
1860
- const serviceUnlinkAddonCommand = cliparse.command(
1861
- 'unlink-addon',
1862
- {
1863
- description: 'Unlink an add-on from this application',
1864
- args: [args.addonIdOrName],
1865
- },
1866
- service.unlinkAddon,
1867
- );
1868
- const serviceCommands = cliparse.command(
1869
- 'service',
1870
- {
1871
- description: 'Manage service dependencies',
1872
- options: [opts.alias, opts.appIdOrName, opts.onlyApps, opts.onlyAddons, opts.showAll],
1873
- privateOptions: [opts.humanJsonOutputFormat],
1874
- commands: [serviceLinkAppCommand, serviceUnlinkAppCommand, serviceLinkAddonCommand, serviceUnlinkAddonCommand],
1875
- },
1876
- service.list,
1877
- );
121
+ // Build subcommands recursively
122
+ const subcommands = [];
123
+ for (const [subName, subEntry] of Object.entries(subcommandsMap)) {
124
+ const subcommand = buildCommand(subName, subEntry, featuresFromConf);
125
+ if (subcommand != null) {
126
+ subcommands.push(subcommand);
127
+ }
128
+ }
1878
129
 
1879
- // SSH COMMAND
1880
- const sshCommand = cliparse.command(
1881
- 'ssh',
1882
- {
1883
- description: 'Connect to running instances through SSH',
1884
- options: [opts.alias, opts.appIdOrName, opts.sshIdentityFile],
1885
- },
1886
- ssh.ssh,
1887
- );
130
+ // Build the command
131
+ const command = convertCommand(name, commandDef, subcommands);
1888
132
 
1889
- // SSH KEYS COMMANDS
1890
- const sshKeysAddCommand = cliparse.command(
1891
- 'add',
1892
- {
1893
- description: 'Add a new SSH key to the current user',
1894
- args: [args.sshKeyName, args.sshKeyPath],
1895
- },
1896
- sshKeys.add,
1897
- );
1898
- const sshKeysRemoveCommand = cliparse.command(
1899
- 'remove',
1900
- {
1901
- description: 'Remove a SSH key from the current user',
1902
- args: [args.sshKeyName],
1903
- },
1904
- sshKeys.remove,
1905
- );
1906
- const sshKeysRemoveAllCommand = cliparse.command(
1907
- 'remove-all',
1908
- {
1909
- description: 'Remove all SSH keys from the current user',
1910
- options: [opts.confirmSshKeyClean],
1911
- },
1912
- sshKeys.removeAll,
1913
- );
1914
- const sshKeysOpenConsoleCommand = cliparse.command(
1915
- 'open',
1916
- {
1917
- description: 'Open the SSH keys management page in the Console',
1918
- },
1919
- sshKeys.openConsole,
1920
- );
1921
- const sshKeysCommands = cliparse.command(
1922
- 'ssh-keys',
1923
- {
1924
- description: 'Manage SSH keys of the current user',
1925
- privateOptions: [opts.humanJsonOutputFormat],
1926
- commands: [sshKeysAddCommand, sshKeysRemoveCommand, sshKeysRemoveAllCommand, sshKeysOpenConsoleCommand],
1927
- },
1928
- sshKeys.list,
1929
- );
133
+ // Add experimental styling if needed
134
+ if (commandDef.isExperimental && commandDef.featureFlag) {
135
+ const featureInfo = EXPERIMENTAL_FEATURES[commandDef.featureFlag];
136
+ if (featureInfo != null) {
137
+ const status = featureInfo.status;
138
+ command.description = styleText('yellow', command.description + ' [' + status.toUpperCase() + ']');
139
+ }
140
+ }
1930
141
 
1931
- // STATUS COMMAND
1932
- const statusCommand = cliparse.command(
1933
- 'status',
1934
- {
1935
- description: 'See the status of an application',
1936
- options: [opts.alias, opts.appIdOrName, opts.humanJsonOutputFormat],
1937
- },
1938
- status.status,
1939
- );
142
+ return command;
143
+ }
1940
144
 
1941
- // STOP COMMAND
1942
- const stopCommand = cliparse.command(
1943
- 'stop',
1944
- {
1945
- description: 'Stop a running application',
1946
- options: [opts.alias, opts.appIdOrName],
1947
- },
1948
- stop.stop,
1949
- );
145
+ /**
146
+ * Convert a command definition to cliparse format
147
+ * @param {string} name - Command name
148
+ * @param {CommandDefinition} commandDef - Command definition object
149
+ * @param {Object[]} subcommands - Array of cliparse subcommands
150
+ * @returns {Object} cliparse command
151
+ */
152
+ function convertCommand(name, commandDef, subcommands = []) {
153
+ const config = {
154
+ description: commandDef.description,
155
+ };
1950
156
 
1951
- // TCP-REDIRS COMMAND
1952
- const tcpRedirsListNamespacesCommand = cliparse.command(
1953
- 'list-namespaces',
1954
- {
1955
- description: 'List the namespaces in which you can create new TCP redirections',
1956
- options: [opts.humanJsonOutputFormat],
1957
- },
1958
- tcpRedirs.listNamespaces,
1959
- );
1960
- const tcpRedirsAddCommand = cliparse.command(
1961
- 'add',
1962
- {
1963
- description: 'Add a new TCP redirection to the application',
1964
- options: [opts.namespace],
1965
- },
1966
- tcpRedirs.add,
1967
- );
1968
- const tcpRedirsRemoveCommand = cliparse.command(
1969
- 'remove',
1970
- {
1971
- description: 'Remove a TCP redirection from the application',
1972
- options: [opts.namespace],
1973
- args: [args.port],
1974
- },
1975
- tcpRedirs.remove,
1976
- );
1977
- const tcpRedirsCommands = cliparse.command(
1978
- 'tcp-redirs',
1979
- {
1980
- description: 'Control the TCP redirections from reverse proxies to your application',
1981
- options: [opts.alias, opts.appIdOrName],
1982
- privateOptions: [opts.humanJsonOutputFormat],
1983
- commands: [tcpRedirsListNamespacesCommand, tcpRedirsAddCommand, tcpRedirsRemoveCommand],
1984
- },
1985
- tcpRedirs.list,
1986
- );
157
+ if (commandDef.options != null) {
158
+ config.privateOptions = Object.values(commandDef.options).map(convertOption);
159
+ }
1987
160
 
1988
- // TOKENS COMMANDS
1989
- const apiTokenCreateCommand = cliparse.command(
1990
- 'create',
1991
- {
1992
- description: 'Create an API token',
1993
- args: [args.apiTokenName],
1994
- options: [opts.apiTokenExpiration, opts.humanJsonOutputFormat],
1995
- },
1996
- tokens.create,
1997
- );
1998
- const apiTokenRevokeCommand = cliparse.command(
1999
- 'revoke',
2000
- {
2001
- description: 'Revoke an API token',
2002
- args: [args.apiTokenId],
2003
- },
2004
- tokens.revoke,
2005
- );
2006
- const tokensCommands = cliparse.command(
2007
- 'tokens',
2008
- {
2009
- description: `Manage API tokens to query Clever Cloud API from ${conf.AUTH_BRIDGE_HOST}`,
2010
- commands: [apiTokenCreateCommand, apiTokenRevokeCommand],
2011
- privateOptions: [opts.humanJsonOutputFormat],
2012
- },
2013
- tokens.list,
2014
- );
161
+ // Convert arguments
162
+ if (commandDef.args != null && commandDef.args.length > 0) {
163
+ config.args = commandDef.args.map(convertArgument);
164
+ }
2015
165
 
2016
- // UNLINK COMMAND
2017
- const appUnlinkCommand = cliparse.command(
2018
- 'unlink',
2019
- {
2020
- description: 'Unlink this repo from an existing application',
2021
- args: [args.alias],
2022
- },
2023
- unlink.unlink,
2024
- );
166
+ // Add subcommands
167
+ if (subcommands.length > 0) {
168
+ config.commands = subcommands;
169
+ }
2025
170
 
2026
- // VERSION COMMAND
2027
- const versionCommand = cliparse.command(
2028
- 'version',
2029
- {
2030
- description: 'Display the clever-tools version',
2031
- args: [],
2032
- },
2033
- version.version,
2034
- );
171
+ const command = cliparse.command(name, config, commandDef.handler);
2035
172
 
2036
- // WEBHOOKS COMMAND
2037
- const addWebhookCommand = cliparse.command(
2038
- 'add',
2039
- {
2040
- description: 'Register webhook to be called when events happen',
2041
- options: [opts.webhookFormat, opts.notificationEventType, opts.notificationScope],
2042
- args: [args.notificationName, args.webhookUrl],
2043
- },
2044
- webhooks.add,
2045
- );
2046
- const removeWebhookCommand = cliparse.command(
2047
- 'remove',
2048
- {
2049
- description: 'Remove an existing webhook',
2050
- args: [args.notificationId],
2051
- },
2052
- webhooks.remove,
2053
- );
2054
- const webhooksCommand = cliparse.command(
2055
- 'webhooks',
2056
- {
2057
- description: 'Manage webhooks',
2058
- options: [opts.orgaIdOrName, opts.listAllNotifications],
2059
- privateOptions: [opts.humanJsonOutputFormat],
2060
- commands: [addWebhookCommand, removeWebhookCommand],
2061
- },
2062
- webhooks.list,
2063
- );
173
+ // Attach the original definition so cliparse-patched.js can use it for --help display
174
+ command._definition = commandDef;
2064
175
 
2065
- // DATABASES COMMANDS
2066
- const downloadBackupCommand = cliparse.command(
2067
- 'download',
2068
- {
2069
- description: 'Download a database backup',
2070
- args: [args.databaseId, args.backupId],
2071
- options: [opts.output],
2072
- },
2073
- database.downloadBackups,
2074
- );
2075
- const backupsCommand = cliparse.command(
2076
- 'backups',
2077
- {
2078
- description: 'List available database backups',
2079
- args: [args.databaseId],
2080
- options: [opts.orgaIdOrName, opts.humanJsonOutputFormat],
2081
- commands: [downloadBackupCommand],
2082
- },
2083
- database.listBackups,
2084
- );
2085
- const databaseCommand = cliparse.command('database', {
2086
- description: 'Manage databases and backups',
2087
- commands: [backupsCommand],
2088
- });
176
+ return command;
177
+ }
2089
178
 
2090
- // Patch help command description
2091
- cliparseCommands.helpCommand.description = 'Display help about the Clever Cloud CLI';
179
+ /**
180
+ * Convert an option definition to cliparse format
181
+ * @param {OptionDefinition} option - Option definition from global.options.js
182
+ * @returns {Object} cliparse option
183
+ */
184
+ function convertOption(option) {
185
+ const config = {
186
+ description: option.description,
187
+ };
2092
188
 
2093
- const commands = [
2094
- accesslogsCommand,
2095
- activityCommand,
2096
- addonCommands,
2097
- appCreateCommand,
2098
- applicationsCommand,
2099
- appLinkCommand,
2100
- appUnlinkCommand,
2101
- cancelDeployCommand,
2102
- configCommands,
2103
- curlCommand,
2104
- databaseCommand,
2105
- deleteCommand,
2106
- deployCommand,
2107
- diagCommand,
2108
- domainCommands,
2109
- drainCommands,
2110
- emailNotificationsCommand,
2111
- emailsCommands,
2112
- envCommands,
2113
- featuresCommands,
2114
- cliparseCommands.helpCommand,
2115
- loginCommand,
2116
- logoutCommand,
2117
- logsCommand,
2118
- makeDefaultCommand,
2119
- openCommand,
2120
- consoleCommand,
2121
- profileCommand,
2122
- publishedConfigCommands,
2123
- restartCommand,
2124
- scaleCommand,
2125
- serviceCommands,
2126
- sshCommand,
2127
- sshKeysCommands,
2128
- statusCommand,
2129
- stopCommand,
2130
- tcpRedirsCommands,
2131
- tokensCommands,
2132
- versionCommand,
2133
- webhooksCommand,
2134
- ];
189
+ if (option.aliases != null) {
190
+ config.aliases = option.aliases;
191
+ }
192
+ const optionDefault = getDefault(option.schema);
193
+ if (optionDefault != null) {
194
+ config.default = optionDefault;
195
+ }
196
+ if (option.placeholder != null) {
197
+ config.metavar = option.placeholder;
198
+ }
199
+ if (option.complete != null) {
200
+ config.complete = option.complete;
201
+ }
2135
202
 
2136
- // Add experimental features only if they are enabled through the configuration file
2137
- const featuresFromConf = await getFeatures();
203
+ // Use Zod's safeParse for validation (handles coercion, enums, refinements, etc.)
204
+ config.parser = (value) => {
205
+ // Log deprecation warning if option is deprecated
206
+ if (option.deprecated) {
207
+ const message = typeof option.deprecated === 'string' ? `, ${option.deprecated}.` : '';
208
+ console.error(styleText('yellow', `Warning: --${option.name} is deprecated${message}`));
209
+ }
210
+
211
+ const result = option.schema.safeParse(value);
212
+ if (!result.success) {
213
+ throw new Error(result.error.issues.map((i) => i.message).join(', '));
214
+ }
215
+ return result.data;
216
+ };
2138
217
 
2139
- if (featuresFromConf.operators) {
2140
- commands.push(colorizeExperimentalCommand(keycloakCommand, 'operators'));
2141
- commands.push(colorizeExperimentalCommand(matomoCommand, 'operators'));
2142
- commands.push(colorizeExperimentalCommand(metabaseCommand, 'operators'));
2143
- commands.push(colorizeExperimentalCommand(otoroshiCommand, 'operators'));
218
+ // Mark as required if schema has no default and is not optional
219
+ if (isRequired(option.schema)) {
220
+ config.required = true;
2144
221
  }
2145
222
 
2146
- if (featuresFromConf.k8s) {
2147
- commands.push(colorizeExperimentalCommand(k8sCommand, 'k8s'));
223
+ // Boolean schemas: use cliparse.option with expects_value: false (like cliparse.flag does)
224
+ if (isBoolean(option.schema)) {
225
+ // eslint-disable-next-line camelcase -- cliparse API uses snake_case
226
+ config.expects_value = false;
2148
227
  }
2149
228
 
2150
- if (featuresFromConf.kv) {
2151
- commands.push(colorizeExperimentalCommand(kvRawCommand, 'kv'));
2152
- }
229
+ return cliparse.option(option.name, config);
230
+ }
2153
231
 
2154
- if (featuresFromConf.ng) {
2155
- commands.push(colorizeExperimentalCommand(networkGroupsCommand, 'ng'));
232
+ /**
233
+ * Convert an argument definition to cliparse format
234
+ * @param {ArgumentDefinition} arg - Argument definition
235
+ * @returns {Object} cliparse argument
236
+ */
237
+ function convertArgument(arg) {
238
+ const config = {
239
+ description: arg.description,
240
+ };
241
+
242
+ if (arg.complete) {
243
+ config.complete = arg.complete;
2156
244
  }
2157
245
 
2158
- // CLI PARSER
2159
- const cliParser = cliparse.cli({
2160
- name: 'clever',
2161
- description: "CLI tool to manage Clever Cloud's data and products",
2162
- version: pkg.version,
2163
- options: [opts.color, opts.updateNotifier, opts.verbose],
2164
- helpCommand: false,
2165
- commands: _sortBy(commands, 'name'),
2166
- });
246
+ // Use Zod's safeParse for validation (handles coercion, enums, refinements, etc.)
247
+ config.parser = (value) => {
248
+ const result = arg.schema.safeParse(value);
249
+ if (!result.success) {
250
+ throw new Error(result.error.issues.map((i) => i.message).join(', '));
251
+ }
252
+ return result.data;
253
+ };
2167
254
 
2168
- // Make sure argv[0] is always "node"
2169
- const cliArgs = process.argv;
2170
- cliArgs[0] = 'node';
2171
- cliparse.parse(cliParser, cliArgs);
2172
- }
255
+ const argDefault = getDefault(arg.schema);
256
+ if (argDefault != null) {
257
+ config.default = argDefault;
258
+ } else if (!isRequired(arg.schema)) {
259
+ config.default = '';
260
+ }
2173
261
 
2174
- // Right now, this is the only way to do this properly
2175
- // cliparse doesn't allow unknown options/arguments
2176
- if (process.argv[2] === 'curl') {
2177
- curl();
2178
- } else {
2179
- run();
262
+ return cliparse.argument(arg.placeholder, config);
2180
263
  }