@taole/deploy-helper 0.3.7 → 0.4.1

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/index.mjs CHANGED
@@ -88,7 +88,6 @@ async function main() {
88
88
  // 默认开启debug debug目前只是打印日志的时候额外打印时间
89
89
  setDebug(true);
90
90
 
91
- log("deploy-helper start");
92
91
 
93
92
  // await devTest();
94
93
  // log(`process.argv: ${process.argv[2]} ${process.argv[3]} ${process.argv[4]}`);
@@ -110,9 +109,13 @@ async function main() {
110
109
  console.log(`command: scp {file} {dest} 复制文件{file}到OfficialSite测试服务器{dest}`);
111
110
  console.log(`command: scpevt {file} 复制文件{file}到events测试服务器{file}`);
112
111
  console.log(`command: scpevt {file} {dest} 复制文件{file}到events测试服务器{dest}`);
113
- log(`command: pipeline {pipelineName|pipelineId} 触发流水线{pipelineName|pipelineId}`);
112
+ console.log(`command: pipeline {pipelineName|pipelineId} 触发流水线{pipelineName|pipelineId}`);
114
113
  process.exit(0);
115
- } else if (command === "init") {
114
+ }
115
+ log("deploy-helper start");
116
+
117
+ // 其他命令
118
+ if (command === "init") {
116
119
  await initConfigJson();
117
120
  process.exit(0);
118
121
  } else if (["scp", "scpevt"].includes(command)) {
@@ -160,7 +163,7 @@ async function main() {
160
163
  process.exit(1);
161
164
  }
162
165
  try {
163
- await triggerPipeline(pipelineName);
166
+ await triggerPipeline(pipelineName, true);
164
167
  process.exit(0);
165
168
  } catch (error) {
166
169
  log(`触发流水线失败: ${error}`);
@@ -196,7 +199,7 @@ async function main() {
196
199
  const entryTestBranch = (config.entry && config.entry.testBranch) || "test";
197
200
  const entryProdBranch = (config.entry && config.entry.prodBranch) || "master";
198
201
  const entryTargetBranch = mode === 'prod' ? entryProdBranch : entryTestBranch;
199
- const currentProdBranch = config.prodBranch || "master";
202
+ // const currentProdBranch = config.prodBranch || "master";
200
203
 
201
204
 
202
205
  // 检查流水线配置
@@ -344,6 +347,7 @@ async function main() {
344
347
 
345
348
  // 7. 推送
346
349
  if (canPushAssets) {
350
+ log(`assets push start`);
347
351
  try {
348
352
  await assetsGit.push();
349
353
  } catch (error) {
@@ -352,6 +356,7 @@ async function main() {
352
356
  log(`assets push done.`);
353
357
  }
354
358
  if (canPushEntry) {
359
+ log(`entry push start`);
355
360
  try {
356
361
  await entryGit.push();
357
362
  } catch (error) {
@@ -3,12 +3,68 @@ import { join } from 'node:path';
3
3
  import fs from 'node:fs';
4
4
  import simpleGit from 'simple-git';
5
5
  import { log, isDebug } from './util.mjs';
6
+ import { PipelineRunSchema } from "../modules/alibabacloud-devops-mcp-server/dist/common/types.js";
7
+ import { z } from "zod";
8
+ import * as utils from "../modules/alibabacloud-devops-mcp-server/dist/common/utils.js";
6
9
 
7
- import { createPipelineRunFunc, listPipelinesFunc, getPipelineFunc } from '../modules/alibabacloud-devops-mcp-server/dist/operations/flow/pipeline.js'
10
+ import { createPipelineRunFunc, listPipelinesFunc, getPipelineFunc, getPipelineRunFunc }
11
+ from '../modules/alibabacloud-devops-mcp-server/dist/operations/flow/pipeline.js'
8
12
 
9
13
  export const organizationId = "5ec8bb7bd1d1abe63b55cd33";
10
14
 
11
15
 
16
+ // docs: https://help.aliyun.com/zh/yunxiao/developer-reference/passpipelinevalidate
17
+ // POST https://{domain}/oapi/v1/flow/organizations/{organizationId}/pipelines/{pipelineId}/pipelineRuns/{pipelineRunId}/jobs/{jobId}/pass
18
+
19
+ /**
20
+ * 获取当前阶段和任务
21
+ * @param {z.infer<typeof PipelineRunSchema>} runDetail 流水线执行详情
22
+ */
23
+ function getCurrentJob(runDetail) {
24
+ let currentStage = null;
25
+ let currentJob = null;
26
+ const stages = runDetail.stages;
27
+
28
+ // 找到当前阶段
29
+ for(let i = 0; i < stages.length; i++) {
30
+ const stage = stages[i];
31
+ currentStage = stage;
32
+ const stageStatus = stage.stageInfo.status;
33
+ if(stageStatus === "RUNNING" || stageStatus === "WAITING" || stageStatus === "FAIL") {
34
+ break;
35
+ }
36
+ }
37
+
38
+ if(currentStage) {
39
+ const jobs = currentStage.stageInfo.jobs;
40
+ for(let i = 0; i < jobs.length; i++) {
41
+ const job = jobs[i];
42
+ currentJob = job;
43
+ const jobStatus = job.status;
44
+ if(jobStatus === "RUNNING" || jobStatus === "WAITING" || jobStatus === "FAIL") {
45
+ break;
46
+ }
47
+ }
48
+ }
49
+ return {stage: currentStage, job: currentJob};
50
+ }
51
+
52
+ /**
53
+ * 通过人工卡点
54
+ * @param {*} pipelineID 流水线id
55
+ * @param {*} runId 流水线执行id
56
+ * @param {*} jobId 流水线执行任务id
57
+ * @returns 是否通过
58
+ */
59
+ async function passPipelineJob(pipelineID, runId, jobId) {
60
+ const url = `/oapi/v1/flow/organizations/${organizationId}/pipelines/${pipelineID}/pipelineRuns/${runId}/jobs/${jobId}/pass`;
61
+ const response = await utils.yunxiaoRequest(url, {
62
+ method: "POST",
63
+ });
64
+ return Boolean(response);
65
+ }
66
+
67
+
12
68
 
13
69
  let isFirstFoundToken = true;
14
70
 
@@ -92,14 +148,69 @@ async function getPipelineInfoByName(name) {
92
148
  return pipeline;
93
149
  }
94
150
 
95
- export async function triggerPipeline(pipelineName) {
151
+ /**
152
+ * 等待流水线执行完成(尝试自行通过人工卡点)
153
+ * @param {string|number} pipelineID 流水线id
154
+ * @param {string|number} runId 流水线执行id
155
+ * @param {number} interval 轮询间隔时间
156
+ * @returns 流水线执行详情
157
+ */
158
+ async function waitPipelineRunFinish(pipelineID, runId, interval = 5000) {
159
+ const pipelineRunDetailUrl = `https://flow.aliyun.com/pipelines/${pipelineID}/builds/${runId}`;
160
+ log(`开始轮询至流水线执行完成`);
161
+ let runDetail = await getPipelineRunFunc(organizationId, pipelineID, runId);
162
+ let passFailedCount = 0;
163
+ let passFailedMaxCount = 3;
164
+ while (runDetail.status === "RUNNING" || runDetail.status === "WAITING") {
165
+ const { stage, job } = getCurrentJob(runDetail);
166
+ log(`流水线执行状态: ${runDetail.status},阶段: ${stage.name},任务: ${job.name},等${interval / 1000}秒后重新轮询`);
167
+ if (runDetail.status === "WAITING") {
168
+ if (stage && job) {
169
+ log(`尝试自动通过人工卡点`);
170
+ try {
171
+ const isPass = await passPipelineJob(pipelineID, runId, job.id);
172
+ if (isPass) {
173
+ log(`人工卡点通过`);
174
+ } else {
175
+ log(`人工卡点通过失败`);
176
+ passFailedCount++;
177
+ }
178
+ } catch (error) {
179
+ passFailedCount++;
180
+ log(`人工卡点通过失败`, error);
181
+ }
182
+
183
+ }
184
+ if(passFailedCount >= passFailedMaxCount) {
185
+ log(`人工卡点通过失败次数超过最大值, 放弃`);
186
+ break;
187
+ }
188
+ }
189
+ await new Promise(resolve => setTimeout(resolve, interval));
190
+ runDetail = await getPipelineRunFunc(organizationId, pipelineID, runId);
191
+ }
192
+ if (runDetail.status === "SUCCESS") {
193
+ log(`流水线执行完成,当前状态: ${runDetail.status}`);
194
+ } else {
195
+ log(`流水线执行失败,当前状态: ${runDetail.status}, 请查看详情${pipelineRunDetailUrl}`);
196
+ }
197
+ return runDetail;
198
+ }
199
+
200
+
201
+ /**
202
+ * 触发流水线
203
+ * @param {*} pipelineName 流水线名称或id
204
+ * @param {*} waitResult 是否等待流水线执行完成
205
+ */
206
+ export async function triggerPipeline(pipelineName, waitResult = false) {
96
207
  const yunxiaoToken = getYunxiaoToken({});
97
- if(!yunxiaoToken) {
208
+ if (!yunxiaoToken) {
98
209
  throw new Error(`未设置云效token, 请检查云效token是否正确`);
99
210
  }
100
211
  let pipelineID = "";
101
212
  let name = pipelineName;
102
- if(/^\d+$/.test(pipelineName)) {
213
+ if (/^\d+$/.test(pipelineName)) {
103
214
  pipelineID = pipelineName;
104
215
  const pipelineInfo = await getPipelineFunc(organizationId, pipelineID);
105
216
  name = pipelineInfo.name;
@@ -111,6 +222,9 @@ export async function triggerPipeline(pipelineName) {
111
222
  const runId = await createPipelineRunFunc(organizationId, pipelineID, {});
112
223
  const pipelineRunDetailUrl = `https://flow.aliyun.com/pipelines/${pipelineID}/builds/${runId}`;
113
224
  log(`流水线${name}[${pipelineID}]触发成功,流水线执行id: ${runId}, 流水线执行详情: ${pipelineRunDetailUrl}`);
225
+ if (waitResult) {
226
+ await waitPipelineRunFinish(pipelineID, runId);
227
+ }
114
228
  }
115
229
 
116
230
 
@@ -163,7 +277,7 @@ async function runSinglePipeline(pipeline, index, total) {
163
277
  if (!fs.existsSync(repo)) {
164
278
  log(`仓库${repo}有未提交的修改, 跳过流水线处理`);
165
279
  return;
166
- }
280
+ }
167
281
  // 强推到指定分支
168
282
  const git = simpleGit(repo);
169
283
  const gitStatus = await git.status();
@@ -187,6 +301,9 @@ async function runSinglePipeline(pipeline, index, total) {
187
301
  const runId = await createPipelineRunFunc(organizationId, pipelineId, {});
188
302
  const piplineRunDetailUrl = `https://flow.aliyun.com/pipelines/${pipelineId}/builds/${runId}`;
189
303
  log(`流水线${pipeline.name}触发成功,流水线执行id: ${runId}, 流水线执行详情: ${piplineRunDetailUrl}`);
304
+ if(pipeline.waitResult) {
305
+ await waitPipelineRunFinish(pipelineId, runId);
306
+ }
190
307
  }
191
308
 
192
309
  export async function runPipeline(config, mode) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@taole/deploy-helper",
3
- "version": "0.3.7",
3
+ "version": "0.4.1",
4
4
  "description": "脚本部署工具,用于将项目部署到测试环境或生产环境",
5
5
  "main": "index.mjs",
6
6
  "type": "module",