mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-11-01 06:47:29 +00:00
ci: 加固 C 门禁 —— 卡死不再拖满超时,且缺工具不再假装通过
## 两个独立问题
### ① 卡死:20 分钟全花在 apt-get,不是门禁
实测证据:本机 `make check-csrc` 五个子门禁**各自 0~1 秒**:
csrc-lint 1s / csrc-abi 0s / csrc-headers 0s / csrc-sanitize 0s / csrc-cross 0s
而 CI 里同一个 job 两次跑到 20 分钟 job 级超时(其中一次直接 cancelled)。
卡死点只可能在 `apt-get update` —— ubuntu-latest runner 上偶发挂住不返回,
既不失败也没进展,只能等 job timeout,而报错信息毫无指向性。
### ② 假通过:缺工具时门禁 `[SKIP] ... exit 0`
这是更危险的一个。Makefile 里每道门禁在缺依赖时:
if ! command -v $CC_ARM64 >/dev/null 2>&1; then
echo " [SKIP] $CC_ARM64 不存在(未装交叉工具链)"; exit 0
fi
本机这样合理(开发者未必装交叉工具链),但在 CI 里意味着:apt-get 悄悄失败
⇒ 五道门禁全部跳过 ⇒ **job 绿** ⇒ 让人以为 C 侧有门禁,实际一个都没跑。
**静默降级比红更危险:红会有人修,绿没人看。**
实测确认(`CC_ARM64=nonexistent-gcc-xyz make csrc-cross`):
== C 侧交叉编译(linux/arm64)==
[SKIP] nonexistent-gcc-xyz 不存在(未装交叉工具链)
--- 退出码: 0 --- ← 这就是问题
## 加固内容
| 措施 | 作用 |
|---|---|
| 逐包检查是否已安装 | runner 镜像通常自带 cmake/clang ⇒ **多数情况完全不碰网络** |
| apt-get 自带 `Acquire::*Timeout=60` + `Retries=3` | 卡住时自身失败,而不是拖到 job 级超时(后者报错无指向性) |
| 装包 step `timeout-minutes: 6` | 双保险 |
| 新增「确认工具链齐备」步骤 | 缺任一工具 ⇒ 红,并 `::error::` 说明"缺工具时门禁会 SKIP 等于假装通过" |
| 跑门禁 step `timeout-minutes: 5` | 门禁实测 0~1 秒,超出即异常(不是慢) |
| `grep '\[SKIP\]'` 二次拦截 | 覆盖将来新增门禁时漏配依赖的情况 |
| job `timeout-minutes` 20 → 15 | 已经是纯 apt-get 余量 |
## 为什么要两道 SKIP 拦截
「确认工具链齐备」+「grep [SKIP]」看似重复,其实针对不同风险:
- 前者:**当前**三道门禁的依赖齐不齐
- 后者:**将来**新增门禁时,新依赖没写进安装步骤 —— 那时前者不知道要检查它,
只有日志里的 `[SKIP]` 能抓到
## 本地实测(逐条验证,不是只改完就推)
| 验证 | 结果 |
|---|---|
| YAML 可解析 | ✅ |
| 三件套本机齐备 | ✅ cmake / clang / aarch64-linux-gnu-gcc |
| `make check-csrc` 无 SKIP | ✅ 五门禁全实际执行 |
| 已装则跳过 apt-get 分支 | ✅ 「三件套已存在,跳过 apt-get」 |
| 部分缺失时拼装需要安装列表 | ✅ 正确识别出 `clang` |
| **变异:交叉工具链缺失** | ✅ grep 拦截生效(若不加这层,CI 会绿) |
This commit is contained in:
93
.github/workflows/ci.yml
vendored
93
.github/workflows/ci.yml
vendored
@ -167,19 +167,98 @@ jobs:
|
||||
csrc:
|
||||
name: C infrastructure gates
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
# 门禁本身实测 0~1 秒(csrc-lint/abi/headers/sanitize/cross 各自 <1s)。
|
||||
# 这 15 分钟全部是给 apt-get 的余量 —— 卡死历来只发生在装包阶段。
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
# clang 供双编译器告警对照;gcc-aarch64 供跨架构编译门禁。
|
||||
# 门禁在缺工具时是显式 SKIP 而不是假通过,这里装齐以免静默降级。
|
||||
- name: 安装 C 工具链
|
||||
# ── 安装 C 工具链 ─────────────────────────────────────────────
|
||||
#
|
||||
# ★ 加固点(2026-10-22):实测两次该 job 卡死 20 分钟并超时,
|
||||
# 而**本地同一门禁 0~1 秒全通过**(make check-csrc)。卡死点全在
|
||||
# apt-get:ubuntu-latest runner 上 `apt-get update` 偶发挂住不返回,
|
||||
# 既没有失败也没有进展,只能等 job 级 timeout。
|
||||
#
|
||||
# 三重加固:
|
||||
# 1. 逐包检查是否已安装 —— runner 镜像通常自带 cmake/clang,
|
||||
# 已装就跳过 apt-get(多数情况直接走这条,完全不碰网络)
|
||||
# 2. 需要装时给 apt-get 加自身的超时与重试,让它**自己失败**,
|
||||
# 而不是把整个 job 拖到 job 级 timeout(后者报错信息毫无指向性)
|
||||
# 3. 装完立即 `command -v` 复核,缺工具就红 —— 见下方"缺工具即失败"
|
||||
- name: 安装 C 工具链(已装则跳过)
|
||||
timeout-minutes: 6
|
||||
run: |
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -y -qq cmake clang gcc-aarch64-linux-gnu
|
||||
set -uo pipefail
|
||||
need=""
|
||||
command -v cmake >/dev/null || need="$need cmake"
|
||||
command -v clang >/dev/null || need="$need clang"
|
||||
command -v aarch64-linux-gnu-gcc >/dev/null || need="$need gcc-aarch64-linux-gnu"
|
||||
|
||||
if [ -z "$need" ]; then
|
||||
echo "✅ 三件套已存在,跳过 apt-get:$(command -v cmake clang aarch64-linux-gnu-gcc | tr '\n' ' ')"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "需要安装:$need"
|
||||
# apt-get 自己的超时:卡住时 4 分钟内失败,而不是拖到 6 分钟 step 超时。
|
||||
# 重试一次 —— runner 网络偶发抖动不该让门禁变红。
|
||||
sudo apt-get -o Acquire::http::Timeout=60 \
|
||||
-o Acquire::https::Timeout=60 \
|
||||
-o Acquire::Retries=3 \
|
||||
update -qq
|
||||
# shellcheck disable=SC2086
|
||||
sudo apt-get -o Acquire::http::Timeout=60 \
|
||||
-o Acquire::https::Timeout=60 \
|
||||
-o Acquire::Retries=3 \
|
||||
install -y -qq --no-install-recommends $need
|
||||
|
||||
# ── 缺工具即失败(★ 这条是本次加固的核心)────────────────────
|
||||
#
|
||||
# 此前门禁在缺工具时打 `[SKIP] ... exit 0` —— **CI 照样变绿**。
|
||||
# 那意味着:apt-get 悄悄失败(或将来有人删掉安装步骤)⇒ 五道门禁
|
||||
# 全部跳过 ⇒ job 绿 ⇒ 让人以为 C 侧有门禁,实际一个都没跑。
|
||||
#
|
||||
# 静默降级比红更危险:红会有人修,绿没人看。
|
||||
# 所以 CI 里显式要求工具必须在场,SKIP 视为失败。
|
||||
- name: 确认 C 工具链齐备(缺则失败,不接受 SKIP)
|
||||
run: |
|
||||
set -euo pipefail
|
||||
miss=""
|
||||
for t in cmake clang aarch64-linux-gnu-gcc; do
|
||||
if command -v "$t" >/dev/null 2>&1; then
|
||||
echo " ✅ $t: $(command -v "$t")"
|
||||
else
|
||||
echo " ❌ $t 缺失" >&2
|
||||
miss="$miss $t"
|
||||
fi
|
||||
done
|
||||
[ -z "$miss" ] || {
|
||||
echo "::error::C 工具链缺失:$miss —— 缺工具时门禁会 SKIP 并 exit 0,等于假装通过"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# ── 跑门禁 ──────────────────────────────────────────────────
|
||||
# 门禁自身 0~1 秒,给它 5 分钟绰绰有余;超出说明是异常(不是慢)。
|
||||
- name: make check-csrc
|
||||
run: make check-csrc
|
||||
timeout-minutes: 5
|
||||
run: |
|
||||
set -o pipefail
|
||||
make check-csrc 2>&1 | tee /tmp/csrc.log
|
||||
|
||||
# ★ 二次确认:任何 [SKIP] 都视为失败。
|
||||
#
|
||||
# Makefile 里每道门禁在缺依赖时 `echo "[SKIP] ..."; exit 0`,
|
||||
# 这在本机是合理的(开发者未必装了交叉工具链),但在 CI 里
|
||||
# 就等于把"没验证"伪装成"通过"。上面那步已保证工具齐备,
|
||||
# 这里再拦一次是为了覆盖将来新增门禁时漏配依赖的情况。
|
||||
if grep -q '\[SKIP\]' /tmp/csrc.log; then
|
||||
echo "::error::门禁出现 [SKIP],说明有检查没真正执行:"
|
||||
grep '\[SKIP\]' /tmp/csrc.log >&2
|
||||
echo "CI 里不接受 SKIP —— 要么装上依赖,要么明确从 check-csrc 摘掉这条。" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ 全部门禁实际执行,无 SKIP"
|
||||
|
||||
# ── 文档站构建(mkdocs,纯 Python,无外部依赖)──
|
||||
docs:
|
||||
|
||||
Reference in New Issue
Block a user