diff --git a/zero-point/core-channel/revive-guard/deploy-pre-receive.sh b/zero-point/core-channel/revive-guard/deploy-pre-receive.sh index f7060f5..38acb93 100644 --- a/zero-point/core-channel/revive-guard/deploy-pre-receive.sh +++ b/zero-point/core-channel/revive-guard/deploy-pre-receive.sh @@ -23,9 +23,11 @@ echo "" # 1. 创建钩子目录 mkdir -p "${FORGEJO_HOOK_DIR}" -# 2. 复制审计脚本 +# 2. 复制服务器守门人及其导航依赖 cp pre-receive-guard.py "${FORGEJO_HOOK_DIR}/${SCRIPT_NAME}" chmod +x "${FORGEJO_HOOK_DIR}/${SCRIPT_NAME}" +cp navigation-memory-guard.py "${FORGEJO_HOOK_DIR}/navigation-memory-guard.py" +chmod +x "${FORGEJO_HOOK_DIR}/navigation-memory-guard.py" # 3. 创建钩子包装器(Forgejo 调用这个 shell 脚本 → 内部调 Python) cat > "${FORGEJO_HOOK_DIR}/${HOOK_NAME}" << 'WRAPPER' diff --git a/zero-point/core-channel/revive-guard/install-hooks.sh b/zero-point/core-channel/revive-guard/install-hooks.sh index 97eed11..e810f92 100644 --- a/zero-point/core-channel/revive-guard/install-hooks.sh +++ b/zero-point/core-channel/revive-guard/install-hooks.sh @@ -16,14 +16,16 @@ fi HOOK_DIR="$REPO_ROOT/.git/hooks" PLUGIN_SRC="$REPO_ROOT/zero-point/core-channel/revive-guard/pre-push-clean.py" +NAVIGATION_GUARD_SRC="$REPO_ROOT/zero-point/core-channel/revive-guard/navigation-memory-guard.py" PLUGIN_DST="$HOOK_DIR/pre-push" +NAVIGATION_GUARD_DST="$HOOK_DIR/navigation-memory-guard.py" echo "🛡️ 光湖 pre-push-clean 插件安装" echo " 仓库: $(basename "$REPO_ROOT")" echo "" # 1. 检查源文件 -if [ ! -f "$PLUGIN_SRC" ]; then +if [ ! -f "$PLUGIN_SRC" ] || [ ! -f "$NAVIGATION_GUARD_SRC" ]; then echo "❌ 找不到 $PLUGIN_SRC" echo " 请确保在 fifth-domain 仓库根目录下执行" exit 1 @@ -42,6 +44,8 @@ fi # 4. 安装插件 cp "$PLUGIN_SRC" "$PLUGIN_DST" chmod +x "$PLUGIN_DST" +cp "$NAVIGATION_GUARD_SRC" "$NAVIGATION_GUARD_DST" +chmod +x "$NAVIGATION_GUARD_DST" # 5. 验证 echo "" diff --git a/zero-point/core-channel/revive-guard/navigation-memory-guard.py b/zero-point/core-channel/revive-guard/navigation-memory-guard.py index e5cc45b..1c9e20c 100644 --- a/zero-point/core-channel/revive-guard/navigation-memory-guard.py +++ b/zero-point/core-channel/revive-guard/navigation-memory-guard.py @@ -5,15 +5,24 @@ This guard is deliberately structural: it does not judge prose. It verifies that a new/changed durable memory leaf has the minimum HLDP recovery fields and can be reached from a current navigation page. """ -import os, re, subprocess, sys +import os, subprocess, sys REQUIRED = ("trigger:", "emergence:", "lock:", "why:", "checkpoint") NAVIGATION = ("INDEX.hdlp", "CURRENT.hdlp", "LL-CURRENT.hdlp", "BROADCAST-TOWER.hdlp") def changed_files(): + """Return candidate files for a local check or a server receive range.""" + if len(sys.argv) == 3 and sys.argv[1] == "--range": + old_rev, new_rev = sys.argv[2].split("..", 1) + if old_rev == "0" * 40: + command = ["git", "diff-tree", "--no-commit-id", "-r", "--name-only", "--diff-filter=ACM", new_rev] + else: + command = ["git", "diff", "--name-only", "--diff-filter=ACM", old_rev, new_rev] + result = subprocess.run(command, capture_output=True, text=True) + return sorted(x for x in result.stdout.splitlines() if x) if result.returncode == 0 else [] + commands = [ ["git", "diff", "--cached", "--name-only", "--diff-filter=ACM"], - ["git", "diff", "--name-only", "HEAD~1", "HEAD", "--diff-filter=ACM"], ] files = set() for cmd in commands: diff --git a/zero-point/core-channel/revive-guard/pre-receive-guard.py b/zero-point/core-channel/revive-guard/pre-receive-guard.py index ac1c40f..e738aa9 100644 --- a/zero-point/core-channel/revive-guard/pre-receive-guard.py +++ b/zero-point/core-channel/revive-guard/pre-receive-guard.py @@ -170,6 +170,7 @@ def main(): all_findings = [] rejected = False + navigation_guard = os.path.join(os.path.dirname(os.path.abspath(__file__)), "navigation-memory-guard.py") for line in sys.stdin: line = line.strip() @@ -184,6 +185,19 @@ def main(): if new_rev == "GIT_TOKEN_REDACTED": continue + # 持续记忆必须先通过结构门禁;服务器使用本次 receive 的精确提交范围, + # 不依赖工作树,也不会扫描上一笔无关提交。 + if os.path.isfile(navigation_guard): + navigation = subprocess.run( + [sys.executable, navigation_guard, "--range", f"{old_rev}..{new_rev}"], + capture_output=True, + text=True, + timeout=15, + ) + if navigation.returncode != 0: + print(navigation.stderr or navigation.stdout, file=sys.stderr) + sys.exit(navigation.returncode) + # 获取新增/修改的文件列表 try: # 先检查 commit message 是否含 [SEC-CLEAN] 标记