🏗️ Build and Test JellyTau / Run Tests (pull_request) Successful in 18m41s
🏗️ Build and Test JellyTau / Supply Chain (pull_request) Successful in 31s
Traceability Validation / Check Requirement Traces (pull_request) Successful in 9s
🏗️ Build and Test JellyTau / Android Compile Check (pull_request) Successful in 4m5s
Preparing v0.10.0 meant building the release locally first. It did not build. Two separate defects were sitting on master, both invisible to every gate this project has, for the same reason: nothing in build-and-test.yml runs `tauri build`. Only a tag does. So the first time anyone would have discovered either was a failed release. **Tauri plugin versions had drifted apart.** Tauri refuses to build when a plugin's Rust crate and npm package are on different minor versions: tauri-plugin-log (v2.8.0) : @tauri-apps/plugin-log (v2.9.0) tauri-plugin-updater (v2.9.0) : @tauri-apps/plugin-updater (v2.10.1) Introduced by the updater and diagnostics work in this same branch -- `cargo add` took what the pinned toolchain allowed while `bun add` took latest, and the caret ranges let them separate. cargo check, clippy, cargo test and svelte-check all passed. Matching upward pulled wry 0.53.5 -> 0.54.2 along with wasm-bindgen, web-sys and webkit2gtk: the webview layer, which on Linux is the video playback path. That is not a change to make while cutting a release, so the npm packages are pinned down to the crates instead -- exactly, not by caret, since the caret is what allowed the drift. The upgrade is worth doing deliberately, with a playback check, and ci-operations.md says so. CI now runs `tauri info`, which performs the same comparison without building. Verified by reintroducing the mismatch and watching it fail. **The AppImage target had never been built.** It was added earlier in this branch because the release notes had advertised an AppImage for months while tauri.conf.json never produced one. It does not work out of the box: linuxdeploy carries its own `strip`, too old to parse the .relr.dyn section modern toolchains emit, and it fails on every bundled library -- strip: libzstd.so.1: unknown type [0x13] section `.relr.dyn' failed to bundle project `failed to run linuxdeploy` Ubuntu 23.10+ links with -z pack-relative-relocs by default, so the CI builder image fails exactly as a modern Arch host does. NO_STRIP=true is linuxdeploy's documented escape hatch. The resulting 153 MB AppImage was verified to be well-formed and to actually start. Without this the release would have failed at the Linux build step -- the artifact check added earlier refuses to publish when no AppImage is produced, which is the behaviour we want, but it would have refused a tagged build rather than a local one. Also: the traceability extractor now reads the tooling shell scripts that carry TRACES comments. DR-207, DR-213 and DR-220 all had them and were counted as uncovered because only .ts/.svelte/.rs were scanned. Listed individually rather than globbing scripts/*.sh -- most implement nothing, and adding one should be a decision. DR-221.
290 lines
12 KiB
YAML
290 lines
12 KiB
YAML
name: '🏗️ Build and Test JellyTau'
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- master
|
|
paths-ignore:
|
|
- '**/*.md'
|
|
pull_request:
|
|
branches:
|
|
- master
|
|
paths-ignore:
|
|
- '**/*.md'
|
|
workflow_dispatch:
|
|
|
|
env:
|
|
# Incremental state is never reused between CI runs -- pure disk cost.
|
|
CARGO_INCREMENTAL: 0
|
|
|
|
jobs:
|
|
test:
|
|
name: Run Tests
|
|
# A release push triggers build-release.yml on the tag, which runs this exact
|
|
# test suite itself — and on a single-slot runner the two ~1h workflows would
|
|
# otherwise serialize/contend. Skip the duplicate for chore(release) commits.
|
|
# (head_commit is absent on pull_request/workflow_dispatch; startsWith(null,…)
|
|
# is false there, so those events still run.)
|
|
if: "!startsWith(github.event.head_commit.message, 'chore(release)')"
|
|
runs-on: linux/amd64
|
|
container:
|
|
image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08
|
|
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Cache Rust dependencies
|
|
uses: actions/cache@v3
|
|
with:
|
|
# Registry only -- never src-tauri/target. That directory is ~16 GB and
|
|
# was cached under five separate keys, which filled the runner's 74 GB
|
|
# disk at ~1.15 GB/day (23 GB in 20 days, measured Aug 2026).
|
|
# registry/src is omitted too: cargo re-extracts it for free from
|
|
# registry/cache (155 MB of .crate tarballs vs 1.1 GB extracted).
|
|
path: |
|
|
~/.cargo/registry/index
|
|
~/.cargo/registry/cache
|
|
~/.cargo/git/db
|
|
# One shared key across every job. The old per-job keys existed to stop
|
|
# debug/release target artifacts clobbering each other; with target no
|
|
# longer cached, registry contents are target-independent, so all jobs
|
|
# want the same crates. First job to finish saves; the rest restore.
|
|
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
|
|
restore-keys: |
|
|
${{ runner.os }}-cargo-registry-
|
|
|
|
- name: Cache Node dependencies
|
|
uses: actions/cache@v3
|
|
with:
|
|
path: |
|
|
~/.bun/install/cache
|
|
node_modules
|
|
key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }}
|
|
restore-keys: |
|
|
${{ runner.os }}-bun-
|
|
|
|
- name: Install dependencies
|
|
run: |
|
|
bun install
|
|
|
|
# Tripwire for domain-taxonomy leaks into the presentation layer (a
|
|
# multi-type includeItemTypes query defining a category in the frontend).
|
|
# See scripts/check-frontend-boundary.sh and
|
|
# docs/specs/scoped-search-boundary.md.
|
|
- name: Check frontend/backend boundary
|
|
run: bash scripts/check-frontend-boundary.sh
|
|
|
|
# The docs are the maintained source of truth for architecture and
|
|
# process, and they cross-reference each other heavily. A rename that
|
|
# misses a link turns a doc into a dead end silently. Pure shell + git —
|
|
# no tool is installed at job time.
|
|
- name: Check documentation links
|
|
run: bash scripts/check-doc-links.sh
|
|
|
|
# Formatting, linting and type-checking were all configured in this repo
|
|
# and enforced by nothing: .prettierrc described a tree where 199 files did
|
|
# not match it, eslint.config.js ran in no workflow and in no hook, and
|
|
# `bun run check` ran only in build-release.yml — i.e. a type error could
|
|
# sit on master until somebody cut a tag. These three steps are what make
|
|
# those configs load-bearing. All are project deps installed by
|
|
# `bun install`; nothing is fetched at job time.
|
|
- name: Check formatting
|
|
run: bun run format:check
|
|
|
|
# RATCHET — this number only ever goes DOWN. Same policy as MIN_THRESHOLD
|
|
# in traceability-check.yml and the coverage thresholds in
|
|
# vitest.config.ts. 159 is what the tree carried when the gate went in; the
|
|
# backlog is real findings (dead bindings, unkeyed {#each}, `any` at the
|
|
# IPC boundary) that eslint.config.js documents rule by rule, each parked
|
|
# at "warn" until its class is cleared and it can be promoted to "error".
|
|
# Lower this as you clear them. Never raise it to make a build pass.
|
|
- name: Lint
|
|
run: bun run lint -- --max-warnings=159
|
|
|
|
- name: Check TypeScript
|
|
run: |
|
|
bunx svelte-kit sync
|
|
bun run check
|
|
|
|
# Tauri refuses to build when a plugin's Rust crate and npm package are on
|
|
# different minor versions. Nothing here runs `tauri build` -- that only
|
|
# happens on a tag -- so a mismatch introduced on master stayed invisible
|
|
# until the release build, which is where it was found: v0.10.0 prep hit
|
|
# `tauri-plugin-log (v2.8.0) : @tauri-apps/plugin-log (v2.9.0)`. `cargo
|
|
# check`, clippy, the tests and svelte-check had all passed.
|
|
#
|
|
# `tauri info` performs the same comparison the bundler does, without a
|
|
# build. Grepping its output is crude, but the alternative is discovering
|
|
# this at tag time again.
|
|
- name: Check Tauri plugin versions match
|
|
run: |
|
|
set -e
|
|
if bunx tauri info 2>&1 | tee /tmp/tauri-info.txt | grep -q "version mismatched"; then
|
|
echo "::error::A Tauri plugin's Rust crate and npm package versions disagree."
|
|
echo "::error::The release build will refuse to start. Align them in"
|
|
echo "::error::src-tauri/Cargo.toml and package.json (both are pinned exactly)."
|
|
grep -A6 "version mismatched" /tmp/tauri-info.txt || true
|
|
exit 1
|
|
fi
|
|
echo "✅ Tauri plugin crate/package versions agree."
|
|
|
|
# Coverage rather than a bare `bun run test`: same suite, plus the
|
|
# thresholds in vitest.config.ts, so a large untested module or a deleted
|
|
# test fails here instead of being noticed months later.
|
|
- name: Run frontend tests
|
|
run: |
|
|
bunx svelte-kit sync
|
|
bun run test:coverage
|
|
|
|
# CLAUDE.md has required `cargo fmt` + `cargo clippy` before every commit
|
|
# for as long as the rule has existed, but nothing in CI checked either,
|
|
# so the requirement rested entirely on memory. Both components are baked
|
|
# into the builder image (Dockerfile.builder: `rustup component add
|
|
# rustfmt clippy`) — nothing is installed at job time.
|
|
- name: Check Rust formatting
|
|
run: |
|
|
cd src-tauri
|
|
cargo fmt --all -- --check
|
|
|
|
# Clippy is a hard gate. It was advisory while the tree carried a warning
|
|
# backlog; that backlog is gone (0 warnings on 1.97.1, the pinned
|
|
# toolchain), so a warning here is now new breakage rather than old noise.
|
|
#
|
|
# This only means anything because src-tauri/rust-toolchain.toml pins the
|
|
# compiler: clippy's lint set moves between releases, so an unpinned gate
|
|
# would fail on whatever the runner happened to install. The pin and this
|
|
# flag stand or fall together — if you unpin, drop this back to advisory.
|
|
- name: Run clippy
|
|
run: |
|
|
cd src-tauri
|
|
cargo clippy --all-targets -- -D warnings
|
|
|
|
- name: Run Rust tests
|
|
run: |
|
|
cd src-tauri
|
|
cargo test
|
|
cd ..
|
|
|
|
# Fast per-commit Android compile check. This does NOT build a shippable APK:
|
|
# the full signed release APK is built only on tag pushes by build-release.yml
|
|
# (which runs sync-android-sources.sh + signing). Running the full bundle here
|
|
# too would duplicate a ~15min build and, without the sync step, produced an
|
|
# unsigned APK missing our custom sources/icons/proguard rules anyway.
|
|
# `cargo check` for the Android target (~1min) catches Android-specific Rust
|
|
# breakage without linking, bundling, or signing.
|
|
android-check:
|
|
name: Android Compile Check
|
|
runs-on: linux/amd64
|
|
needs: test
|
|
container:
|
|
image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08
|
|
env:
|
|
ANDROID_HOME: /opt/android-sdk
|
|
ANDROID_SDK_ROOT: /opt/android-sdk
|
|
NDK_HOME: /opt/android-sdk/ndk/27.0.11902837
|
|
ANDROID_NDK_HOME: /opt/android-sdk/ndk/27.0.11902837
|
|
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Cache Rust dependencies
|
|
uses: actions/cache@v3
|
|
with:
|
|
# Registry only -- never src-tauri/target. That directory is ~16 GB and
|
|
# was cached under five separate keys, which filled the runner's 74 GB
|
|
# disk at ~1.15 GB/day (23 GB in 20 days, measured Aug 2026).
|
|
# registry/src is omitted too: cargo re-extracts it for free from
|
|
# registry/cache (155 MB of .crate tarballs vs 1.1 GB extracted).
|
|
path: |
|
|
~/.cargo/registry/index
|
|
~/.cargo/registry/cache
|
|
~/.cargo/git/db
|
|
# One shared key across every job. The old per-job keys existed to stop
|
|
# debug/release target artifacts clobbering each other; with target no
|
|
# longer cached, registry contents are target-independent, so all jobs
|
|
# want the same crates. First job to finish saves; the rest restore.
|
|
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
|
|
restore-keys: |
|
|
${{ runner.os }}-cargo-registry-
|
|
|
|
- name: Cache Node dependencies
|
|
uses: actions/cache@v3
|
|
with:
|
|
path: |
|
|
~/.bun/install/cache
|
|
node_modules
|
|
key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }}
|
|
restore-keys: |
|
|
${{ runner.os }}-bun-
|
|
|
|
- name: Install dependencies
|
|
run: bun install
|
|
|
|
- name: Cargo check (aarch64-linux-android)
|
|
run: |
|
|
TC="$NDK_HOME/toolchains/llvm/prebuilt/linux-x86_64/bin"
|
|
export CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER="$TC/aarch64-linux-android24-clang"
|
|
export CC_aarch64_linux_android="$TC/aarch64-linux-android24-clang"
|
|
export AR_aarch64_linux_android="$TC/llvm-ar"
|
|
cd src-tauri
|
|
cargo check --target aarch64-linux-android --lib
|
|
|
|
# Supply-chain gate. Until this job existed the project had no vulnerability
|
|
# scanning of any kind: nothing checked the ~500-crate Rust graph or the JS
|
|
# dependencies against a CVE feed, and nothing checked that everything we
|
|
# redistribute is licence-compatible with shipping JellyTau under MIT.
|
|
#
|
|
# The first run of this found eight vulnerabilities and one unsoundness
|
|
# (bytes, four in rustls-webpki, time, two in quick-xml, rand) — all fixed by
|
|
# `cargo update`, none of which anybody had reason to run.
|
|
#
|
|
# Runs in parallel with android-check rather than after `test`: a dependency
|
|
# advisory has nothing to do with whether the tests pass, and finding out
|
|
# sooner is the point.
|
|
security:
|
|
name: Supply Chain
|
|
runs-on: linux/amd64
|
|
container:
|
|
image: gitea.tourolle.paris/dtourolle/jellytau-builder:2026.08
|
|
|
|
steps:
|
|
- name: Checkout repository
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Cache Rust dependencies
|
|
uses: actions/cache@v3
|
|
with:
|
|
path: |
|
|
~/.cargo/registry/index
|
|
~/.cargo/registry/cache
|
|
~/.cargo/git/db
|
|
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
|
|
restore-keys: |
|
|
${{ runner.os }}-cargo-registry-
|
|
|
|
# cargo-deny is baked into the builder image. It fetches the RustSec
|
|
# advisory database at run time — that is *data*, like the crates
|
|
# `bun install` fetches, not a toolchain install, so the 🔴 rule in
|
|
# CLAUDE.md is not in play here.
|
|
#
|
|
# Config and every documented exception live in src-tauri/deny.toml.
|
|
# Vulnerabilities and unsoundness are hard failures with no override;
|
|
# unmaintained transitive crates that have no safe upgrade (Tauri's GTK3
|
|
# stack, the unic-* tables) are ignored there by ID, each with a reason.
|
|
- name: cargo-deny (advisories, licences, bans, sources)
|
|
run: |
|
|
cd src-tauri
|
|
cargo deny check
|
|
|
|
# Advisory for now, deliberately. The Rust graph was clean after one
|
|
# update pass, so gating it costs nothing; the JS graph has not been
|
|
# audited before and a first run that fails the build teaches everyone to
|
|
# ignore this job. Promote to a hard gate once the output is empty and
|
|
# stays empty — same approach that got clippy from advisory to -D warnings.
|
|
- name: bun audit (advisory)
|
|
run: |
|
|
bun install
|
|
bun audit || echo "::warning::bun audit reported findings — advisory for now, see CLAUDE.md"
|