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,609 @@
|
|
|
1
|
+
import * as utils from "../../common/utils.js";
|
|
2
|
+
import { resolveOrganizationId } from "../organization/organization.js";
|
|
3
|
+
import { PipelineDetailSchema, PipelineListItemSchema, PipelineRunSchema, PipelineRunListItemSchema } from "./types.js";
|
|
4
|
+
import { generateModularPipeline } from "../../common/modularTemplates.js";
|
|
5
|
+
import { listServiceConnectionsFunc } from "./serviceConnection.js";
|
|
6
|
+
import { listHostGroupsFunc } from "./hostGroup.js";
|
|
7
|
+
/**
|
|
8
|
+
* 获取流水线详情
|
|
9
|
+
* @param organizationId 组织ID
|
|
10
|
+
* @param pipelineId 流水线ID
|
|
11
|
+
* @returns 流水线详情
|
|
12
|
+
*/
|
|
13
|
+
export async function getPipelineFunc(organizationId, pipelineId) {
|
|
14
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
15
|
+
const url = utils.isRegionEdition()
|
|
16
|
+
? `/oapi/v1/flow/pipelines/${pipelineId}`
|
|
17
|
+
: `/oapi/v1/flow/organizations/${finalOrgId}/pipelines/${pipelineId}`;
|
|
18
|
+
const response = await utils.yunxiaoRequest(url, {
|
|
19
|
+
method: "GET",
|
|
20
|
+
});
|
|
21
|
+
return PipelineDetailSchema.parse(response);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* 获取流水线列表
|
|
25
|
+
* @param organizationId 组织ID
|
|
26
|
+
* @param options 查询选项
|
|
27
|
+
* @returns 流水线列表
|
|
28
|
+
*/
|
|
29
|
+
export async function listPipelinesFunc(organizationId, options) {
|
|
30
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
31
|
+
const baseUrl = utils.isRegionEdition()
|
|
32
|
+
? `/oapi/v1/flow/pipelines`
|
|
33
|
+
: `/oapi/v1/flow/organizations/${finalOrgId}/pipelines`;
|
|
34
|
+
// 构建查询参数
|
|
35
|
+
const queryParams = {};
|
|
36
|
+
// 处理时间戳参数
|
|
37
|
+
// 如果传入的是日期字符串或Date对象,自动转换为毫秒时间戳
|
|
38
|
+
if (options?.createStartTime !== undefined) {
|
|
39
|
+
queryParams.createStartTime = utils.convertToTimestamp(options.createStartTime);
|
|
40
|
+
}
|
|
41
|
+
if (options?.createEndTime !== undefined) {
|
|
42
|
+
queryParams.createEndTime = utils.convertToTimestamp(options.createEndTime);
|
|
43
|
+
}
|
|
44
|
+
if (options?.executeStartTime !== undefined) {
|
|
45
|
+
queryParams.executeStartTime = utils.convertToTimestamp(options.executeStartTime);
|
|
46
|
+
}
|
|
47
|
+
if (options?.executeEndTime !== undefined) {
|
|
48
|
+
queryParams.executeEndTime = utils.convertToTimestamp(options.executeEndTime);
|
|
49
|
+
}
|
|
50
|
+
if (options?.pipelineName !== undefined) {
|
|
51
|
+
queryParams.pipelineName = options.pipelineName;
|
|
52
|
+
}
|
|
53
|
+
if (options?.statusList !== undefined) {
|
|
54
|
+
queryParams.statusList = options.statusList;
|
|
55
|
+
}
|
|
56
|
+
if (options?.perPage !== undefined) {
|
|
57
|
+
queryParams.perPage = options.perPage;
|
|
58
|
+
}
|
|
59
|
+
if (options?.page !== undefined) {
|
|
60
|
+
queryParams.page = options.page;
|
|
61
|
+
}
|
|
62
|
+
const url = utils.buildUrl(baseUrl, queryParams);
|
|
63
|
+
const response = await utils.yunxiaoRequest(url, {
|
|
64
|
+
method: "GET",
|
|
65
|
+
});
|
|
66
|
+
const pagination = {
|
|
67
|
+
nextPage: null,
|
|
68
|
+
page: 1,
|
|
69
|
+
perPage: 10,
|
|
70
|
+
prevPage: null,
|
|
71
|
+
total: 0,
|
|
72
|
+
totalPages: 0
|
|
73
|
+
};
|
|
74
|
+
let items = [];
|
|
75
|
+
if (Array.isArray(response)) {
|
|
76
|
+
items = response.map(item => PipelineListItemSchema.parse(item));
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
items,
|
|
80
|
+
pagination
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* 智能查询流水线列表,能够解析自然语言中的时间表达
|
|
85
|
+
* @param organizationId 组织ID
|
|
86
|
+
* @param timeReference 自然语言时间引用,如"今天"、"本周"、"上个月"
|
|
87
|
+
* @param options 其他查询选项
|
|
88
|
+
* @returns 流水线列表
|
|
89
|
+
*/
|
|
90
|
+
export async function smartListPipelinesFunc(organizationId, timeReference, options) {
|
|
91
|
+
// 解析时间引用获取开始和结束时间戳
|
|
92
|
+
const { startTime, endTime } = utils.parseDateReference(timeReference);
|
|
93
|
+
// 合并选项
|
|
94
|
+
const fullOptions = {
|
|
95
|
+
...options,
|
|
96
|
+
executeStartTime: startTime,
|
|
97
|
+
executeEndTime: endTime
|
|
98
|
+
};
|
|
99
|
+
return listPipelinesFunc(organizationId, fullOptions);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* 运行流水线
|
|
103
|
+
* @param organizationId 组织ID
|
|
104
|
+
* @param pipelineId 流水线ID
|
|
105
|
+
* @param options 运行选项,可以是直接的JSON字符串或者自然语言描述的选项
|
|
106
|
+
* @returns 流水线运行ID
|
|
107
|
+
*/
|
|
108
|
+
export async function createPipelineRunFunc(organizationId, pipelineId, options) {
|
|
109
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
110
|
+
const url = utils.isRegionEdition()
|
|
111
|
+
? `/oapi/v1/flow/pipelines/${pipelineId}/runs`
|
|
112
|
+
: `/oapi/v1/flow/organizations/${finalOrgId}/pipelines/${pipelineId}/runs`;
|
|
113
|
+
// 如果用户已经提供了格式化的params,直接使用
|
|
114
|
+
if (options?.params) {
|
|
115
|
+
const body = {
|
|
116
|
+
params: options.params
|
|
117
|
+
};
|
|
118
|
+
const response = await utils.yunxiaoRequest(url, {
|
|
119
|
+
method: "POST",
|
|
120
|
+
body: body,
|
|
121
|
+
});
|
|
122
|
+
return Number(response);
|
|
123
|
+
}
|
|
124
|
+
// 否则,基于用户提供的简化参数构建params
|
|
125
|
+
const paramsObject = {};
|
|
126
|
+
// ========== 步骤1:提前获取并填充 repositories 信息 ==========
|
|
127
|
+
// 如果用户没有提供 repositories 或提供的是空数组,且提供了 branch 或 tag,则从流水线配置中获取仓库列表
|
|
128
|
+
if ((!options?.repositories || options.repositories.length === 0) && (options?.branch || options?.tag)) {
|
|
129
|
+
try {
|
|
130
|
+
const pipelineDetail = await getPipelineFunc(organizationId, pipelineId);
|
|
131
|
+
const sources = pipelineDetail.pipelineConfig?.sources || [];
|
|
132
|
+
// 从 sources 中提取仓库URL并构建 repositories 数组
|
|
133
|
+
options.repositories = sources
|
|
134
|
+
.filter(source => source.data?.repo)
|
|
135
|
+
.map(source => ({
|
|
136
|
+
url: source.data.repo,
|
|
137
|
+
branch: options.branch, // 如果有branch参数,应用到所有仓库
|
|
138
|
+
tag: options.tag // 如果有tag参数,应用到所有仓库
|
|
139
|
+
}));
|
|
140
|
+
}
|
|
141
|
+
catch (e) {
|
|
142
|
+
// 获取失败时,保持 repositories 为空,后续会有 fallback 处理
|
|
143
|
+
console.error('[ERROR] Failed to fetch pipeline detail for repositories:', e);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// ========== 步骤2:处理分支模式(多个分支) ==========
|
|
147
|
+
if (options?.branchMode && options?.branches && options.branches.length > 0) {
|
|
148
|
+
paramsObject.branchModeBranchs = options.branches;
|
|
149
|
+
}
|
|
150
|
+
// ========== 处理Release分支相关参数 ==========
|
|
151
|
+
if (options?.createReleaseBranch !== undefined) {
|
|
152
|
+
paramsObject.needCreateBranch = options.createReleaseBranch;
|
|
153
|
+
}
|
|
154
|
+
if (options?.releaseBranch) {
|
|
155
|
+
paramsObject.releaseBranch = options.releaseBranch;
|
|
156
|
+
}
|
|
157
|
+
// ========== 处理环境变量 ==========
|
|
158
|
+
if (options?.environmentVariables && Object.keys(options.environmentVariables).length > 0) {
|
|
159
|
+
paramsObject.envs = options.environmentVariables;
|
|
160
|
+
}
|
|
161
|
+
// ========== 处理制品相关参数 ==========
|
|
162
|
+
if (options?.pipelineArtifacts && Object.keys(options.pipelineArtifacts).length > 0) {
|
|
163
|
+
paramsObject.runningPipelineArtifacts = options.pipelineArtifacts;
|
|
164
|
+
}
|
|
165
|
+
if (options?.acrArtifacts && Object.keys(options.acrArtifacts).length > 0) {
|
|
166
|
+
paramsObject.runningAcrArtifacts = options.acrArtifacts;
|
|
167
|
+
}
|
|
168
|
+
if (options?.packagesArtifacts && Object.keys(options.packagesArtifacts).length > 0) {
|
|
169
|
+
paramsObject.runningPackagesArtifacts = options.packagesArtifacts;
|
|
170
|
+
}
|
|
171
|
+
// ========== 处理特定仓库配置 ==========
|
|
172
|
+
if (options?.repositories && options.repositories.length > 0) {
|
|
173
|
+
// 初始化runningBranchs和runningTags对象
|
|
174
|
+
const runningBranchs = {};
|
|
175
|
+
const runningTags = {};
|
|
176
|
+
// 填充分支和标签信息
|
|
177
|
+
options.repositories.forEach((repo, index) => {
|
|
178
|
+
if (repo.branch) {
|
|
179
|
+
runningBranchs[repo.url] = repo.branch;
|
|
180
|
+
}
|
|
181
|
+
if (repo.tag) {
|
|
182
|
+
runningTags[repo.url] = repo.tag;
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
// 只有在有内容时才添加到params对象
|
|
186
|
+
if (Object.keys(runningBranchs).length > 0) {
|
|
187
|
+
paramsObject.runningBranchs = runningBranchs;
|
|
188
|
+
}
|
|
189
|
+
if (Object.keys(runningTags).length > 0) {
|
|
190
|
+
paramsObject.runningTags = runningTags;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
console.log('[DEBUG] No repositories to process, skipping runningBranchs/runningTags generation');
|
|
195
|
+
}
|
|
196
|
+
if (!paramsObject.runningBranchs && !paramsObject.runningTags) {
|
|
197
|
+
// 如果没有生成 runningBranchs 或 runningTags,且有 branch 参数,使用 fallback
|
|
198
|
+
if (options?.branch && !paramsObject.branchModeBranchs) {
|
|
199
|
+
console.log('[DEBUG] Using fallback: setting branchModeBranchs');
|
|
200
|
+
paramsObject.branchModeBranchs = [options.branch];
|
|
201
|
+
}
|
|
202
|
+
// tag 无法使用 branchModeBranchs fallback,只能记录警告
|
|
203
|
+
if (options?.tag) {
|
|
204
|
+
console.warn('[WARN] Tag parameter provided but no repositories found, tag will be ignored');
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
// ========== 处理备注 ==========
|
|
208
|
+
if (options?.comment) {
|
|
209
|
+
paramsObject.comment = options.comment;
|
|
210
|
+
}
|
|
211
|
+
// ========== 如果有自然语言描述,尝试解析它 ==========
|
|
212
|
+
if (options?.description) {
|
|
213
|
+
// 此处可以添加更复杂的自然语言处理逻辑
|
|
214
|
+
// 当前实现是简单的关键词匹配
|
|
215
|
+
const description = options.description.toLowerCase();
|
|
216
|
+
// 检测分支模式
|
|
217
|
+
if ((description.includes('branch mode') || description.includes('分支模式')) &&
|
|
218
|
+
!paramsObject.branchModeBranchs &&
|
|
219
|
+
options?.branches?.length) {
|
|
220
|
+
paramsObject.branchModeBranchs = options.branches;
|
|
221
|
+
}
|
|
222
|
+
// 检测是否需要创建release分支
|
|
223
|
+
if ((description.includes('create release') || description.includes('创建release')) &&
|
|
224
|
+
paramsObject.needCreateBranch === undefined) {
|
|
225
|
+
paramsObject.needCreateBranch = true;
|
|
226
|
+
}
|
|
227
|
+
// 如果提到特定release分支但没有指定
|
|
228
|
+
if ((description.includes('release branch') || description.includes('release分支')) &&
|
|
229
|
+
!paramsObject.releaseBranch &&
|
|
230
|
+
options?.branches?.length) {
|
|
231
|
+
// 假设第一个分支就是release分支
|
|
232
|
+
paramsObject.releaseBranch = options.branches[0];
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
const body = {};
|
|
236
|
+
if (Object.keys(paramsObject).length > 0) {
|
|
237
|
+
body.params = JSON.stringify(paramsObject);
|
|
238
|
+
console.log('[DEBUG] Final body.params:', body.params);
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
console.log('[DEBUG] paramsObject is empty, no params will be sent');
|
|
242
|
+
}
|
|
243
|
+
const response = await utils.yunxiaoRequest(url, {
|
|
244
|
+
method: "POST",
|
|
245
|
+
body: body,
|
|
246
|
+
});
|
|
247
|
+
return Number(response);
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* 获取最近一次流水线运行信息
|
|
251
|
+
* @param organizationId 组织ID
|
|
252
|
+
* @param pipelineId 流水线ID
|
|
253
|
+
* @returns 最近一次流水线运行信息
|
|
254
|
+
*/
|
|
255
|
+
export async function getLatestPipelineRunFunc(organizationId, pipelineId) {
|
|
256
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
257
|
+
const url = utils.isRegionEdition()
|
|
258
|
+
? `/oapi/v1/flow/pipelines/${pipelineId}/runs/latestPipelineRun`
|
|
259
|
+
: `/oapi/v1/flow/organizations/${finalOrgId}/pipelines/${pipelineId}/runs/latestPipelineRun`;
|
|
260
|
+
const response = await utils.yunxiaoRequest(url, {
|
|
261
|
+
method: "GET",
|
|
262
|
+
});
|
|
263
|
+
return PipelineRunSchema.parse(response);
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* 获取特定流水线运行实例
|
|
267
|
+
* @param organizationId 组织ID
|
|
268
|
+
* @param pipelineId 流水线ID
|
|
269
|
+
* @param pipelineRunId 流水线运行ID
|
|
270
|
+
* @returns 流水线运行实例信息
|
|
271
|
+
*/
|
|
272
|
+
export async function getPipelineRunFunc(organizationId, pipelineId, pipelineRunId) {
|
|
273
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
274
|
+
const url = utils.isRegionEdition()
|
|
275
|
+
? `/oapi/v1/flow/pipelines/${pipelineId}/runs/${pipelineRunId}`
|
|
276
|
+
: `/oapi/v1/flow/organizations/${finalOrgId}/pipelines/${pipelineId}/runs/${pipelineRunId}`;
|
|
277
|
+
const response = await utils.yunxiaoRequest(url, {
|
|
278
|
+
method: "GET",
|
|
279
|
+
});
|
|
280
|
+
return PipelineRunSchema.parse(response);
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* 获取流水线运行实例列表
|
|
284
|
+
* @param organizationId 组织ID
|
|
285
|
+
* @param pipelineId 流水线ID
|
|
286
|
+
* @param options 查询选项
|
|
287
|
+
* @returns 流水线运行实例列表和分页信息
|
|
288
|
+
*/
|
|
289
|
+
export async function listPipelineRunsFunc(organizationId, pipelineId, options) {
|
|
290
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
291
|
+
const baseUrl = utils.isRegionEdition()
|
|
292
|
+
? `/oapi/v1/flow/pipelines/${pipelineId}/runs`
|
|
293
|
+
: `/oapi/v1/flow/organizations/${finalOrgId}/pipelines/${pipelineId}/runs`;
|
|
294
|
+
// 构建查询参数
|
|
295
|
+
const queryParams = {};
|
|
296
|
+
if (options?.perPage !== undefined) {
|
|
297
|
+
queryParams.perPage = options.perPage;
|
|
298
|
+
}
|
|
299
|
+
if (options?.page !== undefined) {
|
|
300
|
+
queryParams.page = options.page;
|
|
301
|
+
}
|
|
302
|
+
if (options?.startTime !== undefined) {
|
|
303
|
+
queryParams.startTime = utils.convertToTimestamp(options.startTime);
|
|
304
|
+
}
|
|
305
|
+
if (options?.endTime !== undefined) {
|
|
306
|
+
queryParams.endTme = utils.convertToTimestamp(options.endTime);
|
|
307
|
+
}
|
|
308
|
+
if (options?.status !== undefined) {
|
|
309
|
+
queryParams.status = options.status;
|
|
310
|
+
}
|
|
311
|
+
if (options?.triggerMode !== undefined) {
|
|
312
|
+
queryParams.triggerMode = options.triggerMode;
|
|
313
|
+
}
|
|
314
|
+
const url = utils.buildUrl(baseUrl, queryParams);
|
|
315
|
+
const response = await utils.yunxiaoRequest(url, {
|
|
316
|
+
method: "GET",
|
|
317
|
+
});
|
|
318
|
+
const pagination = {
|
|
319
|
+
nextPage: null,
|
|
320
|
+
page: options?.page ?? 1,
|
|
321
|
+
perPage: options?.perPage ?? 10,
|
|
322
|
+
prevPage: null,
|
|
323
|
+
total: 0,
|
|
324
|
+
totalPages: 0
|
|
325
|
+
};
|
|
326
|
+
let items = [];
|
|
327
|
+
if (Array.isArray(response)) {
|
|
328
|
+
items = response.map(item => PipelineRunListItemSchema.parse(item));
|
|
329
|
+
}
|
|
330
|
+
return {
|
|
331
|
+
items,
|
|
332
|
+
pagination
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* 创建流水线
|
|
337
|
+
* @param organizationId 组织ID
|
|
338
|
+
* @param name 流水线名称
|
|
339
|
+
* @param content 流水线YAML描述
|
|
340
|
+
* @returns 流水线ID
|
|
341
|
+
*/
|
|
342
|
+
export async function createPipelineFunc(organizationId, name, content) {
|
|
343
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
344
|
+
const url = utils.isRegionEdition()
|
|
345
|
+
? `/oapi/v1/flow/pipelines`
|
|
346
|
+
: `/oapi/v1/flow/organizations/${finalOrgId}/pipelines`;
|
|
347
|
+
const body = {
|
|
348
|
+
name: name,
|
|
349
|
+
content: content
|
|
350
|
+
};
|
|
351
|
+
const response = await utils.yunxiaoRequest(url, {
|
|
352
|
+
method: "POST",
|
|
353
|
+
body: body,
|
|
354
|
+
});
|
|
355
|
+
return Number(response);
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* 基于结构化参数生成流水线YAML(不创建流水线)
|
|
359
|
+
* @param options 结构化的流水线配置选项
|
|
360
|
+
* @returns 生成的YAML字符串
|
|
361
|
+
*/
|
|
362
|
+
export async function generatePipelineYamlFunc(options) {
|
|
363
|
+
// 自动从repoUrl解析serviceName(如果用户没有明确指定)
|
|
364
|
+
let derivedServiceName = options.serviceName;
|
|
365
|
+
if (!derivedServiceName && options.repoUrl) {
|
|
366
|
+
// 从Git URL中提取项目名称
|
|
367
|
+
// 支持格式: git@codeup.aliyun.com:org/repo.git 或 https://codeup.aliyun.com/org/repo.git
|
|
368
|
+
const repoUrlMatch = options.repoUrl.match(/[\/:]([^\/]+)\.git$/);
|
|
369
|
+
if (repoUrlMatch) {
|
|
370
|
+
derivedServiceName = repoUrlMatch[1];
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
// 准备变量,确保版本号有双引号
|
|
374
|
+
const variables = {
|
|
375
|
+
// 基础配置
|
|
376
|
+
...(options.repoUrl && { repoUrl: options.repoUrl }),
|
|
377
|
+
...(options.branch && { branch: options.branch }),
|
|
378
|
+
...(derivedServiceName && { serviceName: derivedServiceName }),
|
|
379
|
+
...(options.serviceConnectionId && { serviceConnectionId: options.serviceConnectionId }),
|
|
380
|
+
...(options.packagesServiceConnection && { packagesServiceConnection: options.packagesServiceConnection }),
|
|
381
|
+
...(options.machineGroupId && { machineGroupId: options.machineGroupId }),
|
|
382
|
+
...(options.namespace && { namespace: options.namespace }),
|
|
383
|
+
...(options.dockerImage && { dockerImage: options.dockerImage }),
|
|
384
|
+
// 版本相关(确保双引号)
|
|
385
|
+
...(options.jdkVersion && { jdkVersion: `"${options.jdkVersion}"` }),
|
|
386
|
+
...(options.mavenVersion && { mavenVersion: `"${options.mavenVersion}"` }),
|
|
387
|
+
...(options.nodeVersion && { nodeVersion: `"${options.nodeVersion}"` }),
|
|
388
|
+
...(options.pythonVersion && { pythonVersion: `"${options.pythonVersion}"` }),
|
|
389
|
+
...(options.goVersion && { goVersion: `"${options.goVersion}"` }),
|
|
390
|
+
...(options.kubectlVersion && { kubectlVersion: `"${options.kubectlVersion}"` }),
|
|
391
|
+
// 构建物上传相关
|
|
392
|
+
...(options.uploadType && { uploadType: options.uploadType }),
|
|
393
|
+
...(options.artifactName && { artifactName: options.artifactName }),
|
|
394
|
+
...(options.artifactVersion && { artifactVersion: options.artifactVersion }),
|
|
395
|
+
...(options.packagesRepoId && { packagesRepoId: options.packagesRepoId }),
|
|
396
|
+
...(options.includePathInArtifact !== undefined && { includePathInArtifact: options.includePathInArtifact }),
|
|
397
|
+
// 部署相关
|
|
398
|
+
...(options.executeUser && { executeUser: options.executeUser }),
|
|
399
|
+
...(options.artifactDownloadPath && { artifactDownloadPath: options.artifactDownloadPath }),
|
|
400
|
+
...(options.kubernetesClusterId && { kubernetesClusterId: options.kubernetesClusterId }),
|
|
401
|
+
...(options.yamlPath && { yamlPath: options.yamlPath }),
|
|
402
|
+
// 命令
|
|
403
|
+
...(options.buildCommand && { buildCommand: options.buildCommand }),
|
|
404
|
+
...(options.testCommand && { testCommand: options.testCommand }),
|
|
405
|
+
...(options.deployCommand && { deployCommand: options.deployCommand }),
|
|
406
|
+
};
|
|
407
|
+
// 转换为模块化流水线选项
|
|
408
|
+
const deployTargets = options.deployTarget ? [options.deployTarget] : [];
|
|
409
|
+
// 使用模块化架构生成YAML
|
|
410
|
+
return generateModularPipeline({
|
|
411
|
+
keywords: [options.buildLanguage, options.buildTool],
|
|
412
|
+
buildLanguages: [options.buildLanguage],
|
|
413
|
+
buildTools: [options.buildTool],
|
|
414
|
+
deployTargets: deployTargets,
|
|
415
|
+
uploadType: options.uploadType || 'packages',
|
|
416
|
+
variables: variables
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* 基于结构化参数创建流水线
|
|
421
|
+
* @param organizationId 组织ID
|
|
422
|
+
* @param options 结构化的流水线配置选项
|
|
423
|
+
* @returns 创建结果,包含流水线ID和生成的YAML
|
|
424
|
+
*/
|
|
425
|
+
export async function createPipelineWithOptionsFunc(organizationId, options) {
|
|
426
|
+
// 解析最终的 organizationId
|
|
427
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
428
|
+
// 获取默认服务连接ID(如果用户没有明确指定)
|
|
429
|
+
let defaultServiceConnectionId = null;
|
|
430
|
+
const hasServiceConnectionId = options.serviceConnectionId;
|
|
431
|
+
if (!hasServiceConnectionId) {
|
|
432
|
+
defaultServiceConnectionId = await getDefaultServiceConnectionId(finalOrgId);
|
|
433
|
+
}
|
|
434
|
+
// 获取默认Packages服务连接ID(如果用户没有明确指定且需要packages上传)
|
|
435
|
+
let defaultPackagesServiceConnectionId = null;
|
|
436
|
+
const hasPackagesServiceConnectionId = options.packagesServiceConnection;
|
|
437
|
+
const needsPackagesUpload = !options.uploadType || options.uploadType === 'packages';
|
|
438
|
+
if (!hasPackagesServiceConnectionId && needsPackagesUpload) {
|
|
439
|
+
defaultPackagesServiceConnectionId = await getDefaultPackagesServiceConnectionId(finalOrgId);
|
|
440
|
+
}
|
|
441
|
+
// 获取默认主机组ID(如果用户没有明确指定且需要VM部署)
|
|
442
|
+
let defaultMachineGroupId = null;
|
|
443
|
+
const hasMachineGroupId = options.machineGroupId;
|
|
444
|
+
const needsVMDeploy = options.deployTarget === 'vm';
|
|
445
|
+
if (!hasMachineGroupId && needsVMDeploy) {
|
|
446
|
+
defaultMachineGroupId = await getDefaultHostGroupId(finalOrgId);
|
|
447
|
+
}
|
|
448
|
+
// 自动从repoUrl解析serviceName(如果用户没有明确指定)
|
|
449
|
+
let derivedServiceName = options.serviceName;
|
|
450
|
+
if (!derivedServiceName && options.repoUrl) {
|
|
451
|
+
// 从Git URL中提取项目名称
|
|
452
|
+
// 支持格式: git@codeup.aliyun.com:org/repo.git 或 https://codeup.aliyun.com/org/repo.git
|
|
453
|
+
const repoUrlMatch = options.repoUrl.match(/[\/:]([^\/]+)\.git$/);
|
|
454
|
+
if (repoUrlMatch) {
|
|
455
|
+
derivedServiceName = repoUrlMatch[1];
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
// 准备模块化流水线生成的变量
|
|
459
|
+
const finalVariables = {
|
|
460
|
+
// 基础配置(直接使用用户提供的值)
|
|
461
|
+
...(options.repoUrl && { repoUrl: options.repoUrl }),
|
|
462
|
+
...(options.branch && { branch: options.branch }),
|
|
463
|
+
...(derivedServiceName && { serviceName: derivedServiceName }),
|
|
464
|
+
// 使用获取到的默认服务连接ID
|
|
465
|
+
...(defaultServiceConnectionId && !hasServiceConnectionId && { serviceConnectionId: defaultServiceConnectionId }),
|
|
466
|
+
// 使用获取到的默认Packages服务连接ID
|
|
467
|
+
...(defaultPackagesServiceConnectionId && !hasPackagesServiceConnectionId && { packagesServiceConnection: defaultPackagesServiceConnectionId }),
|
|
468
|
+
// 使用获取到的默认主机组ID
|
|
469
|
+
...(defaultMachineGroupId && !hasMachineGroupId && { machineGroupId: defaultMachineGroupId }),
|
|
470
|
+
// 用户明确指定的值优先级最高
|
|
471
|
+
...(options.serviceConnectionId && { serviceConnectionId: options.serviceConnectionId }),
|
|
472
|
+
...(options.packagesServiceConnection && { packagesServiceConnection: options.packagesServiceConnection }),
|
|
473
|
+
...(options.machineGroupId && { machineGroupId: options.machineGroupId }),
|
|
474
|
+
...(options.namespace && { namespace: options.namespace }),
|
|
475
|
+
...(options.dockerImage && { dockerImage: options.dockerImage }),
|
|
476
|
+
// 版本相关(确保双引号)
|
|
477
|
+
...(options.jdkVersion && { jdkVersion: `"${options.jdkVersion}"` }),
|
|
478
|
+
...(options.mavenVersion && { mavenVersion: `"${options.mavenVersion}"` }),
|
|
479
|
+
...(options.nodeVersion && { nodeVersion: `"${options.nodeVersion}"` }),
|
|
480
|
+
...(options.pythonVersion && { pythonVersion: `"${options.pythonVersion}"` }),
|
|
481
|
+
...(options.goVersion && { goVersion: `"${options.goVersion}"` }),
|
|
482
|
+
...(options.kubectlVersion && { kubectlVersion: `"${options.kubectlVersion}"` }),
|
|
483
|
+
// 构建物上传相关
|
|
484
|
+
...(options.uploadType && { uploadType: options.uploadType }),
|
|
485
|
+
...(options.artifactName && { artifactName: options.artifactName }),
|
|
486
|
+
...(options.artifactVersion && { artifactVersion: options.artifactVersion }),
|
|
487
|
+
...(options.packagesRepoId && { packagesRepoId: options.packagesRepoId }),
|
|
488
|
+
...(options.includePathInArtifact !== undefined && { includePathInArtifact: options.includePathInArtifact }),
|
|
489
|
+
// 部署相关
|
|
490
|
+
...(options.executeUser && { executeUser: options.executeUser }),
|
|
491
|
+
...(options.artifactDownloadPath && { artifactDownloadPath: options.artifactDownloadPath }),
|
|
492
|
+
...(options.kubernetesClusterId && { kubernetesClusterId: options.kubernetesClusterId }),
|
|
493
|
+
...(options.yamlPath && { yamlPath: options.yamlPath }),
|
|
494
|
+
// 命令
|
|
495
|
+
...(options.buildCommand && { buildCommand: options.buildCommand }),
|
|
496
|
+
...(options.testCommand && { testCommand: options.testCommand }),
|
|
497
|
+
...(options.deployCommand && { deployCommand: options.deployCommand }),
|
|
498
|
+
};
|
|
499
|
+
// 转换为模块化流水线选项
|
|
500
|
+
const deployTargets = options.deployTarget ? [options.deployTarget] : [];
|
|
501
|
+
// 使用模块化架构生成YAML
|
|
502
|
+
const generatedYaml = generateModularPipeline({
|
|
503
|
+
keywords: [options.buildLanguage, options.buildTool],
|
|
504
|
+
buildLanguages: [options.buildLanguage],
|
|
505
|
+
buildTools: [options.buildTool],
|
|
506
|
+
deployTargets: deployTargets,
|
|
507
|
+
uploadType: options.uploadType || 'packages',
|
|
508
|
+
variables: finalVariables
|
|
509
|
+
});
|
|
510
|
+
// 创建流水线
|
|
511
|
+
try {
|
|
512
|
+
const pipelineId = await createPipelineFunc(organizationId, options.name, generatedYaml);
|
|
513
|
+
return {
|
|
514
|
+
pipelineId,
|
|
515
|
+
generatedYaml
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
catch (error) {
|
|
519
|
+
// 如果是YAML校验失败或其他流水线创建错误,将详细信息透出给用户
|
|
520
|
+
console.error('Create pipeline failed:', error);
|
|
521
|
+
// 构造包含生成YAML的错误信息,方便用户排查
|
|
522
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
523
|
+
const enhancedError = new Error(`Create pipeline failed: ${errorMessage}\n\n` +
|
|
524
|
+
`YAML content:\n${generatedYaml}\n\n` +
|
|
525
|
+
`Suggestions:\n` +
|
|
526
|
+
`1. Check whether the YAML format is correct.\n` +
|
|
527
|
+
`2. Verify whether the serviceConnectionID、machineGroupID、kubernetesClusterID and other parameters are existed and valid.`);
|
|
528
|
+
// 保持原始错误的堆栈信息
|
|
529
|
+
if (error instanceof Error && error.stack) {
|
|
530
|
+
enhancedError.stack = error.stack;
|
|
531
|
+
}
|
|
532
|
+
throw enhancedError;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
/**
|
|
536
|
+
* 获取默认的服务连接ID(用于代码源配置)
|
|
537
|
+
* @param organizationId 组织ID
|
|
538
|
+
* @returns 服务连接ID
|
|
539
|
+
*/
|
|
540
|
+
async function getDefaultServiceConnectionId(organizationId) {
|
|
541
|
+
try {
|
|
542
|
+
// 获取Codeup类型的服务连接(代码源最常用)
|
|
543
|
+
const serviceConnections = await listServiceConnectionsFunc(organizationId, 'codeup');
|
|
544
|
+
if (serviceConnections && serviceConnections.length > 0) {
|
|
545
|
+
return serviceConnections[0].uuid || null;
|
|
546
|
+
}
|
|
547
|
+
return null;
|
|
548
|
+
}
|
|
549
|
+
catch (error) {
|
|
550
|
+
console.error('获取Codeup服务连接失败:', error);
|
|
551
|
+
return null;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* 获取默认的Packages服务连接ID(用于制品上传配置)
|
|
556
|
+
* @param organizationId 组织ID
|
|
557
|
+
* @returns Packages服务连接ID
|
|
558
|
+
*/
|
|
559
|
+
async function getDefaultPackagesServiceConnectionId(organizationId) {
|
|
560
|
+
try {
|
|
561
|
+
// 获取packages类型的服务连接
|
|
562
|
+
const serviceConnections = await listServiceConnectionsFunc(organizationId, 'packages');
|
|
563
|
+
if (serviceConnections && serviceConnections.length > 0) {
|
|
564
|
+
return serviceConnections[0].uuid || null;
|
|
565
|
+
}
|
|
566
|
+
return null;
|
|
567
|
+
}
|
|
568
|
+
catch (error) {
|
|
569
|
+
console.error('获取Packages服务连接失败:', error);
|
|
570
|
+
return null;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
/**
|
|
574
|
+
* 获取默认的主机组UUID(用于VM部署配置)
|
|
575
|
+
* @param organizationId 组织ID
|
|
576
|
+
* @returns null(暂不自动获取)
|
|
577
|
+
*/
|
|
578
|
+
async function getDefaultHostGroupId(organizationId) {
|
|
579
|
+
try {
|
|
580
|
+
const hostGroups = await listHostGroupsFunc(organizationId);
|
|
581
|
+
if (hostGroups && hostGroups.length > 0) {
|
|
582
|
+
return hostGroups[0].uuid || null;
|
|
583
|
+
}
|
|
584
|
+
return null;
|
|
585
|
+
}
|
|
586
|
+
catch (error) {
|
|
587
|
+
console.error('获取主机组失败:', error);
|
|
588
|
+
return null;
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
/**
|
|
592
|
+
* 更新流水线内容(YAML)
|
|
593
|
+
* @param organizationId
|
|
594
|
+
* @param pipelineId
|
|
595
|
+
* @param name
|
|
596
|
+
* @param content
|
|
597
|
+
*/
|
|
598
|
+
export async function updatePipelineFunc(organizationId, pipelineId, name, content) {
|
|
599
|
+
const finalOrgId = await resolveOrganizationId(organizationId);
|
|
600
|
+
const url = utils.isRegionEdition()
|
|
601
|
+
? `/oapi/v1/flow/pipelines/${pipelineId}`
|
|
602
|
+
: `/oapi/v1/flow/organizations/${finalOrgId}/pipelines/${pipelineId}`;
|
|
603
|
+
const body = { name, content };
|
|
604
|
+
const response = await utils.yunxiaoRequest(url, {
|
|
605
|
+
method: "PUT",
|
|
606
|
+
body
|
|
607
|
+
});
|
|
608
|
+
return Boolean(response);
|
|
609
|
+
}
|