knot 1.3.3

Codebase Graph + Vector RAG Indexer for Java, TypeScript, JavaScript, Kotlin, Rust, Python, Groovy, C/C++, Build Systems, and HTML/CSS codebases
Documentation
name: CI

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

env:
  CARGO_TERM_COLOR: always
  RUST_BACKTRACE: 1

jobs:
  test-unit:
    name: Unit Tests
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Install Rust
        uses: dtolnay/rust-toolchain@stable
        with:
          components: rustfmt, clippy
      
      - name: Cache cargo registry
        uses: actions/cache@v4
        with:
          path: ~/.cargo/registry
          key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
      
      - name: Cache cargo index
        uses: actions/cache@v4
        with:
          path: ~/.cargo/git
          key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}
      
      - name: Cache cargo build
        uses: actions/cache@v4
        with:
          path: target
          key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }}
      
      - name: Run rustfmt
        run: cargo fmt --all -- --check
      
      - name: Run clippy
        run: cargo clippy --all-targets --all-features -- -D warnings
      
      - name: Run unit tests
        run: cargo test --lib --all-features
      
      - name: Build release
        run: cargo build --release --all-features

  test-e2e:
    name: E2E Integration Tests
    runs-on: ubuntu-latest
    needs: test-unit
    steps:
      - uses: actions/checkout@v4
      
      - name: Install Rust
        uses: dtolnay/rust-toolchain@stable
      
      - name: Cache cargo registry
        uses: actions/cache@v4
        with:
          path: ~/.cargo/registry
          key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
      
      - name: Cache cargo index
        uses: actions/cache@v4
        with:
          path: ~/.cargo/git
          key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}
      
      - name: Cache cargo build
        uses: actions/cache@v4
        with:
          path: target
          key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }}
      
      - name: Install netcat (for port checking)
        run: sudo apt-get update && sudo apt-get install -y netcat-openbsd
      
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      
      - name: Run JS/TS/Java E2E tests
        run: ./tests/run_e2e.sh
        timeout-minutes: 15

      - name: Cleanup Docker between E2E test suites
        if: always()
        run: |
          cd tests && docker compose -f docker-compose.e2e.yml down -v 2>/dev/null || true
          rm -rf .e2e_data .e2e_kotlin_data .e2e_rust_data .e2e_python_data .e2e_build_systems_data 2>/dev/null || true
          sleep 5

      - name: Run Kotlin E2E tests
        run: ./tests/run_kotlin_e2e.sh
        timeout-minutes: 10

      - name: Cleanup Docker between E2E test suites
        if: always()
        run: |
          cd tests && docker compose -f docker-compose.e2e.yml down -v 2>/dev/null || true
          rm -rf .e2e_kotlin_data .e2e_rust_data .e2e_python_data 2>/dev/null || true
          sleep 5

      - name: Run Rust E2E tests
        run: ./tests/run_rust_e2e.sh
        timeout-minutes: 10

      - name: Cleanup Docker between E2E test suites
        if: always()
        run: |
          cd tests && docker compose -f docker-compose.e2e.yml down -v 2>/dev/null || true
          rm -rf .e2e_rust_data .e2e_python_data 2>/dev/null || true
          sleep 5

      - name: Run Python E2E tests
        run: ./tests/run_python_e2e.sh
        timeout-minutes: 10

      - name: Cleanup Docker between E2E test suites
        if: always()
        run: |
          cd tests && docker compose -f docker-compose.e2e.yml down -v 2>/dev/null || true
          rm -rf .e2e_python_data .e2e_build_systems_data 2>/dev/null || true
          sleep 5

      - name: Run Build Systems E2E tests
        run: ./tests/run_build_systems_e2e.sh
        timeout-minutes: 10

      - name: Cleanup Docker between E2E test suites
        if: always()
        run: |
          cd tests && docker compose -f docker-compose.e2e.yml down -v 2>/dev/null || true
          rm -rf .e2e_build_systems_data .e2e_build_systems_repo .e2e_groovy_data .e2e_groovy_repo 2>/dev/null || true
          sleep 5

      - name: Run Config Files E2E tests
        run: ./tests/run_config_e2e.sh
        timeout-minutes: 10

      - name: Cleanup Docker between E2E test suites
        if: always()
        run: |
          cd tests && docker compose -f docker-compose.e2e.yml down -v 2>/dev/null || true
          rm -rf .e2e_config_data .e2e_config_repo .e2e_k8s_helm_data .e2e_k8s_helm_repo 2>/dev/null || true
          sleep 5

      - name: Run K8s/Helm E2E tests
        run: ./tests/run_k8s_helm_e2e.sh
        timeout-minutes: 10

      - name: Cleanup Docker between E2E test suites
        if: always()
        run: |
          cd tests && docker compose -f docker-compose.e2e.yml down -v 2>/dev/null || true
          rm -rf .e2e_k8s_helm_data .e2e_k8s_helm_repo .e2e_groovy_data .e2e_groovy_repo 2>/dev/null || true
          sleep 5

      - name: Run Groovy E2E tests
        run: ./tests/run_groovy_e2e.sh
        timeout-minutes: 15

      - name: Cleanup Docker between E2E test suites
        if: always()
        run: |
          cd tests && docker compose -f docker-compose.e2e.yml down -v 2>/dev/null || true
          rm -rf .e2e_groovy_data .e2e_groovy_repo .e2e_crosslang_data .e2e_cpp_data 2>/dev/null || true
          sleep 5

      - name: Run Cross-Language Ref E2E tests
        run: ./tests/run_cross_lang_ref_e2e.sh
        timeout-minutes: 10

      - name: Cleanup Docker between E2E test suites
        if: always()
        run: |
          cd tests && docker compose -f docker-compose.e2e.yml down -v 2>/dev/null || true
          rm -rf .e2e_crosslang_data .e2e_cpp_data 2>/dev/null || true
          sleep 5

      - name: Run C/C++ E2E tests
        run: ./tests/run_cpp_e2e.sh
        timeout-minutes: 10

      - name: Cleanup Docker and data dirs after all E2E tests
        if: always()
        run: |
          cd tests && docker compose -f docker-compose.e2e.yml down -v 2>/dev/null || true
          rm -rf .e2e_cpp_data .e2e_cpp_repo 2>/dev/null || true
          sleep 5

      - name: Upload E2E logs on failure
        if: failure()
        uses: actions/upload-artifact@v4
        with:
          name: e2e-logs
          path: |
            tests/.e2e_data/
            tests/.e2e_kotlin_data/
            tests/.e2e_rust_data/
            tests/.e2e_python_data/
            tests/.e2e_build_systems_data/
            tests/.e2e_groovy_data/
            tests/.e2e_crosslang_data/
            tests/.e2e_cpp_data/
          retention-days: 7

  test-performance:
    name: Performance Benchmarks
    runs-on: ubuntu-latest
    needs: test-e2e
    if: success()
    steps:
      - uses: actions/checkout@v4

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

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

      - name: Cache cargo index
        uses: actions/cache@v4
        with:
          path: ~/.cargo/git
          key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}

      - name: Cache cargo build
        uses: actions/cache@v4
        with:
          path: target
          key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }}

      - name: Install netcat (for port checking)
        run: sudo apt-get update && sudo apt-get install -y netcat-openbsd

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

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

      - name: Run Performance Benchmarks
        run: |
          ./tests/benchmark_e2e.sh --focus java_e2e --output-dir /tmp/perf_results
          scripts/compare_perf_metrics.sh /tmp/perf_results .perf_metrics/baseline.json
        timeout-minutes: 10

      - name: Upload performance metrics
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: perf-metrics
          path: /tmp/perf_results/
          retention-days: 30

      - name: Update Baseline (on main/master merge)
        if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master'
        run: |
          LATEST_AGG=$(find /tmp/perf_results -name aggregated.json -type f | sort | tail -1)
          if [ -n "$LATEST_AGG" ]; then
            cp "$LATEST_AGG" .perf_metrics/baseline.json
            echo "Baseline updated for $(git rev-parse --short HEAD)"
          else
            echo "WARNING: No aggregated.json found in /tmp/perf_results"
          fi