clever-tools 4.4.0 → 4.5.0

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