omni-dev 0.33.0

AI-powered git commit rewriter, PR generator, and MCP server for Jira, Confluence, and Datadog.
Documentation
name: CI

on:
  push:
    branches: [ main ]
    tags: [ 'v*' ]
  pull_request:
    branches: [ main ]

env:
  CARGO_TERM_COLOR: always

jobs:
  # Path-split gate (#609). Docs/skills-only PRs skip the heavy jobs while still
  # publishing every required status context, so they merge in seconds without
  # deadlocking branch protection. A single gate is the source of truth for the
  # split — no twin `ci-skip.yml` whose stub names must be hand-mirrored against
  # this matrix (which would silently rot when the matrix or protection set
  # changes). Non-PR events (main pushes, release tags) always take the full
  # path. `src/templates/*.md` are include_str!-compiled and unit-tested
  # (ADR-0030), so the doc set scopes `*.md` to the repo root only — never
  # `**/*.md` — and anything not explicitly a doc runs full CI (fail-safe).
  gate:
    name: Gate
    runs-on: ubuntu-latest
    outputs:
      code: ${{ steps.decide.outputs.code }}
    steps:
    - uses: actions/checkout@v7
    - uses: dorny/paths-filter@v3
      id: filter
      if: github.event_name == 'pull_request'
      with:
        filters: |
          code:
            - '**'
            - '!*.md'
            - '!docs/**'
            - '!.claude/skills/**'
    - id: decide
      run: |
        if [ "${{ github.event_name }}" != "pull_request" ]; then
          echo "code=true" >> "$GITHUB_OUTPUT"
        else
          echo "code=${{ steps.filter.outputs.code }}" >> "$GITHUB_OUTPUT"
        fi

  # Required-context jobs (Test, Clippy, Rustfmt, Docs) always run so their
  # contexts always report `success`; on the fast path every step is skipped,
  # which is a genuine passing job — not a `skipped` conclusion whose
  # branch-protection semantics vary. Non-required jobs below skip wholesale.
  test:
    name: Test
    needs: gate
    runs-on: ubuntu-latest
    strategy:
      matrix:
        rust:
          - stable
          - beta
          - nightly
        features:
          - ""
          - "mcp"
    steps:
    - uses: actions/checkout@v7
      if: needs.gate.outputs.code == 'true'
    - uses: dtolnay/rust-toolchain@master
      if: needs.gate.outputs.code == 'true'
      with:
        toolchain: ${{ matrix.rust }}
        components: rustfmt
    - uses: Swatinem/rust-cache@v2
      if: needs.gate.outputs.code == 'true'
    - name: Run tests
      if: needs.gate.outputs.code == 'true'
      run: |
        if [ -z "${{ matrix.features }}" ]; then
          cargo test --verbose
        else
          cargo test --features "${{ matrix.features }}" --verbose
        fi

  mcp-build:
    name: MCP Release Build
    needs: gate
    if: needs.gate.outputs.code == 'true'
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v7
    - uses: dtolnay/rust-toolchain@stable
    - uses: Swatinem/rust-cache@v2
    - name: Build omni-dev-mcp (release)
      run: cargo build --release --features mcp --bin omni-dev-mcp

  windows-build:
    name: Windows Build
    needs: gate
    if: needs.gate.outputs.code == 'true'
    runs-on: windows-latest
    # Mirrors the release.yml win-msvc binary build so platform-portability
    # regressions (e.g. the Unix-only daemon, #1041) are caught on every PR
    # rather than only when a release tag is pushed.
    steps:
    - uses: actions/checkout@v7
    - uses: dtolnay/rust-toolchain@stable
    - uses: Swatinem/rust-cache@v2
    - name: Build binaries (release, mcp)
      run: cargo build --release --features mcp --bin omni-dev --bin omni-dev-mcp

  fmt:
    name: Rustfmt
    needs: gate
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v7
      if: needs.gate.outputs.code == 'true'
    - uses: dtolnay/rust-toolchain@stable
      if: needs.gate.outputs.code == 'true'
      with:
        components: rustfmt
    - name: Check formatting
      if: needs.gate.outputs.code == 'true'
      run: cargo fmt --all -- --check

  clippy:
    name: Clippy
    needs: gate
    runs-on: ubuntu-latest
    strategy:
      matrix:
        features:
          - ""
          - "mcp"
    steps:
    - uses: actions/checkout@v7
      if: needs.gate.outputs.code == 'true'
    - uses: dtolnay/rust-toolchain@stable
      if: needs.gate.outputs.code == 'true'
      with:
        components: clippy
    - uses: Swatinem/rust-cache@v2
      if: needs.gate.outputs.code == 'true'
    - name: Run clippy
      if: needs.gate.outputs.code == 'true'
      run: |
        if [ -z "${{ matrix.features }}" ]; then
          cargo clippy --all-targets -- -D warnings
        else
          cargo clippy --all-targets --features "${{ matrix.features }}" -- -D warnings
        fi

  docs:
    name: Docs
    needs: gate
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v7
      if: needs.gate.outputs.code == 'true'
    - uses: dtolnay/rust-toolchain@stable
      if: needs.gate.outputs.code == 'true'
    - uses: Swatinem/rust-cache@v2
      if: needs.gate.outputs.code == 'true'
    - name: Build docs
      if: needs.gate.outputs.code == 'true'
      run: cargo doc --no-deps --document-private-items

  coverage:
    name: Coverage
    needs: gate
    if: needs.gate.outputs.code == 'true'
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write        # required to post the coverage comment
    steps:
    - uses: actions/checkout@v7
      with:
        fetch-depth: 0          # full history so `git merge-base` can resolve the PR's fork point
    # The coverage pipeline — cargo-llvm-cov run, merge-base baseline (download
    # + git-worktree recompute fallback), `omni-dev coverage diff` sticky PR
    # comment, summary + artifacts, baseline publish on main, and the line gate
    # — is packaged in action-works/omni-dev-coverage-check (issue #1024).
    # worktree-system-deps installs the ALSA headers only on the merge-base
    # recompute path, for fork points that predate the cpal/voice removal
    # (#980); a no-op once the merge-base is itself cpal-free.
    - uses: action-works/omni-dev-coverage-check@v1
      with:
        fail-under-lines: 30
        worktree-system-deps: libasound2-dev

  audit:
    name: Security Audit
    needs: gate
    if: needs.gate.outputs.code == 'true'
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v7
    # Pull a prebuilt cargo-audit binary instead of `cargo install` (which
    # compiles from source every run — rust-cache can't help since it doesn't
    # cache ~/.cargo/bin). Mirrors the prebuilt cargo-deny-action below.
    - uses: taiki-e/install-action@v2
      with:
        tool: cargo-audit
    - name: Run cargo-audit
      run: cargo audit

  deny:
    name: Dependency Policy
    needs: gate
    if: needs.gate.outputs.code == 'true'
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v7
    - uses: EmbarkStudios/cargo-deny-action@v2.0.20
      with:
        command: check all

  secrets:
    name: Secret Scanning
    needs: gate
    if: needs.gate.outputs.code == 'true'
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v7
      with:
        fetch-depth: 0
    - name: TruffleHog scan
      uses: trufflesecurity/trufflehog@main
      with:
        extra_args: --only-verified