cargo-crap 0.0.2

Change Risk Anti-Patterns (CRAP) metric for Rust projects
Documentation
name: CI

on:
  push:
    branches: [ main ]
  pull_request:

env:
  CARGO_TERM_COLOR: always
  RUSTFLAGS: "-D warnings"

jobs:
  test:
    name: Test (${{ matrix.os }})
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [ ubuntu-latest, macos-latest, windows-latest ]
    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - name: Build
        run: cargo build --all-targets
      - name: Test
        run: cargo test --all-targets
      - name: Doc tests
        run: cargo test --doc

  lints:
    name: Lints
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: rustfmt, clippy
      - uses: Swatinem/rust-cache@v2
      - name: rustfmt
        run: cargo fmt --all -- --check
      - name: clippy
        run: cargo clippy --all-targets -- -D warnings

  msrv:
    name: MSRV (Rust 1.88)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@1.88
      - uses: Swatinem/rust-cache@v2
      - name: Build (MSRV)
        run: cargo build --all-targets
      - name: Test (MSRV)
        run: cargo test --all-targets

  audit:
    name: Security audit
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - name: Install cargo-audit
        uses: taiki-e/install-action@cargo-audit

      - name: Run security audit
        run: cargo audit

  mutants:
    name: Mutation tests
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@stable
      - uses: Swatinem/rust-cache@v2
      - uses: taiki-e/install-action@v2
        with:
          tool: cargo-mutants
      - name: Run mutation tests
        run: cargo mutants --timeout 60
      - name: Upload mutants output
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: mutants-out
          path: mutants.out/

  # Dogfood: run cargo-crap against its own source, gate on threshold 15.
  # On main: also save a JSON baseline as a CI artifact for PR regression gating.
  # On PRs: download the main baseline and fail if any score regressed.
  self_score:
    name: Self-score
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: llvm-tools-preview
      - uses: taiki-e/install-action@v2
        with:
          tool: cargo-llvm-cov
      - uses: Swatinem/rust-cache@v2

      - name: Generate coverage
        run: cargo llvm-cov --lcov --output-path lcov.info --workspace

      # --- Absolute threshold gate (always) ---
      - name: Score (threshold 15, exclude fixtures)
        run: |
          cargo run --release -- \
            --lcov lcov.info \
            --workspace \
            --exclude 'tests/fixtures/**' \
            --threshold 15 \
            --fail-above \
            --format json \
            --output crap-current.json

      # --- On main: publish baseline for future PRs to compare against ---
      - name: Upload baseline (main only)
        if: github.ref == 'refs/heads/main'
        uses: actions/upload-artifact@v4
        with:
          name: crap-baseline
          path: crap-current.json
          retention-days: 90

      # --- On PRs: download baseline and fail on any regression ---
      - name: Download baseline (PRs only)
        if: github.event_name == 'pull_request'
        uses: actions/download-artifact@v4
        with:
          name: crap-baseline
        continue-on-error: true # first PR after a fresh repo has no baseline yet

      - name: Regression check (PRs only)
        if: github.event_name == 'pull_request'
        run: |
          if [ -f crap-current.json ]; then
            cargo run --release -- \
              --lcov lcov.info \
              --workspace \
              --exclude 'tests/fixtures/**' \
              --baseline crap-current.json \
              --fail-regression
          else
            echo "No baseline available — skipping regression check."
          fi