phostt 0.2.0

Local STT server powered by Zipformer-vi RNN-T — on-device Vietnamese speech recognition via ONNX Runtime
Documentation
name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

env:
  FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true

jobs:
  fmt:
    runs-on: ubuntu-latest
    name: Format
    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: rustfmt
      - run: cargo fmt --check

  clippy:
    runs-on: ubuntu-latest
    name: Clippy
    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: clippy
      - uses: arduino/setup-protoc@v3
        with:
          version: '29.x'
          repo-token: ${{ secrets.GITHUB_TOKEN }}
      - uses: actions/cache@v4
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            target
          key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
      - run: cargo clippy -- -D warnings -A dead_code

  unit-tests:
    runs-on: ubuntu-latest
    name: Unit Tests
    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@stable
      - uses: arduino/setup-protoc@v3
        with:
          version: '29.x'
          repo-token: ${{ secrets.GITHUB_TOKEN }}
      - uses: actions/cache@v4
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            target
          key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
      - run: cargo test

  build-coreml:
    runs-on: macos-14
    name: Build (CoreML)
    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@stable
      - uses: arduino/setup-protoc@v3
        with:
          version: '29.x'
          repo-token: ${{ secrets.GITHUB_TOKEN }}
      - uses: actions/cache@v4
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            target
          key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
      - run: cargo build --release --features coreml

  build-cuda:
    runs-on: ubuntu-latest
    name: Build (CUDA)
    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@stable
      - uses: arduino/setup-protoc@v3
        with:
          version: '29.x'
          repo-token: ${{ secrets.GITHUB_TOKEN }}
      - uses: actions/cache@v4
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            target
          key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
      - run: cargo build --release --features cuda

  build-diarization:
    runs-on: ubuntu-latest
    name: Build (Diarization)
    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@stable
      - uses: arduino/setup-protoc@v3
        with:
          version: '29.x'
          repo-token: ${{ secrets.GITHUB_TOKEN }}
      - uses: actions/cache@v4
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            target
          key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
      - run: cargo build --release --features diarization

  e2e-tests:
    # Only run on main push — PRs get fast feedback from unit tests + clippy
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    runs-on: ubuntu-latest
    name: E2E Tests
    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@stable
      - uses: arduino/setup-protoc@v3
        with:
          version: '29.x'
          repo-token: ${{ secrets.GITHUB_TOKEN }}
      - uses: actions/cache@v4
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            target
          key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}

      - name: Cache model
        id: cache-model
        uses: actions/cache@v4
        with:
          path: ~/.phostt/models
          key: phostt-model-v3-onnx-${{ hashFiles('src/model/mod.rs') }}

      - name: Download model
        if: steps.cache-model.outputs.cache-hit != 'true'
        run: cargo run -- download

      - name: Run e2e tests
        run: cargo test --test e2e_rest --test e2e_ws --test e2e_errors --test e2e_shutdown -- --ignored --test-threads=1
        timeout-minutes: 30

  audit:
    runs-on: ubuntu-latest
    name: Security Audit
    steps:
      - uses: actions/checkout@v6
      # `rustsec/audit-check` ships a prebuilt cargo-audit, saving the ~90 s
      # `cargo install --locked` rebuild on every PR run.
      - uses: rustsec/audit-check@v2
        with:
          token: ${{ secrets.GITHUB_TOKEN }}

  deny:
    runs-on: ubuntu-latest
    name: Cargo Deny
    steps:
      - uses: actions/checkout@v6
      - uses: EmbarkStudios/cargo-deny-action@v2
        with:
          # Default `check` covers licenses + advisories + bans + sources.
          # An earlier attempt passed them as `arguments: licenses advisories …`,
          # but the stable-musl `cargo-deny` refused the trailing positional
          # args as subcommands (`unrecognized subcommand 'licenses'`).
          command: check