Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .config/nextest.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# cargo-nextest, which `make coverage-gate` runs the Rust tests with (cargo llvm-cov nextest).
# Each test runs in a process of its own and the tests of all binaries share the workers, where
# `cargo test` ran the binaries one after another (2026-11-10: 101 binaries, 426 s of their own
# time on the runner, voltip-core's tests/long.rs alone 209 s).
[profile.default]
# Every failure of the run is reported, not only the first.
fail-fast = false
# A test still running after 60 s is reported as SLOW; after ten such periods it is stopped and
# fails as TIMEOUT, so a hung test fails the gate and is named in its log long before the CI
# step's own 25-minute limit.
slow-timeout = { period = "60s", terminate-after = 10 }
10 changes: 6 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ env:
# Toolchain pins. The Rust toolchain itself comes from rust-toolchain.toml (rustup reads it);
# these are the cargo subcommands `make verify` / `make windows-x64` need.
CARGO_LLVM_COV_VERSION: 0.8.7
CARGO_NEXTEST_VERSION: 0.9.151
CARGO_DENY_VERSION: 0.19.9
CARGO_XWIN_VERSION: 0.20.2
CARGO_ABOUT_VERSION: 0.9.2
Expand Down Expand Up @@ -184,10 +185,10 @@ jobs:
rustup target add aarch64-apple-darwin x86_64-pc-windows-msvc
rustc --version && cargo --version && cargo clippy --version && cargo fmt --version

- name: cargo-llvm-cov and cargo-deny (prebuilt, pinned)
- name: cargo-llvm-cov, cargo-nextest and cargo-deny (prebuilt, pinned)
uses: taiki-e/install-action@c509219cf42fba86913069efd0ade2458c59337b # v2.90.1
with:
tool: cargo-llvm-cov@${{ env.CARGO_LLVM_COV_VERSION }},cargo-deny@${{ env.CARGO_DENY_VERSION }}
tool: cargo-llvm-cov@${{ env.CARGO_LLVM_COV_VERSION }},cargo-nextest@${{ env.CARGO_NEXTEST_VERSION }},cargo-deny@${{ env.CARGO_DENY_VERSION }}

- name: Cache Cargo registry and target/
uses: Swatinem/rust-cache@d1527c8f67ef9af357290475d4af4b0fab8f12e0 # v2.9.3
Expand Down Expand Up @@ -226,8 +227,9 @@ jobs:
run: pnpm install --frozen-lockfile

# 2–6 minutes on main (2026-09-30). A test that hangs fails the step here instead of holding
# the job to its hour (and a release candidate's source gate to its 45 minutes); the log
# below still uploads and names the test "running for over 60 seconds".
# the job to its hour (and a release candidate's source gate to its 45 minutes). nextest
# names it as SLOW after 60 seconds and stops it as TIMEOUT after ten minutes
# (.config/nextest.toml); the log below still uploads.
- name: Rust gates (VERIFY_GATES=rust scripts/verify-all.sh)
timeout-minutes: 25
env:
Expand Down
7 changes: 4 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,10 @@ runs landed in a row on 2026-09-30, and one of them stalled a release for an hou
- The `macos` jobs do not run on pull requests. After a merge, check that `main`'s run is green on
both Macs; a red `macos` job is fixed like any other, and until it is, `CI Success` on `main` is
red and no release candidate passes its source gate.
- A CI job that runs twice its usual time is a hung test. The Rust gates step stops at 25 minutes
by itself; cancel the run to learn sooner. Either way the gate logs still upload, and the hung
test is the one "running for over 60 seconds".
- A CI job that runs twice its usual time is a hung test. The coverage gate runs the tests with
cargo-nextest (`.config/nextest.toml`), which names a test as `SLOW` after 60 seconds and stops
it as `TIMEOUT` after ten minutes; the Rust gates step stops at 25 minutes by itself. Cancel the
run to learn sooner. Either way the gate logs still upload and name the hung test.
- Ubuntu packages in `ci.yml`, `release-candidate.yml` and `release.yml` go through
`.github/scripts/apt-install.sh`, never a bare `apt-get`: each download attempt has a deadline,
and a later attempt fetches from archive.ubuntu.com instead of the Azure mirror (2026-10-01: that
Expand Down
5 changes: 3 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,10 @@ frontend-dist-dirs:
@mkdir -p apps/desktop/dist apps/mobile/dist

.PHONY: coverage-gate
coverage-gate: frontend-dist-dirs ## Run the Rust tests once, instrumented, and enforce the line-coverage floor (COVERAGE_MIN)
coverage-gate: frontend-dist-dirs ## Run the Rust tests once, instrumented (cargo-nextest, .config/nextest.toml), and enforce the line-coverage floor (COVERAGE_MIN)
@command -v cargo-llvm-cov >/dev/null 2>&1 || { echo "cargo-llvm-cov not found — cargo install cargo-llvm-cov"; exit 2; }
@$(CARGO) llvm-cov --workspace --all-targets --features "$(RUST_FEATURES)" --summary-only \
@command -v cargo-nextest >/dev/null 2>&1 || { echo "cargo-nextest not found — cargo install cargo-nextest --locked"; exit 2; }
@$(CARGO) llvm-cov nextest --workspace --all-targets --features "$(RUST_FEATURES)" --summary-only \
--ignore-filename-regex '$(COVERAGE_EXCLUDE)' \
| tee -a /dev/stderr \
| awk -v min='$(COVERAGE_MIN)' '\
Expand Down
2 changes: 1 addition & 1 deletion docs/runbook.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ Relay 无状态(内存里只有活动会话 / 频道),可水平扩展前
工作流都跑在 GitHub 托管的 runner 上:

- `ci.yml`:push `main` / PR → `changes`(`.github/scripts/changed-code.sh`:只改了 `docs/**`、`*.md`、`LICENSE*` 时 `code=false`;新分支、API 失败或一次推送 300 个以上文件都算改了代码)→ 并行的 `verify-rust`(`VERIFY_GATES=rust`:格式、features、clippy、交叉检查、覆盖率下跑一遍测试、cargo-deny、IPC e2e)与 `verify-web`(`VERIFY_GATES=web`:web 门禁、台账、主机名守卫、发布脚本测试,外加带 `VOLTIP_PRODUCTION_HOSTS` 的守卫;fork 与 Dependabot 的 PR 没有 secrets,跳过这一步)、`codecov`(上传覆盖率,只做报告,不阻塞)、`windows-cross`(`make windows-x64`,无内置服务)→ `windows-native`(Windows Server 上无头运行便携包与安装后的 exe)、`hooks-windows`(Windows 桌面上真实的单键钩子)、`smoke-desktop`(Xvfb 与纯 Wayland 冒烟、X11 真实钩子、无头识别)、`android`(对 Android 目标跑 clippy;用 `scripts/build-android-rn.sh --unsigned` 构建 React Native 版未签名的 release APK 与 AAB,用当次生成的临时密钥签名,再跑与候选相同的包检查)→ `android-device`(把这个 APK 装进 Android 15 模拟器并启动:首屏出现、20 秒内不退出、日志里没有致命错误,`.github/scripts/android-device-smoke.sh`)、`ci-success` 聚合(CI 的总结论);另有 `candidate-status` 给不是发布 PR 的 PR head 写 `Release candidate = success`(见下面的 ruleset)。只改文档时 `verify-web` 照常跑,其余构建、测试、平台 job 跳过;`ci-success` 只在 `changes` 判定为只改文档时接受跳过,其他任何跳过、失败或取消都判失败。CI 不注入任何内置引擎值。
- CI 的速度(2026-09-28 实测后调整):Rust 测试只在覆盖率门禁里跑一遍(原先 `cargo test` 与 `cargo llvm-cov` 各编一遍、各跑一遍,各约 170 秒);`verify` 拆成可并行的两半;Cargo 缓存只从 `main` 保存(PR 的缓存只有同一个 PR 能用,而仓库 10 GB 的上限会把 `main` 的挤掉),不缓存 `~/.cargo/bin`;dev / test 构建只留行号表(`CARGO_PROFILE_DEV_DEBUG=line-tables-only`);清理预装 SDK 只在剩余空间不足 40 GB 时做(`.github/scripts/free-disk-space.sh`,托管 runner 实测开跑时有 86 GB);CI 的 Windows 包复用按全部输入(锁文件、清单、`about.toml`、生成脚本与许可证文本)缓存的第三方声明,省掉约两分钟的 cargo-about(`VOLTIP_NOTICES_CACHED=1`,发布总是重新生成);下载失败重试(`CARGO_NET_RETRY`、`RUSTUP_MAX_RETRIES`);桌面壳的 lib 只编成 `rlib`(2026-10-05):Tauri 模板为手机准备的 `staticlib` 和 `cdylib` 让 release 构建的每个依赖都同时生成机器码和 bitcode,去掉后依赖只生成 thin LTO 用的 bitcode(`-C linker-plugin-lto`),本机 32 核冷构建 224 → 156 秒,target/ 3.3 → 2.4 GB;macOS 腿的钥匙串测试程序是带 `keychain-harness` feature 的 bin 而不是 example(example 会带上 dev-dependencies,它们的 feature 让应用构建的 60 多个 crate 重编一遍,Intel Mac 冷跑时 11 分钟),应用构建之后只需再编这个包本身;voltip-server 的 release 构建单独用 16 个代码生成单元(2026-11-10,根 `Cargo.toml`):只有一个单元时整个 crate 是一个 LLVM 模块,LLVM 优化要 862 秒、ThinLTO 链接 303 秒,候选的 Linux 腿为此等 19–34 分钟;拆开后 4 个并行任务下分别是 2 秒和 26 秒,其余包仍是一个单元。`verify-*.log` 里每道门都记了耗时(`secs=`)。
- CI 的速度(2026-09-28 实测后调整):Rust 测试只在覆盖率门禁里跑一遍(原先 `cargo test` 与 `cargo llvm-cov` 各编一遍、各跑一遍,各约 170 秒);这一遍由 cargo-nextest 运行(2026-11-10,`cargo llvm-cov nextest`,设置在 `.config/nextest.toml`):每个测试一个进程,所有测试二进制的测试一起排队,不再等一个二进制跑完才开始下一个;本机 32 核上整个覆盖率门禁 431 → 278 秒,测试数(2086 个通过、42 个跳过)和行覆盖率(92.95%)不变;跑过 60 秒的测试在日志里标为 `SLOW`,跑过 10 分钟的被终止并记为 `TIMEOUT`;`verify` 拆成可并行的两半;Cargo 缓存只从 `main` 保存(PR 的缓存只有同一个 PR 能用,而仓库 10 GB 的上限会把 `main` 的挤掉),不缓存 `~/.cargo/bin`;dev / test 构建只留行号表(`CARGO_PROFILE_DEV_DEBUG=line-tables-only`);清理预装 SDK 只在剩余空间不足 40 GB 时做(`.github/scripts/free-disk-space.sh`,托管 runner 实测开跑时有 86 GB);CI 的 Windows 包复用按全部输入(锁文件、清单、`about.toml`、生成脚本与许可证文本)缓存的第三方声明,省掉约两分钟的 cargo-about(`VOLTIP_NOTICES_CACHED=1`,发布总是重新生成);下载失败重试(`CARGO_NET_RETRY`、`RUSTUP_MAX_RETRIES`);桌面壳的 lib 只编成 `rlib`(2026-10-05):Tauri 模板为手机准备的 `staticlib` 和 `cdylib` 让 release 构建的每个依赖都同时生成机器码和 bitcode,去掉后依赖只生成 thin LTO 用的 bitcode(`-C linker-plugin-lto`),本机 32 核冷构建 224 → 156 秒,target/ 3.3 → 2.4 GB;macOS 腿的钥匙串测试程序是带 `keychain-harness` feature 的 bin 而不是 example(example 会带上 dev-dependencies,它们的 feature 让应用构建的 60 多个 crate 重编一遍,Intel Mac 冷跑时 11 分钟),应用构建之后只需再编这个包本身;voltip-server 的 release 构建单独用 16 个代码生成单元(2026-11-10,根 `Cargo.toml`):只有一个单元时整个 crate 是一个 LLVM 模块,LLVM 优化要 862 秒、ThinLTO 链接 303 秒,候选的 Linux 腿为此等 19–34 分钟;拆开后 4 个并行任务下分别是 2 秒和 26 秒,其余包仍是一个单元。`verify-*.log` 里每道门都记了耗时(`secs=`)。
- `ci.yml` 的 `macos` job(2026-09-28 从 `macos.yml` 并入):推送 `main` 或手动运行时在 `macos-15`(Apple 芯片)与 `macos-15-intel`(Intel)上构建 ad-hoc 签名的 `.app` 和 `.dmg`(发布包用固定证书,见下面「macOS 签名」),用 `.github/scripts/check-macos-bundle.sh` 检查,并确认发布用的检查会拒绝这个 ad-hoc 包,跑 macOS 单元测试、真实的事件 tap(仅 Apple 芯片,先在 TCC 里给测试程序授予辅助功能)、无头识别,以及桌面上的 `scripts/smoke-tray-macos.sh`(标题栏左端的内容离红绿灯至少 12 pt,菜单栏图标、菜单与各项操作,截图随产物保存);不跑在 pull request 上。在 push 和手动运行时 `CI Success` 等它(规则在 `.github/scripts/ci-success.sh`,只有 pull request 上允许它被跳过),所以 `main` 上任何一台 Mac 失败,下一个候选都过不了 `source-gate`;Cargo 缓存从 `main` 保存。
- release-please 的 PR(作者 `github-actions[bot]`、分支 `release-please--branches--main--*`、标签 `autorelease: pending`)和它合并到 `main` 的发布提交只改版本号和 CHANGELOG,只跑 `verify-web`:候选在同一个 head 上构建并检查每个安装包,而 head 的基点已经通过 CI(`.github/scripts/changed-code.sh`)。同样的改动换个作者或没有标签,照常跑完整 CI。
- 发布是「构建一次再晋升」(2026-09-28 用户选定):安装包在发布 PR 上构建、签名、证明并封存,合并后只校验和发布,不再编译。
Expand Down
Loading