@d5render/cli 0.1.22 → 0.1.25

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/bin/d5cli CHANGED
@@ -7,15 +7,143 @@ import { fileURLToPath } from "node:url";
7
7
 
8
8
  //#region package.json
9
9
  var name$1 = "@d5render/cli";
10
- var version = "0.1.22";
10
+ var version = "0.1.25";
11
+
12
+ //#endregion
13
+ //#region packages/gitlab/url.ts
14
+ function buildHeaders() {
15
+ const headers = new Headers();
16
+ headers.set("Content-Type", "application/json");
17
+ const { GITLAB_TOKEN = "" } = envUsed;
18
+ if (GITLAB_TOKEN.startsWith("glpat-")) headers.set("PRIVATE-TOKEN", GITLAB_TOKEN);
19
+ else headers.set("Authorization", `Bearer ${GITLAB_TOKEN}`);
20
+ return headers;
21
+ }
22
+ const visitCommit = () => {
23
+ const { CI_PROJECT_PATH, CI_SERVER_URL } = envUsed;
24
+ if (CI_PROJECT_PATH && CI_SERVER_URL) return `${CI_SERVER_URL}/${CI_PROJECT_PATH}/-/commit`;
25
+ };
26
+ function visitPipeline(mid) {
27
+ const { CI_SERVER_URL, CI_PROJECT_PATH, CI_MERGE_REQUEST_IID = mid, CI_COMMIT_SHA } = envUsed;
28
+ if (!CI_SERVER_URL || !CI_PROJECT_PATH) return {};
29
+ if (CI_MERGE_REQUEST_IID) return {
30
+ url: `${CI_SERVER_URL}/${CI_PROJECT_PATH}/-/merge_requests/${CI_MERGE_REQUEST_IID}`,
31
+ type: "merge_request"
32
+ };
33
+ if (CI_COMMIT_SHA) return {
34
+ url: `${visitCommit()}/${CI_COMMIT_SHA}`,
35
+ type: "commit"
36
+ };
37
+ return {};
38
+ }
39
+ const commits = () => {
40
+ const { CI_PROJECT_ID, CI_SERVER_URL } = envUsed;
41
+ if (CI_PROJECT_ID && CI_SERVER_URL) return `${CI_SERVER_URL}/api/v4/projects/${CI_PROJECT_ID}/repository/commits`;
42
+ };
43
+
44
+ //#endregion
45
+ //#region packages/message/sendding.ts
46
+ async function sendding(title, text) {
47
+ if (!envUsed.DINGTALK_WEBHOOK) throw new Error("non DINGTALK_WEBHOOK");
48
+ let res = new Response();
49
+ for (const url of envUsed.DINGTALK_WEBHOOK.split(",")) res = await fetch(url, {
50
+ method: "POST",
51
+ headers: { "Content-Type": "application/json" },
52
+ body: JSON.stringify({
53
+ msgtype: "markdown",
54
+ markdown: {
55
+ title,
56
+ text
57
+ },
58
+ at: {
59
+ atMobiles: ["17856104313"],
60
+ isAtAll: false
61
+ }
62
+ })
63
+ });
64
+ return res;
65
+ }
66
+
67
+ //#endregion
68
+ //#region copilot/bin/utils.ts
69
+ const NAME = name$1.replaceAll("/", "_");
70
+ const VERSION = version;
71
+ const RUNTIME_CWD = join(dirname(fileURLToPath(import.meta.url)), "../");
72
+ const HOME = env.USERPROFILE ?? env.HOME ?? env.HOMEPATH;
73
+ if (!HOME) throw new Error("cannot find `USERPROFILE` directory");
74
+ const TEMP = env.CI_PROJECT_DIR + NAME;
75
+ function deploy() {
76
+ if (!env.CI) return;
77
+ const config = join(HOME, ".copilot/config.json"), dir = join(HOME, ".copilot/skills/codereview");
78
+ console.log("deploy...");
79
+ if (existsSync(config)) {
80
+ rmSync(config);
81
+ console.log("removed config cache.");
82
+ }
83
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
84
+ const versiono = readFileSync(join(RUNTIME_CWD, ".skills/review/version"), "utf8");
85
+ const versionnPath = join(dir, "version");
86
+ if ((existsSync(versionnPath) ? readFileSync(versionnPath, "utf8") : "") === versiono) return;
87
+ const skillRoot = join(RUNTIME_CWD, ".skills/review");
88
+ readdirSync(skillRoot).forEach((skill) => copyFileSync(join(skillRoot, skill), join(dir, skill)));
89
+ const instructionsRoot = join(RUNTIME_CWD, ".github/instructions");
90
+ readdirSync(instructionsRoot).forEach((instruction) => copyFileSync(join(instructionsRoot, instruction), join(dir, instruction)));
91
+ console.log("to new skill.");
92
+ const changelog = readFileSync(join(RUNTIME_CWD, "CHANGELOG.md"), "utf8");
93
+ const cachepath = join(TEMP, "CHANGELOG");
94
+ if (!existsSync(TEMP)) mkdirSync(TEMP, { recursive: true });
95
+ if (changelog !== (existsSync(cachepath) ? readFileSync(cachepath, "utf8") : "")) {
96
+ if (!existsSync(cachepath)) mkdirSync(cachepath, { recursive: true });
97
+ writeFileSync(cachepath, changelog, "utf8");
98
+ console.log("updated CHANGELOG cache.");
99
+ sendding("NOTICE", `NOTICE: codereview skills has been updated to version ${versiono}\n\nfor more details, please refer to [ONLINE](https://www.npmjs.com/package/@d5render/cli?activeTab=readme)`);
100
+ }
101
+ }
102
+ function need() {
103
+ if (!env.CI) return;
104
+ if (!existsSync(TEMP)) mkdirSync(TEMP, { recursive: true });
105
+ const { CI_MERGE_REQUEST_IID, CI_COMMIT_SHA } = env;
106
+ const file$1 = join(TEMP, "CODEREVIEW");
107
+ if (CI_MERGE_REQUEST_IID) {
108
+ let appended = `${existsSync(file$1) ? readFileSync(file$1, "utf8") : ""}\n${CI_MERGE_REQUEST_IID}`.split("\n");
109
+ const max = 1e4;
110
+ if (appended.length > max) appended = appended.slice(-max);
111
+ writeFileSync(file$1, appended.join("\n"), "utf8");
112
+ console.log("merge pipeline, recorded IID:", CI_MERGE_REQUEST_IID, "to:", file$1);
113
+ return true;
114
+ }
115
+ if (!CI_COMMIT_SHA) return true;
116
+ console.log("commit pipeline");
117
+ const match = execSync(`git log -1 --format=%B ${CI_COMMIT_SHA}`, {
118
+ cwd: env.CI_PROJECT_DIR,
119
+ encoding: "utf8"
120
+ }).toString().match(/See merge request[\s\S]+!(\d+)/);
121
+ if (!match) return true;
122
+ const iid = match[1];
123
+ const yes = (existsSync(file$1) ? readFileSync(file$1, "utf8") : "").split("\n").includes(iid);
124
+ if (yes) {
125
+ console.warn(`Merge Request !${iid} has been AI reviewed before.`);
126
+ const mergeURL = visitPipeline(iid).url;
127
+ const url = `${commits()}/${CI_COMMIT_SHA}/comments`;
128
+ fetch(url, {
129
+ method: "POST",
130
+ headers: buildHeaders(),
131
+ body: JSON.stringify({
132
+ note: `请查看管道评论:${mergeURL}#note`,
133
+ line_type: "new"
134
+ })
135
+ }).then((res) => res.text());
136
+ }
137
+ return !yes;
138
+ }
11
139
 
12
140
  //#endregion
13
141
  //#region copilot/server/config.ts
14
142
  const name = "d5_mcp_review_builtin";
15
143
  const report = "report";
16
144
  const getHash = "hash";
17
- const file = "./copilot.js";
18
- const serveFile = join(dirname(fileURLToPath(import.meta.url)), file);
145
+ const file = "bin/copilot.js";
146
+ const serveFile = join(RUNTIME_CWD, file);
19
147
  const envJson = buildEnv();
20
148
  const envUsed = {
21
149
  CI_SERVER_URL: toEnv("CI_SERVER_URL"),
@@ -60,29 +188,6 @@ function buildEnv() {
60
188
  return envJson$1;
61
189
  }
62
190
 
63
- //#endregion
64
- //#region packages/message/sendding.ts
65
- async function sendding(title, text) {
66
- if (!envUsed.DINGTALK_WEBHOOK) throw new Error("non DINGTALK_WEBHOOK");
67
- let res = new Response();
68
- for (const url of envUsed.DINGTALK_WEBHOOK.split(",")) res = await fetch(url, {
69
- method: "POST",
70
- headers: { "Content-Type": "application/json" },
71
- body: JSON.stringify({
72
- msgtype: "markdown",
73
- markdown: {
74
- title,
75
- text
76
- },
77
- at: {
78
- atMobiles: ["17856104313"],
79
- isAtAll: false
80
- }
81
- })
82
- });
83
- return res;
84
- }
85
-
86
191
  //#endregion
87
192
  //#region copilot/bin/install.ts
88
193
  function install() {
@@ -131,130 +236,56 @@ function installCopilot() {
131
236
  execSync("npm install -g @github/copilot --registry=https://registry.npmmirror.com", { stdio: "inherit" });
132
237
  }
133
238
 
134
- //#endregion
135
- //#region packages/gitlab/url.ts
136
- function buildHeaders() {
137
- const headers = new Headers();
138
- headers.set("Content-Type", "application/json");
139
- const { GITLAB_TOKEN = "" } = envUsed;
140
- if (GITLAB_TOKEN.startsWith("glpat-")) headers.set("PRIVATE-TOKEN", GITLAB_TOKEN);
141
- else headers.set("Authorization", `Bearer ${GITLAB_TOKEN}`);
142
- return headers;
143
- }
144
- const visitCommit = () => {
145
- const { CI_PROJECT_PATH, CI_SERVER_URL } = envUsed;
146
- if (CI_PROJECT_PATH && CI_SERVER_URL) return `${CI_SERVER_URL}/${CI_PROJECT_PATH}/-/commit`;
147
- };
148
- function visitPipeline(mid) {
149
- const { CI_SERVER_URL, CI_PROJECT_PATH, CI_MERGE_REQUEST_IID = mid, CI_COMMIT_SHA } = envUsed;
150
- if (!CI_SERVER_URL || !CI_PROJECT_PATH) return {};
151
- if (CI_MERGE_REQUEST_IID) return {
152
- url: `${CI_SERVER_URL}/${CI_PROJECT_PATH}/-/merge_requests/${CI_MERGE_REQUEST_IID}`,
153
- type: "merge_request"
154
- };
155
- if (CI_COMMIT_SHA) return {
156
- url: `${visitCommit()}/${CI_COMMIT_SHA}`,
157
- type: "commit"
158
- };
159
- return {};
160
- }
161
- const commits = () => {
162
- const { CI_PROJECT_ID, CI_SERVER_URL } = envUsed;
163
- if (CI_PROJECT_ID && CI_SERVER_URL) return `${CI_SERVER_URL}/api/v4/projects/${CI_PROJECT_ID}/repository/commits`;
164
- };
165
-
166
- //#endregion
167
- //#region copilot/bin/utils.ts
168
- const HOME = env.HOME ?? env.HOMEPATH;
169
- const root = HOME ?? env.LOCALAPPDATA;
170
- if (!root) throw new Error("cannot find home directory");
171
- const cwd = join(dirname(fileURLToPath(import.meta.url)), "../");
172
- function deploy() {
173
- const config = join(HOME, ".copilot/config.json");
174
- if (existsSync(config)) rmSync(config);
175
- const dir = join(root, ".copilot/skills/codereview");
176
- if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
177
- const skillRoot = join(cwd, ".skills/review");
178
- const versiono = readFileSync(join(cwd, ".skills/review/version"), "utf8");
179
- const versionnPath = join(dir, "version");
180
- if ((existsSync(versionnPath) ? readFileSync(versionnPath, "utf8") : "") === versiono) return;
181
- readdirSync(skillRoot).forEach((skill) => copyFileSync(join(skillRoot, skill), join(dir, skill)));
182
- const instructionsRoot = join(cwd, ".github/instructions");
183
- readdirSync(instructionsRoot).forEach((instruction) => copyFileSync(join(instructionsRoot, instruction), join(dir, instruction)));
184
- }
185
- function need() {
186
- const { CI_MERGE_REQUEST_IID, CI_COMMIT_SHA, CI_PROJECT_PATH } = env;
187
- const file$1 = join(root, ".ci-ai-review", CI_PROJECT_PATH || "defaults");
188
- const dir = dirname(file$1);
189
- if (CI_MERGE_REQUEST_IID) {
190
- if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
191
- let appended = `${existsSync(file$1) ? readFileSync(file$1, "utf8") : ""}\n${CI_MERGE_REQUEST_IID}`.split("\n");
192
- const max = 5;
193
- if (appended.length > max) appended = appended.slice(-max);
194
- writeFileSync(file$1, appended.join("\n"), "utf8");
195
- return true;
196
- }
197
- if (!CI_COMMIT_SHA) return true;
198
- const match = execSync(`git log -1 --format=%B ${CI_COMMIT_SHA}`, {
199
- cwd: env.CI_PROJECT_DIR,
200
- encoding: "utf8"
201
- }).toString().match(/See merge request[\s\S]+!(\d+)/);
202
- if (!match) return true;
203
- const iid = match[1];
204
- const yes = (existsSync(file$1) ? readFileSync(file$1, "utf8") : "").split("\n").includes(iid);
205
- if (yes) {
206
- console.warn(`Merge Request !${iid} has been AI reviewed before.`);
207
- const mergeURL = visitPipeline(iid).url;
208
- const url = `${commits()}/${CI_COMMIT_SHA}/comments`;
209
- fetch(url, {
210
- method: "POST",
211
- headers: buildHeaders(),
212
- body: JSON.stringify({
213
- note: `请查看管道评论:${mergeURL}#note`,
214
- line_type: "new"
215
- })
216
- }).then((res) => res.text());
217
- }
218
- return !yes;
219
- }
220
-
221
239
  //#endregion
222
240
  //#region copilot/bin/index.ts
223
241
  try {
224
- main();
242
+ codereview();
225
243
  } catch (error) {
226
- sendding("CRITICAL", "未知错误,请自行检查日志");
244
+ sendding("CRITICAL", "CRITICAL CI ERROR: 未知错误,请自行检查日志");
227
245
  throw error;
228
246
  }
229
- function main() {
247
+ function codereview() {
248
+ deploy();
249
+ if (!need()) {
250
+ console.log("重复提交,进程跳过");
251
+ return;
252
+ }
253
+ install();
230
254
  const prompt = `Load skills, then call the mcp tool '${name}-${getHash}' to load code-review commits, if the task encounters an error, throw that.
231
255
  Otherwise, use chinese as default language to call the mcp tool '${name}-${report}'`;
232
- deploy();
233
- if (need()) {
234
- install();
235
- const copilot = spawn("node", [
236
- findCopilopt(),
237
- ...tools,
238
- "--stream",
239
- "off",
240
- "-p",
241
- prompt
242
- ], {
243
- cwd: env.CI_PROJECT_DIR,
244
- stdio: [
245
- "inherit",
246
- "pipe",
247
- "pipe"
248
- ],
249
- ...platform === "win32" && { windowsHide: true }
250
- });
251
- copilot.stdout.on("data", (chunk) => console.log(String(chunk)));
252
- copilot.stderr.on("data", (chunk) => console.error(String(chunk)));
253
- copilot.on("close", (code) => {
254
- sendding("CRITICAL", "Copilot 运行过程发生错误,请自行检查日志");
255
- exit(code);
256
- });
257
- } else console.log("重复提交,进程跳过");
256
+ const copilot = spawn("node", [
257
+ findCopilopt(),
258
+ ...tools,
259
+ "--stream",
260
+ "off",
261
+ "-p",
262
+ prompt
263
+ ], {
264
+ cwd: env.CI_PROJECT_DIR,
265
+ stdio: [
266
+ "inherit",
267
+ "pipe",
268
+ "pipe"
269
+ ],
270
+ env: {
271
+ ...env,
272
+ NODE_NO_WARNINGS: "1",
273
+ PYTHONUNBUFFERED: "1"
274
+ },
275
+ ...platform === "win32" && { windowsHide: true }
276
+ });
277
+ copilot.stdout.setEncoding("utf8");
278
+ copilot.stderr.setEncoding("utf8");
279
+ copilot.stdout.on("data", (chunk) => {
280
+ process.stdout.write(chunk);
281
+ });
282
+ copilot.stderr.on("data", (chunk) => {
283
+ process.stderr.write(chunk);
284
+ });
285
+ copilot.on("close", (code) => {
286
+ sendding("CRITICAL", "CRITICAL CI ERROR: 代码审查任务失败,请自行检查日志");
287
+ exit(code);
288
+ });
258
289
  }
259
290
  function findCopilopt() {
260
291
  let copilot = execSync("npm list @github/copilot -g -p").toString().trim();
@@ -272,8 +303,8 @@ function findCopilopt() {
272
303
  if (!binPath) throw new Error("non copilot executable found");
273
304
  const copilotVersion = copilotPackage.version || "unknown";
274
305
  const copilotPath = join(copilot, binPath);
275
- console.log(`${name$1} server:
276
- version: ${version}
306
+ console.log(`${NAME} server:
307
+ version: ${VERSION}
277
308
  path: ${serveFile}
278
309
  copilot:
279
310
  version: ${copilotVersion}
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "license": "MIT",
5
5
  "author": "jasirou",
6
6
  "main": "./bin/d5cli",
7
- "version": "0.1.22",
7
+ "version": "0.1.25",
8
8
  "devDependencies": {
9
9
  "@modelcontextprotocol/sdk": "^1.25.1",
10
10
  "@types/node": "^25.0.3",
@@ -18,9 +18,11 @@
18
18
  "files": [
19
19
  ".github/instructions/review.instructions.md",
20
20
  ".github/instructions/severity.instructions.md",
21
+ ".skills/devops",
21
22
  ".skills/review",
22
23
  "bin/copilot.js",
23
- "bin/d5cli"
24
+ "bin/d5cli",
25
+ "CHANGELOG.md"
24
26
  ],
25
27
  "bin": {
26
28
  "d5cli": "bin/d5cli"