anti-slop源码深度解析(二):inherited数据机制如何消灭非默认分支PR的误报
【免费下载链接】anti-slop
A GitHub action that detects and automatically closes low-quality and AI slop PRs.
anti-slop 是一个自动检测并关闭低质量与 AI slop PR 的 GitHub Action。本文从源码层面拆解它的 inherited 数据机制,讲清楚它如何用一次 API 比较调用,消灭 PR 指向非默认分支(如next)时产生的误报,让检查只评价作者真正写出的代码。
🧩 一、问题背景:为什么非默认分支 PR 会"背锅"
多数仓库的 PR 直接指向默认分支main,但不少团队采用发布流:日常开发先进next分支,再合回main。贡献者的 PR 常常指向next。
当next落后于main时,问题出现了:
main(默认分支): A ── B ── C ── D │ next(PR目标分支): A ── B ── C │ feature(PR源分支): A ── B ── C ── D ── E ↑ ↑ 继承而来 E:作者真正的提交- 贡献者通常基于
main拉分支,feature 分支天然包含提交D; - PR 指向
next时,GitHub API 返回的 commit 列表和文件列表里会带出D以及它改动的文件; - 这些内容不是本次 PR 作者写的,但 commit 检查、file 检查会照单全收 → 误报;
- 误报累计达到
max-failures(默认 4)时,一个无辜 PR 就会被自动关闭。
官方 CHANGELOG.md 用两个版本逐步修掉了这个问题:
- v0.2.0:将继承的默认分支提交排除出 commit 检查
- v0.2.1:将继承文件排除出 file 检查,并把检测逻辑抽取为共享模块
🗂️ 二、inherited 数据是什么
一句话定义:inherited 数据 = 仓库默认分支上已有、但 PR 目标分支尚未合入的那批 commit 和文件。
数据结构非常简单,定义在 src/types/inherited-data.ts:
export interface InheritedData { shas: Set<string>; // 继承的 commit sha files: Set<string>; // 继承的文件路径 }两个Set,各自提供 O(1) 的成员查找。下面看它们怎么产生。
⚙️ 三、getInheritedData 源码拆解:一次调用拿到全部
核心实现在 src/inherited.ts,总共 30 行左右,关键有 4 步。
第 1 步:默认分支短路
if (context.baseBranch === context.defaultBranch) return { shas: new Set(), files: new Set() };见 src/inherited.ts。PR 直接指向main时,继承数据必然为空,直接返回空集合,一次 API 都不发。这是最常见的场景,这一行省掉了绝大多数请求。baseBranch与defaultBranch来自 PR 事件 payload,提取逻辑在 src/context.ts。
第 2 步:一次 compare 拿到全部
const { data } = await client.rest.repos.compareCommitsWithBasehead({ owner: context.owner, repo: context.repo, basehead: `${context.baseBranch}...${context.defaultBranch}`, });见 src/inherited.ts。注意三点语法:next...main表示"在main上但不在next上的 commit",方向恰好就是继承集合,反过来比就不对了。
第 3 步:构建两个 Set
const shas = new Set(data.commits.map((commit) => commit.sha)); const files = new Set((data.files ?? []).map((file) => file.filename));见 src/inherited.ts。
第 4 步:输出审计日志
对每个继承的 commit 和文件各打一行core.info,最后汇总Found X inherited commit(s) and Y inherited file(s)(src/inherited.ts)。维护者在 workflow 日志里一眼就能看到哪些内容被排除了。
🔍 四、inherited 数据的两个消费点
4.1 commit 检查:按 sha 过滤
src/checks/commit-checks.ts 在拉取 PR 全部 commit 后立即过滤:
const commits = allCommits.filter((commit) => !inheritedShas.has(commit.sha));后续的 commit 消息长度、Conventional Commits、commit 作者匹配、block 作者四项检查,全部跑在这份过滤后的列表上——继承 commit 永远不会被检查。
4.2 file 检查:按文件名过滤
src/checks/file-checks.ts 同样的思路:
const files = changedFiles .filter((file) => !inheritedFiles.has(file.filename))文件扩展名、允许路径、block 路径、末尾换行、新增注释数这五项 file 检查,都被这一行豁免了继承文件。
4.3 单点采集、双点消费
src/main.ts 中的调用顺序是:
- 先跑 branch / size / title 检查(不需要 GitHub API);
- 再调用一次
getInheritedData,得到{ shas, files }; shas传给 commit 检查,files传给 file 检查。
全程只发一次compare API 请求,供两组检查共享——这正是 v0.2.1 重构要达到的效果。
💡 五、inherited 数据机制的 3 个设计亮点
| 设计点 | 效果 |
|---|---|
| 默认分支短路判断 | 最常见场景(指向main)零 API 调用 |
| 一次 compare 供两组检查复用 | 请求更少、整个 action 更快(官方宣传 15 秒内跑完) |
| 只过滤内容类检查 | 体积检查仍统计"相对目标分支的实际增量",与 GitHub 页面展示一致 |
第三点值得展开:max-changed-files/max-changed-lines并没有用 inherited 数据过滤,统计直接来自 PR payload(见 src/checks/size-checks.ts)。这是有意为之——继承而来的改动会随这个 PR 一起落到目标分支,计入体积是合理的;真正造成误报的,是"把作者没写的代码算到作者头上"。
📜 六、误报修复时间线
| 版本 | 变化 |
|---|---|
| v0.2.0 | commit 检查排除继承 commit,修复作者匹配、消息长度等检查的误报 |
| v0.2.1 | file 检查同样排除继承文件;检测逻辑从 commit 检查中抽取为独立的 src/inherited.ts,两组检查共享一次比较调用 |
这条演进路径也展示了这类 bug 的通用修法:先在误报最痛的检查点堵漏,再重构为共享模块。
🧾 七、小结
- 是什么:inherited 数据 = 默认分支上已有、PR 目标分支尚未合入的 commit 与文件;
- 怎么来:一次
compareCommitsWithBasehead(base...default),外加默认分支短路判断; - 怎么用:
shas过滤 commit 检查、files过滤 file 检查,均为 O(1) 的Set查找; - 解决什么:anti-slop 只评价"作者真正写的部分",指向
next等分支的 PR 不再被误关。
想继续深入,各检查实现都在 src/checks/ 目录下,整体入口是 src/main.ts。
【免费下载链接】anti-slop
A GitHub action that detects and automatically closes low-quality and AI slop PRs.
相关推荐
Renovate 如何用 baseBranchPatterns 让更新 PR 指向非默认分支
Renovate 如何用 baseBranchPatterns 让更新 PR 指向非默认分支 如果你的仓库默认分支是 main ,但希望 Renovate 的依
开发工具DevOps后端StackPrism 技术栈检测如何消灭误报?规则预编译与 Hint 索引的完整源码剖析
StackPrism 技术栈检测如何消灭误报?规则预编译与 Hint 索引的完整源码剖析 StackPrism 是一款开源的网页技术栈检测浏览器扩展(brows
深度解析 PostgreSQL 默认 Schema 机制:search_path 如何决定建表位置
深度解析 PostgreSQL 默认 Schema 机制:search_path 如何决定建表位置 PostgreSQL 中的 Schema(模式)是组织表、视
文档教程知识管理
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考