@catchmexz/fedin-vibe-mcp-server 0.1.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/LICENSE +202 -0
- package/dist/common/errors.js +69 -0
- package/dist/common/modularTemplates.js +483 -0
- package/dist/common/pipelineTemplates.js +19 -0
- package/dist/common/types.js +42 -0
- package/dist/common/utils.js +347 -0
- package/dist/common/version.js +1 -0
- package/dist/index.js +217 -0
- package/dist/operations/appstack/appOrchestrations.js +235 -0
- package/dist/operations/appstack/appTags.js +147 -0
- package/dist/operations/appstack/appTemplates.js +67 -0
- package/dist/operations/appstack/applications.js +154 -0
- package/dist/operations/appstack/changeOrders.js +293 -0
- package/dist/operations/appstack/changeRequests.js +263 -0
- package/dist/operations/appstack/deploymentResources.js +265 -0
- package/dist/operations/appstack/globalVars.js +200 -0
- package/dist/operations/appstack/releaseWorkflows.js +178 -0
- package/dist/operations/appstack/variableGroups.js +216 -0
- package/dist/operations/codeup/branches.js +144 -0
- package/dist/operations/codeup/changeRequestComments.js +89 -0
- package/dist/operations/codeup/changeRequests.js +203 -0
- package/dist/operations/codeup/compare.js +26 -0
- package/dist/operations/codeup/files.js +483 -0
- package/dist/operations/codeup/repositories.js +83 -0
- package/dist/operations/codeup/types.js +372 -0
- package/dist/operations/flow/hostGroup.js +48 -0
- package/dist/operations/flow/pipeline.js +530 -0
- package/dist/operations/flow/pipelineJob.js +113 -0
- package/dist/operations/flow/serviceConnection.js +23 -0
- package/dist/operations/flow/types.js +377 -0
- package/dist/operations/git/git-repository.js +334 -0
- package/dist/operations/git/index.js +210 -0
- package/dist/operations/organization/members.js +94 -0
- package/dist/operations/organization/organization.js +73 -0
- package/dist/operations/organization/types.js +111 -0
- package/dist/operations/packages/artifacts.js +64 -0
- package/dist/operations/packages/repositories.js +35 -0
- package/dist/operations/packages/types.js +56 -0
- package/dist/operations/projex/project.js +206 -0
- package/dist/operations/projex/sprint.js +90 -0
- package/dist/operations/projex/types.js +390 -0
- package/dist/operations/projex/workitem.js +452 -0
- package/dist/tool-handlers/appstack-change-orders.js +55 -0
- package/dist/tool-handlers/appstack-change-requests.js +49 -0
- package/dist/tool-handlers/appstack-deployment-resources.js +43 -0
- package/dist/tool-handlers/appstack-global-vars.js +43 -0
- package/dist/tool-handlers/appstack-orchestrations.js +49 -0
- package/dist/tool-handlers/appstack-tags.js +43 -0
- package/dist/tool-handlers/appstack-templates.js +19 -0
- package/dist/tool-handlers/appstack-variable-groups.js +55 -0
- package/dist/tool-handlers/appstack.js +37 -0
- package/dist/tool-handlers/code-management.js +174 -0
- package/dist/tool-handlers/git/branch-operations.js +1 -0
- package/dist/tool-handlers/git/clone-repository.js +36 -0
- package/dist/tool-handlers/git/create-branch.js +26 -0
- package/dist/tool-handlers/git/get-repository-status.js +33 -0
- package/dist/tool-handlers/git/pull-repository.js +27 -0
- package/dist/tool-handlers/git/push-repository.js +37 -0
- package/dist/tool-handlers/git/switch-branch.js +25 -0
- package/dist/tool-handlers/index.js +43 -0
- package/dist/tool-handlers/organization.js +90 -0
- package/dist/tool-handlers/packages.js +32 -0
- package/dist/tool-handlers/pipeline.js +272 -0
- package/dist/tool-handlers/project-management.js +152 -0
- package/dist/tool-handlers/service-connections.js +16 -0
- package/dist/tool-registry/appstack-change-orders.js +40 -0
- package/dist/tool-registry/appstack-change-requests.js +35 -0
- package/dist/tool-registry/appstack-deployment-resources.js +30 -0
- package/dist/tool-registry/appstack-global-vars.js +30 -0
- package/dist/tool-registry/appstack-orchestrations.js +35 -0
- package/dist/tool-registry/appstack-tags.js +30 -0
- package/dist/tool-registry/appstack-templates.js +10 -0
- package/dist/tool-registry/appstack-variable-groups.js +40 -0
- package/dist/tool-registry/appstack.js +25 -0
- package/dist/tool-registry/code-management.js +89 -0
- package/dist/tool-registry/git-repository.js +41 -0
- package/dist/tool-registry/index.js +6 -0
- package/dist/tool-registry/organization.js +65 -0
- package/dist/tool-registry/packages.js +21 -0
- package/dist/tool-registry/pipeline.js +157 -0
- package/dist/tool-registry/project-management.js +108 -0
- package/dist/tool-registry/service-connections.js +10 -0
- package/package.json +39 -0
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
import { simpleGit } from 'simple-git';
|
|
2
|
+
import * as fs from 'fs/promises';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
// Git仓库操作的参数验证模式
|
|
6
|
+
export const CloneRepositorySchema = z.object({
|
|
7
|
+
repositoryUrl: z.string().describe('Git仓库URL'),
|
|
8
|
+
localPath: z.string().describe('本地克隆路径'),
|
|
9
|
+
branch: z.string().optional().describe('指定分支,默认为主分支'),
|
|
10
|
+
depth: z.number().optional().describe('克隆深度,用于浅克隆'),
|
|
11
|
+
username: z.string().optional().describe('Git用户名'),
|
|
12
|
+
password: z.string().optional().describe('Git密码或访问令牌'),
|
|
13
|
+
});
|
|
14
|
+
export const PushRepositorySchema = z.object({
|
|
15
|
+
localPath: z.string().describe('本地仓库路径'),
|
|
16
|
+
branch: z.string().optional().describe('推送的分支,默认为当前分支'),
|
|
17
|
+
remote: z.string().optional().describe('远程仓库名,默认为origin'),
|
|
18
|
+
commitMessage: z.string().optional().describe('提交信息'),
|
|
19
|
+
addAll: z.boolean().optional().describe('是否添加所有更改,默认为true'),
|
|
20
|
+
});
|
|
21
|
+
export const PullRepositorySchema = z.object({
|
|
22
|
+
localPath: z.string().describe('本地仓库路径'),
|
|
23
|
+
branch: z.string().optional().describe('拉取的分支,默认为当前分支'),
|
|
24
|
+
remote: z.string().optional().describe('远程仓库名,默认为origin'),
|
|
25
|
+
});
|
|
26
|
+
export const CreateBranchSchema = z.object({
|
|
27
|
+
localPath: z.string().describe('本地仓库路径'),
|
|
28
|
+
branchName: z.string().describe('新分支名称'),
|
|
29
|
+
fromBranch: z.string().optional().describe('基于哪个分支创建,默认为当前分支'),
|
|
30
|
+
checkout: z.boolean().optional().describe('是否切换到新分支,默认为true'),
|
|
31
|
+
});
|
|
32
|
+
export const SwitchBranchSchema = z.object({
|
|
33
|
+
localPath: z.string().describe('本地仓库路径'),
|
|
34
|
+
branchName: z.string().describe('要切换到的分支名称'),
|
|
35
|
+
});
|
|
36
|
+
export const GetRepositoryStatusSchema = z.object({
|
|
37
|
+
localPath: z.string().describe('本地仓库路径'),
|
|
38
|
+
});
|
|
39
|
+
/**
|
|
40
|
+
* Git仓库操作类
|
|
41
|
+
*/
|
|
42
|
+
export class GitRepositoryOperations {
|
|
43
|
+
/**
|
|
44
|
+
* 克隆Git仓库
|
|
45
|
+
*/
|
|
46
|
+
static async cloneRepository(params) {
|
|
47
|
+
const { repositoryUrl, localPath, branch, depth, username, password } = params;
|
|
48
|
+
try {
|
|
49
|
+
// 检查本地路径是否已存在
|
|
50
|
+
try {
|
|
51
|
+
await fs.access(localPath);
|
|
52
|
+
throw new Error(`目标路径 ${localPath} 已存在`);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
if (error.code !== 'ENOENT') {
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// 创建父目录
|
|
60
|
+
await fs.mkdir(path.dirname(localPath), { recursive: true });
|
|
61
|
+
const git = simpleGit();
|
|
62
|
+
// 构建克隆选项
|
|
63
|
+
const cloneOptions = {};
|
|
64
|
+
if (branch) {
|
|
65
|
+
cloneOptions['--branch'] = branch;
|
|
66
|
+
}
|
|
67
|
+
if (depth) {
|
|
68
|
+
cloneOptions['--depth'] = depth;
|
|
69
|
+
}
|
|
70
|
+
// 如果提供了认证信息,修改URL
|
|
71
|
+
let authUrl = repositoryUrl;
|
|
72
|
+
if (username && password) {
|
|
73
|
+
const url = new URL(repositoryUrl);
|
|
74
|
+
url.username = username;
|
|
75
|
+
url.password = password;
|
|
76
|
+
authUrl = url.toString();
|
|
77
|
+
}
|
|
78
|
+
console.log(`开始克隆仓库: ${repositoryUrl} 到 ${localPath}`);
|
|
79
|
+
const result = await git.clone(authUrl, localPath, cloneOptions);
|
|
80
|
+
return {
|
|
81
|
+
success: true,
|
|
82
|
+
message: `成功克隆仓库到 ${localPath}`,
|
|
83
|
+
localPath,
|
|
84
|
+
repositoryUrl,
|
|
85
|
+
branch: branch || 'default',
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
console.error('克隆仓库失败:', error);
|
|
90
|
+
return {
|
|
91
|
+
success: false,
|
|
92
|
+
error: `克隆仓库失败: ${error.message}`,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* 推送代码到远程仓库
|
|
98
|
+
*/
|
|
99
|
+
static async pushRepository(params) {
|
|
100
|
+
const { localPath, branch, remote = 'origin', commitMessage, addAll = true } = params;
|
|
101
|
+
try {
|
|
102
|
+
// 检查本地路径是否存在
|
|
103
|
+
await fs.access(localPath);
|
|
104
|
+
const git = simpleGit(localPath);
|
|
105
|
+
// 检查是否是Git仓库
|
|
106
|
+
const isRepo = await git.checkIsRepo();
|
|
107
|
+
if (!isRepo) {
|
|
108
|
+
throw new Error(`${localPath} 不是一个Git仓库`);
|
|
109
|
+
}
|
|
110
|
+
// 如果需要添加所有更改
|
|
111
|
+
if (addAll) {
|
|
112
|
+
await git.add('.');
|
|
113
|
+
console.log('已添加所有更改到暂存区');
|
|
114
|
+
}
|
|
115
|
+
// 检查是否有更改需要提交
|
|
116
|
+
const status = await git.status();
|
|
117
|
+
if (status.staged.length === 0 && status.not_added.length === 0 && status.modified.length === 0) {
|
|
118
|
+
return {
|
|
119
|
+
success: true,
|
|
120
|
+
message: '没有更改需要推送',
|
|
121
|
+
status,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
// 提交更改
|
|
125
|
+
if (status.staged.length > 0 || (addAll && (status.not_added.length > 0 || status.modified.length > 0))) {
|
|
126
|
+
const message = commitMessage || `自动提交 - ${new Date().toISOString()}`;
|
|
127
|
+
const commitResult = await git.commit(message);
|
|
128
|
+
console.log(`已提交更改: ${message}`);
|
|
129
|
+
}
|
|
130
|
+
// 推送到远程仓库
|
|
131
|
+
const pushBranch = branch || await git.revparse(['--abbrev-ref', 'HEAD']);
|
|
132
|
+
const pushResult = await git.push(remote, pushBranch);
|
|
133
|
+
return {
|
|
134
|
+
success: true,
|
|
135
|
+
message: `成功推送到 ${remote}/${pushBranch}`,
|
|
136
|
+
pushResult,
|
|
137
|
+
branch: pushBranch,
|
|
138
|
+
remote,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
console.error('推送仓库失败:', error);
|
|
143
|
+
return {
|
|
144
|
+
success: false,
|
|
145
|
+
error: `推送仓库失败: ${error.message}`,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* 从远程仓库拉取更新
|
|
151
|
+
*/
|
|
152
|
+
static async pullRepository(params) {
|
|
153
|
+
const { localPath, branch, remote = 'origin' } = params;
|
|
154
|
+
try {
|
|
155
|
+
// 检查本地路径是否存在
|
|
156
|
+
await fs.access(localPath);
|
|
157
|
+
const git = simpleGit(localPath);
|
|
158
|
+
// 检查是否是Git仓库
|
|
159
|
+
const isRepo = await git.checkIsRepo();
|
|
160
|
+
if (!isRepo) {
|
|
161
|
+
throw new Error(`${localPath} 不是一个Git仓库`);
|
|
162
|
+
}
|
|
163
|
+
// 拉取更新
|
|
164
|
+
const pullBranch = branch || await git.revparse(['--abbrev-ref', 'HEAD']);
|
|
165
|
+
const pullResult = await git.pull(remote, pullBranch);
|
|
166
|
+
return {
|
|
167
|
+
success: true,
|
|
168
|
+
message: `成功从 ${remote}/${pullBranch} 拉取更新`,
|
|
169
|
+
pullResult,
|
|
170
|
+
branch: pullBranch,
|
|
171
|
+
remote,
|
|
172
|
+
summary: pullResult.summary,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
console.error('拉取仓库失败:', error);
|
|
177
|
+
return {
|
|
178
|
+
success: false,
|
|
179
|
+
error: `拉取仓库失败: ${error.message}`,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* 创建新分支
|
|
185
|
+
*/
|
|
186
|
+
static async createBranch(params) {
|
|
187
|
+
const { localPath, branchName, fromBranch, checkout = true } = params;
|
|
188
|
+
try {
|
|
189
|
+
// 检查本地路径是否存在
|
|
190
|
+
await fs.access(localPath);
|
|
191
|
+
const git = simpleGit(localPath);
|
|
192
|
+
// 检查是否是Git仓库
|
|
193
|
+
const isRepo = await git.checkIsRepo();
|
|
194
|
+
if (!isRepo) {
|
|
195
|
+
throw new Error(`${localPath} 不是一个Git仓库`);
|
|
196
|
+
}
|
|
197
|
+
// 检查分支是否已存在
|
|
198
|
+
const branches = await git.branchLocal();
|
|
199
|
+
if (branches.all.includes(branchName)) {
|
|
200
|
+
throw new Error(`分支 ${branchName} 已存在`);
|
|
201
|
+
}
|
|
202
|
+
// 创建分支
|
|
203
|
+
if (fromBranch) {
|
|
204
|
+
await git.checkoutBranch(branchName, fromBranch);
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
await git.checkoutLocalBranch(branchName);
|
|
208
|
+
}
|
|
209
|
+
// 如果不需要切换到新分支,切换回原分支
|
|
210
|
+
if (!checkout && fromBranch) {
|
|
211
|
+
await git.checkout(fromBranch);
|
|
212
|
+
}
|
|
213
|
+
return {
|
|
214
|
+
success: true,
|
|
215
|
+
message: `成功创建分支 ${branchName}`,
|
|
216
|
+
branchName,
|
|
217
|
+
fromBranch: fromBranch || 'current',
|
|
218
|
+
checkedOut: checkout,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
catch (error) {
|
|
222
|
+
console.error('创建分支失败:', error);
|
|
223
|
+
return {
|
|
224
|
+
success: false,
|
|
225
|
+
error: `创建分支失败: ${error.message}`,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* 切换分支
|
|
231
|
+
*/
|
|
232
|
+
static async switchBranch(params) {
|
|
233
|
+
const { localPath, branchName } = params;
|
|
234
|
+
try {
|
|
235
|
+
// 检查本地路径是否存在
|
|
236
|
+
await fs.access(localPath);
|
|
237
|
+
const git = simpleGit(localPath);
|
|
238
|
+
// 检查是否是Git仓库
|
|
239
|
+
const isRepo = await git.checkIsRepo();
|
|
240
|
+
if (!isRepo) {
|
|
241
|
+
throw new Error(`${localPath} 不是一个Git仓库`);
|
|
242
|
+
}
|
|
243
|
+
// 检查分支是否存在
|
|
244
|
+
const branches = await git.branchLocal();
|
|
245
|
+
if (!branches.all.includes(branchName)) {
|
|
246
|
+
// 尝试从远程分支创建本地分支
|
|
247
|
+
const remoteBranches = await git.branch(['-r']);
|
|
248
|
+
const remoteBranchName = `origin/${branchName}`;
|
|
249
|
+
if (remoteBranches.all.includes(remoteBranchName)) {
|
|
250
|
+
await git.checkoutBranch(branchName, remoteBranchName);
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
throw new Error(`分支 ${branchName} 不存在`);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
// 切换到现有分支
|
|
258
|
+
await git.checkout(branchName);
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
success: true,
|
|
262
|
+
message: `成功切换到分支 ${branchName}`,
|
|
263
|
+
branchName,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
catch (error) {
|
|
267
|
+
console.error('切换分支失败:', error);
|
|
268
|
+
return {
|
|
269
|
+
success: false,
|
|
270
|
+
error: `切换分支失败: ${error.message}`,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* 获取仓库状态
|
|
276
|
+
*/
|
|
277
|
+
static async getRepositoryStatus(params) {
|
|
278
|
+
const { localPath } = params;
|
|
279
|
+
try {
|
|
280
|
+
// 检查本地路径是否存在
|
|
281
|
+
await fs.access(localPath);
|
|
282
|
+
const git = simpleGit(localPath);
|
|
283
|
+
// 检查是否是Git仓库
|
|
284
|
+
const isRepo = await git.checkIsRepo();
|
|
285
|
+
if (!isRepo) {
|
|
286
|
+
throw new Error(`${localPath} 不是一个Git仓库`);
|
|
287
|
+
}
|
|
288
|
+
// 获取状态信息
|
|
289
|
+
const status = await git.status();
|
|
290
|
+
const branches = await git.branchLocal();
|
|
291
|
+
const remotes = await git.getRemotes(true);
|
|
292
|
+
const currentBranch = await git.revparse(['--abbrev-ref', 'HEAD']);
|
|
293
|
+
// 获取最近的提交信息
|
|
294
|
+
const latestCommit = await git.log({ maxCount: 1 });
|
|
295
|
+
return {
|
|
296
|
+
success: true,
|
|
297
|
+
status: {
|
|
298
|
+
current: currentBranch,
|
|
299
|
+
tracking: status.tracking,
|
|
300
|
+
ahead: status.ahead,
|
|
301
|
+
behind: status.behind,
|
|
302
|
+
staged: status.staged,
|
|
303
|
+
not_added: status.not_added,
|
|
304
|
+
conflicted: status.conflicted,
|
|
305
|
+
created: status.created,
|
|
306
|
+
deleted: status.deleted,
|
|
307
|
+
modified: status.modified,
|
|
308
|
+
renamed: status.renamed,
|
|
309
|
+
},
|
|
310
|
+
branches: {
|
|
311
|
+
current: branches.current,
|
|
312
|
+
all: branches.all,
|
|
313
|
+
},
|
|
314
|
+
remotes: remotes.map(remote => ({
|
|
315
|
+
name: remote.name,
|
|
316
|
+
refs: remote.refs,
|
|
317
|
+
})),
|
|
318
|
+
latestCommit: latestCommit.latest ? {
|
|
319
|
+
hash: latestCommit.latest.hash,
|
|
320
|
+
date: latestCommit.latest.date,
|
|
321
|
+
message: latestCommit.latest.message,
|
|
322
|
+
author: latestCommit.latest.author_name,
|
|
323
|
+
} : null,
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
catch (error) {
|
|
327
|
+
console.error('获取仓库状态失败:', error);
|
|
328
|
+
return {
|
|
329
|
+
success: false,
|
|
330
|
+
error: `获取仓库状态失败: ${error.message}`,
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { simpleGit } from 'simple-git';
|
|
3
|
+
import * as fs from 'fs/promises';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
import { tmpdir } from 'os';
|
|
6
|
+
export class GitOperations {
|
|
7
|
+
git;
|
|
8
|
+
workingDir;
|
|
9
|
+
constructor(workingDir) {
|
|
10
|
+
this.workingDir = workingDir || tmpdir();
|
|
11
|
+
this.git = simpleGit(this.workingDir);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* 克隆整个代码库到本地
|
|
15
|
+
*/
|
|
16
|
+
async cloneRepository(repo) {
|
|
17
|
+
const repoName = this.extractRepoName(repo.url);
|
|
18
|
+
const localPath = repo.localPath || path.join(this.workingDir, repoName);
|
|
19
|
+
// 确保目录存在
|
|
20
|
+
await fs.mkdir(path.dirname(localPath), { recursive: true });
|
|
21
|
+
const cloneOptions = {
|
|
22
|
+
'--depth': 1, // 浅克隆,只获取最新提交
|
|
23
|
+
};
|
|
24
|
+
if (repo.branch) {
|
|
25
|
+
cloneOptions['--branch'] = repo.branch;
|
|
26
|
+
}
|
|
27
|
+
// 构建带认证的URL
|
|
28
|
+
const cloneUrl = this.buildAuthenticatedUrl(repo.url, repo.credentials);
|
|
29
|
+
try {
|
|
30
|
+
await this.git.clone(cloneUrl, localPath, cloneOptions);
|
|
31
|
+
return localPath;
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
throw new Error(`克隆代码库失败: ${error}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* 推送整个代码库的更改
|
|
39
|
+
*/
|
|
40
|
+
async pushRepository(localPath, repo, commitMessage, files) {
|
|
41
|
+
const git = simpleGit(localPath);
|
|
42
|
+
try {
|
|
43
|
+
// 配置用户信息(如果提供了凭据)
|
|
44
|
+
if (repo.credentials) {
|
|
45
|
+
await git.addConfig('user.name', repo.credentials.username);
|
|
46
|
+
await git.addConfig('user.email', `${repo.credentials.username}@example.com`);
|
|
47
|
+
}
|
|
48
|
+
// 添加文件
|
|
49
|
+
if (files && files.length > 0) {
|
|
50
|
+
await git.add(files);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
await git.add('.');
|
|
54
|
+
}
|
|
55
|
+
// 检查是否有更改
|
|
56
|
+
const status = await git.status();
|
|
57
|
+
if (status.files.length === 0) {
|
|
58
|
+
throw new Error('没有检测到文件更改');
|
|
59
|
+
}
|
|
60
|
+
// 提交更改
|
|
61
|
+
await git.commit(commitMessage);
|
|
62
|
+
// 推送到远程仓库
|
|
63
|
+
const pushOptions = {};
|
|
64
|
+
const branch = repo.branch || 'master';
|
|
65
|
+
const pushUrl = this.buildAuthenticatedUrl(repo.url, repo.credentials);
|
|
66
|
+
await git.addRemote('origin', pushUrl).catch(() => { }); // 忽略已存在的错误
|
|
67
|
+
await git.push('origin', branch, pushOptions);
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
throw new Error(`推送代码库失败: ${error}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* 拉取最新更改
|
|
75
|
+
*/
|
|
76
|
+
async pullRepository(localPath, repo) {
|
|
77
|
+
const git = simpleGit(localPath);
|
|
78
|
+
try {
|
|
79
|
+
const branch = repo.branch || 'master';
|
|
80
|
+
await git.pull('origin', branch);
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
throw new Error(`拉取代码库失败: ${error}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* 获取代码库状态
|
|
88
|
+
*/
|
|
89
|
+
async getRepositoryStatus(localPath) {
|
|
90
|
+
const git = simpleGit(localPath);
|
|
91
|
+
try {
|
|
92
|
+
const status = await git.status();
|
|
93
|
+
const log = await git.log({ maxCount: 10 });
|
|
94
|
+
return {
|
|
95
|
+
status,
|
|
96
|
+
recentCommits: log.all,
|
|
97
|
+
currentBranch: status.current,
|
|
98
|
+
ahead: status.ahead,
|
|
99
|
+
behind: status.behind,
|
|
100
|
+
modified: status.modified,
|
|
101
|
+
created: status.created,
|
|
102
|
+
deleted: status.deleted,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
throw new Error(`获取代码库状态失败: ${error}`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* 创建新分支
|
|
111
|
+
*/
|
|
112
|
+
async createBranch(localPath, branchName, fromBranch) {
|
|
113
|
+
const git = simpleGit(localPath);
|
|
114
|
+
try {
|
|
115
|
+
if (fromBranch) {
|
|
116
|
+
await git.checkoutBranch(branchName, fromBranch);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
await git.checkoutLocalBranch(branchName);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
throw new Error(`创建分支失败: ${error}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* 切换分支
|
|
128
|
+
*/
|
|
129
|
+
async switchBranch(localPath, branchName) {
|
|
130
|
+
const git = simpleGit(localPath);
|
|
131
|
+
try {
|
|
132
|
+
await git.checkout(branchName);
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
throw new Error(`切换分支失败: ${error}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* 同步本地代码库与远程Codeup仓库
|
|
140
|
+
*/
|
|
141
|
+
async syncWithCodeup(localPath, organizationId, repositoryId, branch = 'master') {
|
|
142
|
+
// 这里可以结合现有的Codeup API来实现双向同步
|
|
143
|
+
// 1. 从本地推送到Codeup
|
|
144
|
+
// 2. 从Codeup拉取到本地
|
|
145
|
+
// 3. 处理冲突
|
|
146
|
+
// 实现细节可以根据具体需求来完善
|
|
147
|
+
}
|
|
148
|
+
extractRepoName(url) {
|
|
149
|
+
const match = url.match(/\/([^\/]+)\.git$/);
|
|
150
|
+
return match ? match[1] : 'repository';
|
|
151
|
+
}
|
|
152
|
+
buildAuthenticatedUrl(url, credentials) {
|
|
153
|
+
if (!credentials)
|
|
154
|
+
return url;
|
|
155
|
+
// 处理不同格式的Git URL
|
|
156
|
+
if (url.startsWith('https://')) {
|
|
157
|
+
const urlObj = new URL(url);
|
|
158
|
+
urlObj.username = credentials.username;
|
|
159
|
+
urlObj.password = credentials.password;
|
|
160
|
+
return urlObj.toString();
|
|
161
|
+
}
|
|
162
|
+
else if (url.startsWith('git@')) {
|
|
163
|
+
// SSH格式暂时不处理认证,需要SSH密钥配置
|
|
164
|
+
return url;
|
|
165
|
+
}
|
|
166
|
+
return url;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* 清理临时目录
|
|
170
|
+
*/
|
|
171
|
+
async cleanup(localPath) {
|
|
172
|
+
try {
|
|
173
|
+
await fs.rm(localPath, { recursive: true, force: true });
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
console.warn(`清理目录失败: ${error}`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
// 导出操作函数
|
|
181
|
+
export const gitOperations = new GitOperations();
|
|
182
|
+
// Zod schemas for validation
|
|
183
|
+
export const CloneRepositorySchema = z.object({
|
|
184
|
+
organizationId: z.string().describe('组织ID'),
|
|
185
|
+
repositoryUrl: z.string().describe('代码库URL'),
|
|
186
|
+
branch: z.string().optional().describe('分支名称,默认为master'),
|
|
187
|
+
localPath: z.string().optional().describe('本地路径,不指定则使用临时目录'),
|
|
188
|
+
username: z.string().optional().describe('用户名'),
|
|
189
|
+
password: z.string().optional().describe('密码或访问令牌'),
|
|
190
|
+
});
|
|
191
|
+
export const PushRepositorySchema = z.object({
|
|
192
|
+
organizationId: z.string().describe('组织ID'),
|
|
193
|
+
localPath: z.string().describe('本地代码库路径'),
|
|
194
|
+
repositoryUrl: z.string().describe('代码库URL'),
|
|
195
|
+
commitMessage: z.string().describe('提交信息'),
|
|
196
|
+
branch: z.string().optional().describe('分支名称,默认为master'),
|
|
197
|
+
files: z.array(z.string()).optional().describe('要提交的文件列表,不指定则提交所有更改'),
|
|
198
|
+
username: z.string().optional().describe('用户名'),
|
|
199
|
+
password: z.string().optional().describe('密码或访问令牌'),
|
|
200
|
+
});
|
|
201
|
+
export const RepositoryStatusSchema = z.object({
|
|
202
|
+
organizationId: z.string().describe('组织ID'),
|
|
203
|
+
localPath: z.string().describe('本地代码库路径'),
|
|
204
|
+
});
|
|
205
|
+
export const BranchOperationSchema = z.object({
|
|
206
|
+
organizationId: z.string().describe('组织ID'),
|
|
207
|
+
localPath: z.string().describe('本地代码库路径'),
|
|
208
|
+
branchName: z.string().describe('分支名称'),
|
|
209
|
+
fromBranch: z.string().optional().describe('源分支名称(仅创建分支时使用)'),
|
|
210
|
+
});
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { OrganizationMembersSchema, MemberInfoSchema, SearchOrganizationMembersResultSchema, } from './types.js';
|
|
2
|
+
import { buildUrl, yunxiaoRequest } from "../../common/utils.js";
|
|
3
|
+
/**
|
|
4
|
+
* 查询组织成员列表
|
|
5
|
+
* @param organizationId 组织ID
|
|
6
|
+
* @param page 当前页,默认1
|
|
7
|
+
* @param perPage 每页数据条数,默认100
|
|
8
|
+
* @returns 组织成员列表
|
|
9
|
+
*/
|
|
10
|
+
export const getOrganizationMembersFunc = async (organizationId, page = 1, perPage = 100) => {
|
|
11
|
+
const url = `/oapi/v1/platform/organizations/${organizationId}/members`;
|
|
12
|
+
const params = {
|
|
13
|
+
page: page,
|
|
14
|
+
perPage: perPage
|
|
15
|
+
};
|
|
16
|
+
const urlWithParams = buildUrl(url, params);
|
|
17
|
+
const response = await yunxiaoRequest(urlWithParams, {
|
|
18
|
+
method: "GET",
|
|
19
|
+
});
|
|
20
|
+
// 验证响应数据结构
|
|
21
|
+
return OrganizationMembersSchema.parse(response);
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* 查询组织成员详细信息
|
|
25
|
+
* @param organizationId 组织ID
|
|
26
|
+
* @param memberId 成员ID
|
|
27
|
+
* @returns 组织成员详细信息
|
|
28
|
+
*/
|
|
29
|
+
export const getOrganizationMemberInfoFunc = async (organizationId, memberId) => {
|
|
30
|
+
const url = `/oapi/v1/platform/organizations/${organizationId}/members/${memberId}`;
|
|
31
|
+
console.log("aaa", url);
|
|
32
|
+
const response = await yunxiaoRequest(url, {
|
|
33
|
+
method: "GET",
|
|
34
|
+
});
|
|
35
|
+
return MemberInfoSchema.parse(response);
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* 搜索组织成员
|
|
39
|
+
* @param organizationId 组织ID
|
|
40
|
+
* @param includeChildren
|
|
41
|
+
* @param page 当前页,默认1
|
|
42
|
+
* @param perPage 每页数据条数,默认100
|
|
43
|
+
* @param deptIds
|
|
44
|
+
* @param nextToken
|
|
45
|
+
* @param query
|
|
46
|
+
* @param roleIds
|
|
47
|
+
* @param statuses
|
|
48
|
+
* @returns 搜索到的组织成员列表
|
|
49
|
+
*/
|
|
50
|
+
export const searchOrganizationMembersFunc = async (organizationId, includeChildren = false, page = 1, perPage = 100, deptIds, nextToken, query, roleIds, statuses) => {
|
|
51
|
+
const url = `/oapi/v1/platform/organizations/${organizationId}/members:search`;
|
|
52
|
+
const payload = {
|
|
53
|
+
page: page,
|
|
54
|
+
perPage: perPage
|
|
55
|
+
};
|
|
56
|
+
if (deptIds) {
|
|
57
|
+
payload.deptIds = deptIds;
|
|
58
|
+
}
|
|
59
|
+
if (nextToken) {
|
|
60
|
+
payload.nextToken = nextToken;
|
|
61
|
+
}
|
|
62
|
+
if (query) {
|
|
63
|
+
payload.query = query;
|
|
64
|
+
}
|
|
65
|
+
if (roleIds) {
|
|
66
|
+
payload.roleIds = roleIds;
|
|
67
|
+
}
|
|
68
|
+
if (statuses) {
|
|
69
|
+
payload.statuses = statuses;
|
|
70
|
+
}
|
|
71
|
+
const response = await yunxiaoRequest(url, {
|
|
72
|
+
method: "POST",
|
|
73
|
+
body: payload,
|
|
74
|
+
});
|
|
75
|
+
// 验证响应数据结构
|
|
76
|
+
return SearchOrganizationMembersResultSchema.parse(response);
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* 通过用户ID查询组织成员详细信息
|
|
80
|
+
* @param organizationId 组织ID
|
|
81
|
+
* @param userId 用户ID
|
|
82
|
+
* @returns 组织成员详细信息
|
|
83
|
+
*/
|
|
84
|
+
export const getOrganizationMemberByUserIdInfoFunc = async (organizationId, userId) => {
|
|
85
|
+
const url = `/oapi/v1/platform/organizations/${organizationId}/members:readByUser`;
|
|
86
|
+
const params = {
|
|
87
|
+
userId: userId
|
|
88
|
+
};
|
|
89
|
+
const urlWithParams = buildUrl(url, params);
|
|
90
|
+
const response = await yunxiaoRequest(urlWithParams, {
|
|
91
|
+
method: "GET",
|
|
92
|
+
});
|
|
93
|
+
return MemberInfoSchema.parse(response);
|
|
94
|
+
};
|