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 混淆
- `--license`:打开授权 —— 注入厂商授权公钥(缺公钥则构建失败)、跑授权私钥泄漏守卫
打包收集清单分两处:网关本体依赖在根 `config.toml` `[pyinstaller_build_import]`;
内置插件专属依赖(网关本体不 import、只在插件里用)在根目录 `plugins_deps.toml`
(按插件分节,可选文件),`pack` 时自动并入 `--hidden-import` / `--include-module`。
## 项目根目录定位
脚本需要在项目根目录下读写 `config/`、`src/plugins/`、`workers/` 等路径。
+1 -1
View File
@@ -4,7 +4,7 @@ requires = ["hatchling"]
[project]
name = "chongming-cli"
version = "0.3.3"
version = "0.3.4"
description = "chongming 命令脚本集合:构建打包 / 进程插件打包 / 插件脚手架 / 模型生成 / 机器码授权签发"
readme = "README.md"
requires-python = ">=3.12"
+82 -10
View File
@@ -35,6 +35,13 @@
uv run build nuitka --only mbank,prepare
uv run build --encrypt --license # 对外分发:加密模块 + 注入授权公钥
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
@@ -45,6 +52,7 @@ import subprocess
import sys
import asyncio
import time
import tomllib
from dataclasses import dataclass, field
from pathlib import Path
@@ -54,6 +62,8 @@ from sqlite_vfs.core import SQLiteVFS
from sqlite_vfs.folder_packer import FolderPacker
from chongming_config import get_config, Config
from ._root import find_project_root
# ── 常量 ────────────────────────────────────────────────────────────────────
PYINSTALLER = "pyinstaller"
@@ -65,6 +75,12 @@ STEPS = ("mbank", "prepare", "obfuscate", "pack", "clean", "license_guard", "vue
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 "✅ 所有文件混淆成功"
# ── 打包收集清单(网关本体依赖 + 插件专属依赖) ─────────────────────────────
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 打包 ────────────────────────────────────────────────
def _stage_pyinstaller(config: Config) -> tuple[str, str]:
"""执行 PyInstaller 打包,返回 (状态消息, 可执行文件路径)"""
base_imports = config.get("pyinstaller_build_import", {})
build_imports = list(base_imports.get("base", []))
build_imports.extend(
base_imports.get("windows", []) if is_windows else base_imports.get("linux", [])
)
build_imports, plugin_summary = _collect_build_imports(config)
if plugin_summary:
print(f" 📎 并入插件专属依赖 {PLUGIN_DEPS_FILE}({plugin_summary})")
icon = Path("../../public/chongming.ico")
cmd = [
@@ -581,10 +654,9 @@ def _stage_pyinstaller(config: Config) -> tuple[str, str]:
def _stage_nuitka(config: Config) -> tuple[str, str]:
"""执行 Nuitka 打包,返回 (状态消息, 可执行文件路径)"""
base_imports = config.get("pyinstaller_build_import", {}).get("base", [])
extra = config.get("pyinstaller_build_import", {}).get(
"windows" if is_windows else "linux", []
)
build_imports, plugin_summary = _collect_build_imports(config)
if plugin_summary:
print(f" 📎 并入插件专属依赖 {PLUGIN_DEPS_FILE}({plugin_summary})")
cmd = [
sys.executable,
@@ -609,7 +681,7 @@ def _stage_nuitka(config: Config) -> tuple[str, str]:
if shutil.which("upx"):
cmd.append("--enable-plugin=upx")
for mod in list(base_imports) + extra:
for mod in build_imports:
cmd.append(f"--include-module={mod}")
if is_windows:
Generated
+1 -1
View File
@@ -238,7 +238,7 @@ wheels = [
[[package]]
name = "chongming-cli"
version = "0.3.2"
version = "0.3.4"
source = { editable = "." }
dependencies = [
{ name = "chongming-config" },