From 110db1bfee3399ccef6c4e58050619277d8b27d2 Mon Sep 17 00:00:00 2001 From: JianFeeeee Date: Mon, 9 Nov 2026 11:22:28 +0800 Subject: [PATCH] =?UTF-8?q?fix(ci):=20=E5=8C=85=E4=BE=9D=E8=B5=96=E9=97=A8?= =?UTF-8?q?=E7=9A=84=E4=B8=A4=E4=B8=AA=20shell=20bug=20+=20=E6=97=A2?= =?UTF-8?q?=E6=9C=89=20shellcheck/=E8=B6=85=E9=95=BF=E8=A1=8C=E6=B8=85?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## CI 42555311224 红在「包依赖门」 ::error::go list -deps github.com/.../internal/agent/core|internal/kernelboot 失败 两个真实 shell 缺陷,都是我上一提交写进去的: 1. `while read -r pkg bad` 没设 `IFS='|'` ⇒ 整行塞进 pkg, go list 收到 `core|internal/kernelboot` 这种不存在的包路径。 2. `echo "$forbidden_pairs" | while ...` 用管道 ⇒ while 在子 shell 里跑, 里面 `fail=1` 传不出来。即使检出违规依赖,门也会**静默变绿** —— 比红更糟。 改为 `while IFS='|' read ... done <<< "$forbidden_pairs"`, 并用 `grep -c` 显式取命中数(`grep -q` 在 `set -e` + `||` 组合下易误判)。 **变异验证**(针对第 2 条那个假绿入口): 往禁止表插入一条真实存在的依赖 `core|internal/agent/api`: 正向 rc=0 变异后 rc=1 ::error::internal/agent/core 反向依赖了 internal/agent/api —— 分层退化 ## 顺带清掉 lint 标 STOP 的既有问题 - ci.yml 13 处 >80 字符行全部折行(含 awk 覆盖率那段 230 字符长行) - shellcheck SC2086 ×2:`echo $ALL` / `echo $PKGS` 补引号 - shellcheck SC2129:三条 GITHUB_OUTPUT 重定向合成 `{ ... } >>` `python3 .github/scripts/check-ci-gates.py` 通过; 本地实跑两道新门(测试归拢 / 包依赖)均通过。 --- .github/workflows/ci.yml | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ff1b74e1..381c5544 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -102,19 +102,29 @@ jobs: internal/kstate|cmd " fail=0 - echo "$forbidden_pairs" | while read -r pkg bad; do + # ★ while 用**重定向**而非管道:管道会起子 shell, + # 里面置的 fail=1 传不出来(门会静默变绿)。 + # ★ IFS='|' 必须设:不设的话整行都读进 pkg, + # go list 会拿到 "core|internal/kernelboot" 这种不存在的包。 + while IFS='|' read -r pkg bad; do + pkg=$(echo "$pkg" | xargs) + bad=$(echo "$bad" | xargs) [ -z "$pkg" ] && continue deps=$(go list -deps "$M/$pkg" 2>/dev/null || true) if [ -z "$deps" ]; then echo "::error::go list -deps $M/$pkg 失败" exit 1 fi - if echo "$deps" | grep -qx "$M/$bad" || \ - { [ "$bad" = cmd ] && echo "$deps" | grep -q "$M/cmd"; }; then + if [ "$bad" = cmd ]; then + hit=$(echo "$deps" | grep -c "^$M/cmd" || true) + else + hit=$(echo "$deps" | grep -cx "$M/$bad" || true) + fi + if [ "$hit" -ne 0 ]; then echo "::error::$pkg 反向依赖了 $bad —— 分层退化" fail=1 fi - done + done <<< "$forbidden_pairs" # 显式断言 kstate 能反向被 core 引用(环不存在) if ! go build "$M/internal/agent/core"; then echo "::error::core 无法编译(kstate 叶子性可能已破)" @@ -384,7 +394,7 @@ jobs: # cmd/gui 是纯 Electron 目录(只有 doc.go,无测试), # 显式点名它会让 go test 报 "no Go files" 的假红。 ALL=$(go list ./... | grep -v '^.*/cmd/gui$' | tr '\n' ' ') - N=$(echo $ALL | wc -w) + N=$(echo "$ALL" | wc -w) echo " 将用 -race 覆盖 $N 个包" # 兜底:枚举失败(0 个包)必须响亮失败,不能静默跳过整道门。 if [ "$N" -lt 50 ]; then @@ -500,9 +510,11 @@ jobs: echo "════════ 共排除 $n 项(不是静默跳过:每次 CI 都打印本表)════════" echo "::warning title=-race 排除了 $n 项::见本步骤输出(含原因与修复单号)" fi - echo "tests=$tests" >> "$GITHUB_OUTPUT" - echo "pkgs=$pkgs" >> "$GITHUB_OUTPUT" - echo "count=$n" >> "$GITHUB_OUTPUT" + { + echo "tests=$tests" + echo "pkgs=$pkgs" + echo "count=$n" + } >> "$GITHUB_OUTPUT" - name: go test -race(全仓,应用上面的排除表) run: | @@ -523,7 +535,7 @@ jobs: [ "$drop" -eq 0 ] && PKGS="$PKGS $p" done - NPKG=$(echo $PKGS | wc -w) + NPKG=$(echo "$PKGS" | wc -w) echo " 实际参与 -race 的包数: $NPKG(已剔除 pkg 级排除项)" if [ "$NPKG" -lt 50 ]; then echo "::error::剔除后只剩 $NPKG 个包,疑似误剔 —— 拒绝变成假绿"