@taole/deploy-helper 0.3.7 → 0.4.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/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,57 @@ 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 = 3000) {
159
+ const pipelineRunDetailUrl = `https://flow.aliyun.com/pipelines/${pipelineID}/builds/${runId}`;
160
+ log(`开始轮询至流水线执行完成`);
161
+ let runDetail = await getPipelineRunFunc(organizationId, pipelineID, runId);
162
+ while (runDetail.status === "RUNNING" || runDetail.status === "WAITING") {
163
+ const { stage, job } = getCurrentJob(runDetail);
164
+ log(`流水线执行状态: ${runDetail.status},阶段: ${stage.name},任务: ${job.name},等${interval / 1000}秒后重新轮询`);
165
+ if (runDetail.status === "WAITING") {
166
+ if (stage && job) {
167
+ log(`尝试自动通过人工卡点`);
168
+ const isPass = await passPipelineJob(pipelineID, runId, job.id);
169
+ if (isPass) {
170
+ log(`人工卡点通过`);
171
+ } else {
172
+ log(`人工卡点通过失败`);
173
+ break;
174
+ }
175
+ }
176
+ }
177
+ await new Promise(resolve => setTimeout(resolve, interval));
178
+ runDetail = await getPipelineRunFunc(organizationId, pipelineID, runId);
179
+ }
180
+ if (runDetail.status === "SUCCESS") {
181
+ log(`流水线执行完成,当前状态: ${runDetail.status}`);
182
+ } else {
183
+ log(`流水线执行失败,当前状态: ${runDetail.status}, 请查看详情${pipelineRunDetailUrl}`);
184
+ }
185
+ return runDetail;
186
+ }
187
+
188
+
189
+ /**
190
+ * 触发流水线
191
+ * @param {*} pipelineName 流水线名称或id
192
+ * @param {*} waitResult 是否等待流水线执行完成
193
+ */
194
+ export async function triggerPipeline(pipelineName, waitResult = false) {
96
195
  const yunxiaoToken = getYunxiaoToken({});
97
- if(!yunxiaoToken) {
196
+ if (!yunxiaoToken) {
98
197
  throw new Error(`未设置云效token, 请检查云效token是否正确`);
99
198
  }
100
199
  let pipelineID = "";
101
200
  let name = pipelineName;
102
- if(/^\d+$/.test(pipelineName)) {
201
+ if (/^\d+$/.test(pipelineName)) {
103
202
  pipelineID = pipelineName;
104
203
  const pipelineInfo = await getPipelineFunc(organizationId, pipelineID);
105
204
  name = pipelineInfo.name;
@@ -111,6 +210,9 @@ export async function triggerPipeline(pipelineName) {
111
210
  const runId = await createPipelineRunFunc(organizationId, pipelineID, {});
112
211
  const pipelineRunDetailUrl = `https://flow.aliyun.com/pipelines/${pipelineID}/builds/${runId}`;
113
212
  log(`流水线${name}[${pipelineID}]触发成功,流水线执行id: ${runId}, 流水线执行详情: ${pipelineRunDetailUrl}`);
213
+ if (waitResult) {
214
+ await waitPipelineRunFinish(pipelineID, runId);
215
+ }
114
216
  }
115
217
 
116
218
 
@@ -163,7 +265,7 @@ async function runSinglePipeline(pipeline, index, total) {
163
265
  if (!fs.existsSync(repo)) {
164
266
  log(`仓库${repo}有未提交的修改, 跳过流水线处理`);
165
267
  return;
166
- }
268
+ }
167
269
  // 强推到指定分支
168
270
  const git = simpleGit(repo);
169
271
  const gitStatus = await git.status();
@@ -187,6 +289,9 @@ async function runSinglePipeline(pipeline, index, total) {
187
289
  const runId = await createPipelineRunFunc(organizationId, pipelineId, {});
188
290
  const piplineRunDetailUrl = `https://flow.aliyun.com/pipelines/${pipelineId}/builds/${runId}`;
189
291
  log(`流水线${pipeline.name}触发成功,流水线执行id: ${runId}, 流水线执行详情: ${piplineRunDetailUrl}`);
292
+ if(pipeline.waitResult) {
293
+ await waitPipelineRunFinish(pipelineId, runId);
294
+ }
190
295
  }
191
296
 
192
297
  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.0",
4
4
  "description": "脚本部署工具,用于将项目部署到测试环境或生产环境",
5
5
  "main": "index.mjs",
6
6
  "type": "module",