modify: build.py 脚本整理
This commit is contained in:
@@ -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"
|
||||||
|
|||||||
@@ -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",
|
||||||
@@ -44,18 +46,18 @@ def build_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())
|
||||||
|
|||||||
Reference in New Issue
Block a user