CI 必需检查
必需检查通过了。测试却根本没运行。
有条件跳过的测试任务如何满足分支保护,以及始终运行的门禁任务如何阻止未经测试的合并。
阅读时间:4分钟作者 Abdullah Raheel
一次有用的优化造成了策略漏洞
只有添加 run-tests 标签后,单元测试和 E2E 任务才会启动。这样可以在拉取请求仍处于准备阶段时推迟成本较高的测试套件,但每次合并仍应运行这些测试。分支保护并没有强制执行这一意图。
当任务级条件计算为 false 时,测试任务会被跳过。对于必需检查,GitHub 仍会把已跳过任务的结论视为成功。因此,即使没有执行任何测试命令,拉取请求也可能显示为绿色。
保留任务的条件执行
我保留了该标签,因为它在评审期间确实有用。错误在于把条件测试任务本身设为必需检查;即使任务被跳过,GitHub 也可能判定该检查已满足。
unit-tests:
if: |
github.event_name == 'workflow_dispatch' ||
(github.event.action == 'labeled' && github.event.label.name == 'run-tests') ||
(github.event.action == 'synchronize' &&
contains(github.event.pull_request.labels.*.name, 'run-tests'))
steps:
- run: pnpm test:unit:coverage测试任务决定现在是否运行,单独的门禁决定拉取请求能否安全合并。混合这两个决定造成了虚假的绿色状态。
将执行任务与策略分开
我在每个测试任务后添加了一个小型门禁。它通过 needs 声明上游任务,使用 if: always() 运行,并且仅在上游任务结果为 success 时通过。现在,无论测试套件被跳过、取消还是失败,都会阻止合并并显示有用的信息。
ci-gate:
name: CI Gate (Unit)
runs-on: ubuntu-latest
needs: [unit-tests]
if: always()
steps:
- name: Check test results
run: |
result="${{ needs.unit-tests.result }}"
if [[ "$result" != "success" ]]; then
echo "::error::Unit tests did not pass (result: $result)"
exit 1
fi
echo "Unit tests passed"上游结果 必需门禁
success 通过
failure 失败
cancelled 失败
skipped 失败,并显示可执行的标签提示让高成本测试任务保持条件执行
这些门禁位于真实测试套件之前。单元测试覆盖 73 个 Jest 文件,强制执行覆盖率阈值并确保覆盖率只升不降。Playwright 在 Chromium 和 WebKit 中运行,使用四个工作进程、两次 CI 重试和共享的已认证状态。
单元测试和 E2E 测试各自获得一个稳定的门禁名称。即使以后更改测试任务实现,分支保护仍可继续要求这些名称。
把门禁设为必需检查,而不是测试任务
分支保护改为要求 CI Gate (Unit) 和 CI Gate (E2E),而不是条件测试任务。这样便明确了合并约定:预期的测试套件必须成功运行,或者显示可见的失败并说明未运行原因。
- 如果代码仓库有明确理由,可以让高成本工作保持条件执行。
- 始终汇总影响合并策略的每个最终任务结果。
- 在分支保护中要求稳定的聚合器名称。
- 让跳过验证产生可执行的提示,而不是静默显示为绿色。

