sdforge 0.3.1

Multi-protocol SDK framework with unified macro configuration
name: CI

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

permissions:
  contents: read

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

env:
  CARGO_TERM_COLOR: always
  RUST_BACKTRACE: 1

jobs:
  # 代码格式检查
  fmt:
    name: Format Check
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5  # v4

      - name: Install Rust
        uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30  # stable
        with:
          components: rustfmt

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

  # Clippy 代码质量检查
  clippy:
    name: Clippy Lint
    runs-on: ubuntu-latest
    timeout-minutes: 30
    steps:
      - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5  # v4

      - name: Install Rust
        uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30  # stable
        with:
          components: clippy

      - name: Cache cargo registry
        uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9  # v6.1.0
        with:
          path: ~/.cargo/registry
          key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: ${{ runner.os }}-cargo-registry-

      - name: Cache cargo index
        uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9  # v6.1.0
        with:
          path: ~/.cargo/git
          key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: ${{ runner.os }}-cargo-index-

      - name: Run clippy
        run: cargo clippy --all-targets --all-features --workspace -- -D warnings

  # 安全审计
  security:
    name: Security Audit
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5  # v4

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

      - name: Install cargo-deny
        run: cargo install --locked cargo-deny

      - name: Run cargo-deny
        run: cargo deny check advisories bans licenses

  # 依赖检查
  dependencies:
    name: Dependency Check
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5  # v4

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

      - name: Cache cargo registry
        uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9  # v6.1.0
        with:
          path: ~/.cargo/registry
          key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: ${{ runner.os }}-cargo-registry-

      - name: Cache cargo index
        uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9  # v6.1.0
        with:
          path: ~/.cargo/git
          key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: ${{ runner.os }}-cargo-index-

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

      - name: Run cargo-audit
        run: cargo audit

  # 多平台构建检查
  build:
    name: Build (${{ matrix.os }})
    runs-on: ${{ matrix.os }}
    timeout-minutes: 30
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
    steps:
      - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5  # v4

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

      - name: Cache cargo registry
        uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9  # v6.1.0
        with:
          path: ~/.cargo/registry
          key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: ${{ runner.os }}-cargo-registry-

      - name: Cache cargo index
        uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9  # v6.1.0
        with:
          path: ~/.cargo/git
          key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: ${{ runner.os }}-cargo-index-

      - name: Cache cargo build
        uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9  # v6.1.0
        with:
          path: target
          key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: ${{ runner.os }}-cargo-build-target-

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

  # 特性矩阵测试
  test-features:
    name: Test (${{ matrix.features }})
    runs-on: ubuntu-latest
    timeout-minutes: 45
    strategy:
      fail-fast: false
      matrix:
        features:
          - "http"
          - "mcp"
          - "http,mcp"
          - "http,security"
          - "http,cache"
          - "http,websocket"
          - "http,grpc"
          - "http,streaming"
          - "full"
    steps:
      - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5  # v4

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

      - name: Cache cargo registry
        uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9  # v6.1.0
        with:
          path: ~/.cargo/registry
          key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: ${{ runner.os }}-cargo-registry-

      - name: Cache cargo index
        uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9  # v6.1.0
        with:
          path: ~/.cargo/git
          key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: ${{ runner.os }}-cargo-index-

      - name: Cache cargo build
        uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9  # v6.1.0
        with:
          path: target
          key: ${{ runner.os }}-cargo-build-target-${{ matrix.features }}-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: ${{ runner.os }}-cargo-build-target-

      - name: Run tests with features ${{ matrix.features }}
        run: cargo test --features "${{ matrix.features }}" --workspace

  # 代码覆盖率检查
  coverage:
    name: Code Coverage (95% Gate)
    runs-on: ubuntu-latest
    timeout-minutes: 90
    permissions:
      contents: read
      actions: write
    steps:
      - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5  # v4

      - name: Install Rust
        uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30  # stable
        with:
          components: llvm-tools-preview

      - name: Cache cargo registry
        uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9  # v6.1.0
        with:
          path: ~/.cargo/registry
          key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: ${{ runner.os }}-cargo-registry-

      - name: Cache cargo index
        uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9  # v6.1.0
        with:
          path: ~/.cargo/git
          key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: ${{ runner.os }}-cargo-index-

      - name: Cache cargo build
        uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9  # v6.1.0
        with:
          path: target
          key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: ${{ runner.os }}-cargo-build-target-

      - name: Install cargo-tarpaulin
        run: cargo install --locked cargo-tarpaulin

      - name: Generate coverage
        # Use `--features full --lib` instead of `--all-features --workspace` to
        # avoid timeouts on the macros crate's trybuild integration tests, which
        # spawn child processes that exceed tarpaulin's test-response timeout.
        # The .tarpaulin.toml at the repo root already excludes
        # `src/grpc/pb/*` (auto-generated protobuf) and `macros/*`.
        run: |
          cargo tarpaulin \
            --features full \
            --lib \
            --timeout 120 \
            --out xml \
            --out html \
            --output-dir coverage-report \
            --fail-under 95 \
            --exclude-files "target/*" \
            --exclude-files "examples/*" \
            --exclude-files "temp/*" \
            --exclude-files "macros/*" \
            --exclude-files "src/grpc/pb/*"

      - name: Upload coverage to Codecov
        if: always()
        uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f  # v7.0.0
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          files: coverage-report/cobertura.xml
          fail_ci_if_error: true
          flags: unittests

      - name: Upload coverage report
        if: always()
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a  # v7.0.1
        with:
          name: coverage-report
          path: coverage-report/
          retention-days: 7

  # 文档检查
  docs:
    name: Documentation Check
    runs-on: ubuntu-latest
    timeout-minutes: 30
    steps:
      - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5  # v4

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

      - name: Cache cargo registry
        uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9  # v6.1.0
        with:
          path: ~/.cargo/registry
          key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: ${{ runner.os }}-cargo-registry-

      - name: Build documentation
        run: cargo doc --no-deps --all-features --workspace
        continue-on-error: true

      - name: Check doc links
        run: |
          cargo doc --no-deps --all-features --workspace --document-private-items