"""生成可校验的 Client 发布清单。""" from __future__ import annotations import argparse import hashlib import json from pathlib import Path from typing import Any MANIFEST_FILE_NAME = "autobuy——manifest.json" def file_description(path: Path) -> dict[str, Any]: """返回发布文件的名称、字节大小和 SHA256。""" digest = hashlib.sha256() with path.open("rb") as file: for block in iter(lambda: file.read(1024 * 1024), b""): digest.update(block) return { "file": path.name, "size": path.stat().st_size, "sha256": digest.hexdigest(), } def build_manifest(version: str, update_zip: Path, portable_zip: Path) -> dict[str, Any]: """组装发布清单数据。""" if not version.strip(): raise ValueError("版本号不能为空") for path in (update_zip, portable_zip): if not path.is_file(): raise FileNotFoundError(path) return { "schema_version": 1, "product": "CMAutoBuy", "version": version, "update": file_description(update_zip), "portable": file_description(portable_zip), } def write_manifest( version: str, update_zip: Path, portable_zip: Path, output: Path, ) -> None: """把发布清单写成不带 BOM 的 UTF-8 JSON。""" manifest = build_manifest(version, update_zip, portable_zip) output.parent.mkdir(parents=True, exist_ok=True) output.write_text( json.dumps(manifest, ensure_ascii=False, indent=2) + "\n", encoding="utf-8", ) def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser(description="生成 Client 发布清单") parser.add_argument("--version", required=True) parser.add_argument("--update", required=True, type=Path) parser.add_argument("--portable", required=True, type=Path) parser.add_argument("--output", required=True, type=Path) return parser.parse_args() def main() -> int: args = parse_args() write_manifest(args.version, args.update, args.portable, args.output) return 0 if __name__ == "__main__": raise SystemExit(main())