proof-pr 0.1.13 → 0.1.15
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/README.md +18 -5
- package/dist/index.js +287 -12
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ ProofPR 是给开源维护者和工程团队使用的 PR 证据门禁。它在
|
|
|
12
12
|
npx proof-pr@latest --version
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
当前应输出 `0.1.
|
|
15
|
+
当前应输出 `0.1.15`。
|
|
16
16
|
|
|
17
17
|
不知道用哪个功能时:
|
|
18
18
|
|
|
@@ -22,13 +22,26 @@ npx proof-pr@latest
|
|
|
22
22
|
npx proof-pr@latest guide
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
+
不接入仓库,先体验报告:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npx proof-pr@latest demo workflow --locale zh-CN
|
|
29
|
+
npx proof-pr@latest demo --list
|
|
30
|
+
```
|
|
31
|
+
|
|
25
32
|
初始化配置和 GitHub Action:
|
|
26
33
|
|
|
27
34
|
```bash
|
|
28
35
|
npx proof-pr@latest init
|
|
29
36
|
```
|
|
30
37
|
|
|
31
|
-
这个命令会生成 `.proofpr.yml` 和 `.github/
|
|
38
|
+
这个命令会生成 `.proofpr.yml`、`.github/workflows/proofpr.yml` 和 `.github/pull_request_template.md`,提交后打开 PR 即可看到报告。
|
|
39
|
+
|
|
40
|
+
已接入仓库单独补 PR 模板:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npx proof-pr@latest template
|
|
44
|
+
```
|
|
32
45
|
|
|
33
46
|
体检接入状态:
|
|
34
47
|
|
|
@@ -36,7 +49,7 @@ npx proof-pr@latest init
|
|
|
36
49
|
npx proof-pr@latest doctor
|
|
37
50
|
```
|
|
38
51
|
|
|
39
|
-
这个命令会检查配置文件、workflow、Action 版本、PR 权限和本地 diff 是否可读。
|
|
52
|
+
这个命令会检查配置文件、workflow、PR 模板、Action 版本、PR 权限和本地 diff 是否可读。
|
|
40
53
|
|
|
41
54
|
本地扫描当前分支:
|
|
42
55
|
|
|
@@ -47,7 +60,7 @@ npx proof-pr@latest scan --base origin/main --head HEAD --locale zh-CN
|
|
|
47
60
|
扫描内置案例:
|
|
48
61
|
|
|
49
62
|
```bash
|
|
50
|
-
npx proof-pr@latest
|
|
63
|
+
npx proof-pr@latest demo workflow --locale zh-CN
|
|
51
64
|
```
|
|
52
65
|
|
|
53
66
|
生成独立 HTML 可视化报告:
|
|
@@ -65,7 +78,7 @@ npx proof-pr@latest benchmark --cases benchmarks/cases
|
|
|
65
78
|
## GitHub Action
|
|
66
79
|
|
|
67
80
|
```yaml
|
|
68
|
-
- uses: linsk27/proof-pr@v0.1.
|
|
81
|
+
- uses: linsk27/proof-pr@v0.1.15
|
|
69
82
|
with:
|
|
70
83
|
fail-on: high
|
|
71
84
|
comment: "true"
|
package/dist/index.js
CHANGED
|
@@ -25728,7 +25728,131 @@ function dedupeFindings(findings) {
|
|
|
25728
25728
|
|
|
25729
25729
|
|
|
25730
25730
|
const execFileAsync = (0,external_node_util_namespaceObject.promisify)(external_node_child_process_.execFile);
|
|
25731
|
-
const CLI_VERSION = "0.1.
|
|
25731
|
+
const CLI_VERSION = "0.1.15";
|
|
25732
|
+
const DEMO_CASES = [
|
|
25733
|
+
{
|
|
25734
|
+
id: "workflow",
|
|
25735
|
+
title: "高权限 workflow 运行不可信 PR 代码",
|
|
25736
|
+
description: "演示 pull_request_target 与 PR head checkout 组合风险。",
|
|
25737
|
+
diffText: `diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml
|
|
25738
|
+
index 1111111..2222222 100644
|
|
25739
|
+
--- a/.github/workflows/pr.yml
|
|
25740
|
+
+++ b/.github/workflows/pr.yml
|
|
25741
|
+
@@ -1,7 +1,17 @@
|
|
25742
|
+
name: PR automation
|
|
25743
|
+
on:
|
|
25744
|
+
+ pull_request_target:
|
|
25745
|
+
+ types: [opened, synchronize]
|
|
25746
|
+
+
|
|
25747
|
+
jobs:
|
|
25748
|
+
test:
|
|
25749
|
+
runs-on: ubuntu-latest
|
|
25750
|
+
steps:
|
|
25751
|
+
- - uses: actions/checkout@v4
|
|
25752
|
+
+ - uses: actions/checkout@v4
|
|
25753
|
+
+ with:
|
|
25754
|
+
+ repository: \${{ github.event.pull_request.head.repo.full_name }}
|
|
25755
|
+
+ ref: \${{ github.event.pull_request.head.sha }}
|
|
25756
|
+
+ - run: pnpm install
|
|
25757
|
+
+ - run: pnpm test
|
|
25758
|
+
`
|
|
25759
|
+
},
|
|
25760
|
+
{
|
|
25761
|
+
id: "secret",
|
|
25762
|
+
title: "疑似 secret 被提交",
|
|
25763
|
+
description: "演示 .env、OpenAI key、数据库连接串等敏感内容会被拦截。",
|
|
25764
|
+
diffText: `diff --git a/.env b/.env
|
|
25765
|
+
new file mode 100644
|
|
25766
|
+
index 0000000..1111111
|
|
25767
|
+
--- /dev/null
|
|
25768
|
+
+++ b/.env
|
|
25769
|
+
@@ -0,0 +1,2 @@
|
|
25770
|
+
+OPENAI_API_KEY=sk-proj-examplevalueexamplevalue1234567890
|
|
25771
|
+
+DATABASE_URL=postgres://demo:super-secret-password@example.com:5432/app
|
|
25772
|
+
`
|
|
25773
|
+
},
|
|
25774
|
+
{
|
|
25775
|
+
id: "dependency",
|
|
25776
|
+
title: "依赖大版本升级",
|
|
25777
|
+
description: "演示依赖 major upgrade 需要 changelog、迁移说明和验证证据。",
|
|
25778
|
+
diffText: `diff --git a/package.json b/package.json
|
|
25779
|
+
index 1111111..2222222 100644
|
|
25780
|
+
--- a/package.json
|
|
25781
|
+
+++ b/package.json
|
|
25782
|
+
@@ -1,8 +1,8 @@
|
|
25783
|
+
{
|
|
25784
|
+
"dependencies": {
|
|
25785
|
+
- "react": "^18.2.0",
|
|
25786
|
+
+ "react": "^19.0.0",
|
|
25787
|
+
"zod": "^3.25.1"
|
|
25788
|
+
},
|
|
25789
|
+
"devDependencies": {
|
|
25790
|
+
"typescript": "^5.9.3"
|
|
25791
|
+
}
|
|
25792
|
+
}
|
|
25793
|
+
`
|
|
25794
|
+
},
|
|
25795
|
+
{
|
|
25796
|
+
id: "mcp",
|
|
25797
|
+
title: "MCP 本地命令和凭据面",
|
|
25798
|
+
description: "演示 MCP / agent 配置中的 command、args、env 风险。",
|
|
25799
|
+
diffText: `diff --git a/.cursor/mcp.json b/.cursor/mcp.json
|
|
25800
|
+
new file mode 100644
|
|
25801
|
+
index 0000000..1111111
|
|
25802
|
+
--- /dev/null
|
|
25803
|
+
+++ b/.cursor/mcp.json
|
|
25804
|
+
@@ -0,0 +1,11 @@
|
|
25805
|
+
+{
|
|
25806
|
+
+ "mcpServers": {
|
|
25807
|
+
+ "local-admin": {
|
|
25808
|
+
+ "command": "node",
|
|
25809
|
+
+ "args": ["scripts/admin-server.js"],
|
|
25810
|
+
+ "env": {
|
|
25811
|
+
+ "API_TOKEN": "\${LOCAL_API_TOKEN}"
|
|
25812
|
+
+ }
|
|
25813
|
+
+ }
|
|
25814
|
+
+ }
|
|
25815
|
+
+}
|
|
25816
|
+
`,
|
|
25817
|
+
config: { preset: "mcp-security" }
|
|
25818
|
+
},
|
|
25819
|
+
{
|
|
25820
|
+
id: "ui-evidence",
|
|
25821
|
+
title: "UI 改动缺少截图证据",
|
|
25822
|
+
description: "演示 Evidence Contract:组件改动必须提供截图和验证说明。",
|
|
25823
|
+
diffText: `diff --git a/src/components/Button.tsx b/src/components/Button.tsx
|
|
25824
|
+
index 1111111..2222222 100644
|
|
25825
|
+
--- a/src/components/Button.tsx
|
|
25826
|
+
+++ b/src/components/Button.tsx
|
|
25827
|
+
@@ -1,3 +1,7 @@
|
|
25828
|
+
export function Button() {
|
|
25829
|
+
- return <button>Save</button>;
|
|
25830
|
+
+ return (
|
|
25831
|
+
+ <button className="primary">
|
|
25832
|
+
+ Save
|
|
25833
|
+
+ </button>
|
|
25834
|
+
+ );
|
|
25835
|
+
}
|
|
25836
|
+
`,
|
|
25837
|
+
config: {
|
|
25838
|
+
evidence: {
|
|
25839
|
+
contracts: [
|
|
25840
|
+
{
|
|
25841
|
+
id: "ui-screenshot",
|
|
25842
|
+
title: "UI changes need screenshots",
|
|
25843
|
+
paths: ["src/components/**"],
|
|
25844
|
+
requires: ["screenshot", "verification"],
|
|
25845
|
+
severity: "medium"
|
|
25846
|
+
}
|
|
25847
|
+
]
|
|
25848
|
+
}
|
|
25849
|
+
},
|
|
25850
|
+
pullRequest: {
|
|
25851
|
+
title: "Update button styling",
|
|
25852
|
+
body: "This updates the primary button style and spacing so the layout is easier to scan."
|
|
25853
|
+
}
|
|
25854
|
+
}
|
|
25855
|
+
];
|
|
25732
25856
|
const build_program = new Command();
|
|
25733
25857
|
build_program
|
|
25734
25858
|
.name("proof-pr")
|
|
@@ -25745,6 +25869,7 @@ build_program
|
|
|
25745
25869
|
.description("Check whether ProofPR is installed correctly in the current repository.")
|
|
25746
25870
|
.option("--config <path>", "Path to .proofpr.yml.", ".proofpr.yml")
|
|
25747
25871
|
.option("--workflow-path <path>", "Path to the GitHub Actions workflow.", ".github/workflows/proofpr.yml")
|
|
25872
|
+
.option("--pr-template-path <path>", "Path to the pull request template.", ".github/pull_request_template.md")
|
|
25748
25873
|
.option("--base <ref>", "Base git ref used for local diff checks.", "origin/main")
|
|
25749
25874
|
.option("--head <ref>", "Head git ref used for local diff checks.", "HEAD")
|
|
25750
25875
|
.action(async (options) => {
|
|
@@ -25754,6 +25879,46 @@ build_program
|
|
|
25754
25879
|
process.exitCode = 1;
|
|
25755
25880
|
}
|
|
25756
25881
|
});
|
|
25882
|
+
build_program
|
|
25883
|
+
.command("template")
|
|
25884
|
+
.description("Create a ProofPR-friendly pull request template.")
|
|
25885
|
+
.option("--output <path>", "Path to write the pull request template.", ".github/pull_request_template.md")
|
|
25886
|
+
.option("--force", "Overwrite the existing template.", false)
|
|
25887
|
+
.action(async (options) => {
|
|
25888
|
+
await writeIfMissing(options.output, renderPullRequestTemplate(), options.force);
|
|
25889
|
+
process.stdout.write(`ProofPR pull request template written to ${options.output}\n\nNext:\n1. Commit the template.\n2. Ask contributors to fill verification, reproduction, screenshot, changelog, and permission rationale sections when relevant.\n3. Run npx proof-pr@latest doctor to check setup.\n`);
|
|
25890
|
+
});
|
|
25891
|
+
build_program
|
|
25892
|
+
.command("demo")
|
|
25893
|
+
.description("Run a built-in ProofPR demo case without cloning this repository.")
|
|
25894
|
+
.argument("[case]", "Demo case id. Use --list to see available cases.", "workflow")
|
|
25895
|
+
.option("--list", "List built-in demo cases.", false)
|
|
25896
|
+
.option("--format <format>", "Output format: markdown, json, sarif, or html.", parseFormat, "markdown")
|
|
25897
|
+
.option("--output <path>", "Write demo report output to a file instead of stdout.")
|
|
25898
|
+
.option("--locale <locale>", "Report language: en or zh-CN.", "zh-CN")
|
|
25899
|
+
.action(async (caseId, options) => {
|
|
25900
|
+
if (options.list) {
|
|
25901
|
+
process.stdout.write(renderDemoList());
|
|
25902
|
+
return;
|
|
25903
|
+
}
|
|
25904
|
+
const demoCase = DEMO_CASES.find((item) => item.id === caseId);
|
|
25905
|
+
if (!demoCase) {
|
|
25906
|
+
throw new Error(`Unknown demo case "${caseId}". Run "proof-pr demo --list" to see available cases.`);
|
|
25907
|
+
}
|
|
25908
|
+
const result = scanDiff(demoCase.diffText, {
|
|
25909
|
+
config: demoCase.config,
|
|
25910
|
+
pullRequest: demoCase.pullRequest
|
|
25911
|
+
});
|
|
25912
|
+
const locale = parseLocale(options.locale, "zh-CN");
|
|
25913
|
+
const output = renderDemoOutput(demoCase, result, options.format, locale);
|
|
25914
|
+
if (options.output) {
|
|
25915
|
+
await writeOutput(options.output, `${output}\n`);
|
|
25916
|
+
process.stdout.write(`ProofPR demo ${options.format} report written to ${options.output}\n`);
|
|
25917
|
+
}
|
|
25918
|
+
else {
|
|
25919
|
+
process.stdout.write(`${output}\n`);
|
|
25920
|
+
}
|
|
25921
|
+
});
|
|
25757
25922
|
build_program
|
|
25758
25923
|
.command("scan", { isDefault: true })
|
|
25759
25924
|
.description("Scan a git diff and print a ProofPR report.")
|
|
@@ -25796,13 +25961,26 @@ build_program
|
|
|
25796
25961
|
.description("Create a starter .proofpr.yml and GitHub Actions workflow.")
|
|
25797
25962
|
.option("--config-path <path>", "Path to write the ProofPR configuration file.", ".proofpr.yml")
|
|
25798
25963
|
.option("--workflow-path <path>", "Path to write the GitHub Actions workflow.", ".github/workflows/proofpr.yml")
|
|
25964
|
+
.option("--no-pr-template", "Skip creating .github/pull_request_template.md.")
|
|
25965
|
+
.option("--pr-template-path <path>", "Path to write the pull request template.", ".github/pull_request_template.md")
|
|
25799
25966
|
.option("--preset <preset>", `Config preset: ${listConfigPresets().join(", ")}.`, parsePresetOption, "open-source-maintainer")
|
|
25800
25967
|
.option("--fail-on <level>", "Workflow failure threshold: low, medium, high, or never.", parseFailLevel, "high")
|
|
25801
25968
|
.option("--force", "Overwrite existing files.", false)
|
|
25802
25969
|
.action(async (options) => {
|
|
25803
25970
|
await writeIfMissing(options.configPath, renderConfigTemplate(options.preset), options.force);
|
|
25804
25971
|
await writeIfMissing(options.workflowPath, renderWorkflowTemplate(options.failOn), options.force);
|
|
25805
|
-
|
|
25972
|
+
const created = [options.configPath, options.workflowPath];
|
|
25973
|
+
const skipped = [];
|
|
25974
|
+
if (options.prTemplate) {
|
|
25975
|
+
const wroteTemplate = await writeIfMissingSoft(options.prTemplatePath, renderPullRequestTemplate(), options.force);
|
|
25976
|
+
if (wroteTemplate) {
|
|
25977
|
+
created.push(options.prTemplatePath);
|
|
25978
|
+
}
|
|
25979
|
+
else {
|
|
25980
|
+
skipped.push(`${options.prTemplatePath} already exists`);
|
|
25981
|
+
}
|
|
25982
|
+
}
|
|
25983
|
+
process.stdout.write(`ProofPR initialized.\n\nCreated:\n${created.map((item) => `- ${item}`).join("\n")}${skipped.length > 0 ? `\n\nSkipped:\n${skipped.map((item) => `- ${item}`).join("\n")}` : ""}\n\nNext:\n1. Commit these files.\n2. Open or update a pull request.\n3. Read the ProofPR comment or Actions summary.\n\nLocal check:\nnpx proof-pr@latest scan --base origin/main --head HEAD --locale zh-CN\n\nNeed another task?\nnpx proof-pr@latest guide\n`);
|
|
25806
25984
|
});
|
|
25807
25985
|
build_program
|
|
25808
25986
|
.command("benchmark")
|
|
@@ -25849,33 +26027,40 @@ function renderGuide() {
|
|
|
25849
26027
|
|
|
25850
26028
|
1. 接入 GitHub PR 自动检查
|
|
25851
26029
|
npx proof-pr@latest init
|
|
25852
|
-
然后提交 .proofpr.yml 和 .github/
|
|
26030
|
+
然后提交 .proofpr.yml、.github/workflows/proofpr.yml 和 .github/pull_request_template.md,打开 PR 后看评论和 Actions summary。
|
|
25853
26031
|
|
|
25854
26032
|
2. 体检当前仓库接入状态
|
|
25855
26033
|
npx proof-pr@latest doctor
|
|
25856
|
-
检查配置文件、workflow、Action 版本、PR 权限和本地 diff 是否正常。
|
|
26034
|
+
检查配置文件、workflow、PR 模板、Action 版本、PR 权限和本地 diff 是否正常。
|
|
25857
26035
|
|
|
25858
|
-
3.
|
|
26036
|
+
3. 已接入仓库,单独补 PR 模板
|
|
26037
|
+
npx proof-pr@latest template
|
|
26038
|
+
引导贡献者填写验证、复现、截图、changelog 和权限理由。
|
|
26039
|
+
|
|
26040
|
+
4. 本地检查当前分支
|
|
25859
26041
|
npx proof-pr@latest scan --base origin/main --head HEAD --locale zh-CN
|
|
25860
26042
|
适合在发 PR 前先看风险、证据评分和 Review 行动清单。
|
|
25861
26043
|
|
|
25862
|
-
|
|
26044
|
+
5. 生成可分享 HTML 报告
|
|
25863
26045
|
npx proof-pr@latest scan --base origin/main --head HEAD --locale zh-CN --format html --output proofpr-report.html
|
|
25864
26046
|
生成后用浏览器打开 proofpr-report.html。
|
|
25865
26047
|
|
|
25866
|
-
|
|
26048
|
+
6. 生成 GitHub Code Scanning 的 SARIF
|
|
25867
26049
|
npx proof-pr@latest scan --base origin/main --head HEAD --format sarif --output proofpr.sarif
|
|
25868
26050
|
适合在 CI 里配合 github/codeql-action/upload-sarif 使用。
|
|
25869
26051
|
|
|
25870
|
-
|
|
25871
|
-
npx proof-pr@latest
|
|
25872
|
-
|
|
26052
|
+
7. 不接入仓库,先试跑内置案例
|
|
26053
|
+
npx proof-pr@latest demo workflow --locale zh-CN
|
|
26054
|
+
不需要 clone 仓库或寻找 examples 文件,也能快速看到 ProofPR 会抓什么风险。
|
|
26055
|
+
|
|
26056
|
+
8. 查看所有内置案例
|
|
26057
|
+
npx proof-pr@latest demo --list
|
|
25873
26058
|
|
|
25874
|
-
|
|
26059
|
+
9. 验证规则样本是否仍然命中
|
|
25875
26060
|
npx proof-pr@latest benchmark --cases benchmarks/cases
|
|
25876
26061
|
适合维护 ProofPR 规则或发版前回归。
|
|
25877
26062
|
|
|
25878
|
-
|
|
26063
|
+
10. 调整审查强度
|
|
25879
26064
|
打开 .proofpr.yml,把 preset 改成 security-strict、dependency-careful 或 mcp-security。
|
|
25880
26065
|
|
|
25881
26066
|
结果在哪里看:
|
|
@@ -25883,6 +26068,37 @@ function renderGuide() {
|
|
|
25883
26068
|
- 本地 CLI:终端输出;如果用了 --output,就看写出的 HTML / JSON / SARIF / Markdown 文件。
|
|
25884
26069
|
`;
|
|
25885
26070
|
}
|
|
26071
|
+
function renderDemoList() {
|
|
26072
|
+
const rows = DEMO_CASES.map((item) => `- ${item.id}: ${item.title}\n ${item.description}`).join("\n");
|
|
26073
|
+
return `ProofPR 内置案例
|
|
26074
|
+
|
|
26075
|
+
用法:
|
|
26076
|
+
npx proof-pr@latest demo <case> --locale zh-CN
|
|
26077
|
+
|
|
26078
|
+
可用案例:
|
|
26079
|
+
${rows}
|
|
26080
|
+
`;
|
|
26081
|
+
}
|
|
26082
|
+
function renderDemoOutput(demoCase, result, format, locale) {
|
|
26083
|
+
if (format === "json") {
|
|
26084
|
+
return JSON.stringify({
|
|
26085
|
+
demo: {
|
|
26086
|
+
id: demoCase.id,
|
|
26087
|
+
title: demoCase.title,
|
|
26088
|
+
description: demoCase.description
|
|
26089
|
+
},
|
|
26090
|
+
result
|
|
26091
|
+
}, null, 2);
|
|
26092
|
+
}
|
|
26093
|
+
if (format === "markdown") {
|
|
26094
|
+
return `# ProofPR demo: ${demoCase.title}
|
|
26095
|
+
|
|
26096
|
+
${demoCase.description}
|
|
26097
|
+
|
|
26098
|
+
${renderMarkdownReport(result, locale)}`;
|
|
26099
|
+
}
|
|
26100
|
+
return renderOutput(result, format, locale);
|
|
26101
|
+
}
|
|
25886
26102
|
async function runDoctor(options) {
|
|
25887
26103
|
const checks = [];
|
|
25888
26104
|
const nextSteps = new Set();
|
|
@@ -25924,6 +26140,7 @@ async function runDoctor(options) {
|
|
|
25924
26140
|
checks.push({ level: "fail", title: `缺少 ${options.workflowPath}` });
|
|
25925
26141
|
nextSteps.add("运行 npx proof-pr@latest init 生成 .github/workflows/proofpr.yml。");
|
|
25926
26142
|
}
|
|
26143
|
+
await inspectPullRequestTemplate(options.prTemplatePath, checks, nextSteps);
|
|
25927
26144
|
await inspectGitDiff(options, checks, nextSteps);
|
|
25928
26145
|
if (nextSteps.size === 0) {
|
|
25929
26146
|
nextSteps.add("当前接入状态正常;可以打开 PR,或运行 npx proof-pr@latest scan --base origin/main --head HEAD --locale zh-CN 做本地自查。");
|
|
@@ -25969,6 +26186,29 @@ function inspectWorkflow(workflow, checks, nextSteps) {
|
|
|
25969
26186
|
nextSteps.add("建议在 workflow permissions 中加入 contents: read。");
|
|
25970
26187
|
}
|
|
25971
26188
|
}
|
|
26189
|
+
async function inspectPullRequestTemplate(path, checks, nextSteps) {
|
|
26190
|
+
if (!(await pathExists(path))) {
|
|
26191
|
+
checks.push({ level: "warn", title: `缺少 ${path}` });
|
|
26192
|
+
nextSteps.add("运行 npx proof-pr@latest template 生成 PR 模板,引导贡献者补充验证、复现、截图和权限理由。");
|
|
26193
|
+
return;
|
|
26194
|
+
}
|
|
26195
|
+
const template = await (0,promises_namespaceObject.readFile)(path, "utf8");
|
|
26196
|
+
checks.push({ level: "pass", title: `${path} 已存在` });
|
|
26197
|
+
if (/验证|verification|test/i.test(template)) {
|
|
26198
|
+
checks.push({ level: "pass", title: "PR 模板会提示验证证据" });
|
|
26199
|
+
}
|
|
26200
|
+
else {
|
|
26201
|
+
checks.push({ level: "warn", title: "PR 模板没有明显的验证证据提示" });
|
|
26202
|
+
nextSteps.add("在 PR 模板中加入“验证方式”栏目,减少 ProofPR 报告里的证据不足。");
|
|
26203
|
+
}
|
|
26204
|
+
if (/复现|reproduction|before|after|截图|screenshot|权限|permission/i.test(template)) {
|
|
26205
|
+
checks.push({ level: "pass", title: "PR 模板覆盖复现、截图或权限理由提示" });
|
|
26206
|
+
}
|
|
26207
|
+
else {
|
|
26208
|
+
checks.push({ level: "warn", title: "PR 模板缺少复现、截图或权限理由提示" });
|
|
26209
|
+
nextSteps.add("在 PR 模板中加入复现、截图、changelog、权限理由等可选栏目。");
|
|
26210
|
+
}
|
|
26211
|
+
}
|
|
25972
26212
|
async function inspectGitDiff(options, checks, nextSteps) {
|
|
25973
26213
|
try {
|
|
25974
26214
|
const { stdout } = await execFileAsync("git", ["rev-parse", "--is-inside-work-tree"]);
|
|
@@ -26055,6 +26295,14 @@ async function writeIfMissing(path, contents, force) {
|
|
|
26055
26295
|
await (0,promises_namespaceObject.mkdir)((0,external_node_path_.dirname)(path), { recursive: true });
|
|
26056
26296
|
await (0,promises_namespaceObject.writeFile)(path, contents, "utf8");
|
|
26057
26297
|
}
|
|
26298
|
+
async function writeIfMissingSoft(path, contents, force) {
|
|
26299
|
+
if (!force && (await pathExists(path))) {
|
|
26300
|
+
return false;
|
|
26301
|
+
}
|
|
26302
|
+
await (0,promises_namespaceObject.mkdir)((0,external_node_path_.dirname)(path), { recursive: true });
|
|
26303
|
+
await (0,promises_namespaceObject.writeFile)(path, contents, "utf8");
|
|
26304
|
+
return true;
|
|
26305
|
+
}
|
|
26058
26306
|
async function writeOutput(path, contents) {
|
|
26059
26307
|
await (0,promises_namespaceObject.mkdir)((0,external_node_path_.dirname)(path), { recursive: true });
|
|
26060
26308
|
await (0,promises_namespaceObject.writeFile)(path, contents, "utf8");
|
|
@@ -26102,6 +26350,33 @@ jobs:
|
|
|
26102
26350
|
annotations: "true"
|
|
26103
26351
|
`;
|
|
26104
26352
|
}
|
|
26353
|
+
function renderPullRequestTemplate() {
|
|
26354
|
+
return `## 变更说明
|
|
26355
|
+
|
|
26356
|
+
请说明这个 PR 为什么需要、改了什么、影响范围是什么。
|
|
26357
|
+
|
|
26358
|
+
## 验证方式
|
|
26359
|
+
|
|
26360
|
+
- [ ] 已运行自动化测试:
|
|
26361
|
+
- [ ] 已完成手动验证:
|
|
26362
|
+
- [ ] 不需要测试,原因:
|
|
26363
|
+
|
|
26364
|
+
## 复现 / Before & After
|
|
26365
|
+
|
|
26366
|
+
如果是 bug fix,请写复现步骤、预期结果和实际结果。
|
|
26367
|
+
如果是 UI 改动,请附 before/after 截图或录屏。
|
|
26368
|
+
|
|
26369
|
+
## 依赖 / CI / 权限 / MCP 变更
|
|
26370
|
+
|
|
26371
|
+
如果改了依赖、lockfile、GitHub Actions、MCP、环境变量或权限,请说明原因和安全影响。
|
|
26372
|
+
|
|
26373
|
+
## 发布风险
|
|
26374
|
+
|
|
26375
|
+
- [ ] 无破坏性变更
|
|
26376
|
+
- [ ] 需要迁移说明 / changelog
|
|
26377
|
+
- [ ] 需要灰度或回滚方案
|
|
26378
|
+
`;
|
|
26379
|
+
}
|
|
26105
26380
|
function renderOutput(result, format, locale) {
|
|
26106
26381
|
if (format === "json") {
|
|
26107
26382
|
return JSON.stringify(result, null, 2);
|