Repository navigation
fix(linux-egui): 修复设置崩溃与选区助手发送和收起 #2311
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |