modify: build.py 脚本整理

This commit is contained in:
2026-04-26 17:12:35 +08:00
parent 7f7b2b03df
commit a9f7d678ad
2 changed files with 89 additions and 78 deletions
-1
View File
@@ -48,5 +48,4 @@ packages = ["src/chongming"]
[project.scripts] [project.scripts]
build = "chongming.scripts.build:build" build = "chongming.scripts.build:build"
serve = "chongming:serve" serve = "chongming:serve"
# gunicorn = "gunicorn chongming.app:app -k uvicorn.workers.UvicornWorker -w 4 -b 0.0.0.0:8000"
gunicorn = "chongming:gunicorn_serve" gunicorn = "chongming:gunicorn_serve"
+89 -77
View File
@@ -1,30 +1,32 @@
import platform # build.py - 构建脚本(重构后,逻辑保持不变)
from module_bank import PythonToSQLite import platform
from module_bank.encryption import Encryption
import os import os
from pathlib import Path
import shutil import shutil
import subprocess import subprocess
import sys import sys
import asyncio import asyncio
from pathlib import Path
from module_bank import PythonToSQLite
from module_bank.encryption import Encryption
from sqlite_vfs.core import SQLiteVFS from sqlite_vfs.core import SQLiteVFS
from sqlite_vfs.folder_packer import FolderPacker from sqlite_vfs.folder_packer import FolderPacker
is_windows = platform.system() == "Windows" is_windows = platform.system() == "Windows"
# ---------------------------------------------------------------------------
# 前端构建
# ---------------------------------------------------------------------------
def build_vue_web(): def build_vue_web():
"""打包 Vue Web""" """构建 Vue 前端并打包为 static.svfs"""
# 构建 Vue Web 命令
dist_dir = "./src/chongming-web/dist" dist_dir = "./src/chongming-web/dist"
output_db = "./build/static.svfs" output_db = "./build/static.svfs"
fs_name = "frontend" fs_name = "frontend"
cmd = [
"npm", # 1. 执行 npm run build
"run", cmd = ["npm", "run", "build"]
"build",
]
result = subprocess.run( result = subprocess.run(
cmd, cmd,
cwd="./src/chongming-web", cwd="./src/chongming-web",
@@ -43,19 +45,19 @@ def build_vue_web():
print("Vue Web 打包成功") print("Vue Web 打包成功")
if result.stdout: if result.stdout:
print(result.stdout) print(result.stdout)
# 检查 dist 目录是否存在 # 2. 检查 dist 目录
if not os.path.exists(dist_dir): if not os.path.exists(dist_dir):
print(f"错误: dist 目录不存在: {dist_dir}") print(f"错误: dist 目录不存在: {dist_dir}")
return return
# 3. 打包到 svfs
vfs = SQLiteVFS(output_db, compress=False) vfs = SQLiteVFS(output_db, compress=False)
packer = FolderPacker( packer = FolderPacker(
sqlite_vfs=vfs, sqlite_vfs=vfs,
exclude_patterns=[], exclude_patterns=[],
fs_name=fs_name, fs_name=fs_name,
) )
try: try:
packer.pack_folder(dist_dir) packer.pack_folder(dist_dir)
print(f"✅ 打包完成,文件保存为 {output_db}") print(f"✅ 打包完成,文件保存为 {output_db}")
@@ -63,8 +65,11 @@ def build_vue_web():
vfs.close() vfs.close()
# ---------------------------------------------------------------------------
# PyArmor 混淆
# ---------------------------------------------------------------------------
def run_pyarmor_obfuscate(): def run_pyarmor_obfuscate():
# pyarmor obfuscate --output=obfuscated_script.py original_script.py """对关键文件执行 PyArmor 混淆"""
obfuscate_files = [ obfuscate_files = [
("main.py", "./"), ("main.py", "./"),
("server.py", "./"), ("server.py", "./"),
@@ -88,10 +93,12 @@ def run_pyarmor_obfuscate():
print(f"打包文件: {file}") print(f"打包文件: {file}")
# ---------------------------------------------------------------------------
# PyInstaller 打包
# ---------------------------------------------------------------------------
def run_pyinstaller(): def run_pyinstaller():
"""运行 PyInstaller 打包成可执行文件""" """使用 PyInstaller 打包成可执行文件"""
try: try:
# 构建 PyInstaller 命令
if is_windows: if is_windows:
cmd = [ cmd = [
sys.executable, sys.executable,
@@ -107,7 +114,6 @@ def run_pyinstaller():
"--specpath", "--specpath",
"./specs", "./specs",
"--clean", "--clean",
# 隐藏导入
"--hidden-import", "--hidden-import",
"utils", "utils",
"--hidden-import", "--hidden-import",
@@ -150,7 +156,6 @@ def run_pyinstaller():
"sqlite_vfs.core", "sqlite_vfs.core",
"--hidden-import", "--hidden-import",
"jwt", "jwt",
# 其他选项
"--icon", "--icon",
"../../public/chongming.ico", "../../public/chongming.ico",
"main.py", "main.py",
@@ -170,7 +175,6 @@ def run_pyinstaller():
"--specpath", "--specpath",
"./specs", "./specs",
"--clean", "--clean",
# 隐藏导入
"--hidden-import", "--hidden-import",
"utils", "utils",
"--hidden-import", "--hidden-import",
@@ -213,14 +217,13 @@ def run_pyinstaller():
"sqlite_vfs.core", "sqlite_vfs.core",
"--hidden-import", "--hidden-import",
"jwt", "jwt",
# gunicorn # gunicorn 相关
"--hidden-import", "--hidden-import",
"gunicorn.glogging", "gunicorn.glogging",
"--hidden-import", "--hidden-import",
"gunicorn.app", "gunicorn.app",
"--hidden-import", "--hidden-import",
"gunicorn.app.wsgiapp", "gunicorn.app.wsgiapp",
# 其他选项
"--icon", "--icon",
"../../public/chongming.ico", "../../public/chongming.ico",
"main.py", "main.py",
@@ -242,11 +245,18 @@ def run_pyinstaller():
print(f"PyInstaller 执行出错: {e}") print(f"PyInstaller 执行出错: {e}")
# ---------------------------------------------------------------------------
# 数据库初始化
# ---------------------------------------------------------------------------
async def init_database(): async def init_database():
from sqlalchemy.ext.asyncio import create_async_engine """构建环境下的数据库初始化(创建表、管理员、权限)"""
from sqlalchemy.ext.asyncio import (
create_async_engine,
AsyncSession,
async_sessionmaker,
)
from sqlmodel import SQLModel, text from sqlmodel import SQLModel, text
from sqlalchemy.exc import OperationalError from sqlalchemy.exc import OperationalError
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
from ..app.core.config import get_config from ..app.core.config import get_config
from ..app.task.dev_init_db import init_permission, dev_init_admin from ..app.task.dev_init_db import init_permission, dev_init_admin
@@ -272,23 +282,27 @@ async def init_database():
await conn.run_sync( await conn.run_sync(
lambda conn: SQLModel.metadata.create_all(bind=conn, checkfirst=True) lambda conn: SQLModel.metadata.create_all(bind=conn, checkfirst=True)
) )
except OperationalError as e: except OperationalError as e:
if "already exists" in str(e): if "already exists" in str(e):
raise raise
# 添加管理员用户
async with async_session_maker() as session: async with async_session_maker() as session:
# await UserService.create_user(session, "admin", "admin", is_superuser=True) # type: ignore
await init_permission(session) await init_permission(session)
await dev_init_admin(session) await dev_init_admin(session)
# ---------------------------------------------------------------------------
# 构建主流程
# ---------------------------------------------------------------------------
async def main(): async def main():
# 打包模块 # ========================================================
# 1. 生成密钥并创建 resources 目录
# ========================================================
key = Encryption.generate_key() key = Encryption.generate_key()
resources_path = Path("resources") resources_path = Path("resources")
if not resources_path.exists(): resources_path.mkdir(exist_ok=True)
resources_path.mkdir()
# 2. 打包 Python 模块到 .mbank
app_packer = PythonToSQLite("./resources/app.mbank", key) app_packer = PythonToSQLite("./resources/app.mbank", key)
app_packer.pack_directory("src/chongming/app", "app") app_packer.pack_directory("src/chongming/app", "app")
app_packer.verify_package_structure() app_packer.verify_package_structure()
@@ -299,72 +313,70 @@ async def main():
plugin_packer.verify_package_structure() plugin_packer.verify_package_structure()
plugin_packer.delete_source_code(None) plugin_packer.delete_source_code(None)
# 创建 build 目录 # 3. 准备 build 目录
build_path = Path("build") if Path("build").exists() and Path("build").is_dir():
if build_path.exists() and build_path.is_dir():
shutil.rmtree("build") shutil.rmtree("build")
os.makedirs("build", exist_ok=True) os.makedirs("build", exist_ok=True)
# 复制 utils 文件夹到 build 文件夹 # 复制 resources 到 build
utils_path = Path("utils") shutil.copytree(resources_path, Path("build") / "resources", dirs_exist_ok=True)
if utils_path.exists(): print(f"已复制 resources 文件夹到 build/resources")
target_utils_path = Path("build") / "utils"
shutil.copytree(utils_path, target_utils_path, dirs_exist_ok=True)
print(f"已复制 utils 文件夹到 {target_utils_path}")
# 复制 resources 文件夹到 build 文件夹 # 复制 public 中的文件到 build
resources_path = Path("resources") shutil.copy("public/main.py", "build")
if resources_path.exists(): shutil.copy("public/server.py", "build")
target_resources_path = Path("build") / "resources" shutil.copy("public/config.toml", "build")
shutil.copytree(resources_path, target_resources_path, dirs_exist_ok=True) # 复制 utils(先复制 public/utils,后续会处理 launch.py 中的 key)
print(f"已复制 resources 文件夹到 {target_resources_path}") utils_source = Path("public/utils")
if utils_source.exists():
shutil.copytree(utils_source, Path("build") / "utils", dirs_exist_ok=True)
print(f"已复制 utils 文件夹到 build/utils")
print("模块打包完成") print("模块打包完成")
# 复制 main.py 到 build 文件夹 # 4. 替换密钥
shutil.copy("public/main.py", "build") launch_file = Path("./build/utils/launch.py")
shutil.copy("public/server.py", "build") launch_content = launch_file.read_text(encoding="utf-8")
# 复制 config.toml 到 build 文件夹 launch_content = launch_content.replace("key = None", f"key = '{key}'")
shutil.copy("public/config.toml", "build") launch_file.write_text(launch_content, encoding="utf-8")
# 复制 utils 文件夹到 build 文件夹
shutil.copytree("public/utils", "build/utils")
with open("./build/utils/launch.py", "r", encoding="utf-8") as f:
context = f.read()
context = context.replace("key = None", f"key = '{key}'")
with open("./build/utils/launch.py", "w", encoding="utf-8") as f:
f.write(context)
jwt_key = Encryption.generate_key() jwt_key = Encryption.generate_key()
with open("./build/config.toml", "r", encoding="utf-8") as f: config_file = Path("./build/config.toml")
context = f.read() config_content = config_file.read_text(encoding="utf-8")
context = context.replace("your-secret-key-change-in-production", jwt_key) config_content = config_content.replace(
with open("build/config.toml", "w", encoding="utf-8") as f: "your-secret-key-change-in-production", jwt_key
f.write(context) )
config_file.write_text(config_content, encoding="utf-8")
# 混淆 # 5. 混淆
run_pyarmor_obfuscate() run_pyarmor_obfuscate()
# 运行 PyInstaller # 6. PyInstaller 打包
run_pyinstaller() run_pyinstaller()
# 清理临时文件 # 7. 清理临时文件
shutil.rmtree(r"build/build_temp", ignore_errors=True) for temp_dir in [
shutil.rmtree(r"build/specs", ignore_errors=True) "build/build_temp",
shutil.rmtree(r"build/utils", ignore_errors=True) "build/specs",
shutil.rmtree(r"build/pyarmor_runtime_000000", ignore_errors=True) "build/utils",
os.remove(r"build/main.py") "build/pyarmor_runtime_000000",
os.remove(r"build/server.py") ]:
shutil.rmtree(temp_dir, ignore_errors=True)
for temp_file in ["build/main.py", "build/server.py"]:
try:
os.remove(temp_file)
except FileNotFoundError:
pass
print("构建完成") print("构建完成")
# 初始化数据库 # 8. 初始化数据库(构建输出目录下的 database.db)
await init_database() await init_database()
# 构建 vue 项目 # 9. 构建前端并生成 static.svfs
build_vue_web() build_vue_web()
def build(): def build():
"""构建入口"""
asyncio.run(main()) asyncio.run(main())