@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,483 @@
|
|
|
1
|
+
import { yunxiaoRequest, buildUrl, pathEscape } from "../../common/utils.js";
|
|
2
|
+
import { FileContentSchema, CreateFileResponseSchema, DeleteFileResponseSchema, FileInfoSchema } from "./types.js";
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
import pkg from 'fs-extra';
|
|
5
|
+
import * as path from 'path';
|
|
6
|
+
import minimatch from "minimatch";
|
|
7
|
+
const { removeSync } = pkg;
|
|
8
|
+
function readGitignore(projectPath) {
|
|
9
|
+
const gitignorePath = path.join(projectPath, ".gitignore");
|
|
10
|
+
try {
|
|
11
|
+
const gitignoreContent = fs.readFileSync(gitignorePath, "utf-8");
|
|
12
|
+
const originIgnorePatterns = gitignoreContent
|
|
13
|
+
.split("\n")
|
|
14
|
+
.filter((line) => line.trim() !== "" && !line.startsWith("#"))
|
|
15
|
+
.map((pattern) => pattern.trim());
|
|
16
|
+
originIgnorePatterns.push(".git");
|
|
17
|
+
return originIgnorePatterns;
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
console.warn("No .gitignore file found or error reading it");
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function shouldIgnoreFile(filePath, ignorePatterns) {
|
|
25
|
+
const relativePath = path.relative(process.cwd(), filePath);
|
|
26
|
+
return ignorePatterns.some((pattern) => minimatch(relativePath, pattern) ||
|
|
27
|
+
minimatch(path.basename(filePath), pattern));
|
|
28
|
+
}
|
|
29
|
+
// 检测文件是否为二进制文件
|
|
30
|
+
function isBinaryFile(filePath) {
|
|
31
|
+
const binaryExtensions = [
|
|
32
|
+
".jpg",
|
|
33
|
+
".jpeg",
|
|
34
|
+
".png",
|
|
35
|
+
".gif",
|
|
36
|
+
".bmp",
|
|
37
|
+
".webp",
|
|
38
|
+
".svg",
|
|
39
|
+
".ico",
|
|
40
|
+
".pdf",
|
|
41
|
+
".doc",
|
|
42
|
+
".docx",
|
|
43
|
+
".xls",
|
|
44
|
+
".xlsx",
|
|
45
|
+
".ppt",
|
|
46
|
+
".pptx",
|
|
47
|
+
".zip",
|
|
48
|
+
".rar",
|
|
49
|
+
".7z",
|
|
50
|
+
".tar",
|
|
51
|
+
".gz",
|
|
52
|
+
".mp3",
|
|
53
|
+
".mp4",
|
|
54
|
+
".avi",
|
|
55
|
+
".mov",
|
|
56
|
+
".wmv",
|
|
57
|
+
".exe",
|
|
58
|
+
".dll",
|
|
59
|
+
".so",
|
|
60
|
+
".dylib",
|
|
61
|
+
".woff",
|
|
62
|
+
".woff2",
|
|
63
|
+
".ttf",
|
|
64
|
+
".eot"
|
|
65
|
+
];
|
|
66
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
67
|
+
return binaryExtensions.includes(ext);
|
|
68
|
+
}
|
|
69
|
+
function getAllFiles(dir, ignorePatterns) {
|
|
70
|
+
const files = [];
|
|
71
|
+
function traverseDirectory(currentPath) {
|
|
72
|
+
const entries = fs.readdirSync(currentPath);
|
|
73
|
+
for (const entry of entries) {
|
|
74
|
+
const fullPath = path.join(currentPath, entry);
|
|
75
|
+
const stat = fs.statSync(fullPath);
|
|
76
|
+
if (stat.isDirectory()) {
|
|
77
|
+
// 递归遍历子目录
|
|
78
|
+
if (!shouldIgnoreFile(fullPath, ignorePatterns)) {
|
|
79
|
+
traverseDirectory(fullPath);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
// 检查是否应该忽略文件
|
|
84
|
+
if (!shouldIgnoreFile(fullPath, ignorePatterns)) {
|
|
85
|
+
try {
|
|
86
|
+
const relativePath = path.relative(dir, fullPath);
|
|
87
|
+
let content;
|
|
88
|
+
let encoding = "text";
|
|
89
|
+
if (isBinaryFile(fullPath)) {
|
|
90
|
+
// 二进制文件使用base64编码
|
|
91
|
+
content = fs.readFileSync(fullPath).toString("base64");
|
|
92
|
+
encoding = "base64";
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
// 文本文件使用utf-8编码
|
|
96
|
+
content = fs.readFileSync(fullPath, "utf-8");
|
|
97
|
+
encoding = "text";
|
|
98
|
+
}
|
|
99
|
+
files.push({
|
|
100
|
+
path: relativePath,
|
|
101
|
+
content: content,
|
|
102
|
+
encoding: encoding
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
// 对于无法读取的文件,跳过
|
|
107
|
+
console.warn(`Could not read file: ${fullPath}`, error);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
traverseDirectory(dir);
|
|
114
|
+
return files;
|
|
115
|
+
}
|
|
116
|
+
// Common helper function to handle repositoryId and filePath encoding
|
|
117
|
+
function handlePathEncoding(repositoryId, filePath) {
|
|
118
|
+
let encodedRepoId = repositoryId;
|
|
119
|
+
let encodedFilePath = filePath;
|
|
120
|
+
// 自动处理repositoryId中未编码的斜杠
|
|
121
|
+
if (repositoryId.includes("/")) {
|
|
122
|
+
// 发现未编码的斜杠,自动进行URL编码
|
|
123
|
+
const parts = repositoryId.split("/", 2);
|
|
124
|
+
if (parts.length === 2) {
|
|
125
|
+
const encodedRepoName = encodeURIComponent(parts[1]);
|
|
126
|
+
// 移除编码中的+号(空格被编码为+,但我们需要%20)
|
|
127
|
+
const formattedEncodedName = encodedRepoName.replace(/\+/g, "%20");
|
|
128
|
+
encodedRepoId = `${parts[0]}%2F${formattedEncodedName}`;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// 确保filePath已被URL编码
|
|
132
|
+
if (filePath.includes("/")) {
|
|
133
|
+
const startsWithSlash = filePath.startsWith("/");
|
|
134
|
+
if (startsWithSlash) {
|
|
135
|
+
encodedFilePath = encodedFilePath.substring(1);
|
|
136
|
+
}
|
|
137
|
+
encodedFilePath = encodeURIComponent(filePath);
|
|
138
|
+
}
|
|
139
|
+
return { encodedRepoId, encodedFilePath };
|
|
140
|
+
}
|
|
141
|
+
export async function clearProjectPath(projectPath) {
|
|
142
|
+
try {
|
|
143
|
+
// 检查路径是否存在
|
|
144
|
+
if (!fs.existsSync(projectPath)) {
|
|
145
|
+
console.warn(`目录不存在: ${projectPath}`);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
// 确保是一个目录
|
|
149
|
+
const stats = fs.statSync(projectPath);
|
|
150
|
+
if (!stats.isDirectory()) {
|
|
151
|
+
throw new Error(`提供的路径不是一个目录: ${projectPath}`);
|
|
152
|
+
}
|
|
153
|
+
// 读取目录内容
|
|
154
|
+
const files = fs.readdirSync(projectPath);
|
|
155
|
+
// 遍历并删除所有文件和子目录
|
|
156
|
+
for (const file of files) {
|
|
157
|
+
const currentPath = path.join(projectPath, file);
|
|
158
|
+
removeSync(currentPath);
|
|
159
|
+
}
|
|
160
|
+
console.log(`已成功清空目录: ${projectPath}`);
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
if (error instanceof Error) {
|
|
164
|
+
console.error(`清空目录时发生错误: ${error.message}`);
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
console.error(`清空目录时发生未知错误: ${String(error)}`);
|
|
168
|
+
}
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* 查询文件内容
|
|
174
|
+
* @param organizationId
|
|
175
|
+
* @param repositoryId
|
|
176
|
+
* @param filePath
|
|
177
|
+
* @param ref
|
|
178
|
+
*/
|
|
179
|
+
export async function getFileBlobsFunc(organizationId, repositoryId, filePath, ref) {
|
|
180
|
+
// const { encodedRepoId, encodedFilePath } = handlePathEncoding(repositoryId, filePath);
|
|
181
|
+
let encodedRepoId = repositoryId;
|
|
182
|
+
let encodedFilePath = filePath;
|
|
183
|
+
// 自动处理repositoryId中未编码的斜杠
|
|
184
|
+
if (repositoryId.includes("/")) {
|
|
185
|
+
// 发现未编码的斜杠,自动进行URL编码
|
|
186
|
+
const parts = repositoryId.split("/", 2);
|
|
187
|
+
if (parts.length === 2) {
|
|
188
|
+
const encodedRepoName = encodeURIComponent(parts[1]);
|
|
189
|
+
// 移除编码中的+号(空格被编码为+,但我们需要%20)
|
|
190
|
+
const formattedEncodedName = encodedRepoName.replace(/\+/g, "%20");
|
|
191
|
+
encodedRepoId = `${parts[0]}%2F${formattedEncodedName}`;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// 确保filePath已被URL编码
|
|
195
|
+
if (filePath.includes("/")) {
|
|
196
|
+
encodedFilePath = encodeURIComponent(filePath);
|
|
197
|
+
}
|
|
198
|
+
const baseUrl = `/oapi/v1/codeup/organizations/${organizationId}/repositories/${encodedRepoId}/files/${encodedFilePath}`;
|
|
199
|
+
// 构建查询参数
|
|
200
|
+
const queryParams = {
|
|
201
|
+
ref: ref
|
|
202
|
+
};
|
|
203
|
+
// 使用buildUrl函数构建包含查询参数的URL
|
|
204
|
+
const url = buildUrl(baseUrl, queryParams);
|
|
205
|
+
const response = await yunxiaoRequest(url, {
|
|
206
|
+
method: "GET"
|
|
207
|
+
});
|
|
208
|
+
return FileContentSchema.parse(response);
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* 创建文件
|
|
212
|
+
* @param organizationId
|
|
213
|
+
* @param repositoryId
|
|
214
|
+
* @param filePath
|
|
215
|
+
* @param content
|
|
216
|
+
* @param commitMessage
|
|
217
|
+
* @param branch
|
|
218
|
+
* @param encoding
|
|
219
|
+
*/
|
|
220
|
+
export async function createFileFunc(organizationId, repositoryId, filePath, content, commitMessage, branch, encoding) {
|
|
221
|
+
let encodedRepoId = repositoryId;
|
|
222
|
+
let encodedFilePath = filePath;
|
|
223
|
+
if (repositoryId.includes("/")) {
|
|
224
|
+
// 发现未编码的斜杠,自动进行URL编码
|
|
225
|
+
const parts = repositoryId.split("/", 2);
|
|
226
|
+
if (parts.length === 2) {
|
|
227
|
+
const encodedRepoName = encodeURIComponent(parts[1]);
|
|
228
|
+
// 移除编码中的+号(空格被编码为+,但我们需要%20)
|
|
229
|
+
const formattedEncodedName = encodedRepoName.replace(/\+/g, "%20");
|
|
230
|
+
encodedRepoId = `${parts[0]}%2F${formattedEncodedName}`;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
// 确保filePath已被URL编码
|
|
234
|
+
if (filePath.includes("/")) {
|
|
235
|
+
encodedFilePath = pathEscape(filePath);
|
|
236
|
+
}
|
|
237
|
+
const url = `/oapi/v1/codeup/organizations/${organizationId}/repositories/${encodedRepoId}/files`;
|
|
238
|
+
const body = {
|
|
239
|
+
branch: branch,
|
|
240
|
+
filePath: encodedFilePath,
|
|
241
|
+
content: content,
|
|
242
|
+
commitMessage: commitMessage,
|
|
243
|
+
encoding: encoding || "text" // 默认使用text编码
|
|
244
|
+
};
|
|
245
|
+
const response = await yunxiaoRequest(url, {
|
|
246
|
+
method: "POST",
|
|
247
|
+
body: body
|
|
248
|
+
});
|
|
249
|
+
return CreateFileResponseSchema.parse(response);
|
|
250
|
+
}
|
|
251
|
+
export async function pushFilesFunc(organizationId, repositoryId, commitMessage, projectPath) {
|
|
252
|
+
// 读取 .gitignore 模式
|
|
253
|
+
// const ignorePatterns = readGitignore(projectPath);
|
|
254
|
+
const ignorePatterns = [
|
|
255
|
+
'dist',
|
|
256
|
+
'.next',
|
|
257
|
+
'node_modules',
|
|
258
|
+
'.git',
|
|
259
|
+
'.vercel',
|
|
260
|
+
'.cache',
|
|
261
|
+
'build',
|
|
262
|
+
'out',
|
|
263
|
+
'.nuxt',
|
|
264
|
+
'.svelte-kit',
|
|
265
|
+
'package-lock.json',
|
|
266
|
+
'.DS_Store'
|
|
267
|
+
];
|
|
268
|
+
// 获取所有文件
|
|
269
|
+
const files = getAllFiles(projectPath, ignorePatterns);
|
|
270
|
+
const remoteFileList = await listFilesFunc(organizationId, repositoryId, undefined, "master", "RECURSIVE");
|
|
271
|
+
const remoteFilePathArr = remoteFileList.map((i) => i.path);
|
|
272
|
+
const shouldCreateFiles = files.filter((file) => !remoteFilePathArr.includes(file.path));
|
|
273
|
+
const shouldUpdateFiles = files.filter((file) => remoteFilePathArr.includes(file.path));
|
|
274
|
+
const shouldDeleteFiles = remoteFileList.filter((remoteFile) => !files.some((file) => file.path === remoteFile.path));
|
|
275
|
+
const createFilesSuccess = [];
|
|
276
|
+
const createFileErrors = [];
|
|
277
|
+
const updateFilesSuccess = [];
|
|
278
|
+
const updateFileErrors = [];
|
|
279
|
+
const deleteFilesSuccess = [];
|
|
280
|
+
const deleteFileErrors = [];
|
|
281
|
+
for (let i of shouldCreateFiles) {
|
|
282
|
+
console.error("[创建文件]:", i.path);
|
|
283
|
+
try {
|
|
284
|
+
const result = await createFileFunc(organizationId, repositoryId, i.path, i.content, commitMessage, "master", i.encoding);
|
|
285
|
+
createFilesSuccess.push(result.filePath);
|
|
286
|
+
}
|
|
287
|
+
catch (error) {
|
|
288
|
+
createFileErrors.push(error.message);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
for (let i of shouldUpdateFiles) {
|
|
292
|
+
console.error("[更新文件]:", i.path);
|
|
293
|
+
try {
|
|
294
|
+
const result = await updateFileFunc(organizationId, repositoryId, i.path, i.content, commitMessage, "master", i.encoding);
|
|
295
|
+
updateFilesSuccess.push(result.filePath);
|
|
296
|
+
}
|
|
297
|
+
catch (error) {
|
|
298
|
+
updateFileErrors.push(error.message);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
for (let i of shouldDeleteFiles) {
|
|
302
|
+
console.error("[删除文件]:", i.path);
|
|
303
|
+
try {
|
|
304
|
+
const result = await deleteFileFunc(organizationId, repositoryId, i.path, commitMessage, "master");
|
|
305
|
+
deleteFilesSuccess.push(result.filePath);
|
|
306
|
+
}
|
|
307
|
+
catch (error) {
|
|
308
|
+
deleteFileErrors.push(error.message);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return {
|
|
312
|
+
createFilesSuccess,
|
|
313
|
+
createFileErrors,
|
|
314
|
+
updateFilesSuccess,
|
|
315
|
+
updateFileErrors,
|
|
316
|
+
deleteFilesSuccess,
|
|
317
|
+
deleteFileErrors
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* 拉取整个代码库文件
|
|
322
|
+
* @param organizationId
|
|
323
|
+
* @param repositoryId
|
|
324
|
+
* @param projectPath
|
|
325
|
+
*/
|
|
326
|
+
export async function pullFilesFunc(organizationId, repositoryId, currntProjectPath) {
|
|
327
|
+
// 拉取之前 需要将当前projectPath路径下的所有代码文件清空
|
|
328
|
+
await clearProjectPath(currntProjectPath);
|
|
329
|
+
const allRemoteFilesPath = await listFilesFunc(organizationId, repositoryId, undefined, "master", "RECURSIVE");
|
|
330
|
+
const allFiles = [];
|
|
331
|
+
for (let i of allRemoteFilesPath) {
|
|
332
|
+
try {
|
|
333
|
+
const fileBlob = await getFileBlobsFunc(organizationId, repositoryId, i.path, "master");
|
|
334
|
+
allFiles.push(fileBlob);
|
|
335
|
+
// 写入文件到本地目录
|
|
336
|
+
const fullPath = path.join(currntProjectPath, i.path);
|
|
337
|
+
// 确保目录存在
|
|
338
|
+
const fileDir = path.dirname(fullPath);
|
|
339
|
+
fs.mkdirSync(fileDir, { recursive: true });
|
|
340
|
+
// 根据编码写入文件
|
|
341
|
+
if (fileBlob.encoding === 'base64') {
|
|
342
|
+
const buffer = Buffer.from(fileBlob.content || '', 'base64');
|
|
343
|
+
fs.writeFileSync(fullPath, buffer);
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
fs.writeFileSync(fullPath, fileBlob.content || '', 'utf-8');
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
catch (error) {
|
|
350
|
+
console.error('[获取文件内容失败]:', error);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return allRemoteFilesPath;
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* 更新文件内容
|
|
357
|
+
* @param organizationId
|
|
358
|
+
* @param repositoryId
|
|
359
|
+
* @param filePath
|
|
360
|
+
* @param content
|
|
361
|
+
* @param commitMessage
|
|
362
|
+
* @param branch
|
|
363
|
+
* @param encoding
|
|
364
|
+
*/
|
|
365
|
+
export async function updateFileFunc(organizationId, repositoryId, filePath, content, commitMessage, branch, encoding) {
|
|
366
|
+
//const { encodedRepoId, encodedFilePath } = handlePathEncoding(repositoryId, filePath);
|
|
367
|
+
let encodedRepoId = repositoryId;
|
|
368
|
+
let encodedFilePath = filePath;
|
|
369
|
+
// 自动处理repositoryId中未编码的斜杠
|
|
370
|
+
if (repositoryId.includes("/")) {
|
|
371
|
+
// 发现未编码的斜杠,自动进行URL编码
|
|
372
|
+
const parts = repositoryId.split("/", 2);
|
|
373
|
+
if (parts.length === 2) {
|
|
374
|
+
const encodedRepoName = encodeURIComponent(parts[1]);
|
|
375
|
+
// 移除编码中的+号(空格被编码为+,但我们需要%20)
|
|
376
|
+
const formattedEncodedName = encodedRepoName.replace(/\+/g, "%20");
|
|
377
|
+
encodedRepoId = `${parts[0]}%2F${formattedEncodedName}`;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
// 确保filePath已被URL编码
|
|
381
|
+
if (filePath.includes("/")) {
|
|
382
|
+
const pathToEncode = filePath.startsWith("/")
|
|
383
|
+
? filePath.substring(1)
|
|
384
|
+
: filePath;
|
|
385
|
+
encodedFilePath = encodeURIComponent(pathToEncode);
|
|
386
|
+
}
|
|
387
|
+
const url = `/oapi/v1/codeup/organizations/${organizationId}/repositories/${encodedRepoId}/files/${encodedFilePath}`;
|
|
388
|
+
const body = {
|
|
389
|
+
branch: branch,
|
|
390
|
+
commitMessage: commitMessage,
|
|
391
|
+
content: content,
|
|
392
|
+
encoding: encoding || "text" // 默认使用text编码
|
|
393
|
+
};
|
|
394
|
+
const response = await yunxiaoRequest(url, {
|
|
395
|
+
method: "PUT",
|
|
396
|
+
body: body
|
|
397
|
+
});
|
|
398
|
+
return CreateFileResponseSchema.parse(response);
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* 删除文件
|
|
402
|
+
* @param organizationId
|
|
403
|
+
* @param repositoryId
|
|
404
|
+
* @param filePath
|
|
405
|
+
* @param commitMessage
|
|
406
|
+
* @param branch
|
|
407
|
+
*/
|
|
408
|
+
export async function deleteFileFunc(organizationId, repositoryId, filePath, commitMessage, branch) {
|
|
409
|
+
let encodedRepoId = repositoryId;
|
|
410
|
+
let encodedFilePath = filePath;
|
|
411
|
+
if (repositoryId.includes("/")) {
|
|
412
|
+
// 发现未编码的斜杠,自动进行URL编码
|
|
413
|
+
const parts = repositoryId.split("/", 2);
|
|
414
|
+
if (parts.length === 2) {
|
|
415
|
+
const encodedRepoName = encodeURIComponent(parts[1]);
|
|
416
|
+
// 移除编码中的+号(空格被编码为+,但我们需要%20)
|
|
417
|
+
const formattedEncodedName = encodedRepoName.replace(/\+/g, "%20");
|
|
418
|
+
encodedRepoId = `${parts[0]}%2F${formattedEncodedName}`;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
// 确保filePath已被URL编码
|
|
422
|
+
if (filePath.includes("/")) {
|
|
423
|
+
encodedFilePath = encodeURIComponent(filePath);
|
|
424
|
+
}
|
|
425
|
+
const baseUrl = `/oapi/v1/codeup/organizations/${organizationId}/repositories/${encodedRepoId}/files/${encodedFilePath}`;
|
|
426
|
+
// 构建查询参数
|
|
427
|
+
const queryParams = {
|
|
428
|
+
branch: branch,
|
|
429
|
+
commitMessage: commitMessage
|
|
430
|
+
};
|
|
431
|
+
// 使用buildUrl函数构建包含查询参数的URL
|
|
432
|
+
const url = buildUrl(baseUrl, queryParams);
|
|
433
|
+
const response = await yunxiaoRequest(url, {
|
|
434
|
+
method: "DELETE"
|
|
435
|
+
});
|
|
436
|
+
return DeleteFileResponseSchema.parse(response);
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* 查询文件树
|
|
440
|
+
* @param organizationId
|
|
441
|
+
* @param repositoryId
|
|
442
|
+
* @param path
|
|
443
|
+
* @param ref
|
|
444
|
+
* @param type
|
|
445
|
+
*/
|
|
446
|
+
export async function listFilesFunc(organizationId, repositoryId, path, ref, type // Possible values: DIRECT, RECURSIVE, FLATTEN
|
|
447
|
+
) {
|
|
448
|
+
// 自动处理repositoryId中未编码的斜杠
|
|
449
|
+
let encodedRepoId = repositoryId;
|
|
450
|
+
if (repositoryId.includes("/")) {
|
|
451
|
+
// 发现未编码的斜杠,自动进行URL编码
|
|
452
|
+
const parts = repositoryId.split("/", 2);
|
|
453
|
+
if (parts.length === 2) {
|
|
454
|
+
const encodedRepoName = encodeURIComponent(parts[1]);
|
|
455
|
+
// 移除编码中的+号(空格被编码为+,但我们需要%20)
|
|
456
|
+
const formattedEncodedName = encodedRepoName.replace(/\+/g, "%20");
|
|
457
|
+
encodedRepoId = `${parts[0]}%2F${formattedEncodedName}`;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
const baseUrl = `/oapi/v1/codeup/organizations/${organizationId}/repositories/${encodedRepoId}/files/tree`;
|
|
461
|
+
// 构建查询参数
|
|
462
|
+
const queryParams = {};
|
|
463
|
+
if (path) {
|
|
464
|
+
queryParams.path = path;
|
|
465
|
+
}
|
|
466
|
+
if (ref) {
|
|
467
|
+
queryParams.ref = ref;
|
|
468
|
+
}
|
|
469
|
+
if (type) {
|
|
470
|
+
queryParams.type = type;
|
|
471
|
+
}
|
|
472
|
+
// 使用buildUrl函数构建包含查询参数的URL
|
|
473
|
+
const url = buildUrl(baseUrl, queryParams);
|
|
474
|
+
const response = await yunxiaoRequest(url, {
|
|
475
|
+
method: "GET"
|
|
476
|
+
});
|
|
477
|
+
// 确保响应是数组
|
|
478
|
+
if (!Array.isArray(response)) {
|
|
479
|
+
return [];
|
|
480
|
+
}
|
|
481
|
+
// 解析每个文件信息对象
|
|
482
|
+
return response.map((fileInfo) => FileInfoSchema.parse(fileInfo));
|
|
483
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 代码库(Repository)相关操作
|
|
3
|
+
*
|
|
4
|
+
* 概念说明:
|
|
5
|
+
* - 代码库(Repository)是云效平台中的代码管理单元,属于CodeUp产品
|
|
6
|
+
* - 代码库与项目(Project)是不同的概念,项目属于项目管理领域
|
|
7
|
+
* - 代码库用于存储和管理源代码,而项目用于管理工作项、迭代等
|
|
8
|
+
* - 请勿混淆这两个概念,它们是不同的资源类型
|
|
9
|
+
*/
|
|
10
|
+
import { yunxiaoRequest, buildUrl, handleRepositoryIdEncoding } from "../../common/utils.js";
|
|
11
|
+
import { RepositorySchema } from "./types.js";
|
|
12
|
+
/**
|
|
13
|
+
* 创建仓库
|
|
14
|
+
* @param organizationId
|
|
15
|
+
* @param name
|
|
16
|
+
* @param description
|
|
17
|
+
*/
|
|
18
|
+
export async function createRepositoryFunc(organizationId, name, description) {
|
|
19
|
+
const url = `/oapi/v1/codeup/organizations/${organizationId}/repositories?createParentPath=true`;
|
|
20
|
+
const response = await yunxiaoRequest(url, {
|
|
21
|
+
method: "POST",
|
|
22
|
+
body: {
|
|
23
|
+
name,
|
|
24
|
+
description,
|
|
25
|
+
namespaceId: 1670786,
|
|
26
|
+
path: name
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
return RepositorySchema.parse(response);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* 查询仓库详情
|
|
33
|
+
* @param organizationId
|
|
34
|
+
* @param repositoryId
|
|
35
|
+
*/
|
|
36
|
+
export async function getRepositoryFunc(organizationId, repositoryId) {
|
|
37
|
+
const encodedRepoId = handleRepositoryIdEncoding(repositoryId);
|
|
38
|
+
const url = `/oapi/v1/codeup/organizations/${organizationId}/repositories/${encodedRepoId}`;
|
|
39
|
+
const response = await yunxiaoRequest(url, {
|
|
40
|
+
method: "GET"
|
|
41
|
+
});
|
|
42
|
+
return RepositorySchema.parse(response);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 查询仓库列表
|
|
46
|
+
* @param organizationId
|
|
47
|
+
* @param page
|
|
48
|
+
* @param perPage
|
|
49
|
+
* @param orderBy
|
|
50
|
+
* @param sort
|
|
51
|
+
* @param search
|
|
52
|
+
* @param archived
|
|
53
|
+
*/
|
|
54
|
+
export async function listRepositoriesFunc(organizationId, page, perPage, orderBy, sort, search, archived) {
|
|
55
|
+
const baseUrl = `/oapi/v1/codeup/organizations/${organizationId}/repositories`;
|
|
56
|
+
const queryParams = {};
|
|
57
|
+
if (page !== undefined) {
|
|
58
|
+
queryParams.page = page;
|
|
59
|
+
}
|
|
60
|
+
if (perPage !== undefined) {
|
|
61
|
+
queryParams.perPage = perPage;
|
|
62
|
+
}
|
|
63
|
+
if (orderBy !== undefined) {
|
|
64
|
+
queryParams.orderBy = orderBy;
|
|
65
|
+
}
|
|
66
|
+
if (sort !== undefined) {
|
|
67
|
+
queryParams.sort = sort;
|
|
68
|
+
}
|
|
69
|
+
if (search !== undefined) {
|
|
70
|
+
queryParams.search = search;
|
|
71
|
+
}
|
|
72
|
+
if (archived !== undefined) {
|
|
73
|
+
queryParams.archived = String(archived); // Convert boolean to string
|
|
74
|
+
}
|
|
75
|
+
const url = buildUrl(baseUrl, queryParams);
|
|
76
|
+
const response = await yunxiaoRequest(url, {
|
|
77
|
+
method: "GET"
|
|
78
|
+
});
|
|
79
|
+
if (!Array.isArray(response)) {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
return response.map((repo) => RepositorySchema.parse(repo));
|
|
83
|
+
}
|