codenexus 0.4.0-rc.1

A queryable code knowledge graph tool built on LadybugDB and tree-sitter
name: CI

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

env:
  CARGO_TERM_COLOR: always

jobs:
  lint:
    name: fmt + clippy
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      # Toolchain version must match the declared MSRV (Cargo.toml
      # `rust-version` / clippy.toml `msrv`) — this job is what verifies it.
      - name: Install Rust 1.97.1 with clippy
        uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 # stable
        with:
          toolchain: "1.97.1"
          components: clippy
      - name: Install nightly toolchain with rustfmt
        uses: dtolnay/rust-toolchain@efcb852328a9f50117170cc43094fb6f09eaf1ae # nightly
        with:
          components: rustfmt
      - name: Install dependencies
        run: |
          sudo apt-get update
          sudo apt-get install -y libssl-dev pkg-config protobuf-compiler
      - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            target
          key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
      - name: Check formatting
        run: cargo +nightly fmt --all -- --check
      - name: Clippy (full)
        run: cargo +1.97.1 clippy -- -D warnings
      - name: Clippy (minimal)
        run: cargo +1.97.1 clippy --lib --no-default-features --features minimal -- -D warnings
      # 2026-09-19 审计补腿:examples 是 workspace 成员但此前从未进 CI
      - name: Check examples crate
        run: cargo +1.97.1 check -p codenexus-examples --all-targets

  test:
    name: test (${{ matrix.features }})
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        include:
          - features: "minimal"
            cache-key: "minimal"
          - features: "core"
            cache-key: "core"
          - features: "full"
            cache-key: "full"
          - features: "core,daemon,analysis,complexity"
            cache-key: "core-daemon-analysis-complexity"
          - features: "core,lsp,cache"
            cache-key: "core-lsp-cache"
          - features: "full,embeddings"
            cache-key: "full-embeddings"
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      - name: Install Rust 1.97.1 (declared MSRV)
        uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 # stable
        with:
          toolchain: "1.97.1"
      - name: Install dependencies
        run: |
          sudo apt-get update
          sudo apt-get install -y libssl-dev pkg-config protobuf-compiler
      - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            target
          key: ${{ runner.os }}-cargo-${{ matrix.cache-key }}-${{ hashFiles('**/Cargo.lock') }}
      - name: Tests
        run: |
          if [ "${{ matrix.features }}" = "full" ]; then
            cargo test --lib --verbose
            # Integration tests (tests/*.rs: CLI, daemon lifecycle, MCP
            # protocol, non-ASCII paths) must gate merges too — they cover
            # paths the lib-only suite never touches.
            cargo test --tests --features full --verbose
          else
            cargo test --lib --no-default-features --features "${{ matrix.features }}" --verbose
          fi

  coverage:
    name: coverage (≥95%)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      - name: Install Rust 1.97.1 (declared MSRV)
        uses: dtolnay/rust-toolchain@4be7066ada62dd38de10e7b70166bc74ed198c30 # stable
        with:
          toolchain: "1.97.1"
      - name: Install dependencies
        run: |
          sudo apt-get update
          sudo apt-get install -y libssl-dev pkg-config protobuf-compiler
      - uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            target
          key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
      - uses: taiki-e/install-action@9458fed45835344deb5e69e06e831be047c96abf # cargo-llvm-cov
      - name: Coverage (fail under 95% lines, default features)
        run: cargo llvm-cov --lib --fail-under-lines 95 --lcov --output-path lcov.info
      - name: Upload coverage
        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
        with:
          name: coverage
          path: lcov.info

  fuzz-smoke:
    name: fuzz smoke (build + short run)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      # cargo-fuzz requires nightly (sanitizer) + rust-src (build-std).
      - name: Install nightly with rust-src
        uses: dtolnay/rust-toolchain@efcb852328a9f50117170cc43094fb6f09eaf1ae # nightly
        with:
          toolchain: nightly
          components: rust-src
      - name: Install dependencies
        run: |
          sudo apt-get update
          sudo apt-get install -y libssl-dev pkg-config protobuf-compiler
      - uses: taiki-e/install-action@9458fed45835344deb5e69e06e831be047c96abf # cargo-llvm-cov
        with:
          tool: cargo-fuzz
      - name: Build all fuzz targets
        working-directory: fuzz
        run: cargo fuzz build
      - name: Short run per target (smoke, not a corpus campaign)
        working-directory: fuzz
        run: |
          for t in escape_cypher_string escape_identifier cypher_subset_parse cnxp_header; do
            cargo fuzz run "$t" -- -runs=2000 -max_total_time=60
          done

  security:
    name: cargo-audit + cargo-deny
    runs-on: ubuntu-latest
    permissions:
      contents: read
      security-events: write
      checks: write
    steps:
      - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
      # deny.toml is the single source of truth for advisory ignores
      # (audit.toml is a synced mirror; audit-check cannot read config files,
      # so the ignore list is derived here and can never drift again).
      # Constraint: deny.toml comments must only mention RUSTSEC IDs that are
      # actually ignored.
      - name: Derive advisory ignores from deny.toml
        id: audit_ignore
        run: echo "ids=$(grep -oE 'RUSTSEC-[0-9]{4}-[0-9]+' deny.toml | sort -u | paste -sd, -)" >> "$GITHUB_OUTPUT"
      - name: cargo-audit (RustSec 漏洞库)
        uses: rustsec/audit-check@69366f33c96575abad1ee0dba8212993eecbe998 # v2.0.0
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          ignore: ${{ steps.audit_ignore.outputs.ids }}
      - name: cargo-deny (license / ban / advisory)
        uses: EmbarkStudios/cargo-deny-action@b66acf5e9fe20f8aba065be86778a8a4c846f902 # v2