name: CI

on:
  push:
    tags-ignore:
      - "v*"
  pull_request:
  workflow_dispatch:
  schedule:
    - cron: "17 4 * * 1"

env:
  FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true

jobs:
  conventional-commits:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
        with:
          fetch-depth: 0

      - uses: actions/setup-python@v6
        with:
          python-version: '3.12'

      - name: Validate pull request title
        if: github.event_name == 'pull_request'
        run: python execution/conventional_commits.py check-subject "${{ github.event.pull_request.title }}"

      - name: Validate pushed commit subjects
        if: github.event_name == 'push'
        run: python execution/conventional_commits.py check-push-range "${{ github.event.before }}" "$GITHUB_SHA"

      - name: Validate manual run HEAD commit subject
        if: github.event_name == 'workflow_dispatch'
        run: python execution/conventional_commits.py check-range "HEAD^!"

  release-version:
    needs: conventional-commits
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      - uses: actions/setup-python@v6
        with:
          python-version: '3.12'

      - name: Run version sync tests
        run: python -m unittest discover -s execution -p 'test_*.py'

      - name: Verify release metadata stays aligned
        run: python execution/version_sync.py check

  rust:
    needs: release-version
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      # rust-toolchain.toml is the single source of truth for the toolchain
      # version; rustup resolves it natively (channel + components).
      - name: Install Rust toolchain (rust-toolchain.toml)
        run: rustup toolchain install

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

      - name: Run cargo check
        run: cargo check

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

      - name: Run Rust tests
        run: cargo test --all-targets -- --test-threads=1

      - name: Build release binary
        run: cargo build --release

  embed-build:
    needs: release-version
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      # rust-toolchain.toml is the single source of truth for the toolchain
      # version; rustup resolves it natively (channel + components).
      - name: Install Rust toolchain (rust-toolchain.toml)
        run: rustup toolchain install

      # Engine-only build for library embedders (e.g. AAP): no daemon / sidecar /
      # protocol-server / CLI surface. Makes "an embedder can build symforge" a
      # green gate, so a server-only break (e.g. a `#[cfg(unix)]` unsafe block that
      # a Windows-only check cfg's out) can never silently reach embedders.
      - name: Build engine-only embed feature
        run: cargo build --no-default-features --features embed

      - name: Clippy engine-only embed feature
        run: cargo clippy --no-default-features --features embed --lib -- -D warnings

      - name: Run embed-feature lib tests
        run: cargo test --no-default-features --features embed --lib -- --test-threads=1

  embed-musl:
    needs: release-version
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      # rust-toolchain.toml is the single source of truth for the toolchain
      # version; rustup resolves it natively. A hardcoded `toolchain:` input
      # here silently loses when rust-toolchain.toml is bumped: cargo follows
      # the toml and auto-installs that toolchain WITHOUT the musl target,
      # which broke this job with E0463 "can't find crate for core" after the
      # 1.95.0 -> 1.96.0 bump.
      - name: Install Rust toolchain (rust-toolchain.toml)
        run: rustup toolchain install && rustup target add x86_64-unknown-linux-musl

      # Cross-compile the engine-only embed build to musl. AAP compiles the
      # SymForge engine inside musl guest-agent VMs, so a musl regression must
      # fail SymForge's own CI here, not downstream in AAP.
      #
      # musl-tools provides musl-gcc; the embed build statically links C deps
      # built by the `cc` crate (19 tree-sitter grammars, rusqlite `bundled`
      # sqlite) and by libgit2-sys (git2 `vendored-libgit2`, built via cmake,
      # which is preinstalled on ubuntu-latest). Both `cc` and cmake-rs honor
      # the target-scoped CC env var, so pointing it at musl-gcc makes every
      # C-compiled crate link cleanly against musl. All currently pinned
      # tree-sitter grammars ship pure-C scanners; if a future grammar bump
      # reintroduces a C++ (.cc) scanner, also set
      # CXX_x86_64_unknown_linux_musl (e.g. to a musl g++), since CC_<target>
      # does not redirect the C++ compiler.
      - name: Install musl C toolchain
        run: sudo apt-get update && sudo apt-get install -y musl-tools

      - name: Build engine-only embed feature for musl
        env:
          CC_x86_64_unknown_linux_musl: musl-gcc
        run: cargo build --no-default-features --features embed --target x86_64-unknown-linux-musl

      - name: Clippy engine-only embed feature for musl
        env:
          CC_x86_64_unknown_linux_musl: musl-gcc
        run: cargo clippy --no-default-features --features embed --target x86_64-unknown-linux-musl --lib -- -D warnings

  performance-smoke:
    needs: release-version
    if: github.event_name == 'workflow_dispatch' || github.event_name == 'schedule'
    runs-on: ubuntu-latest
    timeout-minutes: 30
    steps:
      - uses: actions/checkout@v6

      # rust-toolchain.toml is the single source of truth for the toolchain
      # version; rustup resolves it natively (channel + components).
      - name: Install Rust toolchain (rust-toolchain.toml)
        run: rustup toolchain install

      - name: Run ignored 1000-file load performance smoke
        run: cargo test --release --test live_index_integration test_load_perf_1000_files -- --ignored --test-threads=1

      - name: Run portable coupling calibration smoke
        run: cargo test --release --test coupling_calibration calibrate_current_repo_smoke -- --ignored --test-threads=1 --nocapture

  npm:
    needs: release-version
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6

      - uses: actions/setup-node@v6
        with:
          node-version: 20

      - name: Test npm package
        working-directory: npm
        run: npm test
