/ci-audit — 发现并修复浪费的 GitHub Actions 时间
先测量,再修复少数关键问题。在实际审计中,最大的收获很少来自慢速测试套件。往往是一个每天失败数月之久的定时流水线、一个每次运行 30 秒但按每月 730 分钟计费的小时级 cron 任务,或者一个 SDK 对已失效请求静默重试了三次。
此技能文件夹中的标准库脚本 scripts/ci_audit.py 负责测量工作。您需要阅读其输出,决定哪些值得修复,并将其提交。
python3 <skill-dir>/scripts/ci_audit.py inventory <owner> # 查看哪些仓库运行 Actions,私有 vs 公开
python3 <skill-dir>/scripts/ci_audit.py month <owner/repo> # 按工作流统计 30 天的计费分钟数
python3 <skill-dir>/scripts/ci_audit.py jobs <owner/repo> --runs 150 # 每个作业的排队/设置/运行时间
python3 <skill-dir>/scripts/ci_audit.py steps <owner/repo> <run-id> # 单次运行中每个步骤的秒数
python3 <skill-dir>/scripts/ci_audit.py schedules <owner/repo> # 每个 cron 任务折算为每月运行次数
它需要已登录的 GitHub CLI (gh)。month 和 jobs 命令每次运行会发起一次 API 调用,因此繁忙的仓库可能需要一两分钟。建议在后台运行它们。
步骤 0 — 范围与规则
在开始测量前,与用户协定:
- 哪些所有者与仓库。 一个仓库,或者过去 60 天内推送过的所有仓库。
- 禁止操作的仓库。 其他团队成员(或其他智能体)正在此仓库进行功能开发。对这些仓库,仅提供带数据的 Issue,绝不推送分支。
- 合并目标分支。 默认是集成分支(例如
development)。发布到默认分支需单独确认,但 cron 和定时修复仅在其合并后(步骤 6)才生效。 - 不在范围内: 削减测试覆盖率、付费运行器、更换 CI 供应商。可以附带数据推荐这些方案,但不要实际执行。
现在请阅读 references/gotchas.md。
步骤 1 — 盘点和费用
- 对每个所有者运行
ci_audit.py inventory <owner>。注意区分私有与公开:仅私有仓库产生费用,但当智能体依赖 CI 时,每个仓库的等待时间都很重要。 - 对于组织,拉取真实账单:
gh api "/organizations/<org>/settings/billing/usage?year=YYYY&month=M"。对于个人账户,对每个私有仓库运行ci_audit.py month,并将总计与套餐包含的分钟数进行比较。 - 记录按计费分钟数排名前 5 的仓库。这就是时间消耗所在。
步骤 2 — 先修复破损
对于每个包含定时工作流的仓库:
- 检查每个工作流的最近 10 次定时运行。任何每次都失败的工作流都是首要发现,无论其成本如何。读取其日志 (
gh api repos/O/R/actions/runs/<id>/logs > logs.zip)。 - 对
.github/workflows/运行actionlint。GitHub 无法解析的文件永远不会执行其定时任务。
预期会遇到多层错误。请参见 references/fixes.md 第 1 节。
步骤 3 — 定时任务
对每个包含 cron 任务的仓库运行 ci_audit.py schedules <repo>。将每月运行次数乘以每次运行的计费分钟数(来自 jobs)。标记以下情况:
- 严格定时下运行时间很短的作业(每小时 30 秒的检查意味着每月 730 计费分钟)。
- 多个 cron 任务在同一分钟运行,并访问相同的频率限制 API。
- 每月运行一次的 cron 任务产生一次巨大的运行。请将工作分摊到多个夜晚。
步骤 4 — 逐作业分解
对于排名靠前的仓库:运行 ci_audit.py jobs <repo> --runs 150,然后对每个昂贵作业的最近一次成功运行执行 steps。将每个作业的时间分类:
| 分类 | 指向的问题 |
|---|---|
| 排队时间高 | 运行器争用或并发组。通常无法廉价修复,记录即可。 |
| 设置时间高 | 依赖安装和缓存 (references/fixes.md 第 3–4 节)。 |
| 运行时间高,应用测试 | 实际测试时间。除非某步骤明显冗余,否则保留。 |
| 运行时间高,数据流水线 | 对阶段进行检测并修复代码问题,而非 CI 问题 (references/fixes.md 第 6 节)。 |
| 失败率 > 5% | 不稳定测试或破损流水线 (references/fixes.md 第 1、7 节)。 |
步骤 5 — 报告并选择
编写一个表格,按每月预估节省的分钟数排序:
| 仓库 | 发现 | 证据 | 修复方案 | 月估节省 | 风险 |
|---|
包括您不会修复的发现(禁止操作的仓库、所有者对 cron 频率或并行度的决策),并附上所有者决策所需的数字。向用户展示并获取对哪些行进行提交的许可。将其归档至 ${MAKERSKILLS_CONFIG:-$HOME/.config/makerskills}/ci-audit/archive/<YYYY-MM-DD>-<scope>.md。
步骤 6 — 修复阶段
一个仓库对应一个工作者。在支持子智能体的主机上,可以并行处理多个仓库,但需遵循相同的简要指令:全新克隆、先建 Issue、以 Issue 命名的分支、小型提交、PR 正文中包含前后对比数据。
对于每个修复:
- 使用测量数据创建一个 Issue。
- 在一个分支上进行更改。本地运行 lint 和测试,通过退出码确认。
- 证明效果:推送,然后重新运行该 PR 的 CI 以利用热缓存,并对比若干旧运行基线。对于流水线修复,从分支调度工作流并设置较小的限制。
- 如果更改没有帮助(这种情况确实会发生,例如 Playwright 缓存问题),则用数据关闭 PR。这同样是结果,而非失败。
- 审核通过且检测变绿后,合并到集成分支。
- 对于任何定时任务,在用户同意后,打开向默认分支的发布 PR。在此之前,生产环境不会发生任何变化。
步骤 7 — 验证并记录
- 在下次定时运行后,读取其时间和日志。确认修复在生产环境中(而非仅限于分支)保持有效。
- 将结果追加到归档文件:每个仓库的前后对比、未合并而关闭的修复以及新的注意事项。
- 建议每月重新运行一次审计,或者在感觉 CI 变慢时运行。使用循环提醒 (
loopify) 效果很好。
可组合使用的技能
loopify— 安排每月重新审计,或安排次日早上检查第一次定时运行的结果。castmate— 让第二个模型对有风险的工作流更改进行审查。unstuck— 当一个本应生效的修复测得速度更慢时,在放弃前使用。project-manager— 跟踪审计发现但未实施的所有者决策(cron 频率、付费运行器)。
质量说明
- 破损比缓慢更重要。 在计时任何东西之前,务必先检查失败的定时工作流。一个每天失败、持续六个月之久的流水线,其损失远超任何缓存问题。
- 没有热缓存数据则不声称性能提升。 永远不要报告单次冷运行带来的加速。需重新运行,以若干旧运行为基线,并在 PR 中同时列出两个数值。
- 计费分钟数,而非秒数。 按作业舍入意味着在私有仓库上,数量更少但更充实的作业优于大量微小作业。并行化是一个涉及成本的加速决策,而非免费的利益。
- 每次测试覆盖率相同。 PR 正文中需包含更改前后的测试数量。一个运行更快但测试更少的套件不算修复。
- 定时任务意味着默认分支。 在 cron 或流水线修复被合并到默认分支并且至少成功运行一次定时任务之前,不能宣告完成。
- 用数据关闭失败的实验。 一个测得速度更慢的 PR 应附带其数据关闭,这样下个季度就不会有人重试同样的想法。
- 尊重他人的分支。 对于禁止操作的仓库,仅创建 Issue,绝不推送。