Files
TRIPandHKLHaoBin 6f9af89583 release: OpenLess 2.0.0-Beta.4+build.20260930 (#1126)
Integrate ASR/Omni fixes, API Route, Android IME and confirmation-based vocabulary learning. Complete settings transitions and macOS single-item credential storage. Synchronize all application versions to 2.0.0-Beta.4+build.20260930.

Integrated PRs: #1116, #1120, #1121, #1123, #1124, #1067 and #1128. Linux #1117 remains independent and is excluded from automatic release.

Validated: full cross-platform CI, 18 UI motion cases, 112 frontend test files, 1245 Core tests (1 ignored) and 572 macOS native tests.

Co-authored-by: HKLHaoBin <we3q@qq.com>
2026-09-30 20:32:53 +08:00

479 lines
20 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
name: CI
# 多平台跨语言质量门禁。release-tauri.yml 是发版流水线(仅打包构建),这里负责
# 在合并前快速验证两件事:
# 1. 全部前端/契约测试与 vite bundle 通过(捕获行为、合同及跨 locale 类型 drift)
# 2. Tauri 后端在 macOS / Windows、移动端在 Android 编译;Linux Core、
# remote_tls、egui 和 deb/rpm 统一使用发版构建链检查(PR 不上传 Release)。
# macOS/Windows/Android 不跑完整 Tauri bundle;Linux 则运行完整 deb/rpm 发版构建校验。
on:
push:
branches: [main, beta]
pull_request:
branches: [main, beta]
workflow_dispatch:
inputs:
platform:
description: Platforms to check (macos runs only macOS gates)
type: choice
options: [all, macos]
default: all
# Cancel old runs when the same PR is pushed repeatedly; workflow_dispatch is isolated by run_id.
concurrency:
group: ${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
# 判定这次改动能不能影响到各平台门禁。只有“改动物理上够不到”的变更才会
# 跳过对应 job:判定脚本对任何未知情况(没有基线、git 失败、空 diff)都返回
# true。push / tag / 手动运行不带基线,因此始终全量验证。
changes:
name: Changed areas
runs-on: ubuntu-latest
timeout-minutes: 10
outputs:
tauri: ${{ steps.detect.outputs.tauri }}
msrv: ${{ steps.detect.outputs.msrv }}
linux: ${{ steps.detect.outputs.linux }}
steps:
- uses: actions/checkout@v4
with:
# 需要基线提交才能算出差异,客户端不再重复 fetch。
fetch-depth: 0
- name: Detect changed areas
id: detect
working-directory: openless-all/app
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
run: |
set -euo pipefail
for area in tauri msrv linux; do
verdict=$(bash scripts/ci-changed-areas.sh "$area" "$BASE_SHA")
# 失败即运行:脚本读不到差异时返回 true,这里也只接受明确的 false。
echo "$area=$( [ "$verdict" = 'false' ] && echo false || echo true )" >> "$GITHUB_OUTPUT"
done
ui-motion:
name: UI motion (Chromium + WebKit)
needs: [changes]
if: (github.event_name != 'workflow_dispatch' || inputs.platform != 'macos') && needs.changes.outputs.tauri != 'false'
runs-on: ubuntu-24.04
timeout-minutes: 20
defaults:
run:
working-directory: openless-all/app
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '22'
cache: npm
cache-dependency-path: openless-all/app/package-lock.json
- run: npm ci
- run: npm run build
- run: npx playwright install --with-deps chromium webkit
- run: npm run test:ui-motion
- uses: actions/upload-artifact@v4
if: failure()
with:
name: ui-motion-failure
path: |
openless-all/app/test-results/
openless-all/app/playwright-report/
android-check:
needs: [changes]
if: (github.event_name != 'workflow_dispatch' || inputs.platform != 'macos') && needs.changes.outputs.tauri != 'false'
name: Android cargo check
runs-on: ubuntu-latest
timeout-minutes: 60
defaults:
run:
working-directory: openless-all/app
steps:
- uses: actions/checkout@v4
with:
# Android uses no desktop local-ASR submodules; the macOS MLX dependency stays out of
# this job.
submodules: false
# Android compiles no local-ASR C code: build.rs excludes the Android triple by TARGET
# (does not call build_qwen_asr). qwen3-asr-rs is a path dependency, and the Cargo
# resolver must read its manifest for all targets — so the ci-disable-macos-qwen3.mjs
# step below removes that dependency line, otherwise cargo check hard-fails.
# Dropping recursive fetch saves one network fetch and one failure point.
- name: Setup Android SDK + NDK
uses: android-actions/setup-android@v3
with:
packages: platform-tools
- name: Install NDK
shell: bash
run: |
set -euo pipefail
sdkmanager "ndk;26.1.10909125" "platforms;android-36" "build-tools;36.0.0"
ndk_dir="$ANDROID_HOME/ndk/26.1.10909125"
echo "ANDROID_NDK_HOME=$ndk_dir" >> "$GITHUB_ENV"
echo "NDK_HOME=$ndk_dir" >> "$GITHUB_ENV"
set +o pipefail
yes | sdkmanager --licenses
set -o pipefail
- name: Configure NDK toolchain for cargo
shell: bash
run: |
set -euo pipefail
prebuilt="$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64"
echo "CC_aarch64_linux_android=$prebuilt/bin/aarch64-linux-android24-clang" >> "$GITHUB_ENV"
echo "CXX_aarch64_linux_android=$prebuilt/bin/aarch64-linux-android24-clang++" >> "$GITHUB_ENV"
echo "AR_aarch64_linux_android=$prebuilt/bin/llvm-ar" >> "$GITHUB_ENV"
echo "CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER=$prebuilt/bin/aarch64-linux-android24-clang" >> "$GITHUB_ENV"
echo "CC_x86_64_linux_android=$prebuilt/bin/x86_64-linux-android24-clang" >> "$GITHUB_ENV"
echo "CXX_x86_64_linux_android=$prebuilt/bin/x86_64-linux-android24-clang++" >> "$GITHUB_ENV"
echo "AR_x86_64_linux_android=$prebuilt/bin/llvm-ar" >> "$GITHUB_ENV"
echo "CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER=$prebuilt/bin/x86_64-linux-android24-clang" >> "$GITHUB_ENV"
- uses: actions/setup-node@v4
with:
node-version: "22"
cache: npm
cache-dependency-path: openless-all/app/package-lock.json
- uses: actions/setup-java@v4
with:
distribution: zulu
java-version: "17"
- uses: dtolnay/rust-toolchain@stable
with:
targets: aarch64-linux-android,x86_64-linux-android
- uses: swatinem/rust-cache@v2
with:
workspaces: 'openless-all/app/src-tauri -> target'
# 仓库缓存上限 10 GB,超出后按 LRU 淘汰。过去每个 PR 都会各存一份
# ~1.6 GB 的依赖树,把其他 job 的缓存挤掉,导致每次运行都从零编译。
# 现在 PR 只从基线分支恢复,只有分支推送和 tag 才写入缓存。
save-if: ${{ github.event_name != 'pull_request' }}
- uses: gradle/actions/setup-gradle@v4
- name: Install Linux check dependencies
run: |
sudo apt-get update
sudo apt-get install -y build-essential curl file libssl-dev wget
- name: Install frontend dependencies
run: npm ci
- name: Build frontend (tsc + vite)
# generate_context! requires frontendDist (../dist) to exist on Android too.
run: npm run build
- name: Check Android updater pubkey matches tauri.conf.json
run: npm run check:android-updater-pubkey
- name: Disable macOS-only Qwen3 MLX dependency
run: node scripts/ci-disable-macos-qwen3.mjs
- name: Check Tauri backend (Android target)
run: cargo check --locked --manifest-path src-tauri/Cargo.toml --target aarch64-linux-android
- name: Initialize generated Android project
run: npm run tauri -- android init --ci
- name: Generate Tauri Gradle dependency scripts
env:
TAURI_ANDROID_PROJECT_PATH: ${{ github.workspace }}/openless-all/app/src-tauri/gen/android
TAURI_ANDROID_PACKAGE_UNESCAPED: com.openless.app
WRY_ANDROID_LIBRARY: openless_lib
WRY_ANDROID_KOTLIN_FILES_OUT_DIR: ${{ github.workspace }}/openless-all/app/src-tauri/gen/android/app/src/main/java/com/openless/app/generated
WRY_ANDROID_PACKAGE: com.openless.app
run: |
mkdir -p "$WRY_ANDROID_KOTLIN_FILES_OUT_DIR"
cargo check --locked --manifest-path src-tauri/Cargo.toml --target x86_64-linux-android
- name: Copy Android production and test scaffolding
run: node scripts/copy-android-scaffolding.mjs
- name: Merge Shizuku manifest
run: node scripts/merge-android-shizuku-manifest.mjs
- name: Patch Shizuku Gradle dependencies
run: node scripts/patch-android-shizuku-deps.mjs
- name: Run JVM credential tests and compile instrumentation tests
# These tests are Kotlin-only. The Rust target is checked above; direct Gradle
# rustBuild requires the live Tauri CLI RPC server used by `tauri android build`.
run: >-
src-tauri/gen/android/gradlew
--project-dir src-tauri/gen/android
:app:testX86_64DebugUnitTest
:app:assembleX86_64DebugAndroidTest
-x :app:rustBuildX86_64Debug
--no-daemon
- name: Enable KVM for Android instrumentation tests
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' \
| sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- name: Run Android Keystore instrumentation tests
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: 35
target: google_apis
arch: x86_64
# The instrumentation suite exercises AndroidKeyStore without launching Tauri.
script: >-
cd openless-all/app &&
src-tauri/gen/android/gradlew
--project-dir src-tauri/gen/android
:app:connectedX86_64DebugAndroidTest
-x :app:rustBuildX86_64Debug
--no-daemon
# The Linux core/egui/deb+rpm checks live in a reusable workflow, which runs
# the same `cargo test -p openless-core` as upstream's `linux-core-contract`
# plus the Linux host, fcitx5 and package contracts.
linux-egui-package:
needs: [changes]
if: github.event_name != 'workflow_dispatch' || inputs.platform != 'macos'
name: Linux Core, egui and deb/rpm checks
permissions:
contents: read
uses: ./.github/workflows/check-linux-egui.yml
with:
# 散文改动跳过整条 Linux 构建与打包链;输出缺失/未知时退回 full。
scope: ${{ needs.changes.outputs.linux == 'false' && 'none' || 'full' }}
cross-platform:
needs: [changes]
# 失败即运行:只有判定脚本明确说“够不到”才跳过。
if: needs.changes.outputs.tauri != 'false'
name: ${{ matrix.label }} checks
strategy:
# One platform failing must not block other platforms from getting verification results.
fail-fast: false
matrix:
include: ${{ fromJSON(github.event_name == 'workflow_dispatch' && inputs.platform == 'macos' && '[{"os":"macos-latest","label":"macOS","preflight":false}]' || '[{"os":"macos-latest","label":"macOS","preflight":false},{"os":"windows-latest","label":"Windows","preflight":true}]') }}
runs-on: ${{ matrix.os }}
timeout-minutes: 60
env:
# With the shared Core in place, macOS's first build compiles the MLX C++ dependencies and
# the full Tauri test binary together; limit concurrency so the arm64 runner is not OOM-killed
# at peak memory.
CARGO_BUILD_JOBS: 2
CARGO_PROFILE_TEST_DEBUG: 0
CMAKE_BUILD_PARALLEL_LEVEL: 2
defaults:
run:
working-directory: openless-all/app
steps:
- uses: actions/checkout@v4
with:
# Initialize the MLX submodule on macOS only; Windows does not resolve that dependency.
submodules: ${{ matrix.os == 'macos-latest' && 'recursive' || 'false' }}
- name: Disable macOS-only Qwen3 MLX dependency
if: runner.os != 'macOS'
run: node scripts/ci-disable-macos-qwen3.mjs
- uses: actions/setup-node@v4
with:
node-version: "22"
cache: npm
cache-dependency-path: openless-all/app/package-lock.json
# macOS MSRV is checked in a separate parallel job; Windows keeps the original check order.
- uses: dtolnay/rust-toolchain@1.88.0
if: runner.os == 'Windows'
- uses: dtolnay/rust-toolchain@stable
- name: Configure macOS check profile
if: runner.os == 'macOS'
run: echo "CARGO_PROFILE_DEV_DEBUG=0" >> "$GITHUB_ENV"
- uses: swatinem/rust-cache@v2
if: runner.os == 'Windows'
with:
workspaces: 'openless-all/app/src-tauri -> target'
save-if: ${{ github.event_name != 'pull_request' }}
- name: Cache macOS stable dependencies
if: runner.os == 'macOS'
uses: swatinem/rust-cache@v2
with:
key: macos-stable-v1
save-if: ${{ github.event_name != 'pull_request' }}
workspaces: |
openless-all/app -> target
openless-all/app/src-tauri -> target
- name: Cache macOS MLX native build
if: runner.os == 'macOS' && runner.arch == 'ARM64'
uses: ./.github/actions/cache-macos-mlx
with:
profile: debug
- name: Prepare Windows Sherpa static libraries
if: runner.os == 'Windows'
shell: pwsh
run: ./scripts/prepare-windows-sherpa.ps1
- name: Install frontend dependencies
run: npm ci
- name: Check Windows prerequisites
if: matrix.preflight
shell: pwsh
run: ./scripts/windows-preflight.ps1 -Toolchain msvc
- name: Check PowerShell scripts
if: matrix.preflight
shell: pwsh
run: |
foreach ($script in @("./scripts/windows-preflight.ps1", "./scripts/windows-build-gnu.ps1", "./scripts/windows-runtime-smoke.ps1", "./scripts/prepare-windows-sherpa.ps1")) {
$errors = $null
[System.Management.Automation.PSParser]::Tokenize((Get-Content -Raw $script), [ref]$errors) | Out-Null
if ($errors) {
$errors | Format-List
exit 1
}
}
- name: Build frontend and run frontend/contract tests
run: npm test
- name: Install pinned Codex CLI for sandbox contract
if: runner.os == 'macOS'
run: npm install --global @openai/codex@0.146.0
- name: Configure isolated Codex home
if: runner.os == 'macOS'
shell: bash
run: |
set -euo pipefail
codex_home="$RUNNER_TEMP/openless-codex-home"
mkdir -p "$codex_home"
echo "CODEX_HOME=$codex_home" >> "$GITHUB_ENV"
- name: Run Codex writable-roots sandbox contract
if: runner.os == 'macOS'
env:
OPENLESS_CODEX_TEST_EXE: codex
run: cargo test --locked -p openless-core hardening_actually_narrows_the_writable_roots -- --ignored --nocapture --test-threads=1
- name: Check Tauri backend (cargo check)
if: runner.os == 'Windows'
run: cargo check --locked --manifest-path src-tauri/Cargo.toml
# test compiles and runs lib/bin, covering the production binary paths that plain cargo
# check misses. Avoids a metadata-only check followed by another codegen pass over the same
# dependency graph.
- name: Run Rust backend unit tests
if: runner.os != 'Windows'
run: cargo test --locked --manifest-path src-tauri/Cargo.toml --lib --bins --timings
- name: Compile Rust backend unit tests (Windows)
# A Windows runner can link the lib test binary, but on a clean image lacking an optional
# native runtime DLL entrypoint, the process exits before the test harness starts. Keep
# the cfg/link coverage here; the shared Core's public compatibility contract is actually
# executed in the next step.
if: runner.os == 'Windows'
run: cargo test --locked --manifest-path src-tauri/Cargo.toml --lib --no-run
- name: Run Rust-only backend unit tests (Windows)
# The standalone test crate does not link the full Tauri app lib; it verifies the public
# Core contract only. Windows-specific Tauri tests on this runner only get the cfg/link
# compile coverage from the step above.
if: runner.os == 'Windows'
run: cargo test --locked --manifest-path src-tauri/backend-tests/Cargo.toml
- name: Check Tauri backend with Rust 1.88 MSRV
if: runner.os == 'Windows'
run: cargo +1.88.0 check --locked --manifest-path src-tauri/Cargo.toml
- name: Compile backend tests with Rust 1.88 MSRV
if: runner.os == 'Windows'
run: cargo +1.88.0 test --locked --manifest-path src-tauri/backend-tests/Cargo.toml --no-run
- name: Verify version sync across all 5 files
# Runs on both platforms: the Windows runner ships git-bash, giving consistent behavior
# across shells. Fail immediately on version drift instead of discovering it at release.
# Checks 5 places: package.json / package-lock.json (root + nested) /
# tauri.conf.json / Cargo.toml / the [openless] package in Cargo.lock.
shell: bash
run: |
PKG=$(node -p "require('./package.json').version")
LOCK_ROOT=$(node -p "require('./package-lock.json').version")
LOCK_NESTED=$(node -p "require('./package-lock.json').packages[''].version")
TAU=$(node -p "require('./src-tauri/tauri.conf.json').version")
CRG=$(grep -E '^version = ' src-tauri/Cargo.toml | head -1 | sed -E 's/^version = "(.+)"$/\1/')
# Cargo.lock: find the version line right after the [openless] package name
CARGO_LOCK_VER=$(awk 'BEGIN{found=0} /^name = "openless"$/{found=1; next} found && /^version = /{gsub(/"/,""); print $3; exit}' src-tauri/Cargo.lock)
echo "package.json = $PKG"
echo "package-lock root = $LOCK_ROOT"
echo "package-lock nested = $LOCK_NESTED"
echo "tauri.conf.json = $TAU"
echo "Cargo.toml = $CRG"
echo "Cargo.lock openless = $CARGO_LOCK_VER"
mismatch=0
for v in "$LOCK_ROOT" "$LOCK_NESTED" "$TAU" "$CRG" "$CARGO_LOCK_VER"; do
if [ "$v" != "$PKG" ]; then mismatch=1; fi
done
if [ "$mismatch" -ne 0 ]; then
echo "::error::版本号未对齐 — 请用 scripts/bump-version.sh 同步更新"
exit 1
fi
echo "[ok] 全部 5 处版本号一致:$PKG"
macos-msrv:
needs: [changes]
# 失败即运行:只有判定脚本明确说“够不到”才跳过。
if: needs.changes.outputs.msrv != 'false'
name: macOS Rust 1.88 MSRV
runs-on: macos-latest
timeout-minutes: 60
env:
CARGO_BUILD_JOBS: 2
CARGO_PROFILE_DEV_DEBUG: 0
CARGO_PROFILE_TEST_DEBUG: 0
CMAKE_BUILD_PARALLEL_LEVEL: 2
defaults:
run:
working-directory: openless-all/app
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: actions/setup-node@v4
with:
node-version: "22"
cache: npm
cache-dependency-path: openless-all/app/package-lock.json
- uses: dtolnay/rust-toolchain@1.88.0
- uses: swatinem/rust-cache@v2
with:
key: macos-msrv-v1
save-if: ${{ github.event_name != 'pull_request' }}
workspaces: |
openless-all/app/src-tauri -> target
openless-all/app/src-tauri/backend-tests -> target
- name: Cache macOS MLX native build
if: runner.arch == 'ARM64'
uses: ./.github/actions/cache-macos-mlx
with:
profile: debug
- run: npm ci
- run: npm run build
- name: Check Tauri backend with Rust 1.88 MSRV
run: cargo +1.88.0 check --locked --manifest-path src-tauri/Cargo.toml --timings
- name: Compile backend tests with Rust 1.88 MSRV
run: cargo +1.88.0 test --locked --manifest-path src-tauri/backend-tests/Cargo.toml --no-run --timings