工程(提交点文档域增量化批·pre-commit 触发粒度): run_all_gates.sh 增量档——--staged <清单文件> 仅配 --docs,触发矩阵唯一落点(任一 .md→06/06b/07/08/08d 先按清单内 .md 扫描·待办清单/roadmap/reports→08c 全量·systems/tests-README/reports→08e 全量·10 恒跑),快路径红必全量复跑定谳(清单面外红不入判定面,与 CI 全量严格对齐),未触发闸记 SKIP 入 not_run;user 目录隔离收窄至代码域(文档域不启引擎省 0.8 秒);pre-commit 钩子收暂存清单(quotepath 关闭保中文名,NUL 转行落临时文件)传 --staged,清单建不起回落全量档;不带 --staged=现行全量档 CI 零影响;窄测三态过(纯代码全 SKIP 6.2s/登记面 08c 触发+面外红定谳注记 7.9s/设计文档快路径 7.3s),Windows 瓶颈=python 进程启动,实际省 1-3 秒;横幅钩子与 config 注册不入 git(.zcode gitignored)随盘生效

This commit is contained in:
DKChan committed 2026-10-31 04:28:03 +08:00
1 parent 654aa73986
commit a7c63850b7
2 files changed
+123 -21

No files matched your search

+9 -1
View File
@@ -12,7 +12,15 @@ REPO="$(git rev-parse --show-toplevel)"
[ -f "$(git rev-parse --git-dir)/MERGE_HEAD" ] && exit 0
RC=0
bash "$REPO/tools/run_all_gates.sh" --docs || RC=$?
# 收暂存清单(增量档输入):quotepath 关闭保中文名原样,NUL 转行落临时文件传闸机
_LIST="$(mktemp "${TMPDIR:-/tmp}/gates-staged.XXXXXX")" || _LIST=""
if [ -n "$_LIST" ]; then
git -C "$REPO" -c core.quotepath=false diff --cached --name-only -z | tr '\0' '\n' >"$_LIST"
bash "$REPO/tools/run_all_gates.sh" --docs --staged "$_LIST" || RC=$?
rm -f "$_LIST"
else
bash "$REPO/tools/run_all_gates.sh" --docs || RC=$?
fi
if [ "$RC" -eq 0 ]; then
exit 0
elif [ "$RC" -eq 1 ]; then
+114 -20
View File
@@ -22,6 +22,13 @@
# ——本批改动只有文档件时用:game/ 内的 .md(README、生成清单)、design/、根规则文件、
# tools/ 工具脚本、.zcode/ 配置等一切非代码面改动。
# --quick 全量档的中段自检形态:跳过渲染闸(窗口化,需开显示器)与静态扫描;完成出库禁用。
# --staged <清单文件> 文档域增量档(仅配 --docs):清单=本次提交暂存文件(pre-commit 钩子生成,NUL 转行)。
# 触发矩阵(唯一落点):任一 .md → 06/06b/07/08/08d 先按清单内 .md 扫描(快路径);
# 待办清单.md/roadmap.md/reports/**.md 任一 → 08c 全量(关系型,不可切片);
# design/gdd/systems/**、game/tests/README.md、reports/**.md 任一 → 08e 全量(关系型);
# 10 熵增闸恒跑(结构哨兵)。快路径扫出红时全量复跑定谳——清单面外文件的失败不在全量档
# 判定面内,全量复跑绿=按绿放行并注记,红=真红;增量判定面与 CI 全量档严格对齐,
# 跨文件漏检面(改 A 断 B 的链)由合回后 CI 全量兜底。缺省不带 --staged = 现行全量档,CI 零影响。
# 不带参数 = 全量档(两域全跑),触发条件=同批既有代码面改动又有文档件改动。闸件(tools/ 下机检脚本
# 与闸壳)自身的改动不占合回全量——其质量义务=落批时人工确认闸可跑通且判据口径未变;
# 闸真崩溃时合回闸 fail-closed(跑不出回执即拦),不以全量为兜底。
@@ -47,22 +54,34 @@ BASELINE="$STAMP_DIR/baseline.json"
QUICK=0
DOMAIN="full"
for _a in "$@"; do
case "$_a" in
STAGED_FILE=""
while [ $# -gt 0 ]; do
case "$1" in
--quick) QUICK=1 ;;
--code|--docs)
[ "$DOMAIN" = "full" ] || { echo "用法: --code 与 --docs 互斥"; exit 2; }
DOMAIN="${_a#--}" ;;
*) echo "用法: tools/run_all_gates.sh [--code|--docs|--quick]"; exit 2 ;;
DOMAIN="${1#--}" ;;
--staged)
[ -n "${2:-}" ] || { echo "用法: --staged <暂存清单文件>"; exit 2; }
STAGED_FILE="$2"; shift ;;
*) echo "用法: tools/run_all_gates.sh [--code|--docs|--quick] [--staged <清单文件>]"; exit 2 ;;
esac
shift
done
if [ -n "$STAGED_FILE" ]; then
[ "$DOMAIN" = "docs" ] || { echo "用法: --staged 仅配 --docs(增量档只定义在文档域)"; exit 2; }
[ -f "$STAGED_FILE" ] || { echo "用法: --staged 清单文件缺位:$STAGED_FILE"; exit 2; }
fi
# 每个代码面 worktree 用独立 Godot user 目录(game/override.cfg 由 setup 脚本自愈生成;主区 no-op)。
# 并行批共用一套夹具、日志与截图目录会互相踩盘造成假红——隔离建不起来就拒跑(fail-closed)。
"$REPO/tools/setup_worktree_userdir.sh" "$REPO" || {
echo "[gates] FAIL — user 目录隔离未建立,拒绝在共享目录上跑闸。"
exit 1
}
# 隔离只服务代码域(Godot 运行面);文档域不启引擎,跳过省 0.8 秒。
if [ "$DOMAIN" = "code" ] || [ "$DOMAIN" = "full" ]; then
"$REPO/tools/setup_worktree_userdir.sh" "$REPO" || {
echo "[gates] FAIL — user 目录隔离未建立,拒绝在共享目录上跑闸。"
exit 1
}
fi
# 控制台编码:本仓库闸件输出中文与 ✅/✗ 符号。Windows GBK 控制台下 python 打印这些字符会抛
# UnicodeEncodeError——待办清单/10 两闸直接崩(假红)、09 的 FAIL 行被写成乱码字节(指纹漂移,
@@ -338,6 +357,38 @@ gate() { # gate <名称> <日志名> <命令...>
return 0
}
# 增量文档检查壳(--staged 档专用):先按暂存清单扫(快路径),扫出红时全量复跑定谳——
# 清单面外文件的失败不在全量档判定面内,全量复跑绿=按绿放行并注记,红=真红。
# 增量判定面与 CI 全量档严格对齐(增量档不产生全量档不会有的红)。
gate_docs_inc() { # gate_docs_inc <名称> <日志名> <全量命令...> -- <增量命令...>
local name="$1" log="$2"; shift 2
local -a full_cmd=() inc_cmd=()
local _in_inc=0 _a
for _a in "$@"; do
if [ "$_a" = "--" ]; then _in_inc=1; continue; fi
if [ $_in_inc -eq 0 ]; then full_cmd+=("$_a"); else inc_cmd+=("$_a"); fi
done
echo ""
echo "════════════════════════════════════════════"
echo " $name(增量档:暂存 .md $(( ${#inc_cmd[@]} - ${#full_cmd[@]} )) 件)"
echo "════════════════════════════════════════════"
local rc=0
"${inc_cmd[@]}" >"$STAMP_DIR/$log" 2>&1
rc=$?
if [ $rc -ne 0 ]; then
"${full_cmd[@]}" >>"$STAMP_DIR/$log" 2>&1
rc=$?
if [ $rc -eq 0 ]; then
echo " [增量注记] 清单扫描出红、全量复跑绿——红项在暂存清单面外(不在全量档判定面内),按绿放行。"
fi
fi
if [ $rc -ne 0 ]; then
grep -E "FAIL|红|Error|error" "$STAMP_DIR/$log" | tail -12 | sed 's/^/ | /'
fi
NAMES+=("$name"); EXITS+=("$rc"); LOGS+=("$log")
return 0
}
gate_unit() { # run_all 是 --script 模式,退出码为唯一判据;分片态=多 Godot 进程各跑一片
local name="02 单元与集成测试"
# 关联档选跑:把套件词列表经 --only= 传给 run_all(闸机来源标记 --only-source=gates,
@@ -425,19 +476,62 @@ else
gate "05 渲染闸" "render.log" bash "$REPO/tools/render_gate.sh" "$GAME"
fi
fi
# ══ 文档域(6–8c)══
# ══ 文档域(6–8e)══
if in_domain docs; then
# ── 6. 文档三闸 ──
gate "06 gdd_lint" "gdd_lint.log" "$PY" "$REPO/tools/gdd_lint.py"
gate "06b 变更记录节" "changelog.log" "$PY" "$REPO/tools/gdd_lint.py" --changelog-only
gate "07 doc_link" "doc_link.log" "$PY" "$REPO/tools/doc_link_audit.py" --strict
gate "08 doc_term" "doc_term.log" "$PY" "$REPO/tools/doc_term_audit.py" --strict
# ── 8c. 流水线闸(待办清单条目状态机+roadmap 迭代总表+一次交叉核,判据=doc-conventions §12)──
gate "08c 流水线闸" "pipeline.log" "$PY" "$REPO/tools/pipeline_gate.py"
# ── 8d. 引用可核(现行文档里「文件:行号」指针的目标存在性与行界,判据=件头注)──
gate "08d 引用可核" "doc_ref.log" "$PY" "$REPO/tools/doc_ref_audit.py" --strict
# ── 8e. 能力审计(GDD 纯度+能力清单与覆盖域表结构,判据=test-conventions §8.9+doc-conventions §5)──
gate "08e 能力审计" "capability.log" "$PY" "$REPO/tools/capability_audit.py" --strict
# ── 增量触发矩阵(唯一落点,判据依据与漏检面见头注 --staged 段)──
_md_list=()
_trigger_08c=0
_trigger_08e=0
if [ -n "$STAGED_FILE" ]; then
while IFS= read -r _f; do
[ -z "$_f" ] && continue
case "$_f" in
待办清单.md|roadmap.md|reports/*) _trigger_08c=1 ;;
esac
case "$_f" in
design/gdd/systems/*|game/tests/README.md|reports/*) _trigger_08e=1 ;;
esac
case "$_f" in
*.md) [ -f "$REPO/$_f" ] && _md_list+=("$_f") ;;
esac
done < "$STAGED_FILE"
fi
if [ -z "$STAGED_FILE" ]; then
# 全量档(CI 与手动档原路径,判定面不变)
gate "06 gdd_lint" "gdd_lint.log" "$PY" "$REPO/tools/gdd_lint.py"
gate "06b 变更记录节" "changelog.log" "$PY" "$REPO/tools/gdd_lint.py" --changelog-only
gate "07 doc_link" "doc_link.log" "$PY" "$REPO/tools/doc_link_audit.py" --strict
gate "08 doc_term" "doc_term.log" "$PY" "$REPO/tools/doc_term_audit.py" --strict
gate "08c 流水线闸" "pipeline.log" "$PY" "$REPO/tools/pipeline_gate.py"
gate "08d 引用可核" "doc_ref.log" "$PY" "$REPO/tools/doc_ref_audit.py" --strict
gate "08e 能力审计" "capability.log" "$PY" "$REPO/tools/capability_audit.py" --strict
else
# 增量档:清单内 .md 走快路径(扫出红全量复跑定谳,见 gate_docs_inc)
if [ "${#_md_list[@]}" -gt 0 ]; then
gate_docs_inc "06 gdd_lint" "gdd_lint.log" "$PY" "$REPO/tools/gdd_lint.py" -- "$PY" "$REPO/tools/gdd_lint.py" "${_md_list[@]}"
gate_docs_inc "06b 变更记录节" "changelog.log" "$PY" "$REPO/tools/gdd_lint.py" --changelog-only -- "$PY" "$REPO/tools/gdd_lint.py" --changelog-only "${_md_list[@]}"
gate_docs_inc "07 doc_link" "doc_link.log" "$PY" "$REPO/tools/doc_link_audit.py" --strict -- "$PY" "$REPO/tools/doc_link_audit.py" --strict "${_md_list[@]}"
gate_docs_inc "08 doc_term" "doc_term.log" "$PY" "$REPO/tools/doc_term_audit.py" --strict -- "$PY" "$REPO/tools/doc_term_audit.py" --strict "${_md_list[@]}"
gate_docs_inc "08d 引用可核" "doc_ref.log" "$PY" "$REPO/tools/doc_ref_audit.py" --strict -- "$PY" "$REPO/tools/doc_ref_audit.py" --strict "${_md_list[@]}"
else
for _n in "06 gdd_lint" "06b 变更记录节" "07 doc_link" "08 doc_term" "08d 引用可核"; do
NAMES+=("$_n"); EXITS+=("SKIP"); LOGS+=("")
done
echo ""; echo "[gates] 06/06b/07/08/08d → SKIP(增量档:暂存清单无 .md)"
fi
if [ "$_trigger_08c" -eq 1 ]; then
gate "08c 流水线闸" "pipeline.log" "$PY" "$REPO/tools/pipeline_gate.py"
else
NAMES+=("08c 流水线闸"); EXITS+=("SKIP"); LOGS+=("")
echo ""; echo "[gates] 08c 流水线闸 → SKIP(增量档:登记面文件未触)"
fi
if [ "$_trigger_08e" -eq 1 ]; then
gate "08e 能力审计" "capability.log" "$PY" "$REPO/tools/capability_audit.py" --strict
else
NAMES+=("08e 能力审计"); EXITS+=("SKIP"); LOGS+=("")
echo ""; echo "[gates] 08e 能力审计 → SKIP(增量档:系统文档/能力清单/卡片未触)"
fi
fi
fi
# ── 7. 静态扫描(代码域)+ 熵增闸(两域都跑:它查顶层目录/根文件/图像白名单/退役路径,