cargo update -p rustls-webpki -p tar moves rustls-webpki 0.103.9 -> 0.103.13 and tar 0.4.44 -> 0.4.46, both within the existing Cargo.toml bounds. They sit under tauri-plugin-updater, which downloads and unpacks application updates, so all six of their advisories were reachable in the shipped binary. The remaining three can neither be fixed nor reached. quick-xml (2x 7.5 high) is pulled by plist, which tauri only needs for Apple bundling: its per-target trees are empty for both shipped targets and it appears solely under x86_64-apple-darwin. Its fix is >= 0.41.0 while plist requires ^0.38, a semver-incompatible boundary [patch.crates-io] cannot cross. rsa has no published fix at all and is never compiled — its only parent is sqlx-mysql, an artifact of sqlx's multi-backend graph on a SQLite project. Leaving those three to red the daily gate forever would reproduce the signal loss that #232 removed the `|| true` to fix, so they move into a versioned .cargo/audit.toml. Entries are keyed by advisory ID, never by crate, so a new advisory against the same crate still reds the gate; each carries its reachability proof and its removal condition. A blocking step in check-rust.yml re-proves that justification on every PR touching src-tauri/ or .cargo/, and fails if a suppressed crate enters a shipped target's graph — the scenario that would rot the list is itself a src-tauri change. It separates cargo tree's exit status from its output (an absent crate and a failed invocation both print nothing) and asserts a canary crate is still found, so its silence proves something. cargo audit: 9 vulnerabilities -> 0, warnings unchanged at 23 (cargo-audit 0.22.2, advisory-db 0bfde9d6 of 2026-07-27). cargo check + cargo test green (106 tests); npm build + 871 vitest green. Resolves #310 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
139 lines
6.2 KiB
YAML
139 lines
6.2 KiB
YAML
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. If a tauri/plist bump ever makes quick-xml unconditional, 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.
|
|
#
|
|
# That bump would itself be a src-tauri change, 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.
|
|
- name: Verify suppressed advisories are still unreachable
|
|
run: |
|
|
set -u
|
|
CRATES="quick-xml rsa"
|
|
TARGETS="x86_64-unknown-linux-gnu x86_64-pc-windows-msvc"
|
|
rc=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 "$crate is now compiled for $target — its .cargo/audit.toml suppression is no longer justified (see #310)."
|
|
rc=1
|
|
fi
|
|
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 "Canary failed: 'tar' was not found although it is a known dependency. The guard is not proving anything."
|
|
rc=1
|
|
fi
|
|
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
|