clever-tools 4.5.2 → 4.6.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 +10 -0
- package/bin/clever.js +25 -22
- package/package.json +2 -2
- package/src/clever-client/auth-bridge.js +0 -25
- package/src/commands/README.md +1 -0
- package/src/commands/accesslogs/accesslogs.command.js +4 -1
- package/src/commands/accesslogs/accesslogs.docs.md +1 -1
- package/src/commands/addon/addon.create.command.js +23 -20
- package/src/commands/config/config.set.command.js +3 -3
- package/src/commands/config-provider/config-provider.args.js +8 -0
- package/src/commands/config-provider/config-provider.command.js +10 -0
- package/src/commands/config-provider/config-provider.docs.md +109 -0
- package/src/commands/config-provider/config-provider.get.command.js +36 -0
- package/src/commands/config-provider/config-provider.import.command.js +40 -0
- package/src/commands/config-provider/config-provider.list.command.js +45 -0
- package/src/commands/config-provider/config-provider.open.command.js +17 -0
- package/src/commands/config-provider/config-provider.rm.command.js +25 -0
- package/src/commands/config-provider/config-provider.set.command.js +37 -0
- package/src/commands/create/create.command.js +7 -6
- package/src/commands/curl/curl.command.js +18 -19
- package/src/commands/deploy/deploy.command.js +14 -8
- package/src/commands/deploy/deploy.docs.md +20 -0
- package/src/commands/diag/diag.command.js +52 -39
- package/src/commands/features/features.disable.command.js +1 -2
- package/src/commands/features/features.enable.command.js +1 -2
- package/src/commands/features/features.info.command.js +1 -1
- package/src/commands/features/features.list.command.js +1 -2
- package/src/commands/global.commands.js +22 -0
- package/src/commands/global.options.js +1 -1
- package/src/commands/login/login.command.js +119 -20
- package/src/commands/login/login.docs.md +9 -2
- package/src/commands/logout/logout.command.js +39 -7
- package/src/commands/logout/logout.docs.md +7 -1
- package/src/commands/logs/logs.command.js +7 -8
- package/src/commands/logs/logs.docs.md +1 -1
- package/src/commands/ng/ng.get-config.command.js +3 -3
- package/src/commands/profile/profile.command.js +13 -37
- package/src/commands/profile/profile.docs.md +28 -0
- package/src/commands/profile/profile.list.command.js +44 -0
- package/src/commands/profile/profile.switch.command.js +80 -0
- package/src/commands/restart/restart.command.js +3 -2
- package/src/commands/ssh/ssh.command.js +2 -2
- package/src/commands/tokens/tokens.command.js +3 -3
- package/src/commands/tokens/tokens.create.command.js +4 -4
- package/src/config/cache.js +60 -0
- package/src/config/config.js +233 -0
- package/src/config/features.js +167 -0
- package/src/config/paths.js +23 -0
- package/src/lib/date-utils.js +48 -0
- package/src/lib/fs.js +49 -0
- package/src/lib/operator-commands.js +4 -4
- package/src/lib/profile.js +116 -0
- package/src/logger.js +123 -69
- package/src/logger.types.d.ts +5 -0
- package/src/models/app_configuration.js +42 -39
- package/src/models/application_configuration.js +30 -30
- package/src/models/config-provider.js +34 -0
- package/src/models/git-isomorphic.js +153 -0
- package/src/models/git-system.js +182 -0
- package/src/models/git.js +150 -128
- package/src/models/ids-resolver.js +1 -1
- package/src/models/log.js +141 -90
- package/src/models/send-to-api.js +58 -33
- package/src/models/user.js +0 -11
- package/src/models/utils.js +2 -2
- package/src/experimental-features.js +0 -91
- package/src/lib/format-date.js +0 -3
- package/src/models/configuration.js +0 -140
- package/src/models/log-v4.js +0 -189
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import * as git from 'isomorphic-git';
|
|
2
|
+
import _ from 'lodash';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import { config } from '../config/config.js';
|
|
5
|
+
import { slugify } from '../lib/slugify.js';
|
|
6
|
+
import { Git } from './git.js';
|
|
7
|
+
import * as http from './isomorphic-http-with-agent.js';
|
|
8
|
+
|
|
9
|
+
export class GitIsomorphic extends Git {
|
|
10
|
+
constructor() {
|
|
11
|
+
super('isomorphic');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async #getRepo() {
|
|
15
|
+
const dir = await this._getRepoDir();
|
|
16
|
+
return { fs, dir, http };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
#onAuth() {
|
|
20
|
+
return {
|
|
21
|
+
username: config.token,
|
|
22
|
+
password: config.secret,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async addRemote(remoteName, url) {
|
|
27
|
+
this._debug('addRemote', remoteName, url);
|
|
28
|
+
const repo = await this.#getRepo();
|
|
29
|
+
const safeRemoteName = slugify(remoteName);
|
|
30
|
+
const allRemotes = await git.listRemotes({ ...repo });
|
|
31
|
+
const existingRemote = _.find(allRemotes, { remote: safeRemoteName });
|
|
32
|
+
if (existingRemote == null) {
|
|
33
|
+
// In some situations, we may end up with race conditions so we force it
|
|
34
|
+
return git.addRemote({ ...repo, remote: safeRemoteName, url, force: true });
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async resolveFullCommitId(commitId) {
|
|
39
|
+
this._debug('resolveFullCommitId', commitId);
|
|
40
|
+
if (commitId == null) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
const repo = await this.#getRepo();
|
|
45
|
+
return await git.expandOid({ ...repo, oid: commitId });
|
|
46
|
+
} catch (e) {
|
|
47
|
+
if (e.code === 'ShortOidNotFound') {
|
|
48
|
+
throw new Error(`Commit id ${commitId} is ambiguous`);
|
|
49
|
+
}
|
|
50
|
+
throw e;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async getRemoteCommit(remoteUrl) {
|
|
55
|
+
this._debug('getRemoteCommit', remoteUrl);
|
|
56
|
+
const repo = await this.#getRepo();
|
|
57
|
+
const remoteInfos = await git.getRemoteInfo({
|
|
58
|
+
...repo,
|
|
59
|
+
onAuth: this.#onAuth,
|
|
60
|
+
url: remoteUrl,
|
|
61
|
+
});
|
|
62
|
+
return _.get(remoteInfos, 'refs.heads.master');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async getFullBranch(branchName) {
|
|
66
|
+
this._debug('getFullBranch', branchName);
|
|
67
|
+
const repo = await this.#getRepo();
|
|
68
|
+
if (branchName === '') {
|
|
69
|
+
const currentBranch = await git.currentBranch({ ...repo, fullname: true });
|
|
70
|
+
return currentBranch || 'HEAD';
|
|
71
|
+
}
|
|
72
|
+
return git.expandRef({ ...repo, ref: branchName });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async getBranchCommit(refspec) {
|
|
76
|
+
this._debug('getBranchCommit', refspec);
|
|
77
|
+
const repo = await this.#getRepo();
|
|
78
|
+
const oid = await git.resolveRef({ ...repo, ref: refspec });
|
|
79
|
+
// When a refspec refers to an annotated tag, the OID ref represents the annotation and not the commit directly,
|
|
80
|
+
// that's why we need a call to `readCommit`.
|
|
81
|
+
const res = await git.readCommit({ ...repo, ref: refspec, oid });
|
|
82
|
+
return res.oid;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async isExistingTag(tag) {
|
|
86
|
+
this._debug('isExistingTag', tag);
|
|
87
|
+
const repo = await this.#getRepo();
|
|
88
|
+
const tags = await git.listTags({
|
|
89
|
+
...repo,
|
|
90
|
+
});
|
|
91
|
+
return tags.includes(tag);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async push(remoteUrl, branchRefspec, force, remoteName) {
|
|
95
|
+
const refspec = `${branchRefspec}:refs/heads/master`;
|
|
96
|
+
this._debug('push', remoteUrl, refspec, force ? '--force' : '');
|
|
97
|
+
const repo = await this.#getRepo();
|
|
98
|
+
try {
|
|
99
|
+
const push = await git.push({
|
|
100
|
+
...repo,
|
|
101
|
+
onAuth: this.#onAuth,
|
|
102
|
+
url: remoteUrl,
|
|
103
|
+
ref: branchRefspec,
|
|
104
|
+
remoteRef: 'master',
|
|
105
|
+
remote: remoteName,
|
|
106
|
+
force,
|
|
107
|
+
});
|
|
108
|
+
if (push.errors != null) {
|
|
109
|
+
throw new Error(push.errors.join(', '));
|
|
110
|
+
}
|
|
111
|
+
return push;
|
|
112
|
+
} catch (e) {
|
|
113
|
+
if (e.code === 'PushRejectedNonFastForward') {
|
|
114
|
+
throw new Error('Push rejected because it was not a simple fast-forward, use --force to override');
|
|
115
|
+
}
|
|
116
|
+
throw e;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async completeBranches() {
|
|
121
|
+
this._debug('completeBranches');
|
|
122
|
+
return this.#getRepo().then((repo) => git.listBranches(repo));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Check if the current directory is a git repository
|
|
127
|
+
* @returns {Promise<boolean>}
|
|
128
|
+
*/
|
|
129
|
+
async isInsideGitRepo() {
|
|
130
|
+
this._debug('isInsideGitRepo');
|
|
131
|
+
return this.#getRepo()
|
|
132
|
+
.then(() => true)
|
|
133
|
+
.catch(() => false);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Check if the current git working directory is clean
|
|
138
|
+
* @returns {Promise<boolean>}
|
|
139
|
+
*/
|
|
140
|
+
async isGitWorkingDirectoryClean() {
|
|
141
|
+
this._debug('isGitWorkingDirectoryClean');
|
|
142
|
+
const repo = await this.#getRepo();
|
|
143
|
+
const status = await git.statusMatrix({ ...repo });
|
|
144
|
+
const isStatusEmpty =
|
|
145
|
+
status.filter(([filepath, head, workdir]) => {
|
|
146
|
+
// WARNING: isomorphic-git does not support global gitignore so we filter hidden files and dirs to reduce the amount of false positives
|
|
147
|
+
const isHidden = filepath.startsWith('.');
|
|
148
|
+
const isCleverJson = filepath === '.clever.json';
|
|
149
|
+
return (!isHidden || isCleverJson) && head !== workdir;
|
|
150
|
+
}).length === 0;
|
|
151
|
+
return isStatusEmpty;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { simpleGit } from 'simple-git';
|
|
2
|
+
import { config } from '../config/config.js';
|
|
3
|
+
import { slugify } from '../lib/slugify.js';
|
|
4
|
+
import { Git } from './git.js';
|
|
5
|
+
|
|
6
|
+
export class GitSystem extends Git {
|
|
7
|
+
#gitAvailabilityChecked = false;
|
|
8
|
+
|
|
9
|
+
constructor() {
|
|
10
|
+
super('system');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async addRemote(remoteName, url) {
|
|
14
|
+
this._debug('addRemote', remoteName, url);
|
|
15
|
+
const git = await this.#getSimpleGit();
|
|
16
|
+
const safeRemoteName = slugify(remoteName);
|
|
17
|
+
const remotes = await git.getRemotes();
|
|
18
|
+
const existingRemote = remotes.find((r) => r.name === safeRemoteName);
|
|
19
|
+
if (existingRemote == null) {
|
|
20
|
+
await git.addRemote(safeRemoteName, url);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async #getSimpleGit() {
|
|
25
|
+
await this.#checkGitAvailability();
|
|
26
|
+
const dir = await this._getRepoDir();
|
|
27
|
+
return simpleGit(dir);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async #checkGitAvailability() {
|
|
31
|
+
if (this.#gitAvailabilityChecked) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const git = simpleGit();
|
|
35
|
+
const ver = await git.version();
|
|
36
|
+
if (!ver.installed) {
|
|
37
|
+
throw new GitNotFoundError();
|
|
38
|
+
}
|
|
39
|
+
this.#gitAvailabilityChecked = true;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async resolveFullCommitId(commitId) {
|
|
43
|
+
this._debug('resolveFullCommitId', commitId);
|
|
44
|
+
if (commitId == null) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
const git = await this.#getSimpleGit();
|
|
48
|
+
try {
|
|
49
|
+
const fullOid = await git.revparse([commitId]);
|
|
50
|
+
return fullOid.trim();
|
|
51
|
+
} catch (e) {
|
|
52
|
+
if (e.message.includes('unknown revision') || e.message.includes('ambiguous argument')) {
|
|
53
|
+
throw new Error(`Commit id ${commitId} is ambiguous`);
|
|
54
|
+
}
|
|
55
|
+
throw e;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async getRemoteCommit(remoteUrl) {
|
|
60
|
+
const git = await this.#getSimpleGit();
|
|
61
|
+
const authUrl = this.#buildAuthenticatedUrl(remoteUrl);
|
|
62
|
+
this._debug('getRemoteCommit', this.#redactUrl(authUrl));
|
|
63
|
+
try {
|
|
64
|
+
const result = await git.listRemote(['--refs', authUrl.toString()]);
|
|
65
|
+
// Parse output: "<sha>\trefs/heads/master"
|
|
66
|
+
const lines = result.trim().split('\n');
|
|
67
|
+
for (const line of lines) {
|
|
68
|
+
const [sha, ref] = line.split('\t');
|
|
69
|
+
if (ref === 'refs/heads/master') {
|
|
70
|
+
return sha;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return undefined;
|
|
74
|
+
} catch {
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
#buildAuthenticatedUrl(url) {
|
|
80
|
+
const urlObj = new URL(url);
|
|
81
|
+
urlObj.username = config.token;
|
|
82
|
+
urlObj.password = config.secret;
|
|
83
|
+
return urlObj;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async getFullBranch(branchName) {
|
|
87
|
+
this._debug('getFullBranch', branchName);
|
|
88
|
+
const git = await this.#getSimpleGit();
|
|
89
|
+
if (branchName === '') {
|
|
90
|
+
const branch = await git.branch();
|
|
91
|
+
if (branch.current) {
|
|
92
|
+
return `refs/heads/${branch.current}`;
|
|
93
|
+
}
|
|
94
|
+
return 'HEAD';
|
|
95
|
+
}
|
|
96
|
+
// Try to expand the ref
|
|
97
|
+
try {
|
|
98
|
+
const fullRef = await git.revparse(['--symbolic-full-name', branchName]);
|
|
99
|
+
return fullRef.trim();
|
|
100
|
+
} catch {
|
|
101
|
+
// If it fails, it might be a commit hash, return as-is
|
|
102
|
+
return branchName;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async getBranchCommit(refspec) {
|
|
107
|
+
this._debug('getBranchCommit', refspec);
|
|
108
|
+
const git = await this.#getSimpleGit();
|
|
109
|
+
// Use rev-parse with ^{commit} to dereference tags to their commit
|
|
110
|
+
const oid = await git.revparse([`${refspec}^{commit}`]);
|
|
111
|
+
return oid.trim();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async isExistingTag(tag) {
|
|
115
|
+
this._debug('isExistingTag', tag);
|
|
116
|
+
const git = await this.#getSimpleGit();
|
|
117
|
+
const tags = await git.tags();
|
|
118
|
+
return tags.all.includes(tag);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
#redactUrl(url) {
|
|
122
|
+
const urlObj = typeof url === 'string' ? new URL(url) : url;
|
|
123
|
+
const redacted = new URL(urlObj.toString());
|
|
124
|
+
if (redacted.username) redacted.username = '***';
|
|
125
|
+
if (redacted.password) redacted.password = '***';
|
|
126
|
+
return redacted.toString();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async push(remoteUrl, branchRefspec, force) {
|
|
130
|
+
const git = await this.#getSimpleGit();
|
|
131
|
+
const authUrl = this.#buildAuthenticatedUrl(remoteUrl);
|
|
132
|
+
const refspec = `${branchRefspec}:refs/heads/master`;
|
|
133
|
+
this._debug('push', this.#redactUrl(authUrl), refspec, force ? '--force' : '');
|
|
134
|
+
const options = ['--porcelain'];
|
|
135
|
+
if (force) {
|
|
136
|
+
options.push('--force');
|
|
137
|
+
}
|
|
138
|
+
try {
|
|
139
|
+
await git.push(authUrl.toString(), refspec, options);
|
|
140
|
+
return {};
|
|
141
|
+
} catch (e) {
|
|
142
|
+
if (e.message.includes('non-fast-forward') || e.message.includes('[rejected]')) {
|
|
143
|
+
throw new Error('Push rejected because it was not a simple fast-forward, use --force to override');
|
|
144
|
+
}
|
|
145
|
+
throw e;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async completeBranches() {
|
|
150
|
+
this._debug('completeBranches');
|
|
151
|
+
const git = await this.#getSimpleGit();
|
|
152
|
+
const branches = await git.branchLocal();
|
|
153
|
+
return branches.all;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async isInsideGitRepo() {
|
|
157
|
+
this._debug('isInsideGitRepo');
|
|
158
|
+
try {
|
|
159
|
+
await this._getRepoDir();
|
|
160
|
+
return true;
|
|
161
|
+
} catch {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async isGitWorkingDirectoryClean() {
|
|
167
|
+
this._debug('isGitWorkingDirectoryClean');
|
|
168
|
+
const git = await this.#getSimpleGit();
|
|
169
|
+
const status = await git.status();
|
|
170
|
+
return status.isClean();
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
class GitNotFoundError extends Error {
|
|
175
|
+
constructor() {
|
|
176
|
+
super(
|
|
177
|
+
'The system git feature requires git to be installed and available in your PATH\n' +
|
|
178
|
+
'Either install git or disable this feature with: clever features disable system-git',
|
|
179
|
+
);
|
|
180
|
+
this.name = 'GitNotFoundError';
|
|
181
|
+
}
|
|
182
|
+
}
|
package/src/models/git.js
CHANGED
|
@@ -1,151 +1,173 @@
|
|
|
1
|
-
import * as git from 'isomorphic-git';
|
|
2
|
-
import _ from 'lodash';
|
|
3
1
|
import fs from 'node:fs';
|
|
4
2
|
import path from 'node:path';
|
|
5
|
-
|
|
6
|
-
import {
|
|
3
|
+
|
|
4
|
+
import { isFeatureEnabled } from '../config/features.js';
|
|
5
|
+
import { Logger } from '../logger.js';
|
|
7
6
|
import { findPath } from './fs-utils.js';
|
|
8
|
-
import * as http from './isomorphic-http-with-agent.js';
|
|
9
|
-
|
|
10
|
-
async function getRepo() {
|
|
11
|
-
try {
|
|
12
|
-
const dir = await findPath('.', '.git');
|
|
13
|
-
return { fs, dir, http };
|
|
14
|
-
} catch {
|
|
15
|
-
throw new Error('Could not find the .git folder.');
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
7
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Abstract base class for git operations.
|
|
10
|
+
* Implementations: GitIsomorphic (JS-based) and GitSystem (system git command)
|
|
11
|
+
*/
|
|
12
|
+
export class Git {
|
|
13
|
+
/** @type {Git | null} */
|
|
14
|
+
static #instance = null;
|
|
15
|
+
|
|
16
|
+
/** @type {string} */
|
|
17
|
+
#name;
|
|
26
18
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if (existingRemote == null) {
|
|
33
|
-
// In some situations, we may end up with race conditions so we force it
|
|
34
|
-
return git.addRemote({ ...repo, remote: safeRemoteName, url, force: true });
|
|
19
|
+
/**
|
|
20
|
+
* @param {string} name - Backend name for logging purposes
|
|
21
|
+
*/
|
|
22
|
+
constructor(name) {
|
|
23
|
+
this.#name = name;
|
|
35
24
|
}
|
|
36
|
-
}
|
|
37
25
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Get the git implementation based on the feature flag.
|
|
28
|
+
* Returns a cached instance if available.
|
|
29
|
+
* @returns {Promise<Git>}
|
|
30
|
+
*/
|
|
31
|
+
static async get() {
|
|
32
|
+
if (Git.#instance == null) {
|
|
33
|
+
const useSystemGit = await isFeatureEnabled('system-git');
|
|
34
|
+
if (useSystemGit) {
|
|
35
|
+
const { GitSystem } = await import('./git-system.js');
|
|
36
|
+
Git.#instance = new GitSystem();
|
|
37
|
+
} else {
|
|
38
|
+
const { GitIsomorphic } = await import('./git-isomorphic.js');
|
|
39
|
+
Git.#instance = new GitIsomorphic();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return Git.#instance;
|
|
41
43
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Log a debug message for git operations
|
|
47
|
+
* @protected
|
|
48
|
+
* @param {string} operation - The operation name
|
|
49
|
+
* @param {...string} args - Additional arguments to log
|
|
50
|
+
*/
|
|
51
|
+
_debug(operation, ...args) {
|
|
52
|
+
const argsStr = args.length > 0 ? ` ${args.join(' ')}` : '';
|
|
53
|
+
Logger.debug(`git(${this.#name}): ${operation}${argsStr}`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Get the repository directory
|
|
58
|
+
* @protected
|
|
59
|
+
* @returns {Promise<string>}
|
|
60
|
+
*/
|
|
61
|
+
async _getRepoDir() {
|
|
62
|
+
try {
|
|
63
|
+
return await findPath('.', '.git');
|
|
64
|
+
} catch {
|
|
65
|
+
throw new Error('Could not find the .git folder');
|
|
48
66
|
}
|
|
49
|
-
throw e;
|
|
50
67
|
}
|
|
51
|
-
}
|
|
52
68
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
69
|
+
/**
|
|
70
|
+
* Add a remote to the repository
|
|
71
|
+
* @param {string} remoteName
|
|
72
|
+
* @param {string} url
|
|
73
|
+
* @returns {Promise<void>}
|
|
74
|
+
*/
|
|
75
|
+
async addRemote(remoteName, url) {
|
|
76
|
+
throw new Error('Not implemented');
|
|
77
|
+
}
|
|
62
78
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
79
|
+
/**
|
|
80
|
+
* Resolve a short commit ID to its full form
|
|
81
|
+
* @param {string | null} commitId
|
|
82
|
+
* @returns {Promise<string | null>}
|
|
83
|
+
*/
|
|
84
|
+
async resolveFullCommitId(commitId) {
|
|
85
|
+
throw new Error('Not implemented');
|
|
68
86
|
}
|
|
69
|
-
return git.expandRef({ ...repo, ref: branchName });
|
|
70
|
-
}
|
|
71
87
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
88
|
+
/**
|
|
89
|
+
* Get the commit SHA of the master branch on a remote
|
|
90
|
+
* @param {string} remoteUrl
|
|
91
|
+
* @returns {Promise<string | undefined>}
|
|
92
|
+
*/
|
|
93
|
+
async getRemoteCommit(remoteUrl) {
|
|
94
|
+
throw new Error('Not implemented');
|
|
95
|
+
}
|
|
80
96
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
97
|
+
/**
|
|
98
|
+
* Get the full ref name for a branch
|
|
99
|
+
* @param {string} branchName - Branch name, or empty string for current branch
|
|
100
|
+
* @returns {Promise<string>}
|
|
101
|
+
*/
|
|
102
|
+
async getFullBranch(branchName) {
|
|
103
|
+
throw new Error('Not implemented');
|
|
104
|
+
}
|
|
88
105
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
ref: branchRefspec,
|
|
97
|
-
remoteRef: 'master',
|
|
98
|
-
force,
|
|
99
|
-
});
|
|
100
|
-
if (push.errors != null) {
|
|
101
|
-
throw new Error(push.errors.join(', '));
|
|
102
|
-
}
|
|
103
|
-
return push;
|
|
104
|
-
} catch (e) {
|
|
105
|
-
if (e.code === 'PushRejectedNonFastForward') {
|
|
106
|
-
throw new Error('Push rejected because it was not a simple fast-forward. Use "--force" to override.');
|
|
107
|
-
}
|
|
108
|
-
throw e;
|
|
106
|
+
/**
|
|
107
|
+
* Get the commit SHA for a branch or tag
|
|
108
|
+
* @param {string} refspec
|
|
109
|
+
* @returns {Promise<string>}
|
|
110
|
+
*/
|
|
111
|
+
async getBranchCommit(refspec) {
|
|
112
|
+
throw new Error('Not implemented');
|
|
109
113
|
}
|
|
110
|
-
}
|
|
111
114
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
+
/**
|
|
116
|
+
* Check if a tag exists
|
|
117
|
+
* @param {string} tag
|
|
118
|
+
* @returns {Promise<boolean>}
|
|
119
|
+
*/
|
|
120
|
+
async isExistingTag(tag) {
|
|
121
|
+
throw new Error('Not implemented');
|
|
122
|
+
}
|
|
115
123
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
124
|
+
/**
|
|
125
|
+
* Push to a remote repository
|
|
126
|
+
* @param {string} remoteUrl
|
|
127
|
+
* @param {string} branchRefspec
|
|
128
|
+
* @param {boolean} force
|
|
129
|
+
* @returns {Promise<object>}
|
|
130
|
+
*/
|
|
131
|
+
async push(remoteUrl, branchRefspec, force) {
|
|
132
|
+
throw new Error('Not implemented');
|
|
123
133
|
}
|
|
124
|
-
}
|
|
125
134
|
|
|
126
|
-
/**
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
.catch(() => false);
|
|
134
|
-
}
|
|
135
|
+
/**
|
|
136
|
+
* List local branches (for autocompletion)
|
|
137
|
+
* @returns {Promise<string[]>}
|
|
138
|
+
*/
|
|
139
|
+
async completeBranches() {
|
|
140
|
+
throw new Error('Not implemented');
|
|
141
|
+
}
|
|
135
142
|
|
|
136
|
-
/**
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
143
|
+
/**
|
|
144
|
+
* Check if the repository is a shallow clone
|
|
145
|
+
* @returns {Promise<boolean>}
|
|
146
|
+
*/
|
|
147
|
+
async isShallow() {
|
|
148
|
+
this._debug('isShallow');
|
|
149
|
+
const dir = await this._getRepoDir();
|
|
150
|
+
try {
|
|
151
|
+
await fs.promises.access(path.join(dir, '.git', 'shallow'));
|
|
152
|
+
return true;
|
|
153
|
+
} catch {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Check if the current directory is inside a git repository
|
|
160
|
+
* @returns {Promise<boolean>}
|
|
161
|
+
*/
|
|
162
|
+
async isInsideGitRepo() {
|
|
163
|
+
throw new Error('Not implemented');
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Check if the git working directory is clean (no uncommitted changes)
|
|
168
|
+
* @returns {Promise<boolean>}
|
|
169
|
+
*/
|
|
170
|
+
async isGitWorkingDirectoryClean() {
|
|
171
|
+
throw new Error('Not implemented');
|
|
172
|
+
}
|
|
151
173
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getSummary } from '@clevercloud/client/esm/api/v2/user.js';
|
|
2
|
+
import { loadIdsCache, writeIdsCache } from '../config/cache.js';
|
|
2
3
|
import { Logger } from '../logger.js';
|
|
3
4
|
import * as User from '../models/user.js';
|
|
4
|
-
import { loadIdsCache, writeIdsCache } from './configuration.js';
|
|
5
5
|
import * as Organisation from './organisation.js';
|
|
6
6
|
import { sendToApi } from './send-to-api.js';
|
|
7
7
|
|