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