--- name: CI pipeline type: architecture-map-page slice: 05-core-storage-hotkey-build last_verified: 2026/05/09 --- # CI pipeline > **Where you are:** [Architecture map](../README.md) → [Core, Storage, Hotkey, Build](README.md) → CI pipeline **Plain English summary.** Three GitHub Actions workflows. `check.yml` runs per-push compile + lint + library tests + frontend build on Linux, Windows, and macOS. `build.yml` produces installer artefacts (.AppImage/.deb on Linux, .msi/.exe on Windows, .dmg/.app on macOS) for tag pushes and on-demand. `audit.yml` runs cargo-audit and npm-audit weekly. ## At a glance - Files: `.github/workflows/check.yml`, `build.yml`, `audit.yml`. - LOC: 7,017 + 7,157 + 1,443 bytes respectively. - Shared concerns across check and build: same Linux / macOS / Windows system-package install steps (libwebkit2gtk, Vulkan SDK, libclang for bindgen), same `Swatinem/rust-cache@v2` cache scoping (`workspaces: .` because the workspace target dir is `./target`, **not `src-tauri/target`** — this was the silent miss that made Windows checks slow). ## `check.yml` — per-push compile + lint + tests ### Triggers ```yaml on: [push, pull_request] concurrency: group: check-${{ github.ref }} cancel-in-progress: true # newer push supersedes older ``` ### Jobs #### `rust` — matrix ```yaml strategy: fail-fast: false matrix: os: [ubuntu-22.04, windows-latest, macos-latest] ``` Steps per OS: 1. **Install system deps** — libwebkit2gtk-4.1, libappindicator3, librsvg2, libasound2, libudev, patchelf, cmake, build-essential, libclang, clang, libvulkan, glslang-tools, spirv-tools (Linux); Homebrew vulkan-headers, vulkan-loader, molten-vk, shaderc, llvm (macOS); choco llvm + vulkan-sdk (Windows). Each install resolves `LIBCLANG_PATH` / `VULKAN_SDK` dynamically so a minor SDK version bump does not hardcode-break the step. 2. `dtolnay/rust-toolchain@stable` with `rustfmt, clippy`. 3. `Swatinem/rust-cache@v2` with `workspaces: .` and shared key `lumotia-${{ matrix.os }}`. 4. `cargo check --workspace --all-targets`. 5. `cargo fmt --all -- --check`. 6. `cargo clippy --workspace --all-targets -- -D warnings`. 7. `cargo test --workspace --lib` (Linux only, gated on `matrix.os == 'ubuntu-22.04'`). 8. `cargo audit` (Linux only). The Linux-only gating on tests + audit keeps macOS and Windows legs focused on compile coverage. The library-tests-only flag (`--lib`) excludes integration tests that need a runtime / GPU. #### `frontend` — Linux only 1. Setup Node 20. 2. `npm ci`. 3. `npm audit --audit-level=high`. 4. `npm run build` — Vite-only build (no `tauri build`). 5. `svelte-check` for type and template errors. ### Why the workspace cache scope matters The original CI step pointed `Swatinem/rust-cache@v2` at `workspaces: src-tauri`. The workspace target dir is at `./target` (defined by the repo-root `Cargo.toml`), not `src-tauri/target`. That meant the cache silently missed every run, and Windows check runs felt like they recompiled `sqlx` from scratch every time. Documented inline at `check.yml`. The current scope `workspaces: .` is the fix. ## `build.yml` — release artefacts ### Triggers ```yaml on: push: tags: ['v*'] # tagged releases (v0.1.0, v0.2.0, ...) workflow_dispatch: # manual, any branch inputs: tag_name: description: 'Optional tag name to attach the build to' required: false ``` `workflow_dispatch` lets us build a Windows binary on demand to dual-boot test without cutting a release. ### Concurrency ```yaml concurrency: group: build-${{ github.ref }} cancel-in-progress: false # release builds finish even on tag re-pushes ``` Different from `check.yml`: a release build is too expensive to abandon mid-flight. ### Matrix ```yaml include: - os: ubuntu-22.04 artefacts: *.AppImage, *.deb - os: windows-latest artefacts: *.msi, *.exe - os: macos-latest artefacts: *.dmg, *.app ``` ### Steps 1. System deps (same as `check.yml`). 2. Rust toolchain. 3. Cache (`shared-key: lumotia-build-${{ matrix.os }}`, distinct from check.yml's key so the build cache is not invalidated by check runs). 4. `npm ci`. 5. `tauri-apps/tauri-action@v0` — runs `tauri build`. On tag pushes, attaches artefacts to a draft GitHub Release. Empty `tagName` for `workflow_dispatch` so we get artefacts only. 6. `actions/upload-artifact@v4` — always uploads to the run page (30-day retention) so the workflow_dispatch path produces something downloadable too. 7. `du -h` on the produced bundle directory for visibility. ### Code signing **Not configured.** Both macOS (Apple Developer ID) and Windows (code-signing certificate) signing blocks are commented out in `build.yml`. Users hit Gatekeeper / SmartScreen on first run. To enable later: - **macOS:** uncomment the `APPLE_SIGNING_IDENTITY` / `APPLE_CERTIFICATE` / `APPLE_CERTIFICATE_PASSWORD` env block; add the corresponding repo secrets. - **Windows:** uncomment the `WINDOWS_CERTIFICATE` / `WINDOWS_CERTIFICATE_PASSWORD` env block; add the secrets. Documented in the file header. ## `audit.yml` — weekly vulnerability scan ### Triggers ```yaml on: schedule: - cron: "0 6 * * 1" # Mondays 06:00 UTC workflow_dispatch: ``` Mondays so any advisory has the whole week to be triaged rather than landing on a Friday. ### Jobs #### `cargo-audit` Uses `rustsec/audit-check@v2` against the RustSec advisory DB. Fails on any unignored advisory. #### `npm-audit` `npm audit --audit-level=high`. Ignores low / moderate noise — only high and critical advisories warrant a bump. ### Why a separate workflow? A newly published advisory surfaces as its own failing run (easy to spot, easy to track) without blocking unrelated PR work. The same `cargo audit` runs as part of `check.yml` on every push, so this workflow is the catch-all for advisories that land between pushes. ## Watch-outs - **`cargo audit` runs in two places.** `check.yml` (Linux only, on every push) and `audit.yml` (weekly). The duplication is cheap and useful. - **`tauri-action@v0` is unpinned.** `@v0` floats. A breaking change in tauri-action would surface on the next release attempt. Worth pinning to a specific version when v0.1 ships. - **macOS `LIBCLANG_PATH` resolution.** `brew --prefix llvm` resolves to the Apple Silicon path on M-series runners (`/opt/homebrew/...`) and the Intel path on intel runners (`/usr/local/...`). The dynamic resolution handles both. - **Linux runners use Ubuntu 22.04, not 24.04.** Pinned because 22.04's `libwebkit2gtk-4.1` package is stable; 24.04's namespace shifted. Worth re-evaluating once GitHub Actions retires 22.04. - **No nightly clippy.** The `-D warnings` clippy step uses stable. A nightly canary job would catch upcoming lint changes earlier. ## See also - [Workspace Cargo.toml](workspace-cargo.md) — the release profile this CI consumes. - [Dev launcher and scripts](dev-launcher-and-scripts.md) — local equivalent.