#!/usr/bin/env python3 """ Skill 发布脚本 使用方式: uv run python publish.py """ import os import sys import shutil import subprocess import tempfile TARGET_REPO_URL = "https://github.com/lanyuanxiaoyao/skills.git" TARGET_PATH = "skills/lyxy-document-reader" def check_build_dir(build_dir: str) -> None: """ 检查 build/ 目录是否存在 Args: build_dir: build 目录路径 Raises: SystemExit: 目录不存在时退出 """ if not os.path.exists(build_dir): print("错误: build/ 目录不存在") print("请先运行 build.py:") print(" uv run python build.py") sys.exit(1) def check_build_skill_md(build_skill_md_path: str) -> None: """ 检查 build/SKILL.md 是否存在 Args: build_skill_md_path: build/SKILL.md 路径 Raises: SystemExit: 文件不存在时退出 """ if not os.path.exists(build_skill_md_path): print("错误: build/SKILL.md 不存在") print("请先运行 build.py:") print(" uv run python build.py") sys.exit(1) def parse_version_from_skill_md(skill_md_path: str) -> str: """ 从 SKILL.md 解析出版本号 Args: skill_md_path: SKILL.md 路径 Returns: 版本号字符串 Raises: SystemExit: 解析失败时退出 """ with open(skill_md_path, "r", encoding="utf-8") as f: content = f.read() # 简单解析 YAML frontmatter 中的 version lines = content.split("\n") in_frontmatter = False in_metadata = False for line in lines: stripped = line.strip() if stripped == "---": if not in_frontmatter: in_frontmatter = True else: break continue if in_frontmatter: if stripped == "metadata:": in_metadata = True elif in_metadata and stripped.startswith("version:"): # 提取版本号,去掉引号 version_part = stripped.split(":", 1)[1].strip() version = version_part.strip('"').strip("'") return version elif in_metadata and stripped and not stripped.startswith(" "): # metadata 块结束 in_metadata = False print("错误: 无法从 build/SKILL.md 解析版本号") print("请检查 build/SKILL.md 是否包含 metadata.version 字段") sys.exit(1) def run_git_command(repo_dir: str, args: list[str]) -> subprocess.CompletedProcess: """ 在指定目录运行 git 命令 Args: repo_dir: 仓库目录 args: git 命令参数列表 Returns: subprocess.CompletedProcess Raises: subprocess.CalledProcessError: 命令失败时 """ cmd = ["git"] + args return subprocess.run( cmd, cwd=repo_dir, check=True, capture_output=True, text=True ) def clone_repo(temp_dir: str) -> str: """ 在临时目录 clone 目标仓库 Args: temp_dir: 临时目录路径 Returns: 仓库目录路径 Raises: SystemExit: clone 失败时退出 """ repo_dir = os.path.join(temp_dir, "skills-repo") try: run_git_command(temp_dir, ["clone", "--depth", "1", TARGET_REPO_URL, "skills-repo"]) except subprocess.CalledProcessError as e: print(f"错误: Clone 仓库失败") print(f" 返回码: {e.returncode}") print(f" 标准输出: {e.stdout}") print(f" 错误输出: {e.stderr}") sys.exit(1) return repo_dir def clear_target_dir(repo_dir: str) -> str: """ 清空目标路径目录 Args: repo_dir: 仓库目录 Returns: 目标目录路径 """ target_dir = os.path.join(repo_dir, TARGET_PATH) if os.path.exists(target_dir): shutil.rmtree(target_dir) os.makedirs(target_dir, exist_ok=True) return target_dir def copy_build_contents(build_dir: str, target_dir: str) -> None: """ 复制 build/ 内容到目标目录 Args: build_dir: build 源目录 target_dir: 目标目录 """ for item in os.listdir(build_dir): src = os.path.join(build_dir, item) dst = os.path.join(target_dir, item) if os.path.isdir(src): shutil.copytree(src, dst) else: shutil.copy2(src, dst) def git_commit_and_push(repo_dir: str, version: str) -> None: """ 执行 git add / commit / push Args: repo_dir: 仓库目录 version: 版本号 Raises: SystemExit: git 操作失败时退出 """ commit_message = f"publish: lyxy-document-reader {version}" try: run_git_command(repo_dir, ["add", "."]) run_git_command(repo_dir, ["commit", "-m", commit_message]) run_git_command(repo_dir, ["push"]) except subprocess.CalledProcessError as e: print(f"错误: Git 操作失败") print(f" 返回码: {e.returncode}") print(f" 标准输出: {e.stdout}") print(f" 错误输出: {e.stderr}") sys.exit(1) def main() -> None: """ 主函数:执行完整的发布流程 """ # 路径配置 project_root = os.path.dirname(os.path.abspath(__file__)) build_dir = os.path.join(project_root, "build") build_skill_md_path = os.path.join(build_dir, "SKILL.md") # 检查 build/ 目录 check_build_dir(build_dir) check_build_skill_md(build_skill_md_path) # 解析版本号 version = parse_version_from_skill_md(build_skill_md_path) # 使用临时目录 with tempfile.TemporaryDirectory(prefix="lyxy-publish-") as temp_dir: # Clone 仓库 repo_dir = clone_repo(temp_dir) # 清空目标路径 target_dir = clear_target_dir(repo_dir) # 复制内容 copy_build_contents(build_dir, target_dir) # Git 提交并推送 git_commit_and_push(repo_dir, version) if __name__ == "__main__": main()