合并(闸件自检档·工程批): 纯闸件/管线批不再零触发——domain_route只含tools/.gitea批判gates(空diff仍skip,full/docs不动)+run_all_gates新增--gates档(文档域python机检件+熵增+静态扫描+闸壳bash -n自检12件,不启引擎,判据口径原样)+ci.yml档位映射三值化(full→空/docs→--docs/gates→--gates)与导入缓存步排除gates档; 闸壳自检入基线4行(用户认可)+并origin/master(c16a2bf7)零冲突后重出--gates绿(exit0,回执86d266a2); 本机全量档12绿+1红(settlement_wiring_snapshot golden平台绑定=在册7046cbaf遗留,非本批引入,远端Mac绿)+3闸影响面SKIP; 分流四例直跑full/docs/gates/skip全对; post-merge钩子推送触发远端CI(diff含baseline.json→全量档)
ci / gates (push) Successful in 5m22s
ci / gates (push) Successful in 5m22s
This commit is contained in:
commit
7e7f737c7e
4 files changed
+82
-21
No files matched your search
@@ -2,7 +2,8 @@
|
|||||||
# 触发:master 收到 push(post-merge 钩子合回自动推,或人手推 master)——分支推送不触发
|
# 触发:master 收到 push(post-merge 钩子合回自动推,或人手推 master)——分支推送不触发
|
||||||
# (开发分支不烧远端引擎时长;分支级验证=开发期手动跑批档)。
|
# (开发分支不烧远端引擎时长;分支级验证=开发期手动跑批档)。
|
||||||
# 跑什么由 tools/ci/domain_route.sh 按改动域分流(乙案)——
|
# 跑什么由 tools/ci/domain_route.sh 按改动域分流(乙案)——
|
||||||
# 游戏码批=两域全量(含渲染闸);文档批=文档域秒级闸;纯闸件/管线批=零触发。
|
# 游戏码批=两域全量(含渲染闸);文档批=文档域秒级闸;纯闸件/管线批=闸件自检档
|
||||||
|
# (run_all_gates.sh --gates:不启引擎的机检面+闸壳自检)。
|
||||||
# 运行环境:Mac host 模式(tools/ci/mac_setup.sh 自举),GODOT_BIN/GDSTYLE_BIN 由自举件安装到位。
|
# 运行环境:Mac host 模式(tools/ci/mac_setup.sh 自举),GODOT_BIN/GDSTYLE_BIN 由自举件安装到位。
|
||||||
# 检出用纯 shell 克隆:host 模式执行 JS 系动作(actions/checkout)需要 node 运行时,
|
# 检出用纯 shell 克隆:host 模式执行 JS 系动作(actions/checkout)需要 node 运行时,
|
||||||
# runner 机器无 node——首跑实证「Cannot find: node in PATH」即败(run 4),本仓禁用 JS 系动作,
|
# runner 机器无 node——首跑实证「Cannot find: node in PATH」即败(run 4),本仓禁用 JS 系动作,
|
||||||
@@ -32,8 +33,8 @@ jobs:
|
|||||||
cd "$GITHUB_WORKSPACE/repo"
|
cd "$GITHUB_WORKSPACE/repo"
|
||||||
bash tools/ci/domain_route.sh "${{ github.event.before }}" "${{ github.sha }}"
|
bash tools/ci/domain_route.sh "${{ github.event.before }}" "${{ github.sha }}"
|
||||||
|
|
||||||
- name: 导入缓存刷新(闸前置——.godot 不入 git,冷检出一付)
|
- name: 导入缓存刷新(闸前置——.godot 不入 git,冷检出一付;gates 档不启引擎,跳过)
|
||||||
if: steps.route.outputs.mode != 'skip'
|
if: steps.route.outputs.mode != 'skip' && steps.route.outputs.mode != 'gates'
|
||||||
run: |
|
run: |
|
||||||
cd "$GITHUB_WORKSPACE/repo"
|
cd "$GITHUB_WORKSPACE/repo"
|
||||||
/usr/local/bin/godot --headless --path game --editor --quit
|
/usr/local/bin/godot --headless --path game --editor --quit
|
||||||
@@ -46,6 +47,10 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
cd "$GITHUB_WORKSPACE/repo"
|
cd "$GITHUB_WORKSPACE/repo"
|
||||||
MODE="${{ steps.route.outputs.mode }}"
|
MODE="${{ steps.route.outputs.mode }}"
|
||||||
if [ "$MODE" = "full" ]; then ARGS=""; else ARGS="--docs"; fi
|
case "$MODE" in
|
||||||
|
full) ARGS="" ;;
|
||||||
|
gates) ARGS="--gates" ;;
|
||||||
|
*) ARGS="--docs" ;;
|
||||||
|
esac
|
||||||
echo "[ci] 域=$MODE 跑闸参数=$ARGS"
|
echo "[ci] 域=$MODE 跑闸参数=$ARGS"
|
||||||
bash tools/run_all_gates.sh $ARGS
|
bash tools/run_all_gates.sh $ARGS
|
||||||
@@ -60,6 +60,10 @@
|
|||||||
"单元与集成测试": {
|
"单元与集成测试": {
|
||||||
"exit": "0",
|
"exit": "0",
|
||||||
"fp": "green"
|
"fp": "green"
|
||||||
|
},
|
||||||
|
"闸壳自检": {
|
||||||
|
"exit": "0",
|
||||||
|
"fp": "green"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,14 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# 乙案域分流——push 触发的远端 CI 按改动域选闸(2026-11-03 用户裁)。
|
# 乙案域分流——push 触发的远端 CI 按改动域选闸(2026-11-03 用户裁)。
|
||||||
# 用法: tools/ci/domain_route.sh <base_sha> <head_sha>
|
# 用法: tools/ci/domain_route.sh <base_sha> <head_sha>
|
||||||
# 输出: stdout 打印 full|docs|skip;GITHUB_OUTPUT 在位时另写 mode= 行(Actions 步骤输出)。
|
# 输出: stdout 打印 full|docs|gates|skip;GITHUB_OUTPUT 在位时另写 mode= 行(Actions 步骤输出)。
|
||||||
# 规则(唯一事实源,乙案语义):
|
# 规则(唯一事实源,乙案语义):
|
||||||
# 1. diff 含 game/ 下非 .md 文件(行为载体:代码/场景/数据表/素材)→ full(两域全量,渲染闸随 code 域)
|
# 1. diff 含 game/ 下非 .md 文件(行为载体:代码/场景/数据表/素材)→ full(两域全量,渲染闸随 code 域)
|
||||||
# 2. 否则 diff 非空(design/ 文档、reports/、根 md、game/ 下 .md 等一切非代码面)→ docs
|
# 2. 否则 diff 非空(design/ 文档、reports/、根 md、game/ 下 .md 等一切非代码面)→ docs
|
||||||
# (乙案:文档批只跑文档域秒级闸——gdd_lint/doc_link/doc_term/pipeline/doc_ref/capability+熵增)
|
# (乙案:文档批只跑文档域秒级闸——gdd_lint/doc_link/doc_term/pipeline/doc_ref/capability+熵增)
|
||||||
# 例外:diff 只含 tools/ 与 .gitea/(闸件与管线自身改动)→ skip——闸件改动不占合回全量,
|
# 例外:diff 只含 tools/ 与 .gitea/(闸件与管线自身改动)→ gates——闸件自检档:跑不启引擎的
|
||||||
# 质量义务=落批人工确认可跑通(run_all_gates.sh 头注既有口径,不在远端重复烧引擎)。
|
# 机检面(文档域 python 机检件+熵增闸+静态扫描+闸壳 bash -n 自检,见 run_all_gates.sh
|
||||||
|
# --gates),闸件改动不再零触发,也不在远端重复烧引擎。
|
||||||
# 3. diff 空 → skip。
|
# 3. diff 空 → skip。
|
||||||
# 判定面说明:game/tests/** 与 game/sim/** 属 game/ 行为载体(.md 除外)→ full,
|
# 判定面说明:game/tests/** 与 game/sim/** 属 game/ 行为载体(.md 除外)→ full,
|
||||||
# 与 run_all_gates.sh 关联档口径一致(测量面批单元与集成测试照跑全套件)。
|
# 与 run_all_gates.sh 关联档口径一致(测量面批单元与集成测试照跑全套件)。
|
||||||
@@ -28,7 +29,7 @@ else
|
|||||||
mode="full"
|
mode="full"
|
||||||
else
|
else
|
||||||
non_tools="$(echo "$files" | grep -vE '^(tools/|\.gitea/)' || true)"
|
non_tools="$(echo "$files" | grep -vE '^(tools/|\.gitea/)' || true)"
|
||||||
if [ -n "$non_tools" ]; then mode="docs"; else mode="skip"; fi
|
if [ -n "$non_tools" ]; then mode="docs"; else mode="gates"; fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
if [ -n "${GITHUB_OUTPUT:-}" ]; then printf 'mode=%s\n' "$mode" >> "$GITHUB_OUTPUT"; fi
|
if [ -n "${GITHUB_OUTPUT:-}" ]; then printf 'mode=%s\n' "$mode" >> "$GITHUB_OUTPUT"; fi
|
||||||
|
|||||||
+64
-13
@@ -5,10 +5,11 @@
|
|||||||
#
|
#
|
||||||
# 跑完写回执 game/tests/.gates/receipt.json(commit+内容树哈希+各闸退出码+存量基线比对+时间戳);
|
# 跑完写回执 game/tests/.gates/receipt.json(commit+内容树哈希+各闸退出码+存量基线比对+时间戳);
|
||||||
# 强制验证点=合回 master 后 post-merge 钩子推送触发的远端跑批;远端结果用 tools/ci_check.sh 现查(只呈报、
|
# 强制验证点=合回 master 后 post-merge 钩子推送触发的远端跑批;远端结果用 tools/ci_check.sh 现查(只呈报、
|
||||||
# 不落盘——2026-11-04 用户裁去掉回执等待件与本地判定缓存)。本脚本手动调用(--code/--docs/全量档)只服务开发期按域跑批。
|
# 不落盘——2026-11-04 用户裁去掉回执等待件与本地判定缓存)。本脚本手动调用(--code/--docs/--gates/全量档)
|
||||||
|
# 只服务开发期按域跑批;--gates 档另为远端纯闸件/管线批的执行形态(domain_route.sh 判 gates → ci.yml 映射 --gates)。
|
||||||
# 跑闸时代码面带着未提交改动,回执会标 dirty_game(验的不是 HEAD 字节)。
|
# 跑闸时代码面带着未提交改动,回执会标 dirty_game(验的不是 HEAD 字节)。
|
||||||
#
|
#
|
||||||
# 用法:tools/run_all_gates.sh [--code|--docs|--quick]
|
# 用法:tools/run_all_gates.sh [--code|--docs|--gates|--quick]
|
||||||
# --code 只跑代码域闸(冒烟测试/系统测试/单元与集成测试/场景探针/
|
# --code 只跑代码域闸(冒烟测试/系统测试/单元与集成测试/场景探针/
|
||||||
# 渲染闸/静态扫描+熵增闸)
|
# 渲染闸/静态扫描+熵增闸)
|
||||||
# ——本批改动含 game/ 下非 .md 的行为载体(代码/测试脚本/探针/场景/数据表/素材)时用。
|
# ——本批改动含 game/ 下非 .md 的行为载体(代码/测试脚本/探针/场景/数据表/素材)时用。
|
||||||
@@ -20,11 +21,15 @@
|
|||||||
# --docs 只跑文档域闸(gdd_lint/doc_link/doc_term/流水线闸/引用可核/
|
# --docs 只跑文档域闸(gdd_lint/doc_link/doc_term/流水线闸/引用可核/
|
||||||
# 能力审计+熵增闸)
|
# 能力审计+熵增闸)
|
||||||
# ——本批改动只有文档件时用:game/ 内的 .md(README、生成清单)、design/、根规则文件、
|
# ——本批改动只有文档件时用:game/ 内的 .md(README、生成清单)、design/、根规则文件、
|
||||||
# tools/ 工具脚本、.zcode/ 配置等一切非代码面改动。
|
# .zcode/ 配置等一切非代码面改动(tools/ 与 .gitea/ 只含批走 --gates 档,见下)。
|
||||||
|
# --gates 闸件自检档(2026-11-05 用户裁):只跑不启引擎的机检面——文档域 python 机检件
|
||||||
|
# (gdd_lint/变更记录节/doc_link/doc_term/流水线闸/引用可核/能力审计)+熵增闸
|
||||||
|
# +静态扫描+闸壳自检(bash -n 过全部 bash 闸件与管线件)。各机检件的命令与判据口径
|
||||||
|
# 与原档一致,不另造判据。本批改动只含 tools/ 与 .gitea/(闸件与管线自身)时用——
|
||||||
|
# 纯闸件/管线批合回后远端照跑本档(domain_route.sh 判 gates),闸件改动不再零验证。
|
||||||
# --quick 全量档的中段自检形态:跳过渲染闸(窗口化,需开显示器)与静态扫描;完成出库禁用。
|
# --quick 全量档的中段自检形态:跳过渲染闸(窗口化,需开显示器)与静态扫描;完成出库禁用。
|
||||||
# 不带参数 = 全量档(两域全跑),触发条件=同批既有代码面改动又有文档件改动。闸件(tools/ 下机检脚本
|
# 不带参数 = 全量档(两域全跑),触发条件=同批既有代码面改动又有文档件改动。闸真崩溃时
|
||||||
# 与闸壳)自身的改动不占合回全量——其质量义务=落批时人工确认闸可跑通且判据口径未变;
|
# 合回闸 fail-closed(跑不出回执即拦),不以全量为兜底。
|
||||||
# 闸真崩溃时合回闸 fail-closed(跑不出回执即拦),不以全量为兜底。
|
|
||||||
# **域划分只在本脚本里定义一处**——文档只写「按改动域跑本脚本」,禁另列闸清单(清单散落多处
|
# **域划分只在本脚本里定义一处**——文档只写「按改动域跑本脚本」,禁另列闸清单(清单散落多处
|
||||||
# 正是既往漏跑场景探针扩展段的成因)。
|
# 正是既往漏跑场景探针扩展段的成因)。
|
||||||
#
|
#
|
||||||
@@ -51,17 +56,17 @@ DOMAIN="full"
|
|||||||
while [ $# -gt 0 ]; do
|
while [ $# -gt 0 ]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
--quick) QUICK=1 ;;
|
--quick) QUICK=1 ;;
|
||||||
--code|--docs)
|
--code|--docs|--gates)
|
||||||
[ "$DOMAIN" = "full" ] || { echo "用法: --code 与 --docs 互斥"; exit 2; }
|
[ "$DOMAIN" = "full" ] || { echo "用法: --code/--docs/--gates 互斥"; exit 2; }
|
||||||
DOMAIN="${1#--}" ;;
|
DOMAIN="${1#--}" ;;
|
||||||
*) echo "用法: tools/run_all_gates.sh [--code|--docs|--quick]"; exit 2 ;;
|
*) echo "用法: tools/run_all_gates.sh [--code|--docs|--gates|--quick]"; exit 2 ;;
|
||||||
esac
|
esac
|
||||||
shift
|
shift
|
||||||
done
|
done
|
||||||
|
|
||||||
# 每个代码面 worktree 用独立 Godot user 目录(game/override.cfg 由 setup 脚本自愈生成;主区 no-op)。
|
# 每个代码面 worktree 用独立 Godot user 目录(game/override.cfg 由 setup 脚本自愈生成;主区 no-op)。
|
||||||
# 并行批共用一套夹具、日志与截图目录会互相踩盘造成假红——隔离建不起来就拒跑(fail-closed)。
|
# 并行批共用一套夹具、日志与截图目录会互相踩盘造成假红——隔离建不起来就拒跑(fail-closed)。
|
||||||
# 隔离只服务代码域(Godot 运行面);文档域不启引擎,跳过省 0.8 秒。
|
# 隔离只服务代码域(Godot 运行面);文档域与闸件自检档不启引擎,跳过省 0.8 秒。
|
||||||
if [ "$DOMAIN" = "code" ] || [ "$DOMAIN" = "full" ]; then
|
if [ "$DOMAIN" = "code" ] || [ "$DOMAIN" = "full" ]; then
|
||||||
"$REPO/tools/setup_worktree_userdir.sh" "$REPO" || {
|
"$REPO/tools/setup_worktree_userdir.sh" "$REPO" || {
|
||||||
echo "[gates] FAIL — user 目录隔离未建立,拒绝在共享目录上跑闸。"
|
echo "[gates] FAIL — user 目录隔离未建立,拒绝在共享目录上跑闸。"
|
||||||
@@ -115,6 +120,8 @@ in_domain() { # in_domain <code|docs|both>
|
|||||||
[ "$dom" = "both" ] && return 0
|
[ "$dom" = "both" ] && return 0
|
||||||
[ "$DOMAIN" = "full" ] && return 0
|
[ "$DOMAIN" = "full" ] && return 0
|
||||||
[ "$DOMAIN" = "$dom" ] && return 0
|
[ "$DOMAIN" = "$dom" ] && return 0
|
||||||
|
# gates 档跑文档域机检件——同一命令同一判据,闸件批不启引擎、机检面照跑。
|
||||||
|
[ "$DOMAIN" = "gates" ] && [ "$dom" = "docs" ] && return 0
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -396,6 +403,45 @@ gate_unit() { # run_all 是 --script 模式,退出码为唯一判据;分
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ══ 闸件自检段(gates 档专属)══
|
||||||
|
# 闸壳自检:bash -n 过全部 bash 闸件与管线件——闸件批改的就是这些件,语法坏=下一批真跑闸时才炸,
|
||||||
|
# 闸件批当场拦住。收件面动态收全:tools/ 全部 .sh+game/tests 顶层 .sh(静态扫描)+hooks/post-merge
|
||||||
|
# (无后缀件);新增 bash 闸件自动入检面,无需维护清单。
|
||||||
|
gate_shell_selfcheck() {
|
||||||
|
local name="闸壳自检"
|
||||||
|
local rc=0 _n=0 _bad=0 _s
|
||||||
|
local -a _shells=()
|
||||||
|
while IFS= read -r _s; do [ -n "$_s" ] && _shells+=("$_s"); done < <(
|
||||||
|
{ find "$REPO/tools" -name '*.sh' 2>/dev/null
|
||||||
|
find "$REPO/game/tests" -maxdepth 1 -name '*.sh' 2>/dev/null
|
||||||
|
echo "$REPO/tools/hooks/post-merge"; } | sort -u
|
||||||
|
)
|
||||||
|
echo ""
|
||||||
|
echo "════════════════════════════════════════════"
|
||||||
|
echo " $name(bash -n 全部 bash 闸件与管线件)"
|
||||||
|
echo "════════════════════════════════════════════"
|
||||||
|
: >"$STAMP_DIR/shell_selfcheck.log"
|
||||||
|
for _s in "${_shells[@]}"; do
|
||||||
|
[ -f "$_s" ] || continue
|
||||||
|
_n=$((_n + 1))
|
||||||
|
if bash -n "$_s" 2>>"$STAMP_DIR/shell_selfcheck.log"; then
|
||||||
|
echo "ok: ${_s#"$REPO"/}" >>"$STAMP_DIR/shell_selfcheck.log"
|
||||||
|
else
|
||||||
|
echo "FAIL: bash -n ${_s#"$REPO"/} 解析失败" >>"$STAMP_DIR/shell_selfcheck.log"
|
||||||
|
_bad=$((_bad + 1))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [ "$_bad" -eq 0 ]; then
|
||||||
|
echo "bash -n × $_n 件全过" >>"$STAMP_DIR/shell_selfcheck.log"
|
||||||
|
else
|
||||||
|
rc=1
|
||||||
|
grep -E "FAIL" "$STAMP_DIR/shell_selfcheck.log" | sed 's/^/ | /'
|
||||||
|
fi
|
||||||
|
echo "[gates] $name:bash -n × $_n 件,失败 $_bad(日志 shell_selfcheck.log)"
|
||||||
|
NAMES+=("$name"); EXITS+=("$rc"); LOGS+=("shell_selfcheck.log")
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
# ══ 代码域 ══
|
# ══ 代码域 ══
|
||||||
if in_domain code; then
|
if in_domain code; then
|
||||||
# ── 冒烟测试(极小启动场景:真实开机路径存活检查,零逻辑判定,秒级)──
|
# ── 冒烟测试(极小启动场景:真实开机路径存活检查,零逻辑判定,秒级)──
|
||||||
@@ -440,15 +486,20 @@ if in_domain docs; then
|
|||||||
gate "能力审计" "capability.log" "$PY" "$REPO/tools/capability_audit.py" --strict
|
gate "能力审计" "capability.log" "$PY" "$REPO/tools/capability_audit.py" --strict
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── 静态扫描(代码域)+ 熵增闸(两域都跑:它查顶层目录/根文件/图像白名单/退役路径,
|
# ── 闸件自检段(gates 档专属)──
|
||||||
|
if [ "$DOMAIN" = "gates" ]; then
|
||||||
|
gate_shell_selfcheck
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 静态扫描(代码域与闸件自检档)+ 熵增闸(各档都跑:它查顶层目录/根文件/图像白名单/退役路径,
|
||||||
# 文档批新增或挪动 design/ 下的文件同样会绊它;纯文本扫描,秒级)──
|
# 文档批新增或挪动 design/ 下的文件同样会绊它;纯文本扫描,秒级)──
|
||||||
if [ $QUICK -eq 0 ]; then
|
if [ $QUICK -eq 0 ]; then
|
||||||
if in_domain code; then
|
if in_domain code || [ "$DOMAIN" = "gates" ]; then
|
||||||
gate "静态扫描" "static_scan.log" bash "$GAME/tests/static_scan.sh"
|
gate "静态扫描" "static_scan.log" bash "$GAME/tests/static_scan.sh"
|
||||||
fi
|
fi
|
||||||
gate "熵增闸" "entropy.log" "$PY" "$REPO/tools/repo_structure_audit.py"
|
gate "熵增闸" "entropy.log" "$PY" "$REPO/tools/repo_structure_audit.py"
|
||||||
else
|
else
|
||||||
if in_domain code; then
|
if in_domain code || [ "$DOMAIN" = "gates" ]; then
|
||||||
NAMES+=("静态扫描"); EXITS+=("SKIP"); LOGS+=("")
|
NAMES+=("静态扫描"); EXITS+=("SKIP"); LOGS+=("")
|
||||||
fi
|
fi
|
||||||
NAMES+=("熵增闸"); EXITS+=("SKIP"); LOGS+=("")
|
NAMES+=("熵增闸"); EXITS+=("SKIP"); LOGS+=("")
|
||||||
|
|||||||
Reference in new issue
Block a user