#!/usr/bin/env python3 """Shared ship-id and YAML helpers. Zero third-party deps.""" from __future__ import annotations import re import sys from pathlib import Path from typing import Any _REPO_ROOT = Path(__file__).resolve().parents[3] if str(_REPO_ROOT) not in sys.path: sys.path.insert(0, str(_REPO_ROOT)) from pouch.yaml_io import safe_dump, safe_load # noqa: E402 SHIP_ID_RE = re.compile(r"^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$") DATE_ID_RE = re.compile(r"^\d{4}-\d{2}-\d{2}$|^\d{8}$") KIND = frozenset({"short", "long"}) SHIP_STATUS = frozenset( { "draft", "spec_review", "approved", "in_progress", "blocked", "done", "parked", "superseded", } ) TASK_STATUS = frozenset( {"open", "in_progress", "blocked", "verified", "merged", "done"} ) DELIVERY_STATE = frozenset( { "change_ready", "verified", "pr_opened", "merged", "regression_passed", "artifact_built", "published", "deployed", } ) SOURCE_KIND = frozenset({"user", "hunt", "ack", "pr", "feishu"}) COMMIT_ROLE = frozenset({"baseline", "merged", "head"}) def skill_dir() -> Path: return Path(__file__).resolve().parents[1] def load_yaml(path: Path) -> Any: return safe_load(path.read_text(encoding="utf-8")) def dump_yaml(path: Path, data: Any) -> None: path.write_text(safe_dump(data), encoding="utf-8") def check_ship_id(ship_id: str) -> str | None: if not ship_id: return "shipId 为空" if len(ship_id) > 64: return f"shipId 超过 64 字符: {ship_id}" if DATE_ID_RE.fullmatch(ship_id): return f"shipId 不能是日期: {ship_id}" if not SHIP_ID_RE.fullmatch(ship_id): return ( f"shipId 无效: {ship_id!r}(小写字母开头,仅小写字母、数字、连字符)" ) return None