#!/bin/sh # .githooks/pre-commit — TVU mockup handoff conformance gate(零依赖 · F62-b) # # 目的:消费仓库 commit 含 标记的 docs/handoffs/*.md 时,强制它 # 引用一份存在、通过(exitCode 全 0)、且新鲜(24h 内)的 conformance report JSON。 # 逻辑全在同目录 audit-mockup-handoff-evidence.mjs,唯一硬依赖是 node。 # # 启用(两变体都用这一条,git 原生支持、不需要 husky/npm): # git config core.hooksPath .githooks # # 变体 A(已装 husky):husky 占用 .husky/ 作为 hooksPath,与本文件二选一。 # 推荐把下面这段追加到 .husky/pre-commit(而不是切换 hooksPath),见 .githooks/README.md。 # # 可选环境变量: # TVU_HANDOFF_GATE_STRICT=1 → node 缺失时判失败(默认 fail-open:只告警不拦) set -e # 解析脚本所在目录(core.hooksPath 调用时 CWD 是 repo 根;husky 调用时也一样)。 # 用 `pwd -P`(物理路径,解 symlink)——否则 macOS /var→/private/var 这类 symlink 会让本路径 # 与 mjs 里的 import.meta.url(realpath) 不一致,CLI 守卫失配、脚本被当模块空跑而假通过。 HOOK_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd -P) CHECK="$HOOK_DIR/audit-mockup-handoff-evidence.mjs" # 只看本次 staged 的(新增/复制/修改)handoff markdown STAGED_HANDOFFS=$(git diff --cached --name-only --diff-filter=ACM | grep -E '^docs/handoffs/.*\.md$' || true) if [ -z "$STAGED_HANDOFFS" ]; then exit 0 fi echo "▶ pre-commit: mockup handoff conformance evidence" # node 缺失降级:写设计 / 无 node 环境的变体 B 不应被 brick,但要大声告警 if ! command -v node >/dev/null 2>&1; then echo "⚠️ 未找到 node —— 无法机校 handoff conformance evidence。" echo " handoff 的 mockup 一致性本次【未经机器验证】,只能靠 owner design-walkthrough 人工兜底。" echo " 装 node 后本闸自动生效:https://nodejs.org (node 是本闸唯一硬依赖)" if [ "${TVU_HANDOFF_GATE_STRICT:-0}" = "1" ]; then echo "❌ TVU_HANDOFF_GATE_STRICT=1:node 缺失按失败处理,阻断提交。" exit 1 fi echo " (fail-open:不阻断本次提交。设 TVU_HANDOFF_GATE_STRICT=1 可改为阻断。)" exit 0 fi if [ ! -f "$CHECK" ]; then echo "⚠️ 缺 $CHECK —— 重新 scaffold 或从 TVU 模板复制 .githooks/。本次不阻断。" exit 0 fi # shellcheck disable=SC2086 # 需按空白分词成多个文件参数 node "$CHECK" $STAGED_HANDOFFS || exit 1 echo " ✓ handoff conformance evidence OK" exit 0