@taole/deploy-helper 1.0.2 → 1.0.4

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 (32) hide show
  1. package/README.md +1 -4
  2. package/index.mjs +2 -39
  3. package/lib/pipelineApi.mjs +17 -16
  4. package/lib/util.mjs +0 -40
  5. package/lib/yunxiaoFlowApi.mjs +115 -0
  6. package/package.json +4 -7
  7. package/lib/offlinePkg.mjs +0 -333
  8. package/lib/upload.js +0 -49
  9. package/modules/alibabacloud-devops-mcp-server/dist/common/errors.js +0 -69
  10. package/modules/alibabacloud-devops-mcp-server/dist/common/modularTemplates.js +0 -483
  11. package/modules/alibabacloud-devops-mcp-server/dist/common/pipelineTemplates.js +0 -19
  12. package/modules/alibabacloud-devops-mcp-server/dist/common/types.js +0 -1119
  13. package/modules/alibabacloud-devops-mcp-server/dist/common/utils.js +0 -353
  14. package/modules/alibabacloud-devops-mcp-server/dist/common/version.js +0 -1
  15. package/modules/alibabacloud-devops-mcp-server/dist/index.js +0 -1067
  16. package/modules/alibabacloud-devops-mcp-server/dist/operations/codeup/branches.js +0 -144
  17. package/modules/alibabacloud-devops-mcp-server/dist/operations/codeup/changeRequestComments.js +0 -89
  18. package/modules/alibabacloud-devops-mcp-server/dist/operations/codeup/changeRequests.js +0 -203
  19. package/modules/alibabacloud-devops-mcp-server/dist/operations/codeup/compare.js +0 -26
  20. package/modules/alibabacloud-devops-mcp-server/dist/operations/codeup/files.js +0 -233
  21. package/modules/alibabacloud-devops-mcp-server/dist/operations/codeup/repositories.js +0 -64
  22. package/modules/alibabacloud-devops-mcp-server/dist/operations/flow/hostGroup.js +0 -48
  23. package/modules/alibabacloud-devops-mcp-server/dist/operations/flow/pipeline.js +0 -514
  24. package/modules/alibabacloud-devops-mcp-server/dist/operations/flow/pipelineJob.js +0 -113
  25. package/modules/alibabacloud-devops-mcp-server/dist/operations/flow/serviceConnection.js +0 -23
  26. package/modules/alibabacloud-devops-mcp-server/dist/operations/organization/members.js +0 -94
  27. package/modules/alibabacloud-devops-mcp-server/dist/operations/organization/organization.js +0 -73
  28. package/modules/alibabacloud-devops-mcp-server/dist/operations/packages/artifacts.js +0 -64
  29. package/modules/alibabacloud-devops-mcp-server/dist/operations/packages/repositories.js +0 -35
  30. package/modules/alibabacloud-devops-mcp-server/dist/operations/projex/project.js +0 -206
  31. package/modules/alibabacloud-devops-mcp-server/dist/operations/projex/sprint.js +0 -30
  32. package/modules/alibabacloud-devops-mcp-server/dist/operations/projex/workitem.js +0 -264
package/README.md CHANGED
@@ -1,8 +1,5 @@
1
1
  # deploy-helper
2
2
  脚本部署用
3
3
 
4
- # 关于modules/alibabacloud-devops-mcp-server
5
-
6
- 使用里边封装的api, 但是太吵了(debug信息有点多,还没法关闭, 所以没有直接使用它的npm包)
7
- npm包地址 https://www.npmjs.com/package/alibabacloud-devops-mcp-server
4
+ 云效流水线相关 HTTP 调用已收敛为 `lib/yunxiaoFlowApi.mjs`(直接调 OpenAPI,无 MCP/Zod 依赖)。
8
5
 
package/index.mjs CHANGED
@@ -4,12 +4,10 @@ import { Client } from 'node-scp'
4
4
  import fs from 'fs';
5
5
  import { join, basename, dirname } from "path";
6
6
  import { simpleGit } from 'simple-git';
7
- import { homedir } from 'os'
8
7
  import { runPipeline, checkYunxiaoToken, triggerPipeline } from './lib/pipelineApi.mjs';
9
- import { setDebug, log, getUserDeployHelperConfig } from './lib/util.mjs';
8
+ import { setDebug, log } from './lib/util.mjs';
10
9
  import path from 'path';
11
10
  import { fileURLToPath } from 'url';
12
- import { checkOfflinePkg, syncApi as syncOfflinePkgApi } from './lib/offlinePkg.mjs';
13
11
  import { cmdWhoami, cmdLogout, cmdLogin } from './lib/login.mjs';
14
12
  import { cmdProjectCreate, cmdProjectPublish, cmdProjectPull } from './lib/project.mjs';
15
13
 
@@ -155,7 +153,7 @@ async function main() {
155
153
  const registeredCommand = commandMap[command];
156
154
  if(registeredCommand){
157
155
  // pass
158
- } else if (!["init", "prod", "test", "scp", "scpevt", "pipeline", "offlinepkgrm"].includes(command)) {
156
+ } else if (!["init", "prod", "test", "scp", "scpevt", "pipeline"].includes(command)) {
159
157
  command = "help";
160
158
  }
161
159
 
@@ -182,7 +180,6 @@ async function main() {
182
180
  console.log(`command: scpevt {file} 复制文件{file}到events测试服务器{file}`);
183
181
  console.log(`command: scpevt {file} {dest} 复制文件{file}到events测试服务器{dest}`);
184
182
  console.log(`command: pipeline {pipelineName|pipelineId} [branch] 触发流水线{pipelineName|pipelineId}, 指定分支(可省略)`);
185
- console.log(`command: offlinepkgrm {name} {platform} [mode] 删除离线包{name}, 指定平台{platform}, 指定模式(可省略,默认test)`);
186
183
  Object.keys(commandMap).forEach(command => {
187
184
  const cmdObj = commandMap[command];
188
185
  console.log(`command: ${command} ${cmdObj.help}`);
@@ -256,29 +253,6 @@ async function main() {
256
253
  process.exit(1);
257
254
  }
258
255
 
259
- } else if (command === "offlinepkgrm") {
260
- const name = process.argv[3];
261
- if (!name) {
262
- log(`name参数不能为空`);
263
- process.exit(1);
264
- }
265
- const platform = process.argv[4];
266
- if (!platform) {
267
- log(`platform参数不能为空`);
268
- process.exit(1);
269
- }
270
- const mode = process.argv[5] || "test";
271
- if (!["prod", "test"].includes(mode)) {
272
- log(`mode参数只能是prod或test`);
273
- process.exit(1);
274
- }
275
- const userDeployHelperConfig = getUserDeployHelperConfig();
276
- if (!userDeployHelperConfig || !userDeployHelperConfig.offlineApi || !userDeployHelperConfig.offlineApi.get || !userDeployHelperConfig.offlineApi.set) {
277
- log(`配置文件未配置离线包api接口, 请先配置`);
278
- process.exit(1);
279
- }
280
- await syncOfflinePkgApi(userDeployHelperConfig, mode, { name, remove: true, platform }, null);
281
- process.exit(0);
282
256
  }
283
257
  try {
284
258
  const workDir = process.cwd(); // 当前项目根目录
@@ -322,17 +296,6 @@ async function main() {
322
296
 
323
297
  // 检查流水线配置
324
298
  checkYunxiaoToken(config, mode);
325
- const offlinePkgResult = checkOfflinePkg({
326
- workDir: workDir,
327
- mode,
328
- });
329
- if (!offlinePkgResult.canBuild && offlinePkgResult.errorMsg) {
330
- log(`${offlinePkgResult.errorMsg}`);
331
- process.exit(1);
332
- }
333
- if (offlinePkgResult.canBuild) {
334
- await offlinePkgResult.hookPostBuild();
335
- }
336
299
 
337
300
  // 检查项目
338
301
  // 1. 检查项目是否存在
@@ -2,13 +2,14 @@ import { homedir } from 'node:os';
2
2
  import { join } from 'node:path';
3
3
  import fs from 'node:fs';
4
4
  import simpleGit from 'simple-git';
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";
9
-
10
- import { createPipelineRunFunc, listPipelinesFunc, getPipelineFunc, getPipelineRunFunc }
11
- from '../modules/alibabacloud-devops-mcp-server/dist/operations/flow/pipeline.js'
5
+ import { log } from './util.mjs';
6
+ import {
7
+ yunxiaoRequest,
8
+ listPipelines,
9
+ getPipeline,
10
+ getPipelineRun,
11
+ createPipelineRun,
12
+ } from './yunxiaoFlowApi.mjs';
12
13
 
13
14
  export const organizationId = "5ec8bb7bd1d1abe63b55cd33";
14
15
 
@@ -18,7 +19,7 @@ export const organizationId = "5ec8bb7bd1d1abe63b55cd33";
18
19
 
19
20
  /**
20
21
  * 获取当前阶段和任务
21
- * @param {z.infer<typeof PipelineRunSchema>} runDetail 流水线执行详情
22
+ * @param {object} runDetail 流水线执行详情(云效 runs 接口原始结构)
22
23
  */
23
24
  function getCurrentJob(runDetail) {
24
25
  let currentStage = null;
@@ -58,7 +59,7 @@ function getCurrentJob(runDetail) {
58
59
  */
59
60
  async function passPipelineJob(pipelineID, runId, jobId) {
60
61
  const url = `/oapi/v1/flow/organizations/${organizationId}/pipelines/${pipelineID}/pipelineRuns/${runId}/jobs/${jobId}/pass`;
61
- const response = await utils.yunxiaoRequest(url, {
62
+ const response = await yunxiaoRequest(url, {
62
63
  method: "POST",
63
64
  });
64
65
  return Boolean(response);
@@ -135,7 +136,7 @@ export function setDevToken(token) {
135
136
  async function getPipelineInfoByName(name) {
136
137
  let pipeline = null;
137
138
  try {
138
- const pipelines = await listPipelinesFunc(organizationId, { pipelineName: name, perPage: 50, page: 1 });
139
+ const pipelines = await listPipelines(organizationId, { pipelineName: name, perPage: 50, page: 1 });
139
140
  if (pipelines && Array.isArray(pipelines.items) && pipelines.items.length > 0) {
140
141
  pipeline = pipelines.items.find(item => item.name === name);
141
142
  }
@@ -158,7 +159,7 @@ async function getPipelineInfoByName(name) {
158
159
  async function waitPipelineRunFinish(pipelineID, runId, interval = 5000) {
159
160
  const pipelineRunDetailUrl = `https://flow.aliyun.com/pipelines/${pipelineID}/builds/${runId}`;
160
161
  log(`开始轮询至流水线执行完成`);
161
- let runDetail = await getPipelineRunFunc(organizationId, pipelineID, runId);
162
+ let runDetail = await getPipelineRun(organizationId, pipelineID, runId);
162
163
  let passFailedCount = 0;
163
164
  let passFailedMaxCount = 3;
164
165
  while (runDetail.status === "RUNNING" || runDetail.status === "WAITING" || runDetail.status === "INIT") {
@@ -189,7 +190,7 @@ async function waitPipelineRunFinish(pipelineID, runId, interval = 5000) {
189
190
  }
190
191
  }
191
192
  await new Promise(resolve => setTimeout(resolve, interval));
192
- runDetail = await getPipelineRunFunc(organizationId, pipelineID, runId);
193
+ runDetail = await getPipelineRun(organizationId, pipelineID, runId);
193
194
  }
194
195
  if (runDetail.status === "SUCCESS") {
195
196
  log(`流水线执行完成,当前状态: ${runDetail.status}`);
@@ -212,7 +213,7 @@ async function getPipelineInfoDetail(pipelineName, targetBranch = "") {
212
213
  let hasDetail = false;
213
214
  if (/^\d+$/.test(pipelineName)) {
214
215
  pipelineID = pipelineName;
215
- pipelineInfo = await getPipelineFunc(organizationId, pipelineID);
216
+ pipelineInfo = await getPipeline(organizationId, pipelineID);
216
217
  pipelineInfo.id = pipelineID;
217
218
  hasDetail = true;
218
219
  } else {
@@ -224,7 +225,7 @@ async function getPipelineInfoDetail(pipelineName, targetBranch = "") {
224
225
  // 如果需要指定分支,则需要获取源码仓库信息
225
226
  if (targetBranch) {
226
227
  if (!hasDetail) {
227
- pipelineInfo = await getPipelineFunc(organizationId, pipelineID);
228
+ pipelineInfo = await getPipeline(organizationId, pipelineID);
228
229
  pipelineInfo.id = pipelineID;
229
230
  }
230
231
  if (pipelineInfo && pipelineInfo.pipelineConfig && Array.isArray(pipelineInfo.pipelineConfig.sources)) {
@@ -266,7 +267,7 @@ export async function triggerPipeline(pipelineName, opts = {}) {
266
267
  throw new Error(`未设置云效token, 请检查云效token是否正确`);
267
268
  }
268
269
  const { pipelineInfo, runParams } = await getPipelineInfoDetail(pipelineName, opts.branch);
269
- const runId = await createPipelineRunFunc(organizationId, pipelineInfo.id, runParams);
270
+ const runId = await createPipelineRun(organizationId, pipelineInfo.id, runParams);
270
271
  const pipelineRunDetailUrl = `https://flow.aliyun.com/pipelines/${pipelineInfo.id}/builds/${runId}`;
271
272
  log(`流水线${pipelineInfo.name}[${pipelineInfo.id}]触发成功,流水线执行id: ${runId}, 流水线执行详情: ${pipelineRunDetailUrl}`);
272
273
  if (waitResult) {
@@ -338,7 +339,7 @@ async function runSinglePipeline(pipeline, index, total) {
338
339
  await git.raw("push", "origin", `${force2Branch.src}:${force2Branch.dest}`, "--force");
339
340
  log(`仓库${repo}强推${force2Branch.src}到${force2Branch.dest}完成`);
340
341
  }
341
- const runId = await createPipelineRunFunc(organizationId, pipelineInfo.id, runParams);
342
+ const runId = await createPipelineRun(organizationId, pipelineInfo.id, runParams);
342
343
  const piplineRunDetailUrl = `https://flow.aliyun.com/pipelines/${pipelineInfo.id}/builds/${runId}`;
343
344
  log(`流水线${pipelineInfo.name}触发成功,流水线执行id: ${runId}, 流水线执行详情: ${piplineRunDetailUrl}`);
344
345
  if (pipeline.waitResult) {
package/lib/util.mjs CHANGED
@@ -1,6 +1,5 @@
1
1
  import fs from 'fs';
2
2
  import { join } from "node:path"
3
- import { homedir } from "node:os"
4
3
  import { exec } from 'child_process';
5
4
 
6
5
  let _isDebug = false;
@@ -21,45 +20,6 @@ export function log(...args) {
21
20
  }
22
21
  }
23
22
 
24
- export function getOssToken() {
25
- let token = "";
26
- token = process.env.TW_DH_OSS_TOKEN || ""
27
- if (token) {
28
- log(`将使用来自环境变量TW_DH_OSS_TOKEN的token`);
29
- return token;
30
- }
31
- const userDeployHelperDir = join(homedir(), "deploy-helper.config.json");
32
- if (fs.existsSync(userDeployHelperDir)) {
33
- try {
34
- const userDeployHelperConfig = JSON.parse(fs.readFileSync(userDeployHelperDir, "utf-8"));
35
- token = userDeployHelperConfig.ossToken || "";
36
- } catch (error) {
37
- console.error(`读取${userDeployHelperDir}配置失败: ${error}`);
38
- }
39
- if (token) {
40
- log(`将使用来自${userDeployHelperDir}的ossToken`);
41
- return token;
42
- }
43
- }
44
- return token;
45
- }
46
-
47
- /**
48
- * 读取dh用户配置文件deploy-helper.config.json
49
- * @returns {Object|null} 用户配置
50
- */
51
- export function getUserDeployHelperConfig() {
52
- const userDeployHelperDir = join(homedir(), "deploy-helper.config.json");
53
- if (fs.existsSync(userDeployHelperDir)) {
54
- try {
55
- const userDeployHelperConfig = JSON.parse(fs.readFileSync(userDeployHelperDir, "utf-8"));
56
- return userDeployHelperConfig;
57
- } catch (error) {
58
- console.error(`读取${userDeployHelperDir}配置失败: ${error}`);
59
- }
60
- }
61
- return null;
62
- }
63
23
  /**
64
24
  * json配置
65
25
  * @param {string} workDir 项目工作目录
@@ -0,0 +1,115 @@
1
+ /**
2
+ * 云效 Flow OpenAPI 薄封装(仅 deploy-helper 流水线触发/查询所需),不做 Zod 等严格校验。
3
+ * 文档基址与鉴权与官方 OpenAPI 一致。
4
+ */
5
+
6
+ const DEFAULT_BASE = "https://openapi-rdc.aliyuncs.com";
7
+
8
+ function getBaseUrl() {
9
+ return process.env.YUNXIAO_API_BASE_URL || DEFAULT_BASE;
10
+ }
11
+
12
+ function buildUrl(path, query = {}) {
13
+ const base = getBaseUrl();
14
+ const full = `${base}${path.startsWith("/") ? path : `/${path}`}`;
15
+ const u = new URL(full);
16
+ for (const [k, v] of Object.entries(query)) {
17
+ if (v !== undefined && v !== null) {
18
+ u.searchParams.append(k, String(v));
19
+ }
20
+ }
21
+ return u.pathname + u.search;
22
+ }
23
+
24
+ async function parseBody(response) {
25
+ const ct = response.headers.get("content-type") || "";
26
+ if (ct.includes("application/json")) {
27
+ return response.json();
28
+ }
29
+ return response.text();
30
+ }
31
+
32
+ /**
33
+ * @param {string} urlPath 相对路径或绝对 URL
34
+ * @param {{ method?: string, body?: object, headers?: Record<string, string> }} [options]
35
+ */
36
+ export async function yunxiaoRequest(urlPath, options = {}) {
37
+ const isAbs = urlPath.startsWith("http://") || urlPath.startsWith("https://");
38
+ const url = isAbs ? urlPath : `${getBaseUrl()}${urlPath.startsWith("/") ? urlPath : `/${urlPath}`}`;
39
+ const headers = {
40
+ Accept: "application/json",
41
+ "Content-Type": "application/json",
42
+ "User-Agent": "@taole/deploy-helper/yunxiao-flow",
43
+ ...options.headers,
44
+ };
45
+ if (process.env.YUNXIAO_ACCESS_TOKEN) {
46
+ headers["x-yunxiao-token"] = process.env.YUNXIAO_ACCESS_TOKEN;
47
+ }
48
+ const res = await fetch(url, {
49
+ method: options.method || "GET",
50
+ headers,
51
+ body: options.body !== undefined ? JSON.stringify(options.body) : undefined,
52
+ });
53
+ const data = await parseBody(res);
54
+ if (!res.ok) {
55
+ const msg =
56
+ data && typeof data === "object" && "message" in data
57
+ ? String(data.message)
58
+ : typeof data === "string"
59
+ ? data
60
+ : res.statusText;
61
+ throw new Error(`云效 API ${res.status}: ${msg}`);
62
+ }
63
+ return data;
64
+ }
65
+
66
+ /**
67
+ * @param {string} organizationId
68
+ * @param {{ pipelineName?: string, perPage?: number, page?: number }} [query]
69
+ */
70
+ export async function listPipelines(organizationId, query = {}) {
71
+ const path = `/oapi/v1/flow/organizations/${organizationId}/pipelines`;
72
+ const url = buildUrl(path, {
73
+ pipelineName: query.pipelineName,
74
+ perPage: query.perPage,
75
+ page: query.page,
76
+ });
77
+ const response = await yunxiaoRequest(url, { method: "GET" });
78
+ let items = [];
79
+ if (Array.isArray(response)) {
80
+ items = response.map((item) => ({
81
+ ...item,
82
+ id: item.id ?? item.pipelineId,
83
+ name: item.name ?? item.pipelineName,
84
+ }));
85
+ }
86
+ return { items };
87
+ }
88
+
89
+ export async function getPipeline(organizationId, pipelineId) {
90
+ const url = `/oapi/v1/flow/organizations/${organizationId}/pipelines/${pipelineId}`;
91
+ return yunxiaoRequest(url, { method: "GET" });
92
+ }
93
+
94
+ export async function getPipelineRun(organizationId, pipelineId, pipelineRunId) {
95
+ const url = `/oapi/v1/flow/organizations/${organizationId}/pipelines/${pipelineId}/runs/${pipelineRunId}`;
96
+ return yunxiaoRequest(url, { method: "GET" });
97
+ }
98
+
99
+ /**
100
+ * @param {string} organizationId
101
+ * @param {string|number} pipelineId
102
+ * @param {{ params?: string }} options params 为 JSON 字符串(与云效 OpenAPI 一致)
103
+ */
104
+ export async function createPipelineRun(organizationId, pipelineId, options = {}) {
105
+ const url = `/oapi/v1/flow/organizations/${organizationId}/pipelines/${pipelineId}/runs`;
106
+ const body = {};
107
+ if (options.params !== undefined) {
108
+ body.params = options.params;
109
+ }
110
+ const response = await yunxiaoRequest(url, {
111
+ method: "POST",
112
+ body,
113
+ });
114
+ return Number(response);
115
+ }
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@taole/deploy-helper",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "脚本部署工具,用于将项目部署到测试环境或生产环境",
5
5
  "main": "index.mjs",
6
6
  "type": "module",
7
7
  "scripts": {
8
8
  "start": "node index.mjs",
9
- "test": "node test.mjs"
9
+ "test": "node test.mjs",
10
+ "test:unit": "node --test ./test/yunxiaoFlowApi.test.mjs"
10
11
  },
11
12
  "bin": {
12
13
  "deploy-helper": "index.mjs",
@@ -18,18 +19,14 @@
18
19
  },
19
20
  "files": [
20
21
  "index.mjs",
21
- "lib",
22
- "modules/alibabacloud-devops-mcp-server/dist"
22
+ "lib"
23
23
  ],
24
24
  "keywords": [],
25
25
  "author": "",
26
26
  "license": "ISC",
27
27
  "dependencies": {
28
- "ali-oss": "^6.23.0",
29
- "alibabacloud-devops-mcp-server": "*",
30
28
  "archiver": "^7.0.1",
31
29
  "form-data": "^4.0.5",
32
- "md5-file": "^5.0.0",
33
30
  "node-scp": "^0.0.25",
34
31
  "simple-git": "^3.28.0"
35
32
  }