ci-audit
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCI Audit (GitHub Actions)
CI 审核(GitHub Actions)
Audit the workflows in for wall clock, spend, and whether the gating actually
gates. This skill reads and reports; it never edits a workflow.
.github/workflows/Scoped to GitHub Actions deliberately. The checks below are about Actions' own semantics — skipped
results counting as passing, matrix legs in check names, groups, artifact retention —
and none of it transfers to another CI system. On a repository with no , say so
and stop rather than guessing at a Jenkinsfile or a .
concurrency.github/workflows/.gitlab-ci.yml审核中的工作流,检查耗时、成本开销以及门控是否真正起到拦截作用。本技能仅读取和报告信息;绝不编辑工作流。
.github/workflows/专门针对GitHub Actions设计。以下检查均围绕Actions自身的语义展开——跳过的结果计为通过、检查名称中的矩阵分支、组、制品留存等——这些内容无法迁移到其他CI系统。如果仓库中没有,则直接说明并终止操作,无需猜测是否存在Jenkinsfile或。
concurrency.github/workflows/.gitlab-ci.ymlWhat this owns, and what it does not
本技能的职责范围
security-audit| This skill | |
|---|---|
| Performance, spend, and gating correctness | The security surface |
| Cache hit rate — key derivation, fallbacks | Cache poisoning and cross-branch scope |
| Whether a required check can fail the run | Whether a workflow can be made to run attacker code |
| Job graph, matrix design, artifact flow | |
| — | Action pinning: SHA vs tag vs branch, with severities |
Two consequences worth stating plainly. Do not report action pinning — it looks like a
maintenance finding and is a supply-chain one, grades it, and duplicating it here
produces two different severities for one line of YAML. And when a required check name matches no
workflow, report the observation and hand it over: reconciling rulesets against triggers needs the
calls that skill already makes.
security-auditgh apisecurity-audit| 本技能 | |
|---|---|
| 性能、成本开销与门控正确性 | 安全面 |
| 缓存命中率——键值推导、降级策略 | 缓存投毒与跨分支作用域 |
| 必填检查是否能终止流水线运行 | 工作流是否可被攻击者利用执行恶意代码 |
| 作业图、矩阵设计、制品流转 | |
| — | Action版本固定:SHA、标签、分支对比及风险等级 |
有两个需要明确说明的影响后果。请勿报告Action版本固定问题——这属于供应链安全范畴,由负责评估,若在此处重复报告,会导致同一行YAML出现两种不同的风险等级。当必填检查名称与任何工作流不匹配时,只需报告该观察结果并移交处理:规则集与触发器的协调需要已实现的调用逻辑。
security-auditsecurity-auditgh apiWhen to use
使用场景
- "Audit my CI", "review the workflows", "why is CI slow"
- "Cut our Actions minutes", "parallelize the pipeline"
- "Fix caching in CI", "the cache never hits"
- "Why did that merge when the tests failed" — gating correctness, family A
Trigger phrases: , , , , ,
, , .
ci auditgithub actionsworkflow performanceactions minutesci slowparallelize cici cachingrequired check- "审核我的CI"、"评审工作流"、"为什么CI这么慢"
- "减少Actions分钟数"、"并行化流水线"
- "修复CI缓存问题"、"缓存从未命中"
- "为什么测试失败了还能合并"——门控正确性,属于A类检查
触发短语:、、、、、、、。
ci auditgithub actionsworkflow performanceactions minutesci slowparallelize cici cachingrequired checkPhase 1: Inventory
阶段1:盘点
Glob and . An empty result — the directory is
missing, or holds no YAML — means print and stop.
.github/workflows/*.yml.github/workflows/*.yamlNo GitHub Actions workflows found.Use globbing rather than shelling out. is not guaranteed present, and it exits non-zero with
when is absent, so a bare returns an error
where the stop condition expects an empty result. Where a shell is preferred anyway, guard it:
fdSearch path is not a directory.github/workflows/fdbash
[ -d .github/workflows ] && ls .github/workflows/*.y*ml 2>/dev/nullFor each workflow, extract the shape before reading any step. The job graph is what most findings are
about, and it is not legible by reading top to bottom:
bash
yq -r '.jobs | to_entries | map(.key + " <- " + ((.value.needs // []) | tostring)) | .[]' <file>
yq -r '[.on | keys | join(",")] | join("")' <file>
yq -r '.concurrency // "none"' <file>Record per workflow: triggers, the block, the graph, which jobs carry a
, which have , and which upload or download artifacts.
concurrencyneedsstrategy.matrixtimeout-minutesThen state the critical path — the longest chain through the graph — because that is the number
any parallelization finding has to move. Splitting a job that is not on it changes nothing.
Where the run history is available, get real numbers rather than guessing which job is slow:
bash
gh run list --workflow <file> --limit 20 --json databaseId,conclusion,createdAt,updatedAt
gh run view <id> --json jobs --jq '.jobs[] | "\(.name) \(.startedAt) \(.completedAt) \(.conclusion)"'Say whether timings are measured or estimated. An estimated saving stated as a measured one is the
fastest way to lose an audit's credibility.
遍历和文件。如果结果为空——目录不存在或没有YAML文件——则输出并终止操作。
.github/workflows/*.yml.github/workflows/*.yamlNo GitHub Actions workflows found.使用遍历逻辑而非调用shell命令。工具并非一定存在,且当不存在时会返回的非零退出码,直接使用会在终止条件需要空结果时返回错误。如果偏好使用shell命令,请添加保护逻辑:
fd.github/workflows/Search path is not a directoryfdbash
[ -d .github/workflows ] && ls .github/workflows/*.y*ml 2>/dev/null对于每个工作流,在读取任何步骤之前先提取其结构。作业图是大多数问题的核心,无法通过自上而下阅读来理解:
bash
yq -r '.jobs | to_entries | map(.key + " <- " + ((.value.needs // []) | tostring)) | .[]' <file>
yq -r '[.on | keys | join(",")] | join("")' <file>
yq -r '.concurrency // "none"' <file>记录每个工作流的以下信息:触发器、块、依赖图、包含的作业、设置了的作业,以及上传或下载制品的作业。
concurrencyneedsstrategy.matrixtimeout-minutes然后明确关键路径——依赖图中最长的执行链——因为任何并行化优化都需要针对这条路径。拆分不在关键路径上的作业不会带来任何改变。
如果运行历史可用,则获取真实数据而非猜测哪个作业较慢:
bash
gh run list --workflow <file> --limit 20 --json databaseId,conclusion,createdAt,updatedAt
gh run view <id> --json jobs --jq '.jobs[] | "\(.name) \(.startedAt) \(.completedAt) \(.conclusion)"'说明耗时是实测值还是估算值。将估算的节省时间表述为实测值会最快失去审核的可信度。
Phase 2: Run the catalog
阶段2:执行检查清单
Read and work the four families. Each check there carries what to look for,
the cost when it is wrong, and the shape that fixes it.
references/checks.md| Family | Covers |
|---|---|
| A. Gating correctness | Matrix jobs with no stable fan-in gate, jobs that cannot fail the run, gates on noisy signals, required names nothing produces |
| B. Critical path | Serialized independent steps, no cheap head gate, expensive jobs on every event, setup repeated instead of artifacts consumed, push and pull-request double runs |
| C. Caching | Keys not derived from the lockfile, missing |
| D. Spend and hygiene | Missing |
Family A first. A pipeline that is fast and gates nothing is worse than a slow one, and these
findings fail green — nobody notices them from the run list.
Two rules on severity:
- Rate by consequence, not by how odd the YAML looks. A missing on a job that reliably finishes in 40 seconds is a low finding; the same gap on a job that can hang on a dev server is the one that burns six hours.
timeout-minutes - A finding needs the number it moves. "Split these jobs" is not a finding. "These three steps are independent and sit on the critical path; splitting them removes ~90s from every run" is. Where the number cannot be established, say it is an estimate.
Recognise what is already right. A workflow doing the non-obvious things well — a fan-in gate with
, on the release, an unprivileged job producing the
artifact a privileged one consumes — should be told so, in one line each. It is how the report earns
the right to be believed about the rest.
if: always()cancel-in-progress: false读取并执行四类检查。每个检查都包含检查内容、问题带来的影响以及修复方案。
references/checks.md| 类别 | 涵盖范围 |
|---|---|
| A. 门控正确性 | 无稳定聚合门控的矩阵作业、无法终止流水线的作业、基于噪声信号的门控、无对应工作流的必填检查名称 |
| B. 关键路径 | 串行执行的独立步骤、无轻量前置门控、每次事件都运行的高成本作业、重复执行的初始化步骤而非复用制品、推送和拉取请求重复运行 |
| C. 缓存 | 未基于锁文件生成的缓存键、缺失 |
| D. 成本开销与卫生性 | 缺失 |
优先执行A类检查。一个速度快但无有效门控的流水线比慢流水线更糟糕,这类问题会导致流水线“伪成功”——从运行列表中无法察觉。
关于严重程度的两条规则:
- 根据影响后果评级,而非YAML的怪异程度。一个能稳定在40秒内完成的作业缺失属于低风险问题;而一个可能在开发服务器上挂起的作业存在同样的问题,则会导致6小时的资源浪费,属于高风险。
timeout-minutes - 每个问题都需要明确影响的量化值。“拆分这些作业”不是有效的问题描述。“这三个独立步骤位于关键路径上;拆分它们可将每次运行的耗时减少约90秒”才是有效的描述。如果无法确定量化值,请说明是估算值。
认可已有的正确实践。如果工作流在一些非显而易见的方面做得很好——比如设置了的聚合门控、发布作业设置、非特权作业生成制品供特权作业使用——应在报告中用单独一行指出。这能让报告的其余内容更具可信度。
if: always()cancel-in-progress: falsePhase 3: Report
阶段3:生成报告
undefinedundefinedCI audit: <N> workflows, <J> jobs · critical path <T> (measured|estimated)
CI 审核:<N> 个工作流,<J> 个作业 · 关键路径耗时 <T>(实测/估算)
Gating correctness
门控正确性
<finding: what is wrong, what it lets through, the shape that fixes it>
<问题描述:存在的问题、带来的影响、修复方案>
Critical path
关键路径
<finding, with the time it moves>
<问题描述,包含影响的耗时变化>
Caching
缓存
<finding>
<问题描述>
Spend and hygiene
成本开销与卫生性
<finding>
<问题描述>
Already right
已有的正确实践
- <one line per non-obvious thing the workflows get right>
- <每条非显而易见的正确实践单独一行>
Handed to security-audit
移交至security-audit
- <required-check reconciliation, or anything touching the security surface>
Drop any section with no findings. Do not pad a clean result — a workflow set with nothing wrong is a
real outcome, and `Already right` carries it.
`references/ci-template.yaml` is an optimized pnpm workflow with parallel jobs, path filters and
concurrency control; `references/ci-template-vp.yaml` is the Vite+ (`voidzero-dev/setup-vp`) variant.
Offer them when a repository is starting from nothing, not as a target to converge every pipeline on.- <必填检查协调问题,或任何涉及安全面的内容>
删除没有问题的章节。不要为无问题的结果凑内容——工作流完全没有问题是真实的结果,“已有的正确实践”部分可以支撑这份结论。
`references/ci-template.yaml`是经过优化的pnpm工作流,包含并行作业、路径过滤和并发控制;`references/ci-template-vp.yaml`是Vite+(`voidzero-dev/setup-vp`)的变体。当仓库从零开始搭建CI时可以推荐这些模板,但不要将其作为所有流水线的统一目标。Rules
规则
- Read and report. Never edit a workflow. Findings are for the maintainer to apply.
- Gating correctness before speed. A faster pipeline that checks less is a regression.
- No finding without a consequence. Name what it costs or what it lets through.
- Measured or estimated, always stated. Never present an estimate as a measurement.
- Stay off the security surface. Pinning, , and trigger safety belong to
permissions:. Report the boundary crossing, not the verdict.security-audit - Do not recommend splitting a unified check command without establishing that the toolchain does not already parallelize internally.
- 仅读取和报告,绝不编辑工作流。问题修复由维护者自行处理。
- 优先保证门控正确性,再优化速度。更快但检查更少的流水线是一种倒退。
- 无影响后果的内容不列为问题。明确说明问题带来的成本或风险。
- 始终标注是实测值还是估算值。绝不将估算值表述为实测值。
- 不涉及安全面内容。版本固定、和触发器安全性属于
permissions:的职责范围。仅报告跨边界的内容,不给出评估结论。security-audit - 在未确认工具链未内置并行化的情况下,不建议拆分统一的检查命令。
Error handling
错误处理
| Situation | Action |
|---|---|
No | Print |
| Same message. A directory with only a README is not a pipeline |
Workflows exist but only | Audit them, and say nothing runs automatically |
| A workflow fails to parse | Report the parse error as the first finding and audit the rest |
| Read the YAML directly, and say the job graph was derived by reading |
| Audit statically, and mark every timing an estimate |
A reusable workflow ( | Audit the caller's graph; say the callee was not read unless it is in this repository |
| A composite action in the repository | Read it — its steps are on the critical path too |
| Only one job, doing everything | Still audit families A, C and D. A single job is not automatically wrong |
| 场景 | 操作 |
|---|---|
无 | 输出 |
| 输出相同信息。仅包含README的目录不算流水线 |
工作流仅设置 | 正常审核,并说明无自动运行的流水线 |
| 工作流解析失败 | 将解析错误作为第一个问题报告,继续审核其余工作流 |
| 直接读取YAML文件,并说明作业图是通过阅读推导得出 |
| 执行静态审核,并将所有耗时标记为估算值 |
可复用工作流(作业级 | 审核调用方的依赖图;仅当被调用方在当前仓库时才读取其内容 |
| 仓库内的复合Action | 读取其内容——其步骤也属于关键路径的一部分 |
| 仅包含一个作业,执行所有任务 | 仍需审核A、C、D类检查。单个作业并非一定存在问题 |