Skip to content

Commit e749e2d

Browse files
committed
feat: 校验补 @grant、更新加版本闸,审核结果回贴原 issue 通知投稿人
1 parent 13c9c21 commit e749e2d

3 files changed

Lines changed: 79 additions & 7 deletions

File tree

scripts/header.mjs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
/** 各字段长度上限 */
88
export const LIMITS = {
99
id: 64,
10+
grant: 64,
1011
name: 24,
1112
description: 256,
1213
author: 56,
@@ -46,10 +47,26 @@ export function validateSource(source) {
4647
if (!header.id) errors.push("缺少 @id");
4748
else if (!ID_RE.test(header.id)) errors.push(`@id 非法:${header.id}`);
4849
if (!header.name) errors.push("缺少 @name");
49-
if ((header.type ?? "source") !== "control") errors.push("@type 需声明为 control");
50+
if ((header.type ?? "source") !== "control") errors.push("脚本头需声明 @type control");
5051
else if (!header.apiLevel || Number(header.apiLevel) < 2) errors.push("控制类插件需 @apiLevel ≥ 2");
5152
if (header.version && !/^\d+(\.\d+)*$/.test(header.version))
5253
errors.push(`@version 非法:${header.version}`);
54+
// 权限:声明的 @grant 必须在白名单内;用到 request / 反向控制却没声明对应权限会在 App 端被拒,提前拦
55+
const grants = (header.grant ?? "")
56+
.split(/[,\s]+/)
57+
.map((g) => g.trim().toLowerCase())
58+
.filter(Boolean);
59+
for (const g of grants) {
60+
if (g !== "network" && g !== "control")
61+
errors.push(`未知权限 @grant:${g}(仅支持 network / control)`);
62+
}
63+
if (/\bsplayer\.request\b/.test(source) && !grants.includes("network"))
64+
errors.push("脚本用到 splayer.request,需声明 @grant network");
65+
if (
66+
/\bsplayer\.player\.(play|pause|next|prev|seek|setVolume|getPosition)\b/.test(source) &&
67+
!grants.includes("control")
68+
)
69+
errors.push("脚本用到 splayer.player.* 反向控制,需声明 @grant control");
5370
return { id: header.id, header, errors };
5471
}
5572

@@ -64,6 +81,28 @@ export function validate(id, source) {
6481
return result;
6582
}
6683

84+
/**
85+
* remote 版本是否严格高于 current:点分数字逐段比较,缺省段补 0,非数字段当 0
86+
* @param remote - 新版本号
87+
* @param current - 现版本号
88+
*/
89+
export function isNewerVersion(remote, current) {
90+
const parse = (v) =>
91+
String(v)
92+
.trim()
93+
.replace(/^v/i, "")
94+
.split(/[.+-]/)
95+
.map((seg) => Number(seg) || 0);
96+
const a = parse(remote);
97+
const b = parse(current);
98+
for (let i = 0; i < Math.max(a.length, b.length); i++) {
99+
const x = a[i] ?? 0;
100+
const y = b[i] ?? 0;
101+
if (x !== y) return x > y;
102+
}
103+
return false;
104+
}
105+
67106
/** 仅供人工注意的敏感用法(沙箱才是真边界,这里只提示不拦截) */
68107
export const DANGER_PATTERNS = [
69108
/\beval\s*\(/,

scripts/intake.mjs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
import { existsSync, readFileSync, writeFileSync } from "node:fs";
99
import { execSync } from "node:child_process";
10-
import { parseHeader, validateSource } from "./header.mjs";
10+
import { parseHeader, validateSource, isNewerVersion } from "./header.mjs";
1111

1212
const token = process.env.GITHUB_TOKEN;
1313
const repo = process.env.GITHUB_REPOSITORY;
@@ -70,9 +70,18 @@ if (errors.length) {
7070
// 查重
7171
const file = `plugins/${id}.js`;
7272
const is_update = existsSync(file);
73-
const old_version = is_update ? (parseHeader(readFileSync(file, "utf-8")).version ?? "?") : null;
73+
const old_version = is_update ? (parseHeader(readFileSync(file, "utf-8")).version ?? "0.0.0") : null;
7474
const version = header.version ?? "0.0.0";
7575

76+
// 更新必须是严格递增的版本,杜绝同版本/降级盲覆盖(历史版本仍留在 git 提交记录里)
77+
if (is_update && !isNewerVersion(version, old_version)) {
78+
await setStatus(false);
79+
await comment(
80+
`${at}❌ 更新被拒:新版本 v${version} 必须高于现版本 v${old_version}。请提高 @version 后编辑本 issue 重新校验。`,
81+
);
82+
process.exit(0);
83+
}
84+
7685
// 仅把插件写进 PR;registry.json 由合并后的 build-registry 从 main 重建(避免并发丢条目)
7786
writeFileSync(file, source);
7887

scripts/review-status.mjs

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
/**
2-
* 维护者在 PR 提交 review 后同步 PR 的审核状态标签
2+
* 维护者在 PR 提交 review 后同步 PR 状态标签,并把结果回贴到关联 issue 通知投稿人
33
*
4-
* approved → 已通过;changes_requested → 已拒绝。仅协作者的 review 会触发分支保护放行。
4+
* approved → 已通过;changes_requested → 已拒绝。PR 由机器人创建(作者不是投稿人),
5+
* 故审核结果必须经 PR 正文的 Closes #N 回到原 issue,否则投稿人在 issue 上看不到驳回意见。
56
*/
67

78
import { readFileSync } from "node:fs";
@@ -10,8 +11,10 @@ const token = process.env.GITHUB_TOKEN;
1011
const repo = process.env.GITHUB_REPOSITORY;
1112
const api = process.env.GITHUB_API_URL ?? "https://api.github.com";
1213
const event = JSON.parse(readFileSync(process.env.GITHUB_EVENT_PATH, "utf-8"));
13-
const number = event.pull_request.number;
14-
const state = event.review.state;
14+
const pr = event.pull_request;
15+
const review = event.review;
16+
const number = pr.number;
17+
const state = review.state;
1518

1619
const gh = (method, path, payload) =>
1720
fetch(`${api}${path}`, {
@@ -22,12 +25,33 @@ const gh = (method, path, payload) =>
2225
const add = (n) => gh("POST", `/repos/${repo}/issues/${number}/labels`, { labels: [n] });
2326
const remove = (n) => gh("DELETE", `/repos/${repo}/issues/${number}/labels/${encodeURIComponent(n)}`);
2427

28+
/** 从 PR 正文的 Closes #N 取关联 issue 号 */
29+
const linkedIssue = () => {
30+
const matched = (pr.body ?? "").match(/#(\d+)/);
31+
return matched ? Number(matched[1]) : null;
32+
};
33+
34+
/** 回贴到关联 issue,并 @ 投稿人 */
35+
async function notifyIssue(body) {
36+
const issueNo = linkedIssue();
37+
if (!issueNo) return;
38+
const res = await gh("GET", `/repos/${repo}/issues/${issueNo}`);
39+
const issue = res.ok ? await res.json() : null;
40+
const at = issue?.user?.login ? `@${issue.user.login} ` : "";
41+
await gh("POST", `/repos/${repo}/issues/${issueNo}/comments`, { body: at + body });
42+
}
43+
2544
if (state === "approved") {
2645
await add("已通过");
2746
await remove("待审核");
2847
await remove("已拒绝");
48+
await notifyIssue("✅ 审核通过,合并后即收录到插件市场。");
2949
} else if (state === "changes_requested") {
3050
await add("已拒绝");
3151
await remove("待审核");
3252
await remove("已通过");
53+
const note = review.body?.trim() ? "\n\n> " + review.body.trim().replace(/\n/g, "\n> ") : "";
54+
await notifyIssue(
55+
`❌ 维护者请求修改:${note}\n\n请按意见修改脚本后,编辑本 issue 触发重新校验(或评论 \`/recheck\`)。`,
56+
);
3357
}

0 commit comments

Comments
 (0)