devops-mcp-server-extension 1.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/LICENSE +218 -0
- package/README.md +527 -0
- package/README.zh-cn.md +503 -0
- package/dist/common/errors.js +77 -0
- package/dist/common/modularTemplates.js +483 -0
- package/dist/common/pipelineTemplates.js +19 -0
- package/dist/common/toolsetManager.js +123 -0
- package/dist/common/toolsets.js +23 -0
- package/dist/common/types.js +60 -0
- package/dist/common/utils.js +381 -0
- package/dist/common/version.js +1 -0
- package/dist/index.js +225 -0
- package/dist/operations/appstack/appOrchestrations.js +260 -0
- package/dist/operations/appstack/appTags.js +168 -0
- package/dist/operations/appstack/appTemplates.js +72 -0
- package/dist/operations/appstack/applications.js +171 -0
- package/dist/operations/appstack/changeOrders.js +320 -0
- package/dist/operations/appstack/changeRequests.js +288 -0
- package/dist/operations/appstack/deploymentResources.js +286 -0
- package/dist/operations/appstack/globalVars.js +221 -0
- package/dist/operations/appstack/releaseWorkflows.js +695 -0
- package/dist/operations/appstack/variableGroups.js +245 -0
- package/dist/operations/codeup/branches.js +157 -0
- package/dist/operations/codeup/changeRequestComments.js +140 -0
- package/dist/operations/codeup/changeRequests.js +230 -0
- package/dist/operations/codeup/commits.js +121 -0
- package/dist/operations/codeup/compare.js +30 -0
- package/dist/operations/codeup/files.js +249 -0
- package/dist/operations/codeup/repositories.js +71 -0
- package/dist/operations/codeup/types.js +414 -0
- package/dist/operations/flow/hostGroup.js +52 -0
- package/dist/operations/flow/pipeline.js +609 -0
- package/dist/operations/flow/pipelineJob.js +126 -0
- package/dist/operations/flow/resourceMember.js +137 -0
- package/dist/operations/flow/serviceConnection.js +27 -0
- package/dist/operations/flow/tag.js +191 -0
- package/dist/operations/flow/types.js +523 -0
- package/dist/operations/flow/vmDeployOrder.js +171 -0
- package/dist/operations/organization/members.js +106 -0
- package/dist/operations/organization/organization.js +110 -0
- package/dist/operations/organization/types.js +111 -0
- package/dist/operations/packages/artifacts.js +71 -0
- package/dist/operations/packages/repositories.js +39 -0
- package/dist/operations/packages/types.js +56 -0
- package/dist/operations/projex/effort.js +122 -0
- package/dist/operations/projex/project.js +243 -0
- package/dist/operations/projex/sprint.js +103 -0
- package/dist/operations/projex/types.js +618 -0
- package/dist/operations/projex/workitem.js +826 -0
- package/dist/operations/testhub/testcases.js +240 -0
- package/dist/operations/testhub/testplans.js +128 -0
- package/dist/tool-handlers/appstack-app-release-workflows.js +103 -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 +31 -0
- package/dist/tool-handlers/appstack-global-vars.js +37 -0
- package/dist/tool-handlers/appstack-orchestrations.js +49 -0
- package/dist/tool-handlers/appstack-release-workflows.js +37 -0
- package/dist/tool-handlers/appstack-tags.js +37 -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/base.js +25 -0
- package/dist/tool-handlers/code-management.js +150 -0
- package/dist/tool-handlers/commit.js +31 -0
- package/dist/tool-handlers/effort.js +103 -0
- package/dist/tool-handlers/index.js +119 -0
- package/dist/tool-handlers/organization.js +72 -0
- package/dist/tool-handlers/packages.js +32 -0
- package/dist/tool-handlers/pipeline.js +289 -0
- package/dist/tool-handlers/project-management.js +201 -0
- package/dist/tool-handlers/resourceMember.js +43 -0
- package/dist/tool-handlers/service-connections.js +16 -0
- package/dist/tool-handlers/tag.js +64 -0
- package/dist/tool-handlers/test-management.js +74 -0
- package/dist/tool-handlers/vmDeployOrder.js +50 -0
- package/dist/tool-registry/appstack-app-release-workflows.js +80 -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 +20 -0
- package/dist/tool-registry/appstack-global-vars.js +25 -0
- package/dist/tool-registry/appstack-orchestrations.js +35 -0
- package/dist/tool-registry/appstack-release-workflows.js +25 -0
- package/dist/tool-registry/appstack-tags.js +25 -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/base.js +19 -0
- package/dist/tool-registry/code-management.js +109 -0
- package/dist/tool-registry/commit.js +20 -0
- package/dist/tool-registry/effort.js +39 -0
- package/dist/tool-registry/index.js +7 -0
- package/dist/tool-registry/organization.js +65 -0
- package/dist/tool-registry/packages.js +21 -0
- package/dist/tool-registry/pipeline.js +190 -0
- package/dist/tool-registry/project-management.js +143 -0
- package/dist/tool-registry/resourceMember.js +29 -0
- package/dist/tool-registry/service-connections.js +10 -0
- package/dist/tool-registry/tag.js +44 -0
- package/dist/tool-registry/test-management.js +59 -0
- package/dist/tool-registry/vmDeployOrder.js +34 -0
- package/package.json +52 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { yunxiaoRequest, buildUrl, handleRepositoryIdEncoding, floatToIntString, isRegionEdition } from "../../common/utils.js";
|
|
2
|
+
import { resolveOrganizationId } from "../organization/organization.js";
|
|
3
|
+
import { ChangeRequestSchema, PatchSetSchema } from "./types.js";
|
|
4
|
+
// 通过API获取仓库的数字ID
|
|
5
|
+
async function getRepositoryNumericId(organizationId, repositoryId) {
|
|
6
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
7
|
+
const url = isRegionEdition()
|
|
8
|
+
? `/oapi/v1/codeup/repositories/${repositoryId}`
|
|
9
|
+
: `/oapi/v1/codeup/organizations/${finalOrgId}/repositories/${repositoryId}`;
|
|
10
|
+
const response = await yunxiaoRequest(url, {
|
|
11
|
+
method: "GET",
|
|
12
|
+
});
|
|
13
|
+
if (!response || typeof response !== 'object' || !('id' in response)) {
|
|
14
|
+
throw new Error("Failed to get repository ID");
|
|
15
|
+
}
|
|
16
|
+
const repoId = response.id;
|
|
17
|
+
if (!repoId) {
|
|
18
|
+
throw new Error("Could not get repository ID");
|
|
19
|
+
}
|
|
20
|
+
return repoId.toString();
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* 查询合并请求详情
|
|
24
|
+
*
|
|
25
|
+
* @param organizationId 组织ID,示例:'60d54f3daccf2bbd6659f3ad'
|
|
26
|
+
* @param repositoryId 代码库ID或路径,示例:'2835387' 或 '60de7a6852743a5162b5f957%2FDemoRepo'
|
|
27
|
+
* @param localId 合并请求局部ID,示例:'1'
|
|
28
|
+
*/
|
|
29
|
+
export async function getChangeRequestFunc(organizationId, repositoryId, localId) {
|
|
30
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
31
|
+
const encodedRepoId = handleRepositoryIdEncoding(repositoryId);
|
|
32
|
+
const url = isRegionEdition()
|
|
33
|
+
? `/oapi/v1/codeup/repositories/${encodedRepoId}/changeRequests/${localId}`
|
|
34
|
+
: `/oapi/v1/codeup/organizations/${finalOrgId}/repositories/${encodedRepoId}/changeRequests/${localId}`;
|
|
35
|
+
const response = await yunxiaoRequest(url, {
|
|
36
|
+
method: "GET",
|
|
37
|
+
});
|
|
38
|
+
return ChangeRequestSchema.parse(response);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* 查询合并请求列表
|
|
42
|
+
* 支持多条件筛选、分页以及排序
|
|
43
|
+
*
|
|
44
|
+
* @param organizationId 组织ID,示例:'60d54f3daccf2bbd6659f3ad'
|
|
45
|
+
* @param page 页码,从1开始,默认1
|
|
46
|
+
* @param perPage 每页大小,默认20
|
|
47
|
+
* @param projectIds 代码库ID或路径列表,多个以逗号分隔,示例:'2813489,2813490'
|
|
48
|
+
* @param authorIds 创建者用户ID列表,多个以逗号分隔,示例:'62c795xxxb468af8'
|
|
49
|
+
* @param reviewerIds 评审人用户ID列表,多个以逗号分隔,示例:'62c795xxxb468af8'
|
|
50
|
+
* @param state 合并请求筛选状态:'opened' - 已开启;'merged' - 已合并;'closed' - 已关闭。默认为null,即查询全部状态
|
|
51
|
+
* @param search 标题关键字搜索,示例:'mr title'
|
|
52
|
+
* @param orderBy 排序字段:'created_at' - 创建时间;'updated_at' - 更新时间(默认)
|
|
53
|
+
* @param sort 排序方式:'asc' - 升序;'desc' - 降序(默认)
|
|
54
|
+
* @param createdBefore 起始创建时间,ISO 8601格式,示例:'2024-04-05T15:30:45Z'
|
|
55
|
+
* @param createdAfter 截止创建时间,ISO 8601格式,示例:'2024-04-05T15:30:45Z'
|
|
56
|
+
*/
|
|
57
|
+
export async function listChangeRequestsFunc(organizationId, page, perPage, projectIds, authorIds, reviewerIds, state, // Possible values: opened, merged, closed
|
|
58
|
+
search, orderBy, // Possible values: created_at, updated_at
|
|
59
|
+
sort, // Possible values: asc, desc
|
|
60
|
+
createdBefore, createdAfter) {
|
|
61
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
62
|
+
const baseUrl = isRegionEdition()
|
|
63
|
+
? `/oapi/v1/codeup/changeRequests`
|
|
64
|
+
: `/oapi/v1/codeup/organizations/${finalOrgId}/changeRequests`;
|
|
65
|
+
// 构建查询参数
|
|
66
|
+
const queryParams = {};
|
|
67
|
+
if (page !== undefined) {
|
|
68
|
+
queryParams.page = page;
|
|
69
|
+
}
|
|
70
|
+
if (perPage !== undefined) {
|
|
71
|
+
queryParams.perPage = perPage;
|
|
72
|
+
}
|
|
73
|
+
if (projectIds !== undefined) {
|
|
74
|
+
queryParams.projectIds = projectIds;
|
|
75
|
+
}
|
|
76
|
+
if (authorIds !== undefined) {
|
|
77
|
+
queryParams.authorIds = authorIds;
|
|
78
|
+
}
|
|
79
|
+
if (reviewerIds !== undefined) {
|
|
80
|
+
queryParams.reviewerIds = reviewerIds;
|
|
81
|
+
}
|
|
82
|
+
if (state !== undefined) {
|
|
83
|
+
queryParams.state = state;
|
|
84
|
+
}
|
|
85
|
+
if (search !== undefined) {
|
|
86
|
+
queryParams.search = search;
|
|
87
|
+
}
|
|
88
|
+
if (orderBy !== undefined) {
|
|
89
|
+
queryParams.orderBy = orderBy;
|
|
90
|
+
}
|
|
91
|
+
if (sort !== undefined) {
|
|
92
|
+
queryParams.sort = sort;
|
|
93
|
+
}
|
|
94
|
+
if (createdBefore !== undefined) {
|
|
95
|
+
queryParams.createdBefore = createdBefore;
|
|
96
|
+
}
|
|
97
|
+
if (createdAfter !== undefined) {
|
|
98
|
+
queryParams.createdAfter = createdAfter;
|
|
99
|
+
}
|
|
100
|
+
// 使用buildUrl函数构建包含查询参数的URL
|
|
101
|
+
const url = buildUrl(baseUrl, queryParams);
|
|
102
|
+
const response = await yunxiaoRequest(url, {
|
|
103
|
+
method: "GET",
|
|
104
|
+
});
|
|
105
|
+
// 确保响应是数组
|
|
106
|
+
if (!Array.isArray(response)) {
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
// 解析每个变更请求对象
|
|
110
|
+
return response.map(changeRequest => ChangeRequestSchema.parse(changeRequest));
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* 查询合并请求的版本列表(Patch Sets)
|
|
114
|
+
* Patch Sets 表示合并请求在不同时间点的版本快照
|
|
115
|
+
*
|
|
116
|
+
* @param organizationId 组织ID,示例:'60d54f3daccf2bbd6659f3ad'
|
|
117
|
+
* @param repositoryId 代码库ID或路径,示例:'2835387' 或 '60de7a6852743a5162b5f957%2FDemoRepo'
|
|
118
|
+
* @param localId 合并请求局部ID,示例:'1'
|
|
119
|
+
*/
|
|
120
|
+
export async function listChangeRequestPatchSetsFunc(organizationId, repositoryId, localId) {
|
|
121
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
122
|
+
const encodedRepoId = handleRepositoryIdEncoding(repositoryId);
|
|
123
|
+
const url = isRegionEdition()
|
|
124
|
+
? `/oapi/v1/codeup/repositories/${encodedRepoId}/changeRequests/${localId}/diffs/patches`
|
|
125
|
+
: `/oapi/v1/codeup/organizations/${finalOrgId}/repositories/${encodedRepoId}/changeRequests/${localId}/diffs/patches`;
|
|
126
|
+
const response = await yunxiaoRequest(url, {
|
|
127
|
+
method: "GET",
|
|
128
|
+
});
|
|
129
|
+
// 确保响应是数组
|
|
130
|
+
if (!Array.isArray(response)) {
|
|
131
|
+
return [];
|
|
132
|
+
}
|
|
133
|
+
// 解析每个版本对象
|
|
134
|
+
return response.map(patchSet => PatchSetSchema.parse(patchSet));
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* 创建合并请求
|
|
138
|
+
*
|
|
139
|
+
* @param organizationId 组织ID,示例:'60d54f3daccf2bbd6659f3ad'
|
|
140
|
+
* @param repositoryId 代码库ID或路径,示例:'2835387' 或 '60de7a6852743a5162b5f957%2FDemoRepo'
|
|
141
|
+
* @param title 标题,不超过256个字符,示例:'mr title'
|
|
142
|
+
* @param sourceBranch 源分支名称,示例:'demo-branch'
|
|
143
|
+
* @param targetBranch 目标分支名称,示例:'master'
|
|
144
|
+
* @param description 描述,不超过10000个字符(可选),示例:'mr description'
|
|
145
|
+
* @param sourceProjectId 源库ID(可选,未提供时将尝试自动获取),示例:2813489
|
|
146
|
+
* @param targetProjectId 目标库ID(可选,未提供时将尝试自动获取),示例:2813489
|
|
147
|
+
* @param reviewerUserIds 评审人用户ID列表(可选),示例:['62c795xxxb468af8']
|
|
148
|
+
* @param workItemIds 关联工作项ID列表(可选),示例:['workitem-123']
|
|
149
|
+
* @param createFrom 创建来源,默认 'WEB'。可选值:'WEB' - 页面创建;'COMMAND_LINE' - 命令行创建
|
|
150
|
+
* @param triggerAIReviewRun 是否触发AI评审,默认 false
|
|
151
|
+
*/
|
|
152
|
+
export async function createChangeRequestFunc(organizationId, repositoryId, title, sourceBranch, targetBranch, description, sourceProjectId, targetProjectId, reviewerUserIds, workItemIds, createFrom = "WEB", // Possible values: WEB, COMMAND_LINE
|
|
153
|
+
triggerAIReviewRun = false // Whether to trigger AI review
|
|
154
|
+
) {
|
|
155
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
156
|
+
const encodedRepoId = handleRepositoryIdEncoding(repositoryId);
|
|
157
|
+
// 检查和获取sourceProjectId和targetProjectId
|
|
158
|
+
let sourceIdString;
|
|
159
|
+
let targetIdString;
|
|
160
|
+
if (sourceProjectId !== undefined) {
|
|
161
|
+
sourceIdString = floatToIntString(sourceProjectId);
|
|
162
|
+
}
|
|
163
|
+
if (targetProjectId !== undefined) {
|
|
164
|
+
targetIdString = floatToIntString(targetProjectId);
|
|
165
|
+
}
|
|
166
|
+
// 如果repositoryId是纯数字,且sourceProjectId或targetProjectId未提供,直接使用repositoryId的值
|
|
167
|
+
if (!isNaN(Number(repositoryId))) {
|
|
168
|
+
// 是数字ID,可以直接使用
|
|
169
|
+
if (sourceIdString === undefined) {
|
|
170
|
+
sourceIdString = repositoryId;
|
|
171
|
+
}
|
|
172
|
+
if (targetIdString === undefined) {
|
|
173
|
+
targetIdString = repositoryId;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
else if (repositoryId.includes("%2F") || repositoryId.includes("/")) {
|
|
177
|
+
// 如果是组织ID与仓库名称的组合,调用API获取数字ID
|
|
178
|
+
if (sourceIdString === undefined || targetIdString === undefined) {
|
|
179
|
+
try {
|
|
180
|
+
const numericId = await getRepositoryNumericId(organizationId, encodedRepoId);
|
|
181
|
+
if (sourceIdString === undefined) {
|
|
182
|
+
sourceIdString = numericId;
|
|
183
|
+
}
|
|
184
|
+
if (targetIdString === undefined) {
|
|
185
|
+
targetIdString = numericId;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
throw new Error(`When using 'organizationId%2Frepo-name' format, you must first get the numeric ID of the repository and use it for sourceProjectId and targetProjectId parameters. Please use get_repository tool to get the numeric ID of '${repositoryId}' and then use that ID as the value for sourceProjectId and targetProjectId.`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
// 确保sourceProjectId和targetProjectId已设置
|
|
194
|
+
if (sourceIdString === undefined) {
|
|
195
|
+
throw new Error("Could not get sourceProjectId, please provide this parameter manually");
|
|
196
|
+
}
|
|
197
|
+
if (targetIdString === undefined) {
|
|
198
|
+
throw new Error("Could not get targetProjectId, please provide this parameter manually");
|
|
199
|
+
}
|
|
200
|
+
const url = isRegionEdition()
|
|
201
|
+
? `/oapi/v1/codeup/repositories/${encodedRepoId}/changeRequests`
|
|
202
|
+
: `/oapi/v1/codeup/organizations/${finalOrgId}/repositories/${encodedRepoId}/changeRequests`;
|
|
203
|
+
// 准备payload
|
|
204
|
+
const payload = {
|
|
205
|
+
title: title,
|
|
206
|
+
sourceBranch: sourceBranch,
|
|
207
|
+
targetBranch: targetBranch,
|
|
208
|
+
sourceProjectId: sourceIdString,
|
|
209
|
+
targetProjectId: targetIdString,
|
|
210
|
+
createFrom: createFrom,
|
|
211
|
+
};
|
|
212
|
+
// 添加可选参数
|
|
213
|
+
if (description !== undefined) {
|
|
214
|
+
payload.description = description;
|
|
215
|
+
}
|
|
216
|
+
if (reviewerUserIds !== undefined) {
|
|
217
|
+
payload.reviewerUserIds = reviewerUserIds;
|
|
218
|
+
}
|
|
219
|
+
if (workItemIds !== undefined) {
|
|
220
|
+
payload.workItemIds = workItemIds;
|
|
221
|
+
}
|
|
222
|
+
if (triggerAIReviewRun !== undefined) {
|
|
223
|
+
payload.triggerAIReviewRun = triggerAIReviewRun;
|
|
224
|
+
}
|
|
225
|
+
const response = await yunxiaoRequest(url, {
|
|
226
|
+
method: "POST",
|
|
227
|
+
body: payload,
|
|
228
|
+
});
|
|
229
|
+
return ChangeRequestSchema.parse(response);
|
|
230
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { yunxiaoRequest, buildUrl, handleRepositoryIdEncoding, isRegionEdition } from "../../common/utils.js";
|
|
3
|
+
import { resolveOrganizationId } from "../organization/organization.js";
|
|
4
|
+
import { DevopsCommitVOSchema as DevopsCommitVOSchemaType } from "./types.js";
|
|
5
|
+
// Commit schemas
|
|
6
|
+
export const ListCommitsRequestSchema = z.object({
|
|
7
|
+
organizationId: z.string().describe("组织ID"),
|
|
8
|
+
repositoryId: z.string().describe("代码库ID或者URL-Encoder编码的全路径"),
|
|
9
|
+
refName: z.string().describe("分支名称、标签名称或提交版本,默认为代码库默认分支"),
|
|
10
|
+
since: z.string().optional().describe("提交起始时间,格式:YYYY-MM-DDTHH:MM:SSZ"),
|
|
11
|
+
until: z.string().optional().describe("提交截止时间,格式:YYYY-MM-DDTHH:MM:SSZ"),
|
|
12
|
+
page: z.number().int().optional().describe("页码"),
|
|
13
|
+
perPage: z.number().int().optional().describe("每页大小"),
|
|
14
|
+
path: z.string().optional().describe("文件路径"),
|
|
15
|
+
search: z.string().optional().describe("搜索关键字"),
|
|
16
|
+
showSignature: z.boolean().optional().describe("是否展示签名"),
|
|
17
|
+
committerIds: z.string().optional().describe("提交人ID列表(多个ID以逗号隔开)"),
|
|
18
|
+
});
|
|
19
|
+
export const GetCommitRequestSchema = z.object({
|
|
20
|
+
organizationId: z.string().describe("组织ID"),
|
|
21
|
+
repositoryId: z.string().describe("代码库ID或者URL-Encoder编码的全路径"),
|
|
22
|
+
sha: z.string().describe("提交ID,即Commit SHA值"),
|
|
23
|
+
});
|
|
24
|
+
export const CreateCommitCommentRequestSchema = z.object({
|
|
25
|
+
organizationId: z.string().describe("组织ID"),
|
|
26
|
+
repositoryId: z.string().describe("代码库ID或者URL-Encoder编码的全路径"),
|
|
27
|
+
sha: z.string().describe("提交的SHA值"),
|
|
28
|
+
content: z.string().describe("commit的评论内容"),
|
|
29
|
+
});
|
|
30
|
+
// Response schemas
|
|
31
|
+
export const DevopsCommitVOSchema = z.object({
|
|
32
|
+
id: z.string().nullable().optional().describe("提交ID"),
|
|
33
|
+
shortId: z.string().nullable().optional().describe("代码组路径"),
|
|
34
|
+
title: z.string().nullable().optional().describe("标题,提交的第一行内容"),
|
|
35
|
+
message: z.string().nullable().optional().describe("提交内容"),
|
|
36
|
+
authorName: z.string().nullable().optional().describe("作者姓名"),
|
|
37
|
+
authorEmail: z.string().nullable().optional().describe("作者邮箱"),
|
|
38
|
+
authoredDate: z.string().nullable().optional().describe("作者提交时间"),
|
|
39
|
+
committerName: z.string().nullable().optional().describe("提交者姓名"),
|
|
40
|
+
committerEmail: z.string().nullable().optional().describe("提交者邮箱"),
|
|
41
|
+
committedDate: z.string().nullable().optional().describe("提交者提交时间"),
|
|
42
|
+
webUrl: z.string().nullable().optional().describe("页面访问地址"),
|
|
43
|
+
parentIds: z.array(z.string()).nullable().optional().describe("父提交ID"),
|
|
44
|
+
});
|
|
45
|
+
export const DevopsCommitStatVOSchema = z.object({
|
|
46
|
+
additions: z.number().int().nullable().optional().describe("增加行数"),
|
|
47
|
+
deletions: z.number().int().nullable().optional().describe("删除行数"),
|
|
48
|
+
total: z.number().int().nullable().optional().describe("总变动行数"),
|
|
49
|
+
});
|
|
50
|
+
export const CreateCommitCommentVOSchema = z.object({
|
|
51
|
+
content: z.string().describe("commit的评论内容"),
|
|
52
|
+
});
|
|
53
|
+
// API functions
|
|
54
|
+
export async function listCommits(params) {
|
|
55
|
+
const { organizationId, repositoryId, refName, since, until, page, perPage, path, search, showSignature, committerIds } = params;
|
|
56
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
57
|
+
const encodedRepoId = handleRepositoryIdEncoding(repositoryId);
|
|
58
|
+
const baseUrl = isRegionEdition()
|
|
59
|
+
? `/oapi/v1/codeup/repositories/${encodedRepoId}/commits`
|
|
60
|
+
: `/oapi/v1/codeup/organizations/${finalOrgId}/repositories/${encodedRepoId}/commits`;
|
|
61
|
+
const queryParams = {
|
|
62
|
+
refName: refName
|
|
63
|
+
};
|
|
64
|
+
if (since !== undefined) {
|
|
65
|
+
queryParams.since = since;
|
|
66
|
+
}
|
|
67
|
+
if (until !== undefined) {
|
|
68
|
+
queryParams.until = until;
|
|
69
|
+
}
|
|
70
|
+
if (page !== undefined) {
|
|
71
|
+
queryParams.page = page;
|
|
72
|
+
}
|
|
73
|
+
if (perPage !== undefined) {
|
|
74
|
+
queryParams.perPage = perPage;
|
|
75
|
+
}
|
|
76
|
+
if (path !== undefined) {
|
|
77
|
+
queryParams.path = path;
|
|
78
|
+
}
|
|
79
|
+
if (search !== undefined) {
|
|
80
|
+
queryParams.search = search;
|
|
81
|
+
}
|
|
82
|
+
if (showSignature !== undefined) {
|
|
83
|
+
queryParams.showSignature = String(showSignature);
|
|
84
|
+
}
|
|
85
|
+
if (committerIds !== undefined) {
|
|
86
|
+
queryParams.committerIds = committerIds;
|
|
87
|
+
}
|
|
88
|
+
const url = buildUrl(baseUrl, queryParams);
|
|
89
|
+
const response = await yunxiaoRequest(url, {
|
|
90
|
+
method: "GET",
|
|
91
|
+
});
|
|
92
|
+
if (!Array.isArray(response)) {
|
|
93
|
+
return [];
|
|
94
|
+
}
|
|
95
|
+
return response.map(commit => DevopsCommitVOSchemaType.parse(commit));
|
|
96
|
+
}
|
|
97
|
+
export async function getCommit(params) {
|
|
98
|
+
const { organizationId, repositoryId, sha } = params;
|
|
99
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
100
|
+
const encodedRepoId = handleRepositoryIdEncoding(repositoryId);
|
|
101
|
+
const url = isRegionEdition()
|
|
102
|
+
? `/oapi/v1/codeup/repositories/${encodedRepoId}/commits/${sha}`
|
|
103
|
+
: `/oapi/v1/codeup/organizations/${finalOrgId}/repositories/${encodedRepoId}/commits/${sha}`;
|
|
104
|
+
const response = await yunxiaoRequest(url, {
|
|
105
|
+
method: "GET",
|
|
106
|
+
});
|
|
107
|
+
return DevopsCommitVOSchemaType.parse(response);
|
|
108
|
+
}
|
|
109
|
+
export async function createCommitComment(params) {
|
|
110
|
+
const { organizationId, repositoryId, sha, content } = params;
|
|
111
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
112
|
+
const encodedRepoId = handleRepositoryIdEncoding(repositoryId);
|
|
113
|
+
const url = isRegionEdition()
|
|
114
|
+
? `/oapi/v1/codeup/repositories/${encodedRepoId}/commits/${sha}/comments`
|
|
115
|
+
: `/oapi/v1/codeup/organizations/${finalOrgId}/repositories/${encodedRepoId}/commits/${sha}/comments`;
|
|
116
|
+
const response = await yunxiaoRequest(url, {
|
|
117
|
+
method: "POST",
|
|
118
|
+
body: { content }
|
|
119
|
+
});
|
|
120
|
+
return response;
|
|
121
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { yunxiaoRequest, buildUrl, handleRepositoryIdEncoding, isRegionEdition } from "../../common/utils.js";
|
|
2
|
+
import { resolveOrganizationId } from "../organization/organization.js";
|
|
3
|
+
import { CompareSchema } from "./types.js";
|
|
4
|
+
export async function getCompareFunc(organizationId, repositoryId, from, to, sourceType, // Possible values: branch, tag
|
|
5
|
+
targetType, // Possible values: branch, tag
|
|
6
|
+
straight) {
|
|
7
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
8
|
+
const encodedRepoId = handleRepositoryIdEncoding(repositoryId);
|
|
9
|
+
const baseUrl = isRegionEdition()
|
|
10
|
+
? `/oapi/v1/codeup/repositories/${encodedRepoId}/compares`
|
|
11
|
+
: `/oapi/v1/codeup/organizations/${finalOrgId}/repositories/${encodedRepoId}/compares`;
|
|
12
|
+
const queryParams = {
|
|
13
|
+
from,
|
|
14
|
+
to
|
|
15
|
+
};
|
|
16
|
+
if (sourceType !== undefined) {
|
|
17
|
+
queryParams.sourceType = sourceType;
|
|
18
|
+
}
|
|
19
|
+
if (targetType !== undefined) {
|
|
20
|
+
queryParams.targetType = targetType;
|
|
21
|
+
}
|
|
22
|
+
if (straight !== undefined) {
|
|
23
|
+
queryParams.straight = straight;
|
|
24
|
+
}
|
|
25
|
+
const url = buildUrl(baseUrl, queryParams);
|
|
26
|
+
const response = await yunxiaoRequest(url, {
|
|
27
|
+
method: "GET",
|
|
28
|
+
});
|
|
29
|
+
return CompareSchema.parse(response);
|
|
30
|
+
}
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import { yunxiaoRequest, buildUrl, pathEscape, isRegionEdition } from "../../common/utils.js";
|
|
2
|
+
import { resolveOrganizationId } from "../organization/organization.js";
|
|
3
|
+
import { FileContentSchema, CreateFileResponseSchema, DeleteFileResponseSchema, FileInfoSchema } from "./types.js";
|
|
4
|
+
// Common helper function to handle repositoryId and filePath encoding
|
|
5
|
+
function handlePathEncoding(repositoryId, filePath) {
|
|
6
|
+
let encodedRepoId = repositoryId;
|
|
7
|
+
let encodedFilePath = filePath;
|
|
8
|
+
// 自动处理repositoryId中未编码的斜杠
|
|
9
|
+
if (repositoryId.includes("/")) {
|
|
10
|
+
// 发现未编码的斜杠,自动进行URL编码
|
|
11
|
+
const parts = repositoryId.split("/", 2);
|
|
12
|
+
if (parts.length === 2) {
|
|
13
|
+
const encodedRepoName = encodeURIComponent(parts[1]);
|
|
14
|
+
// 移除编码中的+号(空格被编码为+,但我们需要%20)
|
|
15
|
+
const formattedEncodedName = encodedRepoName.replace(/\+/g, "%20");
|
|
16
|
+
encodedRepoId = `${parts[0]}%2F${formattedEncodedName}`;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
// 确保filePath已被URL编码
|
|
20
|
+
if (filePath.includes("/")) {
|
|
21
|
+
const startsWithSlash = filePath.startsWith("/");
|
|
22
|
+
if (startsWithSlash) {
|
|
23
|
+
encodedFilePath = encodedFilePath.substring(1);
|
|
24
|
+
}
|
|
25
|
+
encodedFilePath = encodeURIComponent(filePath);
|
|
26
|
+
}
|
|
27
|
+
return { encodedRepoId, encodedFilePath };
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* 查询文件内容
|
|
31
|
+
* @param organizationId
|
|
32
|
+
* @param repositoryId
|
|
33
|
+
* @param filePath
|
|
34
|
+
* @param ref
|
|
35
|
+
*/
|
|
36
|
+
export async function getFileBlobsFunc(organizationId, repositoryId, filePath, ref) {
|
|
37
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
38
|
+
// const { encodedRepoId, encodedFilePath } = handlePathEncoding(repositoryId, filePath);
|
|
39
|
+
let encodedRepoId = repositoryId;
|
|
40
|
+
let encodedFilePath = filePath;
|
|
41
|
+
// 自动处理repositoryId中未编码的斜杠
|
|
42
|
+
if (repositoryId.includes("/")) {
|
|
43
|
+
// 发现未编码的斜杠,自动进行URL编码
|
|
44
|
+
const parts = repositoryId.split("/", 2);
|
|
45
|
+
if (parts.length === 2) {
|
|
46
|
+
const encodedRepoName = encodeURIComponent(parts[1]);
|
|
47
|
+
// 移除编码中的+号(空格被编码为+,但我们需要%20)
|
|
48
|
+
const formattedEncodedName = encodedRepoName.replace(/\+/g, "%20");
|
|
49
|
+
encodedRepoId = `${parts[0]}%2F${formattedEncodedName}`;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// 确保filePath已被URL编码
|
|
53
|
+
if (filePath.includes("/")) {
|
|
54
|
+
encodedFilePath = encodeURIComponent(filePath);
|
|
55
|
+
}
|
|
56
|
+
const baseUrl = isRegionEdition()
|
|
57
|
+
? `/oapi/v1/codeup/repositories/${encodedRepoId}/files/${encodedFilePath}`
|
|
58
|
+
: `/oapi/v1/codeup/organizations/${finalOrgId}/repositories/${encodedRepoId}/files/${encodedFilePath}`;
|
|
59
|
+
// 构建查询参数
|
|
60
|
+
const queryParams = {
|
|
61
|
+
ref: ref
|
|
62
|
+
};
|
|
63
|
+
// 使用buildUrl函数构建包含查询参数的URL
|
|
64
|
+
const url = buildUrl(baseUrl, queryParams);
|
|
65
|
+
const response = await yunxiaoRequest(url, {
|
|
66
|
+
method: "GET",
|
|
67
|
+
});
|
|
68
|
+
return FileContentSchema.parse(response);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* 创建文件
|
|
72
|
+
* @param organizationId
|
|
73
|
+
* @param repositoryId
|
|
74
|
+
* @param filePath
|
|
75
|
+
* @param content
|
|
76
|
+
* @param commitMessage
|
|
77
|
+
* @param branch
|
|
78
|
+
* @param encoding
|
|
79
|
+
*/
|
|
80
|
+
export async function createFileFunc(organizationId, repositoryId, filePath, content, commitMessage, branch, encoding) {
|
|
81
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
82
|
+
let encodedRepoId = repositoryId;
|
|
83
|
+
let encodedFilePath = filePath;
|
|
84
|
+
if (repositoryId.includes("/")) {
|
|
85
|
+
// 发现未编码的斜杠,自动进行URL编码
|
|
86
|
+
const parts = repositoryId.split("/", 2);
|
|
87
|
+
if (parts.length === 2) {
|
|
88
|
+
const encodedRepoName = encodeURIComponent(parts[1]);
|
|
89
|
+
// 移除编码中的+号(空格被编码为+,但我们需要%20)
|
|
90
|
+
const formattedEncodedName = encodedRepoName.replace(/\+/g, "%20");
|
|
91
|
+
encodedRepoId = `${parts[0]}%2F${formattedEncodedName}`;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// 确保filePath已被URL编码
|
|
95
|
+
if (filePath.includes("/")) {
|
|
96
|
+
encodedFilePath = pathEscape(filePath);
|
|
97
|
+
}
|
|
98
|
+
const url = isRegionEdition()
|
|
99
|
+
? `/oapi/v1/codeup/repositories/${encodedRepoId}/files`
|
|
100
|
+
: `/oapi/v1/codeup/organizations/${finalOrgId}/repositories/${encodedRepoId}/files`;
|
|
101
|
+
const body = {
|
|
102
|
+
branch: branch,
|
|
103
|
+
filePath: encodedFilePath,
|
|
104
|
+
content: content,
|
|
105
|
+
commitMessage: commitMessage,
|
|
106
|
+
encoding: encoding || "text" // 默认使用text编码
|
|
107
|
+
};
|
|
108
|
+
const response = await yunxiaoRequest(url, {
|
|
109
|
+
method: "POST",
|
|
110
|
+
body: body
|
|
111
|
+
});
|
|
112
|
+
return CreateFileResponseSchema.parse(response);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* 更新文件内容
|
|
116
|
+
* @param organizationId
|
|
117
|
+
* @param repositoryId
|
|
118
|
+
* @param filePath
|
|
119
|
+
* @param content
|
|
120
|
+
* @param commitMessage
|
|
121
|
+
* @param branch
|
|
122
|
+
* @param encoding
|
|
123
|
+
*/
|
|
124
|
+
export async function updateFileFunc(organizationId, repositoryId, filePath, content, commitMessage, branch, encoding) {
|
|
125
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
126
|
+
//const { encodedRepoId, encodedFilePath } = handlePathEncoding(repositoryId, filePath);
|
|
127
|
+
let encodedRepoId = repositoryId;
|
|
128
|
+
let encodedFilePath = filePath;
|
|
129
|
+
// 自动处理repositoryId中未编码的斜杠
|
|
130
|
+
if (repositoryId.includes("/")) {
|
|
131
|
+
// 发现未编码的斜杠,自动进行URL编码
|
|
132
|
+
const parts = repositoryId.split("/", 2);
|
|
133
|
+
if (parts.length === 2) {
|
|
134
|
+
const encodedRepoName = encodeURIComponent(parts[1]);
|
|
135
|
+
// 移除编码中的+号(空格被编码为+,但我们需要%20)
|
|
136
|
+
const formattedEncodedName = encodedRepoName.replace(/\+/g, "%20");
|
|
137
|
+
encodedRepoId = `${parts[0]}%2F${formattedEncodedName}`;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// 确保filePath已被URL编码
|
|
141
|
+
if (filePath.includes("/")) {
|
|
142
|
+
const pathToEncode = filePath.startsWith("/") ? filePath.substring(1) : filePath;
|
|
143
|
+
encodedFilePath = encodeURIComponent(pathToEncode);
|
|
144
|
+
}
|
|
145
|
+
const url = isRegionEdition()
|
|
146
|
+
? `/oapi/v1/codeup/repositories/${encodedRepoId}/files/${encodedFilePath}`
|
|
147
|
+
: `/oapi/v1/codeup/organizations/${finalOrgId}/repositories/${encodedRepoId}/files/${encodedFilePath}`;
|
|
148
|
+
const body = {
|
|
149
|
+
branch: branch,
|
|
150
|
+
commitMessage: commitMessage,
|
|
151
|
+
content: content,
|
|
152
|
+
encoding: encoding || "text" // 默认使用text编码
|
|
153
|
+
};
|
|
154
|
+
const response = await yunxiaoRequest(url, {
|
|
155
|
+
method: "PUT",
|
|
156
|
+
body: body
|
|
157
|
+
});
|
|
158
|
+
return CreateFileResponseSchema.parse(response);
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* 删除文件
|
|
162
|
+
* @param organizationId
|
|
163
|
+
* @param repositoryId
|
|
164
|
+
* @param filePath
|
|
165
|
+
* @param commitMessage
|
|
166
|
+
* @param branch
|
|
167
|
+
*/
|
|
168
|
+
export async function deleteFileFunc(organizationId, repositoryId, filePath, commitMessage, branch) {
|
|
169
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
170
|
+
let encodedRepoId = repositoryId;
|
|
171
|
+
let encodedFilePath = filePath;
|
|
172
|
+
if (repositoryId.includes("/")) {
|
|
173
|
+
// 发现未编码的斜杠,自动进行URL编码
|
|
174
|
+
const parts = repositoryId.split("/", 2);
|
|
175
|
+
if (parts.length === 2) {
|
|
176
|
+
const encodedRepoName = encodeURIComponent(parts[1]);
|
|
177
|
+
// 移除编码中的+号(空格被编码为+,但我们需要%20)
|
|
178
|
+
const formattedEncodedName = encodedRepoName.replace(/\+/g, "%20");
|
|
179
|
+
encodedRepoId = `${parts[0]}%2F${formattedEncodedName}`;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
// 确保filePath已被URL编码
|
|
183
|
+
if (filePath.includes("/")) {
|
|
184
|
+
encodedFilePath = encodeURIComponent(filePath);
|
|
185
|
+
}
|
|
186
|
+
const baseUrl = isRegionEdition()
|
|
187
|
+
? `/oapi/v1/codeup/repositories/${encodedRepoId}/files/${encodedFilePath}`
|
|
188
|
+
: `/oapi/v1/codeup/organizations/${finalOrgId}/repositories/${encodedRepoId}/files/${encodedFilePath}`;
|
|
189
|
+
// 构建查询参数
|
|
190
|
+
const queryParams = {
|
|
191
|
+
branch: branch,
|
|
192
|
+
commitMessage: commitMessage
|
|
193
|
+
};
|
|
194
|
+
// 使用buildUrl函数构建包含查询参数的URL
|
|
195
|
+
const url = buildUrl(baseUrl, queryParams);
|
|
196
|
+
const response = await yunxiaoRequest(url, {
|
|
197
|
+
method: "DELETE",
|
|
198
|
+
});
|
|
199
|
+
return DeleteFileResponseSchema.parse(response);
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* 查询文件树
|
|
203
|
+
* @param organizationId
|
|
204
|
+
* @param repositoryId
|
|
205
|
+
* @param path
|
|
206
|
+
* @param ref
|
|
207
|
+
* @param type
|
|
208
|
+
*/
|
|
209
|
+
export async function listFilesFunc(organizationId, repositoryId, path, ref, type // Possible values: DIRECT, RECURSIVE, FLATTEN
|
|
210
|
+
) {
|
|
211
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
212
|
+
// 自动处理repositoryId中未编码的斜杠
|
|
213
|
+
let encodedRepoId = repositoryId;
|
|
214
|
+
if (repositoryId.includes("/")) {
|
|
215
|
+
// 发现未编码的斜杠,自动进行URL编码
|
|
216
|
+
const parts = repositoryId.split("/", 2);
|
|
217
|
+
if (parts.length === 2) {
|
|
218
|
+
const encodedRepoName = encodeURIComponent(parts[1]);
|
|
219
|
+
// 移除编码中的+号(空格被编码为+,但我们需要%20)
|
|
220
|
+
const formattedEncodedName = encodedRepoName.replace(/\+/g, "%20");
|
|
221
|
+
encodedRepoId = `${parts[0]}%2F${formattedEncodedName}`;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
const baseUrl = isRegionEdition()
|
|
225
|
+
? `/oapi/v1/codeup/repositories/${encodedRepoId}/files/tree`
|
|
226
|
+
: `/oapi/v1/codeup/organizations/${finalOrgId}/repositories/${encodedRepoId}/files/tree`;
|
|
227
|
+
// 构建查询参数
|
|
228
|
+
const queryParams = {};
|
|
229
|
+
if (path) {
|
|
230
|
+
queryParams.path = path;
|
|
231
|
+
}
|
|
232
|
+
if (ref) {
|
|
233
|
+
queryParams.ref = ref;
|
|
234
|
+
}
|
|
235
|
+
if (type) {
|
|
236
|
+
queryParams.type = type;
|
|
237
|
+
}
|
|
238
|
+
// 使用buildUrl函数构建包含查询参数的URL
|
|
239
|
+
const url = buildUrl(baseUrl, queryParams);
|
|
240
|
+
const response = await yunxiaoRequest(url, {
|
|
241
|
+
method: "GET",
|
|
242
|
+
});
|
|
243
|
+
// 确保响应是数组
|
|
244
|
+
if (!Array.isArray(response)) {
|
|
245
|
+
return [];
|
|
246
|
+
}
|
|
247
|
+
// 解析每个文件信息对象
|
|
248
|
+
return response.map(fileInfo => FileInfoSchema.parse(fileInfo));
|
|
249
|
+
}
|