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
|
@@ -1,20 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getAllDeployments, getDeployment } from '@clevercloud/client/esm/api/v2/application.js';
|
|
2
|
+
import { setTimeout as delay } from 'node:timers/promises';
|
|
2
3
|
import { Logger } from '../logger.js';
|
|
3
|
-
import { getDeployment, getAllDeployments } from '@clevercloud/client/esm/api/v2/application.js';
|
|
4
4
|
import { sendToApi } from './send-to-api.js';
|
|
5
5
|
|
|
6
|
-
const delay = promisify(setTimeout);
|
|
7
|
-
|
|
8
6
|
const DEPLOYMENT_POLLING_DELAY = 5000;
|
|
9
7
|
const BACKOFF_FACTOR = 1.25;
|
|
10
8
|
const INIT_RETRY_TIMEOUT = 1500;
|
|
11
9
|
const MAX_RETRY_COUNT = 5;
|
|
12
10
|
|
|
13
|
-
export async function waitForDeploymentStart
|
|
14
|
-
|
|
11
|
+
export async function waitForDeploymentStart({ ownerId, appId, deploymentId, commitId, knownDeployments }) {
|
|
15
12
|
return waitFor(async () => {
|
|
16
13
|
try {
|
|
17
|
-
|
|
18
14
|
// In a deploy situation, we don't have the deployment ID so we get the latest deployments,
|
|
19
15
|
// then we match by commit ID and we filter out "known deployments" that existed before the deploy.
|
|
20
16
|
// In a restart situation, we have a deployment ID but fetching it too soon may result in an error so we get latest deployments,
|
|
@@ -35,15 +31,14 @@ export async function waitForDeploymentStart ({ ownerId, appId, deploymentId, co
|
|
|
35
31
|
return deployment;
|
|
36
32
|
}
|
|
37
33
|
Logger.debug('Deployment cannot be found yet');
|
|
38
|
-
}
|
|
39
|
-
catch (e) {
|
|
34
|
+
} catch (e) {
|
|
40
35
|
Logger.debug('Failed to retrieve deployment');
|
|
41
36
|
throw e;
|
|
42
37
|
}
|
|
43
38
|
});
|
|
44
39
|
}
|
|
45
40
|
|
|
46
|
-
export async function waitForDeploymentEnd
|
|
41
|
+
export async function waitForDeploymentEnd({ ownerId, appId, deploymentId }) {
|
|
47
42
|
return waitFor(async () => {
|
|
48
43
|
try {
|
|
49
44
|
const deployment = await getDeployment({ id: ownerId, appId, deploymentId }).then(sendToApi);
|
|
@@ -53,8 +48,7 @@ export async function waitForDeploymentEnd ({ ownerId, appId, deploymentId }) {
|
|
|
53
48
|
return deployment;
|
|
54
49
|
}
|
|
55
50
|
Logger.debug(`Deployment is not finished yet (state:${deployment.state})`);
|
|
56
|
-
}
|
|
57
|
-
catch (e) {
|
|
51
|
+
} catch (e) {
|
|
58
52
|
Logger.debug('Failed to retrieve current deployment status');
|
|
59
53
|
throw e;
|
|
60
54
|
}
|
|
@@ -65,14 +59,11 @@ export async function waitForDeploymentEnd ({ ownerId, appId, deploymentId }) {
|
|
|
65
59
|
// Return fetchResult's result if it's not null
|
|
66
60
|
// Retry with simple "infinite polling" if fetchResult succeeds and returns null
|
|
67
61
|
// Retry with exponential backoff if fetchResult fails
|
|
68
|
-
async function waitFor
|
|
69
|
-
|
|
62
|
+
async function waitFor(fetchResult) {
|
|
70
63
|
let failCount = 0;
|
|
71
64
|
|
|
72
65
|
while (true) {
|
|
73
|
-
|
|
74
66
|
try {
|
|
75
|
-
|
|
76
67
|
const result = await fetchResult();
|
|
77
68
|
if (result != null) {
|
|
78
69
|
return result;
|
|
@@ -83,8 +74,7 @@ async function waitFor (fetchResult) {
|
|
|
83
74
|
|
|
84
75
|
// Retry with simple polling when API calls succeed
|
|
85
76
|
await delay(DEPLOYMENT_POLLING_DELAY);
|
|
86
|
-
}
|
|
87
|
-
catch (e) {
|
|
77
|
+
} catch (e) {
|
|
88
78
|
// If only retry if it's a network error
|
|
89
79
|
if (e.code !== 'EAI_AGAIN') {
|
|
90
80
|
throw e;
|
|
@@ -97,7 +87,7 @@ async function waitFor (fetchResult) {
|
|
|
97
87
|
}
|
|
98
88
|
|
|
99
89
|
// If API call fails, retry with an exponential backoff
|
|
100
|
-
await delay(INIT_RETRY_TIMEOUT *
|
|
90
|
+
await delay(INIT_RETRY_TIMEOUT * BACKOFF_FACTOR ** failCount);
|
|
101
91
|
}
|
|
102
92
|
}
|
|
103
93
|
}
|
package/src/models/domain.js
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
|
+
import { getAllDomains, getFavouriteDomain } from '@clevercloud/client/esm/api/v2/application.js';
|
|
1
2
|
import _ from 'lodash';
|
|
2
|
-
|
|
3
3
|
import { Logger } from '../logger.js';
|
|
4
|
-
import {
|
|
5
|
-
import { sendToApi } from '../models/send-to-api.js';
|
|
4
|
+
import { sendToApi } from './send-to-api.js';
|
|
6
5
|
|
|
7
|
-
export async function getBest
|
|
6
|
+
export async function getBest(appId, orgaId) {
|
|
8
7
|
Logger.debug('Trying to get the favourite vhost for ' + appId);
|
|
9
|
-
return getFavouriteDomain({ id: orgaId, appId })
|
|
8
|
+
return getFavouriteDomain({ id: orgaId, appId })
|
|
9
|
+
.then(sendToApi)
|
|
10
10
|
.catch(async (e) => {
|
|
11
|
-
|
|
12
11
|
if (e.response.status !== 404) {
|
|
13
12
|
throw e;
|
|
14
13
|
}
|
|
@@ -18,19 +17,21 @@ export async function getBest (appId, orgaId) {
|
|
|
18
17
|
const result = selectBest(allDomains);
|
|
19
18
|
|
|
20
19
|
if (result == null) {
|
|
21
|
-
throw new Error(
|
|
20
|
+
throw new Error("Couldn't find a domain name");
|
|
22
21
|
}
|
|
23
22
|
|
|
24
23
|
return result;
|
|
25
24
|
});
|
|
26
25
|
}
|
|
27
26
|
|
|
28
|
-
export function selectBest
|
|
27
|
+
export function selectBest(vhosts) {
|
|
29
28
|
const customVhost = _.find(vhosts, (vhost) => {
|
|
30
29
|
return !vhost.fqdn.endsWith('.cleverapps.io');
|
|
31
30
|
});
|
|
32
31
|
const withoutDefaultDomain = _.find(vhosts, (vhost) => {
|
|
33
|
-
return !vhost.fqdn.match(
|
|
32
|
+
return !vhost.fqdn.match(
|
|
33
|
+
/^app-[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\.cleverapps\.io$/,
|
|
34
|
+
);
|
|
34
35
|
});
|
|
35
36
|
return customVhost || withoutDefaultDomain || vhosts[0];
|
|
36
37
|
}
|
package/src/models/drain.js
CHANGED
|
@@ -9,10 +9,11 @@ const DRAIN_TYPES = [
|
|
|
9
9
|
{ id: 'NewRelicHTTP', apiKey: 'MANDATORY' },
|
|
10
10
|
];
|
|
11
11
|
|
|
12
|
-
export function createDrainBody
|
|
13
|
-
|
|
12
|
+
export function createDrainBody(appId, drainTargetURL, drainTargetType, drainTargetCredentials, drainTargetConfig) {
|
|
14
13
|
if (!authorizeDrainCreation(drainTargetType, drainTargetCredentials, drainTargetConfig)) {
|
|
15
|
-
throw new Error(
|
|
14
|
+
throw new Error(
|
|
15
|
+
"Credentials are: optional for HTTP, mandatory for ElasticSearch, NewRelicHTTP and TCPSyslog/UDPSyslog don't need them.",
|
|
16
|
+
);
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
const body = {
|
|
@@ -37,7 +38,7 @@ export function createDrainBody (appId, drainTargetURL, drainTargetType, drainTa
|
|
|
37
38
|
return body;
|
|
38
39
|
}
|
|
39
40
|
|
|
40
|
-
export function authorizeDrainCreation
|
|
41
|
+
export function authorizeDrainCreation(drainTargetType, drainTargetCredentials, drainTargetConfig) {
|
|
41
42
|
if (drainTypeExists(drainTargetType)) {
|
|
42
43
|
// retrieve field for drain type ('mandatory', 'optional', undefined)
|
|
43
44
|
const credStatus = fieldStatus(drainTargetType).credentials;
|
|
@@ -87,46 +88,46 @@ export function authorizeDrainCreation (drainTargetType, drainTargetCredentials,
|
|
|
87
88
|
}
|
|
88
89
|
}
|
|
89
90
|
|
|
90
|
-
function fieldStatus
|
|
91
|
+
function fieldStatus(drainTargetType) {
|
|
91
92
|
return DRAIN_TYPES.find(({ id }) => id === drainTargetType);
|
|
92
93
|
}
|
|
93
94
|
|
|
94
|
-
function drainTypeExists
|
|
95
|
+
function drainTypeExists(drainTargetType) {
|
|
95
96
|
return DRAIN_TYPES.some(({ id }) => id === drainTargetType);
|
|
96
97
|
}
|
|
97
98
|
|
|
98
|
-
function credentialsExist
|
|
99
|
+
function credentialsExist({ username, password }) {
|
|
99
100
|
return username != null && password != null;
|
|
100
101
|
}
|
|
101
102
|
|
|
102
|
-
function credentialsEmpty
|
|
103
|
+
function credentialsEmpty({ username, password }) {
|
|
103
104
|
return username == null && password == null;
|
|
104
105
|
}
|
|
105
106
|
|
|
106
|
-
function indexPrefixExist
|
|
107
|
+
function indexPrefixExist({ indexPrefix }) {
|
|
107
108
|
return indexPrefix != null;
|
|
108
109
|
}
|
|
109
110
|
|
|
110
|
-
function indexPrefixEmpty
|
|
111
|
+
function indexPrefixEmpty({ indexPrefix }) {
|
|
111
112
|
return indexPrefix == null;
|
|
112
113
|
}
|
|
113
114
|
|
|
114
|
-
function structuredDataParametersExist
|
|
115
|
+
function structuredDataParametersExist({ structuredDataParameters }) {
|
|
115
116
|
return structuredDataParameters != null;
|
|
116
117
|
}
|
|
117
118
|
|
|
118
|
-
function structuredDataParametersEmpty
|
|
119
|
+
function structuredDataParametersEmpty({ structuredDataParameters }) {
|
|
119
120
|
return structuredDataParameters == null;
|
|
120
121
|
}
|
|
121
122
|
|
|
122
|
-
function keyExist
|
|
123
|
+
function keyExist({ apiKey }) {
|
|
123
124
|
return apiKey != null;
|
|
124
125
|
}
|
|
125
126
|
|
|
126
|
-
function keyEmpty
|
|
127
|
+
function keyEmpty({ apiKey }) {
|
|
127
128
|
return apiKey == null;
|
|
128
129
|
}
|
|
129
130
|
|
|
130
|
-
export function listDrainTypes
|
|
131
|
+
export function listDrainTypes() {
|
|
131
132
|
return cliparse.autocomplete.words(DRAIN_TYPES.map((type) => type.id));
|
|
132
133
|
}
|
|
@@ -1,20 +1,30 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { styleText } from 'node:util';
|
|
2
2
|
import { Logger } from '../logger.js';
|
|
3
3
|
|
|
4
|
-
export function get
|
|
4
|
+
export function get(follow, exitOnDeploy) {
|
|
5
5
|
if (follow) {
|
|
6
6
|
if (exitOnDeploy === 'deploy-start') {
|
|
7
7
|
throw new Error('The `follow` and `exit-on` set to "deploy-start" options are not compatible');
|
|
8
8
|
}
|
|
9
|
-
Logger.println(
|
|
9
|
+
Logger.println(
|
|
10
|
+
styleText(
|
|
11
|
+
'yellow',
|
|
12
|
+
'The `follow` option is deprecated and will be removed in an upcoming major, use --exit-on set to "never" instead',
|
|
13
|
+
),
|
|
14
|
+
);
|
|
10
15
|
return 'never';
|
|
11
16
|
}
|
|
12
17
|
return exitOnDeploy;
|
|
13
18
|
}
|
|
14
19
|
|
|
15
20
|
// plotQuietWarning: If in quiet mode and exitStrategy set to never plot a warning to indicate that the command will end
|
|
16
|
-
export function plotQuietWarning
|
|
21
|
+
export function plotQuietWarning(exitStrategy, quiet) {
|
|
17
22
|
if (exitStrategy === 'never' && quiet) {
|
|
18
|
-
Logger.println(
|
|
23
|
+
Logger.println(
|
|
24
|
+
styleText(
|
|
25
|
+
['bold', 'yellow'],
|
|
26
|
+
'The "never" exit-on strategy is not compatible with the "quiet" mode, it will exit once the deployment ends',
|
|
27
|
+
),
|
|
28
|
+
);
|
|
19
29
|
}
|
|
20
30
|
}
|
package/src/models/fs-utils.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { join } from 'node:path';
|
|
2
1
|
import { promises as fs } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
3
|
|
|
4
|
-
export function findPath
|
|
4
|
+
export function findPath(dir, name) {
|
|
5
5
|
const fullPath = join(dir, name);
|
|
6
|
-
return fs
|
|
6
|
+
return fs
|
|
7
|
+
.stat(fullPath)
|
|
7
8
|
.then(() => dir)
|
|
8
9
|
.catch((e) => {
|
|
9
10
|
if (e.code === 'ENOENT' && dir !== '/') {
|
package/src/models/git.js
CHANGED
|
@@ -1,25 +1,23 @@
|
|
|
1
|
+
import cliparse from 'cliparse';
|
|
2
|
+
import * as git from 'isomorphic-git';
|
|
3
|
+
import _ from 'lodash';
|
|
1
4
|
import fs from 'node:fs';
|
|
2
5
|
import path from 'node:path';
|
|
3
|
-
|
|
4
|
-
import _ from 'lodash';
|
|
5
|
-
import git from 'isomorphic-git';
|
|
6
|
-
import * as http from './isomorphic-http-with-agent.js';
|
|
7
|
-
import cliparse from 'cliparse';
|
|
8
6
|
import { slugify } from '../lib/slugify.js';
|
|
9
|
-
import { findPath } from './fs-utils.js';
|
|
10
7
|
import { loadOAuthConf } from './configuration.js';
|
|
8
|
+
import { findPath } from './fs-utils.js';
|
|
9
|
+
import * as http from './isomorphic-http-with-agent.js';
|
|
11
10
|
|
|
12
|
-
async function getRepo
|
|
11
|
+
async function getRepo() {
|
|
13
12
|
try {
|
|
14
13
|
const dir = await findPath('.', '.git');
|
|
15
14
|
return { fs, dir, http };
|
|
16
|
-
}
|
|
17
|
-
catch (e) {
|
|
15
|
+
} catch {
|
|
18
16
|
throw new Error('Could not find the .git folder.');
|
|
19
17
|
}
|
|
20
18
|
}
|
|
21
19
|
|
|
22
|
-
async function onAuth
|
|
20
|
+
async function onAuth() {
|
|
23
21
|
const tokens = await loadOAuthConf();
|
|
24
22
|
return {
|
|
25
23
|
username: tokens.token,
|
|
@@ -27,7 +25,7 @@ async function onAuth () {
|
|
|
27
25
|
};
|
|
28
26
|
}
|
|
29
27
|
|
|
30
|
-
export async function addRemote
|
|
28
|
+
export async function addRemote(remoteName, url) {
|
|
31
29
|
const repo = await getRepo();
|
|
32
30
|
const safeRemoteName = slugify(remoteName);
|
|
33
31
|
const allRemotes = await git.listRemotes({ ...repo });
|
|
@@ -38,15 +36,14 @@ export async function addRemote (remoteName, url) {
|
|
|
38
36
|
}
|
|
39
37
|
}
|
|
40
38
|
|
|
41
|
-
export async function resolveFullCommitId
|
|
39
|
+
export async function resolveFullCommitId(commitId) {
|
|
42
40
|
if (commitId == null) {
|
|
43
41
|
return null;
|
|
44
42
|
}
|
|
45
43
|
try {
|
|
46
44
|
const repo = await getRepo();
|
|
47
45
|
return await git.expandOid({ ...repo, oid: commitId });
|
|
48
|
-
}
|
|
49
|
-
catch (e) {
|
|
46
|
+
} catch (e) {
|
|
50
47
|
if (e.code === 'ShortOidNotFound') {
|
|
51
48
|
throw new Error(`Commit id ${commitId} is ambiguous`);
|
|
52
49
|
}
|
|
@@ -54,7 +51,7 @@ export async function resolveFullCommitId (commitId) {
|
|
|
54
51
|
}
|
|
55
52
|
}
|
|
56
53
|
|
|
57
|
-
export async function getRemoteCommit
|
|
54
|
+
export async function getRemoteCommit(remoteUrl) {
|
|
58
55
|
const repo = await getRepo();
|
|
59
56
|
const remoteInfos = await git.getRemoteInfo({
|
|
60
57
|
...repo,
|
|
@@ -64,16 +61,16 @@ export async function getRemoteCommit (remoteUrl) {
|
|
|
64
61
|
return _.get(remoteInfos, 'refs.heads.master');
|
|
65
62
|
}
|
|
66
63
|
|
|
67
|
-
export async function getFullBranch
|
|
64
|
+
export async function getFullBranch(branchName) {
|
|
68
65
|
const repo = await getRepo();
|
|
69
66
|
if (branchName === '') {
|
|
70
67
|
const currentBranch = await git.currentBranch({ ...repo, fullname: true });
|
|
71
68
|
return currentBranch || 'HEAD';
|
|
72
69
|
}
|
|
73
70
|
return git.expandRef({ ...repo, ref: branchName });
|
|
74
|
-
}
|
|
71
|
+
}
|
|
75
72
|
|
|
76
|
-
export async function getBranchCommit
|
|
73
|
+
export async function getBranchCommit(refspec) {
|
|
77
74
|
const repo = await getRepo();
|
|
78
75
|
const oid = await git.resolveRef({ ...repo, ref: refspec });
|
|
79
76
|
// When a refspec refers to an annotated tag, the OID ref represents the annotation and not the commit directly,
|
|
@@ -82,7 +79,7 @@ export async function getBranchCommit (refspec) {
|
|
|
82
79
|
return res.oid;
|
|
83
80
|
}
|
|
84
81
|
|
|
85
|
-
export async function isExistingTag
|
|
82
|
+
export async function isExistingTag(tag) {
|
|
86
83
|
const repo = await getRepo();
|
|
87
84
|
const tags = await git.listTags({
|
|
88
85
|
...repo,
|
|
@@ -90,7 +87,7 @@ export async function isExistingTag (tag) {
|
|
|
90
87
|
return tags.includes(tag);
|
|
91
88
|
}
|
|
92
89
|
|
|
93
|
-
export async function push
|
|
90
|
+
export async function push(remoteUrl, branchRefspec, force) {
|
|
94
91
|
const repo = await getRepo();
|
|
95
92
|
try {
|
|
96
93
|
const push = await git.push({
|
|
@@ -105,8 +102,7 @@ export async function push (remoteUrl, branchRefspec, force) {
|
|
|
105
102
|
throw new Error(push.errors.join(', '));
|
|
106
103
|
}
|
|
107
104
|
return push;
|
|
108
|
-
}
|
|
109
|
-
catch (e) {
|
|
105
|
+
} catch (e) {
|
|
110
106
|
if (e.code === 'PushRejectedNonFastForward') {
|
|
111
107
|
throw new Error('Push rejected because it was not a simple fast-forward. Use "--force" to override.');
|
|
112
108
|
}
|
|
@@ -114,19 +110,18 @@ export async function push (remoteUrl, branchRefspec, force) {
|
|
|
114
110
|
}
|
|
115
111
|
}
|
|
116
112
|
|
|
117
|
-
export function completeBranches
|
|
113
|
+
export function completeBranches() {
|
|
118
114
|
return getRepo()
|
|
119
115
|
.then((repo) => git.listBranches(repo))
|
|
120
116
|
.then(cliparse.autocomplete.words);
|
|
121
117
|
}
|
|
122
118
|
|
|
123
|
-
export async function isShallow
|
|
119
|
+
export async function isShallow() {
|
|
124
120
|
const { dir } = await getRepo();
|
|
125
121
|
try {
|
|
126
122
|
await fs.promises.access(path.join(dir, '.git', 'shallow'));
|
|
127
123
|
return true;
|
|
128
|
-
}
|
|
129
|
-
catch (e) {
|
|
124
|
+
} catch {
|
|
130
125
|
return false;
|
|
131
126
|
}
|
|
132
127
|
}
|
|
@@ -135,7 +130,7 @@ export async function isShallow () {
|
|
|
135
130
|
* Check if the current directory is a git repository
|
|
136
131
|
* @returns {Promise<boolean>}
|
|
137
132
|
*/
|
|
138
|
-
export async function isInsideGitRepo
|
|
133
|
+
export async function isInsideGitRepo() {
|
|
139
134
|
return getRepo()
|
|
140
135
|
.then(() => true)
|
|
141
136
|
.catch(() => false);
|
|
@@ -145,16 +140,15 @@ export async function isInsideGitRepo () {
|
|
|
145
140
|
* Check if the current git working directory is clean
|
|
146
141
|
* @returns {Promise<boolean>}
|
|
147
142
|
*/
|
|
148
|
-
export async function isGitWorkingDirectoryClean
|
|
143
|
+
export async function isGitWorkingDirectoryClean() {
|
|
149
144
|
const repo = await getRepo();
|
|
150
145
|
const status = await git.statusMatrix({ ...repo });
|
|
151
|
-
const isStatusEmpty =
|
|
152
|
-
.filter(([filepath, head, workdir]) => {
|
|
146
|
+
const isStatusEmpty =
|
|
147
|
+
status.filter(([filepath, head, workdir]) => {
|
|
153
148
|
// WARNING: isomorphic-git does not support global gitignore so we filter hidden files and dirs to reduce the amount of false positives
|
|
154
149
|
const isHidden = filepath.startsWith('.');
|
|
155
150
|
const isCleverJson = filepath === '.clever.json';
|
|
156
151
|
return (!isHidden || isCleverJson) && head !== workdir;
|
|
157
|
-
})
|
|
158
|
-
.length === 0;
|
|
152
|
+
}).length === 0;
|
|
159
153
|
return isStatusEmpty;
|
|
160
154
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Logger } from '../logger.js';
|
|
2
|
-
import { sendToApi } from './send-to-api.js';
|
|
3
1
|
import { getSummary } from '@clevercloud/client/esm/api/v2/user.js';
|
|
2
|
+
import { Logger } from '../logger.js';
|
|
4
3
|
import { loadIdsCache, writeIdsCache } from './configuration.js';
|
|
4
|
+
import { sendToApi } from './send-to-api.js';
|
|
5
5
|
|
|
6
6
|
/*
|
|
7
7
|
This system uses a simplified representation of the summary to expose IDs links:
|
|
@@ -25,14 +25,13 @@ This system uses a simplified representation of the summary to expose IDs links:
|
|
|
25
25
|
}
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
|
-
export async function resolveOwnerId
|
|
28
|
+
export async function resolveOwnerId(id) {
|
|
29
29
|
return getIdFromCacheOrSummary((ids) => ids.owners[id]);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
export async function resolveAddonId
|
|
33
|
-
|
|
32
|
+
export async function resolveAddonId(id) {
|
|
34
33
|
const addonId = await getIdFromCacheOrSummary((ids) => {
|
|
35
|
-
return
|
|
34
|
+
return ids.addons[id] != null ? ids.addons[id].addonId : null;
|
|
36
35
|
});
|
|
37
36
|
|
|
38
37
|
if (addonId != null) {
|
|
@@ -42,10 +41,9 @@ export async function resolveAddonId (id) {
|
|
|
42
41
|
throw new Error(`Add-on ${id} does not exist`);
|
|
43
42
|
}
|
|
44
43
|
|
|
45
|
-
export async function resolveRealId
|
|
46
|
-
|
|
44
|
+
export async function resolveRealId(id) {
|
|
47
45
|
const realId = await getIdFromCacheOrSummary((ids) => {
|
|
48
|
-
return
|
|
46
|
+
return ids.addons[id] != null ? ids.addons[id].realId : null;
|
|
49
47
|
});
|
|
50
48
|
|
|
51
49
|
if (realId != null) {
|
|
@@ -55,8 +53,7 @@ export async function resolveRealId (id) {
|
|
|
55
53
|
throw new Error(`Add-on ${id} does not exist foo`);
|
|
56
54
|
}
|
|
57
55
|
|
|
58
|
-
async function getIdFromCacheOrSummary
|
|
59
|
-
|
|
56
|
+
async function getIdFromCacheOrSummary(callback) {
|
|
60
57
|
const idsFromCache = await loadIdsCache();
|
|
61
58
|
const idFromCache = callback(idsFromCache);
|
|
62
59
|
if (idFromCache != null) {
|
|
@@ -74,8 +71,7 @@ async function getIdFromCacheOrSummary (callback) {
|
|
|
74
71
|
return null;
|
|
75
72
|
}
|
|
76
73
|
|
|
77
|
-
async function getIdsFromSummary
|
|
78
|
-
|
|
74
|
+
async function getIdsFromSummary() {
|
|
79
75
|
const ids = {
|
|
80
76
|
owners: {},
|
|
81
77
|
addons: {},
|
|
@@ -83,10 +79,7 @@ async function getIdsFromSummary () {
|
|
|
83
79
|
|
|
84
80
|
const summary = await getSummary().then(sendToApi);
|
|
85
81
|
|
|
86
|
-
const owners = [
|
|
87
|
-
summary.user,
|
|
88
|
-
...summary.organisations,
|
|
89
|
-
];
|
|
82
|
+
const owners = [summary.user, ...summary.organisations];
|
|
90
83
|
|
|
91
84
|
for (const owner of owners) {
|
|
92
85
|
for (const app of owner.applications) {
|
|
@@ -112,19 +105,21 @@ async function getIdsFromSummary () {
|
|
|
112
105
|
* @throws {Error} if several add-ons are found
|
|
113
106
|
* @returns {Object} The name, IDs and owner ID of the add-on { name, addonId, realId, ownerId }
|
|
114
107
|
*/
|
|
115
|
-
export async function findAddonsByNameOrId
|
|
108
|
+
export async function findAddonsByNameOrId(addonIdOrRealIdOrName, ownerNameOrId) {
|
|
116
109
|
const summary = await getSummary().then(sendToApi);
|
|
117
110
|
|
|
118
|
-
Logger.debug(
|
|
111
|
+
Logger.debug(
|
|
112
|
+
`Searching for add-on '${addonIdOrRealIdOrName}' in ${summary.user.id} and ${summary.organisations.map((org) => org.id).join(', ')}`,
|
|
113
|
+
);
|
|
119
114
|
const candidates = [summary.user, ...summary.organisations]
|
|
120
115
|
.flatMap((owner) => owner.addons.map((addon) => ({ addon, owner })))
|
|
121
116
|
.filter(({ addon, owner }) => {
|
|
122
|
-
const matchOwner =
|
|
123
|
-
|| owner.id === ownerNameOrId.orga_id
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
117
|
+
const matchOwner =
|
|
118
|
+
ownerNameOrId == null || owner.id === ownerNameOrId.orga_id || owner.name === ownerNameOrId.orga_name;
|
|
119
|
+
const matchAddon =
|
|
120
|
+
addon.name === addonIdOrRealIdOrName ||
|
|
121
|
+
addon.realId === addonIdOrRealIdOrName ||
|
|
122
|
+
addon.id === addonIdOrRealIdOrName;
|
|
128
123
|
return matchOwner && matchAddon;
|
|
129
124
|
})
|
|
130
125
|
.map(({ addon, owner }) => ({
|
|
@@ -149,24 +144,25 @@ export async function findAddonsByNameOrId (addonIdOrRealIdOrName, ownerNameOrId
|
|
|
149
144
|
* @throws {Error} if several add-ons are found
|
|
150
145
|
* @returns {Object} The name, IDs and owner ID of the add-on { name, addonId, realId, ownerId }
|
|
151
146
|
*/
|
|
152
|
-
export async function findAddonsByAddonProvider
|
|
147
|
+
export async function findAddonsByAddonProvider(provider) {
|
|
153
148
|
const summary = await getSummary().then(sendToApi);
|
|
154
149
|
|
|
155
|
-
Logger.debug(
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
169
|
-
|
|
150
|
+
Logger.debug(
|
|
151
|
+
`Searching for ${provider} add-ons in ${summary.user.id} and ${summary.organisations.map((org) => org.id).join(', ')}`,
|
|
152
|
+
);
|
|
153
|
+
const candidates = [summary.user, ...summary.organisations].flatMap((owner) => {
|
|
154
|
+
return owner.addons
|
|
155
|
+
.filter((addon) => addon.providerId === provider)
|
|
156
|
+
.map((addon) => {
|
|
157
|
+
return {
|
|
158
|
+
name: addon.name,
|
|
159
|
+
addonId: addon.id,
|
|
160
|
+
realId: addon.realId,
|
|
161
|
+
ownerId: owner.id,
|
|
162
|
+
ownerName: owner.name,
|
|
163
|
+
};
|
|
164
|
+
});
|
|
165
|
+
});
|
|
170
166
|
|
|
171
167
|
Logger.debug(`Found ${candidates.length} candidate(s) for provider ${provider}:`);
|
|
172
168
|
for (const candidate of candidates) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as git from 'isomorphic-git/http/node';
|
|
2
2
|
import https from 'node:https';
|
|
3
3
|
|
|
4
4
|
// We use our own HTTP plugin, so we can customize the agent used for requests and configure a long timeout (default is 5 seconds).
|
|
@@ -8,6 +8,6 @@ const agent = new https.Agent({
|
|
|
8
8
|
timeout: 10 * 60 * 1000,
|
|
9
9
|
});
|
|
10
10
|
|
|
11
|
-
export function request
|
|
12
|
-
return
|
|
11
|
+
export function request(params) {
|
|
12
|
+
return git.request({ ...params, agent });
|
|
13
13
|
}
|
package/src/models/json-array.js
CHANGED
|
@@ -2,25 +2,24 @@
|
|
|
2
2
|
* Helper to print a real JSON array with starting `[` and ending `]`
|
|
3
3
|
*/
|
|
4
4
|
export class JsonArray {
|
|
5
|
-
constructor
|
|
5
|
+
constructor() {
|
|
6
6
|
this._isFirst = true;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
open
|
|
9
|
+
open() {
|
|
10
10
|
process.stdout.write('[\n');
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
push
|
|
13
|
+
push(log) {
|
|
14
14
|
if (this._isFirst) {
|
|
15
15
|
this._isFirst = false;
|
|
16
|
-
}
|
|
17
|
-
else {
|
|
16
|
+
} else {
|
|
18
17
|
process.stdout.write(',\n');
|
|
19
18
|
}
|
|
20
19
|
process.stdout.write(` ${JSON.stringify(log)}`);
|
|
21
20
|
}
|
|
22
21
|
|
|
23
|
-
close
|
|
22
|
+
close() {
|
|
24
23
|
process.stdout.write('\n]');
|
|
25
24
|
}
|
|
26
25
|
}
|