# -*- coding: utf-8 -*-
"""
代码指纹 (零依赖, 不连库, 不 import 业务模块)
================================================
运行: python scripts/code_fingerprint.py     只打印一行 12 位短哈希

为什么需要它
------------
**源码是打进镜像的**, `docker compose run --rm pms-web python scripts/xxx.py` 跑的是
**镜像里的那份**, 不是工作树里刚 `git pull` 下来的那份。于是有一条特别难发现的路径:

    git pull → make test → ALL SUITES PASS

这个 PASS 是**旧代码的 PASS**。它读起来像"新改的代码验过了", 实际上新代码一行都没跑过 ——
连单测清单都还是旧的 (新增的一批测试文件根本不在镜像里, `run_tests.py` 只会打印
"跳过 xxx (文件不存在)" 然后照样 ALL SUITES PASS)。2026-07-31 实机就是这样:
`make t-pre` 的返回里少了新加的字段, 才反推出容器跑的是旧镜像。

这跟本项目一直在治的是同一种病 —— **一个不成立的结论长得像成立**。所以给它一个指纹:
`make test` 会把容器里的指纹和工作树的指纹对一遍, 对不上就喊, 而不是让那个 PASS 蒙人。

指纹只覆盖 .py 源码 (app/ scripts/ config/)。改 Makefile、compose、DDL 不影响它 ——
那几样不进 Python 运行时, 另有各自的生效路径。
"""
import hashlib
import os
import sys

ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
DIRS = ("app", "scripts", "config")


def fingerprint(root: str = ROOT) -> str:
    h = hashlib.sha256()
    files = []
    for d in DIRS:
        base = os.path.join(root, d)
        for dirpath, dirnames, filenames in os.walk(base):
            # __pycache__ 里是编译产物, 跟着源码走, 不该进指纹
            dirnames[:] = [x for x in dirnames if x != "__pycache__"]
            for fn in filenames:
                if fn.endswith(".py"):
                    files.append(os.path.join(dirpath, fn))
    for p in sorted(files, key=lambda x: os.path.relpath(x, root)):
        rel = os.path.relpath(p, root).replace(os.sep, "/")
        h.update(rel.encode("utf-8"))
        h.update(b"\0")
        with open(p, "rb") as f:
            h.update(hashlib.sha256(f.read()).digest())
    return h.hexdigest()[:12]


if __name__ == "__main__":
    # 只打印指纹本身, 一行, 不带任何前后缀 —— 调用方要拿它做字符串比较
    sys.stdout.write(fingerprint() + "\n")
