Files
KimiCodeSwitch/docs/superpowers/specs/2026-07-29-multi-platform-packaging-design.md
T
954801926@qq.com 00322fb503 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
2026-07-30 00:12:19 +08:00

9.1 KiB
Raw Blame History

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 实现

// 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 调整:

// 旧
#![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 显示"请手动下载")