clever-tools 4.10.0 → 5.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/bin/clever.js +5 -10
- package/package.json +27 -27
- package/src/clever-client/drains.js +17 -0
- package/src/commands/accesslogs/accesslogs.command.js +35 -18
- package/src/commands/accesslogs/accesslogs.docs.md +1 -1
- package/src/commands/addon/addon.docs.md +2 -2
- package/src/commands/config-provider/config-provider.docs.md +5 -5
- package/src/commands/create/create.command.js +8 -1
- package/src/commands/database/database.docs.md +2 -2
- package/src/commands/deploy/deploy.docs.md +7 -6
- package/src/commands/drain/drain.args.js +6 -0
- package/src/commands/drain/drain.check.command.js +43 -0
- package/src/commands/drain/drain.create.betterstack.command.js +36 -0
- package/src/commands/drain/drain.create.command.js +0 -118
- package/src/commands/drain/drain.create.datadog.command.js +27 -0
- package/src/commands/drain/drain.create.elasticsearch.command.js +48 -0
- package/src/commands/drain/drain.create.newrelic.command.js +36 -0
- package/src/commands/drain/drain.create.ovh-tcp.command.js +29 -0
- package/src/commands/drain/drain.create.raw-http.command.js +30 -0
- package/src/commands/drain/drain.create.splunk.command.js +61 -0
- package/src/commands/drain/drain.create.syslog-tcp.command.js +29 -0
- package/src/commands/drain/drain.create.syslog-udp.command.js +29 -0
- package/src/commands/drain/drain.docs.md +296 -7
- package/src/commands/drain/drain.options.js +26 -0
- package/src/commands/features/features.list.command.js +6 -13
- package/src/commands/global.commands.js +25 -1
- package/src/commands/global.options.js +8 -0
- package/src/commands/k8s/k8s.docs.md +17 -17
- package/src/commands/keycloak/keycloak.docs.md +11 -11
- package/src/commands/kv/kv.docs.md +1 -1
- package/src/commands/link/link.docs.md +1 -1
- package/src/commands/login/login.command.js +11 -1
- package/src/commands/matomo/matomo.docs.md +6 -6
- package/src/commands/metabase/metabase.docs.md +9 -9
- package/src/commands/ng/ng.docs.md +10 -10
- package/src/commands/oauth-consumers/oauth-consumers.docs.md +4 -4
- package/src/commands/otoroshi/otoroshi.docs.md +13 -13
- package/src/commands/profile/profile.switch.command.js +5 -1
- package/src/commands/service/service.docs.md +4 -4
- package/src/commands/ssh/ssh.command.js +7 -1
- package/src/config/features.js +61 -29
- package/src/format-table.js +14 -10
- package/src/initial-setup.js +13 -0
- package/src/initial-update-notifier.js +2 -5
- package/src/lib/access-logs-clf.js +52 -0
- package/src/lib/access-logs-transport.js +29 -0
- package/src/lib/cliparse-patched.js +19 -1
- package/src/lib/define-command.types.d.ts +7 -2
- package/src/lib/k8s.js +1 -0
- package/src/lib/operator-commands.js +1 -0
- package/src/lib/profile.js +8 -1
- package/src/lib/prompts.js +30 -2
- package/src/models/drain.js +39 -12
- package/src/models/git-isomorphic.js +16 -1
- package/src/models/git-system.js +2 -2
- package/src/models/git.js +19 -1
- package/src/models/oauth-consumer.js +1 -1
- package/src/models/send-to-api.js +15 -0
package/bin/clever.js
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
verboseOption,
|
|
15
15
|
versionOption,
|
|
16
16
|
} from '../src/commands/global.options.js';
|
|
17
|
-
import { EXPERIMENTAL_FEATURES,
|
|
17
|
+
import { EXPERIMENTAL_FEATURES, isFeatureEnabled } from '../src/config/features.js';
|
|
18
18
|
import { cliparse } from '../src/lib/cliparse-patched.js';
|
|
19
19
|
import { styleText } from '../src/lib/style-text.js';
|
|
20
20
|
import { getDefault, getEnumValues, isBoolean, isRequired } from '../src/lib/zod-utils.js';
|
|
@@ -47,14 +47,10 @@ if (process.argv[2] === 'curl') {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
async function run() {
|
|
50
|
-
// Get enabled experimental features
|
|
51
|
-
/** @type {Record<string, boolean>} */
|
|
52
|
-
const featuresFromConf = await getFeatures();
|
|
53
|
-
|
|
54
50
|
// Build all commands from globalCommands
|
|
55
51
|
const commands = [];
|
|
56
52
|
for (const [name, entry] of /** @type {[string, CommandEntry][]} */ (Object.entries(globalCommands))) {
|
|
57
|
-
const command = buildCommand(name, entry
|
|
53
|
+
const command = buildCommand(name, entry);
|
|
58
54
|
if (command != null) {
|
|
59
55
|
commands.push(command);
|
|
60
56
|
}
|
|
@@ -96,10 +92,9 @@ async function run() {
|
|
|
96
92
|
* Recursively build commands from the global commands structure
|
|
97
93
|
* @param {string} name - Command name
|
|
98
94
|
* @param {CommandEntry} commandEntry - Command entry (either a command object or [command, subcommands])
|
|
99
|
-
* @param {Record<string, boolean>} featuresFromConf - Enabled features configuration
|
|
100
95
|
* @returns {Object|null} cliparse command or null if filtered out
|
|
101
96
|
*/
|
|
102
|
-
function buildCommand(name, commandEntry
|
|
97
|
+
function buildCommand(name, commandEntry) {
|
|
103
98
|
/** @type {CommandDefinition} */
|
|
104
99
|
let commandDef;
|
|
105
100
|
/** @type {Record<string, CommandEntry>} */
|
|
@@ -113,14 +108,14 @@ function buildCommand(name, commandEntry, featuresFromConf) {
|
|
|
113
108
|
}
|
|
114
109
|
|
|
115
110
|
// Check if this is an experimental feature that needs to be enabled
|
|
116
|
-
if (commandDef.featureFlag && !
|
|
111
|
+
if (commandDef.featureFlag && !isFeatureEnabled(commandDef.featureFlag)) {
|
|
117
112
|
return null;
|
|
118
113
|
}
|
|
119
114
|
|
|
120
115
|
// Build subcommands recursively
|
|
121
116
|
const subcommands = [];
|
|
122
117
|
for (const [subName, subEntry] of Object.entries(subcommandsMap)) {
|
|
123
|
-
const subcommand = buildCommand(subName, subEntry
|
|
118
|
+
const subcommand = buildCommand(subName, subEntry);
|
|
124
119
|
if (subcommand != null) {
|
|
125
120
|
subcommands.push(subcommand);
|
|
126
121
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clever-tools",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0",
|
|
4
4
|
"description": "Command Line Interface for Clever Cloud.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -49,56 +49,56 @@
|
|
|
49
49
|
"typecheck": "tsc -p tsconfig.json"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@
|
|
53
|
-
"@
|
|
54
|
-
"@inquirer/prompts": "7.8.4",
|
|
52
|
+
"@clevercloud/client": "12.6.3",
|
|
53
|
+
"@inquirer/prompts": "8.7.2",
|
|
55
54
|
"char-regex": "2.0.2",
|
|
56
55
|
"cliparse": "0.5.0",
|
|
57
|
-
"dedent": "1.7.
|
|
56
|
+
"dedent": "1.7.2",
|
|
58
57
|
"eventsource": "4.1.0",
|
|
59
58
|
"ioredis": "5.8.2",
|
|
60
|
-
"iso8601-duration": "2.1.
|
|
61
|
-
"isomorphic-git": "1.
|
|
59
|
+
"iso8601-duration": "2.1.4",
|
|
60
|
+
"isomorphic-git": "1.41.9",
|
|
62
61
|
"linux-release-info": "3.0.0",
|
|
63
|
-
"lodash": "4.
|
|
64
|
-
"open": "
|
|
65
|
-
"semver": "7.
|
|
66
|
-
"simple-git": "3.
|
|
67
|
-
"slugify": "1.6.
|
|
68
|
-
"tldts": "7.
|
|
62
|
+
"lodash": "4.18.1",
|
|
63
|
+
"open": "11.0.2",
|
|
64
|
+
"semver": "7.8.5",
|
|
65
|
+
"simple-git": "3.36.0",
|
|
66
|
+
"slugify": "1.6.9",
|
|
67
|
+
"tldts": "7.4.12",
|
|
68
|
+
"undici": "8.10.2",
|
|
69
69
|
"update-notifier": "7.3.1",
|
|
70
70
|
"xdg": "0.1.1",
|
|
71
|
-
"zod": "4.
|
|
71
|
+
"zod": "4.5.4"
|
|
72
72
|
},
|
|
73
73
|
"devDependencies": {
|
|
74
|
-
"@aws-sdk/client-s3": "3.
|
|
74
|
+
"@aws-sdk/client-s3": "3.1128.0",
|
|
75
75
|
"@clevercloud/eslint-config": "1.1.0",
|
|
76
|
-
"@commitlint/cli": "
|
|
77
|
-
"@commitlint/config-conventional": "
|
|
78
|
-
"@eslint/compat": "1.
|
|
79
|
-
"@rollup/plugin-commonjs": "
|
|
76
|
+
"@commitlint/cli": "21.2.2",
|
|
77
|
+
"@commitlint/config-conventional": "21.2.2",
|
|
78
|
+
"@eslint/compat": "2.1.1",
|
|
79
|
+
"@rollup/plugin-commonjs": "29.0.3",
|
|
80
80
|
"@rollup/plugin-json": "6.1.0",
|
|
81
81
|
"@rollup/plugin-node-resolve": "16.0.3",
|
|
82
82
|
"@types/mime-types": "3.0.1",
|
|
83
|
-
"@types/picomatch": "4.0.
|
|
84
|
-
"@yao-pkg/pkg": "6.
|
|
83
|
+
"@types/picomatch": "4.0.3",
|
|
84
|
+
"@yao-pkg/pkg": "6.22.0",
|
|
85
85
|
"eslint": "9.39.1",
|
|
86
|
-
"globals": "
|
|
86
|
+
"globals": "17.12.0",
|
|
87
87
|
"mime-types": "3.0.2",
|
|
88
|
-
"prettier": "3.
|
|
88
|
+
"prettier": "3.9.6",
|
|
89
89
|
"prettier-plugin-organize-imports": "4.3.0",
|
|
90
90
|
"remark-parse": "11.0.0",
|
|
91
91
|
"remark-stringify": "11.0.0",
|
|
92
|
-
"rollup": "4.
|
|
93
|
-
"tinyglobby": "0.2.
|
|
92
|
+
"rollup": "4.63.1",
|
|
93
|
+
"tinyglobby": "0.2.17",
|
|
94
94
|
"typescript": "5.9.3",
|
|
95
95
|
"unified": "11.0.5"
|
|
96
96
|
},
|
|
97
97
|
"engines": {
|
|
98
|
-
"node": ">=
|
|
98
|
+
"node": ">=24"
|
|
99
99
|
},
|
|
100
100
|
"volta": {
|
|
101
|
-
"node": "
|
|
101
|
+
"node": "24.18.1",
|
|
102
102
|
"npm": "11.6.2"
|
|
103
103
|
}
|
|
104
104
|
}
|
|
@@ -108,3 +108,20 @@ export function enableDrain(params) {
|
|
|
108
108
|
// no body
|
|
109
109
|
});
|
|
110
110
|
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* POST /v4/drains/organisations/{ownerId}/resources/{resourceId}/drains/{drainId}/check
|
|
114
|
+
* @param {Object} params
|
|
115
|
+
* @param {String} params.ownerId
|
|
116
|
+
* @param {String} params.resourceId
|
|
117
|
+
* @param {String} params.drainId
|
|
118
|
+
*/
|
|
119
|
+
export function checkDrain(params) {
|
|
120
|
+
// no multipath for /self or /organisations/{id}
|
|
121
|
+
return Promise.resolve({
|
|
122
|
+
method: 'post',
|
|
123
|
+
url: `/v4/drains/organisations/${params.ownerId}/resources/${params.resourceId}/drains/${params.drainId}/check`,
|
|
124
|
+
headers: { Accept: 'application/json' },
|
|
125
|
+
// no body
|
|
126
|
+
});
|
|
127
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { ApplicationAccessLogStream } from '@clevercloud/client/esm/streams/access-logs.js';
|
|
2
2
|
import { formatTable } from '../../format-table.js';
|
|
3
|
+
import { formatClf } from '../../lib/access-logs-clf.js';
|
|
4
|
+
import { guessAccessLogTransport } from '../../lib/access-logs-transport.js';
|
|
3
5
|
import { defineCommand } from '../../lib/define-command.js';
|
|
4
6
|
import { styleText } from '../../lib/style-text.js';
|
|
5
7
|
import { Logger } from '../../logger.js';
|
|
@@ -8,12 +10,12 @@ import { JsonArray } from '../../models/json-array.js';
|
|
|
8
10
|
import { getHostAndTokens } from '../../models/send-to-api.js';
|
|
9
11
|
import { truncateWithEllipsis } from '../../models/utils.js';
|
|
10
12
|
import {
|
|
13
|
+
accessLogsFormatOption,
|
|
11
14
|
addonIdOrRealIdOption,
|
|
12
15
|
afterOption,
|
|
13
16
|
aliasOption,
|
|
14
17
|
appIdOrNameOption,
|
|
15
18
|
beforeOption,
|
|
16
|
-
logsFormatOption,
|
|
17
19
|
} from '../global.options.js';
|
|
18
20
|
|
|
19
21
|
const THROTTLE_ELEMENTS = 2000;
|
|
@@ -22,28 +24,40 @@ const THROTTLE_PER_IN_MILLISECONDS = 100;
|
|
|
22
24
|
|
|
23
25
|
const CITY_MAX_LENGTH = 20;
|
|
24
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Format an access log as a human readable table row.
|
|
29
|
+
*
|
|
30
|
+
* Non-HTTP access logs (TCP redirections, SSH connections, or HTTP connections cut before the
|
|
31
|
+
* proxy could answer) have no `http` section: their status and request columns are omitted.
|
|
32
|
+
*
|
|
33
|
+
* @param {object} log an access log, as emitted by `ApplicationAccessLogStream`
|
|
34
|
+
* @returns {string}
|
|
35
|
+
*/
|
|
25
36
|
function formatHuman(log) {
|
|
26
37
|
const { date, http, source } = log;
|
|
27
38
|
const country = source.countryCode ?? '(unknown)';
|
|
28
39
|
const hasSourceCity = source.city ?? '';
|
|
29
40
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
const columns = [
|
|
42
|
+
styleText('grey', date.toISOString(date)),
|
|
43
|
+
guessAccessLogTransport(log),
|
|
44
|
+
source.ip,
|
|
45
|
+
`${country}${hasSourceCity ? '/' + truncateWithEllipsis(CITY_MAX_LENGTH, source.city) : ''}`,
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
if (http != null) {
|
|
49
|
+
columns.push(colorStatusCode(http.response.statusCode));
|
|
50
|
+
columns.push(http.request.method);
|
|
51
|
+
columns.push(http.request.path);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return formatTable([columns], ACCESSLOG_COLUMN_WIDTHS);
|
|
43
55
|
}
|
|
44
56
|
|
|
45
57
|
const ACCESSLOG_COLUMN_WIDTHS = [
|
|
46
58
|
'2024-06-24T08:05:43.880Z',
|
|
59
|
+
// longest transport name
|
|
60
|
+
'HTTP',
|
|
47
61
|
'255.255.255.255',
|
|
48
62
|
// country / city
|
|
49
63
|
2 + 1 + CITY_MAX_LENGTH,
|
|
@@ -76,7 +90,7 @@ export const accesslogsCommand = defineCommand({
|
|
|
76
90
|
options: {
|
|
77
91
|
alias: aliasOption,
|
|
78
92
|
app: appIdOrNameOption,
|
|
79
|
-
format:
|
|
93
|
+
format: accessLogsFormatOption,
|
|
80
94
|
before: beforeOption,
|
|
81
95
|
after: afterOption,
|
|
82
96
|
addon: addonIdOrRealIdOption,
|
|
@@ -132,13 +146,16 @@ export const accesslogsCommand = defineCommand({
|
|
|
132
146
|
case 'json-stream':
|
|
133
147
|
Logger.printJson(log);
|
|
134
148
|
break;
|
|
135
|
-
case '
|
|
136
|
-
|
|
137
|
-
// when the connection is cut too early, or for TCP redirections, we don't have HTTP section
|
|
149
|
+
case 'clf':
|
|
150
|
+
// CLF only describes HTTP requests, so logs without an HTTP section are skipped
|
|
138
151
|
if (log.http == null) {
|
|
139
152
|
break;
|
|
140
153
|
}
|
|
141
154
|
|
|
155
|
+
Logger.println(formatClf(log));
|
|
156
|
+
break;
|
|
157
|
+
case 'human':
|
|
158
|
+
default:
|
|
142
159
|
Logger.println(formatHuman(log));
|
|
143
160
|
break;
|
|
144
161
|
}
|
|
@@ -17,4 +17,4 @@ clever accesslogs [options]
|
|
|
17
17
|
|`-a`, `--alias` `<alias>`|Short name for the application|
|
|
18
18
|
|`--app` `<app-id\|app-name>`|Application to manage by its ID (or name, if unambiguous)|
|
|
19
19
|
|`--before`, `--until` `<before>`|Fetch logs before this date/time (ISO8601 date, positive number in seconds or duration, e.g.: 1h)|
|
|
20
|
-
|`-F`, `--format` `<format>`|Output format (human, json, json-stream) (default: human)|
|
|
20
|
+
|`-F`, `--format` `<format>`|Output format (clf only outputs HTTP access logs) (human, json, json-stream, clf) (default: human)|
|
|
@@ -55,7 +55,7 @@ clever addon delete <addon-id|addon-name> [options]
|
|
|
55
55
|
|
|
56
56
|
|Name|Description|
|
|
57
57
|
|---|---|
|
|
58
|
-
|`addon-id
|
|
58
|
+
|`addon-id\|addon-name`|Add-on ID (or name, if unambiguous)|
|
|
59
59
|
|
|
60
60
|
### ⚙️ Options
|
|
61
61
|
|
|
@@ -148,7 +148,7 @@ clever addon rename <addon-id|addon-name> <addon-name> [options]
|
|
|
148
148
|
|
|
149
149
|
|Name|Description|
|
|
150
150
|
|---|---|
|
|
151
|
-
|`addon-id
|
|
151
|
+
|`addon-id\|addon-name`|Add-on ID (or name, if unambiguous)|
|
|
152
152
|
|`addon-name`|Add-on name|
|
|
153
153
|
|
|
154
154
|
### ⚙️ Options
|
|
@@ -20,7 +20,7 @@ clever config-provider get <addon-id|config-provider-id|addon-name> [options]
|
|
|
20
20
|
|
|
21
21
|
|Name|Description|
|
|
22
22
|
|---|---|
|
|
23
|
-
|`addon-id
|
|
23
|
+
|`addon-id\|config-provider-id\|addon-name`|Add-on ID, real ID (config_xxx) or name (if unambiguous)|
|
|
24
24
|
|
|
25
25
|
### ⚙️ Options
|
|
26
26
|
|
|
@@ -41,7 +41,7 @@ clever config-provider import <addon-id|config-provider-id|addon-name> [options]
|
|
|
41
41
|
|
|
42
42
|
|Name|Description|
|
|
43
43
|
|---|---|
|
|
44
|
-
|`addon-id
|
|
44
|
+
|`addon-id\|config-provider-id\|addon-name`|Add-on ID, real ID (config_xxx) or name (if unambiguous)|
|
|
45
45
|
|
|
46
46
|
### ⚙️ Options
|
|
47
47
|
|
|
@@ -75,7 +75,7 @@ clever config-provider open <addon-id|config-provider-id|addon-name>
|
|
|
75
75
|
|
|
76
76
|
|Name|Description|
|
|
77
77
|
|---|---|
|
|
78
|
-
|`addon-id
|
|
78
|
+
|`addon-id\|config-provider-id\|addon-name`|Add-on ID, real ID (config_xxx) or name (if unambiguous)|
|
|
79
79
|
|
|
80
80
|
## ➡️ `clever config-provider rm` <kbd>Since 4.6.0</kbd>
|
|
81
81
|
|
|
@@ -89,7 +89,7 @@ clever config-provider rm <addon-id|config-provider-id|addon-name> <variable-nam
|
|
|
89
89
|
|
|
90
90
|
|Name|Description|
|
|
91
91
|
|---|---|
|
|
92
|
-
|`addon-id
|
|
92
|
+
|`addon-id\|config-provider-id\|addon-name`|Add-on ID, real ID (config_xxx) or name (if unambiguous)|
|
|
93
93
|
|`variable-name`|Name of the environment variable|
|
|
94
94
|
|
|
95
95
|
## ➡️ `clever config-provider set` <kbd>Since 4.6.0</kbd>
|
|
@@ -104,6 +104,6 @@ clever config-provider set <addon-id|config-provider-id|addon-name> <variable-na
|
|
|
104
104
|
|
|
105
105
|
|Name|Description|
|
|
106
106
|
|---|---|
|
|
107
|
-
|`addon-id
|
|
107
|
+
|`addon-id\|config-provider-id\|addon-name`|Add-on ID, real ID (config_xxx) or name (if unambiguous)|
|
|
108
108
|
|`variable-name`|Name of the environment variable|
|
|
109
109
|
|`variable-value`|Value of the environment variable|
|
|
@@ -9,6 +9,7 @@ import { Logger } from '../../logger.js';
|
|
|
9
9
|
import * as AppConfig from '../../models/app_configuration.js';
|
|
10
10
|
import * as Application from '../../models/application.js';
|
|
11
11
|
import { AVAILABLE_ZONES, listAvailableTypes, listAvailableZones } from '../../models/application.js';
|
|
12
|
+
import { LinkedWorktreeNotSupportedError } from '../../models/git-isomorphic.js';
|
|
12
13
|
import { Git } from '../../models/git.js';
|
|
13
14
|
import { aliasCreationOption, humanJsonOutputFormatOption, orgaIdOrNameOption } from '../global.options.js';
|
|
14
15
|
|
|
@@ -54,7 +55,13 @@ async function displayAppCreation(app, alias, github, taskCommand) {
|
|
|
54
55
|
Logger.println(` ${shellCommand('git commit -m "Initial commit"')}`);
|
|
55
56
|
Logger.println();
|
|
56
57
|
} else {
|
|
57
|
-
|
|
58
|
+
// Best-effort hint: if we can't tell because of a linked worktree on the JS backend, don't nag
|
|
59
|
+
const isClean = await git.isGitWorkingDirectoryClean().catch((error) => {
|
|
60
|
+
if (error instanceof LinkedWorktreeNotSupportedError) {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
throw error;
|
|
64
|
+
});
|
|
58
65
|
if (!isClean) {
|
|
59
66
|
Logger.println(` ${styleText('yellow', '!')} Commit your changes first:`);
|
|
60
67
|
Logger.println(` ${shellCommand('git add .')}`);
|
|
@@ -20,7 +20,7 @@ clever database backups <database-id|addon-id> [options]
|
|
|
20
20
|
|
|
21
21
|
|Name|Description|
|
|
22
22
|
|---|---|
|
|
23
|
-
|`database-id
|
|
23
|
+
|`database-id\|addon-id`|Any database ID (format: addon_UUID, postgresql_UUID, mysql_UUID, ...)|
|
|
24
24
|
|
|
25
25
|
### ⚙️ Options
|
|
26
26
|
|
|
@@ -41,7 +41,7 @@ clever database backups download <database-id|addon-id> <backup-id> [options]
|
|
|
41
41
|
|
|
42
42
|
|Name|Description|
|
|
43
43
|
|---|---|
|
|
44
|
-
|`database-id
|
|
44
|
+
|`database-id\|addon-id`|Any database ID (format: addon_UUID, postgresql_UUID, mysql_UUID, ...)|
|
|
45
45
|
|`backup-id`|A Database backup ID (format: UUID)|
|
|
46
46
|
|
|
47
47
|
### ⚙️ Options
|
|
@@ -23,20 +23,21 @@ clever deploy [options]
|
|
|
23
23
|
|
|
24
24
|
### 🧪 Experimental: System git backend
|
|
25
25
|
|
|
26
|
-
Clever Tools uses
|
|
26
|
+
Clever Tools uses the `git` command installed on your system for git operations (it must be in your `PATH` environment variable).
|
|
27
|
+
|
|
28
|
+
If `git` is not available, or you experience an issue with this backend, you can fall back to the previous pure JS implementation. This works without requiring git to be installed on your system, but has some limitations:
|
|
27
29
|
|
|
28
30
|
* **HTTP-only**: cannot use SSH-based git protocols
|
|
29
31
|
* **Slow performance** on repositories with rewritten history (rebases, squashes)
|
|
30
32
|
* **Connection timeouts** on large repositories or when pushing big files, due to HTTP-based transfers
|
|
31
|
-
|
|
32
|
-
If you experience any of these issues, you can enable the **system git backend** which uses the `git` command installed on your system (it must be in your `PATH` environment variable).
|
|
33
|
+
* **No git worktree support**: deploying from a linked git worktree (`git worktree add`) fails with `Could not find HEAD`
|
|
33
34
|
|
|
34
35
|
```bash
|
|
35
|
-
clever features
|
|
36
|
+
clever features disable system-git
|
|
36
37
|
```
|
|
37
38
|
|
|
38
|
-
To
|
|
39
|
+
To switch back to the system git backend:
|
|
39
40
|
|
|
40
41
|
```bash
|
|
41
|
-
clever features
|
|
42
|
+
clever features enable system-git
|
|
42
43
|
```
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { checkDrain } from '../../clever-client/drains.js';
|
|
2
|
+
import { defineCommand } from '../../lib/define-command.js';
|
|
3
|
+
import { styleText } from '../../lib/style-text.js';
|
|
4
|
+
import { Logger } from '../../logger.js';
|
|
5
|
+
import { resolveDrainResource } from '../../models/drain.js';
|
|
6
|
+
import { sendToApi } from '../../models/send-to-api.js';
|
|
7
|
+
import {
|
|
8
|
+
addonIdOrRealIdOption,
|
|
9
|
+
aliasOption,
|
|
10
|
+
appIdOrNameOption,
|
|
11
|
+
humanJsonOutputFormatOption,
|
|
12
|
+
} from '../global.options.js';
|
|
13
|
+
import { drainIdArg } from './drain.args.js';
|
|
14
|
+
|
|
15
|
+
export const drainCheckCommand = defineCommand({
|
|
16
|
+
description: "Check that a drain's recipient is reachable and accepts deliveries",
|
|
17
|
+
since: '4.11.0',
|
|
18
|
+
options: {
|
|
19
|
+
alias: aliasOption,
|
|
20
|
+
appIdOrName: appIdOrNameOption,
|
|
21
|
+
addonIdOrRealId: addonIdOrRealIdOption,
|
|
22
|
+
format: humanJsonOutputFormatOption,
|
|
23
|
+
},
|
|
24
|
+
args: [drainIdArg],
|
|
25
|
+
async handler(options, drainId) {
|
|
26
|
+
const { alias, appIdOrName, addonIdOrRealId, format } = options;
|
|
27
|
+
const { ownerId, resourceId } = await resolveDrainResource(alias, appIdOrName, addonIdOrRealId);
|
|
28
|
+
|
|
29
|
+
const probe = await checkDrain({ ownerId, resourceId, drainId }).then(sendToApi);
|
|
30
|
+
switch (format) {
|
|
31
|
+
case 'json': {
|
|
32
|
+
Logger.printJson({ ok: probe.ok, message: probe.message });
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
case 'human':
|
|
36
|
+
default: {
|
|
37
|
+
const status = probe.ok ? styleText(['bold', 'green'], 'OK') : styleText(['bold', 'red'], 'FAILED');
|
|
38
|
+
Logger.println(`Probe: ${status}`);
|
|
39
|
+
Logger.println(probe.message);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
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 { createLogDrain, resolveDrainResource } from '../../models/drain.js';
|
|
7
|
+
import { addonIdOrRealIdOption, aliasOption, appIdOrNameOption } from '../global.options.js';
|
|
8
|
+
import { drainUrlArg } from './drain.args.js';
|
|
9
|
+
|
|
10
|
+
export const drainCreateBetterstackCommand = defineCommand({
|
|
11
|
+
description: 'Create a Better Stack drain',
|
|
12
|
+
since: '4.11.0',
|
|
13
|
+
options: {
|
|
14
|
+
sourceToken: defineOption({
|
|
15
|
+
name: 'source-token',
|
|
16
|
+
schema: z.string().min(1),
|
|
17
|
+
description: 'Source token',
|
|
18
|
+
aliases: ['t'],
|
|
19
|
+
placeholder: 'source-token',
|
|
20
|
+
}),
|
|
21
|
+
alias: aliasOption,
|
|
22
|
+
appIdOrName: appIdOrNameOption,
|
|
23
|
+
addonIdOrRealId: addonIdOrRealIdOption,
|
|
24
|
+
},
|
|
25
|
+
args: [drainUrlArg],
|
|
26
|
+
async handler(options, url) {
|
|
27
|
+
const { alias, appIdOrName, addonIdOrRealId, sourceToken } = options;
|
|
28
|
+
const { ownerId, resourceId } = await resolveDrainResource(alias, appIdOrName, addonIdOrRealId);
|
|
29
|
+
|
|
30
|
+
const drain = await createLogDrain('BETTERSTACK', ownerId, resourceId, url, { sourceToken });
|
|
31
|
+
|
|
32
|
+
Logger.printSuccess(
|
|
33
|
+
`Better Stack drain ${styleText(['bold', 'green'], drain.id)} has been successfully created and enabled!`,
|
|
34
|
+
);
|
|
35
|
+
},
|
|
36
|
+
});
|
|
@@ -1,124 +1,6 @@
|
|
|
1
|
-
import { z } from 'zod';
|
|
2
|
-
import { createDrain } from '../../clever-client/drains.js';
|
|
3
|
-
import { defineArgument } from '../../lib/define-argument.js';
|
|
4
1
|
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 { DRAIN_TYPE_CLI_CODES, DRAIN_TYPES, resolveDrainResource } from '../../models/drain.js';
|
|
9
|
-
import { sendToApi } from '../../models/send-to-api.js';
|
|
10
|
-
import { addonIdOrRealIdOption, aliasOption, appIdOrNameOption } from '../global.options.js';
|
|
11
2
|
|
|
12
3
|
export const drainCreateCommand = defineCommand({
|
|
13
4
|
description: 'Create a drain',
|
|
14
5
|
since: '0.9.0',
|
|
15
|
-
options: {
|
|
16
|
-
username: defineOption({
|
|
17
|
-
name: 'username',
|
|
18
|
-
schema: z.string().optional(),
|
|
19
|
-
description: 'Basic auth username (for elasticsearch or raw-http)',
|
|
20
|
-
aliases: ['u'],
|
|
21
|
-
placeholder: 'username',
|
|
22
|
-
}),
|
|
23
|
-
password: defineOption({
|
|
24
|
-
name: 'password',
|
|
25
|
-
schema: z.string().optional(),
|
|
26
|
-
description: 'Basic auth password (for elasticsearch or raw-http)',
|
|
27
|
-
aliases: ['p'],
|
|
28
|
-
placeholder: 'password',
|
|
29
|
-
}),
|
|
30
|
-
apiKey: defineOption({
|
|
31
|
-
name: 'api-key',
|
|
32
|
-
schema: z.string().optional(),
|
|
33
|
-
description: 'API key (for newrelic)',
|
|
34
|
-
aliases: ['k'],
|
|
35
|
-
placeholder: 'api-key',
|
|
36
|
-
}),
|
|
37
|
-
indexPrefix: defineOption({
|
|
38
|
-
name: 'index-prefix',
|
|
39
|
-
schema: z.string().optional(),
|
|
40
|
-
description: 'Optional index prefix (for elasticsearch), `logstash` value is used if not set',
|
|
41
|
-
aliases: ['i'],
|
|
42
|
-
placeholder: 'index-prefix',
|
|
43
|
-
}),
|
|
44
|
-
rfc5424StructuredDataParameters: defineOption({
|
|
45
|
-
name: 'sd-params',
|
|
46
|
-
schema: z.string().optional(),
|
|
47
|
-
description: 'RFC5424 structured data parameters (for ovh-tcp), e.g.: `X-OVH-TOKEN=\\\"REDACTED\\\"`',
|
|
48
|
-
aliases: ['s'],
|
|
49
|
-
placeholder: 'sd-params',
|
|
50
|
-
}),
|
|
51
|
-
alias: aliasOption,
|
|
52
|
-
appIdOrName: appIdOrNameOption,
|
|
53
|
-
addonIdOrRealId: addonIdOrRealIdOption,
|
|
54
|
-
},
|
|
55
|
-
args: [
|
|
56
|
-
defineArgument({
|
|
57
|
-
schema: z.enum(DRAIN_TYPE_CLI_CODES),
|
|
58
|
-
description: 'Drain type',
|
|
59
|
-
placeholder: 'drain-type',
|
|
60
|
-
}),
|
|
61
|
-
defineArgument({
|
|
62
|
-
schema: z.string(),
|
|
63
|
-
description: 'Drain URL',
|
|
64
|
-
placeholder: 'drain-url',
|
|
65
|
-
}),
|
|
66
|
-
],
|
|
67
|
-
async handler(options, drainTypeCliCode, url) {
|
|
68
|
-
const { alias, appIdOrName, addonIdOrRealId } = options;
|
|
69
|
-
const { username, password, apiKey, indexPrefix, rfc5424StructuredDataParameters } = options;
|
|
70
|
-
|
|
71
|
-
const drainType = Object.values(DRAIN_TYPES).find((drainType) => drainType.cliCode === drainTypeCliCode);
|
|
72
|
-
|
|
73
|
-
const { ownerId, resourceId } = await resolveDrainResource(alias, appIdOrName, addonIdOrRealId);
|
|
74
|
-
|
|
75
|
-
const body = {
|
|
76
|
-
kind: 'LOG',
|
|
77
|
-
recipient: {
|
|
78
|
-
type: drainType.apiCode,
|
|
79
|
-
url,
|
|
80
|
-
},
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
if (drainTypeCliCode === DRAIN_TYPES.ELASTICSEARCH.cliCode) {
|
|
84
|
-
if (!indexPrefix) {
|
|
85
|
-
throw new Error(
|
|
86
|
-
`${DRAIN_TYPES.ELASTICSEARCH.cliCode} drains require an index prefix (--index-prefix) to be set`,
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
if (!url.endsWith('/_bulk')) {
|
|
90
|
-
throw new Error(`${DRAIN_TYPES.ELASTICSEARCH.cliCode} drain URL must end with '/_bulk'`);
|
|
91
|
-
}
|
|
92
|
-
body.recipient.index = indexPrefix;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
if (drainTypeCliCode === DRAIN_TYPES.ELASTICSEARCH.cliCode || drainTypeCliCode === DRAIN_TYPES.RAW_HTTP.cliCode) {
|
|
96
|
-
if (username) {
|
|
97
|
-
body.recipient.username = username;
|
|
98
|
-
}
|
|
99
|
-
if (password) {
|
|
100
|
-
body.recipient.password = password;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
if (drainTypeCliCode === DRAIN_TYPES.NEWRELIC.cliCode) {
|
|
105
|
-
if (!apiKey) {
|
|
106
|
-
throw new Error(`${DRAIN_TYPES.NEWRELIC.cliCode} drains require an API key (--api-key) to be set`);
|
|
107
|
-
}
|
|
108
|
-
body.recipient.apiKey = apiKey;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (
|
|
112
|
-
drainTypeCliCode === DRAIN_TYPES.OVH_TCP.cliCode ||
|
|
113
|
-
drainTypeCliCode === DRAIN_TYPES.SYSLOG_TCP.cliCode ||
|
|
114
|
-
drainTypeCliCode === DRAIN_TYPES.SYSLOG_UDP.cliCode
|
|
115
|
-
) {
|
|
116
|
-
if (rfc5424StructuredDataParameters) {
|
|
117
|
-
body.recipient.rfc5424StructuredDataParameters = rfc5424StructuredDataParameters;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
const drain = await createDrain({ ownerId, resourceId, body }).then(sendToApi);
|
|
122
|
-
Logger.printSuccess(`Drain ${styleText(['bold', 'green'], drain.id)} has been successfully created and enabled!`);
|
|
123
|
-
},
|
|
124
6
|
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { defineCommand } from '../../lib/define-command.js';
|
|
2
|
+
import { styleText } from '../../lib/style-text.js';
|
|
3
|
+
import { Logger } from '../../logger.js';
|
|
4
|
+
import { createLogDrain, resolveDrainResource } from '../../models/drain.js';
|
|
5
|
+
import { addonIdOrRealIdOption, aliasOption, appIdOrNameOption } from '../global.options.js';
|
|
6
|
+
import { drainUrlArg } from './drain.args.js';
|
|
7
|
+
|
|
8
|
+
export const drainCreateDatadogCommand = defineCommand({
|
|
9
|
+
description: 'Create a Datadog drain',
|
|
10
|
+
since: '0.9.0',
|
|
11
|
+
options: {
|
|
12
|
+
alias: aliasOption,
|
|
13
|
+
appIdOrName: appIdOrNameOption,
|
|
14
|
+
addonIdOrRealId: addonIdOrRealIdOption,
|
|
15
|
+
},
|
|
16
|
+
args: [drainUrlArg],
|
|
17
|
+
async handler(options, url) {
|
|
18
|
+
const { alias, appIdOrName, addonIdOrRealId } = options;
|
|
19
|
+
const { ownerId, resourceId } = await resolveDrainResource(alias, appIdOrName, addonIdOrRealId);
|
|
20
|
+
|
|
21
|
+
const drain = await createLogDrain('DATADOG', ownerId, resourceId, url);
|
|
22
|
+
|
|
23
|
+
Logger.printSuccess(
|
|
24
|
+
`Datadog drain ${styleText(['bold', 'green'], drain.id)} has been successfully created and enabled!`,
|
|
25
|
+
);
|
|
26
|
+
},
|
|
27
|
+
});
|