Files
Firelink/.github/workflows/ci.yml
T
NimBold eca4efb1ad fix(ci): optimize engine build times and add granular Aria2 caching
- replace hardcoded make -j2 with dynamic CPU core detection in aria2 build.sh

- isolate Aria2 source build cache from prebuilt engine downloads

- add atomic cache validation, restoration, and promotion in scripts/engine-aria2-cache.js

- add Rust dependency and target caching with Swatinem/rust-cache@v2

- raise Desktop checks timeout to 45 minutes to prevent job aborts

- harden temporary directory cleanup on Windows process abort
2026-09-07 19:58:49 +03:30

233 lines
10 KiB
YAML

name: CI
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
jobs:
rust-security:
name: Rust advisory audit
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- name: Install locked cargo-audit
run: cargo install cargo-audit --version 0.22.2 --locked
- name: Reject vulnerable resolved dependencies
working-directory: src-tauri
run: cargo audit
frontend:
name: Frontend checks
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v7
with:
submodules: recursive
- uses: actions/setup-node@v6
with:
node-version: 22.12
cache: npm
- run: npm ci
- run: node --test scripts/*.node-test.js
- run: npm test -- --run
- run: npm run build
desktop:
name: Desktop checks (${{ matrix.target }})
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
include:
- os: macos-latest
target: aarch64-apple-darwin
- os: windows-latest
target: x86_64-pc-windows-msvc
- os: ubuntu-22.04
target: x86_64-unknown-linux-gnu
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v7
with:
submodules: recursive
- uses: actions/setup-node@v6
with:
node-version: 22.12
cache: npm
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Cache Rust dependencies and build targets
uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri -> target
- name: Install Linux dependencies
if: runner.os == 'Linux'
run: |
# The hosted Ubuntu image can expose an unreachable Azure mirror in
# its apt sources while the public Ubuntu archive is reachable.
# Normalize every apt source, including the image's mirror list,
# before refreshing indexes so a transient runner-local mirror does
# not consume the whole job timeout.
sudo find /etc/apt -type f \
-exec sed -i \
-e 's#http://azure\.archive\.ubuntu\.com#https://archive.ubuntu.com#g' \
-e 's#https://azure\.archive\.ubuntu\.com#https://archive.ubuntu.com#g' \
{} +
sudo env DEBIAN_FRONTEND=noninteractive timeout --foreground --signal=TERM --kill-after=30s 10m apt-get \
-o Acquire::Retries=3 \
-o Acquire::http::Timeout=30 \
-o Acquire::https::Timeout=30 \
-o DPkg::Lock::Timeout=60 \
update
sudo env DEBIAN_FRONTEND=noninteractive timeout --foreground --signal=TERM --kill-after=30s 10m apt-get \
-o Acquire::Retries=3 \
-o Acquire::http::Timeout=30 \
-o Acquire::https::Timeout=30 \
-o DPkg::Lock::Timeout=60 \
install -y --no-install-recommends \
libwebkit2gtk-4.1-dev \
libappindicator3-dev \
librsvg2-dev \
patchelf \
libdbus-1-dev \
pkg-config
- run: npm ci
- name: Test Rust backend
if: runner.os != 'Windows'
working-directory: src-tauri
run: cargo test --all-targets --target ${{ matrix.target }}
- name: Test Windows Torrent RPC integration
if: runner.os == 'Windows'
working-directory: src-tauri
run: cargo test --test torrent_rpc --target ${{ matrix.target }} -- --nocapture
- name: Compile Windows queue-manager integration
if: runner.os == 'Windows'
working-directory: src-tauri
run: cargo test --test queue_manager --no-run --target ${{ matrix.target }}
- name: Compile Windows Rust library tests
if: runner.os == 'Windows'
working-directory: src-tauri
run: cargo test --lib --no-run --target ${{ matrix.target }}
- name: Run Windows headless production contracts
if: runner.os == 'Windows'
working-directory: src-tauri
run: cargo test --test production_contract --target ${{ matrix.target }} -- --nocapture
- name: Verify Windows atomic Torrent storage
if: runner.os == 'Windows'
working-directory: src-tauri
run: cargo test --test atomic_file --target ${{ matrix.target }} -- --nocapture
- name: Verify Windows Torrent cache safety
if: runner.os == 'Windows'
working-directory: src-tauri
run: cargo test --test torrent_cache --target ${{ matrix.target }} -- --nocapture
- name: Verify Windows Torrent web-seed normalization
if: runner.os == 'Windows'
working-directory: src-tauri
run: cargo test --test torrent_web_seed --target ${{ matrix.target }} -- --nocapture
- name: Install Aria2 source build dependencies (Linux)
if: runner.os == 'Linux'
run: sudo apt-get install -y autoconf automake libtool gettext autopoint libssl-dev libssh2-1-dev libgcrypt20-dev libc-ares-dev libexpat1-dev libsqlite3-dev zlib1g-dev
- name: Install Aria2 source build dependencies (Windows)
id: aria2-msys
if: runner.os == 'Windows'
uses: msys2/setup-msys2@v2
with:
msystem: MINGW64
install: >-
base-devel autoconf automake libtool gettext-devel pkgconf
mingw-w64-x86_64-gcc mingw-w64-x86_64-pkgconf
mingw-w64-x86_64-openssl mingw-w64-x86_64-libssh2
mingw-w64-x86_64-c-ares mingw-w64-x86_64-expat
mingw-w64-x86_64-sqlite3 mingw-w64-x86_64-zlib
- name: Fingerprint engine toolchain
id: engine-toolchain
if: runner.os != 'macOS'
env:
FIRELINK_MSYS2_ROOT: ${{ steps.aria2-msys.outputs.msys2-location }}
FIRELINK_TARGET_TRIPLE: ${{ matrix.target }}
run: node scripts/engine-toolchain-fingerprint.js
- name: Restore verified engine payload cache
id: engine-cache
if: runner.os != 'macOS'
# v4.2.0 pinned to an immutable commit; this cache is an optimization,
# and a miss always falls back to source provisioning below.
uses: actions/cache/restore@1bd1e32a3bdc45362d1e726936510720a7c30a57
with:
path: src-tauri/provisioned-engines/${{ matrix.target }}
# The target, toolchain fingerprint, lockfiles, provisioning code,
# payload validators, and runner package lists all invalidate the key.
key: firelink-engine-payload-v1-${{ matrix.target }}-${{ steps.engine-toolchain.outputs.fingerprint }}-${{ hashFiles('engine-sources.lock.json', 'engines.lock.json', 'scripts/aria2/**', 'scripts/engine-*.js', 'scripts/provision-engines.js', 'scripts/stage-engines.js', 'scripts/verify-binaries.js', 'scripts/aria2-route-contract.js', '.github/workflows/ci.yml', '.github/workflows/release.yml') }}
- name: Validate restored engine payload
id: engine-cache-validation
if: runner.os != 'macOS' && steps.engine-cache.outputs.cache-hit == 'true'
continue-on-error: true
env:
FIRELINK_TARGET_TRIPLE: ${{ matrix.target }}
FIRELINK_ENGINE_OUTPUT_ROOT: ${{ runner.temp }}/firelink-engine-cache-validation/${{ matrix.target }}
run: |
node scripts/stage-engines.js
node scripts/verify-binaries.js --staged
- name: Restore verified Aria2 build cache
id: aria2-cache
if: >-
runner.os != 'macOS' &&
(steps.engine-cache.outputs.cache-hit != 'true' ||
steps.engine-cache-validation.outcome != 'success')
uses: actions/cache/restore@1bd1e32a3bdc45362d1e726936510720a7c30a57
with:
path: src-tauri/provisioned-engines/.aria2-cache/${{ matrix.target }}
key: firelink-aria2-build-v1-${{ matrix.target }}-${{ steps.engine-toolchain.outputs.aria2-fingerprint }}
- name: Provision locked engines
if: >-
runner.os != 'macOS' &&
(steps.engine-cache.outputs.cache-hit != 'true' ||
steps.engine-cache-validation.outcome != 'success')
env:
FIRELINK_MSYS2_ROOT: ${{ steps.aria2-msys.outputs.msys2-location }}
FIRELINK_TOOLCHAIN_FINGERPRINT: ${{ steps.engine-toolchain.outputs.fingerprint }}
run: node scripts/provision-engines.js --target ${{ matrix.target }}
- name: Stage and verify engines
env:
FIRELINK_ENGINE_OUTPUT_ROOT: ${{ runner.temp }}/firelink-engine-workspace/${{ matrix.target }}/engine-dist
run: |
node scripts/stage-engines.js --target ${{ matrix.target }}
node scripts/verify-binaries.js --staged --target ${{ matrix.target }}
- name: Run Torrent process smoke
env:
FIRELINK_ENGINE_OUTPUT_ROOT: ${{ runner.temp }}/firelink-engine-workspace/${{ matrix.target }}/engine-dist
run: node scripts/smoke-torrent.js --failure-paths
- name: Run Aria2 resolver smoke
env:
FIRELINK_ENGINE_OUTPUT_ROOT: ${{ runner.temp }}/firelink-engine-workspace/${{ matrix.target }}/engine-dist
run: node scripts/smoke-aria2-resolver.js
- name: Run Aria2 normal-transfer smoke
env:
FIRELINK_ENGINE_OUTPUT_ROOT: ${{ runner.temp }}/firelink-engine-workspace/${{ matrix.target }}/engine-dist
run: node scripts/smoke-aria2-transfers.js
- name: Save verified Aria2 build cache
if: >-
runner.os != 'macOS' &&
github.event_name == 'push' &&
github.ref == 'refs/heads/main' &&
steps.aria2-cache.outputs.cache-hit != 'true'
uses: actions/cache/save@1bd1e32a3bdc45362d1e726936510720a7c30a57
with:
path: src-tauri/provisioned-engines/.aria2-cache/${{ matrix.target }}
key: firelink-aria2-build-v1-${{ matrix.target }}-${{ steps.engine-toolchain.outputs.aria2-fingerprint }}
- name: Save verified engine payload cache
if: >-
runner.os != 'macOS' &&
github.event_name == 'push' &&
github.ref == 'refs/heads/main' &&
steps.engine-cache.outputs.cache-hit != 'true'
uses: actions/cache/save@1bd1e32a3bdc45362d1e726936510720a7c30a57
with:
path: src-tauri/provisioned-engines/${{ matrix.target }}
key: firelink-engine-payload-v1-${{ matrix.target }}-${{ steps.engine-toolchain.outputs.fingerprint }}-${{ hashFiles('engine-sources.lock.json', 'engines.lock.json', 'scripts/aria2/**', 'scripts/engine-*.js', 'scripts/provision-engines.js', 'scripts/stage-engines.js', 'scripts/verify-binaries.js', 'scripts/aria2-route-contract.js', '.github/workflows/ci.yml', '.github/workflows/release.yml') }}