# .github/workflows/ci.yml
#
# GitHub CI for the mentat mirror. Codeberg's .forgejo/workflows/ci.yml is the
# canonical gate; this mirrors it (Codeberg push-mirrors to GitHub, so both run
# on every push) and adds the DuckDB backend. It exercises all THREE ways to
# deploy mentat:
#   a) SQLite   — test-sqlite, test-mino, test-ffi, test-sqlite-ext
#                 (the embedded library + CLI + C ABI + the loadable extension)
#   b) Postgres — test-pg (the pg_mentat pgrx extension, pg13..pg18)
#   c) DuckDB   — test-duckdb (the mentat_duckdb loadable extension)
# plus fmt / clippy / cargo-deny / nix-build.
#
# The other .github workflows only publish (release.yml, docs.yml, …); this one
# is the test matrix.

name: ci

on:
  push:
  pull_request:

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

env:
  CARGO_TERM_COLOR: always
  CARGO_INCREMENTAL: "0"
  PGRX_VERSION: "0.17.0"
  RUST_TOOLCHAIN: "1.90.0"
  DUCKDB_VERSION: "v1.5.5"

jobs:
  # --- lint / hygiene ----------------------------------------------------
  fmt:
    name: fmt
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@v4
      - name: Install Rust
        run: |
          curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
            | sh -s -- -y --default-toolchain ${{ env.RUST_TOOLCHAIN }} --component rustfmt
          echo "$HOME/.cargo/bin" >> $GITHUB_PATH
      - name: cargo fmt --check
        run: cargo fmt --all -- --check

  clippy:
    name: clippy (-D warnings)
    runs-on: ubuntu-latest
    timeout-minutes: 20
    steps:
      - uses: actions/checkout@v4
      - name: Install build deps
        run: |
          sudo apt-get update
          sudo apt-get install -y --no-install-recommends \
              build-essential pkg-config libsqlite3-dev libssl-dev
      - name: Install Rust
        run: |
          curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
            | sh -s -- -y --default-toolchain ${{ env.RUST_TOOLCHAIN }} --component clippy
          echo "$HOME/.cargo/bin" >> $GITHUB_PATH
      - name: Cache cargo
        uses: actions/cache@v4
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            target
          key: ci-clippy-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: ci-clippy-${{ runner.os }}-
      # SQLite side + shared front-end + mino. pg_mentat's clippy runs in
      # test-pg (needs pg_config); mentat_duckdb's in test-duckdb (needs the
      # DuckDB extension cfg).
      - name: cargo clippy
        run: cargo clippy --workspace --exclude pg_mentat --exclude mentat_duckdb --exclude mentat_sqlite_ext -- -D warnings

  deny:
    name: cargo deny
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@v4
      - name: Install Rust
        run: |
          curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
            | sh -s -- -y --default-toolchain ${{ env.RUST_TOOLCHAIN }}
          echo "$HOME/.cargo/bin" >> $GITHUB_PATH
      - name: Install cargo-deny
        run: cargo install --locked cargo-deny
      - name: cargo deny check
        run: cargo deny check

  # --- a) SQLite: the embedded library + CLI + C ABI + scripting --------
  test-sqlite:
    name: test-sqlite (+ mino feature)
    runs-on: ubuntu-latest
    timeout-minutes: 30
    steps:
      - uses: actions/checkout@v4
      - name: Install build deps
        run: |
          sudo apt-get update
          sudo apt-get install -y --no-install-recommends \
              build-essential pkg-config libsqlite3-dev libssl-dev
      - name: Install Rust
        run: |
          curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
            | sh -s -- -y --default-toolchain ${{ env.RUST_TOOLCHAIN }}
          echo "$HOME/.cargo/bin" >> $GITHUB_PATH
      - name: Cache cargo
        uses: actions/cache@v4
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            target
          key: ci-sqlite-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: ci-sqlite-${{ runner.os }}-
      - name: cargo test (default members)
        run: cargo test
      - name: cargo test -p mentat --features mino
        run: cargo test -p mentat --features mino

  test-mino:
    name: test-mino
    runs-on: ubuntu-latest
    timeout-minutes: 20
    steps:
      - uses: actions/checkout@v4
      - name: Install Rust
        run: |
          curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
            | sh -s -- -y --default-toolchain ${{ env.RUST_TOOLCHAIN }}
          echo "$HOME/.cargo/bin" >> $GITHUB_PATH
      - name: Cache cargo
        uses: actions/cache@v4
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            target
          key: ci-mino-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: ci-mino-${{ runner.os }}-
      - name: cargo test -p mino-rs
        run: cargo test -p mino-rs
      - name: cargo test -p mentat_script
        run: cargo test -p mentat_script

  test-ffi:
    name: test-ffi (C ABI)
    runs-on: ubuntu-latest
    timeout-minutes: 20
    steps:
      - uses: actions/checkout@v4
      - name: Install build deps
        run: |
          sudo apt-get update
          sudo apt-get install -y --no-install-recommends \
              build-essential pkg-config libsqlite3-dev libssl-dev
      - name: Install Rust
        run: |
          curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
            | sh -s -- -y --default-toolchain ${{ env.RUST_TOOLCHAIN }}
          echo "$HOME/.cargo/bin" >> $GITHUB_PATH
      - name: Cache cargo
        uses: actions/cache@v4
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            target
          key: ci-ffi-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: ci-ffi-${{ runner.os }}-
      - name: build + test crates/sqlite/ffi
        run: |
          cargo build -p mentat_ffi
          cargo test -p mentat_ffi

  test-sqlite-ext:
    name: test-sqlite-ext (loadable extension)
    runs-on: ubuntu-latest
    timeout-minutes: 30
    steps:
      - uses: actions/checkout@v4
      - name: Install build deps + sqlite3 CLI
        run: |
          sudo apt-get update
          sudo apt-get install -y --no-install-recommends \
              build-essential pkg-config libsqlite3-dev libssl-dev sqlite3
          sqlite3 --version
      - name: Install Rust
        run: |
          curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
            | sh -s -- -y --default-toolchain ${{ env.RUST_TOOLCHAIN }} --component clippy
          echo "$HOME/.cargo/bin" >> $GITHUB_PATH
      - name: Cache cargo
        uses: actions/cache@v4
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            target
          key: ci-sqlite-ext-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: ci-sqlite-ext-${{ runner.os }}-
      - name: clippy + unit tests
        run: |
          cargo clippy -p mentat_sqlite_ext -- -D warnings
          cargo test -p mentat_sqlite_ext
      - name: Build the loadable extension
        run: cargo build -p mentat_sqlite_ext
      - name: Only the init symbol is exported (no leaked sqlite3_* API)
        run: |
          nm -D --defined-only target/debug/libmentat_sqlite.so | awk '{print $3}' \
            | grep -E '^sqlite3_' | tee /tmp/syms
          test "$(cat /tmp/syms)" = "sqlite3_mentatsqlite_init"
      - name: Load + query smoke test (edn_t/edn_q/edn_pull/edn_eval + json_each JOIN)
        working-directory: crates/sqlite/ext
        run: bash test/smoke.sh

  # --- b) Postgres: the pg_mentat pgrx extension, pg13..pg18 ------------
  test-pg:
    name: test-pg (pg${{ matrix.pg }})
    runs-on: ubuntu-latest
    timeout-minutes: 45
    strategy:
      fail-fast: false
      matrix:
        pg: [13, 14, 15, 16, 17, 18]
    steps:
      - uses: actions/checkout@v4
      - name: Install build deps
        run: |
          sudo apt-get update
          sudo apt-get install -y --no-install-recommends \
              build-essential pkg-config libpq-dev libclang-dev clang \
              libreadline-dev zlib1g-dev flex bison libicu-dev
      - name: Install Rust
        run: |
          curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
            | sh -s -- -y --default-toolchain ${{ env.RUST_TOOLCHAIN }}
          echo "$HOME/.cargo/bin" >> $GITHUB_PATH
      - name: Cache cargo
        uses: actions/cache@v4
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            target
          key: ci-pg${{ matrix.pg }}-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: ci-pg${{ matrix.pg }}-${{ runner.os }}-
      - name: Install cargo-pgrx
        run: cargo install --locked cargo-pgrx --version "${PGRX_VERSION}"
      - name: pgrx init (download PG${{ matrix.pg }})
        run: cargo pgrx init --pg${{ matrix.pg }} download
      - name: cargo pgrx test (+ script feature)
        working-directory: crates/pg/pg_mentat
        run: cargo pgrx test --no-default-features --features "pg${{ matrix.pg }} script" pg${{ matrix.pg }}

  # --- c) DuckDB: the mentat_duckdb loadable extension ------------------
  test-duckdb:
    name: test-duckdb
    runs-on: ubuntu-latest
    timeout-minutes: 40
    steps:
      - uses: actions/checkout@v4
        with:
          submodules: recursive        # extension-ci-tools (the DuckDB build harness)
      - name: Install build deps
        run: |
          sudo apt-get update
          sudo apt-get install -y --no-install-recommends \
              build-essential pkg-config libsqlite3-dev libssl-dev unzip python3
      - name: Install Rust
        run: |
          curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
            | sh -s -- -y --default-toolchain ${{ env.RUST_TOOLCHAIN }}
          echo "$HOME/.cargo/bin" >> $GITHUB_PATH
      - name: Cache cargo
        uses: actions/cache@v4
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            target
          key: ci-duckdb-${{ runner.os }}-${{ hashFiles('**/Cargo.lock') }}
          restore-keys: ci-duckdb-${{ runner.os }}-
      - name: Build the loadable extension
        working-directory: crates/duckdb
        run: |
          make configure
          make debug
      - name: Fetch a matching DuckDB CLI
        run: |
          set -euxo pipefail
          curl -fsSL \
            "https://github.com/duckdb/duckdb/releases/download/${DUCKDB_VERSION}/duckdb_cli-linux-amd64.zip" \
            -o /tmp/duckdb.zip
          mkdir -p /tmp/duckdb-cli
          unzip -o /tmp/duckdb.zip -d /tmp/duckdb-cli
          /tmp/duckdb-cli/duckdb --version
      - name: Load + query smoke test (edn_t/edn_q/edn_pull/edn_eval + a native JOIN)
        working-directory: crates/duckdb
        run: DUCKDB=/tmp/duckdb-cli/duckdb bash test/smoke.sh

  # --- Nix flake: builds the SQLite CLI + one extension major -----------
  nix-build:
    name: nix-build
    runs-on: ubuntu-latest
    timeout-minutes: 60
    steps:
      - uses: actions/checkout@v4
      - uses: cachix/install-nix-action@v27
        with:
          extra_nix_config: |
            experimental-features = nix-command flakes
      - name: Build mentat-cli + mentatd + one extension
        run: |
          nix build --option sandbox relaxed .#mentat-cli .#mentatd .#pg_mentat-pg16
