modify: 0.3.4

This commit is contained in:
2026-10-13 11:04:18 +08:00
parent ec5ce1b617
commit cd841f4f2d
4 changed files with 88 additions and 12 deletions
+4
View File
@@ -36,6 +36,10 @@ uv run gen_models workers/myplugin/config/workers/myplugin workers/myplugin/work
- `--encrypt`:打开加密与混淆 —— `.mbank` 加密存字节码并写 `.key` 密钥文件、做 PyArmor 混淆 - `--encrypt`:打开加密与混淆 —— `.mbank` 加密存字节码并写 `.key` 密钥文件、做 PyArmor 混淆
- `--license`:打开授权 —— 注入厂商授权公钥(缺公钥则构建失败)、跑授权私钥泄漏守卫 - `--license`:打开授权 —— 注入厂商授权公钥(缺公钥则构建失败)、跑授权私钥泄漏守卫
打包收集清单分两处:网关本体依赖在根 `config.toml` `[pyinstaller_build_import]`;
内置插件专属依赖(网关本体不 import、只在插件里用)在根目录 `plugins_deps.toml`
(按插件分节,可选文件),`pack` 时自动并入 `--hidden-import` / `--include-module`。
## 项目根目录定位 ## 项目根目录定位
脚本需要在项目根目录下读写 `config/`、`src/plugins/`、`workers/` 等路径。 脚本需要在项目根目录下读写 `config/`、`src/plugins/`、`workers/` 等路径。
+1 -1
View File
@@ -4,7 +4,7 @@ requires = ["hatchling"]
[project] [project]
name = "chongming-cli" name = "chongming-cli"
version = "0.3.3" version = "0.3.4"
description = "chongming 命令脚本集合:构建打包 / 进程插件打包 / 插件脚手架 / 模型生成 / 机器码授权签发" description = "chongming 命令脚本集合:构建打包 / 进程插件打包 / 插件脚手架 / 模型生成 / 机器码授权签发"
readme = "README.md" readme = "README.md"
requires-python = ">=3.12" requires-python = ">=3.12"
+82 -10
View File
@@ -35,6 +35,13 @@
uv run build nuitka --only mbank,prepare uv run build nuitka --only mbank,prepare
uv run build --encrypt --license # 对外分发:加密模块 + 注入授权公钥 uv run build --encrypt --license # 对外分发:加密模块 + 注入授权公钥
uv run build # 内部调试:明文模块、不带授权 uv run build # 内部调试:明文模块、不带授权
依赖收集(--only pack 生效):
打包收集清单 = 根 config.toml [pyinstaller_build_import](网关本体依赖)
+ 根目录 plugins_deps.toml(内置插件专属依赖 —— 网关本体不 import、只在
mbank 内 import 的包,按插件分节声明;文件可选)。
mbank 里的 import 静态分析看不到,两条清单都会被展开为 --hidden-import /
--include-module,缺一个都会在运行期加载插件时才 ImportError。
""" """
import os import os
@@ -45,6 +52,7 @@ import subprocess
import sys import sys
import asyncio import asyncio
import time import time
import tomllib
from dataclasses import dataclass, field from dataclasses import dataclass, field
from pathlib import Path from pathlib import Path
@@ -54,6 +62,8 @@ from sqlite_vfs.core import SQLiteVFS
from sqlite_vfs.folder_packer import FolderPacker from sqlite_vfs.folder_packer import FolderPacker
from chongming_config import get_config, Config from chongming_config import get_config, Config
from ._root import find_project_root
# ── 常量 ──────────────────────────────────────────────────────────────────── # ── 常量 ────────────────────────────────────────────────────────────────────
PYINSTALLER = "pyinstaller" PYINSTALLER = "pyinstaller"
@@ -65,6 +75,12 @@ STEPS = ("mbank", "prepare", "obfuscate", "pack", "clean", "license_guard", "vue
is_windows = platform.system() == "Windows" is_windows = platform.system() == "Windows"
# 插件专属依赖清单(项目根,可选文件):内置插件(src/plugins/*)专属、网关本体
# (src/chongming/)不 import 的第三方依赖,按插件分节声明。放在插件自己的清单里
# 而不是 config.toml [pyinstaller_build_import],避免插件依赖混进网关构建配置:
# 插件下线/改 external 进程插件时删掉对应节即可,网关配置不受影响。
PLUGIN_DEPS_FILE = "plugins_deps.toml"
# ── 构建结果数据结构 ───────────────────────────────────────────────────────── # ── 构建结果数据结构 ─────────────────────────────────────────────────────────
@@ -517,16 +533,73 @@ def _stage_pyarmor_obfuscate() -> str:
return "✅ 所有文件混淆成功" return "✅ 所有文件混淆成功"
# ── 打包收集清单(网关本体依赖 + 插件专属依赖) ─────────────────────────────
def _load_plugin_imports() -> tuple[list[str], str]:
"""读取插件专属依赖清单(项目根 plugins_deps.toml,可选),返回 (模块列表, 摘要)。
文件结构(节名 = src/plugins/ 下的插件名):
imports = ["httpx"] # 多个插件共用的插件专属依赖
[doubao]
imports = ["openai", ...] # 单插件专属
文件不存在 = 无插件专属依赖(其他项目复用本 CLI 时不受影响),返回 ([], "")。
配置写错(imports 不是字符串列表、顶层出现非表非 imports 的键)直接抛错:
漏收依赖打包期不报错、运行期加载插件才 ImportError,静默放过的排查成本高得多。
"""
path = find_project_root() / PLUGIN_DEPS_FILE
if not path.exists():
return [], ""
with open(path, "rb") as f:
data = tomllib.load(f)
modules: list[str] = []
notes: list[str] = []
def collect(section: str, entry) -> None:
if not isinstance(entry, list) or not all(isinstance(m, str) for m in entry):
raise ValueError(
f"{PLUGIN_DEPS_FILE} 的 [{section}] imports 必须是字符串列表"
)
modules.extend(entry)
if entry:
notes.append(f"{section}:{len(entry)}")
for name, value in data.items():
if name == "imports":
collect("共用", value)
elif isinstance(value, dict):
collect(name, value.get("imports"))
else:
raise ValueError(
f"{PLUGIN_DEPS_FILE} 顶层 [{name}] 只能是共用 imports 列表或 [插件名] 表"
)
return modules, "、".join(notes)
def _collect_build_imports(config: Config) -> tuple[list[str], str]:
"""打包收集清单 = 网关本体依赖(config.toml)+ 插件专属依赖(plugins_deps.toml)。
去重且保持顺序(网关本体在前);摘要空串 = 未声明插件专属依赖。
"""
base = config.get("pyinstaller_build_import", {})
items = list(base.get("base", []))
items.extend(base.get("windows", []) if is_windows else base.get("linux", []))
plugin_items, summary = _load_plugin_imports()
return list(dict.fromkeys([*items, *plugin_items])), summary
# ── 阶段 4: PyInstaller 打包 ──────────────────────────────────────────────── # ── 阶段 4: PyInstaller 打包 ────────────────────────────────────────────────
def _stage_pyinstaller(config: Config) -> tuple[str, str]: def _stage_pyinstaller(config: Config) -> tuple[str, str]:
"""执行 PyInstaller 打包,返回 (状态消息, 可执行文件路径)""" """执行 PyInstaller 打包,返回 (状态消息, 可执行文件路径)"""
base_imports = config.get("pyinstaller_build_import", {}) build_imports, plugin_summary = _collect_build_imports(config)
build_imports = list(base_imports.get("base", [])) if plugin_summary:
build_imports.extend( print(f" 📎 并入插件专属依赖 {PLUGIN_DEPS_FILE}({plugin_summary})")
base_imports.get("windows", []) if is_windows else base_imports.get("linux", [])
)
icon = Path("../../public/chongming.ico") icon = Path("../../public/chongming.ico")
cmd = [ cmd = [
@@ -581,10 +654,9 @@ def _stage_pyinstaller(config: Config) -> tuple[str, str]:
def _stage_nuitka(config: Config) -> tuple[str, str]: def _stage_nuitka(config: Config) -> tuple[str, str]:
"""执行 Nuitka 打包,返回 (状态消息, 可执行文件路径)""" """执行 Nuitka 打包,返回 (状态消息, 可执行文件路径)"""
base_imports = config.get("pyinstaller_build_import", {}).get("base", []) build_imports, plugin_summary = _collect_build_imports(config)
extra = config.get("pyinstaller_build_import", {}).get( if plugin_summary:
"windows" if is_windows else "linux", [] print(f" 📎 并入插件专属依赖 {PLUGIN_DEPS_FILE}({plugin_summary})")
)
cmd = [ cmd = [
sys.executable, sys.executable,
@@ -609,7 +681,7 @@ def _stage_nuitka(config: Config) -> tuple[str, str]:
if shutil.which("upx"): if shutil.which("upx"):
cmd.append("--enable-plugin=upx") cmd.append("--enable-plugin=upx")
for mod in list(base_imports) + extra: for mod in build_imports:
cmd.append(f"--include-module={mod}") cmd.append(f"--include-module={mod}")
if is_windows: if is_windows:
Generated
+1 -1
View File
@@ -238,7 +238,7 @@ wheels = [
[[package]] [[package]]
name = "chongming-cli" name = "chongming-cli"
version = "0.3.2" version = "0.3.4"
source = { editable = "." } source = { editable = "." }
dependencies = [ dependencies = [
{ name = "chongming-config" }, { name = "chongming-config" },