Files
openless/.github/workflows/ci.yml
T
Chris233 fc38edd1a3 refactor: consolidate Core 2.0 and restore cross-platform behavior (#1019)
* refactor: split Linux egui host from shared backend

* fix: keep CI contracts portable across runners

* fix(ci): preserve CRLF while removing qwen dependency

* fix(ci): regenerate target lockfile and install ripgrep

* fix(ci): gate desktop-only selection voice module

* ci: limit native build parallelism on cross-platform runners

* fix(host): expose blocking task seam to coordinator

* fix(settings): avoid macOS default hotkey collision

* docs: record cross-platform CI evidence

* ci: expose Linux validation package artifact

* ci: grant reusable Linux package permissions

* refactor: remove legacy coordinator dictation facade

* ci: skip unsupported MSI prerelease builds

* ci: install minisign only for signed Linux releases

* fix: keep test-only coordinator session helper

* refactor: move style preview diagnostics into core

* fix: import shared preferences type from core

* refactor: centralize enabled vocabulary projection

* ci: install ripgrep for Linux package gates

* test: make source contract line-ending agnostic

* fix: keep Linux desktop entry standards compliant

* docs: expose core style diagnostics contract

* ci: resolve Linux package version in app directory

* refactor: centralize ASR vocabulary priority

* fix: use shared vocabulary note constant

* refactor: keep vocabulary priority inside core

* test: retain starting-stop queue coverage

* ci: scope Linux updater URL to the current repository

* docs: close implementation and artifact evidence

* docs: record final CI artifact evidence

* fix(android): align updater plugin versions

* fix(android): pin updater crate exactly

* refactor(core): remove unused type anchor

* docs: record final artifact runs

* fix(remote-input): preserve beta status fields

* fix(tauri): finish beta host merge

* fix(asr): pass scoped operation to native qwen adapter

* fix(macos): bundle mlx metallib as a resource

* test(remote-input): update status wire contract

* feat: share Less Computer voice flow with Linux

* refactor(core): centralize host-independent runtime policy

* refactor(core): complete 2.0 backend consolidation

* fix(ci): prepare Sherpa after cache restore

* fix(macos): route native model paths through Core store

* fix(macos): use ModelStore catalog API

* fix(ci): serialize macOS native test build

* test(ci): preserve macOS cargo diagnostics

* fix(macos): release input source lock before await

* test(core): replace legacy hotkey injection gate

* test(core): move Codex sandbox contract into Core

* fix(core): close final runtime policy gaps

* fix(windows): terminate coding agent process trees

* fix(core): complete local model migration

* fix(credentials): align Core LLM model account

* fix(core): restore 1.x parity across shared backend

* fix(android): complete shared backend stubs

* fix: close cross-platform migration review gaps

* fix: complete migration lifecycles and platform compatibility

* docs: define desktop 2.0 acceptance and Linux handoff

* fix: close desktop parity and session lifecycle gaps

* fix: finish voice lifecycle and desktop feedback parity

* fix: close desktop cancellation and feedback race gaps

* fix: honor proxy settings and reject stale session effects

* fix: dispatch native cleanup on host runtimes and order events

* fix: keep optional recording effects best effort

* fix: close desktop agent model and QA lifecycle gaps

* fix: preserve desktop raw voice and QA turn ownership

* fix: finalize desktop dictation and QA feedback
2026-09-06 18:39:46 +08:00

389 lines
15 KiB
YAML
Raw 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 与 egui host。
# 跑 build-mac.sh / windows-package-msvc.ps1 / Tauri bundle 太重;只跑轻量 cargo check + vite build。
on:
push:
branches: [main, beta]
pull_request:
branches: [main, beta]
workflow_dispatch:
inputs:
upload_linux_validation_artifact:
description: Build and upload the Linux egui validation packages (never a release)
required: false
default: false
type: boolean
# 同一 PR 快速重复推送时取消旧运行;workflow_dispatch 用 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:
android-check:
name: Android cargo check
runs-on: ubuntu-latest
defaults:
run:
working-directory: openless-all/app
steps:
- uses: actions/checkout@v4
with:
# Android 不使用任何桌面本地 ASR 子模块;macOS MLX 依赖不进入此任务。
submodules: false
# Android 不编译任何本地 ASR C 代码:build.rs 按 TARGET 解析排除
# Android triple(不调 build_qwen_asr)。qwen3-asr-rs 是 path 依赖,
# Cargo 解析器跨所有 target 都要读它的 manifest——所以由下方的
# ci-disable-macos-qwen3.mjs 删掉该依赖行,否则 cargo check 硬失败。
# 去掉递归拉取省一次网络 fetch + 失败点。
- 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'
- 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
linux-core-contract:
name: Linux core and egui host
runs-on: ubuntu-22.04
defaults:
run:
working-directory: openless-all/app
steps:
- uses: actions/checkout@v4
with:
submodules: false
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- uses: swatinem/rust-cache@v2
with:
workspaces: 'openless-all/app -> target'
- name: Install Linux native dependencies without WebKitGTK
run: |
sudo apt-get update
sudo apt-get install -y \
build-essential \
libasound2-dev \
libdbus-1-dev \
libssl-dev \
libwayland-dev \
libx11-dev \
libxkbcommon-dev \
ripgrep \
pkg-config
- name: Test and lint shared backend
run: |
cargo test --locked -p openless-core
cargo clippy --locked -p openless-core --all-targets -- -D warnings
- name: Test Linux host and UI contract
run: |
cargo test --locked -p openless-linux-egui --all-targets
cargo check --locked -p openless-linux-egui --all-targets
- name: Verify dependency direction, compatibility baseline, and secret surfaces
shell: pwsh
run: |
./scripts/check-core-deps.ps1
./scripts/check-core-deps.ps1 openless-linux-egui
./scripts/check-command-event-baseline.ps1
./scripts/check-core-secret-surface.ps1
./scripts/check-core-test-isolation.ps1
./scripts/check-core-runtime-seam.ps1
./scripts/check-linux-public-surface.ps1
linux-egui-validation-artifact:
name: Linux egui validation artifact
if: github.event_name == 'workflow_dispatch' && inputs.upload_linux_validation_artifact
needs: linux-core-contract
permissions:
contents: write
uses: ./.github/workflows/release-linux-egui.yml
with:
release_tag: ''
secrets: inherit
cross-platform:
name: ${{ matrix.label }} checks
strategy:
# 一个平台挂掉不阻塞其他平台拿到验证结果。
fail-fast: false
matrix:
include:
- os: macos-latest
label: macOS
preflight: false
- os: windows-latest
label: Windows
preflight: true
runs-on: ${{ matrix.os }}
env:
# 新增 shared Core 后,macOS 首次编译同时构建 MLX C++ 依赖和完整
# Tauri test binary;限制并发避免 arm64 runner 在峰值内存处被系统终止。
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:
# 只在 macOS 初始化 MLX 子模块;Windows 不解析该依赖。
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
# 现有测试继续用 stable;额外安装声明的 MSRV,供下方兼容性门禁显式调用。
- uses: dtolnay/rust-toolchain@1.88.0
- uses: dtolnay/rust-toolchain@stable
- uses: swatinem/rust-cache@v2
with:
workspaces: 'openless-all/app/src-tauri -> target'
- 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)
run: cargo check --locked --manifest-path src-tauri/Cargo.toml
- name: Run Rust backend unit tests
if: runner.os != 'Windows'
run: cargo test --locked --manifest-path src-tauri/Cargo.toml --lib
- name: Compile Rust backend unit tests (Windows)
# Windows runner 能链接 lib test binary,但干净镜像缺少可选 native runtime
# DLL entrypoint 时,进程会在 test harness 启动前退出。这里保留 cfg/link
# 覆盖;共享 Core 的公开 compatibility contract 由下一步实际执行。
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)
# 独立 test crate 不链接完整 Tauri app lib,只验证公开 Core contract。
# Windows 专属 Tauri 测试在此 runner 只做上一步的 cfg/link 编译覆盖。
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
run: cargo +1.88.0 check --locked --manifest-path src-tauri/Cargo.toml
- 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
- name: Verify version sync across all 5 files
# 两个平台都跑这个校验:Windows runner 自带 git-bash,跨 shell 表现一致。
# 一旦版本号 drift 立刻 fail,避免发版时再发现漏改。
# 校验 5 处:package.json / package-lock.json (root + nested) /
# tauri.conf.json / Cargo.toml / Cargo.lock 的 [openless] 包。
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:找 [openless] 包紧跟的 version 行
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"