Files
lyxy-document/build.py
lanyuanxiaoyao d860e17b2c feat: 添加 PyArmor 代码混淆支持
- 新增 --obfuscate 命令行参数,支持使用 PyArmor 混淆代码
- 通过 uv run --with pyarmor 按需加载 PyArmor,不污染主机环境
- 添加友好的错误提示,引导用户正确使用 --with pyarmor
- 保持非混淆模式完全向后兼容
- 更新 skill-packaging spec,新增混淆相关需求
2026-03-09 14:36:52 +08:00

248 lines
6.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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()