系统自动升级说明(简化版)
适用范围:Windows / macOS / Linux 三平台分发包
定位:系统支持自动更新,采用"启动时检查 + 自动更新 + 自动重启一次"的轻量方案
一、背景
系统以压缩包形式分发给终端用户,用户双击各平台启动脚本(Windows start.bat / macOS start.command / Linux 启动脚本)即可使用。此前没有代码自动升级能力,仅支持数据库结构自动迁移。
用户侧约束(已确认):
- 用户是普通终端用户,不要求额外安装工具(分发包可自带所需工具)
- 二进制锁定(内置数据库 / 音视频处理工具 / 包管理工具不更新),仅升级 Python 代码
- 希望尽量简单,不要复杂的独立升级进程和状态机
二、核心思路
一句话:在主程序启动之前检查是否有新版本 → 有新版本则自动完成更新(启动脚本自动重启一次)→ 正常启动。
启动脚本
│
▼
[阶段 1] 检查更新(随启动脚本执行,无独立进程)
1. 检查更新开关与工具是否可用
2. 拉取远程版本信息,与本地版本比较
3. 检查二进制依赖是否齐全(缺失则跳过本次更新)
4. 有新版本 → 显示新旧版本号,自动完成更新(不保留本地修改)
5. 更新成功 → 返回特殊码,启动脚本自动重启一次
6. 无差异/跳过/失败 → 继续
│
▼
[阶段 2] 原有启动流程(不变)
检查运行环境 → 检查配置 → 检查数据库 → 启动主程序
为什么不需要独立升级进程?
- 更新动作在主程序启动之前完成
- 此时没有进程占用代码文件,更新可以正常覆盖
- 不需要"进程替换自己"的复杂处理
更新完成后的收尾(自动):
- 更新成功后检查脚本返回特殊码(10),启动脚本自动重启一次,随后进入原有启动流程;
- 依赖清单有变化时,启动流程会自动安装新依赖;
- 数据库迁移脚本随更新带入,启动时自动执行数据库结构迁移。
三、启动流程修改
各平台启动脚本在原有逻辑(检查运行环境等)之前插入"检查更新"一步:
- 检查脚本返回严重错误(码 2)时,暂停并提示用户;
- 更新完成(码 10)时,启动脚本自动重启一次,再继续原有启动流程;
- 其余情况(无需更新 / 跳过 / 失败)均继续原有启动流程。
三平台(Windows / macOS / Linux)修改方式一致。
四、检查更新脚本要点
4.0 二进制依赖检查
在执行更新前,脚本会先校验目标(新)版本所需的内置二进制依赖是否齐全:
- 依赖清单随版本一起发布(仓库内配置文件
config/required_binaries.yml,按平台声明所需文件与版本要求;更新时从远程分支读取目标版本的清单); - 若存在缺失的二进制文件:打印缺失清单及下载地址 → 跳过本次自动更新(不执行更新)→ 允许程序以当前版本继续启动。
设计意图:
- 每个版本的二进制依赖都记录在仓库中,随版本发布,不依赖外部接口;
- 支持多版本场景(某版本需要依赖 A,后续版本需要 A+B)。
4.1 更新流程关键逻辑(概述)
- 找工具:Windows 仅使用分发包内置的 git(固定路径);macOS/Linux 优先使用内置 git,找不到时回退到系统 git。完全找不到可用 git 则提示并跳过更新,正常启动。
- 读配置:更新开关、仓库地址(多源,按顺序尝试)、跟踪分支。
- 首次运行:本地无 git 仓库(
.git不存在)时自动初始化(多源依次尝试,任一成功即可)。 - 拉取远程:网络超时/失败 → 提示并跳过更新,使用本地版本继续启动。
- 比较差异:无差异 → 静默通过(提示"已是最新版本");有差异 → 显示新旧版本号。
- 自动更新:
- 不保留本地修改:更新通过 fetch +
git reset --hard强制覆盖(不对本地修改做暂存/恢复); - 用户模块目录由独立的路径契约保护:目标版本内容会写入用户模块目录、或用户模块与目标版本不兼容(ABI 校验)时,拒绝自动更新(或按配置隔离)并提示;
- 失败(fetch/reset 失败)→ 提示原因,回退使用本地版本继续启动。
- 收尾提示:依赖清单(requirements.txt)有变化时提示"依赖有更新,启动时将自动安装"。
脚本返回值约定:0 = 正常继续(无需更新/跳过等);1 = 更新失败但可继续(使用本地版本);2 = 严重错误(暂停并提示用户);10 = 代码已更新,启动脚本自动重启一次后继续启动。
五、关键设计点
| 设计点 | 处理 |
|---|---|
| 本地版本检测 | 读取 pyproject.toml 的版本号([project].version,唯一真源);git tag 仅用于发现远程版本 |
| 更新工具 | Windows 仅用分发包内置工具;macOS/Linux 优先内置、缺失时回退系统环境 |
| 本地无仓库 | 自动初始化(多源依次尝试) |
| 网络不通 | 超时 30 秒,失败跳过更新、继续启动 |
| 无差异 | 静默通过 |
| 有差异 | 显示新旧版本号,自动更新 |
| 更新失败 | 提示失败,使用本地版本继续启动 |
| 二进制依赖缺失 | 跳过更新,打印缺失清单,允许以当前版本启动 |
| 本地修改 | 不保留(更新直接覆盖);用户模块目录由路径契约保护,冲突/不兼容时拒绝更新 |
| 依赖变化 | 检测并提示,启动时自动安装 |
| 数据库迁移 | 更新后迁移脚本就位,启动时自动执行 |
六、用户数据保护
更新过程不会触碰用户数据(已按仓库忽略规则保护):
数据目录(数据库数据)
上传目录(用户文件)
日志目录
自定义配置文件
本地特殊文件
更新后这些文件保持不变。
如果用户修改了被跟踪的代码文件:
- 本地修改不做暂存,也不保留,更新时直接覆盖;
- 重要自定义内容建议提前备份,或放入用户模块目录(默认
data/user_modules); - 用户模块目录由独立的路径契约保护:该目录不被 Git 跟踪,若目标版本会写入该目录或用户模块与目标版本不兼容,自动更新会被拒绝(或按配置隔离)并提示。
七、分发包内置工具
- 各平台分发包均内置所需版本控制工具,用户无需自行安装;
- 找不到任何可用 git 工具时(Windows 未找到内置工具;macOS/Linux 内置与系统 git 均缺失),更新检查自动跳过(返回正常),不影响正常启动;
- 工具来源为公开开源渠道(如 Git for Windows 的 MinGit 最小化版本等),随分发包放置于固定目录。
八、配置项(概述)
配置文件中新增升级段(详见配置文件):
- 更新总开关(启动时检查并自动更新);
- 仓库地址(多源列表,按顺序尝试,第一个成功即停止,建议国内源在前);
- 跟踪分支;
- 拉取超时时间。
九、边界情况
| 场景 | 处理 |
|---|---|
| 找不到工具 / 本地无仓库且无网络 | 跳过检查,正常启动 |
| 拉取超时 / 失败(仓库不存在/权限不足) | 提示后跳过更新,使用本地版本 |
| 更新失败(fetch/reset 失败) | 提示失败,使用本地版本继续启动 |
| 二进制依赖缺失 | 跳过更新,打印缺失清单及下载地址,正常启动 |
| 用户模块与目标版本不兼容 | 拒绝自动更新并提示(或按配置隔离) |
| 用户修改了被跟踪文件 | 本地修改被覆盖且不保留,建议提前备份 |
| 中文路径 | 全程使用跨平台路径处理与 utf-8,无问题 |
| 更新后依赖变化 | 提示"依赖更新",启动时自动安装 |
| 更新后数据库迁移失败 | 启动失败并提示;建议大版本升级前用户先备份 |
| 并发启动 | 顺序执行,无并发状态,无问题 |
| 主程序未启动时文件占用 | 无问题(更新在主程序启动前完成) |
| 跨平台(Windows / macOS ARM/Intel / Linux) | 行为一致 |
十、发布流程(给维护者,概述)
- 开发完成、测试通过;
- 更新版本号;
- 提交代码并打版本标签后推送;
- 完成——用户下次启动时自动检测并提示更新。
(本简化方案不需要额外的发布清单、签名、上传对象存储、校验和计算等步骤。)
十一、测试计划
- 无网络环境:断网启动 → 提示"网络不可用" → 正常启动
- 无工具:移除内置工具 → 提示"未找到工具" → 正常启动
- 无差异:本地与远程一致 → 静默通过
- 有差异:推送新版本 → 启动时显示新旧版本号 → 自动完成更新并重启
- 更新失败:fetch/reset 失败 → 提示 → 正常启动本地版本
- 本地修改覆盖:修改被跟踪文件 → 更新 → 本地修改被覆盖(验证提示与数据保护范围)
- 依赖变化:更新后检测并提示依赖更新
- 跨平台:Windows / macOS / Linux 分别测试
- 中文路径:用户目录含中文 → 正常执行
- 首次运行无仓库:自动初始化后正常
十二、已知问题
| 问题 | 影响 | 解决方案 |
|---|---|---|
| Linux 启动脚本直接调用系统 Python 执行检查脚本 | 若系统缺少 YAML 解析库,配置解析回退到默认值 | Linux 推荐容器化部署(容器内已含依赖),或手动安装对应依赖 |
十三、风险提示
- 无分批发布:所有用户同时看到更新,发布前必须充分测试;
- 问题版本需再次发布修复:已更新到问题版本的用户只能通过下一次更新修复;
- 数据库迁移失败:更新后若迁移脚本有 bug,启动会失败。建议大版本升级前在更新说明中强提示用户备份;
- 用户本地修改:被跟踪文件的本地修改不保留、更新时直接覆盖,启动输出中已明确说明,建议用户提前备份重要自定义内容或放入用户模块目录。