docs(spec): 多平台打包设计 (macOS + Linux + Windows 同时 GH Actions 发布)
- 构建策略: GH Actions + Docker 本地 - macOS: 不签名 / 跳过公证 - Linux: deb + AppImage + rpm - Release 触发: tag 推送 + 4 处版本一致性 - update checker: 按 OS + 扩展名自动选 asset 完整内容: docs/superpowers/specs/2026-07-29-multi-platform-packaging-design.md
This commit is contained in:
1 parent
084883a54a
commit
00322fb503
1 file changed
+248
@@ -0,0 +1,248 @@
|
|||||||
|
# Kimi Switch 多平台打包 — 设计
|
||||||
|
|
||||||
|
**日期**:2026-07-29
|
||||||
|
**状态**:待用户审查
|
||||||
|
**关联 issue / 任务**:新增 macOS + Linux 打包支持(gh Actions 为主 + Docker 本地调 Linux)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 背景与目标
|
||||||
|
|
||||||
|
Kimi Switch 是 Tauri 2.x 桌面配置管理器,目前仅支持 Windows MSI(`tauri.conf.json` 中 `bundle.targets: ["msi"]`)。本次改动让同一份代码可同时构建 Windows / macOS / Linux 三平台,整理出能在 GitHub Releases 上统一发布的工作流。
|
||||||
|
|
||||||
|
成功标准:
|
||||||
|
- `git tag v0.7.0 && git push origin v0.7.0` → GitHub Actions 自动产出 6 个资产(`.msi` + `.dmg` + `.app` + `.deb` + `.AppImage` + `.rpm`),全部挂到 v0.7.0 Release
|
||||||
|
- 开发者本机 Windows + Docker 可本地调 Linux 三种格式
|
||||||
|
- 应用内"检查更新"按 OS 自动选对应 asset 下载
|
||||||
|
- 不回归现有 Windows 用户的体验
|
||||||
|
|
||||||
|
## 2. 决策(已与用户确认)
|
||||||
|
|
||||||
|
| 决策点 | 选定 |
|
||||||
|
|--------|------|
|
||||||
|
| 构建策略 | GH Actions 为主(tauri-action)+ Docker 本地调 Linux |
|
||||||
|
| macOS 签名 | 不签名 / 跳过公证(用户在 README 中被告知右键 → 打开绕过 Gatekeeper) |
|
||||||
|
| Linux 格式 | deb + AppImage + rpm(全部覆盖) |
|
||||||
|
| Release 触发 | tag 推送触发 + tag / package.json / Cargo.toml / tauri.conf.json 四处版本一致性检查 |
|
||||||
|
| Update checker | 按 OS + 扩展名自动选 asset(Windows: .msi,macOS: .dmg,Linux: .AppImage) |
|
||||||
|
|
||||||
|
## 3. 架构
|
||||||
|
|
||||||
|
### 3.1 发布流
|
||||||
|
|
||||||
|
```
|
||||||
|
git tag v0.7.0 && git push origin v0.7.0
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
GH Actions: version-check job
|
||||||
|
│ (tag == package.json == Cargo.toml == tauri.conf.json ?)
|
||||||
|
▼
|
||||||
|
3 个并行 jobs 同时 build:
|
||||||
|
ubuntu-latest → .deb / .AppImage / .rpm
|
||||||
|
macos-latest → .app + .dmg(无签名)
|
||||||
|
windows-latest → .msi
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
tauri-action 自动收集 artifacts → 上传到 GitHub Release v0.7.0
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2 本地调试流
|
||||||
|
|
||||||
|
```
|
||||||
|
Developer on Windows
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
docker compose run --rm build-linux
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
ubuntu 22.04 容器内
|
||||||
|
apt-get install webkit2gtk-4.1 librsvg2-dev
|
||||||
|
cargo install tauri-cli
|
||||||
|
npm ci && npm run build
|
||||||
|
cargo tauri build --bundles deb,appimage,rpm
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
src-tauri/target/release/bundle/
|
||||||
|
├── deb/KimiSwitch_0.7.0_amd64.deb
|
||||||
|
├── appimage/KimiSwitch_0.7.0_amd64.AppImage
|
||||||
|
└── rpm/KimiSwitch-0.7.0-1.x86_64.rpm
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.3 用户更新流
|
||||||
|
|
||||||
|
```
|
||||||
|
App 启动 / 每 8h / 手动
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
GET https://api.github.com/repos/billowliu2/KimiSwitch/releases?per_page=1
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
解析 JSON,按 OS 过滤 assets
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
? update_available → footer 弹提示
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
用户点"下载" → 后端流式下载到 temp_dir()/KimiSwitch_update.<ext>
|
||||||
|
│
|
||||||
|
▼
|
||||||
|
open_installer(path) → 系统默认 handler 打开
|
||||||
|
Windows: 启动 MSI 安装
|
||||||
|
macOS: 挂载 .dmg,用户拖入 Applications
|
||||||
|
Linux: AppImage 直接 chmod +x 跑;deb/rpm 走系统包管理器
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. 文件 / 代码变更
|
||||||
|
|
||||||
|
### 4.1 新增
|
||||||
|
|
||||||
|
| 路径 | 目的 |
|
||||||
|
|------|------|
|
||||||
|
| `.github/workflows/release.yml` | GH Actions 工作流(version-check + 3 平台矩阵) |
|
||||||
|
| `Dockerfile` | Ubuntu 22.04 + webkit2gtk-4.1 + librsvg2 + cargo + node,构建工具链 |
|
||||||
|
| `docker-compose.yml` | `docker compose run --rm build-linux` 一行起 |
|
||||||
|
| `scripts/check-version.sh` | 校验 tag / package.json / Cargo.toml / tauri.conf.json 四处版本号一致 |
|
||||||
|
| `docs/BUILD.md` | 本地 Docker 构建、平台差异、签名状态说明 |
|
||||||
|
|
||||||
|
### 4.2 修改
|
||||||
|
|
||||||
|
| 路径 | 改动 |
|
||||||
|
|------|------|
|
||||||
|
| `src-tauri/tauri.conf.json` | `bundle.targets: ["msi"]` → `"all"`;icon 列表追加 macOS 专用 `.icns`;加 `macOS.minimumSystemVersion: "10.15"` |
|
||||||
|
| `src-tauri/src/main.rs` | `windows_subsystem = "windows"` 改为仅 Windows 启用(macOS / Linux 仍 console subsystem) |
|
||||||
|
| `src-tauri/src/commands.rs` | `download_update` 临时文件名按 OS 走;`check_for_update` 按 OS 过滤 asset |
|
||||||
|
| `README.md` | 三平台下载链接;Docker 调试一节;GH Actions 状态徽章 |
|
||||||
|
| `README_EN.md` | 同上英文版 |
|
||||||
|
|
||||||
|
### 4.3 核心 Rust 实现
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// src-tauri/src/commands.rs
|
||||||
|
|
||||||
|
/// Pick the GitHub release asset matching the current OS by file extension.
|
||||||
|
/// Preference for Linux is AppImage (portable, no install). If the preferred
|
||||||
|
/// extension is not present, returns None so the UI can fall back to
|
||||||
|
/// "open release page" instead of guessing a less-preferred format.
|
||||||
|
fn pick_asset_for_current_os(assets: &[serde_json::Value]) -> Option<String> {
|
||||||
|
let target_ext = match std::env::consts::OS {
|
||||||
|
"macos" => "dmg",
|
||||||
|
"linux" => "AppImage",
|
||||||
|
"windows" => "msi",
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
assets.iter()
|
||||||
|
.find(|a| a.get("name").and_then(|n| n.as_str())
|
||||||
|
.map(|n| n.ends_with(target_ext)).unwrap_or(false))
|
||||||
|
.and_then(|a| a.get("browser_download_url"))
|
||||||
|
.and_then(|v| v.as_str())
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build the platform-ext-specific temp filename for downloaded installer.
|
||||||
|
fn update_temp_filename() -> String {
|
||||||
|
let ext = match std::env::consts::OS {
|
||||||
|
"macos" => "dmg",
|
||||||
|
"linux" => "AppImage",
|
||||||
|
"windows" => "msi",
|
||||||
|
_ => "bin",
|
||||||
|
};
|
||||||
|
format!("KimiSwitch_update.{ext}")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`main.rs` 调整:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// 旧
|
||||||
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||||
|
|
||||||
|
// 新
|
||||||
|
#![cfg_attr(
|
||||||
|
all(not(debug_assertions), target_os = "windows"),
|
||||||
|
windows_subsystem = "windows"
|
||||||
|
)]
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. 错误处理
|
||||||
|
|
||||||
|
| 场景 | 处理 |
|
||||||
|
|------|------|
|
||||||
|
| tag 与 Cargo.toml 版本不一致 | workflow 立即 fail,输出 diff;不进入 build |
|
||||||
|
| GitHub Release 已有同名 asset | tauri-action 自动覆盖(默认行为) |
|
||||||
|
| Linux runner 缺 webkit2gtk 等系统包 | tauri-action 文档明确会自动 `apt-get install`;失败 bubble up |
|
||||||
|
| macOS 构建失败 | 失败 job 标红,其他平台不受影响;release 仍上传成功的资产 |
|
||||||
|
| 用户端 update check 网络失败 | 静默失败(`useUpdateCheck.ts` 已有 try/catch) |
|
||||||
|
| 本地 Docker 构建失败 | 多阶段 Dockerfile,失败 → 容器退出码 + 完整日志 |
|
||||||
|
| macOS 未签名 .dmg 被 Gatekeeper 拦 | README 明确"右键 → 打开"绕过;远期可加签名(独立设计) |
|
||||||
|
|
||||||
|
质量门:
|
||||||
|
- `cargo check --target x86_64-unknown-linux-gnu`(Docker 内)
|
||||||
|
- `npm run build`(前端)
|
||||||
|
- 不引入新的 lint 失败
|
||||||
|
- macOS unsigned 状态在 release body 里显式标注
|
||||||
|
|
||||||
|
## 6. 测试验证
|
||||||
|
|
||||||
|
### 6.1 本地构建验证
|
||||||
|
|
||||||
|
1. **单测 / lint**
|
||||||
|
- `npm run lint`
|
||||||
|
- `cargo check`
|
||||||
|
- `cargo check --target x86_64-unknown-linux-gnu`(Docker 内)
|
||||||
|
|
||||||
|
2. **本地 Docker Linux 构建**
|
||||||
|
- `docker compose build build-linux` → 容器构建成功
|
||||||
|
- `docker compose run --rm build-linux` → 产出 3 个 Linux 包
|
||||||
|
- `dpkg --info kimiswitch_0.7.0_amd64.deb` 确认 metadata
|
||||||
|
- `file kimiswitch_0.7.0_amd64.AppImage` 确认 ELF 可执行
|
||||||
|
|
||||||
|
3. **Rust 单元测**
|
||||||
|
- `pick_asset_for_current_os`:
|
||||||
|
- `windows` + assets `[msi, dmg, AppImage]` → 选 msi
|
||||||
|
- `macos` + assets `[msi, dmg, AppImage]` → 选 dmg
|
||||||
|
- `linux` + assets `[msi, dmg, AppImage]` → 选 AppImage
|
||||||
|
- 都不匹配 → 返回 None
|
||||||
|
|
||||||
|
### 6.2 GH Actions 端到端
|
||||||
|
|
||||||
|
- 推 test tag `v0.6.0-rc1`(prerelease,**不污染正式 v0.6.0**)
|
||||||
|
- 验证 3 个 jobs 都绿
|
||||||
|
- 验证 assets 上传到 GitHub Release
|
||||||
|
|
||||||
|
### 6.3 运行时手测(自动化跳过,仅文档化)
|
||||||
|
|
||||||
|
- macOS: 挂载 .dmg,拖入 Applications;启动确认(首次会被 Gatekeeper 拦,文档说明)
|
||||||
|
- Linux: AppImage `chmod +x && ./KimiSwitch_0.7.0_amd64.AppImage`;deb `sudo dpkg -i`;rpm `sudo rpm -i`
|
||||||
|
- Windows: 已有 MSI 流程,不回归
|
||||||
|
|
||||||
|
### 6.4 Update checker 联调
|
||||||
|
|
||||||
|
- App 内"检查更新" → 后端 fetch GH releases → 按 OS 选对 asset → 下载正确文件
|
||||||
|
- Linux 上 AppImage 装完后启动 app,update check 选 AppImage 而非其他格式
|
||||||
|
|
||||||
|
### 6.5 验收
|
||||||
|
|
||||||
|
- ✅ 三个平台都跑出可安装产物
|
||||||
|
- ✅ GH Actions 总耗时 < 30 分钟
|
||||||
|
- ✅ 版本不一致时 workflow 立即 fail
|
||||||
|
- ✅ Update checker 跨平台行为正确
|
||||||
|
- ✅ README 三平台下载链接齐全
|
||||||
|
|
||||||
|
## 7. 范围外(本次不做)
|
||||||
|
|
||||||
|
- macOS 代码签名 + notarization(用户已确认本期跳过;独立设计未来再加)
|
||||||
|
- 自动更新检查替换为 Tauri 官方 `tauri-plugin-updater`(本期仍用自定义 `check_for_update`)
|
||||||
|
- iOS / Android(已有 icon 资源,但本期不构建)
|
||||||
|
- 自动 changelog 生成(用 commit log + 手动整理)
|
||||||
|
- Linux 平台 snap / flatpak
|
||||||
|
- Linux 32 位 / ARM 构建(先 x86_64 就够了)
|
||||||
|
|
||||||
|
## 8. 风险与回退
|
||||||
|
|
||||||
|
| 风险 | 缓解 |
|
||||||
|
|------|------|
|
||||||
|
| macOS unsigned 限制分发 | README 引导用户绕过;后续可补签名 |
|
||||||
|
| GH Actions 30 分钟耗时不够 | 用 `tauri-action` 自带缓存;3 平台并行通常 10-15 分钟 |
|
||||||
|
| Docker 镜像构建慢 | 用 prebuilt `tauri-apps/tauri:2-jammy` 风格基础镜像(自维护一份);接下来 PR 可替换 |
|
||||||
|
| 单元测 build 时间拖慢 | `pick_asset_for_current_os` 是纯函数,单测几百 ms;不会显著拖慢 CI |
|
||||||
|
| 用户升级后旧版检查更新路径被破坏 | 新逻辑兼容旧 release(只有一个 msi asset 时返回 None,UI 显示"请手动下载") |
|
||||||
Reference in new issue
Block a user