clever-tools 3.11.0 → 3.13.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.
- package/bin/clever.js +416 -3
- package/package.json +4 -3
- package/src/clever-client/auth-bridge.js +61 -0
- package/src/clever-client/ng.js +18 -0
- package/src/clever-client/operators.js +121 -0
- package/src/commands/addon.js +34 -28
- package/src/commands/cancel-deploy.js +3 -4
- package/src/commands/config.js +16 -20
- package/src/commands/console.js +4 -10
- package/src/commands/create.js +76 -9
- package/src/commands/curl.js +17 -17
- package/src/commands/database.js +9 -9
- package/src/commands/delete.js +11 -10
- package/src/commands/deploy.js +35 -21
- package/src/commands/domain.js +13 -16
- package/src/commands/emails.js +174 -0
- package/src/commands/env.js +15 -8
- package/src/commands/keycloak.js +138 -0
- package/src/commands/kv.js +1 -1
- package/src/commands/link.js +7 -3
- package/src/commands/makeDefault.js +2 -1
- package/src/commands/matomo.js +85 -0
- package/src/commands/metabase.js +112 -0
- package/src/commands/ng.js +202 -0
- package/src/commands/open.js +2 -5
- package/src/commands/otoroshi.js +138 -0
- package/src/commands/profile.js +11 -10
- package/src/commands/published-config.js +7 -7
- package/src/commands/restart.js +1 -1
- package/src/commands/ssh-keys.js +159 -0
- package/src/commands/stop.js +3 -3
- package/src/commands/tcp-redirs.js +8 -8
- package/src/commands/tokens.js +146 -0
- package/src/commands/unlink.js +2 -1
- package/src/experimental-features.js +47 -2
- package/src/lib/ng-print.js +195 -0
- package/src/lib/operator-commands.js +281 -0
- package/src/lib/prompts.js +30 -0
- package/src/lib/slugify.js +10 -0
- package/src/logger.js +3 -0
- package/src/models/activity.js +2 -3
- package/src/models/addon.js +9 -6
- package/src/models/app_configuration.js +12 -9
- package/src/models/application.js +44 -22
- package/src/models/application_configuration.js +72 -72
- package/src/models/configuration.js +1 -0
- package/src/models/git.js +29 -1
- package/src/models/ids-resolver.js +37 -1
- package/src/models/interact.js +0 -24
- package/src/models/log-v4.js +13 -4
- package/src/models/namespaces.js +3 -2
- package/src/models/ng-resources.js +270 -0
- package/src/models/ng.js +276 -0
- package/src/models/operator.js +48 -0
- package/src/models/send-to-api.js +18 -0
- package/src/models/utils.js +21 -0
- package/src/parsers.js +71 -3
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// TODO: Move this to the Clever Cloud JS Client
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* GET /v4/addon-providers/addon-{provider}/addons/{realId}
|
|
5
|
+
* @param {Object} params
|
|
6
|
+
* @param {String} params.provider
|
|
7
|
+
* @param {String} params.realId
|
|
8
|
+
*/
|
|
9
|
+
export function getOperator (params) {
|
|
10
|
+
// no multipath for /self or /organisations/{id}
|
|
11
|
+
return Promise.resolve({
|
|
12
|
+
method: 'get',
|
|
13
|
+
url: `/v4/addon-providers/addon-${params.provider}/addons/${params.realId}`,
|
|
14
|
+
headers: { Accept: 'application/json' },
|
|
15
|
+
// no queryParams
|
|
16
|
+
// no body
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* POST /v4/addon-providers/addon-{provider}/addons/{realId}/reboot
|
|
22
|
+
* @param {Object} params
|
|
23
|
+
* @param {String} params.provider
|
|
24
|
+
* @param {String} params.realId
|
|
25
|
+
*/
|
|
26
|
+
export function rebootOperator (params) {
|
|
27
|
+
// no multipath for /self or /organisations/{id}
|
|
28
|
+
return Promise.resolve({
|
|
29
|
+
method: 'post',
|
|
30
|
+
url: `/v4/addon-providers/addon-${params.provider}/addons/${params.realId}/reboot`,
|
|
31
|
+
headers: { Accept: 'application/json' },
|
|
32
|
+
// no queryParams
|
|
33
|
+
// no body
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* POST /v4/addon-providers/addon-{provider}/addons/{realId}/rebuild
|
|
39
|
+
* @param {Object} params
|
|
40
|
+
* @param {String} params.provider
|
|
41
|
+
* @param {String} params.realId
|
|
42
|
+
*/
|
|
43
|
+
export function rebuildOperator (params) {
|
|
44
|
+
// no multipath for /self or /organisations/{id}
|
|
45
|
+
return Promise.resolve({
|
|
46
|
+
method: 'post',
|
|
47
|
+
url: `/v4/addon-providers/addon-${params.provider}/addons/${params.realId}/rebuild`,
|
|
48
|
+
headers: { Accept: 'application/json' },
|
|
49
|
+
// no queryParams
|
|
50
|
+
// no body
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* POST /v4/addon-providers/addon-{provider}/addons/{realId}/networkgroup
|
|
56
|
+
* @param {Object} params
|
|
57
|
+
* @param {String} params.provider
|
|
58
|
+
* @param {String} params.realId
|
|
59
|
+
*/
|
|
60
|
+
export function ngEnableOperator (params) {
|
|
61
|
+
// no multipath for /self or /organisations/{id}
|
|
62
|
+
return Promise.resolve({
|
|
63
|
+
method: 'post',
|
|
64
|
+
url: `/v4/addon-providers/addon-${params.provider}/addons/${params.realId}/networkgroup`,
|
|
65
|
+
headers: { Accept: 'application/json' },
|
|
66
|
+
// no queryParams
|
|
67
|
+
// no body
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* POST /v4/addon-providers/addon-{provider}/addons/{realId}/networkgroup
|
|
73
|
+
* @param {Object} params
|
|
74
|
+
* @param {String} params.provider
|
|
75
|
+
* @param {String} params.realId
|
|
76
|
+
*/
|
|
77
|
+
export function ngDisableOperator (params) {
|
|
78
|
+
// no multipath for /self or /organisations/{id}
|
|
79
|
+
return Promise.resolve({
|
|
80
|
+
method: 'delete',
|
|
81
|
+
url: `/v4/addon-providers/addon-${params.provider}/addons/${params.realId}/networkgroup`,
|
|
82
|
+
headers: { Accept: 'application/json' },
|
|
83
|
+
// no queryParams
|
|
84
|
+
// no body
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* GET /v4/addon-providers/addon-{provider}/addons/{realId}/version/check
|
|
90
|
+
* @param {Object} params
|
|
91
|
+
* @param {String} params.provider
|
|
92
|
+
* @param {String} params.realId
|
|
93
|
+
*/
|
|
94
|
+
export function versionCheck (params) {
|
|
95
|
+
// no multipath for /self or /organisations/{id}
|
|
96
|
+
return Promise.resolve({
|
|
97
|
+
method: 'get',
|
|
98
|
+
url: `/v4/addon-providers/addon-${params.provider}/addons/${params.realId}/version/check`,
|
|
99
|
+
headers: { Accept: 'application/json' },
|
|
100
|
+
// no queryParams
|
|
101
|
+
// no body
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* POST /v4/addon-providers/addon-{provider}/addons/{realId}/version/update
|
|
107
|
+
* @param {Object} params
|
|
108
|
+
* @param {String} params.provider
|
|
109
|
+
* @param {String} params.realId
|
|
110
|
+
* @param {Object} body
|
|
111
|
+
*/
|
|
112
|
+
export function versionUpdate (params, body) {
|
|
113
|
+
// no multipath for /self or /organisations/{id}
|
|
114
|
+
return Promise.resolve({
|
|
115
|
+
method: 'post',
|
|
116
|
+
url: `/v4/addon-providers/addon-${params.provider}/addons/${params.realId}/version/update`,
|
|
117
|
+
headers: { Accept: 'application/json', 'Content-Type': 'application/json' },
|
|
118
|
+
// no queryParams
|
|
119
|
+
body,
|
|
120
|
+
});
|
|
121
|
+
}
|
package/src/commands/addon.js
CHANGED
|
@@ -12,6 +12,7 @@ import { sendToApi } from '../models/send-to-api.js';
|
|
|
12
12
|
import { toNameEqualsValueString } from '@clevercloud/client/esm/utils/env-vars.js';
|
|
13
13
|
import { resolveAddonId } from '../models/ids-resolver.js';
|
|
14
14
|
import { conf } from '../models/configuration.js';
|
|
15
|
+
import dedent from 'dedent';
|
|
15
16
|
|
|
16
17
|
const formatTable = initFormatTable();
|
|
17
18
|
|
|
@@ -123,49 +124,49 @@ function displayAddon (format, addon, providerName, message) {
|
|
|
123
124
|
|
|
124
125
|
const WIP_PROVIDERS = {
|
|
125
126
|
keycloak: {
|
|
126
|
-
status: '
|
|
127
|
-
postCreateInstructions:
|
|
128
|
-
|
|
129
|
-
|
|
127
|
+
status: '',
|
|
128
|
+
postCreateInstructions: dedent`
|
|
129
|
+
Learn more about Keycloak on Clever Cloud: ${conf.DOC_URL}/addons/keycloak/,
|
|
130
|
+
`,
|
|
130
131
|
},
|
|
131
132
|
kv: {
|
|
132
133
|
status: 'alpha',
|
|
133
|
-
postCreateInstructions:
|
|
134
|
-
colors.yellow('You can easily use Materia KV with \'redis-cli\', with such commands:')
|
|
135
|
-
colors.blue(`source <(clever addon env ${addon.id} -F shell)`)
|
|
136
|
-
colors.blue('redis-cli -h $KV_HOST -p $KV_PORT --tls')
|
|
137
|
-
|
|
138
|
-
|
|
134
|
+
postCreateInstructions: dedent`
|
|
135
|
+
${colors.yellow('You can easily use Materia KV with \'redis-cli\', with such commands:')}
|
|
136
|
+
${colors.blue(`source <(clever addon env ${addon.id} -F shell)`)}
|
|
137
|
+
${colors.blue('redis-cli -h $KV_HOST -p $KV_PORT --tls')}
|
|
138
|
+
Learn more about Materia KV on Clever Cloud: ${conf.DOC_URL}/addons/materia-kv/
|
|
139
|
+
`,
|
|
139
140
|
},
|
|
140
141
|
'addon-matomo': {
|
|
141
|
-
status: '
|
|
142
|
-
postCreateInstructions:
|
|
143
|
-
|
|
144
|
-
|
|
142
|
+
status: '',
|
|
143
|
+
postCreateInstructions: dedent`
|
|
144
|
+
Learn more about Matomo on Clever Cloud: ${conf.DOC_URL}/addons/matomo/
|
|
145
|
+
`,
|
|
145
146
|
},
|
|
146
147
|
metabase: {
|
|
147
|
-
status: '
|
|
148
|
-
postCreateInstructions:
|
|
149
|
-
|
|
150
|
-
|
|
148
|
+
status: '',
|
|
149
|
+
postCreateInstructions: dedent`
|
|
150
|
+
Learn more about Metabase on Clever Cloud: ${conf.DOC_URL}/addons/metabase/
|
|
151
|
+
`,
|
|
151
152
|
},
|
|
152
153
|
otoroshi: {
|
|
153
|
-
status: '
|
|
154
|
-
postCreateInstructions:
|
|
155
|
-
|
|
156
|
-
|
|
154
|
+
status: '',
|
|
155
|
+
postCreateInstructions: dedent`
|
|
156
|
+
Learn more about Otoroshi with LLM on Clever Cloud: ${conf.DOC_URL}/addons/otoroshi/
|
|
157
|
+
`,
|
|
157
158
|
},
|
|
158
159
|
'addon-pulsar': {
|
|
159
160
|
status: 'beta',
|
|
160
|
-
postCreateInstructions:
|
|
161
|
-
|
|
162
|
-
|
|
161
|
+
postCreateInstructions: dedent`
|
|
162
|
+
Learn more about Pulsar on Clever Cloud: ${conf.DOC_URL}/addons/pulsar/
|
|
163
|
+
`,
|
|
163
164
|
},
|
|
164
165
|
};
|
|
165
166
|
|
|
166
167
|
let providerNameToShow = '';
|
|
167
168
|
let statusMessage = '';
|
|
168
|
-
if (providerName in WIP_PROVIDERS) {
|
|
169
|
+
if (providerName in WIP_PROVIDERS && WIP_PROVIDERS[providerName].status !== '') {
|
|
169
170
|
|
|
170
171
|
providerNameToShow = providerName === 'kv'
|
|
171
172
|
? 'Materia KV'
|
|
@@ -206,11 +207,12 @@ function displayAddon (format, addon, providerName, message) {
|
|
|
206
207
|
const provider = PROVIDERS_WITH_URL[providerName];
|
|
207
208
|
const urlEnv = addon.env.find((entry) => entry.name === provider.urlEnv);
|
|
208
209
|
const urlToShow = urlEnv.value;
|
|
210
|
+
const urlToShowWithHttps = urlToShow.startsWith('http') ? urlToShow : `https://${urlToShow}`;
|
|
209
211
|
|
|
210
212
|
if (urlEnv) {
|
|
211
213
|
Logger.println();
|
|
212
214
|
Logger.println(`Your ${provider.name} is starting:`);
|
|
213
|
-
Logger.println(` - Access it: ${
|
|
215
|
+
Logger.println(` - Access it: ${urlToShowWithHttps}`);
|
|
214
216
|
Logger.println(` - Manage it: ${conf.GOTO_URL}/${addon.id}`);
|
|
215
217
|
}
|
|
216
218
|
|
|
@@ -232,7 +234,11 @@ function displayAddon (format, addon, providerName, message) {
|
|
|
232
234
|
if (providerName in WIP_PROVIDERS) {
|
|
233
235
|
|
|
234
236
|
Logger.println();
|
|
235
|
-
|
|
237
|
+
|
|
238
|
+
if (statusMessage !== '') {
|
|
239
|
+
Logger.println(colors.yellow(`/!\\ ${statusMessage}`));
|
|
240
|
+
}
|
|
241
|
+
|
|
236
242
|
Logger.println(WIP_PROVIDERS[providerName].postCreateInstructions);
|
|
237
243
|
}
|
|
238
244
|
}
|
|
@@ -2,6 +2,7 @@ import * as Application from '../models/application.js';
|
|
|
2
2
|
import { Logger } from '../logger.js';
|
|
3
3
|
import { getAllDeployments, cancelDeployment } from '@clevercloud/client/esm/api/v2/application.js';
|
|
4
4
|
import { sendToApi } from '../models/send-to-api.js';
|
|
5
|
+
import colors from 'colors/safe.js';
|
|
5
6
|
|
|
6
7
|
export async function cancelDeploy (params) {
|
|
7
8
|
const { alias, app: appIdOrName } = params.options;
|
|
@@ -13,8 +14,6 @@ export async function cancelDeploy (params) {
|
|
|
13
14
|
throw new Error('There is no ongoing deployment for this application');
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
Logger.println('Deployment cancelled!');
|
|
17
|
+
await cancelDeployment({ id: ownerId, appId, deploymentId: deployments[0].id }).then(sendToApi);
|
|
18
|
+
Logger.printSuccess(`Deployment ${colors.bold.green(deployments[0].uuid)} successfully cancelled!`);
|
|
20
19
|
};
|
package/src/commands/config.js
CHANGED
|
@@ -1,22 +1,21 @@
|
|
|
1
|
-
import * as application from '@clevercloud/client/esm/api/v2/application.js';
|
|
2
|
-
|
|
3
1
|
import * as Application from '../models/application.js';
|
|
4
2
|
import * as ApplicationConfiguration from '../models/application_configuration.js';
|
|
3
|
+
import { Logger } from '../logger.js';
|
|
4
|
+
import colors from 'colors/safe.js';
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
export async function list (params) {
|
|
7
|
+
const { alias, app: appIdOrName } = params.options;
|
|
8
|
+
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
9
|
+
const app = await Application.get(ownerId, appId);
|
|
10
|
+
ApplicationConfiguration.printAllValues(app);
|
|
11
|
+
}
|
|
7
12
|
|
|
8
13
|
export async function get (params) {
|
|
9
14
|
const [configurationName] = params.args;
|
|
10
15
|
const { alias, app: appIdOrName } = params.options;
|
|
11
16
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
12
17
|
const app = await Application.get(ownerId, appId);
|
|
13
|
-
|
|
14
|
-
if (configurationName == null) {
|
|
15
|
-
ApplicationConfiguration.print(app);
|
|
16
|
-
}
|
|
17
|
-
else {
|
|
18
|
-
ApplicationConfiguration.printById(app, configurationName);
|
|
19
|
-
}
|
|
18
|
+
ApplicationConfiguration.printValue(app, configurationName);
|
|
20
19
|
}
|
|
21
20
|
|
|
22
21
|
export async function set (params) {
|
|
@@ -24,12 +23,11 @@ export async function set (params) {
|
|
|
24
23
|
const { alias, app: appIdOrName } = params.options;
|
|
25
24
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
26
25
|
const config = ApplicationConfiguration.getById(configurationName);
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
26
|
+
const options = {
|
|
27
|
+
[config.name]: ApplicationConfiguration.parse(config, configurationValue),
|
|
28
|
+
};
|
|
29
|
+
const app = await Application.updateOptions(ownerId, appId, options);
|
|
30
|
+
Logger.printSuccess(`Config ${colors.green(config.id)} successfully updated to ${colors.green(ApplicationConfiguration.formatValue(config, app[config.name]))}!`);
|
|
33
31
|
}
|
|
34
32
|
|
|
35
33
|
export async function update (params) {
|
|
@@ -41,9 +39,7 @@ export async function update (params) {
|
|
|
41
39
|
throw new Error('No configuration to update');
|
|
42
40
|
}
|
|
43
41
|
|
|
44
|
-
const app = await
|
|
42
|
+
const app = await Application.updateOptions(ownerId, appId, options);
|
|
45
43
|
|
|
46
|
-
|
|
47
|
-
ApplicationConfiguration.printByName(app, configName);
|
|
48
|
-
}
|
|
44
|
+
ApplicationConfiguration.printAllValues(app);
|
|
49
45
|
}
|
package/src/commands/console.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import * as Application from '../models/application.js';
|
|
2
2
|
import * as AppConfig from '../models/app_configuration.js';
|
|
3
|
-
import {
|
|
4
|
-
import openPage from 'open';
|
|
5
|
-
import { conf } from '../models/configuration.js';
|
|
3
|
+
import { openBrowser } from '../models/utils.js';
|
|
6
4
|
|
|
7
5
|
export async function openConsole (params) {
|
|
8
6
|
const { alias, app: appIdOrName } = params.options;
|
|
@@ -10,17 +8,13 @@ export async function openConsole (params) {
|
|
|
10
8
|
const { apps } = await AppConfig.loadApplicationConf();
|
|
11
9
|
// If no app is linked or asked, open the Console without any context
|
|
12
10
|
if (apps.length === 0 && !appIdOrName) {
|
|
13
|
-
|
|
14
|
-
await openPage(conf.CONSOLE_URL, { wait: false });
|
|
11
|
+
await openBrowser('/', 'Opening the Console in your browser');
|
|
15
12
|
return;
|
|
16
13
|
}
|
|
17
14
|
|
|
18
15
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
19
16
|
const prefixPath = (ownerId.startsWith('user_')) ? 'users/me' : `organisations/${ownerId}`;
|
|
20
|
-
const
|
|
17
|
+
const consolePath = `/${prefixPath}/applications/${appId}`;
|
|
21
18
|
|
|
22
|
-
|
|
23
|
-
Logger.println(`Opening the Console in your browser for application ${appId}`);
|
|
24
|
-
|
|
25
|
-
await openPage(url, { wait: false });
|
|
19
|
+
await openBrowser(consolePath, `Opening the Console in your browser for application ${appId}`);
|
|
26
20
|
}
|
package/src/commands/create.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
+
import colors from 'colors/safe.js';
|
|
2
3
|
import * as Application from '../models/application.js';
|
|
3
4
|
import * as AppConfig from '../models/app_configuration.js';
|
|
4
5
|
import { Logger } from '../logger.js';
|
|
6
|
+
import { isGitWorkingDirectoryClean, isInsideGitRepo } from '../models/git.js';
|
|
7
|
+
import { conf } from '../models/configuration.js';
|
|
5
8
|
|
|
6
9
|
export async function create (params) {
|
|
7
10
|
const { type: typeName } = params.options;
|
|
@@ -37,15 +40,7 @@ export async function create (params) {
|
|
|
37
40
|
|
|
38
41
|
case 'human':
|
|
39
42
|
default:
|
|
40
|
-
|
|
41
|
-
Logger.println('Your application has been successfully created as a task!');
|
|
42
|
-
Logger.println(`The "CC_RUN_COMMAND" environment variable has been set to "${taskCommand}"`);
|
|
43
|
-
}
|
|
44
|
-
else {
|
|
45
|
-
Logger.println('Your application has been successfully created!');
|
|
46
|
-
}
|
|
47
|
-
Logger.println(`ID: ${app.id}`);
|
|
48
|
-
Logger.println(`Name: ${name}`);
|
|
43
|
+
await displayAppCreation(app, alias, github, taskCommand);
|
|
49
44
|
}
|
|
50
45
|
};
|
|
51
46
|
|
|
@@ -59,3 +54,75 @@ function getGithubDetails (githubOwnerRepo) {
|
|
|
59
54
|
function getCurrentDirectoryName () {
|
|
60
55
|
return path.basename(process.cwd());
|
|
61
56
|
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Display the application creation message in a human-readable format
|
|
60
|
+
* @param {Object} app - The application object
|
|
61
|
+
* @param {string} alias - The alias of the application
|
|
62
|
+
* @param {Object} github - The GitHub details
|
|
63
|
+
* @param {string} taskCommand - The task command
|
|
64
|
+
*/
|
|
65
|
+
async function displayAppCreation (app, alias, github, taskCommand) {
|
|
66
|
+
|
|
67
|
+
Logger.printSuccess(`Application ${colors.green(app.name)} successfully created!`);
|
|
68
|
+
|
|
69
|
+
const hasDistinctAlias = alias != null && alias !== app.name;
|
|
70
|
+
const isTask = app.instance.lifetime === 'TASK';
|
|
71
|
+
|
|
72
|
+
Logger.println();
|
|
73
|
+
printFieldsAsTable(2, {
|
|
74
|
+
Type: colors.blue(`⬢ ${app.instance.variant.name}`),
|
|
75
|
+
ID: app.id,
|
|
76
|
+
'Org ID': app.ownerId,
|
|
77
|
+
Name: app.name,
|
|
78
|
+
GitHub: github != null && `${github.owner}/${github.name}`,
|
|
79
|
+
Alias: hasDistinctAlias && alias,
|
|
80
|
+
Zone: app.zone,
|
|
81
|
+
Task: isTask && `"${taskCommand}"`,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
Logger.println();
|
|
85
|
+
Logger.println(' ' + colors.bold('Next steps:'));
|
|
86
|
+
|
|
87
|
+
if (!github) {
|
|
88
|
+
const isInsideGit = await isInsideGitRepo();
|
|
89
|
+
if (!isInsideGit) {
|
|
90
|
+
Logger.println(` ${colors.yellow('!')} Initialize a git repository first, for example:`);
|
|
91
|
+
Logger.println(` ${shellCommand('git init')}`);
|
|
92
|
+
Logger.println(` ${shellCommand('git add .')}`);
|
|
93
|
+
Logger.println(` ${shellCommand('git commit -m "Initial commit"')}`);
|
|
94
|
+
Logger.println();
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
const isClean = await isGitWorkingDirectoryClean();
|
|
98
|
+
if (!isClean) {
|
|
99
|
+
Logger.println(` ${colors.yellow('!')} Commit your changes first:`);
|
|
100
|
+
Logger.println(` ${shellCommand('git add .')}`);
|
|
101
|
+
Logger.println(` ${shellCommand('git commit -m "My changes"')}`);
|
|
102
|
+
Logger.println();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (github) {
|
|
108
|
+
Logger.println(` ${colors.blue('→')} Push changes to ${colors.blue(`${github.owner}/${github.name}`)} GitHub repository to trigger a deployment, or ${colors.blue('clever restart')} the latest pushed commit`);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
Logger.println(` ${colors.blue('→')} Run ${colors.blue('clever deploy')} ${isTask ? 'to execute your task' : 'to deploy your application'}`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
Logger.println(` ${colors.blue('→')} Manage your application at: ${colors.underline(`${conf.GOTO_URL}/${app.id}`)}`);
|
|
115
|
+
Logger.println('');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function shellCommand (command) {
|
|
119
|
+
return `${colors.grey('$')} ${colors.yellow(command)}`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function printFieldsAsTable (indent, fields) {
|
|
123
|
+
const fieldsWithValues = Object.fromEntries(Object.entries(fields).filter(([_, value]) => typeof value === 'string'));
|
|
124
|
+
const labelMaxWidth = Math.max(...Object.keys(fieldsWithValues).map((label) => label.length));
|
|
125
|
+
return Object.entries(fieldsWithValues).forEach(([label, value]) => {
|
|
126
|
+
Logger.println(`${' '.repeat(indent)}${label.padEnd(labelMaxWidth, ' ')} ${colors.grey(value)}`);
|
|
127
|
+
});
|
|
128
|
+
}
|
package/src/commands/curl.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
-
import {
|
|
2
|
+
import { conf, loadOAuthConf } from '../models/configuration.js';
|
|
3
3
|
import { addOauthHeader } from '@clevercloud/client/esm/oauth.js';
|
|
4
4
|
import { Logger } from '../logger.js';
|
|
5
5
|
import colors from 'colors/safe.js';
|
|
6
6
|
import curlconverter from 'curlconverter';
|
|
7
|
+
import dedent from 'dedent';
|
|
7
8
|
|
|
8
9
|
async function loadTokens () {
|
|
9
10
|
const tokens = await loadOAuthConf();
|
|
@@ -16,22 +17,21 @@ async function loadTokens () {
|
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
function printCleverCurlHelp () {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
${apiDocUrlv4}`);
|
|
20
|
+
Logger.println(dedent`
|
|
21
|
+
Usage: clever curl
|
|
22
|
+
Query Clever Cloud's API using Clever Tools credentials. For example:
|
|
23
|
+
|
|
24
|
+
clever curl ${conf.API_HOST}/v2/self
|
|
25
|
+
clever curl ${conf.API_HOST}/v2/summary
|
|
26
|
+
clever curl ${conf.API_HOST}/v4/products/zones
|
|
27
|
+
clever curl ${conf.API_HOST}/v2/organisations/<ORGANISATION_ID>/applications | jq '.[].id'
|
|
28
|
+
clever curl ${conf.API_HOST}/v4/billing/organisations/<ORGANISATION_ID>/<INVOICE_NUMBER>.pdf > invoice.pdf
|
|
29
|
+
|
|
30
|
+
Our API documentation is available here :
|
|
31
|
+
|
|
32
|
+
${conf.API_DOC_URL}/v2/
|
|
33
|
+
${conf.API_DOC_URL}/v4/
|
|
34
|
+
`);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
export async function curl () {
|
package/src/commands/database.js
CHANGED
|
@@ -2,8 +2,8 @@ import { sendToApi } from '../models/send-to-api.js';
|
|
|
2
2
|
import { getBackups } from '@clevercloud/client/esm/api/v2/backups.js';
|
|
3
3
|
import { Logger } from '../logger.js';
|
|
4
4
|
import { formatTable as initFormatTable } from '../format-table.js';
|
|
5
|
-
import superagent from 'superagent';
|
|
6
5
|
import fs from 'node:fs';
|
|
6
|
+
import { Writable } from 'node:stream';
|
|
7
7
|
import { findOwnerId } from '../models/addon.js';
|
|
8
8
|
import { resolveRealId, resolveAddonId } from '../models/ids-resolver.js';
|
|
9
9
|
|
|
@@ -80,14 +80,14 @@ export async function downloadBackups (params) {
|
|
|
80
80
|
throw new Error('no backup with this ID');
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if (output) {
|
|
88
|
-
fs.writeFileSync(output, res.body);
|
|
89
|
-
return;
|
|
83
|
+
const response = await globalThis.fetch(backup.download_url);
|
|
84
|
+
if (!response.ok) {
|
|
85
|
+
throw new Error('Failed to download backup');
|
|
90
86
|
}
|
|
91
87
|
|
|
92
|
-
|
|
88
|
+
await response
|
|
89
|
+
.body
|
|
90
|
+
.pipeTo(
|
|
91
|
+
Writable.toWeb(output ? fs.createWriteStream(output) : process.stdout),
|
|
92
|
+
);
|
|
93
93
|
}
|
package/src/commands/delete.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as AppConfig from '../models/app_configuration.js';
|
|
2
2
|
import * as Application from '../models/application.js';
|
|
3
3
|
import { Logger } from '../logger.js';
|
|
4
|
+
import colors from 'colors/safe.js';
|
|
4
5
|
|
|
5
6
|
export async function deleteApp (params) {
|
|
6
7
|
const { alias, app: appIdOrName, yes: skipConfirmation } = params.options;
|
|
@@ -8,16 +9,16 @@ export async function deleteApp (params) {
|
|
|
8
9
|
|
|
9
10
|
const app = await Application.get(ownerId, appId);
|
|
10
11
|
if (app == null) {
|
|
11
|
-
|
|
12
|
+
throw new Error('The application doesn\'t exist');
|
|
12
13
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
14
|
+
|
|
15
|
+
// delete app
|
|
16
|
+
await Application.deleteApp(app, skipConfirmation);
|
|
17
|
+
Logger.printSuccess(`Application ${colors.green(colors.bold(`${app.name}`))} successfully deleted!`);
|
|
18
|
+
Logger.println(` ${colors.grey('•')} Application ID: ${colors.grey(app.id)}`);
|
|
19
|
+
|
|
20
|
+
const wasUnlinked = await AppConfig.removeLinkedApplication({ appId, alias });
|
|
21
|
+
if (wasUnlinked) {
|
|
22
|
+
Logger.println(` ${colors.blue('→')} Local alias ${colors.blue(alias || app.name)} unlinked`);
|
|
22
23
|
}
|
|
23
24
|
};
|