name: PR Check — Rust # Rust half of the former check.yml (split in #232). # # Only runs when Rust actually changes. On this repo roughly 1 PR in 40 touches # src-tauri/, and the runner has capacity 1 — jobs queue instead of running in # parallel, so every minute spent here is a minute the next PR waits. # # No `branches:` filter on purpose. `branches: [main]` never matched a PR # stacked on top of another feature branch, which is what /autopilot produces: # 4 of the 5 PRs in the feature-gating milestone ran no CI at all. on: pull_request: paths: - 'src-tauri/**' - '.forgejo/workflows/check-rust.yml' # Whole directory, not just audit.toml: a later .cargo/config.toml # (rustflags, linker, target dir) would change the Rust build, and an # exact-path entry would let it skip Rust CI unnoticed. - '.cargo/**' # Cancel obsolete runs (e.g. on force-push) so only the latest commit runs. # Distinct from the frontend group: a shared group would make the two # workflows cancel each other on a PR that touches both. concurrency: group: ci-rust-${{ github.ref }} cancel-in-progress: true permissions: contents: read jobs: rust: runs-on: ubuntu container: ubuntu:22.04 env: PATH: /root/.cargo/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin CARGO_TERM_COLOR: always # Nothing persists between runs (see the caching note below), so # incremental artifacts get written and never reused — pure overhead. # Test debug info is dead weight here for the same reason. CARGO_INCREMENTAL: 0 CARGO_PROFILE_TEST_DEBUG: 0 steps: - name: Install system dependencies, Node.js and Rust run: | apt-get update apt-get install -y --no-install-recommends \ curl wget git ca-certificates build-essential pkg-config \ libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev libssl-dev \ libdbus-1-dev # Node.js is required by actions/checkout and taiki-e/install-action # (they are JavaScript actions and need `node` in the container PATH). curl -fsSL https://deb.nodesource.com/setup_20.x | bash - apt-get install -y nodejs # Rust toolchain curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable --profile minimal node --version rustc --version cargo --version - name: Checkout uses: https://github.com/actions/checkout@v4 # No actions/cache step here, deliberately. The job container cannot # reach the runner's cache server (#234): the restore times out into a # miss AND the save times out, so the cache cost ~7 min per run and # returned nothing. Bring caching back through Swatinem/rust-cache once # #234 is fixed — not before, it shares the same backend. - name: cargo check run: cargo check --manifest-path src-tauri/Cargo.toml --all-targets # Anti-rot guard for the suppressions in .cargo/audit.toml (#310). Each # entry there is justified by its crate being absent from every shipped # target's graph — a property of today's resolved graph, not a permanent # one. rsa is currently reachable from nothing: its only parent in the # lockfile is sqlx-mysql, which this SQLite project never compiles. If a # dependency change ever pulls it into a shipped target, the suppression # would silently hide a live advisory and the daily audit would stay # green: the inverse of the permanent red #310 exists to kill. # # Such a change would itself touch src-tauri, which is exactly what # triggers this workflow. Runs after cargo check so the registry index is # already warm, and --locked so cargo tree cannot rewrite the lockfile the # audit was taken against. # # CRATES must mirror the crates named in .cargo/audit.toml. Adding an # entry there without adding its crate here leaves it unguarded. # # Every check echoes its result, including the passing ones. A guard that # is silent on success cannot be told apart in the log from a guard that # never ran — which is the same silent-skip failure mode this step exists # to catch, one level up. - name: Verify suppressed advisories are still unreachable run: | set -u CRATES="rsa" TARGETS="x86_64-unknown-linux-gnu x86_64-pc-windows-msvc" rc=0 checks=0 for crate in $CRATES; do for target in $TARGETS; do # An absent crate exits 0 with empty stdout ("nothing to print" # goes to stderr). A non-zero exit means cargo tree itself failed # — treat that as a failure rather than as proof of absence. out=$(cargo tree --manifest-path src-tauri/Cargo.toml --locked \ -i "$crate" --target "$target" 2>/dev/null) || { echo "cargo tree failed for $crate / $target — cannot verify the suppression" rc=1 continue } if [ -n "$out" ]; then echo "FAIL: $crate is now compiled for $target — its .cargo/audit.toml suppression is no longer justified (see #310)." rc=1 else echo "ok: $crate absent from $target" fi checks=$((checks + 1)) done done # Canary: a crate known to be present. If this stops being found, the # loop above is broken and its silence means nothing. canary=$(cargo tree --manifest-path src-tauri/Cargo.toml --locked \ -i tar --target x86_64-unknown-linux-gnu 2>/dev/null) || true if [ -z "$canary" ]; then echo "FAIL: canary 'tar' was not found although it is a known dependency. The guard is not proving anything." rc=1 else echo "ok: canary 'tar' found for x86_64-unknown-linux-gnu" fi echo "Suppression guard: $checks checks, exit $rc" exit $rc - name: cargo test run: cargo test --manifest-path src-tauri/Cargo.toml --all-targets # Prebuilt binary. `cargo install --locked cargo-audit` recompiled the # tool from source on every single run (~4m40s). No continue-on-error: # a tooling failure should fail the job rather than be swallowed. - name: Install cargo-audit uses: https://github.com/taiki-e/install-action@v2 with: tool: cargo-audit # Advisories are informational — they can land on unrelated crates and # would otherwise stall unrelated work — so the step is non-blocking. # It no longer hides real failures behind `|| true` though. Daily # RustSec coverage outside Rust PRs lives in audit.yml. - name: cargo audit continue-on-error: true run: cargo audit --file src-tauri/Cargo.lock