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.
Files changed (102) hide show
  1. package/LICENSE +218 -0
  2. package/README.md +527 -0
  3. package/README.zh-cn.md +503 -0
  4. package/dist/common/errors.js +77 -0
  5. package/dist/common/modularTemplates.js +483 -0
  6. package/dist/common/pipelineTemplates.js +19 -0
  7. package/dist/common/toolsetManager.js +123 -0
  8. package/dist/common/toolsets.js +23 -0
  9. package/dist/common/types.js +60 -0
  10. package/dist/common/utils.js +381 -0
  11. package/dist/common/version.js +1 -0
  12. package/dist/index.js +225 -0
  13. package/dist/operations/appstack/appOrchestrations.js +260 -0
  14. package/dist/operations/appstack/appTags.js +168 -0
  15. package/dist/operations/appstack/appTemplates.js +72 -0
  16. package/dist/operations/appstack/applications.js +171 -0
  17. package/dist/operations/appstack/changeOrders.js +320 -0
  18. package/dist/operations/appstack/changeRequests.js +288 -0
  19. package/dist/operations/appstack/deploymentResources.js +286 -0
  20. package/dist/operations/appstack/globalVars.js +221 -0
  21. package/dist/operations/appstack/releaseWorkflows.js +695 -0
  22. package/dist/operations/appstack/variableGroups.js +245 -0
  23. package/dist/operations/codeup/branches.js +157 -0
  24. package/dist/operations/codeup/changeRequestComments.js +140 -0
  25. package/dist/operations/codeup/changeRequests.js +230 -0
  26. package/dist/operations/codeup/commits.js +121 -0
  27. package/dist/operations/codeup/compare.js +30 -0
  28. package/dist/operations/codeup/files.js +249 -0
  29. package/dist/operations/codeup/repositories.js +71 -0
  30. package/dist/operations/codeup/types.js +414 -0
  31. package/dist/operations/flow/hostGroup.js +52 -0
  32. package/dist/operations/flow/pipeline.js +609 -0
  33. package/dist/operations/flow/pipelineJob.js +126 -0
  34. package/dist/operations/flow/resourceMember.js +137 -0
  35. package/dist/operations/flow/serviceConnection.js +27 -0
  36. package/dist/operations/flow/tag.js +191 -0
  37. package/dist/operations/flow/types.js +523 -0
  38. package/dist/operations/flow/vmDeployOrder.js +171 -0
  39. package/dist/operations/organization/members.js +106 -0
  40. package/dist/operations/organization/organization.js +110 -0
  41. package/dist/operations/organization/types.js +111 -0
  42. package/dist/operations/packages/artifacts.js +71 -0
  43. package/dist/operations/packages/repositories.js +39 -0
  44. package/dist/operations/packages/types.js +56 -0
  45. package/dist/operations/projex/effort.js +122 -0
  46. package/dist/operations/projex/project.js +243 -0
  47. package/dist/operations/projex/sprint.js +103 -0
  48. package/dist/operations/projex/types.js +618 -0
  49. package/dist/operations/projex/workitem.js +826 -0
  50. package/dist/operations/testhub/testcases.js +240 -0
  51. package/dist/operations/testhub/testplans.js +128 -0
  52. package/dist/tool-handlers/appstack-app-release-workflows.js +103 -0
  53. package/dist/tool-handlers/appstack-change-orders.js +55 -0
  54. package/dist/tool-handlers/appstack-change-requests.js +49 -0
  55. package/dist/tool-handlers/appstack-deployment-resources.js +31 -0
  56. package/dist/tool-handlers/appstack-global-vars.js +37 -0
  57. package/dist/tool-handlers/appstack-orchestrations.js +49 -0
  58. package/dist/tool-handlers/appstack-release-workflows.js +37 -0
  59. package/dist/tool-handlers/appstack-tags.js +37 -0
  60. package/dist/tool-handlers/appstack-templates.js +19 -0
  61. package/dist/tool-handlers/appstack-variable-groups.js +55 -0
  62. package/dist/tool-handlers/appstack.js +37 -0
  63. package/dist/tool-handlers/base.js +25 -0
  64. package/dist/tool-handlers/code-management.js +150 -0
  65. package/dist/tool-handlers/commit.js +31 -0
  66. package/dist/tool-handlers/effort.js +103 -0
  67. package/dist/tool-handlers/index.js +119 -0
  68. package/dist/tool-handlers/organization.js +72 -0
  69. package/dist/tool-handlers/packages.js +32 -0
  70. package/dist/tool-handlers/pipeline.js +289 -0
  71. package/dist/tool-handlers/project-management.js +201 -0
  72. package/dist/tool-handlers/resourceMember.js +43 -0
  73. package/dist/tool-handlers/service-connections.js +16 -0
  74. package/dist/tool-handlers/tag.js +64 -0
  75. package/dist/tool-handlers/test-management.js +74 -0
  76. package/dist/tool-handlers/vmDeployOrder.js +50 -0
  77. package/dist/tool-registry/appstack-app-release-workflows.js +80 -0
  78. package/dist/tool-registry/appstack-change-orders.js +40 -0
  79. package/dist/tool-registry/appstack-change-requests.js +35 -0
  80. package/dist/tool-registry/appstack-deployment-resources.js +20 -0
  81. package/dist/tool-registry/appstack-global-vars.js +25 -0
  82. package/dist/tool-registry/appstack-orchestrations.js +35 -0
  83. package/dist/tool-registry/appstack-release-workflows.js +25 -0
  84. package/dist/tool-registry/appstack-tags.js +25 -0
  85. package/dist/tool-registry/appstack-templates.js +10 -0
  86. package/dist/tool-registry/appstack-variable-groups.js +40 -0
  87. package/dist/tool-registry/appstack.js +25 -0
  88. package/dist/tool-registry/base.js +19 -0
  89. package/dist/tool-registry/code-management.js +109 -0
  90. package/dist/tool-registry/commit.js +20 -0
  91. package/dist/tool-registry/effort.js +39 -0
  92. package/dist/tool-registry/index.js +7 -0
  93. package/dist/tool-registry/organization.js +65 -0
  94. package/dist/tool-registry/packages.js +21 -0
  95. package/dist/tool-registry/pipeline.js +190 -0
  96. package/dist/tool-registry/project-management.js +143 -0
  97. package/dist/tool-registry/resourceMember.js +29 -0
  98. package/dist/tool-registry/service-connections.js +10 -0
  99. package/dist/tool-registry/tag.js +44 -0
  100. package/dist/tool-registry/test-management.js +59 -0
  101. package/dist/tool-registry/vmDeployOrder.js +34 -0
  102. 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
+ }