Skip to content

docs(x402): spike #206 — hand-roll verdict + proven local EIP-3009 fa… #267

docs(x402): spike #206 — hand-roll verdict + proven local EIP-3009 fa…

docs(x402): spike #206 — hand-roll verdict + proven local EIP-3009 fa… #267

Workflow file for this run

name: CI
# Right-sized cross-platform CI for the Deckard virtual workspace.
#
# Why this shape: the GPUI app renders with Metal on macOS and wgpu/Vulkan on Linux, and the tray is
# OS-specific (objc2 on macOS, GTK/appindicator on Linux) — THAT is what genuinely needs both OSes.
# The headless crates (deckard-core / -contract / -signerd) are OS-independent, so we TEST them once
# (Linux) and only BUILD + lint the app's macOS-specific path on macOS. Testing the same crypto/wire
# logic twice buys nothing.
#
# quick (fmt, ~1 min) ──┬── linux : full build + clippy + test --workspace (+ anvil/Foundry)
# ├── macos : build the shipping binaries + lint the macOS-only tray path
# ├── cargo-deny-advisories : security advisories gate (BLOCKING; a bare yank only warns on PRs, #82)
# └── cargo-deny-supply-chain : bans/licenses/sources gate (BLOCKING)
#
# The `quick` gate fails cheap on the most common trivial mistake (unformatted code) before the two
# heavy runners ever spin up.
#
# REUSE: this workflow is also the release gate — .github/workflows/release.yml calls it via
# workflow_call on a `v*` tag, so a release runs exactly these checks (no drift, no separate gate).
#
# COST: free on PUBLIC repos (the macOS 10x multiplier only bills PRIVATE repos). If you make this
# repo private, add to the macos job: `if: github.event_name == 'pull_request' || startsWith(github.ref, 'refs/tags/v')`.
# Don't switch to "*-large" runners — those are billed even on public repos.
on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:
# Reused as the release gate: .github/workflows/release.yml calls this verbatim on a
# `v*` tag so the gate can never drift from CI. The tag trigger lives in release.yml
# (one workflow owns the tag — no double build).
workflow_call:
env:
CARGO_TERM_COLOR: always
# One-shot CI never rebuilds, so incremental artifacts are pure disk cost. Off keeps the Linux
# `target/` dir from outgrowing the freed disk across the workspace build + tray build + tests +
# three clippy passes (the linux job kept OOMing the runner on "No space left on device").
CARGO_INCREMENTAL: "0"
jobs:
# Fail-fast gate: formatting is OS-independent, so check it once, fast, before the heavy builds.
quick:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# rustup (preinstalled) auto-installs the rust-toolchain.toml-pinned version + rustfmt on the
# first cargo call. No build needed — `cargo fmt` only parses, so this is ~1 min.
- run: cargo fmt --all --check
# The full logic gate. Everything OS-independent (all crates' clippy + tests) runs here, once.
linux:
needs: quick
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# The dep tree (gpui + Helios/revm/bls + the Railgun ZK tree) overflows the stock ubuntu
# runner's ~14 GB and OOMs the build / cache-save — and the rust-cache RESTORE below extracts a
# multi-GB target dir that can itself exhaust the disk before anything builds. Reclaim ~30 GB of
# preinstalled toolchains we never use FIRST, before the cache restore, so neither the restore
# nor the build runs out of space. This job uses rustup + Foundry, NOT any hosted-toolcache
# runtime (python/node/go), so the whole cache is dead weight here. (macOS has the headroom, so
# this is Linux-only.) Paired with CARGO_INCREMENTAL=0 above, which caps target/ growth.
- name: Free up disk space
run: |
sudo rm -rf /usr/local/lib/android /usr/share/dotnet /opt/ghc \
/opt/hostedtoolcache /usr/local/share/boost \
/usr/local/lib/node_modules /usr/local/share/powershell \
/usr/local/share/chromium /usr/lib/google-cloud-sdk || true
sudo docker image prune --all --force >/dev/null 2>&1 || true
df -h /
- uses: Swatinem/rust-cache@v2
# System libraries GPUI needs on Linux (X11 + Wayland + Vulkan + fonts), plus GTK/appindicator
# + libxdo for the optional tray feature (tray-icon/muda link against libxdo).
- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
build-essential pkg-config \
libxcb1-dev libxkbcommon-dev libxkbcommon-x11-dev \
libwayland-dev libxkbcommon-dev \
libvulkan-dev \
libfontconfig1-dev libfreetype6-dev \
libssl-dev \
libgtk-3-dev libayatana-appindicator3-dev libxdo-dev
# Foundry (anvil) for the deckard-signerd broadcast integration tests. Pass GITHUB_TOKEN so the
# release-tag fetch is authenticated — the unauthenticated path hits a 403 API rate-limit.
- uses: foundry-rs/foundry-toolchain@v1
with:
token: ${{ github.token }}
# --locked everywhere: reproducibility lives in the committed Cargo.lock (exact git pins).
- run: cargo build --locked --workspace
- run: cargo build --locked -p deckard-app --features tray
- run: cargo test --locked --workspace
- run: cargo clippy --locked --workspace --all-targets -- -D warnings
- run: cargo clippy --locked -p deckard-app --all-targets --features tray -- -D warnings
# The dev-only daemon-binary resolver is feature-gated out of release builds (finding C1 /
# #106). Lint + test that arm so `just run`/`qa`/`demo` keep working.
- run: cargo clippy --locked -p deckard-signerd --all-targets --features dev-signerd-bin -- -D warnings
- run: cargo test --locked -p deckard-signerd --features dev-signerd-bin
# Cross-platform compile proof. The app (Metal renderer) + the macOS-only objc2 tray are the only
# things that genuinely need macOS coverage, and that's a compile/link concern, not a logic one
# (logic is tested on Linux). So we build the shipping binaries (app + daemon + the MCP sidecar
# Claude Desktop registers — macOS is the demo platform) and lint the macOS-only tray path — no
# redundant tests, and no Foundry (the anvil tests run on Linux; this also removes the
# GitHub-API 403 flake from the 10x-billed runner).
macos:
needs: quick
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: Swatinem/rust-cache@v2
- run: cargo build --locked -p deckard-app -p deckard-signerd -p deckard-mcp
- run: cargo build --locked -p deckard-app --features tray
- run: cargo clippy --locked -p deckard-app --all-targets --features tray -- -D warnings
# Supply-chain SECURITY gate — advisories only. BLOCKING on real RUSTSEC advisories. Config:
# deny.toml. Every advisory the RustSec DB reports is either fixed or carries a written, justified
# `ignore` in deny.toml; a new, untriaged advisory fails this check and blocks the merge until a
# human triages it. deny.toml sets `yanked = "deny"`; ON PULL REQUESTS ONLY we downgrade a bare
# crates.io yank to a warning (`--warn yanked`) so outside-world drift can't block an unrelated PR
# (#82). At release (workflow_call → event_name=push), on push to main, and in the nightly audit,
# yanks still block. A daily re-scan runs from .github/workflows/audit.yml (which opens a tracking
# issue on failure). Rationale: docs/AGENTIC-ENGINEERING.md §4.
cargo-deny-advisories:
needs: quick
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: EmbarkStudios/cargo-deny-action@v2
with:
command: check advisories
# PR-only yank downgrade (warn, not block). Empty elsewhere → deny.toml's `yanked = "deny"`
# stands, so release/push/nightly still block yanks. cargo-deny-action appends
# command-arguments after the command: `cargo-deny … check advisories --warn yanked`. (#82)
command-arguments: ${{ github.event_name == 'pull_request' && '--warn yanked' || '' }}
# Supply-chain HYGIENE — bans / licenses / sources. BLOCKING. The license allow-list and bans
# policy are now fully seeded (deny.toml): permissive licenses allowed, GPL-3.0 scoped per-crate to
# the Zed gpui crates, unlicensed git crates clarified, and the git-dep wildcard policy set to warn.
# A new banned crate / disallowed license / untrusted source now blocks the merge. (#40)
cargo-deny-supply-chain:
needs: quick
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: EmbarkStudios/cargo-deny-action@v2
with:
command: check bans sources licenses