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
@@ -0,0 +1,173 @@
1
+ import path from 'node:path';
2
+ import { z } from 'zod';
3
+ import { defineArgument } from '../../lib/define-argument.js';
4
+ import { defineCommand } from '../../lib/define-command.js';
5
+ import { defineOption } from '../../lib/define-option.js';
6
+ import { styleText } from '../../lib/style-text.js';
7
+ import { Logger } from '../../logger.js';
8
+ import * as AppConfig from '../../models/app_configuration.js';
9
+ import * as Application from '../../models/application.js';
10
+ import { AVAILABLE_ZONES, listAvailableTypes, listAvailableZones } from '../../models/application.js';
11
+ import { conf } from '../../models/configuration.js';
12
+ import { isGitWorkingDirectoryClean, isInsideGitRepo } from '../../models/git.js';
13
+ import { aliasCreationOption, humanJsonOutputFormatOption, orgaIdOrNameOption } from '../global.options.js';
14
+
15
+ function getGithubDetails(githubOwnerRepo) {
16
+ if (githubOwnerRepo != null) {
17
+ const [owner, name] = githubOwnerRepo.split('/');
18
+ return { owner, name };
19
+ }
20
+ }
21
+
22
+ function getCurrentDirectoryName() {
23
+ return path.basename(process.cwd());
24
+ }
25
+
26
+ async function displayAppCreation(app, alias, github, taskCommand) {
27
+ Logger.printSuccess(`Application ${styleText('green', app.name)} successfully created!`);
28
+
29
+ const hasDistinctAlias = alias != null && alias !== app.name;
30
+ const isTask = app.instance.lifetime === 'TASK';
31
+
32
+ Logger.println();
33
+ printFieldsAsTable(2, {
34
+ Type: styleText('blue', `⬢ ${app.instance.variant.name}`),
35
+ ID: app.id,
36
+ 'Organisation ID': app.ownerId,
37
+ Name: app.name,
38
+ GitHub: github != null && `${github.owner}/${github.name}`,
39
+ Alias: hasDistinctAlias && alias,
40
+ Zone: app.zone,
41
+ Task: isTask && `"${taskCommand}"`,
42
+ });
43
+
44
+ Logger.println();
45
+ Logger.println(' ' + styleText('bold', 'Next steps:'));
46
+
47
+ if (!github) {
48
+ const isInsideGit = await isInsideGitRepo();
49
+ if (!isInsideGit) {
50
+ Logger.println(` ${styleText('yellow', '!')} Initialize a git repository first, for example:`);
51
+ Logger.println(` ${shellCommand('git init')}`);
52
+ Logger.println(` ${shellCommand('git add .')}`);
53
+ Logger.println(` ${shellCommand('git commit -m "Initial commit"')}`);
54
+ Logger.println();
55
+ } else {
56
+ const isClean = await isGitWorkingDirectoryClean();
57
+ if (!isClean) {
58
+ Logger.println(` ${styleText('yellow', '!')} Commit your changes first:`);
59
+ Logger.println(` ${shellCommand('git add .')}`);
60
+ Logger.println(` ${shellCommand('git commit -m "My changes"')}`);
61
+ Logger.println();
62
+ }
63
+ }
64
+ }
65
+
66
+ if (github) {
67
+ Logger.println(
68
+ ` ${styleText('blue', '→')} Push changes to ${styleText('blue', `${github.owner}/${github.name}`)} GitHub repository to trigger a deployment, or ${styleText('blue', 'clever restart')} the latest pushed commit`,
69
+ );
70
+ } else {
71
+ Logger.println(
72
+ ` ${styleText('blue', '→')} Run ${styleText('blue', 'clever deploy')} ${isTask ? 'to execute your task' : 'to deploy your application'}`,
73
+ );
74
+ }
75
+
76
+ Logger.println(
77
+ ` ${styleText('blue', '→')} Manage your application at: ${styleText('underline', `${conf.GOTO_URL}/${app.id}`)}`,
78
+ );
79
+ Logger.println('');
80
+ }
81
+
82
+ function shellCommand(command) {
83
+ return `${styleText('grey', '$')} ${styleText('yellow', command)}`;
84
+ }
85
+
86
+ function printFieldsAsTable(indent, fields) {
87
+ const fieldsWithValues = Object.fromEntries(Object.entries(fields).filter(([_, value]) => typeof value === 'string'));
88
+ const labelMaxWidth = Math.max(...Object.keys(fieldsWithValues).map((label) => label.length));
89
+ return Object.entries(fieldsWithValues).forEach(([label, value]) => {
90
+ Logger.println(`${' '.repeat(indent)}${label.padEnd(labelMaxWidth, ' ')} ${styleText('grey', value)}`);
91
+ });
92
+ }
93
+
94
+ export const createCommand = defineCommand({
95
+ description: 'Create an application',
96
+ since: '0.2.0',
97
+ options: {
98
+ type: defineOption({
99
+ name: 'type',
100
+ schema: z.string(),
101
+ description: 'Instance type',
102
+ aliases: ['t'],
103
+ placeholder: 'instance-type',
104
+ complete: listAvailableTypes,
105
+ }),
106
+ region: defineOption({
107
+ name: 'region',
108
+ schema: z.string().default('par'),
109
+ description: `Region, can be ${AVAILABLE_ZONES.map((name) => `'${name}'`).join(', ')}`,
110
+ aliases: ['r'],
111
+ placeholder: 'zone',
112
+ complete: listAvailableZones,
113
+ }),
114
+ github: defineOption({
115
+ name: 'github',
116
+ schema: z.string().optional(),
117
+ description: 'GitHub application to use for deployments',
118
+ placeholder: 'owner/repo',
119
+ }),
120
+ task: defineOption({
121
+ name: 'task',
122
+ schema: z.string().min(1).optional(),
123
+ description: 'The application launch as a task executing the given command, then stopped',
124
+ aliases: ['T'],
125
+ placeholder: 'command',
126
+ }),
127
+ org: orgaIdOrNameOption,
128
+ alias: aliasCreationOption,
129
+ format: humanJsonOutputFormatOption,
130
+ },
131
+ args: [
132
+ defineArgument({
133
+ schema: z.string().optional(),
134
+ description: 'Application name (current directory name is used if not specified)',
135
+ placeholder: 'app-name',
136
+ }),
137
+ ],
138
+ async handler(options, rawName) {
139
+ const { type: typeName } = options;
140
+
141
+ const { org: orgaIdOrName, alias, region, github: githubOwnerRepo, format, task: taskCommand } = options;
142
+ const { apps } = await AppConfig.loadApplicationConf();
143
+
144
+ // Application name is optionnal, use current directory name if not specified (empty string)
145
+ const name = rawName !== '' ? rawName : getCurrentDirectoryName();
146
+
147
+ const isTask = taskCommand != null;
148
+ const envVars = isTask ? { CC_RUN_COMMAND: taskCommand } : {};
149
+
150
+ AppConfig.checkAlreadyLinked(apps, name, alias);
151
+
152
+ const github = getGithubDetails(githubOwnerRepo);
153
+ const app = await Application.create(name, typeName, region, orgaIdOrName, github, isTask, envVars);
154
+ await AppConfig.addLinkedApplication(app, alias);
155
+
156
+ switch (format) {
157
+ case 'json': {
158
+ Logger.printJson({
159
+ id: app.id,
160
+ name: app.name,
161
+ executedAs: app.instance.lifetime,
162
+ env: app.env,
163
+ deployUrl: app.deployUrl,
164
+ });
165
+ break;
166
+ }
167
+
168
+ case 'human':
169
+ default:
170
+ await displayAppCreation(app, alias, github, taskCommand);
171
+ }
172
+ },
173
+ });
@@ -0,0 +1,27 @@
1
+ # 📖 `clever create` command reference
2
+
3
+ ## ➡️ `clever create` <kbd>Since 0.2.0</kbd>
4
+
5
+ Create an application
6
+
7
+ ```bash
8
+ clever create --type <instance-type> [<app-name>] [options]
9
+ ```
10
+
11
+ ### 📥 Arguments
12
+
13
+ |Name|Description|
14
+ |---|---|
15
+ |`app-name`|Application name (current directory name is used if not specified) *(optional)*|
16
+
17
+ ### ⚙️ Options
18
+
19
+ |Name|Description|
20
+ |---|---|
21
+ |`-t`, `--type` `<instance-type>`|Instance type **(required)**|
22
+ |`-a`, `--alias` `<alias>`|Short name for the application|
23
+ |`-F`, `--format` `<format>`|Output format (human, json) (default: human)|
24
+ |`--github` `<owner/repo>`|GitHub application to use for deployments|
25
+ |`-o`, `--org`, `--owner` `<org-id\|org-name>`|Organisation to target by its ID (or name, if unambiguous)|
26
+ |`-r`, `--region` `<zone>`|Region, can be 'par', 'parhds', 'grahds', 'rbx', 'rbxhds', 'scw', 'ldn', 'mtl', 'sgp', 'syd', 'wsw' (default: par)|
27
+ |`-T`, `--task` `<command>`|The application launch as a task executing the given command, then stopped|
@@ -1,9 +1,10 @@
1
1
  import { addOauthHeader } from '@clevercloud/client/esm/oauth.js';
2
2
  import dedent from 'dedent';
3
3
  import { spawn } from 'node:child_process';
4
- import { styleText } from '../lib/style-text.js';
5
- import { Logger } from '../logger.js';
6
- import { conf, loadOAuthConf } from '../models/configuration.js';
4
+ import { defineCommand } from '../../lib/define-command.js';
5
+ import { styleText } from '../../lib/style-text.js';
6
+ import { Logger } from '../../logger.js';
7
+ import { conf, loadOAuthConf } from '../../models/configuration.js';
7
8
 
8
9
  async function loadTokens() {
9
10
  const tokens = await loadOAuthConf();
@@ -69,3 +70,9 @@ export async function curl() {
69
70
 
70
71
  spawn('curl', curlArgs, { stdio: 'inherit' });
71
72
  }
73
+
74
+ export const curlCommand = defineCommand({
75
+ description: "Query Clever Cloud's API using Clever Tools credentials",
76
+ since: '2.10.0',
77
+ handler: null,
78
+ });
@@ -0,0 +1,9 @@
1
+ # 📖 `clever curl` command reference
2
+
3
+ ## ➡️ `clever curl` <kbd>Since 2.10.0</kbd>
4
+
5
+ Query Clever Cloud's API using Clever Tools credentials
6
+
7
+ ```bash
8
+ clever curl
9
+ ```
@@ -0,0 +1,8 @@
1
+ import { z } from 'zod';
2
+ import { defineArgument } from '../../lib/define-argument.js';
3
+
4
+ export const databaseIdArg = defineArgument({
5
+ schema: z.string(),
6
+ description: 'Any database ID (format: addon_UUID, postgresql_UUID, mysql_UUID, ...)',
7
+ placeholder: 'database-id|addon-id',
8
+ });
@@ -0,0 +1,60 @@
1
+ import { getBackups } from '@clevercloud/client/esm/api/v2/backups.js';
2
+ import { formatTable } from '../../format-table.js';
3
+ import { defineCommand } from '../../lib/define-command.js';
4
+ import { Logger } from '../../logger.js';
5
+ import { findOwnerId } from '../../models/addon.js';
6
+ import { resolveAddonId, resolveRealId } from '../../models/ids-resolver.js';
7
+ import { sendToApi } from '../../models/send-to-api.js';
8
+ import { humanJsonOutputFormatOption, orgaIdOrNameOption } from '../global.options.js';
9
+ import { databaseIdArg } from './database.args.js';
10
+
11
+ export const databaseBackupsCommand = defineCommand({
12
+ description: 'List available database backups',
13
+ since: '2.10.0',
14
+ options: {
15
+ org: orgaIdOrNameOption,
16
+ format: humanJsonOutputFormatOption,
17
+ },
18
+ args: [databaseIdArg],
19
+ async handler(options, addonIdOrRealId) {
20
+ const { org, format } = options;
21
+
22
+ const realId = await resolveRealId(addonIdOrRealId);
23
+ const addonId = await resolveAddonId(addonIdOrRealId);
24
+ const ownerId = await findOwnerId(org, realId);
25
+
26
+ const backups = await getBackups({ ownerId, ref: realId }).then(sendToApi);
27
+
28
+ if (backups.length === 0 && format === 'human') {
29
+ Logger.println('There are no backups yet');
30
+ return;
31
+ }
32
+
33
+ const sortedBackups = backups.sort((a, b) => a.creation_date.localeCompare(b.creation_date));
34
+
35
+ switch (format) {
36
+ case 'json': {
37
+ const formattedBackups = sortedBackups.map((backup) => {
38
+ return {
39
+ addonId: addonId,
40
+ backupId: backup.backup_id,
41
+ creationDate: backup.creation_date,
42
+ downloadUrl: backup.download_url,
43
+ ownerId: ownerId,
44
+ realId: realId,
45
+ status: backup.status,
46
+ };
47
+ });
48
+ Logger.printJson(formattedBackups);
49
+ break;
50
+ }
51
+ case 'human': {
52
+ const formattedLines = sortedBackups.map((backup) => [backup.backup_id, backup.creation_date, backup.status]);
53
+
54
+ const head = ['BACKUP ID', 'CREATION DATE', 'STATUS'];
55
+
56
+ Logger.println(formatTable([head, ...formattedLines]));
57
+ }
58
+ }
59
+ },
60
+ });
@@ -0,0 +1,55 @@
1
+ import { getBackups } from '@clevercloud/client/esm/api/v2/backups.js';
2
+ import fs from 'node:fs';
3
+ import { Writable } from 'node:stream';
4
+ import { z } from 'zod';
5
+ import { defineArgument } from '../../lib/define-argument.js';
6
+ import { defineCommand } from '../../lib/define-command.js';
7
+ import { defineOption } from '../../lib/define-option.js';
8
+ import { findOwnerId } from '../../models/addon.js';
9
+ import { resolveRealId } from '../../models/ids-resolver.js';
10
+ import { sendToApi } from '../../models/send-to-api.js';
11
+ import { orgaIdOrNameOption } from '../global.options.js';
12
+ import { databaseIdArg } from './database.args.js';
13
+
14
+ export const databaseBackupsDownloadCommand = defineCommand({
15
+ description: 'Download a database backup',
16
+ since: '2.10.0',
17
+ options: {
18
+ output: defineOption({
19
+ name: 'output',
20
+ schema: z.string().optional(),
21
+ description: 'Redirect the output of the command in a file',
22
+ aliases: ['out'],
23
+ placeholder: 'file-path',
24
+ }),
25
+ org: orgaIdOrNameOption,
26
+ },
27
+ args: [
28
+ databaseIdArg,
29
+ defineArgument({
30
+ schema: z.string(),
31
+ description: 'A Database backup ID (format: UUID)',
32
+ placeholder: 'backup-id',
33
+ }),
34
+ ],
35
+ async handler(options, addonIdOrRealId, backupId) {
36
+ const { org, output } = options;
37
+
38
+ const addonId = await resolveRealId(addonIdOrRealId);
39
+ const ownerId = await findOwnerId(org, addonId);
40
+
41
+ const backups = await getBackups({ ownerId, ref: addonId }).then(sendToApi);
42
+ const backup = backups.find((backup) => backup.backup_id === backupId);
43
+
44
+ if (backup == null) {
45
+ throw new Error('no backup with this ID');
46
+ }
47
+
48
+ const response = await globalThis.fetch(backup.download_url);
49
+ if (!response.ok) {
50
+ throw new Error('Failed to download backup');
51
+ }
52
+
53
+ await response.body.pipeTo(Writable.toWeb(output ? fs.createWriteStream(output) : process.stdout));
54
+ },
55
+ });
@@ -0,0 +1,10 @@
1
+ import { defineCommand } from '../../lib/define-command.js';
2
+
3
+ export const databaseCommand = defineCommand({
4
+ description: 'Manage databases and backups',
5
+ since: '2.10.0',
6
+ options: {},
7
+ args: [],
8
+ // Parent command - no handler, only contains subcommands
9
+ handler: null,
10
+ });
@@ -0,0 +1,52 @@
1
+ # 📖 `clever database` command reference
2
+
3
+ ## ➡️ `clever database` <kbd>Since 2.10.0</kbd>
4
+
5
+ Manage databases and backups
6
+
7
+ ```bash
8
+ clever database
9
+ ```
10
+
11
+ ## ➡️ `clever database backups` <kbd>Since 2.10.0</kbd>
12
+
13
+ List available database backups
14
+
15
+ ```bash
16
+ clever database backups <database-id|addon-id> [options]
17
+ ```
18
+
19
+ ### 📥 Arguments
20
+
21
+ |Name|Description|
22
+ |---|---|
23
+ |`database-id|addon-id`|Any database ID (format: addon_UUID, postgresql_UUID, mysql_UUID, ...)|
24
+
25
+ ### ⚙️ Options
26
+
27
+ |Name|Description|
28
+ |---|---|
29
+ |`-F`, `--format` `<format>`|Output format (human, json) (default: human)|
30
+ |`-o`, `--org`, `--owner` `<org-id\|org-name>`|Organisation to target by its ID (or name, if unambiguous)|
31
+
32
+ ## ➡️ `clever database backups download` <kbd>Since 2.10.0</kbd>
33
+
34
+ Download a database backup
35
+
36
+ ```bash
37
+ clever database backups download <database-id|addon-id> <backup-id> [options]
38
+ ```
39
+
40
+ ### 📥 Arguments
41
+
42
+ |Name|Description|
43
+ |---|---|
44
+ |`database-id|addon-id`|Any database ID (format: addon_UUID, postgresql_UUID, mysql_UUID, ...)|
45
+ |`backup-id`|A Database backup ID (format: UUID)|
46
+
47
+ ### ⚙️ Options
48
+
49
+ |Name|Description|
50
+ |---|---|
51
+ |`-o`, `--org`, `--owner` `<org-id\|org-name>`|Organisation to target by its ID (or name, if unambiguous)|
52
+ |`--output`, `--out` `<file-path>`|Redirect the output of the command in a file|
@@ -0,0 +1,43 @@
1
+ import { z } from 'zod';
2
+ import { defineCommand } from '../../lib/define-command.js';
3
+ import { defineOption } from '../../lib/define-option.js';
4
+ import { styleText } from '../../lib/style-text.js';
5
+ import { Logger } from '../../logger.js';
6
+ import * as AppConfig from '../../models/app_configuration.js';
7
+ import * as Application from '../../models/application.js';
8
+ import { aliasOption, appIdOrNameOption } from '../global.options.js';
9
+
10
+ export const deleteCommand = defineCommand({
11
+ description: 'Delete an application',
12
+ since: '0.7.0',
13
+ options: {
14
+ yes: defineOption({
15
+ name: 'yes',
16
+ schema: z.boolean().default(false),
17
+ description: 'Skip confirmation and delete the application directly',
18
+ aliases: ['y'],
19
+ }),
20
+ alias: aliasOption,
21
+ app: appIdOrNameOption,
22
+ },
23
+ args: [],
24
+ async handler(options) {
25
+ const { alias, app: appIdOrName, yes: skipConfirmation } = options;
26
+ const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
27
+
28
+ const app = await Application.get(ownerId, appId);
29
+ if (app == null) {
30
+ throw new Error("The application doesn't exist");
31
+ }
32
+
33
+ // delete app
34
+ await Application.deleteApp(app, skipConfirmation);
35
+ Logger.printSuccess(`Application ${styleText('green', styleText('bold', `${app.name}`))} successfully deleted!`);
36
+ Logger.println(` ${styleText('grey', '•')} Application ID: ${styleText('grey', app.id)}`);
37
+
38
+ const wasUnlinked = await AppConfig.removeLinkedApplication({ appId, alias });
39
+ if (wasUnlinked) {
40
+ Logger.println(` ${styleText('blue', '→')} Local alias ${styleText('blue', alias || app.name)} unlinked`);
41
+ }
42
+ },
43
+ });
@@ -0,0 +1,17 @@
1
+ # 📖 `clever delete` command reference
2
+
3
+ ## ➡️ `clever delete` <kbd>Since 0.7.0</kbd>
4
+
5
+ Delete an application
6
+
7
+ ```bash
8
+ clever delete [options]
9
+ ```
10
+
11
+ ### ⚙️ Options
12
+
13
+ |Name|Description|
14
+ |---|---|
15
+ |`-a`, `--alias` `<alias>`|Short name for the application|
16
+ |`--app` `<app-id\|app-name>`|Application to manage by its ID (or name, if unambiguous)|
17
+ |`-y`, `--yes`|Skip confirmation and delete the application directly|
@@ -0,0 +1,166 @@
1
+ import { getAllDeployments } from '@clevercloud/client/esm/api/v2/application.js';
2
+ import dedent from 'dedent';
3
+ import { z } from 'zod';
4
+ import { defineCommand } from '../../lib/define-command.js';
5
+ import { defineOption } from '../../lib/define-option.js';
6
+ import { styleText } from '../../lib/style-text.js';
7
+ import { Logger } from '../../logger.js';
8
+ import * as AppConfig from '../../models/app_configuration.js';
9
+ import * as Application from '../../models/application.js';
10
+ import * as ExitStrategy from '../../models/exit-strategy-option.js';
11
+ import * as git from '../../models/git.js';
12
+ import { completeBranches } from '../../models/git.js';
13
+ import * as Log from '../../models/log-v4.js';
14
+ import { sendToApi } from '../../models/send-to-api.js';
15
+ import { aliasOption, exitOnDeployOption, followDeployLogsOption, quietOption } from '../global.options.js';
16
+
17
+ async function restartOnSameCommit(ownerId, appId, commitIdToPush, quiet, withoutCache, exitStrategy) {
18
+ const cacheSuffix = withoutCache ? ' without using cache' : '';
19
+ Logger.println(`🔄 Restarting ${styleText('bold', appId)}${cacheSuffix} ${styleText('grey', `(${commitIdToPush})`)}`);
20
+
21
+ const restart = await Application.redeploy(ownerId, appId, commitIdToPush, withoutCache);
22
+ return Log.watchDeploymentAndDisplayLogs({ ownerId, appId, deploymentId: restart.deploymentId, quiet, exitStrategy });
23
+ }
24
+
25
+ async function getBranchToDeploy(branchName, tagName) {
26
+ if (tagName) {
27
+ const useTag = await git.isExistingTag(tagName);
28
+ if (useTag) {
29
+ const tagRefspec = await git.getFullBranch(tagName);
30
+ return tagRefspec;
31
+ } else {
32
+ throw new Error(`Tag ${tagName} doesn't exist locally`);
33
+ }
34
+ } else {
35
+ return await git.getFullBranch(branchName);
36
+ }
37
+ }
38
+
39
+ export const deployCommand = defineCommand({
40
+ description: 'Deploy an application',
41
+ since: '0.2.0',
42
+ options: {
43
+ branch: defineOption({
44
+ name: 'branch',
45
+ schema: z.string().default(''),
46
+ description: 'Branch to push (current branch by default)',
47
+ aliases: ['b'],
48
+ placeholder: 'branch',
49
+ complete: completeBranches,
50
+ }),
51
+ tag: defineOption({
52
+ name: 'tag',
53
+ schema: z.string().default(''),
54
+ description: 'Tag to push (none by default)',
55
+ aliases: ['t'],
56
+ placeholder: 'tag',
57
+ }),
58
+ force: defineOption({
59
+ name: 'force',
60
+ schema: z.boolean().default(false),
61
+ description: "Force deploy even if it's not fast-forwardable",
62
+ aliases: ['f'],
63
+ }),
64
+ sameCommitPolicy: defineOption({
65
+ name: 'same-commit-policy',
66
+ schema: z.enum(['error', 'ignore', 'restart', 'rebuild']).default('error'),
67
+ description: 'What to do when local and remote commit are identical (error, ignore, restart, rebuild)',
68
+ aliases: ['p'],
69
+ placeholder: 'policy',
70
+ }),
71
+ alias: aliasOption,
72
+ quiet: quietOption,
73
+ follow: followDeployLogsOption,
74
+ exitOnDeploy: exitOnDeployOption,
75
+ },
76
+ args: [],
77
+ async handler(options) {
78
+ const { alias, branch: branchName, tag: tagName, quiet, force, follow, sameCommitPolicy, exitOnDeploy } = options;
79
+
80
+ const exitStrategy = ExitStrategy.get(follow, exitOnDeploy);
81
+
82
+ const appData = await AppConfig.getAppDetails({ alias });
83
+ const { ownerId, appId } = appData;
84
+
85
+ const branchRefspec = await getBranchToDeploy(branchName, tagName);
86
+ const commitIdToPush = await git.getBranchCommit(branchRefspec);
87
+ const remoteHeadCommitId = await git.getRemoteCommit(appData.deployUrl);
88
+ const deployedCommitId = await Application.get(ownerId, appId).then(({ commitId }) => commitId);
89
+
90
+ await git.addRemote(appData.alias, appData.deployUrl);
91
+
92
+ if (commitIdToPush === remoteHeadCommitId) {
93
+ switch (sameCommitPolicy) {
94
+ case 'ignore':
95
+ Logger.printSuccess(`The application is up-to-date (${styleText('grey', remoteHeadCommitId)})`);
96
+ return;
97
+ case 'restart':
98
+ return restartOnSameCommit(ownerId, appId, commitIdToPush, quiet, false, exitStrategy);
99
+ case 'rebuild':
100
+ return restartOnSameCommit(ownerId, appId, commitIdToPush, quiet, true, exitStrategy);
101
+ case 'error':
102
+ default: {
103
+ const restartCommand =
104
+ commitIdToPush !== deployedCommitId ? `clever restart --commit ${commitIdToPush}` : 'clever restart';
105
+ throw new Error(dedent`
106
+ Remote HEAD has the same commit as the one to push ${styleText('grey', `(${remoteHeadCommitId})`)}, your application is up-to-date.
107
+ Create a new commit, use ${styleText('blue', restartCommand)} or the ${styleText('blue', '--same-commit-policy')} option.
108
+ `);
109
+ }
110
+ }
111
+ }
112
+
113
+ // It's sometimes tricky to figure out the deployment ID for the current git push.
114
+ // We on have the commit ID but there in a situation where the last deployment was cancelled, it may have the same commit ID.
115
+ // So before pushing, we get the last deployments so we can after the push figure out which deployment is new…
116
+ const knownDeployments = await getAllDeployments({ id: ownerId, appId, limit: 5 }).then(sendToApi);
117
+
118
+ Logger.println(dedent`
119
+ ${styleText('bold', `🚀 Deploying ${styleText('green', appData.name)}`)}
120
+ Application ID ${styleText('grey', `${appId}`)}
121
+ Organisation ID ${styleText('grey', `${ownerId}`)}
122
+ `);
123
+
124
+ Logger.println();
125
+
126
+ Logger.println(styleText('bold', '🔀 Git information'));
127
+ if (remoteHeadCommitId == null || deployedCommitId == null) {
128
+ Logger.println(` ${styleText('yellow', '!')} App is brand new, no commits on remote yet`);
129
+ } else {
130
+ Logger.println(` Remote head ${styleText('yellow', remoteHeadCommitId)} (${branchRefspec})`);
131
+ Logger.println(` Deployed commit ${styleText('yellow', deployedCommitId)}`);
132
+ }
133
+ Logger.println(
134
+ ` Local commit ${styleText('yellow', commitIdToPush)} ${styleText('blue', '[will be deployed]')}`,
135
+ );
136
+
137
+ Logger.println();
138
+
139
+ Logger.println(dedent`
140
+ ${styleText('bold', '🔄 Deployment progress')}
141
+ ${styleText('blue', '→ Pushing source code to Clever Cloud…')}
142
+ `);
143
+
144
+ await git.push(appData.deployUrl, commitIdToPush, force).catch(async (e) => {
145
+ const isShallow = await git.isShallow();
146
+ if (isShallow) {
147
+ throw new Error(
148
+ 'Failed to push your source code because your repository is shallow and therefore cannot be pushed to the Clever Cloud remote.',
149
+ );
150
+ } else {
151
+ throw e;
152
+ }
153
+ });
154
+
155
+ await Logger.println(` ${styleText('green', '✓ Code pushed to Clever Cloud')}`);
156
+
157
+ return Log.watchDeploymentAndDisplayLogs({
158
+ ownerId,
159
+ appId,
160
+ commitId: commitIdToPush,
161
+ knownDeployments,
162
+ quiet,
163
+ exitStrategy,
164
+ });
165
+ },
166
+ });
@@ -0,0 +1,22 @@
1
+ # 📖 `clever deploy` command reference
2
+
3
+ ## ➡️ `clever deploy` <kbd>Since 0.2.0</kbd>
4
+
5
+ Deploy an application
6
+
7
+ ```bash
8
+ clever deploy [options]
9
+ ```
10
+
11
+ ### ⚙️ Options
12
+
13
+ |Name|Description|
14
+ |---|---|
15
+ |`-a`, `--alias` `<alias>`|Short name for the application|
16
+ |`-b`, `--branch` `<branch>`|Branch to push (current branch by default)|
17
+ |`-e`, `--exit-on` `<step>`|Step at which the logs streaming is ended, steps are: deploy-start, deploy-end, never (default: deploy-end)|
18
+ |`--follow`|Continue to follow logs after deployment has ended *(deprecated, use `--exit-on never` instead)*|
19
+ |`-f`, `--force`|Force deploy even if it's not fast-forwardable|
20
+ |`-q`, `--quiet`|Don't show logs during deployment|
21
+ |`-p`, `--same-commit-policy` `<policy>`|What to do when local and remote commit are identical (error, ignore, restart, rebuild) (default: error)|
22
+ |`-t`, `--tag` `<tag>`|Tag to push (none by default)|