proof-pr 0.1.11 → 0.1.13

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 (3) hide show
  1. package/README.md +18 -2
  2. package/dist/index.js +221 -4
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -12,7 +12,15 @@ ProofPR 是给开源维护者和工程团队使用的 PR 证据门禁。它在
12
12
  npx proof-pr@latest --version
13
13
  ```
14
14
 
15
- 当前应输出 `0.1.11`。
15
+ 当前应输出 `0.1.13`。
16
+
17
+ 不知道用哪个功能时:
18
+
19
+ ```bash
20
+ npx proof-pr@latest
21
+ # 或
22
+ npx proof-pr@latest guide
23
+ ```
16
24
 
17
25
  初始化配置和 GitHub Action:
18
26
 
@@ -22,6 +30,14 @@ npx proof-pr@latest init
22
30
 
23
31
  这个命令会生成 `.proofpr.yml` 和 `.github/workflows/proofpr.yml`,提交后打开 PR 即可看到报告。
24
32
 
33
+ 体检接入状态:
34
+
35
+ ```bash
36
+ npx proof-pr@latest doctor
37
+ ```
38
+
39
+ 这个命令会检查配置文件、workflow、Action 版本、PR 权限和本地 diff 是否可读。
40
+
25
41
  本地扫描当前分支:
26
42
 
27
43
  ```bash
@@ -49,7 +65,7 @@ npx proof-pr@latest benchmark --cases benchmarks/cases
49
65
  ## GitHub Action
50
66
 
51
67
  ```yaml
52
- - uses: linsk27/proof-pr@v0.1.11
68
+ - uses: linsk27/proof-pr@v0.1.13
53
69
  with:
54
70
  fail-on: high
55
71
  comment: "true"
package/dist/index.js CHANGED
@@ -25728,11 +25728,32 @@ 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.13";
25731
25732
  const build_program = new Command();
25732
25733
  build_program
25733
25734
  .name("proof-pr")
25734
25735
  .description("Review pull request evidence, scope, and safety before maintainers spend time on it.")
25735
- .version("0.1.11");
25736
+ .version(CLI_VERSION);
25737
+ build_program
25738
+ .command("guide")
25739
+ .description("Show a copy-paste friendly guide for common ProofPR tasks.")
25740
+ .action(() => {
25741
+ process.stdout.write(renderGuide());
25742
+ });
25743
+ build_program
25744
+ .command("doctor")
25745
+ .description("Check whether ProofPR is installed correctly in the current repository.")
25746
+ .option("--config <path>", "Path to .proofpr.yml.", ".proofpr.yml")
25747
+ .option("--workflow-path <path>", "Path to the GitHub Actions workflow.", ".github/workflows/proofpr.yml")
25748
+ .option("--base <ref>", "Base git ref used for local diff checks.", "origin/main")
25749
+ .option("--head <ref>", "Head git ref used for local diff checks.", "HEAD")
25750
+ .action(async (options) => {
25751
+ const report = await runDoctor(options);
25752
+ process.stdout.write(renderDoctorReport(report));
25753
+ if (report.checks.some((check) => check.level === "fail")) {
25754
+ process.exitCode = 1;
25755
+ }
25756
+ });
25736
25757
  build_program
25737
25758
  .command("scan", { isDefault: true })
25738
25759
  .description("Scan a git diff and print a ProofPR report.")
@@ -25781,7 +25802,7 @@ build_program
25781
25802
  .action(async (options) => {
25782
25803
  await writeIfMissing(options.configPath, renderConfigTemplate(options.preset), options.force);
25783
25804
  await writeIfMissing(options.workflowPath, renderWorkflowTemplate(options.failOn), options.force);
25784
- process.stdout.write(`ProofPR initialized.\n\nCreated:\n- ${options.configPath}\n- ${options.workflowPath}\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`);
25805
+ process.stdout.write(`ProofPR initialized.\n\nCreated:\n- ${options.configPath}\n- ${options.workflowPath}\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`);
25785
25806
  });
25786
25807
  build_program
25787
25808
  .command("benchmark")
@@ -25812,11 +25833,207 @@ build_program
25812
25833
  process.exitCode = 1;
25813
25834
  }
25814
25835
  });
25815
- build_program.parseAsync(process.argv).catch((error) => {
25836
+ const args = process.argv.slice(2);
25837
+ const parseTask = args.length === 0
25838
+ ? Promise.resolve(process.stdout.write(renderGuide()))
25839
+ : build_program.parseAsync(process.argv);
25840
+ parseTask.catch((error) => {
25816
25841
  const message = error instanceof Error ? error.message : String(error);
25817
25842
  process.stderr.write(`ProofPR failed: ${message}\n`);
25818
25843
  process.exitCode = 1;
25819
25844
  });
25845
+ function renderGuide() {
25846
+ return `ProofPR 功能菜单
25847
+
25848
+ 最推荐先做第 1 步。已经接入过的项目,按目标复制下面的命令即可。
25849
+
25850
+ 1. 接入 GitHub PR 自动检查
25851
+ npx proof-pr@latest init
25852
+ 然后提交 .proofpr.yml 和 .github/workflows/proofpr.yml,打开 PR 后看评论和 Actions summary。
25853
+
25854
+ 2. 体检当前仓库接入状态
25855
+ npx proof-pr@latest doctor
25856
+ 检查配置文件、workflow、Action 版本、PR 权限和本地 diff 是否正常。
25857
+
25858
+ 3. 本地检查当前分支
25859
+ npx proof-pr@latest scan --base origin/main --head HEAD --locale zh-CN
25860
+ 适合在发 PR 前先看风险、证据评分和 Review 行动清单。
25861
+
25862
+ 4. 生成可分享 HTML 报告
25863
+ npx proof-pr@latest scan --base origin/main --head HEAD --locale zh-CN --format html --output proofpr-report.html
25864
+ 生成后用浏览器打开 proofpr-report.html。
25865
+
25866
+ 5. 生成 GitHub Code Scanning 的 SARIF
25867
+ npx proof-pr@latest scan --base origin/main --head HEAD --format sarif --output proofpr.sarif
25868
+ 适合在 CI 里配合 github/codeql-action/upload-sarif 使用。
25869
+
25870
+ 6. 试跑内置风险案例
25871
+ npx proof-pr@latest scan --diff-file examples/cases/workflow-untrusted-checkout.diff --locale zh-CN
25872
+ 不需要改项目代码,也能快速看到 ProofPR 会抓什么风险。
25873
+
25874
+ 7. 验证规则样本是否仍然命中
25875
+ npx proof-pr@latest benchmark --cases benchmarks/cases
25876
+ 适合维护 ProofPR 规则或发版前回归。
25877
+
25878
+ 8. 调整审查强度
25879
+ 打开 .proofpr.yml,把 preset 改成 security-strict、dependency-careful 或 mcp-security。
25880
+
25881
+ 结果在哪里看:
25882
+ - GitHub Action:PR Conversation 评论、Actions summary、Checks 状态。
25883
+ - 本地 CLI:终端输出;如果用了 --output,就看写出的 HTML / JSON / SARIF / Markdown 文件。
25884
+ `;
25885
+ }
25886
+ async function runDoctor(options) {
25887
+ const checks = [];
25888
+ const nextSteps = new Set();
25889
+ if (await pathExists(options.config)) {
25890
+ try {
25891
+ const config = await loadConfig(options.config);
25892
+ checks.push({
25893
+ level: "pass",
25894
+ title: `${options.config} 可读取`,
25895
+ detail: `locale=${config.locale}, preset=${config.preset}, riskThreshold=${config.riskThreshold}`
25896
+ });
25897
+ if (config.comment.enabled) {
25898
+ checks.push({ level: "pass", title: "PR 评论已启用", detail: "comment.enabled=true" });
25899
+ }
25900
+ else {
25901
+ checks.push({ level: "warn", title: "PR 评论未启用", detail: "comment.enabled=false" });
25902
+ nextSteps.add("如果希望在 PR Conversation 里看到报告,把 .proofpr.yml 的 comment.enabled 改成 true。");
25903
+ }
25904
+ }
25905
+ catch (error) {
25906
+ checks.push({
25907
+ level: "fail",
25908
+ title: `${options.config} 解析失败`,
25909
+ detail: error instanceof Error ? error.message : String(error)
25910
+ });
25911
+ nextSteps.add("修复 .proofpr.yml 的 YAML 格式,或重新运行 npx proof-pr@latest init --force。");
25912
+ }
25913
+ }
25914
+ else {
25915
+ checks.push({ level: "fail", title: `缺少 ${options.config}` });
25916
+ nextSteps.add("运行 npx proof-pr@latest init 生成 .proofpr.yml 和 GitHub Actions workflow。");
25917
+ }
25918
+ if (await pathExists(options.workflowPath)) {
25919
+ const workflow = await (0,promises_namespaceObject.readFile)(options.workflowPath, "utf8");
25920
+ checks.push({ level: "pass", title: `${options.workflowPath} 已存在` });
25921
+ inspectWorkflow(workflow, checks, nextSteps);
25922
+ }
25923
+ else {
25924
+ checks.push({ level: "fail", title: `缺少 ${options.workflowPath}` });
25925
+ nextSteps.add("运行 npx proof-pr@latest init 生成 .github/workflows/proofpr.yml。");
25926
+ }
25927
+ await inspectGitDiff(options, checks, nextSteps);
25928
+ if (nextSteps.size === 0) {
25929
+ nextSteps.add("当前接入状态正常;可以打开 PR,或运行 npx proof-pr@latest scan --base origin/main --head HEAD --locale zh-CN 做本地自查。");
25930
+ }
25931
+ return { checks, nextSteps: [...nextSteps] };
25932
+ }
25933
+ function inspectWorkflow(workflow, checks, nextSteps) {
25934
+ if (/pull_request\s*:/.test(workflow)) {
25935
+ checks.push({ level: "pass", title: "workflow 会在 Pull Request 事件运行" });
25936
+ }
25937
+ else {
25938
+ checks.push({ level: "fail", title: "workflow 没有监听 pull_request" });
25939
+ nextSteps.add("确认 .github/workflows/proofpr.yml 包含 on.pull_request。");
25940
+ }
25941
+ const actionVersion = workflow.match(/linsk27\/proof-pr@(v[0-9]+\.[0-9]+\.[0-9]+)/)?.[1];
25942
+ if (!actionVersion) {
25943
+ checks.push({ level: "fail", title: "workflow 没有使用 linsk27/proof-pr Action" });
25944
+ nextSteps.add(`把 workflow step 更新为 uses: linsk27/proof-pr@v${CLI_VERSION}。`);
25945
+ }
25946
+ else if (actionVersion === `v${CLI_VERSION}`) {
25947
+ checks.push({ level: "pass", title: `GitHub Action 版本为 ${actionVersion}` });
25948
+ }
25949
+ else {
25950
+ checks.push({
25951
+ level: "warn",
25952
+ title: `GitHub Action 版本较旧:${actionVersion}`,
25953
+ detail: `当前 CLI 版本是 v${CLI_VERSION}`
25954
+ });
25955
+ nextSteps.add(`把 workflow 里的 uses 更新为 linsk27/proof-pr@v${CLI_VERSION}。`);
25956
+ }
25957
+ if (/pull-requests\s*:\s*write/.test(workflow)) {
25958
+ checks.push({ level: "pass", title: "workflow 具备写 PR 评论权限" });
25959
+ }
25960
+ else {
25961
+ checks.push({ level: "warn", title: "workflow 可能缺少 pull-requests: write 权限" });
25962
+ nextSteps.add("如果需要自动评论 PR,在 workflow permissions 中加入 pull-requests: write。");
25963
+ }
25964
+ if (/contents\s*:\s*read/.test(workflow)) {
25965
+ checks.push({ level: "pass", title: "workflow 具备读取仓库内容权限" });
25966
+ }
25967
+ else {
25968
+ checks.push({ level: "warn", title: "workflow 未显式声明 contents: read" });
25969
+ nextSteps.add("建议在 workflow permissions 中加入 contents: read。");
25970
+ }
25971
+ }
25972
+ async function inspectGitDiff(options, checks, nextSteps) {
25973
+ try {
25974
+ const { stdout } = await execFileAsync("git", ["rev-parse", "--is-inside-work-tree"]);
25975
+ if (stdout.trim() !== "true") {
25976
+ checks.push({ level: "warn", title: "当前目录不是 Git 仓库" });
25977
+ nextSteps.add("进入项目 Git 仓库根目录后再运行 npx proof-pr@latest doctor。");
25978
+ return;
25979
+ }
25980
+ }
25981
+ catch {
25982
+ checks.push({ level: "warn", title: "当前目录不是 Git 仓库" });
25983
+ nextSteps.add("进入项目 Git 仓库根目录后再运行 npx proof-pr@latest doctor。");
25984
+ return;
25985
+ }
25986
+ checks.push({ level: "pass", title: "当前目录位于 Git 仓库中" });
25987
+ try {
25988
+ const branch = (await execFileAsync("git", ["branch", "--show-current"])).stdout.trim();
25989
+ checks.push({
25990
+ level: "info",
25991
+ title: branch ? `当前分支:${branch}` : "当前处于 detached HEAD"
25992
+ });
25993
+ }
25994
+ catch {
25995
+ checks.push({ level: "info", title: "无法读取当前分支名" });
25996
+ }
25997
+ try {
25998
+ const diff = await readGitDiff(options.base, options.head);
25999
+ checks.push({
26000
+ level: "pass",
26001
+ title: `可以读取 ${options.base}...${options.head} diff`,
26002
+ detail: diff.length === 0 ? "当前没有可扫描的 diff。" : `diff 大小约 ${diff.length} 字符。`
26003
+ });
26004
+ }
26005
+ catch (error) {
26006
+ checks.push({
26007
+ level: "warn",
26008
+ title: `无法读取 ${options.base}...${options.head} diff`,
26009
+ detail: error instanceof Error ? error.message : String(error)
26010
+ });
26011
+ nextSteps.add(`运行 git fetch origin 后重试;如果主分支不是 main,请使用 --base origin/master 或你的实际主分支。`);
26012
+ }
26013
+ }
26014
+ function renderDoctorReport(report) {
26015
+ const failCount = report.checks.filter((check) => check.level === "fail").length;
26016
+ const warnCount = report.checks.filter((check) => check.level === "warn").length;
26017
+ const status = failCount > 0 ? "需要先修复" : warnCount > 0 ? "基本可用,但建议优化" : "接入正常";
26018
+ const checks = report.checks
26019
+ .map((check) => {
26020
+ const detail = check.detail ? `\n ${check.detail}` : "";
26021
+ return `[${check.level}] ${check.title}${detail}`;
26022
+ })
26023
+ .join("\n");
26024
+ const nextSteps = report.nextSteps.map((step) => `- ${step}`).join("\n");
26025
+ return `ProofPR doctor
26026
+
26027
+ 状态:${status}
26028
+ 统计:${failCount} fail, ${warnCount} warn, ${report.checks.length} checks
26029
+
26030
+ Checks:
26031
+ ${checks}
26032
+
26033
+ Next:
26034
+ ${nextSteps}
26035
+ `;
26036
+ }
25820
26037
  async function readGitDiff(base, head) {
25821
26038
  const args = ["diff", "--no-ext-diff", "--unified=0"];
25822
26039
  if (base) {
@@ -25878,7 +26095,7 @@ jobs:
25878
26095
  runs-on: ubuntu-latest
25879
26096
  steps:
25880
26097
  - uses: actions/checkout@v4
25881
- - uses: linsk27/proof-pr@v0.1.11
26098
+ - uses: linsk27/proof-pr@v${CLI_VERSION}
25882
26099
  with:
25883
26100
  fail-on: ${failOn}
25884
26101
  comment: "true"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "proof-pr",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
4
4
  "description": "CLI for ProofPR, a maintainer-focused pull request evidence scanner.",
5
5
  "license": "MIT",
6
6
  "type": "module",