- 新增 --obfuscate 命令行参数,支持使用 PyArmor 混淆代码 - 通过 uv run --with pyarmor 按需加载 PyArmor,不污染主机环境 - 添加友好的错误提示,引导用户正确使用 --with pyarmor - 保持非混淆模式完全向后兼容 - 更新 skill-packaging spec,新增混淆相关需求
248 lines
6.8 KiB
Python
248 lines
6.8 KiB
Python
#!/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()
|