Enforces the Step 1b §1.4 widget boundary invariant: widget logic
(Widget impls + layout/paint/access_node methods) lives in
crates/openpencil-shell-core/src/widgets/; shell-web's only
widget-touching file is `widget_host.rs` and even there the only
widget-method signature allowed is the `// glue:` marked paint
dispatcher.
Forward checks (no widget logic in shell-web/src/):
- F1: `impl(<...>)?[[:space:]]+(ns::)*Widget[[:space:]]+for[[:space:]]`
anywhere under shell-web/src/. Allows generic params + arbitrary
namespace depth so `impl<T> shell_core::widgets::Widget for X` is
caught. No `// glue:` exemption — Widget impls have no place in
shell-web period.
- F2: `fn[[:space:]]+(layout|access_node)\(` anywhere under
shell-web/src/. No exemption.
- F3: `fn[[:space:]]+paint\(` under shell-web/src/, EXCEPT lines in
widget_host.rs that ALSO carry `// glue:`. Tight exemption — the
marker only blesses one specific signature, not arbitrary tagged
lines.
- F4: any line under shell-web/src/ mentioning both
`openpencil_shell_core` AND `widgets`, except widget_host.rs.
Catches direct + grouped `use` forms (e.g. `use
openpencil_shell_core::{widgets::TreeWidget};`) plus path
expressions. Multi-line braced `use` is out of scope (single-line
policy in this crate).
Reverse check (shell-core/src/widgets/ has all four impls):
- R1: For each of {tree, prop_row, dropdown, text_input}, the file
must exist AND, after stripping `//` line comments, must contain
a live `impl Widget for X`. Block comments out of scope (line
comments only in this directory).
CI integration:
- New "Verify Step 1b widget boundary (spec §1.4)" step in
.github/workflows/rust-check.yml right after the existing
"Verify Jian boundary invariants" step, gated to Linux runner
(matches the jian-boundaries pattern).
- Added `tools/check-jian-boundaries.sh` and
`tools/check-widget-boundary.sh` to the rust-check.yml push +
pull_request path filters so PRs editing only the checker still
trigger CI.
7-test regression matrix (positive + 6 negative cases):
- positive (real codebase) → PASS
- generic `impl<T> Widget for X` injected → FAIL F1
- direct `use openpencil_shell_core::widgets` outside host → FAIL F4
- `// glue:` tag on `impl Widget for X` line → FAIL F1 (exemption
doesn't save it; only `fn paint` lines are exempted)
- shell-core file replaced with `// stub` → FAIL R1
- grouped `use openpencil_shell_core::{widgets::TreeWidget};` → FAIL F4
- grouped `use openpencil_shell_core::{widgets};` → FAIL F4
- shell-core file body replaced with `// impl Widget for X { ... }` → FAIL R1
Codex iterate review: 5 rounds → GO. Round 1 BLOCK (greedy
WidgetHost match), R2 BLOCK + 3 CONCERN (calls/imports unchecked,
generic impls, broad exemption, filename-only count), R3 BLOCK +
CONCERN (grouped imports, commented-out impls), R4 2 NITs
(documentation parity), R5 GO clean.
94 lines
3.4 KiB
YAML
94 lines
3.4 KiB
YAML
name: Rust check (native)
|
|
|
|
on:
|
|
pull_request:
|
|
paths:
|
|
- 'Cargo.toml'
|
|
- 'Cargo.lock'
|
|
- 'crates/**'
|
|
- 'rust-toolchain.toml'
|
|
- 'rustfmt.toml'
|
|
- 'deny.toml'
|
|
- 'tools/check-jian-boundaries.sh'
|
|
- 'tools/check-widget-boundary.sh'
|
|
- '.github/workflows/rust-check.yml'
|
|
push:
|
|
branches: [main, 'feat/rust-ification']
|
|
paths:
|
|
- 'Cargo.toml'
|
|
- 'Cargo.lock'
|
|
- 'crates/**'
|
|
- 'rust-toolchain.toml'
|
|
- 'tools/check-jian-boundaries.sh'
|
|
- 'tools/check-widget-boundary.sh'
|
|
|
|
jobs:
|
|
check:
|
|
name: ${{ matrix.os }} / 1.85
|
|
runs-on: ${{ matrix.os }}
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
os: [macos-latest, ubuntu-latest, windows-latest]
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
submodules: recursive
|
|
- uses: dtolnay/rust-toolchain@stable
|
|
with:
|
|
toolchain: '1.85'
|
|
components: rustfmt, clippy
|
|
- uses: Swatinem/rust-cache@v2
|
|
# Linux GL prerequisites for winit's x11+wayland feature link.
|
|
# libxkbcommon-x11-dev / libwayland-dev are winit's link-time deps on
|
|
# Linux; the wayland-csd-adwaita feature pulls in xkbcommon + wayland-client.
|
|
- name: Install Linux GL prereqs
|
|
if: runner.os == 'Linux'
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y \
|
|
libxkbcommon-dev libxkbcommon-x11-dev \
|
|
libwayland-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev \
|
|
libegl1-mesa-dev libgles2-mesa-dev libgbm-dev mesa-utils \
|
|
libfreetype-dev libfontconfig1-dev
|
|
- run: cargo fmt --all -- --check
|
|
- run: cargo build --workspace
|
|
# Step 1a §9.2: Linux GPU smoke must hard-fail on EGL/GL setup failure
|
|
# in CI (real Mesa software-rendered EGL pbuffer is available). Local
|
|
# dev runners (no GPU + no env var) skip with INCONCLUSIVE marker.
|
|
- name: Run tests (Linux with strict GPU gate)
|
|
if: runner.os == 'Linux'
|
|
env:
|
|
STEP1A_REQUIRE_GPU: '1'
|
|
run: cargo test --workspace
|
|
- name: Run tests (macOS / Windows)
|
|
if: runner.os != 'Linux'
|
|
run: cargo test --workspace
|
|
- run: cargo clippy --workspace --all-targets -- -D warnings
|
|
# Step 1a Phase C Task 4: spec v19 §11 + §12.3 boundary invariants.
|
|
# Linux runner has the full mobile target stdlib (`rustup target add`
|
|
# in subsequent steps would handle ios/android cargo metadata too,
|
|
# but the script uses `cargo tree --target` which only needs the
|
|
# cfg-gate evaluation, not the actual target sysroot).
|
|
- name: Verify Jian boundary invariants
|
|
if: runner.os == 'Linux'
|
|
run: |
|
|
rustup target add aarch64-linux-android aarch64-apple-ios wasm32-unknown-unknown
|
|
bash tools/check-jian-boundaries.sh
|
|
|
|
- name: Verify Step 1b widget boundary (spec §1.4)
|
|
if: runner.os == 'Linux'
|
|
run: bash tools/check-widget-boundary.sh
|
|
|
|
deny:
|
|
name: cargo-deny (native)
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
with:
|
|
submodules: recursive
|
|
- uses: dtolnay/rust-toolchain@stable
|
|
- uses: EmbarkStudios/cargo-deny-action@v2 # auto-updates within v2.x; runner cargo at this stage carries cargo-deny 0.18+ which handles modern transitive manifests (Phase 1 Task 1.8 finding)
|
|
with:
|
|
command: check
|