Skip to content

feat(queue): backend queue state machine replacing frontend shuffle, navigation, and play-next logic #361

feat(queue): backend queue state machine replacing frontend shuffle, navigation, and play-next logic

feat(queue): backend queue state machine replacing frontend shuffle, navigation, and play-next logic #361

Workflow file for this run

name: Test
on:
workflow_dispatch:
inputs:
linux-runner:
description: 'Linux runner label (custom image or vanilla fallback)'
required: false
default: 'blacksmith-4vcpu-ubuntu-2404'
type: choice
options:
- blacksmith-4vcpu-ubuntu-2404
- blacksmith-4vcpu-ubuntu-2404-mt
pull_request:
paths:
- 'crates/**'
- 'app/**'
- 'Cargo.*'
- 'taskfiles/**'
- '.github/**'
push:
branches: [main]
paths:
- 'crates/**'
- 'app/**'
- 'Cargo.*'
- 'taskfiles/**'
- '.github/**'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
rust:
name: Rust Lint, Format, and Test
runs-on: [macOS, ARM64]
timeout-minutes: 20
env:
CARGO_INCREMENTAL: 1
CARGO_TERM_COLOR: always
steps:
- uses: actions/checkout@v6
- name: Setup Tauri build environment
uses: ./.github/actions/setup-tauri-build
with:
mode: test
- name: Cargo fmt check
run: cargo fmt --all -- --check
- name: Cargo clippy
run: cargo clippy --workspace --all-features -- -D warnings
- name: Run Rust tests
run: cargo nextest run --workspace
# Coverage runs only on main push (not PRs) to keep the critical path fast
rust-coverage:
name: Rust Coverage
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: [macOS, ARM64]
timeout-minutes: 25
env:
CARGO_INCREMENTAL: 1
CARGO_TERM_COLOR: always
steps:
- uses: actions/checkout@v6
- name: Setup Tauri build environment
uses: ./.github/actions/setup-tauri-build
with:
mode: check
- uses: cargo-bins/cargo-binstall@main
- name: Install cargo-tarpaulin
run: cargo binstall --no-confirm --force cargo-tarpaulin
- name: Run Rust tests with coverage
run: |
cargo tarpaulin --workspace --out Html --out Json --output-dir coverage \
--ignore-tests --skip-clean \
--exclude-files 'crates/mt-tauri/src/commands/*' 'crates/mt-tauri/src/lib.rs' 'crates/mt-tauri/src/main.rs' 'crates/mt-tauri/src/watcher.rs' 'crates/mt-tauri/src/dialog.rs' 'crates/mt-tauri/src/media_keys.rs' \
--fail-under 50
- name: Upload coverage report
if: always()
continue-on-error: true
uses: actions/upload-artifact@v7
with:
name: rust-coverage
path: coverage/
retention-days: 30
deno-lint:
name: Deno Lint and Format Check
runs-on: blacksmith-4vcpu-ubuntu-2404
timeout-minutes: 10
steps:
- uses: actions/checkout@v6
- name: Install Deno
uses: denoland/setup-deno@v2
with:
deno-version: v2.x
- name: Check Deno formatting
run: deno fmt --check
- name: Run Deno lint
run: deno lint
build:
name: Build (${{ matrix.platform }})
# Gate on lint/format/test jobs so builds don't burn CI minutes
# when basic checks fail.
needs: [deno-lint, rust, vitest-tests, playwright-tests]
if: ${{ !failure() && !cancelled() }}
strategy:
fail-fast: false
matrix:
include:
- platform: macos
os: [macOS, ARM64]
target: aarch64-apple-darwin
- platform: linux
os: ${{ inputs.linux-runner || 'blacksmith-4vcpu-ubuntu-2404' }}
target: x86_64-unknown-linux-gnu
- platform: windows
os: blacksmith-4vcpu-windows-2025
target: x86_64-pc-windows-msvc
runs-on: ${{ matrix.os }}
timeout-minutes: 30
steps:
- uses: actions/checkout@v6
# Linux: skip Docker build when cargo-check inputs haven't changed.
# Hash covers everything the Dockerfile check target copies:
# Cargo manifests, lockfile, Rust source, cargo config, and the
# Dockerfile itself. Cache hit means cargo check already passed
# for these exact inputs β€” no need to spin up Docker at all.
- name: Check cargo-check cache (Linux)
if: runner.os == 'Linux'
id: cargo-check-cache
uses: actions/cache@v4
with:
path: .cargo-check-sentinel
key: linux-cargo-check-${{ hashFiles('Cargo.toml', 'Cargo.lock', 'crates/**/*.rs', 'crates/**/Cargo.toml', 'crates/**/.cargo/**', 'docker/linux/Dockerfile') }}
- name: Skip notice (Linux)
if: runner.os == 'Linux' && steps.cargo-check-cache.outputs.cache-hit == 'true'
run: echo "cargo check inputs unchanged β€” skipping Docker build"
# Linux: containerized cargo check with Blacksmith Docker layer caching
- name: Setup Docker builder (Linux)
if: runner.os == 'Linux' && steps.cargo-check-cache.outputs.cache-hit != 'true'
uses: useblacksmith/setup-docker-builder@v1
- name: Cargo check (Linux)
if: runner.os == 'Linux' && steps.cargo-check-cache.outputs.cache-hit != 'true'
uses: useblacksmith/build-push-action@v2
with:
context: .
file: docker/linux/Dockerfile
target: check
push: false
- name: Write cargo-check sentinel (Linux)
if: runner.os == 'Linux' && steps.cargo-check-cache.outputs.cache-hit != 'true'
run: date -u > .cargo-check-sentinel
# macOS/Windows: bare-metal setup + cargo check
- name: Setup Tauri build environment
if: runner.os != 'Linux'
uses: ./.github/actions/setup-tauri-build
with:
mode: check
- name: Cargo check (macOS)
if: runner.os == 'macOS'
shell: bash
env:
GITHUB_TOKEN: ${{ github.token }}
run: task ci:check TARGET=${{ matrix.target }}
- name: Cargo check (Windows)
if: runner.os == 'Windows'
shell: pwsh
env:
GITHUB_TOKEN: ${{ github.token }}
run: task ci:check TARGET=${{ matrix.target }}
vitest-tests:
name: Vitest Unit Tests
runs-on: blacksmith-4vcpu-ubuntu-2404
timeout-minutes: 10
steps:
- uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: app/frontend/package-lock.json
- name: Install frontend dependencies
working-directory: ./app/frontend
run: npm ci
- name: Run Vitest with coverage
working-directory: ./app/frontend
run: npm run test:coverage
- name: Upload coverage report
if: always()
continue-on-error: true
uses: actions/upload-artifact@v7
with:
name: vitest-coverage
path: app/frontend/coverage/
retention-days: 30
# Detect frontend changes for conditional Playwright execution
changes:
name: Detect Changes
runs-on: blacksmith-4vcpu-ubuntu-2404
timeout-minutes: 2
outputs:
frontend: ${{ steps.filter.outputs.frontend }}
steps:
- uses: actions/checkout@v6
- uses: dorny/paths-filter@v4
id: filter
with:
filters: |
frontend:
- 'app/frontend/**'
playwright-tests:
name: Playwright E2E Tests
needs: [changes, deno-lint, rust, vitest-tests]
if: ${{ !failure() && !cancelled() && (needs.changes.outputs.frontend == 'true' || github.event_name == 'push') }}
runs-on: [macOS, ARM64]
timeout-minutes: 20
steps:
- uses: actions/checkout@v6
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '20'
- name: Install frontend dependencies
working-directory: ./app/frontend
run: npm ci
- name: Install Playwright browsers
working-directory: ./app/frontend
run: npx playwright install --with-deps webkit
- name: Kill stale dev server on port 5173
run: lsof -ti:5173 | xargs kill -9 2>/dev/null || true
- name: Run Playwright tests
working-directory: ./app/frontend
run: npx playwright test
env:
CI: true
- name: Upload Playwright report
if: always()
continue-on-error: true
uses: actions/upload-artifact@v7
with:
name: playwright-report
path: playwright-report/
retention-days: 30
- name: Upload test results
if: always()
continue-on-error: true
uses: actions/upload-artifact@v7
with:
name: playwright-results
path: test-results/
retention-days: 30