name: CI

on:
  push:
    branches: [main, "stable/*"]
    tags: ["v*"]
  pull_request:
    branches: [main, "stable/*"]

permissions:
  contents: read

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
      - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
        with:
          python-version: "3.14"
      - run: pip install pre-commit
      # mypy runs separately in typecheck job with full project deps
      - run: SKIP=mypy pre-commit run --all-files

  typecheck:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
      - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
        with:
          python-version: "3.14"
      - run: pip install mypy
      - run: pip install -e ".[all]"
      - run: mypy turnstone/

  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["3.11", "3.12", "3.13"]
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
      - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
        with:
          python-version: ${{ matrix.python-version }}
      - run: pip install -e ".[test]"
      - run: pytest tests/ -m "not live" --cov=turnstone --cov-report=term-missing --cov-report=xml -q
      - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
        if: always()
        with:
          name: coverage-${{ matrix.python-version }}
          path: coverage.xml

  test-postgres:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:18
        env:
          POSTGRES_USER: postgres
          POSTGRES_PASSWORD: postgres
          POSTGRES_DB: turnstone_test
        ports:
          - 5432:5432
        options: >-
          --health-cmd="pg_isready -U postgres"
          --health-interval=10s
          --health-timeout=5s
          --health-retries=5
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
      - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
        with:
          python-version: "3.14"
      - run: pip install -e ".[test,postgres]"
      - run: pytest tests/ -m "not live" --storage-backend=postgresql -q
        env:
          TURNSTONE_TEST_PG_URL: postgresql+psycopg://postgres:postgres@localhost:5432/turnstone_test

  wheel-completeness:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
      - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
        with:
          python-version: "3.14"
      - run: pip install build
      - run: python -m build --wheel
      - name: Check all data files are in wheel
        run: |
          SOURCE=$(find turnstone -type f \
            ! -name '*.py' ! -name '*.pyc' ! -path '*__pycache__*' \
            | sort)
          WHEEL=$(python -m zipfile -l dist/*.whl \
            | awk '{print $1}' \
            | grep -v '\.py$' | grep -v '\.dist-info' | grep -v '\.pyc' | grep -v '^File$' \
            | sort)

          # Files intentionally excluded from the wheel (one per line)
          ALLOW="
          turnstone/core/storage/migrations/script.py.mako
          "

          MISSING=$(comm -23 <(echo "$SOURCE") <(echo "$WHEEL") \
            | grep -vFxf <(echo "$ALLOW" | sed '/^[[:space:]]*$/d; s/^[[:space:]]*//' ) || true)

          if [ -n "$MISSING" ]; then
            echo "::error::Data files in source tree but missing from wheel:"
            echo "$MISSING"
            echo ""
            echo "Add them to [tool.hatch.build.targets.wheel] in pyproject.toml"
            echo "or to the ALLOW list in this job if intentionally excluded."
            exit 1
          fi
          echo "All source data files present in wheel"
      - name: Smoke-test entry points from installed wheel
        run: |
          python -m venv /tmp/smoke
          /tmp/smoke/bin/pip install dist/*.whl
          /tmp/smoke/bin/turnstone --help
          /tmp/smoke/bin/turnstone-server --help
          /tmp/smoke/bin/turnstone-console --help
          /tmp/smoke/bin/turnstone-admin --help
          /tmp/smoke/bin/turnstone-channel --help
          /tmp/smoke/bin/turnstone-bootstrap --help

  lock-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
      - uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7
        with:
          uv-version: "0.9.18"
      - run: uv lock --check

  security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
      - uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7
        with:
          uv-version: "0.9.18"
      - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
        with:
          python-version: "3.14"
      - run: uv sync --frozen --all-extras
      - run: uv pip install pip-audit
      - name: Security audit (dependencies)
        run: uv export --no-emit-project --frozen | uv run pip-audit --strict --desc -r /dev/stdin

  security-ts:
    runs-on: ubuntu-latest
    defaults:
      run:
        working-directory: sdk/typescript
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
      - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6
        with:
          node-version: "24"
      - run: npm ci
      - run: npm audit --audit-level=moderate
