#!/usr/bin/env python3 """ Skill 打包构建脚本 使用方式: # 开发模式 - 快速构建,不混淆 uv run python build.py # 发布模式 - 完整构建,PyArmor 混淆 uv run --with pyarmor python build.py --obfuscate """ import os import sys import shutil import subprocess import argparse from datetime import datetime def generate_timestamp() -> str: """ 生成 YYYYMMDD_HHMMSS 格式的时间戳 Returns: 时间戳字符串 """ return datetime.now().strftime("%Y%m%d_%H%M%S") def clean_and_create_build_dir(build_dir: str) -> None: """ 删除旧 build 目录并创建新的空目录 Args: build_dir: 构建目录路径 """ if os.path.exists(build_dir): print(f"清理旧构建目录: {build_dir}") shutil.rmtree(build_dir) os.makedirs(build_dir) print(f"创建构建目录: {build_dir}") def copy_skill_md(source_path: str, target_dir: str) -> None: """ 复制 skill/SKILL.md 到 build/SKILL.md Args: source_path: 源 SKILL.md 路径 target_dir: 目标目录 """ target_path = os.path.join(target_dir, "SKILL.md") shutil.copy2(source_path, target_path) print(f"复制: {source_path} -> {target_path}") def copy_scripts_dir(source_dir: str, target_dir: str) -> int: """ 递归复制 scripts/ 目录,仅复制 .py 文件 Args: source_dir: 源目录 target_dir: 目标目录 Returns: 复制的文件数量 """ file_count = 0 for root, dirs, files in os.walk(source_dir): # 计算相对路径 rel_path = os.path.relpath(root, source_dir) # 处理相对路径为 "." 的情况 if rel_path == ".": target_root = target_dir else: target_root = os.path.join(target_dir, rel_path) # 检查此目录下是否有 .py 文件需要复制 has_py_files = any(file.endswith(".py") for file in files) # 只有当有 .py 文件需要复制时才创建目录并复制 if has_py_files: if not os.path.exists(target_root): os.makedirs(target_root) # 只复制 .py 文件 for file in files: if file.endswith(".py"): source_file = os.path.join(root, file) target_file = os.path.join(target_root, file) shutil.copy2(source_file, target_file) file_count += 1 print(f"复制: {source_file} -> {target_file}") return file_count def obfuscate_scripts_dir(source_dir: str, target_dir: str) -> None: """ 使用 PyArmor 混淆 scripts 目录 Args: source_dir: 源代码目录 (scripts/) target_dir: 目标构建目录 (build/) """ # 检查 pyarmor 是否可用 try: __import__("pyarmor") except ImportError: print(""" ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 错误: PyArmor 未安装 请使用以下命令启用混淆: uv run --with pyarmor python build.py --obfuscate ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ """) sys.exit(1) # 临时目录 temp_dir = os.path.join(target_dir, "temp_pyarmor") # 清理已存在的临时目录 if os.path.exists(temp_dir): shutil.rmtree(temp_dir) # PyArmor 命令 (Normal Mode) cmd = [ "pyarmor", "gen", "--recursive", "-O", temp_dir, source_dir ] print(f" 执行: {' '.join(cmd)}") try: result = subprocess.run( cmd, check=True, capture_output=True, text=True ) except subprocess.CalledProcessError as e: print(f"\nPyArmor 混淆失败:") print(f" 返回码: {e.returncode}") print(f" 标准输出: {e.stdout}") print(f" 错误输出: {e.stderr}") sys.exit(1) # 移动混淆后的文件到最终位置 for item in os.listdir(temp_dir): src = os.path.join(temp_dir, item) dst = os.path.join(target_dir, item) if os.path.exists(dst): if os.path.isdir(dst): shutil.rmtree(dst) else: os.remove(dst) shutil.move(src, dst) # 清理临时目录 os.rmdir(temp_dir) print(" 混淆完成") def main() -> None: """ 主函数:执行完整的打包流程 """ parser = argparse.ArgumentParser( description="Skill 打包构建", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" 使用示例: # 开发模式 - 快速构建,不混淆 uv run python build.py # 发布模式 - 完整构建,PyArmor 混淆 uv run --with pyarmor python build.py --obfuscate """ ) parser.add_argument( "--obfuscate", action="store_true", help="使用 PyArmor 混淆代码 (需: uv run --with pyarmor)" ) args = parser.parse_args() print("=" * 60) print("Skill 打包构建") print("=" * 60) # 路径配置 project_root = os.path.dirname(os.path.abspath(__file__)) skill_md_path = os.path.join(project_root, "SKILL.md") scripts_source_dir = os.path.join(project_root, "scripts") build_dir = os.path.join(project_root, "build") # 生成时间戳 version = generate_timestamp() print(f"版本号: {version}") print() # 清理并创建 build 目录 clean_and_create_build_dir(build_dir) print() # 复制 SKILL.md copy_skill_md(skill_md_path, build_dir) print() # 根据 --obfuscate 选择执行路径 if args.obfuscate: print("────────────────────────────────────────") print(" 使用 PyArmor 混淆代码 (Normal Mode)") print("────────────────────────────────────────") obfuscate_scripts_dir(scripts_source_dir, build_dir) file_count = None else: scripts_target_dir = os.path.join(build_dir, "scripts") print("复制 scripts/ 目录(仅 .py 文件):") file_count = copy_scripts_dir(scripts_source_dir, scripts_target_dir) print() # 完成信息 print("=" * 60) print("构建完成!") print(f"版本号: {version}") if file_count is not None: print(f"复制文件数: {file_count}") else: print("混淆模式: 已生成 .pyx 和 pyarmor_runtime") print(f"输出目录: {build_dir}") print("=" * 60) if __name__ == "__main__": main()