"""验证期写闸门:验证会话持锁,生成与格式化入口在锁被持有时拒绝改写。 两个反复踩到的坑都从这里堵: - 验证跑着的时候改 `src/`,本轮结论作废,但报告已经按旧结论写出去; - `make 格式写入` 也算改源码,改完没重新 `make 生成`,整层用例报「已安装源码与资源发布身份不符」, 几百个错误全是假的。 锁是 `fcntl.flock`,进程退出即释放,不靠残留 PID 文件判断;锁文件里记一句当前验证说明。 """ from __future__ import annotations import argparse import fcntl import json import os import sys from collections.abc import Iterator from contextlib import contextmanager from pathlib import Path from typing import Any 锁路径 = Path(os.environ.get("MUSE_验证锁", "/tmp/muse-验证锁.lock")) # noqa: S108 _持锁标记 = "MUSE_写入锁已持有" def 已在独占锁内() -> bool: """本进程是否由 `--执行` 启动:父进程已持独占写锁,不能再取共享锁。""" return os.environ.get(_持锁标记) == "1" def 正在验证() -> str | None: """返回当前验证说明;没有验证在跑时返回 None。 `--执行` 会在独占锁内跑写命令,被它启动的子进程(如构建编排)本身就是持锁方, 不能把自己判成「验证正在运行」;用环境变量把这一事实传给子进程。 """ if 已在独占锁内(): return None if not 锁路径.exists(): return None with 锁路径.open("a+", encoding="utf-8") as 文件: try: fcntl.flock(文件, fcntl.LOCK_EX | fcntl.LOCK_NB) except OSError: return "有一个或多个验证会话在运行" else: fcntl.flock(文件, fcntl.LOCK_UN) return None def 登记说明(说明: str) -> None: """把本次验证写进锁文件,供人工查看谁在跑。""" with 锁路径.open("a+", encoding="utf-8") as 文件: 文件.write(f"{说明}\n") @contextmanager def 持有(说明: str) -> Iterator[None]: """验证会话全程持共享锁;写入方要求独占,故分片并行验证互不阻塞。""" 锁路径.parent.mkdir(parents=True, exist_ok=True) with 锁路径.open("a+", encoding="utf-8") as 文件: fcntl.flock(文件, fcntl.LOCK_SH) try: yield finally: fcntl.flock(文件, fcntl.LOCK_UN) def 已安装代码哈希() -> str | None: """直接读安装清单里的代码哈希;不需要打开资源提供器。""" 清单 = Path(__file__).resolve().parents[1] / "src" / "muse" / "资源" / "清单.json" if not 清单.is_file(): return None try: 数据: dict[str, Any] = json.loads(清单.read_text(encoding="utf-8")) except (OSError, ValueError): return None return 数据.get("代码哈希") def 源码身份核对() -> str | None: """返回不一致说明;一致返回 None。只算一次源码哈希,约 0.3 秒。""" from muse.基础设施.系统运行 import 运行代码哈希 期望 = 已安装代码哈希() if 期望 is None: return "缺少已安装资源清单;先执行 make 生成" 当前 = 运行代码哈希() if 当前 != 期望: return "已安装源码与资源发布身份不符;先执行 make 生成(改过 src/ 或跑过格式写入都要重跑)" return None def 独占执行(命令: list[str]) -> int: """在独占锁内执行写命令:写窗口全程持锁,验证会话无法插入。 与 `--要求空闲` 的区别:后者查完即放锁,检查与写入之间仍可被验证会话插入。 """ import subprocess 锁路径.parent.mkdir(parents=True, exist_ok=True) with 锁路径.open("a+", encoding="utf-8") as 文件: try: fcntl.flock(文件, fcntl.LOCK_EX | fcntl.LOCK_NB) except OSError: print( "拒绝改写:验证正在运行;等它结束再改,否则本轮验证结果作废", file=sys.stderr, ) return 1 try: os.environ[_持锁标记] = "1" return subprocess.run(命令, check=False).returncode finally: os.environ.pop(_持锁标记, None) fcntl.flock(文件, fcntl.LOCK_UN) def main(argv: list[str] | None = None) -> int: 解析 = argparse.ArgumentParser(description=__doc__) 解析.add_argument( "--要求空闲", action="store_true", help="锁被持有时非零退出,供写入入口前置检查(查完即放锁)" ) 解析.add_argument("--核对源码身份", action="store_true", help="比较源码哈希与安装清单") 解析.add_argument( "--执行", nargs=argparse.REMAINDER, metavar="命令", help="在独占锁内执行写命令,例如 --执行 uv run --no-sync python 工具/构建编排.py", ) 值 = 解析.parse_args(argv) if 值.要求空闲: 说明 = 正在验证() if 说明: print( f"拒绝改写:验证正在运行({说明});等它结束再改,否则本轮验证结果作废", file=sys.stderr, ) return 1 print("验证空闲:可以改写源码") if 值.核对源码身份: 问题 = 源码身份核对() if 问题: print(问题, file=sys.stderr) return 1 print("源码身份一致:安装清单与当前源码同哈希") if 值.执行 is not None: if not 值.执行: print("--执行 需要给出命令", file=sys.stderr) return 2 return 独占执行(list(值.执行)) return 0 if __name__ == "__main__": raise SystemExit(main())