本文档以中文撰写。

构建指南

本指南介绍如何从源码构建 Zephyr。Zephyr 是基于 Tauri v2 的 Mihomo GUI 代理客户端,前端使用原生 JavaScript(无框架,UnoCSS 样式),后端使用 Rust;仓库是 pnpm workspace + Cargo workspace 组成的 monorepo。

目录


项目结构

前端与 Rust 后端通过 Tauri IPC 通信,IPC 命令名常量集中在 packages/shared:

%%{init: {'themeVariables': {'fontSize': '9px', 'nodeBorder': '1px', 'clusterBorder': '1px', 'edgeLabelHeight': '8px'}}}%%
graph TB
    subgraph root["仓库根目录"]
        A["package.json<br/>workspace 根脚本"]
        B["pnpm-workspace.yaml<br/>apps/* + packages/*"]
        C["Cargo.toml<br/>Cargo workspace + 统一 Lint"]
        D["rust-toolchain.toml<br/>固定 Rust 1.96.0"]
    end

    subgraph desktop["apps/desktop/ - 桌面应用 (@zephyr/desktop)"]
        E["src/<br/>原生 JS 前端 / 样式 / UI 模块"]
        F["src-tauri/<br/>Rust 后端 (crate: Zephyr)"]
        G["src-tauri/tauri.conf.json<br/>Tauri 与打包配置"]
        H["src-tauri/bundled/<br/>Full 版内置资源目录"]
        I["uno.config.js / eslint.config.mjs<br/>vitest.config.js"]
    end

    subgraph crates["crates/ - 共享 Rust 库"]
        J["core/<br/>zephyr-core 跨平台业务逻辑"]
        K["core-ffi/<br/>zephyr-core-ffi UniFFI 移动端绑定"]
    end

    subgraph packages["packages/ - 共享包"]
        L["shared/<br/>IPC 命令名常量 (COMMANDS)"]
        M["tokens/<br/>Design Token (Style Dictionary)"]
        N["scripts/<br/>i18n 完整性检查"]
    end

    A --> desktop
    B --> packages
    C --> F
    C --> crates
    F --> J
    L --> E
    M --> E

目录说明

目录/文件 说明
apps/desktop/ 桌面应用主包 @zephyr/desktop
apps/desktop/src/ 原生 JavaScript 前端(index.html、main.js、api.js、ui/、utils/、modules/ 等),也是 Tauri 的 frontendDist
apps/desktop/src-tauri/ Tauri v2 Rust 后端(crate 名 Zephyr,lib 名 my_clash_gui_lib)
apps/desktop/src-tauri/tauri.conf.json 应用标识 com.zephyr.desktop、窗口、CSP、打包目标与安装器配置
apps/desktop/src-tauri/tauri.linux.conf.json Linux 专用:打包前安装自定义 linuxdeploy-plugin-gtk.sh(AppImage 用)
apps/desktop/src-tauri/bundled/ Full 版内置资源(mihomo 内核与 Geo 数据),仓库中只有 .gitkeep
apps/desktop/src-tauri/THIRD-PARTY-LICENSES.txt 随安装包分发的第三方许可证全文(脚本生成,见下文)
crates/core/ zephyr-core:跨平台纯业务逻辑(配置合并、清洗、订阅等)
crates/core-ffi/ zephyr-core-ffi:UniFFI 移动端绑定入口(cdylib / staticlib)
packages/shared/ 前后端共享的 IPC 命令名常量(构建时复制到 src/_shared/)
packages/tokens/ Design Token 源文件与 Style Dictionary 配置
packages/scripts/ check-i18n.js i18n 完整性检查器
scripts/ 许可证生成、SBOM 合并、AppImage GTK 插件、移动端预构建脚本
.github/workflows/ GitHub Actions 工作流
.husky/ 本地 Git 钩子(pre-commit / pre-push)
.devcontainer/ VS Code 开发容器配置

环境要求

通用依赖

工具 版本 来源 / 说明
Node.js ≥ 20.18.1 package.json 的 engines;CI 使用当前 LTS
pnpm 10.12.1 package.json 的 packageManager,推荐通过 Corepack 启用
Rust 1.96.0(含 rustfmt、clippy) rust-toolchain.toml 固定版本,rustup 进入仓库目录时自动安装;Cargo.toml 声明的最低 rust-version 为 1.92
Tauri CLI 2.12.3 作为根 devDependencies 安装,无需全局安装

仓库不使用 npm:所有前端命令都应通过 pnpm 执行。

安装 Rust 工具链

bash
# 安装 rustup
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# 进入仓库后,rustup 会按 rust-toolchain.toml 自动安装 1.96.0
cd Zephyr
rustc --version   # rustc 1.96.0

# 如需为其他架构构建,添加对应 target(作用于固定的 1.96.0 工具链)
rustup target add aarch64-apple-darwin      # macOS Apple Silicon
rustup target add x86_64-apple-darwin       # macOS Intel
rustup target add aarch64-pc-windows-msvc   # Windows ARM64

安装 Node.js 与 pnpm

bash
# 任选一种 Node 版本管理器安装 Node 20.18.1+ / 当前 LTS
fnm install --lts      # 或 nvm install --lts

# 通过 Corepack 启用 package.json 指定的 pnpm 版本
corepack enable
pnpm --version         # 10.12.1

平台特定依赖

Windows

依赖 说明
Visual Studio 2022 Build Tools 勾选"使用 C++ 的桌面开发"(含 MSVC 与 Windows SDK)
WebView2 Runtime Windows 10(较新版本)/11 通常已预装
NSIS / WiX 无需手动安装,Tauri CLI 打包时会自动下载

macOS

依赖 说明
Xcode Command Line Tools xcode-select --install
pkg-config、glib(可选) 交叉编译另一架构时需要,CI 中通过 brew install pkg-config glib 安装

DMG 由 Tauri 自带的打包流程生成,无需额外安装 create-dmg。

Linux

以下为 CI 与开发容器(Ubuntu 22.04)实际安装的依赖:

Ubuntu / Debian:

bash
sudo apt update
sudo apt install -y build-essential pkg-config file \
    libwebkit2gtk-4.1-dev \
    libgtk-3-dev \
    libappindicator3-dev \
    libayatana-appindicator3-dev \
    librsvg2-dev \
    libssl-dev \
    libxdo-dev \
    patchelf \
    xdg-utils

其他发行版可参考 Tauri 官方前置条件:

Fedora:

bash
sudo dnf install webkit2gtk4.1-devel openssl-devel curl wget file \
    libappindicator-gtk3-devel librsvg2-devel libxdo-devel
sudo dnf group install "c-development"

Arch Linux:

bash
sudo pacman -S --needed webkit2gtk-4.1 base-devel curl wget file openssl \
    appmenu-gtk-module libappindicator-gtk3 librsvg xdotool

快速开始

1. 克隆仓库

bash
git clone https://github.com/Juwan-Hwang/Zephyr.git
cd Zephyr

2. 安装前端依赖

bash
pnpm install

pnpm install 会通过 prepare 脚本执行 husky,安装本地 Git 钩子。pnpm-workspace.yaml 的 onlyBuiltDependencies 只允许 esbuild 与 @parcel/watcher 运行安装脚本;CI 采用更严格的 pnpm install --ignore-scripts && pnpm rebuild esbuild @parcel/watcher,本地也可照此执行。

3. 开发模式运行

bash
pnpm dev
# 等价于 pnpm --filter @zephyr/desktop tauri dev

Tauri 会先执行 beforeDevCommand(pnpm run dev:css:复制 @zephyr/shared → 生成 Design Token CSS → UnoCSS watch),再编译并启动 Rust 后端。首次编译需要下载并编译全部 crate,耗时较长;之后为增量编译。

开发模式说明:

  • Debug 构建不启用单实例限制,可同时运行多个实例便于测试。
  • 应用启动需要 mihomo 内核。最简单的做法是把内核放进 apps/desktop/src-tauri/bundled/(见下一节),或直接放到应用数据目录的 core/ 下(路径见 Installation.md 的"Lite 版内核放置")。
  • 需要 WebView 开发者工具时,可启用 crate 的 devtools feature:pnpm tauri dev --features devtools。

4. 生产构建

bash
pnpm build
# 等价于:pnpm --filter @zephyr/desktop build:css
#         pnpm --filter @zephyr/desktop tauri build

tauri.conf.json 中 bundle.targets 为 "all",会在当前平台生成该平台支持的全部安装包格式。由于是 Cargo workspace,产物位于仓库根目录的 target/ 下:

平台 路径 格式
Windows target/release/bundle/nsis/ Zephyr_<版本>_x64-setup.exe
Windows target/release/bundle/msi/ Zephyr_<版本>_x64_zh-CN.msi
macOS target/release/bundle/dmg/ .dmg
macOS target/release/bundle/macos/ Zephyr.app
Linux target/release/bundle/deb/ .deb
Linux target/release/bundle/rpm/ .rpm
Linux target/release/bundle/appimage/ .AppImage

使用 --target <triple> 交叉构建时,产物位于 target/<triple>/release/bundle/,例如:

bash
pnpm tauri build --target aarch64-apple-darwin
pnpm tauri build --target aarch64-pc-windows-msvc

版本号:仓库中 tauri.conf.json、apps/desktop/package.json 与 src-tauri/Cargo.toml 的版本字段是占位值,正式版本号由发布工作流在构建时按 Git tag 写入。因此本地构建出的应用显示的版本号与正式发布不同。

安装包配置要点

平台 配置 说明
Windows NSIS 语言 SimpChinese、English,不显示语言选择器 安装器图标、页眉 Header.bmp、侧栏 Sidebar.bmp
Windows MSI WiX 语言 zh-CN 文件名带 _zh-CN
macOS minimumSystemVersion: "10.13" DMG 使用自定义背景,窗口 660×400
Linux deb 依赖 libwebkit2gtk-4.1-0、libgtk-3-0、libayatana-appindicator3-1
Linux rpm 依赖 webkit2gtk4.1、gtk3、libayatana-appindicator-gtk3
Linux AppImage tauri.linux.conf.json 的 beforeBundleCommand 安装 scripts/linuxdeploy-plugin-gtk.sh 剔除 GPU/Mesa 库,并修补 WebKitGTK 辅助进程查找路径,使其在 AppImage 内解析
全部 resources:bundled/*、THIRD-PARTY-LICENSES.txt 许可证文件随所有安装包分发

Tauri 构建打包流程

%%{init: {'themeVariables': {'fontSize': '9px', 'nodeBorder': '1px', 'clusterBorder': '1px', 'edgeLabelHeight': '8px'}, 'flowchart': {'curve': 'basis', 'nodeSpacing': 12, 'rankSpacing': 15}}}%%
flowchart TD
    A["pnpm build"] --> B["build:css<br/>copy-shared → build:tokens → UnoCSS<br/>(src/uno-generated.css)"]
    B --> C["tauri build<br/>(beforeBuildCommand 再次执行 build:css)"]
    C --> D["Cargo 编译 Rust 后端<br/>(release: lto + codegen-units=1)"]
    D --> E{"平台?"}

    E -- "Windows" --> F1["NSIS .exe"]
    E -- "Windows" --> F2["WiX .msi"]
    E -- "macOS" --> G1[".app + .dmg"]
    E -- "Linux" --> H1[".deb"]
    E -- "Linux" --> H2[".rpm"]
    E -- "Linux" --> H3[".AppImage<br/>(自定义 GTK 插件)"]

    F1 --> I["target/[triple/]release/bundle/"]
    F2 --> I
    G1 --> I
    H1 --> I
    H2 --> I
    H3 --> I

    style A fill:#3498db,stroke:#2980b9,color:#fff
    style I fill:#2ecc71,stroke:#27ae60,color:#fff

Cargo 提供两个发布 profile(定义在根 Cargo.toml):

Profile 设置 用途
release(默认) opt-level = 3、lto = true、codegen-units = 1、panic = "unwind"、strip = "debuginfo" 性能优先
release-small 继承 release,opt-level = "z" 体积优先,需手动指定

打包 Full 版(内置内核)

Full 版与 Lite 版使用同一份代码,区别只在于打包时 apps/desktop/src-tauri/bundled/ 是否有内容:

  • Full:bundled/ 中放入内核与 Geo 数据,随安装包分发;应用首次启动时把它们复制到数据目录的 core/(目标文件已存在时不覆盖)。
  • Lite:bundled/ 为空。

本地打包 Full 版需手动准备以下文件:

文件 来源 说明
zephyr-mihomo(Windows 为 zephyr-mihomo.exe) MetaCubeX/mihomo Releases 内核二进制须重命名为此名称
geoip.dat MetaCubeX/meta-rules-dat GeoIP 数据
geosite.dat MetaCubeX/meta-rules-dat GeoSite 规则集
Country.mmdb MetaCubeX/meta-rules-dat MaxMind 格式 GeoIP 数据库

发布流水线下载的内核资产:

平台 / 架构 mihomo 资产
Windows x64 mihomo-windows-amd64-compatible-<版本>.zip
Windows ARM64 mihomo-windows-arm64-<版本>.zip
macOS x64 mihomo-darwin-amd64-compatible-<版本>.gz
macOS ARM64 mihomo-darwin-arm64-<版本>.gz
Linux x64 mihomo-linux-amd64-compatible-<版本>.gz
Linux ARM64 mihomo-linux-arm64-<版本>.gz

资源获取流程(发布流水线)

%%{init: {'themeVariables': {'fontSize': '10px', 'nodeBorder': '1px', 'clusterBorder': '1px', 'edgeLabelHeight': '10px'}, 'flowchart': {'curve': 'basis', 'nodeSpacing': 15, 'rankSpacing': 25}}}%%
flowchart LR
    A["查询 mihomo<br/>最新正式版 tag"] --> B["下载对应平台内核"]
    B --> C["与 GitHub API 返回的<br/>资产 SHA256 digest 比对"]
    C --> D["解压并重命名为<br/>zephyr-mihomo(.exe)"]
    A2["下载 geoip.dat / geosite.dat /<br/>Country.mmdb + .sha256sum"] --> E["SHA256 校验"]
    D --> F["apps/desktop/src-tauri/bundled/"]
    E --> F
    F --> G["打包 Full 版"]

    style C fill:#e74c3c,stroke:#c0392b,color:#fff
    style E fill:#e74c3c,stroke:#c0392b,color:#fff
    style G fill:#2ecc71,stroke:#27ae60,color:#fff

任何一项校验失败都会使构建中止。内核版本即发布构建当时 MetaCubeX/mihomo 的最新正式版。


脚本命令说明

根目录(package.json)

命令 实际执行 用途
pnpm dev pnpm --filter @zephyr/desktop tauri dev 开发模式
pnpm build build:css + tauri build(桌面包) 生产构建
pnpm build:css 桌面包 build:css 生成样式产物
pnpm lint pnpm -r lint ESLint
pnpm test pnpm -r test Vitest 单元测试
pnpm typecheck pnpm -r typecheck tsc 对 JSDoc 做类型检查
pnpm tauri <子命令> pnpm --filter @zephyr/desktop tauri Tauri CLI 通用入口
pnpm check:i18n node packages/scripts/check-i18n.js 检查各语言翻译完整性

桌面包(apps/desktop/package.json)

命令 说明
build:css copy-shared → build:tokens → build:uno
dev:css copy-shared → build:tokens → dev:uno(watch)
copy-shared 复制 packages/shared/src/index.js 到 src/_shared/index.js
build:tokens 在仓库根目录执行 Style Dictionary,生成 packages/tokens/build/css/
copy-tokens 复制 Token CSS 为 src/tokens-variables.css、src/tokens-theme.css
build:uno / dev:uno UnoCSS CLI 扫描 src/**/*.{html,js},输出 src/uno-generated.css
build:cm6 esbuild 打包 CodeMirror 6 入口为 src/cm6.bundle.js(该文件已提交到仓库,仅在修改编辑器入口时需要重新运行)
lint eslint .
test vitest run
test:coverage vitest run --coverage(lcov + text 报告,输出到 apps/desktop/coverage/)
typecheck tsc -p jsconfig.json --noEmit

src/_shared/、src/uno-generated.css、src/tokens-*.css 都是生成文件,已被 .gitignore 忽略。

Tauri CLI 常用命令

bash
pnpm tauri dev                         # 开发模式
pnpm tauri build                       # 生产构建
pnpm tauri build --debug               # 构建 debug 版安装包
pnpm tauri build --bundles app         # 仅构建指定格式(如 macOS .app)
pnpm tauri build --target <triple>     # 指定目标架构
pnpm tauri info                        # 查看环境信息

代码质量与提交检查

前端

工具 命令 配置
ESLint 10 pnpm lint apps/desktop/eslint.config.mjs:浏览器全局变量 + jsyaml、__TAURI__、__TAURI_INTERNALS__;内置 no-unsanitized 规则,检查 innerHTML/outerHTML 赋值和 insertAdjacentHTML 等调用是否经过转义
TypeScript(JSDoc 检查) pnpm typecheck apps/desktop/jsconfig.json
Vitest 5 pnpm test apps/desktop/vitest.config.js:happy-dom 环境,匹配 src/**/*.test.js
i18n 检查 pnpm check:i18n packages/scripts/check-i18n.js

后端(在仓库根目录执行)

bash
cargo fmt --all -- --check                                  # 格式检查
cargo clippy --workspace --all-targets --locked -- -D warnings
cargo test --workspace --locked
  • Clippy 规则集中定义在根 Cargo.toml 的 [workspace.lints],所有成员 crate 通过 [lints] workspace = true 继承,本地与 CI 使用同一套规则;.clippy.toml 设置认知复杂度阈值 25。
  • 快照测试使用 insta;CI 中缺失或过期的快照会导致失败。

依赖与安全审计

工具 命令 / 配置 说明
cargo-audit cd apps/desktop/src-tauri && cargo audit 忽略列表见 .cargo/audit.toml
cargo-deny cargo deny --manifest-path apps/desktop/src-tauri/Cargo.toml check 配置 deny.toml
Semgrep semgrep scan --config auto --config .semgrep.yml 官方规则 + 自定义规则
OSV-Scanner osv-scanner.toml 带期限的忽略项
CodeQL、Snyk、GitGuardian、OSSF Scorecard CI 自动运行 见下方工作流列表

依赖更新由 Renovate 负责(renovate.json:每周一 6 点前、小版本/补丁分组自动合并,webview2-com 不自动更新,npm 包需发布满 3 天)。

本地 Git 钩子(husky)

钩子 执行内容
pre-commit cargo fmt --check --all → pnpm lint → pnpm typecheck
pre-push cargo clippy --workspace --all-targets -- -D warnings → cargo test --workspace → pnpm test(仅删除分支的推送会跳过)

pre-commit.ci

.pre-commit-config.yaml 只包含快速的文件卫生检查(trailing-whitespace、end-of-file-fixer、check-yaml、check-toml、check-json、check-added-large-files),由 pre-commit.ci 在云端对 PR 运行并自动推送修复提交。Rust/JS 的格式与 lint 不在其中,仍由 CI 负责。本地如需使用:pip install pre-commit && pre-commit run --all-files。

开发工作流

%%{init: {'themeVariables': {'fontSize': '9px', 'nodeBorder': '1px', 'clusterBorder': '1px', 'edgeLabelHeight': '8px'}, 'flowchart': {'curve': 'basis', 'nodeSpacing': 12, 'rankSpacing': 15}}}%%
flowchart TD
    A[克隆仓库] --> B[pnpm install<br/>同时安装 husky 钩子]
    B --> C[pnpm dev<br/>开发调试]
    C --> D[git commit]
    D --> E["pre-commit 钩子<br/>cargo fmt --check · pnpm lint · pnpm typecheck"]
    E --> F[git push]
    F --> G["pre-push 钩子<br/>cargo clippy · cargo test · pnpm test"]
    G --> H[提交 PR]
    H --> I["CI 工作流<br/>前端 / 许可证 / Rust 三平台"]
    H --> J["安全扫描<br/>Security Scan · CodeQL · GitGuardian 等"]
    H --> K["pre-commit.ci<br/>文件卫生检查"]
    I --> L[审查与合并]
    J --> L
    K --> L

第三方许可证文件

apps/desktop/src-tauri/THIRD-PARTY-LICENSES.txt 随所有安装包与便携版分发,也是应用内"设置 → 关于 → 第三方许可证"查看器的数据来源。它由脚本生成,不要手动编辑:

bash
# 前置条件:cargo-about 0.9.2、Rust 工具链、Node.js + pnpm,且已执行 pnpm install
cargo install cargo-about --version 0.9.2 --locked
./scripts/generate-licenses.sh

文件由三部分拼接而成:

部分 来源
Part 1 — Rust 组件 cargo about generate(about.toml 定义可接受许可证,about.hbs 为模板),基于 Cargo.lock;工作区自身的 crate 会被剔除
Part 2 — JavaScript 组件 scripts/generate-licenses-js.mjs,读取 pnpm licenses list --prod 的生产依赖并逐个读取包内许可证全文(缺少许可证文本的包会使生成失败)
Part 3 — 内置组件与其他材料 手工维护的 scripts/third-party-assets-licenses.txt(mihomo 内核、Geo 数据、字体子集等)

CI 的 licenses 任务会重新生成并在文件与提交版本不一致时失败。修改 Rust 或 JS 依赖(lockfile 变化)后,需要重新运行脚本并提交结果。 发布流水线在构建前也会重新生成一次,确保随包文件与当次构建的 lockfile 完全一致。


CI/CD 与发布流水线

工作流一览

工作流 触发 内容
ci.yml PR、推送到 main 前端:i18n 检查、ESLint、typecheck、Vitest 覆盖率(上传 Codecov);许可证文件漂移检查;Rust(Ubuntu):fmt、clippy、cargo llvm-cov 测试(上传 Codecov);Rust(Windows、macOS):debug 测试
security.yml 推送到 main/dev、PR、每日定时 cargo audit、cargo-deny、clippy、fmt、依赖审查、Semgrep、密钥检测、OSSF Scorecard、Tauri 安全配置检查、Linux 构建验证、macOS 构建检查
codeql.yml 推送/PR 到 main、每周定时 CodeQL 语义分析
snyk.yml JS 依赖文件变化时 Snyk 扫描
gitguardian.yml 推送到 main/dev、PR 到 main GitGuardian 密钥扫描
release.yml 推送 v* tag 全平台构建、签名并创建草稿 Release
update-aur.yml / update-scoop.yml / update-winget.yml Release 发布后 更新 AUR、Scoop bucket、Winget 清单
mac-debug.yml 手动触发(仅仓库所有者) 在 macOS 26 Runner 上构建 Apple Silicon .app,通过 Tailscale 开放 SSH 与 VNC 远程桌面,用于在真实 Mac 上手动测试未发布代码
mobile.yml 手动触发 Android APK 与 iOS XCFramework(crates/core-ffi)
deploy-demo.yml 前端源码变化时 部署 GitHub Pages 在线演示
test-badges.yml 推送到 main 更新测试数量徽章
test-signing.yml 手动触发 SignPath 签名流程测试

CI 中 Rust 工具链固定为 1.96.0(与 rust-toolchain.toml 同步);Codecov 覆盖率状态目前仅作提示,不阻塞合并(codecov.yml)。

发布构建矩阵

推送 v* tag 后,release.yml 在 6 个矩阵任务上并行构建:

%%{init: {'themeVariables': {'fontSize': '9px', 'nodeBorder': '1px', 'clusterBorder': '1px', 'edgeLabelHeight': '8px'}}}%%
graph TB
    BUILD["release.yml<br/>推送 v* tag"] --> MA["macOS arm64<br/>aarch64-apple-darwin<br/>macos-latest"]
    BUILD --> MX["macOS x64<br/>x86_64-apple-darwin<br/>macos-latest"]
    BUILD --> LX["Linux x64<br/>ubuntu-22.04"]
    BUILD --> LA["Linux arm64<br/>ubuntu-22.04-arm"]
    BUILD --> WX["Windows x64<br/>windows-latest"]
    BUILD --> WA["Windows arm64<br/>aarch64-pc-windows-msvc<br/>windows-latest"]

    MA --> DMG[".dmg(Full / Lite)"]
    MX --> DMG
    LX --> LIN[".deb / .rpm / .AppImage(Full / Lite)<br/>+ 便携版 tar.gz"]
    LA --> LIN
    WX --> WIN["NSIS .exe / .msi(Full / Lite)<br/>+ 便携版 zip"]
    WA --> WIN

    style BUILD fill:#3498db,stroke:#2980b9,color:#fff

发布流程

%%{init: {'themeVariables': {'fontSize': '9px', 'nodeBorder': '1px', 'clusterBorder': '1px', 'edgeLabelHeight': '8px'}, 'flowchart': {'curve': 'basis', 'nodeSpacing': 12, 'rankSpacing': 15}}}%%
flowchart TD
    A["从 tag 解析版本号<br/>写入 tauri.conf.json / Cargo.toml / package.json"] --> B["安装 pnpm、Node LTS、Rust<br/>pnpm install --ignore-scripts"]
    B --> C["cargo-about 重新生成<br/>THIRD-PARTY-LICENSES.txt"]
    C --> D["下载并校验 mihomo + Geo 数据<br/>→ bundled/"]
    D --> E["构建 Full 版"]
    E --> F["清空 bundled/<br/>构建 Lite 版"]
    F --> G{"平台"}
    G -- "Linux" --> H["Full AppImage + core/ + .portable<br/>→ 便携版 tar.gz"]
    G -- "Windows" --> I["SignPath 签名 NSIS / MSI / 便携版 exe<br/>校验签名者为 SignPath Foundation"]
    I --> J["签名后的 exe + core/ + .portable<br/>→ 便携版 zip"]
    H --> K["Minisign 为全部产物生成 .minisig"]
    J --> K
    G -- "macOS" --> K
    K --> L["上传到草稿 Release"]
    L --> M["SBOM 任务:cargo-cyclonedx + cdxgen<br/>合并为 v&lt;版本&gt;.cdx.json 附加到 Release"]
    M --> N["人工发布 Release"]
    N --> O["自动更新 AUR / Scoop / Winget"]

要点:

  • 发布任务持有签名密钥,因此不使用依赖缓存,每次都干净安装。
  • Full 与 Lite 在同一任务中先后构建;便携版复用 Full 构建的二进制,不额外编译。
  • Windows 产物通过 SignPath 签名(证书由 SignPath Foundation 提供),流水线逐个校验 Authenticode 签名有效且签名者为 SignPath Foundation 后才替换未签名文件。详见仓库 CODE_SIGNING_POLICY.md。
  • macOS 产物未经 Apple 开发者证书签名与公证。
  • 所有产物另附 Minisign(Ed25519)签名 .minisig,客户端内更新会校验该签名。
  • Release 以草稿形式创建,由维护者手动发布;发布事件再触发 AUR / Scoop / Winget 更新。

发布产物命名

平台 Full / Lite 便携版
Windows Zephyr_<版本>_{x64,arm64}-setup-{full,lite}.exe
Zephyr_<版本>_{x64,arm64}_zh-CN-{full,lite}.msi
Zephyr_{x64,arm64}-portable.zip
macOS Zephyr_<版本>_{aarch64,x64}-{full,lite}.dmg —
Linux Zephyr_<版本>_{amd64,aarch64}-{full,lite}.AppImage
Zephyr_<版本>_{amd64,arm64}-{full,lite}.deb
Zephyr-<版本>-1.{x86_64,aarch64}-{full,lite}.rpm
Zephyr-linux-{x64,arm64}-portable.tar.gz
通用 每个文件对应一个 .minisig;SBOM v<版本>.cdx.json

开发容器

仓库提供 .devcontainer/,可在 VS Code / GitHub Codespaces 中一键获得 Linux 构建环境:

项目 配置
基础镜像 mcr.microsoft.com/devcontainers/base:ubuntu-22.04
工具 Rust(stable,进入仓库后按 rust-toolchain.toml 切换到 1.96.0)、Node LTS
初始化脚本 post-create.sh:安装 WebKitGTK 等系统依赖 → corepack enable → pnpm install --ignore-scripts → pnpm rebuild esbuild @parcel/watcher
VS Code 扩展 rust-analyzer、Tauri、ESLint、Tailwind CSS IntelliSense

由于使用 --ignore-scripts,容器内不会自动安装 husky 钩子;如需钩子,手动执行 pnpm exec husky。


常见构建问题

Q: cargo 报错 "linker 'link.exe' not found"(Windows)

A: 安装 Visual Studio Build Tools,勾选"使用 C++ 的桌面开发"(包含 MSVC 与 Windows SDK),然后重新打开终端。

Q: 编译时提示找不到 webkit2gtk(Linux)

A: 安装 WebKitGTK 4.1 开发包(Ubuntu/Debian:libwebkit2gtk-4.1-dev;Fedora:webkit2gtk4.1-devel;Arch:webkit2gtk-4.1),完整列表见平台特定依赖。

Q: rustc 版本与预期不符 / clippy 结果和 CI 不一致

A: 仓库通过 rust-toolchain.toml 固定 1.96.0。确认在仓库目录内执行 rustc --version;若显示其他版本,检查是否设置了 RUSTUP_TOOLCHAIN 环境变量或 rustup override。

Q: CI 的 "Third-party licenses (up to date)" 失败

A: 依赖变化后许可证文件过期。安装 cargo-about 0.9.2,执行 pnpm install 和 ./scripts/generate-licenses.sh,提交更新后的 apps/desktop/src-tauri/THIRD-PARTY-LICENSES.txt。

Q: pnpm dev 启动后内核无法运行

A: 源码中不包含 mihomo 内核。把内核(重命名为 zephyr-mihomo / zephyr-mihomo.exe)放入 apps/desktop/src-tauri/bundled/ 或应用数据目录的 core/ 下,见打包 Full 版。

Q: 首次编译时间过长

A: 首次需要下载并编译全部依赖,release profile 还开启了 LTO 与 codegen-units = 1,耗时明显长于 debug 构建。可以:

  1. 日常调试使用 pnpm dev(debug 构建);
  2. 使用 sccache:cargo install sccache && export RUSTC_WRAPPER=sccache;
  3. 配置 crates.io 镜像加速下载(见下一问)。

Q: 如何配置 Cargo 镜像源加速下载?

A: 在 ~/.cargo/config.toml 中添加:

toml
[source.crates-io]
replace-with = 'ustc'

[source.ustc]
registry = "sparse+https://mirrors.ustc.edu.cn/crates.io-index/"

Q: macOS 本地构建的应用打不开 / 签名相关报错

A: 仓库未配置 Apple 签名身份,本地构建的 .app 未签名。在本机运行通常没有问题;若从其他机器拷贝过来被 Gatekeeper 拦截,可执行 xattr -cr /path/to/Zephyr.app。如需临时 ad-hoc 签名,可在 tauri.conf.json 的 bundle.macOS 中设置 "signingIdentity": "-"。

Q: 如何构建其他架构?

A: 先 rustup target add <triple>,再执行 pnpm tauri build --target <triple>。同一操作系统内跨架构(如 macOS arm64 ↔ x64、Windows x64 → arm64)可行;跨操作系统构建建议使用发布流水线。macOS 交叉编译需要 brew install pkg-config glib 并设置 PKG_CONFIG_ALLOW_CROSS=1。


构建完成后,建议阅读 Contributing.md 了解代码规范和提交流程。

↑↓ 选择↵ 打开