pdmt 1.0.1

High-performance, deterministic templating library for Model Context Protocol (MCP) applications with comprehensive todo validation and quality enforcement
name: CI

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

env:
  CARGO_TERM_COLOR: always
  RUST_BACKTRACE: 1

jobs:
  test:
    name: Test Suite
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest]
        rust: [stable, beta, nightly]
        exclude:
          # Skip some combinations to reduce CI time
          - os: macos-latest
            rust: beta
    
    steps:
    - name: Checkout repository
      uses: actions/checkout@v4

    - name: Install Rust toolchain
      uses: dtolnay/rust-toolchain@master
      with:
        toolchain: ${{ matrix.rust }}
        components: rustfmt, clippy

    - name: Cache Cargo registry
      uses: actions/cache@v3
      with:
        path: |
          ~/.cargo/registry
          ~/.cargo/git
          target
        key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
        restore-keys: |
          ${{ runner.os }}-cargo-

    - name: Install cargo-tarpaulin (Ubuntu only)
      if: matrix.os == 'ubuntu-latest' && matrix.rust == 'stable'
      run: cargo install cargo-tarpaulin

    - name: Check formatting
      run: cargo fmt --all -- --check

    - name: Lint with clippy
      run: |
        cargo clippy --all-targets --all-features -- \
          -D clippy::all \
          -D clippy::pedantic \
          -D clippy::nursery \
          -D clippy::cargo \
          -A clippy::multiple-crate-versions \
          -A clippy::format-push-string \
          -A clippy::only-used-in-recursion \
          -A clippy::match-same-arms \
          -A clippy::map-unwrap-or \
          -A clippy::struct-excessive-bools \
          -A clippy::field-reassign-with-default \
          -A clippy::cast-possible-truncation \
          -A clippy::option-if-let-else \
          -A clippy::cast-precision-loss \
          -A clippy::if-not-else \
          -A clippy::missing-const-for-fn \
          -A clippy::return-self-not-must-use \
          -A clippy::doc-markdown \
          -A clippy::float-cmp \
          -A clippy::unused-async \
          -A clippy::suboptimal-flops \
          -A clippy::use-self \
          -A clippy::must-use-candidate \
          -A clippy::missing-errors-doc \
          -A clippy::uninlined-format-args \
          -A clippy::derivable-impls \
          -A clippy::redundant-closure-for-method-calls \
          -A clippy::cast-lossless \
          -A clippy::too-many-lines \
          -A clippy::let-and-return \
          -A clippy::needless-raw-string-hashes \
          -A clippy::len-zero \
          -A clippy::no-effect-underscore-binding \
          -A clippy::redundant-clone \
          -A clippy::unnecessary-wraps \
          -A clippy::manual-clamp \
          -A clippy::single-char-add-str \
          -A clippy::items-after-statements \
          -A clippy::needless-collect \
          -A clippy::needless-borrows-for-generic-args \
          -A clippy::assigning-clones \
          -A clippy::trivially-copy-pass-by-ref

    - name: Build
      run: cargo build --verbose --all-features

    - name: Run tests
      run: cargo test --verbose --all-features

    - name: Run doctests
      run: cargo test --doc --all-features

    - name: Test examples
      run: |
        cargo run --example todo_generation --features="full" -- --project "CI Test Project" --requirement "Test requirement 1" --requirement "Test requirement 2" --granularity high --max-todos 5 --format yaml

    - name: Generate coverage report (Ubuntu stable only)
      if: matrix.os == 'ubuntu-latest' && matrix.rust == 'stable'
      run: |
        cargo tarpaulin --all-features --workspace --timeout 120 --out xml --output-dir ./coverage/

    - name: Upload coverage to Codecov
      if: matrix.os == 'ubuntu-latest' && matrix.rust == 'stable'
      uses: codecov/codecov-action@v3
      with:
        file: ./coverage/cobertura.xml
        flags: unittests
        name: codecov-umbrella
        fail_ci_if_error: false

  security:
    name: Security Audit
    runs-on: ubuntu-latest
    steps:
    - name: Checkout repository  
      uses: actions/checkout@v4

    - name: Install Rust toolchain
      uses: dtolnay/rust-toolchain@stable

    - name: Install cargo-audit
      run: cargo install cargo-audit

    - name: Security audit
      run: cargo audit

  fuzz:
    name: Fuzz Testing
    runs-on: ubuntu-latest
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    
    steps:
    - name: Checkout repository
      uses: actions/checkout@v4

    - name: Install Rust nightly
      uses: dtolnay/rust-toolchain@nightly

    - name: Install cargo-fuzz
      run: cargo install cargo-fuzz

    - name: Run fuzz tests (short duration for CI)
      run: |
        cd fuzz
        timeout 60 cargo fuzz run fuzz_template_engine -- -max_total_time=30 || true
        timeout 60 cargo fuzz run fuzz_todo_validator -- -max_total_time=30 || true

  minimum-supported-rust-version:
    name: Minimum Supported Rust Version
    runs-on: ubuntu-latest
    
    steps:
    - name: Checkout repository
      uses: actions/checkout@v4

    - name: Install Rust 1.82.0
      uses: dtolnay/rust-toolchain@master
      with:
        toolchain: 1.82.0

    - name: Check build with MSRV
      run: cargo check --all-features

  docs:
    name: Documentation
    runs-on: ubuntu-latest
    
    steps:
    - name: Checkout repository
      uses: actions/checkout@v4

    - name: Install Rust toolchain
      uses: dtolnay/rust-toolchain@stable

    - name: Build documentation
      run: cargo doc --all-features --no-deps --document-private-items

    - name: Check documentation tests
      run: cargo test --doc --all-features