From 8cd03adacd7283bb11a0724b11d2c13e97c2633e Mon Sep 17 00:00:00 2001 From: Kayshen-X Date: Mon, 6 Jul 2026 22:04:56 +0800 Subject: [PATCH] fix(ci): repair three v0.8.0-new failures (canvaskit test, windows-aarch64 deps, windows nextest) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three independent CI failures, none caused by the font-lock push: 1. CanvasKit web-host test variables_panel_open_does_not_paint_legacy_modal false-tripped: its '> half viewport' heuristic was a proxy for 'no full-viewport modal', but only passed because the property panel used to narrow the canvas and clamp the floating VariablesPanel below half-width. The nothing-selected-by-default change collapsed the property panel, widening the canvas so the legitimate 820x480 panel paints unclamped. There is no legacy modal. Retarget the assertion at a near full-viewport round fill and positively assert the panel paints. 2. windows-aarch64 cross-check failed: op-host-services + op-orchestrator pulled op-mcp's native-only 'script' feature unconditionally, dragging in rquickjs-sys which ships no bindings for aarch64-pc-windows-msvc. Enable 'script' per-target (off on that unshipped check-only arch). 3. Windows workspace tests segfaulted single-threaded (before the first test prints, under --test-threads=1) — an intra-test race a thread flag can't serialize. Switch the Windows runner to cargo-nextest (one process per test) so the crash is isolated + named; doctests run separately. --- .github/workflows/rust-check.yml | 28 +++++++++++++------ .github/workflows/rust-multiplatform.yml | 24 ++++++++++------ crates/op-host-services/Cargo.toml | 12 +++++++- .../src/widget_host/overlay_press_tests.rs | 19 +++++++++++-- crates/op-orchestrator/Cargo.toml | 9 +++++- 5 files changed, 71 insertions(+), 21 deletions(-) diff --git a/.github/workflows/rust-check.yml b/.github/workflows/rust-check.yml index 5b826a9d3..a4345dff9 100644 --- a/.github/workflows/rust-check.yml +++ b/.github/workflows/rust-check.yml @@ -71,19 +71,29 @@ jobs: # EGL, so STEP1A_REQUIRE_GPU is left unset: the op-host-native GPU # smoke (memory_loop) self-uses the raster fallback instead of # hard-failing on EGL init. Real-GPU coverage runs on dev machines. - # Windows runs the whole workspace single-threaded. Many crates' tests - # (op-host-native / -desktop / -services / …) trigger a skia `FontMgr` - # (DirectWrite on Windows) via NativeBackend or SkiaMeasure; concurrent - # DirectWrite init across a test binary's parallel worker threads - # segfaults (STATUS_ACCESS_VIOLATION). `--test-threads=1` serializes each - # binary — cross-binary (separate-process) concurrency is safe, so one - # flag covers every skia-touching crate. macOS / Linux run in parallel. - name: Test workspace (non-Windows, parallel) if: runner.os != 'Windows' run: cargo test --workspace - - name: Test workspace (Windows, serialized — skia/DirectWrite) + # Windows: skia's `FontMgr` is DirectWrite, which segfaults + # (STATUS_ACCESS_VIOLATION) when a test spawns a worker thread doing + # font layout (SkiaMeasure) concurrently with the main thread — an + # intra-test race `--test-threads=1` can NOT serialize (it only + # serializes tests against each other, not threads inside one test). + # nextest runs every test in its OWN process, so such a crash is + # isolated + named instead of taking down the whole test binary, and + # cross-test DirectWrite state can't leak between them. Doctests run + # separately (nextest doesn't execute them). + - name: Install cargo-nextest (Windows) if: runner.os == 'Windows' - run: cargo test --workspace -- --test-threads=1 + uses: taiki-e/install-action@v2 + with: + tool: nextest + - name: Test workspace (Windows, nextest per-process isolation) + if: runner.os == 'Windows' + run: cargo nextest run --workspace + - name: Doctests (Windows) + if: runner.os == 'Windows' + run: cargo test --workspace --doc - name: Run CanvasKit web host tests run: cargo test -p op-host-web --features canvaskit -- --test-threads=1 - run: cargo clippy --workspace --all-targets -- -D warnings diff --git a/.github/workflows/rust-multiplatform.yml b/.github/workflows/rust-multiplatform.yml index f10840ff6..2348aa0d1 100644 --- a/.github/workflows/rust-multiplatform.yml +++ b/.github/workflows/rust-multiplatform.yml @@ -117,15 +117,23 @@ jobs: - name: Test (host, macOS — parallel) if: matrix.cross == false && matrix.check_only != true && runner.os == 'macOS' run: cargo test --workspace --target ${{ matrix.target }} - # Windows runs the whole workspace single-threaded: many crates' tests - # trigger a skia FontMgr (DirectWrite) via NativeBackend / SkiaMeasure, - # and concurrent DirectWrite init across a test binary's parallel worker - # threads segfaults (STATUS_ACCESS_VIOLATION). --test-threads=1 serializes - # each binary; cross-binary (separate-process) concurrency is safe, so - # one flag covers every skia-touching crate. Matches rust-check.yml. - - name: Test (host, Windows — serialized skia/DirectWrite) + # Windows: skia's FontMgr (DirectWrite) segfaults when a test spawns a + # worker thread doing font layout (SkiaMeasure) concurrently with the + # main thread — an intra-test race --test-threads=1 can't serialize. + # nextest runs each test in its own process so such a crash is isolated + # + named and cross-test DirectWrite state can't leak. Doctests run + # separately (nextest doesn't execute them). Matches rust-check.yml. + - name: Install cargo-nextest (Windows) if: matrix.cross == false && matrix.check_only != true && runner.os == 'Windows' - run: cargo test --workspace --target ${{ matrix.target }} -- --test-threads=1 + uses: taiki-e/install-action@v2 + with: + tool: nextest + - name: Test (host, Windows — nextest per-process isolation) + if: matrix.cross == false && matrix.check_only != true && runner.os == 'Windows' + run: cargo nextest run --workspace --target ${{ matrix.target }} + - name: Doctests (host, Windows) + if: matrix.cross == false && matrix.check_only != true && runner.os == 'Windows' + run: cargo test --workspace --target ${{ matrix.target }} --doc # Desktop binary artifact upload is intentionally not wired here yet. # The real desktop binary lives in the `op-host-desktop` crate; a # build + upload step can be added once release packaging is finalized. diff --git a/crates/op-host-services/Cargo.toml b/crates/op-host-services/Cargo.toml index 1bb6c2f65..03d92fbee 100644 --- a/crates/op-host-services/Cargo.toml +++ b/crates/op-host-services/Cargo.toml @@ -22,7 +22,11 @@ mcp-debug-tools = ["op-mcp/debug-tools"] op-editor-core = { path = "../op-editor-core" } op-editor-ui = { path = "../op-editor-ui" } op-editor-host-core = { path = "../op-editor-host-core" } -op-mcp = { path = "../op-mcp", features = ["script"] } +# The native-only `script` feature (QuickJS via rquickjs-sys) is enabled +# per-target below: rquickjs-sys ships no prebuilt bindings for +# aarch64-pc-windows-msvc (an unshipped check-only arch), so it stays off +# there while remaining on for every target we actually build/run. +op-mcp = { path = "../op-mcp" } op-ai = { path = "../op-ai" } op-ai-skills = { path = "../op-ai-skills" } op-orchestrator = { path = "../op-orchestrator" } @@ -70,3 +74,9 @@ skia-safe = { version = "0.97.0", default-features = false, features = [ "textlayout", "pdf", ] } + +# Native-only QuickJS script feature — enabled everywhere except +# aarch64-pc-windows-msvc, whose rquickjs-sys has no prebuilt bindings +# (that arch is a check-only target, never shipped). +[target.'cfg(not(all(target_os = "windows", target_arch = "aarch64")))'.dependencies] +op-mcp = { path = "../op-mcp", features = ["script"] } diff --git a/crates/op-host-web/src/widget_host/overlay_press_tests.rs b/crates/op-host-web/src/widget_host/overlay_press_tests.rs index 2a788ceeb..2bdf27c5c 100644 --- a/crates/op-host-web/src/widget_host/overlay_press_tests.rs +++ b/crates/op-host-web/src/widget_host/overlay_press_tests.rs @@ -369,12 +369,27 @@ fn variables_panel_open_does_not_paint_legacy_modal() { host.paint_editor(&mut backend, W, H); + // The retired legacy variables surface was a near full-viewport modal + // backdrop; the current surface is the floating VariablesPanel + // (820x480, anchored beside the toolbar). Guard only against a round + // fill spanning almost the whole viewport — the earlier "> half + // viewport" heuristic false-tripped once the property panel collapsed + // (nothing-selected default) widened the canvas so the legitimate + // floating panel paints unclamped. assert!( !backend .round_fills .iter() - .any(|r| r.size.x > W * 0.5 && r.size.y > H * 0.5), - "variables_panel_open should not paint the legacy variables modal" + .any(|r| r.size.x >= W * 0.9 && r.size.y >= H * 0.9), + "variables_panel_open should not paint the legacy full-viewport modal" + ); + // ...and the floating panel itself must actually paint. + let panel = host + .variables_panel_rect(W, H) + .expect("variables panel fits in the test viewport"); + assert!( + painted_inside(&backend, panel), + "the floating VariablesPanel background should paint" ); } diff --git a/crates/op-orchestrator/Cargo.toml b/crates/op-orchestrator/Cargo.toml index 7a7afc5fc..aae6a1ca9 100644 --- a/crates/op-orchestrator/Cargo.toml +++ b/crates/op-orchestrator/Cargo.toml @@ -9,7 +9,9 @@ repository.workspace = true [dependencies] op-editor-core = { path = "../op-editor-core" } -op-mcp = { path = "../op-mcp", features = ["script"] } +# `script` feature enabled per-target below (off on aarch64-pc-windows-msvc, +# whose rquickjs-sys has no prebuilt bindings; that arch is check-only). +op-mcp = { path = "../op-mcp" } op-ai-skills = { path = "../op-ai-skills" } # Real jian flex layout (resolved absolute rects) for geometry-driven structural # validation — the deterministic analogue of Pencil's per-batch snapshot_layout. @@ -28,3 +30,8 @@ tokio = { version = "1", features = ["sync", "rt", "macros"] } [dev-dependencies] serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } + +# Native-only QuickJS script feature — off on aarch64-pc-windows-msvc +# (no rquickjs-sys prebuilt bindings; check-only target, never shipped). +[target.'cfg(not(all(target_os = "windows", target_arch = "aarch64")))'.dependencies] +op-mcp = { path = "../op-mcp", features = ["script"] }