#!/usr/bin/env python3 # -*- coding: utf-8 -*- from __future__ import annotations import argparse import sys from pathlib import Path EXPECTED_PROJECT_VERSION = "0.4.5" EXPECTED_RUNTIME_VERSION = "0.4.5-production-nonrenderable-label-omit" EXPECTED_BUILDER = "cm95pbf-builder/0.4.5-nonrenderable-label-omit" EXPECTED_INVALIDATE_COUNT = 16 def fail(msg: str) -> None: print(f"FAIL: {msg}") raise SystemExit(2) def main() -> None: root = Path.cwd() pyproject = root / "pyproject.toml" init_py = root / "src" / "cm95pbf" / "__init__.py" cli_py = root / "src" / "cm95pbf" / "cli.py" production_py = root / "src" / "cm95pbf" / "production.py" tiles_path = root / "config" / "pbf" / "cm95_v1_4_5_nonrenderable_label_tiles.txt" for path in (pyproject, init_py, cli_py, production_py, tiles_path): if not path.exists(): fail(f"缺少文件: {path}") pyproject_text = pyproject.read_text(encoding="utf-8") if f'version = "{EXPECTED_PROJECT_VERSION}"' not in pyproject_text: fail(f"pyproject 不是 {EXPECTED_PROJECT_VERSION}") init_text = init_py.read_text(encoding="utf-8") if EXPECTED_RUNTIME_VERSION not in init_text: fail("src/cm95pbf/__init__.py runtime version 不正确") if EXPECTED_BUILDER not in init_text: fail("src/cm95pbf/__init__.py builder contract 不正确") cli_text = cli_py.read_text(encoding="utf-8") if "--invalidate-tiles-file" not in cli_text: fail("CLI 缺少 --invalidate-tiles-file") production_text = production_py.read_text(encoding="utf-8") if '"builder_contract_version": BUILDER_CONTRACT_VERSION' not in production_text: fail("production plan hash 未纳入 builder_contract_version") if "_load_explicit_invalidate_tiles" not in production_text: fail("production migration 缺少 explicit invalidate 实现") tiles = [] for raw in tiles_path.read_text(encoding="utf-8").splitlines(): line = raw.strip() if not line or line.startswith("#"): continue parts = line.split("/") if len(parts) != 3: fail(f"非法 invalidate tile: {line}") z, x, y = map(int, parts) if x < 0 or y < 0 or x >= (1 << z) or y >= (1 << z): fail(f"非法 XYZ: {line}") tiles.append((z, x, y)) if len(tiles) != EXPECTED_INVALIDATE_COUNT or len(set(tiles)) != EXPECTED_INVALIDATE_COUNT: fail(f"invalidate tile 数量应为 {EXPECTED_INVALIDATE_COUNT},实际 {len(tiles)} / unique {len(set(tiles))}") # Installed/editable runtime check. try: import cm95pbf from cm95pbf.cli import build_parser except Exception as exc: fail(f"无法 import 已安装 cm95pbf: {exc}") if getattr(cm95pbf, "__version__", None) != EXPECTED_RUNTIME_VERSION: fail(f"installed cm95pbf.__version__={getattr(cm95pbf, '__version__', None)!r}") if getattr(cm95pbf, "BUILDER_CONTRACT_VERSION", None) != EXPECTED_BUILDER: fail(f"installed BUILDER_CONTRACT_VERSION={getattr(cm95pbf, 'BUILDER_CONTRACT_VERSION', None)!r}") parser = build_parser() migrate = None for action in parser._actions: if isinstance(action, argparse._SubParsersAction): migrate = action.choices.get("migrate-production-release") break if migrate is None: fail("CLI 缺少 migrate-production-release") options = {opt for action in migrate._actions for opt in action.option_strings} if "--invalidate-tiles-file" not in options: fail("installed CLI parser 未注册 --invalidate-tiles-file") print("CM95 PBF v1.4.5 preflight") print("=" * 68) print(f"Project version : {EXPECTED_PROJECT_VERSION} PASS") print(f"Runtime version : {EXPECTED_RUNTIME_VERSION} PASS") print(f"Builder contract : {EXPECTED_BUILDER} PASS") print("CLI invalidate option : PASS") print(f"Explicit invalidate tiles: {EXPECTED_INVALIDATE_COUNT} PASS") print("Plan hash builder binding: PASS") print("OVERALL : PASS") if __name__ == "__main__": main()