#!/usr/bin/env bash
set -uo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)"
MODE=check
ROOT=""
for a in "$@"; do
case "$a" in
--sign) MODE=sign ;;
-*) echo "器之自败: 未知旗标 $a" >&2; exit 2 ;;
*) ROOT="$a" ;;
esac
done
if [ -z "$ROOT" ]; then ROOT="$(cd "$HERE/../.." && pwd)"; fi
[ -d "$ROOT/.git" ] || { echo "器之自败: $ROOT 非 git 仓" >&2; exit 2; }
cd "$ROOT" || exit 2
SELF="$HERE/$(basename "$0")"
MANIFEST=".xuanwu/gate-face.manifest"
ANCHOR=".xuanwu/gate-face.manifest.sha256"
face_files() {
{
git ls-files '.xuanwu/**'
git ls-files 'scripts/tests/**'
git ls-files 'scripts/verify_release.sh'
git ls-files 'release/*/tools/**'
git ls-files 'botgate.toml' 'deny.toml'
git ls-files '.github/workflows/**'
} | grep -vx "$MANIFEST" | grep -vx "$ANCHOR" | sort -u
}
if [ "$MODE" = "sign" ]; then
mkdir -p "$(dirname "$MANIFEST")"
{ face_files | while IFS= read -r f; do sha256sum "$f"; done; } > "$MANIFEST.tmp" \
|| { echo "器之自败: 生成失败" >&2; exit 2; }
mv "$MANIFEST.tmp" "$MANIFEST"
sha256sum "$MANIFEST" > "$ANCHOR"
echo "已重签: $MANIFEST($(wc -l < "$MANIFEST") 条)· $ANCHOR($(cat "$ANCHOR"))"
exit 0
fi
RC=0
echo "== gate-face-manifest · 面 =="
echo " 仓根 = $ROOT"
echo " 支 = $(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo '<无>')"
echo " 尖 = $(git rev-parse HEAD 2>/dev/null || echo '<无>')"
echo " 器 sha256 = $(sha256sum "$SELF" | awk '{print $1}')"
echo " 清单 = $MANIFEST · 锚 = $ANCHOR"
[ -f "$MANIFEST" ] || { echo "器之自败: 清单不在位(先 --sign)" >&2; exit 2; }
[ -f "$ANCHOR" ] || { echo "器之自败: 锚不在位(先 --sign)" >&2; exit 2; }
mapfile -t UNTRACKED < <(git ls-files --others --exclude-standard -- \
.xuanwu scripts/tests release 2>/dev/null | grep -vx "$MANIFEST" | grep -vx "$ANCHOR" \
| grep -E '^\.xuanwu/|^scripts/tests/|^scripts/verify_release\.sh$|^release/[^/]+/tools/|^botgate\.toml$|^deny\.toml$|^\.github/workflows/' | sort)
if [ "${#UNTRACKED[@]}" -gt 0 ]; then
echo "器之自败: 判据面域内有【未跟踪】之档 ${#UNTRACKED[@]} 件 ⇒ 清单必不全(先 \`git add\` 再 --sign):" >&2
printf ' ? %s\n' "${UNTRACKED[@]}" >&2
exit 2
fi
ACT="$(mktemp)"; EXP="$(mktemp)"
trap 'rm -f "$ACT" "$EXP"' EXIT
echo
echo "-- 判据① 同集: 判据面枚举 ⇄ manifest 键集(双向 · 双侧含锚)"
{ face_files; printf '%s\n' "$ANCHOR"; } | sort -u > "$ACT"
{ awk '{ $1=""; sub(/^ +/,""); print }' "$MANIFEST"; printf '%s\n' "$ANCHOR"; } | sort -u > "$EXP"
N_ACT="$(wc -l < "$ACT")"; N_EXP="$(wc -l < "$EXP")"
echo " 枚举档数 = $N_ACT · manifest 键数(含锚)= $N_EXP"
if diff -u "$ACT" "$EXP" > /dev/null 2>&1; then
echo " ✓ 判据① 逐名相符"
else
echo " ✗ 判据① 不同集(★ \`-\` = 枚举有而 manifest 无 ⇒ **未登册**; \`+\` = manifest 有而档不在 ⇒ **键腐**):" >&2
diff -u "$ACT" "$EXP" | sed -n '3,$p' | sed 's/^/ /' >&2
RC=1
fi
echo
echo "-- 判据② 逐件 \`sha256sum -c\`"
if out="$(sha256sum -c "$MANIFEST" 2>&1)"; then
echo " ✓ 判据② $(wc -l < "$MANIFEST") 件哈希皆符"
else
echo " ✗ 判据② 哈希失配 · 具名:" >&2
printf '%s\n' "$out" | grep -v ': OK$' | sed 's/^/ /' >&2
RC=1
fi
echo
echo "-- 判据③ 自举: 锚对 manifest 校验"
if out="$(sha256sum -c "$ANCHOR" 2>&1)"; then
echo " ✓ 判据③ 自举咬合($(printf '%s' "$out" | tr '\n' ' '))"
else
echo " ✗ 判据③ 自举破(★ 改 manifest 或改锚 而未重签): $out" >&2
RC=1
fi
echo
if [ "${XW_GFM_NO_CONTROL:-0}" = "1" ]; then
echo "-- 判据④ 对照: **本面内跳过**(XW_GFM_NO_CONTROL=1 · 防递归)"
echo "== 判据 =="
exit "$RC"
fi
echo "-- 判据④ 对照(仓外副本 · 四向皆须咬合)"
T="$(mktemp -d)"; trap 'rm -f "$ACT" "$EXP"; rm -rf "$T"' EXIT
F="$T/f"
mkdir -p "$F/scripts/tests" "$F/.xuanwu"
git -C "$F" init -q
git -C "$F" config user.email t@t; git -C "$F" config user.name t
printf '#!/bin/bash\necho leg-a\n' > "$F/scripts/tests/leg_a.sh"
printf '#!/bin/bash\necho leg-b\n' > "$F/scripts/tests/leg_b.sh"
git -C "$F" add -A >/dev/null 2>&1; git -C "$F" commit -qm init >/dev/null 2>&1
bash "$SELF" --sign "$F" >/dev/null 2>&1 || { echo " ✗ 对照面重签失败(器之自败)" >&2; exit 2; }
chk() { XW_GFM_NO_CONTROL=1 bash "$SELF" "$F" 2>&1; }
chk > "$T/o0" 2>&1; r0=$?
echo " 甲 不篡改 rc=$r0(期 0)"
printf '#!/bin/bash\necho leg-a-TAMPERED\n' > "$F/scripts/tests/leg_a.sh"
chk > "$T/o1" 2>&1; r1=$?
echo " 乙 改判据面一字节 rc=$r1(期 ≠ 0)"
git -C "$F" checkout -q -- scripts/tests/leg_a.sh
sed -i '1s/./f/' "$F/$MANIFEST"
chk > "$T/o2" 2>&1; r2=$?
echo " 丙 改 manifest 一行 rc=$r2(期 ≠ 0)"
bash "$SELF" --sign "$F" >/dev/null 2>&1
printf '#!/bin/bash\necho new-leg\n' > "$F/scripts/tests/leg_c.sh"
git -C "$F" add -A >/dev/null 2>&1; git -C "$F" commit -qm add-c >/dev/null 2>&1
chk > "$T/o3" 2>&1; r3=$?
echo " 丁 新增未登册档 rc=$r3(期 ≠ 0)"
if [ "$r0" -eq 0 ] && [ "$r1" -ne 0 ] && [ "$r2" -ne 0 ] && [ "$r3" -ne 0 ]; then
echo " ✓ 判据④ 四向咬合"
else
echo " ✗ 判据④ 未咬合(r0=$r0 r1=$r1 r2=$r2 r3=$r3)—— 谓词无判别力" >&2; RC=1
fi
echo " -- 具名抽验(乙/丙/丁 各取一行 · 证「红」皆**具名**而非仅 『rc』)"
grep -m1 'FAILED' "$T/o1" | sed 's/^/ 乙: /' || true
grep -m1 '自举破' "$T/o2" | sed 's/^/ 丙: /'
grep -m1 'leg_c\.sh' "$T/o3" | sed 's/^/ 丁: /' || true
echo
echo "== 判据(★ 唯载【不变量】· 读数唯见上列各行 · 禁由本块推)=="
echo " · 判据① = 判据面枚举 ⇄ manifest 键集 逐名相符(新档未登册 ⇒ 红具名);"
echo " · 判据② = manifest 逐件 sha256sum -c rc=0(改一字节 ⇒ 红具名);"
echo " · 判据③ = 自举: 锚对 manifest 校验 rc=0;"
echo " · 判据④ = 四向对照(不篡改绿 ∧ 改判据面红 ∧ 改 manifest 红 ∧ 新增档红);"
echo " · ★ 限 = manifest ∧ 锚**同改自洽**则可伪造(自指之固有限 · 治在人)。"
exit "$RC"