mirror of
https://github.com/farion1231/cc-switch.git
synced 2026-10-26 01:34:51 +08:00
Windows SQLite cannot reliably read a live Hermes WAL database through a WSL UNC path. For wsl.localhost / wsl$ Hermes paths only, read the database inside the owning distribution via wsl.exe and Python SQLite (mode=ro, query_only, one short read transaction), streaming the result back in bounded JSON frames into the existing session projection. Non-WSL paths and macOS/Linux are unchanged. Requires python3 (3.7+) and timeout inside the distribution. Fixes #8205.
301 lines
11 KiB
YAML
301 lines
11 KiB
YAML
name: CI
|
||
|
||
on:
|
||
pull_request:
|
||
branches: [main]
|
||
push:
|
||
branches: [main]
|
||
# 每周一次全量:push 到 main 也按路径过滤后,靠它在缓存被淘汰后重新建起来
|
||
schedule:
|
||
- cron: "41 19 * * 0" # 周一 03:41(北京时间)
|
||
workflow_dispatch:
|
||
|
||
concurrency:
|
||
# 带上事件名:定时全量和 push 到 main 都在 refs/heads/main 上,别互相取消
|
||
group: ci-${{ github.event_name }}-${{ github.ref }}
|
||
cancel-in-progress: true
|
||
|
||
jobs:
|
||
changes:
|
||
name: Detect changed areas
|
||
runs-on: ubuntu-latest
|
||
permissions:
|
||
contents: read
|
||
pull-requests: read
|
||
outputs:
|
||
# 定时 / 手动运行不过滤(过滤步骤被跳过、输出为空),一律当作两边都有改动
|
||
frontend: ${{ steps.filter.outputs.frontend || 'true' }}
|
||
backend: ${{ steps.filter.outputs.backend || 'true' }}
|
||
steps:
|
||
# On pull_request the filter reads changed files via the API (no checkout);
|
||
# on push it needs a local git history to diff against.
|
||
- name: Checkout
|
||
if: github.event_name == 'push'
|
||
uses: actions/checkout@v6
|
||
|
||
- name: Detect changed paths
|
||
id: filter
|
||
if: github.event_name == 'pull_request' || github.event_name == 'push'
|
||
uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3
|
||
with:
|
||
filters: |
|
||
frontend:
|
||
- "src/**"
|
||
- "tests/**"
|
||
- "index.html"
|
||
- "package.json"
|
||
- "pnpm-lock.yaml"
|
||
- "pnpm-workspace.yaml"
|
||
- "tsconfig.json"
|
||
- "tsconfig.node.json"
|
||
- "vite.config.ts"
|
||
- "vitest.config.ts"
|
||
- "tailwind.config.cjs"
|
||
- "postcss.config.cjs"
|
||
- "components.json"
|
||
- ".github/workflows/**"
|
||
backend:
|
||
- "src-tauri/**"
|
||
- "rust-toolchain.toml"
|
||
- ".github/workflows/**"
|
||
|
||
frontend:
|
||
name: Frontend Checks
|
||
runs-on: ubuntu-latest
|
||
needs: changes
|
||
# PR 和 push 到 main 都按路径过滤。缓存 key 精确命中时不会重存,main 全量跑并不能
|
||
# 预热什么;缓存被淘汰后由每周的定时全量重建。
|
||
if: needs.changes.outputs.frontend == 'true'
|
||
steps:
|
||
- name: Checkout
|
||
uses: actions/checkout@v6
|
||
|
||
- name: Setup Node.js
|
||
uses: actions/setup-node@v6
|
||
with:
|
||
node-version: "20"
|
||
|
||
- name: Setup pnpm
|
||
env:
|
||
COREPACK_ENABLE_DOWNLOAD_PROMPT: "0"
|
||
run: |
|
||
corepack enable
|
||
corepack install
|
||
pnpm --version
|
||
|
||
- name: Get pnpm store directory
|
||
id: pnpm-store
|
||
shell: bash
|
||
run: echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
|
||
|
||
- name: Cache pnpm store
|
||
uses: actions/cache@v5
|
||
with:
|
||
path: ${{ steps.pnpm-store.outputs.path }}
|
||
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
||
restore-keys: ${{ runner.os }}-pnpm-store-
|
||
|
||
- name: Install dependencies
|
||
run: pnpm install --frozen-lockfile
|
||
|
||
- name: TypeScript type check
|
||
run: pnpm typecheck
|
||
|
||
- name: Check formatting
|
||
run: pnpm format:check
|
||
|
||
- name: Unit tests
|
||
run: pnpm test:unit
|
||
|
||
- name: Build frontend
|
||
run: pnpm build:renderer
|
||
|
||
backend:
|
||
name: Backend Checks
|
||
runs-on: ${{ matrix.os }}
|
||
needs: changes
|
||
if: needs.changes.outputs.backend == 'true'
|
||
strategy:
|
||
fail-fast: false
|
||
matrix:
|
||
os: [ubuntu-22.04, windows-latest, macos-latest]
|
||
steps:
|
||
- name: Checkout
|
||
uses: actions/checkout@v6
|
||
|
||
# rust-toolchain.toml 是编译器版本的唯一来源:直接装它,免得先装 stable 再被 rustup 换掉
|
||
- name: Read pinned Rust toolchain
|
||
id: rust-toolchain
|
||
shell: bash
|
||
run: |
|
||
channel=$(sed -nE 's/^channel *= *"([^"]+)".*/\1/p' rust-toolchain.toml)
|
||
test -n "$channel"
|
||
echo "channel=$channel" >> "$GITHUB_OUTPUT"
|
||
|
||
- name: Setup Rust
|
||
uses: dtolnay/rust-toolchain@stable
|
||
with:
|
||
toolchain: ${{ steps.rust-toolchain.outputs.channel }}
|
||
components: rustfmt, clippy
|
||
|
||
- name: Install Linux system deps
|
||
if: runner.os == 'Linux'
|
||
run: |
|
||
sudo apt-get update
|
||
sudo apt-get install -y --no-install-recommends \
|
||
build-essential pkg-config libssl-dev \
|
||
libgtk-3-dev librsvg2-dev libayatana-appindicator3-dev
|
||
sudo apt-get install -y --no-install-recommends libwebkit2gtk-4.1-dev \
|
||
|| sudo apt-get install -y --no-install-recommends libwebkit2gtk-4.0-dev
|
||
sudo apt-get install -y --no-install-recommends libsoup-3.0-dev \
|
||
|| sudo apt-get install -y --no-install-recommends libsoup2.4-dev
|
||
|
||
# 只缓存依赖:本仓库的 crate 每次都要重编,缓存它只会让缓存变大、恢复变慢。
|
||
# key 自带 OS、rustc 版本和 Cargo.lock;shared-key 让 WSL2 job 能读到 Windows 这份。
|
||
- name: Cache Rust dependencies
|
||
uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||
with:
|
||
workspaces: src-tauri -> target
|
||
shared-key: backend
|
||
|
||
- name: Install cargo-nextest
|
||
uses: taiki-e/install-action@e407f7bafb71fd004bc5c2da3032e5470cbb6ef0 # v2.87.24
|
||
with:
|
||
tool: cargo-nextest
|
||
|
||
- name: Create frontend dist placeholder
|
||
shell: bash
|
||
run: mkdir -p dist
|
||
|
||
- name: Check Rust formatting
|
||
run: cargo fmt --check --manifest-path src-tauri/Cargo.toml
|
||
|
||
- name: Clippy
|
||
run: cargo clippy --manifest-path src-tauri/Cargo.toml -- -D warnings
|
||
|
||
# 每个测试一个进程,单测超时见 src-tauri/.config/nextest.toml。doctest 目前只有两个
|
||
# ignored,nextest 不跑 doctest 也不漏。
|
||
- name: Run tests
|
||
run: cargo nextest run --manifest-path src-tauri/Cargo.toml --profile ci
|
||
|
||
backend-windows-wsl2:
|
||
name: Backend Checks (Windows + WSL2 home)
|
||
runs-on: windows-2025
|
||
timeout-minutes: 45
|
||
needs: changes
|
||
if: needs.changes.outputs.backend == 'true'
|
||
permissions:
|
||
contents: read
|
||
steps:
|
||
- name: Checkout
|
||
uses: actions/checkout@v6
|
||
|
||
- name: Read pinned Rust toolchain
|
||
id: rust-toolchain
|
||
shell: bash
|
||
run: |
|
||
channel=$(sed -nE 's/^channel *= *"([^"]+)".*/\1/p' rust-toolchain.toml)
|
||
test -n "$channel"
|
||
echo "channel=$channel" >> "$GITHUB_OUTPUT"
|
||
|
||
- name: Setup Rust
|
||
uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
|
||
with:
|
||
toolchain: ${{ steps.rust-toolchain.outputs.channel }}
|
||
|
||
- name: Setup WSL2
|
||
uses: Vampire/setup-wsl@d1da7f2c0322a5ee4f24975344f67fc0f5baf364 # v7
|
||
with:
|
||
distribution: Ubuntu-24.04
|
||
wsl-version: 2
|
||
additional-packages: python3
|
||
|
||
# 只恢复不保存:这里只编 lib 测试,若由它先存,同 key 的 Windows 缓存就只剩一小半。
|
||
# 完整缓存由 backend 矩阵的 Windows job 保存(同一个 shared-key)。
|
||
- name: Restore Rust dependencies
|
||
uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
|
||
with:
|
||
workspaces: src-tauri -> target
|
||
shared-key: backend
|
||
save-if: false
|
||
|
||
- name: Create frontend dist placeholder
|
||
shell: bash
|
||
run: mkdir -p dist
|
||
|
||
- name: Prepare WSL2-backed test environment
|
||
shell: pwsh
|
||
run: |
|
||
wsl.exe -d Ubuntu-24.04 -- bash -lc "rm -rf /tmp/cc-switch-windows-test-root && mkdir -p /tmp/cc-switch-windows-test-root/{home,temp} && chmod -R 0777 /tmp/cc-switch-windows-test-root"
|
||
$testRoot = "\\wsl.localhost\Ubuntu-24.04\tmp\cc-switch-windows-test-root"
|
||
$testHome = Join-Path $testRoot "home"
|
||
$testTemp = Join-Path $testRoot "temp"
|
||
foreach ($path in @($testRoot, $testHome, $testTemp)) {
|
||
if (-not (Test-Path -LiteralPath $path)) {
|
||
throw "WSL2-backed test path is unavailable: $path"
|
||
}
|
||
}
|
||
"CC_SWITCH_TEST_HOME=$testHome" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
|
||
"CC_SWITCH_WSL_TEST_DIR=$testRoot" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
|
||
"CC_SWITCH_WSL_TEST_TEMP=$testTemp" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
|
||
|
||
- name: Compile backend tests with native temp
|
||
# link.exe/mt.exe cannot create manifests when TEMP points at a WSL UNC path.
|
||
# Record the built test binary path so the run step below can execute
|
||
# it directly instead of re-invoking cargo with TEMP on \\wsl.localhost.
|
||
env:
|
||
TEMP: ${{ runner.temp }}
|
||
TMP: ${{ runner.temp }}
|
||
shell: pwsh
|
||
run: |
|
||
$testBinary = cargo test --lib --manifest-path src-tauri/Cargo.toml --no-run --message-format=json |
|
||
ForEach-Object {
|
||
if ($_ -match '"reason":"compiler-artifact"') {
|
||
$artifact = $_ | ConvertFrom-Json
|
||
if ($artifact.executable -and $artifact.profile.test -and $artifact.target.name -eq 'cc_switch_lib') {
|
||
$artifact.executable
|
||
}
|
||
}
|
||
} | Select-Object -First 1 -Wait
|
||
if ($LASTEXITCODE -ne 0) {
|
||
exit $LASTEXITCODE
|
||
}
|
||
if (-not $testBinary) {
|
||
throw "cc_switch_lib test binary not found in cargo compiler artifacts"
|
||
}
|
||
"CC_SWITCH_TEST_EXE=$testBinary" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
|
||
|
||
- name: Run Windows-to-WSL2 filesystem contract
|
||
# Run the test binary built in the previous step directly. Invoking cargo
|
||
# here with TEMP pointing at a WSL UNC path can re-link the crate, and
|
||
# link.exe/mt.exe cannot create manifest temp files on \\wsl.localhost.
|
||
shell: pwsh
|
||
run: |
|
||
$env:TEMP = $env:CC_SWITCH_WSL_TEST_TEMP
|
||
$env:TMP = $env:CC_SWITCH_WSL_TEST_TEMP
|
||
$testName = "config::tests::atomic_write_replaces_existing_wsl_unc_file"
|
||
$testList = & $env:CC_SWITCH_TEST_EXE --list --ignored
|
||
if ($LASTEXITCODE -ne 0) {
|
||
exit $LASTEXITCODE
|
||
}
|
||
$expected = "${testName}: test"
|
||
if ($testList -notcontains $expected) {
|
||
throw "Expected ignored test was not discovered: $testName"
|
||
}
|
||
& $env:CC_SWITCH_TEST_EXE $testName --ignored --exact --nocapture
|
||
if ($LASTEXITCODE -ne 0) {
|
||
exit $LASTEXITCODE
|
||
}
|
||
|
||
- name: Run Hermes live WAL reads inside WSL2
|
||
env:
|
||
CC_SWITCH_WSL_DISTRO: Ubuntu-24.04
|
||
shell: pwsh
|
||
run: |
|
||
$testName = "session_manager::providers::hermes::tests::wsl2_live_wal_readonly_reference"
|
||
$testList = & $env:CC_SWITCH_TEST_EXE --list --ignored
|
||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||
if ($testList -notcontains "${testName}: test") { throw "Hermes WSL test not discovered" }
|
||
& $env:CC_SWITCH_TEST_EXE $testName --ignored --exact --nocapture
|
||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|