clever-tools 3.14.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +130 -37
- package/bin/clever.js +1427 -718
- package/package.json +61 -77
- package/src/clever-client/auth-bridge.js +9 -12
- package/src/clever-client/ng.js +1 -1
- package/src/clever-client/operators.js +7 -7
- package/src/command-options.js +6 -6
- package/src/command-promise-handler.js +6 -6
- package/src/commands/accesslogs.js +38 -29
- package/src/commands/activity.js +62 -40
- package/src/commands/addon.js +65 -69
- package/src/commands/applications.js +33 -31
- package/src/commands/cancel-deploy.js +7 -7
- package/src/commands/config.js +9 -7
- package/src/commands/console.js +3 -3
- package/src/commands/create.js +32 -31
- package/src/commands/curl.js +14 -36
- package/src/commands/database.js +10 -30
- package/src/commands/delete.js +8 -8
- package/src/commands/deploy.js +60 -46
- package/src/commands/diag.js +27 -25
- package/src/commands/domain.js +89 -91
- package/src/commands/drain.js +26 -14
- package/src/commands/emails.js +24 -28
- package/src/commands/env.js +18 -18
- package/src/commands/features.js +12 -18
- package/src/commands/keycloak.js +11 -11
- package/src/commands/kv.js +13 -13
- package/src/commands/link.js +9 -8
- package/src/commands/login.js +19 -27
- package/src/commands/logout.js +1 -1
- package/src/commands/logs.js +20 -12
- package/src/commands/makeDefault.js +5 -5
- package/src/commands/matomo.js +7 -7
- package/src/commands/metabase.js +9 -9
- package/src/commands/ng.js +32 -32
- package/src/commands/notify-email.js +11 -15
- package/src/commands/open.js +1 -1
- package/src/commands/otoroshi.js +11 -11
- package/src/commands/profile.js +8 -8
- package/src/commands/published-config.js +11 -11
- package/src/commands/restart.js +15 -8
- package/src/commands/scale.js +25 -16
- package/src/commands/service.js +18 -9
- package/src/commands/ssh-keys.js +25 -29
- package/src/commands/ssh.js +1 -2
- package/src/commands/status.js +31 -22
- package/src/commands/stop.js +3 -3
- package/src/commands/tcp-redirs.js +27 -26
- package/src/commands/tokens.js +44 -43
- package/src/commands/unlink.js +7 -5
- package/src/commands/version.js +2 -4
- package/src/commands/webhooks.js +8 -10
- package/src/experimental-features.js +7 -5
- package/src/format-table.js +46 -15
- package/src/initial-setup.js +2 -32
- package/src/initial-update-notifier.js +18 -0
- package/src/lib/has-param.js +16 -0
- package/src/lib/ng-print.js +21 -24
- package/src/lib/operator-commands.js +58 -53
- package/src/lib/prompts.js +5 -5
- package/src/lib/slugify.js +1 -1
- package/src/logger.js +24 -22
- package/src/models/activity.js +3 -3
- package/src/models/addon.js +52 -53
- package/src/models/app_configuration.js +34 -32
- package/src/models/application.js +90 -77
- package/src/models/application_configuration.js +19 -27
- package/src/models/configuration.js +30 -32
- package/src/models/deployments.js +9 -19
- package/src/models/domain.js +10 -9
- package/src/models/drain.js +16 -15
- package/src/models/exit-strategy-option.js +15 -5
- package/src/models/fs-utils.js +4 -3
- package/src/models/git.js +26 -32
- package/src/models/ids-resolver.js +37 -41
- package/src/models/isomorphic-http-with-agent.js +3 -3
- package/src/models/json-array.js +5 -6
- package/src/models/log-v4.js +44 -52
- package/src/models/log.js +40 -45
- package/src/models/namespaces.js +4 -4
- package/src/models/ng-resources.js +49 -39
- package/src/models/ng.js +41 -40
- package/src/models/node-dns-resolver.js +3 -3
- package/src/models/notification.js +7 -10
- package/src/models/operator.js +8 -10
- package/src/models/organisation.js +5 -8
- package/src/models/send-to-api.js +18 -11
- package/src/models/user.js +6 -5
- package/src/models/utils.js +5 -8
- package/src/models/variables.js +18 -20
- package/src/parsers.js +34 -34
- package/src/load-package-json.cjs +0 -5
package/src/commands/config.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
+
import { styleText } from 'node:util';
|
|
2
|
+
import { Logger } from '../logger.js';
|
|
1
3
|
import * as Application from '../models/application.js';
|
|
2
4
|
import * as ApplicationConfiguration from '../models/application_configuration.js';
|
|
3
|
-
import { Logger } from '../logger.js';
|
|
4
|
-
import colors from 'colors/safe.js';
|
|
5
5
|
|
|
6
|
-
export async function list
|
|
6
|
+
export async function list(params) {
|
|
7
7
|
const { alias, app: appIdOrName } = params.options;
|
|
8
8
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
9
9
|
const app = await Application.get(ownerId, appId);
|
|
10
10
|
ApplicationConfiguration.printAllValues(app);
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export async function get
|
|
13
|
+
export async function get(params) {
|
|
14
14
|
const [configurationName] = params.args;
|
|
15
15
|
const { alias, app: appIdOrName } = params.options;
|
|
16
16
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
@@ -18,7 +18,7 @@ export async function get (params) {
|
|
|
18
18
|
ApplicationConfiguration.printValue(app, configurationName);
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
export async function set
|
|
21
|
+
export async function set(params) {
|
|
22
22
|
const [configurationName, configurationValue] = params.args;
|
|
23
23
|
const { alias, app: appIdOrName } = params.options;
|
|
24
24
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
@@ -27,10 +27,12 @@ export async function set (params) {
|
|
|
27
27
|
[config.name]: ApplicationConfiguration.parse(config, configurationValue),
|
|
28
28
|
};
|
|
29
29
|
const app = await Application.updateOptions(ownerId, appId, options);
|
|
30
|
-
Logger.printSuccess(
|
|
30
|
+
Logger.printSuccess(
|
|
31
|
+
`Config ${styleText('green', config.id)} successfully updated to ${styleText('green', ApplicationConfiguration.formatValue(config, app[config.name]).toString())}!`,
|
|
32
|
+
);
|
|
31
33
|
}
|
|
32
34
|
|
|
33
|
-
export async function update
|
|
35
|
+
export async function update(params) {
|
|
34
36
|
const { alias, app: appIdOrName } = params.options;
|
|
35
37
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
36
38
|
const options = ApplicationConfiguration.parseOptions(params.options);
|
package/src/commands/console.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import * as Application from '../models/application.js';
|
|
2
1
|
import * as AppConfig from '../models/app_configuration.js';
|
|
2
|
+
import * as Application from '../models/application.js';
|
|
3
3
|
import { openBrowser } from '../models/utils.js';
|
|
4
4
|
|
|
5
|
-
export async function openConsole
|
|
5
|
+
export async function openConsole(params) {
|
|
6
6
|
const { alias, app: appIdOrName } = params.options;
|
|
7
7
|
|
|
8
8
|
const { apps } = await AppConfig.loadApplicationConf();
|
|
@@ -13,7 +13,7 @@ export async function openConsole (params) {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
16
|
-
const prefixPath =
|
|
16
|
+
const prefixPath = ownerId.startsWith('user_') ? 'users/me' : `organisations/${ownerId}`;
|
|
17
17
|
const consolePath = `/${prefixPath}/applications/${appId}`;
|
|
18
18
|
|
|
19
19
|
await openBrowser(consolePath, `Opening the Console in your browser for application ${appId}`);
|
package/src/commands/create.js
CHANGED
|
@@ -1,24 +1,22 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
|
-
import
|
|
3
|
-
import * as Application from '../models/application.js';
|
|
4
|
-
import * as AppConfig from '../models/app_configuration.js';
|
|
2
|
+
import { styleText } from 'node:util';
|
|
5
3
|
import { Logger } from '../logger.js';
|
|
6
|
-
import
|
|
4
|
+
import * as AppConfig from '../models/app_configuration.js';
|
|
5
|
+
import * as Application from '../models/application.js';
|
|
7
6
|
import { conf } from '../models/configuration.js';
|
|
7
|
+
import { isGitWorkingDirectoryClean, isInsideGitRepo } from '../models/git.js';
|
|
8
8
|
|
|
9
|
-
export async function create
|
|
9
|
+
export async function create(params) {
|
|
10
10
|
const { type: typeName } = params.options;
|
|
11
11
|
const [rawName] = params.args;
|
|
12
12
|
const { org: orgaIdOrName, alias, region, github: githubOwnerRepo, format, task: taskCommand } = params.options;
|
|
13
13
|
const { apps } = await AppConfig.loadApplicationConf();
|
|
14
14
|
|
|
15
15
|
// Application name is optionnal, use current directory name if not specified (empty string)
|
|
16
|
-
const name =
|
|
16
|
+
const name = rawName !== '' ? rawName : getCurrentDirectoryName();
|
|
17
17
|
|
|
18
|
-
const isTask =
|
|
19
|
-
const envVars = isTask
|
|
20
|
-
? { CC_RUN_COMMAND: taskCommand }
|
|
21
|
-
: {};
|
|
18
|
+
const isTask = taskCommand != null;
|
|
19
|
+
const envVars = isTask ? { CC_RUN_COMMAND: taskCommand } : {};
|
|
22
20
|
|
|
23
21
|
AppConfig.checkAlreadyLinked(apps, name, alias);
|
|
24
22
|
|
|
@@ -42,16 +40,16 @@ export async function create (params) {
|
|
|
42
40
|
default:
|
|
43
41
|
await displayAppCreation(app, alias, github, taskCommand);
|
|
44
42
|
}
|
|
45
|
-
}
|
|
43
|
+
}
|
|
46
44
|
|
|
47
|
-
function getGithubDetails
|
|
45
|
+
function getGithubDetails(githubOwnerRepo) {
|
|
48
46
|
if (githubOwnerRepo != null) {
|
|
49
47
|
const [owner, name] = githubOwnerRepo.split('/');
|
|
50
48
|
return { owner, name };
|
|
51
49
|
}
|
|
52
50
|
}
|
|
53
51
|
|
|
54
|
-
function getCurrentDirectoryName
|
|
52
|
+
function getCurrentDirectoryName() {
|
|
55
53
|
return path.basename(process.cwd());
|
|
56
54
|
}
|
|
57
55
|
|
|
@@ -62,16 +60,15 @@ function getCurrentDirectoryName () {
|
|
|
62
60
|
* @param {Object} github - The GitHub details
|
|
63
61
|
* @param {string} taskCommand - The task command
|
|
64
62
|
*/
|
|
65
|
-
async function displayAppCreation
|
|
66
|
-
|
|
67
|
-
Logger.printSuccess(`Application ${colors.green(app.name)} successfully created!`);
|
|
63
|
+
async function displayAppCreation(app, alias, github, taskCommand) {
|
|
64
|
+
Logger.printSuccess(`Application ${styleText('green', app.name)} successfully created!`);
|
|
68
65
|
|
|
69
66
|
const hasDistinctAlias = alias != null && alias !== app.name;
|
|
70
67
|
const isTask = app.instance.lifetime === 'TASK';
|
|
71
68
|
|
|
72
69
|
Logger.println();
|
|
73
70
|
printFieldsAsTable(2, {
|
|
74
|
-
Type:
|
|
71
|
+
Type: styleText('blue', `⬢ ${app.instance.variant.name}`),
|
|
75
72
|
ID: app.id,
|
|
76
73
|
'Organisation ID': app.ownerId,
|
|
77
74
|
Name: app.name,
|
|
@@ -82,21 +79,20 @@ async function displayAppCreation (app, alias, github, taskCommand) {
|
|
|
82
79
|
});
|
|
83
80
|
|
|
84
81
|
Logger.println();
|
|
85
|
-
Logger.println(' ' +
|
|
82
|
+
Logger.println(' ' + styleText('bold', 'Next steps:'));
|
|
86
83
|
|
|
87
84
|
if (!github) {
|
|
88
85
|
const isInsideGit = await isInsideGitRepo();
|
|
89
86
|
if (!isInsideGit) {
|
|
90
|
-
Logger.println(` ${
|
|
87
|
+
Logger.println(` ${styleText('yellow', '!')} Initialize a git repository first, for example:`);
|
|
91
88
|
Logger.println(` ${shellCommand('git init')}`);
|
|
92
89
|
Logger.println(` ${shellCommand('git add .')}`);
|
|
93
90
|
Logger.println(` ${shellCommand('git commit -m "Initial commit"')}`);
|
|
94
91
|
Logger.println();
|
|
95
|
-
}
|
|
96
|
-
else {
|
|
92
|
+
} else {
|
|
97
93
|
const isClean = await isGitWorkingDirectoryClean();
|
|
98
94
|
if (!isClean) {
|
|
99
|
-
Logger.println(` ${
|
|
95
|
+
Logger.println(` ${styleText('yellow', '!')} Commit your changes first:`);
|
|
100
96
|
Logger.println(` ${shellCommand('git add .')}`);
|
|
101
97
|
Logger.println(` ${shellCommand('git commit -m "My changes"')}`);
|
|
102
98
|
Logger.println();
|
|
@@ -105,24 +101,29 @@ async function displayAppCreation (app, alias, github, taskCommand) {
|
|
|
105
101
|
}
|
|
106
102
|
|
|
107
103
|
if (github) {
|
|
108
|
-
Logger.println(
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
|
|
104
|
+
Logger.println(
|
|
105
|
+
` ${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`,
|
|
106
|
+
);
|
|
107
|
+
} else {
|
|
108
|
+
Logger.println(
|
|
109
|
+
` ${styleText('blue', '→')} Run ${styleText('blue', 'clever deploy')} ${isTask ? 'to execute your task' : 'to deploy your application'}`,
|
|
110
|
+
);
|
|
112
111
|
}
|
|
113
112
|
|
|
114
|
-
Logger.println(
|
|
113
|
+
Logger.println(
|
|
114
|
+
` ${styleText('blue', '→')} Manage your application at: ${styleText('underline', `${conf.GOTO_URL}/${app.id}`)}`,
|
|
115
|
+
);
|
|
115
116
|
Logger.println('');
|
|
116
117
|
}
|
|
117
118
|
|
|
118
|
-
function shellCommand
|
|
119
|
-
return `${
|
|
119
|
+
function shellCommand(command) {
|
|
120
|
+
return `${styleText('grey', '$')} ${styleText('yellow', command)}`;
|
|
120
121
|
}
|
|
121
122
|
|
|
122
|
-
function printFieldsAsTable
|
|
123
|
+
function printFieldsAsTable(indent, fields) {
|
|
123
124
|
const fieldsWithValues = Object.fromEntries(Object.entries(fields).filter(([_, value]) => typeof value === 'string'));
|
|
124
125
|
const labelMaxWidth = Math.max(...Object.keys(fieldsWithValues).map((label) => label.length));
|
|
125
126
|
return Object.entries(fieldsWithValues).forEach(([label, value]) => {
|
|
126
|
-
Logger.println(`${' '.repeat(indent)}${label.padEnd(labelMaxWidth, ' ')} ${
|
|
127
|
+
Logger.println(`${' '.repeat(indent)}${label.padEnd(labelMaxWidth, ' ')} ${styleText('grey', value)}`);
|
|
127
128
|
});
|
|
128
129
|
}
|
package/src/commands/curl.js
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import { spawn } from 'node:child_process';
|
|
2
|
-
import { conf, loadOAuthConf } from '../models/configuration.js';
|
|
3
1
|
import { addOauthHeader } from '@clevercloud/client/esm/oauth.js';
|
|
4
|
-
import { Logger } from '../logger.js';
|
|
5
|
-
import colors from 'colors/safe.js';
|
|
6
|
-
import curlconverter from 'curlconverter';
|
|
7
2
|
import dedent from 'dedent';
|
|
3
|
+
import { spawn } from 'node:child_process';
|
|
4
|
+
import { styleText } from 'node:util';
|
|
5
|
+
import { Logger } from '../logger.js';
|
|
6
|
+
import { conf, loadOAuthConf } from '../models/configuration.js';
|
|
8
7
|
|
|
9
|
-
async function loadTokens
|
|
8
|
+
async function loadTokens() {
|
|
10
9
|
const tokens = await loadOAuthConf();
|
|
11
10
|
return {
|
|
12
11
|
OAUTH_CONSUMER_KEY: conf.OAUTH_CONSUMER_KEY,
|
|
@@ -16,7 +15,7 @@ async function loadTokens () {
|
|
|
16
15
|
};
|
|
17
16
|
}
|
|
18
17
|
|
|
19
|
-
function printCleverCurlHelp
|
|
18
|
+
function printCleverCurlHelp() {
|
|
20
19
|
Logger.println(dedent`
|
|
21
20
|
Usage: clever curl
|
|
22
21
|
Query Clever Cloud's API using Clever Tools credentials. For example:
|
|
@@ -34,8 +33,7 @@ function printCleverCurlHelp () {
|
|
|
34
33
|
`);
|
|
35
34
|
}
|
|
36
35
|
|
|
37
|
-
export async function curl
|
|
38
|
-
|
|
36
|
+
export async function curl() {
|
|
39
37
|
// We remove the first three args: "node", "clever" and "curl"
|
|
40
38
|
const curlArgs = process.argv.slice(3);
|
|
41
39
|
const hasNoArgs = curlArgs.length === 0;
|
|
@@ -47,47 +45,27 @@ export async function curl () {
|
|
|
47
45
|
return;
|
|
48
46
|
}
|
|
49
47
|
|
|
50
|
-
|
|
51
|
-
// For "reasons", when the command has a help param, it stops the parsing and triggers a very short and localized version of some help (WAT?).
|
|
52
|
-
// That's why we remove the help param before parsing and "reinstate" it before execution.
|
|
53
|
-
const curlArgsNoHelp = curlArgs.filter((arg) => arg !== '--help' && arg !== '-h');
|
|
54
|
-
const curlCommand = ['curl', ...curlArgsNoHelp].join(' ');
|
|
55
|
-
const requestParams = await parseCurlCommand(curlCommand);
|
|
48
|
+
const curlUrl = curlArgs.find((part) => part.startsWith(conf.API_HOST));
|
|
56
49
|
|
|
57
50
|
// We only allow request to the respective API_HOST
|
|
58
|
-
if (
|
|
59
|
-
Logger.error('"clever curl" command must be used with ' +
|
|
51
|
+
if (curlUrl == null) {
|
|
52
|
+
Logger.error('"clever curl" command must be used with ' + styleText('blue', conf.API_HOST));
|
|
60
53
|
process.exit(1);
|
|
61
54
|
}
|
|
62
55
|
|
|
63
56
|
const lastCurlArg = curlArgs.at(-1);
|
|
64
57
|
const lastCurlArgIsHelp = lastCurlArg !== '--help' && lastCurlArg !== '-h';
|
|
65
58
|
|
|
66
|
-
// Add
|
|
59
|
+
// Add OAuth header, only if last cURL arg is not help
|
|
67
60
|
// We do this because cURL's help arg expect a category
|
|
68
61
|
if (lastCurlArgIsHelp) {
|
|
69
|
-
|
|
70
62
|
const tokens = await loadTokens();
|
|
71
|
-
const oauthHeader = await Promise.resolve(
|
|
63
|
+
const oauthHeader = await Promise.resolve({})
|
|
72
64
|
.then(addOauthHeader(tokens))
|
|
73
|
-
.then((request) => request.headers.
|
|
65
|
+
.then((request) => request.headers.authorization);
|
|
74
66
|
|
|
75
|
-
curlArgs.push('-H', `
|
|
67
|
+
curlArgs.push('-H', `authorization: ${oauthHeader}`);
|
|
76
68
|
}
|
|
77
69
|
|
|
78
70
|
spawn('curl', curlArgs, { stdio: 'inherit' });
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
async function parseCurlCommand (curlCommand) {
|
|
83
|
-
|
|
84
|
-
const jsonString = curlconverter.toJsonString(curlCommand);
|
|
85
|
-
const curlRequestParams = JSON.parse(jsonString);
|
|
86
|
-
|
|
87
|
-
return {
|
|
88
|
-
method: curlRequestParams.method,
|
|
89
|
-
url: curlRequestParams.url,
|
|
90
|
-
headers: curlRequestParams.headers,
|
|
91
|
-
queryParams: curlRequestParams.queries,
|
|
92
|
-
};
|
|
93
71
|
}
|
package/src/commands/database.js
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
|
-
import { sendToApi } from '../models/send-to-api.js';
|
|
2
1
|
import { getBackups } from '@clevercloud/client/esm/api/v2/backups.js';
|
|
3
|
-
import { Logger } from '../logger.js';
|
|
4
|
-
import { formatTable as initFormatTable } from '../format-table.js';
|
|
5
2
|
import fs from 'node:fs';
|
|
6
3
|
import { Writable } from 'node:stream';
|
|
4
|
+
import { formatTable } from '../format-table.js';
|
|
5
|
+
import { Logger } from '../logger.js';
|
|
7
6
|
import { findOwnerId } from '../models/addon.js';
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
const formatTable = initFormatTable();
|
|
11
|
-
|
|
12
|
-
export async function listBackups (params) {
|
|
7
|
+
import { resolveAddonId, resolveRealId } from '../models/ids-resolver.js';
|
|
8
|
+
import { sendToApi } from '../models/send-to-api.js';
|
|
13
9
|
|
|
10
|
+
export async function listBackups(params) {
|
|
14
11
|
const { org, format } = params.options;
|
|
15
12
|
const [addonIdOrRealId] = params.args;
|
|
16
13
|
|
|
@@ -44,29 +41,16 @@ export async function listBackups (params) {
|
|
|
44
41
|
break;
|
|
45
42
|
}
|
|
46
43
|
case 'human': {
|
|
47
|
-
const formattedLines = sortedBackups
|
|
48
|
-
.map((backup) => [
|
|
49
|
-
backup.backup_id,
|
|
50
|
-
backup.creation_date,
|
|
51
|
-
backup.status,
|
|
52
|
-
]);
|
|
44
|
+
const formattedLines = sortedBackups.map((backup) => [backup.backup_id, backup.creation_date, backup.status]);
|
|
53
45
|
|
|
54
|
-
const head = [
|
|
55
|
-
'BACKUP ID',
|
|
56
|
-
'CREATION DATE',
|
|
57
|
-
'STATUS',
|
|
58
|
-
];
|
|
46
|
+
const head = ['BACKUP ID', 'CREATION DATE', 'STATUS'];
|
|
59
47
|
|
|
60
|
-
Logger.println(formatTable([
|
|
61
|
-
head,
|
|
62
|
-
...formattedLines,
|
|
63
|
-
]));
|
|
48
|
+
Logger.println(formatTable([head, ...formattedLines]));
|
|
64
49
|
}
|
|
65
50
|
}
|
|
66
51
|
}
|
|
67
52
|
|
|
68
|
-
export async function downloadBackups
|
|
69
|
-
|
|
53
|
+
export async function downloadBackups(params) {
|
|
70
54
|
const { org, output } = params.options;
|
|
71
55
|
const [addonIdOrRealId, backupId] = params.args;
|
|
72
56
|
|
|
@@ -85,9 +69,5 @@ export async function downloadBackups (params) {
|
|
|
85
69
|
throw new Error('Failed to download backup');
|
|
86
70
|
}
|
|
87
71
|
|
|
88
|
-
await response
|
|
89
|
-
.body
|
|
90
|
-
.pipeTo(
|
|
91
|
-
Writable.toWeb(output ? fs.createWriteStream(output) : process.stdout),
|
|
92
|
-
);
|
|
72
|
+
await response.body.pipeTo(Writable.toWeb(output ? fs.createWriteStream(output) : process.stdout));
|
|
93
73
|
}
|
package/src/commands/delete.js
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
+
import { styleText } from 'node:util';
|
|
2
|
+
import { Logger } from '../logger.js';
|
|
1
3
|
import * as AppConfig from '../models/app_configuration.js';
|
|
2
4
|
import * as Application from '../models/application.js';
|
|
3
|
-
import { Logger } from '../logger.js';
|
|
4
|
-
import colors from 'colors/safe.js';
|
|
5
5
|
|
|
6
|
-
export async function deleteApp
|
|
6
|
+
export async function deleteApp(params) {
|
|
7
7
|
const { alias, app: appIdOrName, yes: skipConfirmation } = params.options;
|
|
8
8
|
const { ownerId, appId } = await Application.resolveId(appIdOrName, alias);
|
|
9
9
|
|
|
10
10
|
const app = await Application.get(ownerId, appId);
|
|
11
11
|
if (app == null) {
|
|
12
|
-
throw new Error(
|
|
12
|
+
throw new Error("The application doesn't exist");
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
// delete app
|
|
16
16
|
await Application.deleteApp(app, skipConfirmation);
|
|
17
|
-
Logger.printSuccess(`Application ${
|
|
18
|
-
Logger.println(` ${
|
|
17
|
+
Logger.printSuccess(`Application ${styleText('green', styleText('bold', `${app.name}`))} successfully deleted!`);
|
|
18
|
+
Logger.println(` ${styleText('grey', '•')} Application ID: ${styleText('grey', app.id)}`);
|
|
19
19
|
|
|
20
20
|
const wasUnlinked = await AppConfig.removeLinkedApplication({ appId, alias });
|
|
21
21
|
if (wasUnlinked) {
|
|
22
|
-
Logger.println(` ${
|
|
22
|
+
Logger.println(` ${styleText('blue', '→')} Local alias ${styleText('blue', alias || app.name)} unlinked`);
|
|
23
23
|
}
|
|
24
|
-
}
|
|
24
|
+
}
|
package/src/commands/deploy.js
CHANGED
|
@@ -1,19 +1,27 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { getAllDeployments } from '@clevercloud/client/esm/api/v2/application.js';
|
|
2
2
|
import dedent from 'dedent';
|
|
3
|
-
|
|
3
|
+
import { styleText } from 'node:util';
|
|
4
|
+
import { Logger } from '../logger.js';
|
|
4
5
|
import * as AppConfig from '../models/app_configuration.js';
|
|
5
6
|
import * as Application from '../models/application.js';
|
|
7
|
+
import * as ExitStrategy from '../models/exit-strategy-option.js';
|
|
6
8
|
import * as git from '../models/git.js';
|
|
7
9
|
import * as Log from '../models/log-v4.js';
|
|
8
|
-
import { Logger } from '../logger.js';
|
|
9
|
-
import { getAllDeployments } from '@clevercloud/client/esm/api/v2/application.js';
|
|
10
10
|
import { sendToApi } from '../models/send-to-api.js';
|
|
11
|
-
import * as ExitStrategy from '../models/exit-strategy-option.js';
|
|
12
11
|
|
|
13
12
|
// Once the API call to redeploy() has been triggered successfully,
|
|
14
13
|
// the rest (waiting for deployment state to evolve and displaying logs) is done with auto retry (resilient to network failures)
|
|
15
|
-
export async function deploy
|
|
16
|
-
const {
|
|
14
|
+
export async function deploy(params) {
|
|
15
|
+
const {
|
|
16
|
+
alias,
|
|
17
|
+
branch: branchName,
|
|
18
|
+
tag: tagName,
|
|
19
|
+
quiet,
|
|
20
|
+
force,
|
|
21
|
+
follow,
|
|
22
|
+
'same-commit-policy': sameCommitPolicy,
|
|
23
|
+
'exit-on': exitOnDeploy,
|
|
24
|
+
} = params.options;
|
|
17
25
|
|
|
18
26
|
const exitStrategy = ExitStrategy.get(follow, exitOnDeploy);
|
|
19
27
|
|
|
@@ -23,15 +31,14 @@ export async function deploy (params) {
|
|
|
23
31
|
const branchRefspec = await getBranchToDeploy(branchName, tagName);
|
|
24
32
|
const commitIdToPush = await git.getBranchCommit(branchRefspec);
|
|
25
33
|
const remoteHeadCommitId = await git.getRemoteCommit(appData.deployUrl);
|
|
26
|
-
const deployedCommitId = await Application.get(ownerId, appId)
|
|
27
|
-
.then(({ commitId }) => commitId);
|
|
34
|
+
const deployedCommitId = await Application.get(ownerId, appId).then(({ commitId }) => commitId);
|
|
28
35
|
|
|
29
36
|
await git.addRemote(appData.alias, appData.deployUrl);
|
|
30
37
|
|
|
31
38
|
if (commitIdToPush === remoteHeadCommitId) {
|
|
32
39
|
switch (sameCommitPolicy) {
|
|
33
40
|
case 'ignore':
|
|
34
|
-
Logger.printSuccess(`The application is up-to-date (${
|
|
41
|
+
Logger.printSuccess(`The application is up-to-date (${styleText('grey', remoteHeadCommitId)})`);
|
|
35
42
|
return;
|
|
36
43
|
case 'restart':
|
|
37
44
|
return restartOnSameCommit(ownerId, appId, commitIdToPush, quiet, false, exitStrategy);
|
|
@@ -39,10 +46,11 @@ export async function deploy (params) {
|
|
|
39
46
|
return restartOnSameCommit(ownerId, appId, commitIdToPush, quiet, true, exitStrategy);
|
|
40
47
|
case 'error':
|
|
41
48
|
default: {
|
|
42
|
-
const restartCommand =
|
|
49
|
+
const restartCommand =
|
|
50
|
+
commitIdToPush !== deployedCommitId ? `clever restart --commit ${commitIdToPush}` : 'clever restart';
|
|
43
51
|
throw new Error(dedent`
|
|
44
|
-
Remote HEAD has the same commit as the one to push ${
|
|
45
|
-
Create a new commit, use ${
|
|
52
|
+
Remote HEAD has the same commit as the one to push ${styleText('grey', `(${remoteHeadCommitId})`)}, your application is up-to-date.
|
|
53
|
+
Create a new commit, use ${styleText('blue', restartCommand)} or the ${styleText('blue', '--same-commit-policy')} option.
|
|
46
54
|
`);
|
|
47
55
|
}
|
|
48
56
|
}
|
|
@@ -54,66 +62,72 @@ export async function deploy (params) {
|
|
|
54
62
|
const knownDeployments = await getAllDeployments({ id: ownerId, appId, limit: 5 }).then(sendToApi);
|
|
55
63
|
|
|
56
64
|
Logger.println(dedent`
|
|
57
|
-
${
|
|
58
|
-
Application ID ${
|
|
59
|
-
Organisation ID ${
|
|
65
|
+
${styleText('bold', `🚀 Deploying ${styleText('green', appData.name)}`)}
|
|
66
|
+
Application ID ${styleText('grey', `${appId}`)}
|
|
67
|
+
Organisation ID ${styleText('grey', `${ownerId}`)}
|
|
60
68
|
`);
|
|
61
69
|
|
|
62
70
|
Logger.println();
|
|
63
71
|
|
|
64
|
-
Logger.println(
|
|
72
|
+
Logger.println(styleText('bold', '🔀 Git information'));
|
|
65
73
|
if (remoteHeadCommitId == null || deployedCommitId == null) {
|
|
66
|
-
Logger.println(` ${
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
Logger.println(`
|
|
70
|
-
Logger.println(` Deployed commit ${colors.yellow(deployedCommitId)}`);
|
|
74
|
+
Logger.println(` ${styleText('yellow', '!')} App is brand new, no commits on remote yet`);
|
|
75
|
+
} else {
|
|
76
|
+
Logger.println(` Remote head ${styleText('yellow', remoteHeadCommitId)} (${branchRefspec})`);
|
|
77
|
+
Logger.println(` Deployed commit ${styleText('yellow', deployedCommitId)}`);
|
|
71
78
|
}
|
|
72
|
-
Logger.println(
|
|
79
|
+
Logger.println(
|
|
80
|
+
` Local commit ${styleText('yellow', commitIdToPush)} ${styleText('blue', '[will be deployed]')}`,
|
|
81
|
+
);
|
|
73
82
|
|
|
74
83
|
Logger.println();
|
|
75
84
|
|
|
76
85
|
Logger.println(dedent`
|
|
77
|
-
${
|
|
78
|
-
${
|
|
86
|
+
${styleText('bold', '🔄 Deployment progress')}
|
|
87
|
+
${styleText('blue', '→ Pushing source code to Clever Cloud…')}
|
|
79
88
|
`);
|
|
80
89
|
|
|
81
|
-
await git.push(appData.deployUrl, commitIdToPush, force)
|
|
82
|
-
.
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
await Logger.println(` ${
|
|
93
|
-
|
|
94
|
-
return Log.watchDeploymentAndDisplayLogs({
|
|
90
|
+
await git.push(appData.deployUrl, commitIdToPush, force).catch(async (e) => {
|
|
91
|
+
const isShallow = await git.isShallow();
|
|
92
|
+
if (isShallow) {
|
|
93
|
+
throw new Error(
|
|
94
|
+
'Failed to push your source code because your repository is shallow and therefore cannot be pushed to the Clever Cloud remote.',
|
|
95
|
+
);
|
|
96
|
+
} else {
|
|
97
|
+
throw e;
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
await Logger.println(` ${styleText('green', '✓ Code pushed to Clever Cloud')}`);
|
|
102
|
+
|
|
103
|
+
return Log.watchDeploymentAndDisplayLogs({
|
|
104
|
+
ownerId,
|
|
105
|
+
appId,
|
|
106
|
+
commitId: commitIdToPush,
|
|
107
|
+
knownDeployments,
|
|
108
|
+
quiet,
|
|
109
|
+
exitStrategy,
|
|
110
|
+
});
|
|
95
111
|
}
|
|
96
112
|
|
|
97
|
-
async function restartOnSameCommit
|
|
113
|
+
async function restartOnSameCommit(ownerId, appId, commitIdToPush, quiet, withoutCache, exitStrategy) {
|
|
98
114
|
const cacheSuffix = withoutCache ? ' without using cache' : '';
|
|
99
|
-
Logger.println(`🔄 Restarting ${
|
|
115
|
+
Logger.println(`🔄 Restarting ${styleText('bold', appId)}${cacheSuffix} ${styleText('grey', `(${commitIdToPush})`)}`);
|
|
100
116
|
|
|
101
117
|
const restart = await Application.redeploy(ownerId, appId, commitIdToPush, withoutCache);
|
|
102
118
|
return Log.watchDeploymentAndDisplayLogs({ ownerId, appId, deploymentId: restart.deploymentId, quiet, exitStrategy });
|
|
103
119
|
}
|
|
104
120
|
|
|
105
|
-
async function getBranchToDeploy
|
|
121
|
+
async function getBranchToDeploy(branchName, tagName) {
|
|
106
122
|
if (tagName) {
|
|
107
123
|
const useTag = await git.isExistingTag(tagName);
|
|
108
124
|
if (useTag) {
|
|
109
125
|
const tagRefspec = await git.getFullBranch(tagName);
|
|
110
126
|
return tagRefspec;
|
|
111
|
-
}
|
|
112
|
-
else {
|
|
127
|
+
} else {
|
|
113
128
|
throw new Error(`Tag ${tagName} doesn't exist locally`);
|
|
114
129
|
}
|
|
115
|
-
}
|
|
116
|
-
else {
|
|
130
|
+
} else {
|
|
117
131
|
return await git.getFullBranch(branchName);
|
|
118
132
|
}
|
|
119
133
|
}
|