From 653c87f5f2db8160a79703148df527d3c63ce44a Mon Sep 17 00:00:00 2001 From: JianFeeeee Date: Sat, 24 Oct 2026 12:41:51 +0800 Subject: [PATCH] =?UTF-8?q?fix(ci):=20ID:15=20=E6=8E=92=E9=99=A4=E8=A1=A8?= =?UTF-8?q?=E5=AE=88=E5=8D=AB=E6=94=B9=E4=B8=BA=E3=80=8C=E6=B3=A8=E5=85=A5?= =?UTF-8?q?=E5=90=88=E6=88=90=E8=A1=8C=E7=9C=9F=E8=B7=91=E3=80=8D=EF=BC=8C?= =?UTF-8?q?=E6=9D=9C=E7=BB=9D=E5=BD=A2=E4=BC=BC=E8=80=8C=E6=97=A0=E6=95=88?= =?UTF-8?q?=E7=9A=84=E5=81=87=E7=BB=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原判据(efe0cf1)剥注释后查 token:`${reason// /}` + `exit 1` 都在、提示文案都在, 但把 if 条件改成 `&& false` 后条件恒假、步骤实跑 EXIT=0,判据仍然绿 —— 假绿。 改为从 race job 里抽出「计算 -race 排除表并打印」步骤的 run 体,在临时 bash 里真跑三次: 1. 原样 → 必须 EXIT=0(正向基线) 2. 注入 reason 空的合成行 → 必须 EXIT=1 3. 注入 ticket 空的合成行 → 必须 EXIT=1 两条守卫各用只违反自己的输入单独打,不用一条顶另一条的包。 用合成行而非真实 webui 排除项 ⇒ 该排除项修好后被删掉也不会误报。 验证矩阵(verify_v4.py,5 变异 + 1 误报 + 基线): ✓ reason 守卫失效(token 全在 + && false) EXIT=1 ✓ ticket 守卫失效(token 全在 + && false) EXIT=1 ✓ 删 cmd/gui 剔除 EXIT=1 ✓ 启动图判据步去掉 -count=1 EXIT=1 ✓ race 退回写死两包 EXIT=1 ✓ 删 webui 排除行(误报检查) EXIT=0 ✓ 还原后基线 EXIT=0 go build ./... = 0 error,go vet ./... = 0 error --- .github/scripts/check-ci-gates.py | 71 ++++++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 11 deletions(-) diff --git a/.github/scripts/check-ci-gates.py b/.github/scripts/check-ci-gates.py index 602433f7..358a3194 100755 --- a/.github/scripts/check-ci-gates.py +++ b/.github/scripts/check-ci-gates.py @@ -96,18 +96,67 @@ for _n, r in evid: check("DATA RACE" in r, "取证步骤应显式检查 DATA RACE 报告数(否则取证没意义)") -# ── 3. skip 排除表:每项必须写原因,无原因必须步骤自己失败 ── +# ── 3. skip 排除表:真跑一次坏输入,要求它必须失败 ── # -# ★ 这里曾经写成 `any("没有写原因" in r ...)` —— 只检查**提示文案还在**。 -# 实测:把 `if [ -z "${reason// /}" ]; then` 改成 `if false; then` -# (文案全保留、校验实际失效),该断言仍然绿,而该步骤实跑 EXIT=0 -# —— 也就是说「无原因必须失败」这条保证彻底破掉,判据却看不见。 -# 这正是本脚本自己在 docstring 里批判的「被文案顶包」,不能自己再犯。 -# ⇒ 改为剥注释后断言**可执行的判空逻辑**:必须同时出现判空变量与 exit 1。 -race_code = "\n".join(code_lines(r) for _n, r in race) -check('${reason// /}' in race_code and "exit 1" in race_code, - "race job 排除表缺少真校验:剥注释后必须能看到 ${reason// /} 判空 + exit 1," - "只有提示文案不算(把 if 条件改 false 就能骗过纯文案断言)") +# ★ 这里换过三版,每一版的失效方式都值得记: +# v1 `any("没有写原因" in r ...)` —— 只查提示文案;把 if 条件改 false 仍绿 +# v2 剥注释后查 `${reason// /}` + `exit 1` —— 两个 token 都在仍可能不成立; +# 实测 `if [ -z "${reason// /}" ] && false; then` 依然绿 +# v3 真跑、但拿真实 webui 那一行做坏输入 —— 该排除项修好被删掉时**误报红** +# v4(本条)用**合成行**注入 TABLE 再真跑: +# · 不依赖任何真实排除项 ⇒ 排除项增删不会引起误报 +# · 真跑 ⇒ `&& false` 这类「形似而无效」的改动会被抓出来 +# +# 两条守卫必须**各自**用只违反它的输入来验,否则一条会顶另一条的包 +# (第一版我把 reason 与 ticket 同时清空,ticket 守卫兜住了,reason 守卫死没死看不出来)。 +import subprocess as _sp +import tempfile as _tf + +_tbl = None +for _n, _r in race: + if "计算 -race 排除表并打印" in (_n or ""): + _tbl = _r +if _tbl is None: + fails.append("race job 找不到「计算 -race 排除表并打印」步骤 —— 无法自测排除表守卫") +elif "${{" in _tbl: + fails.append("排除表步骤含 GitHub 表达式,本脚本无法本地执行它(需改用别的验证方式)") +else: + import re as _re + _m = _re.search(r"read -r -d '' TABLE <<'EOF'[^\n]*\n(.*?)\nEOF\n", _tbl, _re.S) + if _m is None: + fails.append("排除表步骤里找不到 TABLE heredoc —— 无法注入合成坏行") + else: + def _run_body(body): + fd, tmp_sh = _tf.mkstemp(suffix=".sh") + with __import__("os").fdopen(fd, "w", encoding="utf-8") as f: + f.write(body) + out = _tf.mktemp() + env = dict(__import__("os").environ) + env["GITHUB_OUTPUT"] = out + try: + return _sp.run(["bash", tmp_sh], capture_output=True, text=True, env=env) + finally: + __import__("os").unlink(tmp_sh) + if __import__("os").path.exists(out): + __import__("os").unlink(out) + + def _inject(line): + inner = _m.group(1) + new_inner = (line + "\n" + inner) if inner.strip() else line + return _tbl[:_m.start(1)] + new_inner + _tbl[_m.end(1):] + + # 正向:原样必须通过 + if _run_body(_tbl).returncode != 0: + fails.append("排除表步骤用当前 ci.yml 实跑不通过(正向基线坏)") + + _probe = "test|internal/probe/synthetic|TestSyntheticProbe" + # 只打 reason:reason 空、ticket 有效 + if _run_body(_inject(_probe + "||PROBE-TICKET")).returncode == 0: + fails.append("合成行「reason 空 / ticket 有效」竟通过 ⇒ reason 守卫已失效" + "(形似而无效的改动,例如 if 条件加 && false)") + # 只打 ticket:reason 有效、ticket 空 + if _run_body(_inject(_probe + "|合成探针原因|")).returncode == 0: + fails.append("合成行「reason 有效 / ticket 空」竟通过 ⇒ ticket 守卫已失效") # ── 4. go job:整机启动图判据必须单列一步跑 kernelboot/modules ── gojob = step_runs("go")