From 2b45de3287da1b98f6173d87dbd218dff8115052 Mon Sep 17 00:00:00 2001 From: "cloudbrid-agent[bot]" Date: Sun, 30 Aug 2026 08:41:18 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20butler-reconcile=20=E8=90=BD=E7=9B=98=20?= =?UTF-8?q?git=20add=20=E8=A1=A5=20-f=EF=BC=88#475/#476=EF=BC=89=E2=80=94?= =?UTF-8?q?=E2=80=94=E5=BD=B1=E5=AD=90=E8=B4=A6=E6=9C=AC=E8=A2=AB=20.gitig?= =?UTF-8?q?nore=20=E6=8B=A6=E6=88=AA=E8=87=B4=20butler-ledger=20=E5=88=86?= =?UTF-8?q?=E6=94=AF=E4=BB=8E=E6=9C=AA=E5=BB=BA=E5=87=BA=EF=BC=88ADR-0103?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/butler-reconcile.yml | 6 +- governance/tests/test-ledger-add-force.sh | 96 +++++++++++++++++++++++ 2 files changed, 101 insertions(+), 1 deletion(-) create mode 100755 governance/tests/test-ledger-add-force.sh diff --git a/.github/workflows/butler-reconcile.yml b/.github/workflows/butler-reconcile.yml index 8ffb21d..d699ef8 100644 --- a/.github/workflows/butler-reconcile.yml +++ b/.github/workflows/butler-reconcile.yml @@ -77,7 +77,11 @@ jobs: python3 governance/evidence_shadow.py verify --file merged.jsonl mkdir -p "ledger/governance/butler" cp merged.jsonl "$BASE" - git -C ledger add "$SHADOW" + # -f 必带(#475/#476):governance/butler/shadow-evidence.jsonl 被 .gitignore + # 忽略(影子真源在 *-ledger 分支,工作树副本不入库)——无 -f 时 git add + # 被 ignore 拦截 exit 1,push 不执行,butler-ledger 分支永远建不出来 + # (butler 源恒 0)。同类先例:feishu-drill/env-drift/feedback-edge 均 -f。 + git -C ledger add -f "$SHADOW" git -C ledger diff --cached --quiet && { echo "OK 影子无新增——不提交(幂等)"; exit 0; } git -C ledger commit -m "butler: 影子账本追加(IR-0006 W1-B2 双写,链验通过)" for i in 1 2 3; do git -C ledger push "https://x-access-token:${BUTLER_TOKEN}@github.com/Cloudbird-Software/.github.git" HEAD:refs/heads/butler-ledger && break diff --git a/governance/tests/test-ledger-add-force.sh b/governance/tests/test-ledger-add-force.sh new file mode 100755 index 0000000..c0dd1b0 --- /dev/null +++ b/governance/tests/test-ledger-add-force.sh @@ -0,0 +1,96 @@ +#!/usr/bin/env bash +# test-ledger-add-force.sh —— *-ledger 落盘 git add -f 执法自测(IR-0006 残留 #475/#476) +# +# 背景:影子账本真源在 *-ledger 分支,工作树副本被 .gitignore 忽略—— +# workflow 落盘步 `git add` 不带 -f 时被 ignore 拦截 exit 1,push 永不执行, +# 对应源恒 0(butler-reconcile 曾连续 4 次红,butler-ledger 分支从未建出)。 +# 本测试机械扫描全部 workflow:凡 add 的目标路径命中 .gitignore 字面路径, +# add 行必须含 -f/--force;另做真实 git 行为复现(无 -f 必失败)锚定判定。 +# 用法: bash governance/tests/test-ledger-add-force.sh(gate.yml 自动纳入) +set -uo pipefail +DIR="$(cd "$(dirname "$0")/../.." && pwd)" +FAILS=0 +pass() { echo "PASS $1"; } +fail() { echo "FAIL $1"; FAILS=$((FAILS+1)); } + +# ---- 静态扫描:workflow 中 add gitignore 路径必须 -f ---- +python3 - "$DIR" <<'PYEOF' || FAILS=$((FAILS+1)) +import glob, os, re, sys + +root = sys.argv[1] +# .gitignore 字面路径(无通配符)——忽略注释/空行/否定规则 +ignored = set() +with open(os.path.join(root, ".gitignore"), encoding="utf-8") as f: + for line in f: + line = line.strip() + if not line or line.startswith("#") or line.startswith("!") or any(c in line for c in "*?[]"): + continue + ignored.add(line.lstrip("/")) + +bad = 0 +for wf in sorted(glob.glob(os.path.join(root, ".github/workflows/*.yml")) + + glob.glob(os.path.join(root, ".github/workflows/*.yaml"))): + text = open(wf, encoding="utf-8").read() + # 同文件内变量赋值(如 SHADOW="governance/.../shadow-evidence.jsonl") + vars_ = dict(re.findall(r'^\s*([A-Z_][A-Z0-9_]*)="([^"\n]+)"', text, re.M)) + for m in re.finditer(r'^\s*git\s+[^\n]*\badd\b([^\n]*)$', text, re.M): + line, args = m.group(0), m.group(1) + # 展开 "$VAR" / ${VAR} 引用 + targets = set(re.findall(r'["\']?([\w./-]+|"\$\{?[A-Z_][A-Z0-9_]*\}?")["\']?', args)) + resolved = set() + for t in targets: + t = t.strip('"\'') + vm = re.fullmatch(r'\$\{?([A-Z_][A-Z0-9_]*)\}?', t) + if vm: + resolved.add(vars_.get(vm.group(1), "")) + elif t and not t.startswith("-"): + resolved.add(t) + hit = [p for p in resolved if p in ignored] + if hit and not re.search(r'(?:^|\s)(?:-f|--force)(?:\s|$)', line): + print(f"FAIL {os.path.relpath(wf, root)}: git add 命中 .gitignore 路径 {hit} 但缺 -f(ledger 落盘将被 ignore 拦截)") + print(f" {line.strip()}") + bad += 1 +if bad == 0: + print("PASS 全部 workflow:gitignore 内路径的 git add 均带 -f") +else: + sys.exit(1) +PYEOF +[[ $? -eq 0 ]] || FAILS=$((FAILS+1)) + +# ---- 行为锚定:真实 git 复现「无 -f 必失败」(判定不依赖静态正则自洽) ---- +TMP=$(mktemp -d); trap 'rm -rf "$TMP"' EXIT +mkdir -p "$TMP/rep/governance/butler" +cp "$DIR/.gitignore" "$TMP/rep/" +( cd "$TMP/rep" && git init -q . && git config user.email t@t && git config user.name t + : > governance/butler/shadow-evidence.jsonl + git add governance/butler/shadow-evidence.jsonl 2>/dev/null ) +if [[ $? -ne 0 ]]; then + pass "行为锚定:gitignore 路径 git add(无 -f)真实失败——测试前提成立" +else + fail "行为锚定失效:无 -f 竟可 add(.gitignore 变更?需复查本测试前提)" +fi +( cd "$TMP/rep" && git add -f governance/butler/shadow-evidence.jsonl ) 2>/dev/null \ + && pass "行为锚定:-f 可 add(修复路径有效)" \ + || fail "行为锚定:-f 亦失败(异常,需人工复查)" + +# ---- 修复面直接断言(当前已知落盘点,防扫描器静默漏检) ---- +for wfsrc in "butler-reconcile.yml:governance/butler/shadow-evidence.jsonl" \ + "feishu-drill.yml:governance/feishu/shadow-evidence.jsonl" \ + "env-drift.yml:governance/env/shadow-evidence.jsonl" \ + "feedback-edge.yml:governance/feedback/shadow-evidence.jsonl"; do + wf="${wfsrc%%:*}"; path="${wfsrc#*:}" + f="$DIR/.github/workflows/$wf" + [[ -f "$f" ]] || continue + if grep -q "^governance/.*shadow-evidence.jsonl$" <(grep -v '^#' "$DIR/.gitignore") && \ + grep -q 'git -C ledger add -f' "$f"; then + pass "$wf 落盘 add -f 在位($path)" + else + fail "$wf 落盘 add -f 缺失($path)" + fi +done + +if [[ $FAILS -gt 0 ]]; then + echo "RESULT: $FAILS 项失败" + exit 1 +fi +echo "RESULT: 全部通过"