dbnexus 0.3.4

An enterprise-grade database abstraction layer for Rust with built-in permission control and connection pooling
Documentation
name: CI

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

env:
  CARGO_TERM_COLOR: always
  RUST_BACKTRACE: 1
  CARGO_INCREMENTAL: 0
  CARGO_NET_GIT_FETCH_WITH_CLI: false

permissions:
  contents: read
  checks: write
  pull-requests: write

jobs:
  # 基础检查 - 快速反馈
  lint:
    name: Lint
    runs-on: ubuntu-latest
    timeout-minutes: 30
    steps:
      - uses: actions/checkout@v7

      - name: Install Rust
        uses: dtolnay/rust-toolchain@stable
        with:
          components: rustfmt, clippy

      - name: Cache dependencies
        uses: Swatinem/rust-cache@v2
        with:
          shared-key: lint

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

      - name: Install protoc
        run: sudo apt-get update && sudo apt-get install -y protobuf-compiler

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

      - name: Check docs
        run: cargo doc --all-features --no-deps --workspace

  # OpenSpec 规范检查 — 已移除(anomalyco/openspec 仓库已不存在)
  # 如需恢复,需找到新的 openspec 工具源

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

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

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

      - name: Install cargo-deny
        run: |
          if [ ! -f ~/.cargo/bin/cargo-deny ]; then
            cargo install --locked cargo-deny
          fi

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

      - name: Check for vulnerable dependencies
        run: cargo deny check bans --show-stats

  # 数据库矩阵测试
  test:
    name: Test (${{ matrix.db }})
    needs: [lint, security]
    runs-on: ubuntu-latest
    timeout-minutes: 25
    strategy:
      fail-fast: false
      matrix:
        db: [sqlite, postgres, mysql]
        include:
          - db: sqlite
            db-image: sqlite
          - db: postgres
            db-image: postgres:15-alpine
          - db: mysql
            db-image: mysql:8.0
    services:
      postgres:
        image: postgres:15-alpine
        ports:
          - 5432:5432
        env:
          POSTGRES_USER: dbnexus
          POSTGRES_PASSWORD: dbnexus_password
          POSTGRES_DB: dbnexus_test
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
      mysql:
        image: mysql:8.0
        ports:
          - 3306:3306
        env:
          MYSQL_ROOT_PASSWORD: dbnexus_password
          MYSQL_DATABASE: dbnexus_test
          MYSQL_USER: dbnexus
          MYSQL_PASSWORD: dbnexus_password
        options: >-
          --health-cmd "mysqladmin ping -h localhost"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
    steps:
      - uses: actions/checkout@v7

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

      - name: Cache dependencies
        uses: Swatinem/rust-cache@v2
        with:
          shared-key: ${{ matrix.db }}-${{ runner.os }}

      - name: Install protoc
        run: sudo apt-get update && sudo apt-get install -y protobuf-compiler

      - name: Set up database environment
        run: |
          echo "TEST_DB_TYPE=${{ matrix.db }}" >> $GITHUB_ENV
          if [ "${{ matrix.db }}" == "sqlite" ]; then
            echo "DATABASE_URL=sqlite::memory:" >> $GITHUB_ENV
          elif [ "${{ matrix.db }}" == "postgres" ]; then
            echo "DATABASE_URL=postgres://dbnexus:dbnexus_password@localhost:5432/dbnexus_test" >> $GITHUB_ENV
          elif [ "${{ matrix.db }}" == "mysql" ]; then
            echo "DATABASE_URL=mysql://dbnexus:dbnexus_password@localhost:3306/dbnexus_test" >> $GITHUB_ENV
          fi

      - name: Wait for database
        run: |
          if [ "${{ matrix.db }}" == "postgres" ]; then
            for i in {1..30}; do
              if pg_isready -h localhost -p 5432 -q; then
                echo "PostgreSQL is ready"
                break
              fi
              echo "Waiting for PostgreSQL..."
              sleep 2
            done
          fi
          if [ "${{ matrix.db }}" == "mysql" ]; then
            for i in {1..30}; do
              if mysqladmin ping -h localhost -P 3306 -q; then
                echo "MySQL is ready"
                break
              fi
              echo "Waiting for MySQL..."
              sleep 2
            done
          fi

      - name: Run tests
        # Exclude dbnexus-examples (hardcodes sqlite+duckdb in [dependencies]) and
        # dbnexus-macros (hardcodes sqlite in [dev-dependencies]). Both conflict
        # with mysql/postgres matrix testing via feature unification.
        run: cargo test --no-default-features --features ${{ matrix.db }},default-no-db,all-optional --workspace --exclude dbnexus-examples --exclude dbnexus-macros

      - name: Run doc tests
        run: cargo test --no-default-features --features ${{ matrix.db }},default-no-db,all-optional --doc --workspace --exclude dbnexus-examples --exclude dbnexus-macros

  # 跨平台构建检查
  build:
    name: Build (${{ matrix.os }}-${{ matrix.db }})
    needs: [lint, security]
    runs-on: ${{ matrix.os }}
    timeout-minutes: 20
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
        db: [sqlite]
        include:
          - os: ubuntu-latest
            target: x86_64-unknown-linux-gnu
          - os: macos-latest
            target: x86_64-apple-darwin
          - os: windows-latest
            target: x86_64-pc-windows-msvc
    steps:
      - uses: actions/checkout@v7

      - name: Install Rust
        uses: dtolnay/rust-toolchain@stable
        with:
          targets: ${{ matrix.target }}

      - name: Cache dependencies
        uses: Swatinem/rust-cache@v2
        with:
          shared-key: ${{ matrix.db }}-${{ runner.os }}

      - name: Install protoc (Linux)
        if: runner.os == 'Linux'
        run: sudo apt-get update && sudo apt-get install -y protobuf-compiler

      - name: Install protoc (macOS)
        if: runner.os == 'macOS'
        run: brew install protobuf

      - name: Install protoc (Windows)
        if: runner.os == 'Windows'
        run: choco install protoc -y

      - name: Build
        run: cargo build --no-default-features --features ${{ matrix.db }},default-no-db,all-optional --workspace --exclude dbnexus-examples --target ${{ matrix.target }}

      - name: Build release
        run: cargo build --no-default-features --features ${{ matrix.db }},default-no-db,all-optional --workspace --exclude dbnexus-examples --target ${{ matrix.target }} --release

  # 代码覆盖率
  coverage:
    name: Code Coverage
    needs: test
    runs-on: ubuntu-latest
    timeout-minutes: 30
    steps:
      - uses: actions/checkout@v7

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

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

      - name: Install protoc
        run: sudo apt-get update && sudo apt-get install -y protobuf-compiler

      - name: Install tarpaulin
        run: |
          if [ ! -f ~/.cargo/bin/cargo-tarpaulin ]; then
            cargo install cargo-tarpaulin
          fi

      - name: Generate coverage
        run: |
          cargo tarpaulin --verbose --no-default-features --features sqlite,default-no-db,all-optional --workspace --exclude dbnexus-cli --exclude dbnexus-macros --exclude dbnexus-examples --timeout 300 --out xml --exclude-files "**/tests/**"

      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v7
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          fail_ci_if_error: false
          files: ./cobertura.xml
          directory: coverage-report
          flags: unittests
          name: codecov-umbrella

      - name: Upload coverage to Coveralls
        uses: coverallsapp/github-action@v2
        with:
          parallel: true
          flag-name: Unit Tests

  # 文档检查
  docs:
    name: Documentation
    needs: [lint]
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - uses: actions/checkout@v7

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

      - name: Cache dependencies
        uses: Swatinem/rust-cache@v2
        with:
          shared-key: docs

      - name: Install protoc
        run: sudo apt-get update && sudo apt-get install -y protobuf-compiler

      - name: Build documentation
        run: cargo doc --no-deps --no-default-features --features sqlite,default-no-db,all-optional --workspace

      - name: Check documentation links
        run: |
          # 验证文档构建成功
          ls -la target/doc/dbnexus/

      - name: Verify docs.rs configuration
        run: |
          # 检查 docs.rs 配置是否正确
          cargo metadata --format-version 1 | jq -r '.packages[0].metadata["docs.rs"]'

  # 依赖更新检查
  dependabot:
    name: Dependency Updates
    runs-on: ubuntu-latest
    timeout-minutes: 10
    if: github.event_name == 'pull_request'
    steps:
      - uses: actions/checkout@v7

      - name: Check for outdated dependencies
        run: |
          cargo outdated --root-deps-only --exit-code 1 || echo "OUTDATED=true" >> $GITHUB_ENV

      - name: Report outdated dependencies
        if: env.OUTDATED == 'true'
        run: |
          echo "⚠️  Some dependencies are outdated"
          cargo outdated --root-deps-only

  # 提交状态汇总
  status:
    name: Status Check
    needs: [test, build, coverage, docs]
    runs-on: ubuntu-latest
    timeout-minutes: 5
    if: always()
    steps:
      - name: Check status
        run: |
          echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
          echo "📊 CI Status Summary"
          echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
          echo ""
          echo "✅ Lint: ${{ needs.lint.result }}"
          echo "✅ Security: ${{ needs.security.result }}"
          echo "✅ Test: ${{ needs.test.result }}"
          echo "✅ Build: ${{ needs.build.result }}"
          echo "✅ Coverage: ${{ needs.coverage.result }}"
          echo "✅ Documentation: ${{ needs.docs.result }}"
          echo ""
          if [[ "${{ needs.test.result }}" == "success" ]] && \
             [[ "${{ needs.build.result }}" == "success" ]]; then
            echo "🎉 All critical checks passed!"
            exit 0
          else
            echo "❌ Some checks failed"
            exit 1
          fi