关于 optimise-github-actions
用于衡量 GitHub Actions 用量并指导安全优化的技能,面向维护 CI 工作流的团队。它要求先估算节省的分钟数、耗时变化与风险,再修改配置。
主要功能
- 01统计计费分钟数与运行耗时
- 02定位工作流和作业浪费
- 03估算节省、提速效果与风险
- 04指导工作流修改和验证
适用场景
- 01找出耗时或费用较高的 CI 作业
- 02检查拉取请求与 main 的重复运行
- 03规划缓存、过滤或合并作业
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ enesgules/ dotfiles --skill optimise-github-actions安装后在 Claude Code 中输入/optimise-github-actions确认已加载。
附带文件scripts/measure.mjs
详细介绍
优化 GitHub Actions
计费时间和实际经过的时间是两个不同的数字,大部分 CI 浪费都发生在两者之间。一个 5 路矩阵用时 2 分钟,会计费 10 分钟甚至更多。一个 5 秒的作业会按整分钟计费。合并到 main 后,可能会重新运行拉取请求已经通过的所有测试套件。因此,先进行测量,再削减占比最大的项目,同时保留保护交付内容的检查。
速度和成本通常会一起变化,但并非总是如此。缓存和移除重复工作对两者都有帮助。将长作业拆分为并行分片能加快 CI,但成本更高,因为每个分片都要重新进行设置。合并小作业则会产生相反的效果。请弄清楚用户更在意哪一项,或者说明每项改动会如何影响两者。
1. 测量
运行此技能文件夹中的脚本:
node <skill-dir>/scripts/measure.mjs OWNER/REPO --days 14 --out jobs.json
该脚本只需要具有读取权限的 gh。它会输出:
- 计费总分钟数、月度估算值、因时间取整而浪费的分钟数,以及已取消和失败的分钟数
- 一张按工作流和事件分类的表格,以及一张按作业分类并显示各作业运行占比的表格
- 成功运行的墙钟时间中位数和 p90
- 每个 PR 分支启动的运行次数
jobs.json 会保存原始数据行,供后续查询。繁忙的仓库需要几分钟才能完成。在公共仓库中,标准运行器免费,因此请将这些数字视为运行器耗时,并着重优化速度。
若要查看单个作业内的耗时分布,请输出一次典型运行的步骤耗时:
gh api repos/OWNER/REPO/actions/runs/RUN_ID/jobs --paginate \
-q '.jobs[] | .name, (.steps[] | select(.started_at) | " \((.completed_at|fromdate)-(.started_at|fromdate))s \(.name)")'
组织计划决定包含的分钟数以及是否提供合并队列。使用 gh api orgs/ORG -q .plan.name 检查计划。
2. 了解每个作业保护的内容
在提出任何建议之前,请先阅读以下内容。
- 每个工作流的触发条件、
concurrency、路径筛选器和needs:依赖关系图。最长的needs:链就是关键路径,只有缩短关键路径才能加快 CI。 - 必需的状态检查。运行
gh api repos/OWNER/REPO/rulesets,按 ID 查看每个规则集,并运行gh api repos/OWNER/REPO/branches/main/protection。如果重命名或移除必需检查,每次合并都会被阻止。 - “要求分支保持最新”是否已启用。API 中将其称为
strict。它决定了合并后,main 分支上的运行还能捕获多少问题。 - 会解析工作流文件的测试。使用
grep -rln "workflows/" --include='*.test.*' .查找。许多仓库会在单元测试中断言 CI 布局,这些测试会在首次改动时失败。 - 部署所依赖的内容,例如镜像标签、发布制品或“CI 已通过”状态。CI 仍必须构建并检查部署所依赖的一切。
3. 找出浪费
按第 1 步中的数字排序。以下每种模式都会说明如何发现和修复。
主分支重复运行拉取请求的测试套件。 同一批作业出现在 push 和 pull_request 下。如果每次合并都来自已通过检查的拉取请求,就在 push 时保留快速检查以及构建和发布步骤,并移除耗时较长的测试套件。告诉用户他们会失去什么。如果关闭了 strict,未经测试的合并结果可能会导致主分支出错,问题会在下一个拉取请求中显现。
路径筛选器几乎匹配所有拉取请求。 如果某项作业在 80 到 100% 的拉取请求运行中都会执行,那么它的筛选器什么也没筛掉。使用 gh api repos/OWNER/REPO/pulls/N/files,在真实拉取请求的文件列表上重放筛选器,并统计每种模式的匹配频率。常见原因包括:每个版本升级拉取请求都会修改的清单文件或锁定文件、范围较窄的作业规则中使用了宽泛的 src/**,以及工作流文件本身。要发现仅升级版本的改动,请在合并基点和分支头提交处比较文件,并移除 version 字段后再比较。使用 paths-ignore 让仅修改文档的变更跳过 CI。
许多小型作业。 每项作业都要花费时间检出代码、安装工具链和依赖项,通常为 30 到 60 秒,随后还会按整分钟计费。如果取整增加的时间超过约 10%,说明作业太小。将执行时间与设置时间相近的矩阵任务合并到一个作业中,按顺序执行各步骤,并在每个步骤上添加 if: ${{ !cancelled() }},这样即使某项失败,仍会报告所有用例的结果。耗时较长的任务仍应并行运行。将只有一个步骤的作业移到已经检出代码的相邻作业中。
依赖项安装缓慢。 在步骤耗时中比较安装步骤与作业其余部分的耗时。启用设置操作的缓存,例如在 actions/setup-node 中使用 cache: npm,或在 actions/setup-python 中使用 cache: pip,也可以使用以锁定文件为键的 actions/cache。只安装作业所用的软件包。除非作业需要读取 Git 历史记录,否则保留默认值 fetch-depth: 1。
每次向拉取请求推送都会运行所有内容。 每个分支有许多次运行,且取消的运行占用大量分钟数,说明有很多工作被白白浪费。按拉取请求设置 concurrency,并设置 cancel-in-progress: true。使用 if: ... && !github.event.pull_request.draft 跳过草稿拉取请求中的耗时测试套件,并将 ready_for_review 添加到 pull_request 类型中,以便拉取请求准备就绪时运行。如果团队从不创建草稿拉取请求,这样做就省不了任何资源,因此承诺节省资源前先确认这一点。
作业挂起。 未设置 timeout-minutes 的作业可能运行长达 6 小时。找出最长运行时间远高于平均值的作业,并将超时设为略高于其正常 p90 的值。
Docker 镜像构建。 留意耗时较长的构建步骤和较长的 Post Set up Docker Buildx 步骤,后者负责导出缓存。将依赖项安装放在 COPY . . 之前。避免在许多作用域中使用 cache-to: mode=max,因为这会导致仓库的 10 GB 缓存不断淘汰自身。查看拉取请求构建是否重复了主分支构建已经完成的工作。一种不错的取舍是:只有在 Dockerfile、锁定文件或 Dockerfile 按名称复制的文件发生更改时,才在拉取请求中构建镜像;发布前先测试主分支实际发布的镜像。这样发布的内容就都经过测试。
Windows 和 macOS 运行器。 Windows 的计费费率是 Linux 的 2 倍,macOS 则是 10 倍。仅在涉及特定平台的代码时使用它们,并且不要在主分支上再次运行。
机器人和计划任务。 Copilot 代码审查和 Dependabot 在 dynamic 事件下运行,cron 工作流则在 schedule 下运行。两者使用的分钟数相同。检查 cron 的频率,以及用户重新请求审查的频率。
自托管运行器。 在现有自托管组上运行的作业不会产生费用。在迁移任何作业之前,负责这些机器的人员必须确认容量、架构(arm64 或 x64)以及对软件包注册表的访问权限。提出迁移建议,并等待对方确认。
合并队列。 它会在每次合并时运行一次完整测试套件,而不是在每次向拉取请求推送时都运行,这通常是节省资源效果最大的一项。私有仓库需要 GitHub Enterprise Cloud 才能使用,因此请先确认套餐。
4. 编辑前先估算
对测量数据重放每项变更。将移除的任务数乘以它们的计费分钟数,比较变更前后的筛选器选择率,汇总合并操作节省的设置开销,并比较变更前后的关键路径。用表格列出变更、节省的分钟数、节省的时间和风险,然后让用户选择。估算时取保守值。对于取决于团队工作方式的节省项(例如草稿),标记为未测量。
5. 安全地更改工作流
- 每次只做小幅变更。在工作流中添加注释说明原因,避免后来的人将其撤销。
- 更新断言工作流布局的测试。只针对可能出问题的行为添加测试,例如仅针对版本的筛选器,或草稿跳过大型测试套件。不要添加重复 YAML 内容的测试。
- 使用
actionlint进行 lint;如果缺少该工具,则下载发布版二进制文件,并在本地运行仓库的 CI 测试。 - 推送并关注 PR。工作流变更通常会启动所有任务,因此首次运行的成本最高。
报告
先列出数据,再说明计划。
## Where the minutes and time go (last N days)
Total billed, monthly estimate, included minutes if known, median and p90 run time.
| Area | Minutes | Share | (by workflow and event)
| Job | Share | (top 5 jobs)
## Why
Numbered causes, each tied to a number from the data.
## Changes
| # | Change | Saves (min) | Saves (time) | Risk |
## Not done
What you skipped and why, such as required check names, runner capacity nobody confirmed, or plan limits.