dotnet/runtime 自动化 Issue 清理机制:两阶段 stale 标记到关闭的完整实现解析
【免费下载链接】runtime.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.项目地址: https://gitcode.com/GitHub_Trending/runtime6/runtime
dotnet/runtime 是 .NET 运行时、类库与 Mono/CoreCLR 等子系统的统一仓库,社区每天会提交数十个 Issue。为了不让积压(backlog)中大量三年以上无任何活动的旧 Issue 持续沉淀,仓库团队实现了自动化清理机制:先把 stale Issue 标记为清理候选并通知,若 14 天内无任何新评论则自动关闭,有任何评论则自动撤销流程。读完本文,你将掌握该机制的完整运作流程、仓库内真实配置的逐项解读(.github/policies/resourceManagement.yml),以及这套模式在自有仓库中的落地要点。
背景:为什么 dotnet/runtime 需要自动化清理 backlog
dotnet/runtime 是一个高流量的开源仓库,社区每天都会提交大量 Issue。尽管维护者尽力快速响应与解决,仍不可避免地有一部分 Issue 会在 backlog 中长期停滞。据 docs/issue-cleanup.md 记载,仓库中曾存在数百个超过三年没有任何活动的 Issue。
这些长期闲置的 Issue 会带来几个实际问题:
- 噪音与维护成本:维护者筛选 backlog、定位真实待办时,陈旧 Issue 会稀释注意力;
- 信息失真:很多旧 Issue 所基于的版本早已迭代,问题可能已修复或已过时;
- 社区体验:贡献者面对大量"僵尸 Issue",难以判断哪些仍然有效、值得投入。
自动化清理的目标,是让 backlog 更精简、更聚焦:要么促使维护者与社区重新评估旧 Issue(重新排期或提升优先级),要么将其作为"已解决"或"已过时"关闭。
两阶段清理机制的核心流程
这套自动化采用两阶段(two-phase)流程,核心设计是"先警告、后关闭",给所有利益相关方留下充分的反应窗口:
第一阶段:标记为清理候选当自动化发现满足条件的 stale Issue(长期无活动且处于打开状态)时,会:
- 在 Issue 下追加一条通知评论;
- 打上
backlog-cleanup-candidate标签,将其标记为"清理候选"。
反应窗口:任何反馈都会撤销流程如果这条通知触发了任何反馈——例如有人(不限于 Issue 作者本人)回复了评论、补充了复现信息、维护者重新评估了优先级——流程会立即撤销(the process is undone),该 Issue 不再走向关闭。
第二阶段:14 天后自动关闭如果 14 天内没有任何进一步活动,自动化会执行关闭操作。
这一设计意图明确:它不是为了"无差别删除旧 Issue",而是触发对旧 Issue 的重新评估——既面向维护者(可以重新排期、重新优先级化),也面向社区(可以补充信息证明其仍然有效)。最终结果只有两种:被重新激活,或被作为已解决/已过时关闭。
源码级解读:自动化在仓库中的真实配置
这套机制并不是文档中的"纸面设计",而是由仓库根目录下的策略文件.github/policies/resourceManagement.yml真实承载的。该文件基于GitOps.PullRequestIssueManagement原语(primitive),核心由scheduledSearches(定时搜索)与eventResponderTasks(事件响应)两部分组成。
1. 清理候选的识别规则
- description: Automated Issue cleanup frequencies: - hourly: hour: 6 filters: - noActivitySince: days: 1644 - isIssue - isOpen - isNotLabeledWith: label: backlog-cleanup-candidate - isNotLabeledWith: label: area-codegen-coreclr - isNotLabeledWith: label: area-iltools-coreclr - isNotLabeledWith: label: area-tools-ilverification actions: - addReply: reply: >- Due to lack of recent activity, this issue has been marked as a candidate for backlog cleanup. It will be closed if no further activity occurs within 14 more days. Any new comment (by anyone, not necessarily the author) will undo this process. This process is part of our [issue cleanup automation](https://github.com/dotnet/runtime/blob/main/docs/issue-cleanup.md). - addLabel: label: backlog-cleanup-candidate - addLabel: label: no-recent-activity逐项解读这套过滤规则:
| 配置项 | 取值 | 含义 |
|---|---|---|
frequencies | hourly: hour: 6 | 定时任务每小时运行一次,在每小时的第 6 分钟触发扫描 |
noActivitySince.days | 1644 | 无任何活动超过 1644 天(约 4.5 年)的 Issue 才进入候选扫描范围 |
isIssue | — | 只处理 Issue,不处理 Pull Request |
isOpen | — | 只处理仍处于打开状态的 Issue |
isNotLabeledWith: backlog-cleanup-candidate | — | 已经打过该标签的 Issue 不重复处理 |
isNotLabeledWith三个area-*标签 | area-codegen-coreclr、area-iltools-coreclr、area-tools-ilverification | 特定领域(coreclr 代码生成、IL 工具、IL 验证)的 Issue 被排除在自动清理之外 |
排除列表的存在说明:自动化清理并非一刀切,涉及 JIT/代码生成等复杂领域的旧 Issue 往往仍具参考价值,因此被显式豁免。这与 docs/project/issue-guide.md 中"Issue 代表未来应做的可执行工作,只要属于团队职责范围就保持打开"的原则保持一致。
而触发后执行的三个动作(addReply+ 两个addLabel)正是原文档描述的"通知 + 标记":回复模板明确说明"由于缺少近期活动,本 Issue 已被标记为 backlog 清理候选;若 14 天内无进一步活动将被关闭;任何新评论(不限作者)都会撤销此流程"。
2. 配套的no-recent-activity标签流
同一个策略文件还定义了围绕no-recent-activity标签的完整生命周期,它服务于另一类场景——等待作者回应的 Issue/PR:
- 打标签:对带
needs-author-action标签、14 天无活动的打开 Issue(以及 PR),自动加上no-recent-activity标签并追加说明评论; - 关闭:对已带
no-recent-activity标签、又过了 14 天仍无活动的 Issue/PR,追加评论后执行closeIssue关闭; - 关闭后的提醒:关闭评论会提示——Issue 仍可重新打开或评论,但若再保持 30 天无活动,将被锁定(lock)。
由此可见,仓库实际运行着两套并行的时间轴:一套是面向"三年以上老 Issue"的backlog-cleanup-candidate流程,另一套是面向"等待作者动作"的no-recent-activity流程。两者共享"14 天无活动即关闭"的窗口期设计,前者窗口更宽松、仅针对超长期闲置,后者则约束需要作者回应的新近 Issue。
3. Draft PR 的自动关闭
策略文件还覆盖了长期搁置的草稿 PR:
- description: Close inactive Draft PRs filters: - isDraftPullRequest - isOpen - noActivitySince: days: 30 actions: - closeIssue - addReply: Draft Pull Request was automatically closed for 30 days of inactivity.30 天无活动的 Draft PR 会被自动关闭,关闭评论会引导作者联系 docs/area-owners.md 中的负责人申请重新打开。这与 docs/project/issue-guide.md 中"保持只有活跃 PR;WIP PR 不应变得 stale(超过 2 周);若 PR stale 且没有立即推进路径,考虑关闭直到解除阻塞"的维护原则相互印证。
4. 事件响应:按 area 标签自动通知负责人
除了定时扫描,同一文件中的eventResponderTasks还实现了事件驱动的联动:当 Issue 或 PR 被打上某个area-*标签(如area-System.Text.Json、area-GC-coreclr、area-System.Net.Http等上百个领域标签)时,自动通过mentionUsers在评论中 @ 对应领域的维护者或团队(如dotnet/gc、dotnet/jit-contrib),并附上 docs/area-owners.md 的订阅指引。这保证了"打标签 → 领域负责人收到通知"的链路自动化,是 issue 治理中与清理机制互补的另一半。
清理链条的收尾:locker 工作流
被自动关闭的 Issue 并非流程终点。仓库通过.github/workflows/locker.yml实现"锁定期"治理:该工作流每天定时(cron37 8 * * *)运行,默认对关闭后 30 天且更新后 30 天无活动的 Issue/PR 执行锁定(lock),且支持workflow_dispatch手动触发并传入自定义天数。同时,它监听reopened事件:若关闭的 Issue/PR 被重新打开但处于锁定状态,会自动执行解锁(unlock),确保重新激活的讨论不被锁死。
至此形成完整闭环:
打开 Issue →(超 1644 天无活动)→ 打 backlog-cleanup-candidate 标签 + 通知 → 14 天内有人评论?→ 是:撤销流程,继续保留 → 否:自动关闭 →(关闭后 30 天无活动)→ 锁定而needs-author-action场景则走no-recent-activity(14 天打标签 + 14 天关闭)的更短周期,两者共同构成仓库的自动清理体系。
对社区贡献者与维护者的实践启示
无论你是 dotnet/runtime 的 Issue 提交者、贡献者还是其他开源仓库的维护者,这套机制都提供了可复用的经验:
- 对 Issue 作者:收到
backlog-cleanup-candidate通知意味着你的 Issue 即将进入关闭倒计时。任何新评论(不限作者本人)都会撤销流程——补充复现步骤、更新当前版本下的行为、或说明为何仍需要该功能,都是有效的"续命"方式;如果问题已解决,也可以借此机会主动关闭并注明原因。 - 对维护者:清理自动化是"触发重新评估"的机制而非"删除工具"。通过 docs/project/issue-guide.md 中"每个 Issue 恰好一个
area-*标签、无 Assignee 除非正在处理、尽量用help wanted、不害怕拒绝但要礼貌解释"的 triage 规则,配合本机制即可让 backlog 长期保持聚焦。 - 对自有仓库:
resourceManagement.yml中scheduledSearches的noActivitySince(天数)、isNotLabeledWith(豁免标签列表)、frequencies(扫描频率)和actions(回复文案 + 标签)都是可参数化的,任何基于 GitHub 的仓库都可以按需裁剪这套"两阶段 stale 清理"模式。关键设计要点是:先通知、给窗口、可撤销、再关闭——这比直接关闭旧 Issue 更尊重社区,也更有数据依据。
小结
dotnet/runtime 的自动化 Issue 清理是一套精心设计的"软关闭"机制:以backlog-cleanup-candidate标签和通知评论为第一阶段的警示,以 14 天活动窗口为缓冲,以自动关闭和后续锁定为收尾。它的真实配置位于.github/policies/resourceManagement.yml,配套的.github/workflows/locker.yml与 docs/project/issue-guide.md 共同构成了仓库完整的 Issue 治理闭环。理解这套机制,既能帮助你在参与 .NET 开源社区时正确应对"清理候选"通知,也能为自有仓库设计自动化 backlog 治理提供可直接借鉴的范式。
【免费下载链接】runtime.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.项目地址: https://gitcode.com/GitHub_Trending/runtime6/runtime
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考