chore: initialize qiming workspace repository

This commit is contained in:
Codex
2026-05-29 14:22:48 +08:00
commit bfd67a0f2c
10750 changed files with 1885711 additions and 0 deletions

View File

@@ -0,0 +1,160 @@
# 应用自动更新
## 概述
基于 `electron-updater` 实现应用内自动更新,支持 macOS / Windows (NSIS) / Linux 自动下载安装Windows MSI 安装引导到下载页(优先 OSS 安装包直链)。
**强制使用 latest.json**:应用与 Release 流程均以 OSS 上的 `latest.json` 为**唯一数据源**。版本检查、各平台下载地址、签名与大小均来自该文件;不直接依赖 GitHub Release 或 electron-builder 生成的 yml 作为来源。
**更新源macOS / Linux 应用内升级)**:检查与下载均走阿里云 OSS。
- **检查更新****仅**拉取 OSS `latest/latest.json` 获取最新版本;其中 `platforms` 已包含各平台完整的 OSS 下载包地址url、signature、size。若有更新则将 electron-updater 的 feed 设为版本化 OSS 路径 `${OSS_BASE}/electron-v${version}`
- **下载安装包**:安装包地址**仅**来自 `latest.json``platforms`(已是完整 OSS URL。electron-updater 实际下载时从 feed 请求的 `latest-mac.yml` / `latest-linux.yml` 等,**必须**由同一份 `latest.json``platforms` 在 Release 流程中派生生成,不得使用 GitHub 自带的 yml。
- **失败处理**OSS 不可达或元数据异常时,直接报错并引导用户前往下载页,不回退到 GitHub。
## 核心文件
| 文件 | 职责 |
|------|------|
| `src/main/services/autoUpdater.ts` | 更新服务:检查、下载、安装、状态管理 |
| `src/renderer/components/pages/AboutPage.tsx` | 关于页面:手动检查更新 UI |
| `src/main/window/trayManager.ts` | 托盘菜单:检查更新入口 |
| `src/main/ipc/appHandlers.ts` | IPC 处理checkUpdate / downloadUpdate / installUpdate / openReleasesPage |
| `src/preload/index.ts` | preload 暴露 API |
| `src/shared/types/updateTypes.ts` | 类型定义UpdateState / UpdateInfo / UpdateProgress |
| `dev-app-update.yml` | 开发模式更新源配置 |
## 更新流程
### 1. 启动自动检查
```
App 启动 → 延迟 10s → checkForUpdates()
├── 无更新 → 静默,不提示
├── 有更新 → 检查 skipped version
│ │
│ ├── 已跳过 → 不弹窗
│ │
│ └── 未跳过 → 弹窗提示
│ │
│ ├── "立即更新" → 下载 → 二次确认 → 重启安装
│ │
│ └── "跳过此版本" → 记录到文件,下次不再提示
└── 失败 → 静默记录日志
```
### 2. 手动检查About 页面)
```
用户点击 "检查更新"
├── 检查中... (loading 状态)
├── 无更新 → 提示 "当前已是最新版本",重置按钮
├── 有更新 → 显示 "下载更新" 按钮
│ │
│ └── 点击 → 下载(显示进度条)→ 完成 → Modal 确认 → 重启安装
└── 错误 → 显示错误信息 + 重试按钮
```
### 3. 手动检查(托盘菜单)
与 About 页面类似,但使用原生 `dialog.showMessageBox` 弹窗交互。
## Windows 安装类型检测
通过检查 NSIS 卸载程序文件是否存在来区分安装方式:
```typescript
// NSIS 安装会生成: {appDir}/Uninstall {productName}.exe
// MSI 安装由 Windows Installer 管理,无此文件
function detectInstallerType(): InstallerType {
const nsisUninstaller = path.join(appDir, `Uninstall ${productName}.exe`);
return fs.existsSync(nsisUninstaller) ? 'nsis' : 'msi';
}
```
| 安装类型 | 自动更新 | 行为 |
|----------|---------|------|
| NSIS (.exe) | 支持 | 下载 → 重启安装 |
| MSI (.msi) | 不支持 | 引导到下载页(按 MSI 优先解析下载包) |
| macOS (.dmg) | 支持 | 下载 → 重启安装 |
| Linux (.AppImage/.deb) | 支持 | 下载 → 重启安装 |
## 跳过版本
- 用户在启动弹窗中选择"跳过此版本"后,版本号写入 `~/.qimingclaw/.skipped-update-version`
- 下次启动时,若远程最新版本与已跳过版本相同,不弹窗
- 出现更新的版本(高于已跳过版本)时,重新弹窗提示
- 手动检查更新About 页面 / 托盘菜单)不受跳过逻辑影响
## 安装前清理
`installUpdate()` 在调用 `quitAndInstall()` 之前,先执行 `cleanupAllProcesses()`
- 停止 Computer Server
- 销毁 Unified Agent Service
- 终止 Agent Runner / Lanproxy / File Server 进程
- 清理 MCP Proxy
- 停止所有 Engine 进程
## 开发模式
- 通过 `dev-app-update.yml` 配置更新源GitHub
- `forceDevUpdateConfig = true` 启用开发模式检查
- 允许检查更新(验证 API 通路)
- 下载和安装被阻止Squirrel.Mac bundle ID 不匹配会导致崩溃)
- `autoInstallOnAppQuit = false`
## OSS 单源策略(强制 latest.json
所有更新元数据**强制以 latest.json 为准**:应用端只读 OSS `latest/latest.json` 做版本判断与地址解析Release 流程只产出并上传由该文件派生的 yml禁止使用或上传 GitHub 自带的 yml。
| 步骤 | 策略 |
|--------------|------|
| 检查更新 | 仅 OSS latest.json |
| Feed 基地址 | 仅 OSS 版本化路径 |
| 下载安装包 | 仅 OSSyml 必须由 latest.json 的 platforms 派生) |
| OSS 异常 | 报错 + 引导下载页,不回退 GitHub |
**数据源规则**OSS 上**只**维护并信任 `latest.json`(含 version、notes、pub_date、platforms 多架构与完整 OSS 下载地址。Release 流程(`.github/workflows/release-electron.yml`**必须**根据该 `latest.json``platforms` 生成 electron-updater 所需的 `latest-mac.yml``latest-linux.yml``latest.yml`,并写入 release-assets 后一并上传;**禁止**使用或上传 GitHub 自带的 ymlyml 内下载地址为相对路径(相对 feed 基地址即 OSS 版本化路径)。
本地触发 OSS 同步时,可在本 crate 下执行 `scripts/sync-oss.sh <tag>`(如 `electron-v0.8.0`),会触发上述 workflow 并轮询直至完成;依赖 `gh``jq`
## 自定义更新源
通过环境变量 `QIMING_UPDATE_SERVER` 可指定自定义更新服务器:
```bash
QIMING_UPDATE_SERVER=http://localhost:8080/updates npm run dev
```
使用 `generic` provider适合本地测试。
## UpdateState 类型
```typescript
interface UpdateState {
status: 'idle' | 'checking' | 'available' | 'not-available' | 'downloading' | 'downloaded' | 'error';
version?: string;
progress?: { percent: number; bytesPerSecond: number; transferred: number; total: number };
error?: string;
canAutoUpdate?: boolean; // false 表示 MSI 安装,需手动下载
}
```
## IPC 通道
| 通道 | 方向 | 说明 |
|------|------|------|
| `app:checkUpdate` | renderer → main | 手动检查更新 |
| `app:downloadUpdate` | renderer → main | 下载更新 |
| `app:installUpdate` | renderer → main | 重启安装 |
| `app:getUpdateState` | renderer → main | 获取当前更新状态 |
| `app:openReleasesPage` | renderer → main | 打开下载页(从 latest.json 解析 OSS 安装包直链) |
| `update:status` | main → renderer | 推送更新状态变化 |