From 87a1cde2254ef4427b178d1f9d7fbd636a25a668 Mon Sep 17 00:00:00 2001 From: "sanze.li" <2522048902@qq.com> Date: Sun, 31 May 2026 13:29:20 +0800 Subject: [PATCH] refactor: extract golden snapshot regeneration + auto-fix in pre-commit - New: scripts/regenerate-golden-snapshots.py (single source of truth) - release-preflight.sh: auto-regenerate stale snapshots during pre-commit - release-sync.sh: replace 48-line inline Python with script call - .githooks/pre-commit: stage golden-snapshots.json after version bump - Graceful ImportError skip for test fixture environments Context-Checkpoint: B --- .githooks/pre-commit | 3 +- scripts/regenerate-golden-snapshots.py | 91 ++++++++++++++++++++++++++ scripts/release-preflight.sh | 11 +++- scripts/release-sync.sh | 49 +------------- tests/test_release_hooks.py | 1 + 5 files changed, 105 insertions(+), 50 deletions(-) create mode 100644 scripts/regenerate-golden-snapshots.py diff --git a/.githooks/pre-commit b/.githooks/pre-commit index 2c53289..4afe02e 100755 --- a/.githooks/pre-commit +++ b/.githooks/pre-commit @@ -151,7 +151,8 @@ git -C "$ROOT_DIR" add \ README.zh-CN.md \ CHANGELOG.md \ skills/zh/header.md.template \ - skills/en/header.md.template + skills/en/header.md.template \ + tests/golden-snapshots.json cat >"$STATE_FILE" < str: + """Hash the rendered header template for a host adapter.""" + source = adapter.source_root(ROOT, direction) + content = (source / HEADER_TEMPLATE_NAME).read_text() + content = content.replace("{{config_dir}}", adapter.config_dir or "") + return hashlib.sha256(f"{adapter.header_filename}\x00{content}".encode()).hexdigest() + + +def _tree_hash(direction: str) -> str: + """Hash all skill files to detect content drift.""" + skill_root = ROOT / "skills" / language_to_source_dir(direction) / "skills" / "sopify" + parts = [ + f.relative_to(skill_root).as_posix().encode() + b"\x00" + f.read_bytes() + for f in sorted(skill_root.rglob("*")) + if f.is_file() and f.name not in IGNORE + ] + return hashlib.sha256(b"\n".join(parts)).hexdigest() + + +def _payload_hash(direction: str) -> str: + """Hash the fully rendered Copilot managed block payload.""" + source = COPILOT_ADAPTER.source_root(ROOT, direction) + rendered = render_single_file( + source / HEADER_TEMPLATE_NAME, source / "skills" / "sopify", COPILOT_ADAPTER + ) + return hashlib.sha256(rendered.encode()).hexdigest() + + +def main() -> int: + if not GOLDEN.exists(): + return 0 + + snapshots: dict[str, str] = {} + for adapter, direction, locale in ADAPTERS: + snapshots[f"{adapter.host_name}:{locale}:header"] = _header_hash(adapter, direction) + + for direction, locale in DIRECTIONS: + snapshots[f"copilot:{locale}:managed_block_payload"] = _payload_hash(direction) + snapshots[f"skills:{locale}:tree"] = _tree_hash(direction) + + golden = json.loads(GOLDEN.read_text()) + golden["snapshots"] = snapshots + GOLDEN.write_text(json.dumps(golden, indent=2, ensure_ascii=False) + "\n") + print(f" Golden snapshots regenerated ({len(snapshots)} entries).") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/release-preflight.sh b/scripts/release-preflight.sh index 06e313e..7867df8 100755 --- a/scripts/release-preflight.sh +++ b/scripts/release-preflight.sh @@ -57,7 +57,16 @@ PY } run_step "Check version consistency" bash "$ROOT_DIR/scripts/check-version-consistency.sh" -run_step "Check golden snapshots" python3 -m pytest "$ROOT_DIR/tests/test_golden_snapshots.py" -q + +# Golden snapshots: auto-regenerate if stale, then verify +if ! python3 -m pytest "$ROOT_DIR/tests/test_golden_snapshots.py" -q 2>/dev/null; then + echo "[release-preflight] Golden snapshots stale — regenerating..." + python3 "$ROOT_DIR/scripts/regenerate-golden-snapshots.py" + git add "$ROOT_DIR/tests/golden-snapshots.json" + run_step "Re-check golden snapshots" python3 -m pytest "$ROOT_DIR/tests/test_golden_snapshots.py" -q +else + echo "[release-preflight] Check golden snapshots" +fi run_step "Check builtin catalog drift" check_builtin_catalog_drift run_step "Check context checkpoints" python3 "$ROOT_DIR/scripts/check-context-checkpoints.py" repo --root "$ROOT_DIR" run_step "Run hard gate tests (contract + smoke + distribution)" python3 -m pytest "$ROOT_DIR/tests" -m "not implementation_mirror" -v diff --git a/scripts/release-sync.sh b/scripts/release-sync.sh index 1019397..2c53f9e 100755 --- a/scripts/release-sync.sh +++ b/scripts/release-sync.sh @@ -298,53 +298,6 @@ replace_once "$SKILLS_EN" '^$' "