name: CI

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

permissions:
  contents: read

jobs:
  lint:
    name: Lint & Format
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - name: Set up uv
        uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b
        with:
          enable-cache: true
      - name: Install dependencies
        run: uv sync --extra dev
      - name: Run Ruff Linter
        run: uv run ruff check .
      - name: Run Ruff Formatter
        run: uv run ruff format --check .

  build-check:
    name: Build Verification
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - name: Set up uv
        uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b
        with:
          enable-cache: true
      - name: Build package
        run: uv build
      - name: Verify package metadata
        run: uvx twine check dist/*

  test:
    name: Unit Tests (Python ${{ matrix.python-version }})
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        python-version: ["3.10", "3.14"]
    steps:
      - uses: actions/checkout@v7
      - name: Set up uv
        uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b
        with:
          python-version: ${{ matrix.python-version }}
          enable-cache: true
      - name: Install dependencies
        run: uv sync --extra dev
      - name: Run tests with pytest
        run: |
          uv run pytest tests/ -n auto

  root-check:
    name: Repo Root Hygiene
    runs-on: ubuntu-latest
    if: github.event_name == 'pull_request'
    steps:
      - name: Checkout code
        uses: actions/checkout@v7
      - name: Check for unapproved root-level entries
        run: |
          allowlist=$(grep -vE '^[[:space:]]*(#|$)' .github/root-allowlist.txt | sort -u)
          actual=$(git ls-tree --name-only HEAD | sort -u)
          new_entries=$(comm -13 <(echo "$allowlist") <(echo "$actual"))
          if [[ -n "$new_entries" ]]; then
            echo "::error::New root-level files or directories are not allowed. Move these into the existing directory structure (src/, tests/, docs/, scripts/, ...):"
            echo "$new_entries" | sed 's/^/  - /'
            echo ""
            echo "If a new root entry is genuinely required, add it to .github/root-allowlist.txt in this PR (that file requires code-owner approval)."
            exit 1
          fi
          echo "Repository root matches the allowlist."

  changelog-check:
    name: Prevent Manual CHANGELOG Edits
    runs-on: ubuntu-latest
    if: github.event_name == 'pull_request'
    steps:
      - name: Checkout code
        uses: actions/checkout@v7
        with:
          fetch-depth: 0
      - name: Check for CHANGELOG.md and manifest changes
        run: |
          if [[ "${{ github.head_ref }}" =~ ^release-please- ]]; then
            echo "Skipping changelog check for release-please branch: ${{ github.head_ref }}"
            exit 0
          fi
          if git diff --name-only origin/main...HEAD | grep -qE '^(CHANGELOG\.md|\.release-please-manifest\.json)$'; then
            echo "::error::CHANGELOG.md and .release-please-manifest.json should not be edited manually. Changes to these files are managed automatically by release-please."
            exit 1
          fi

