name: 'Changed Files Check'
description: 'Detect changed files using filters.yaml and check for uncovered files'

inputs:
  gh_token:
    description: 'GitHub token for PR API calls'
    required: true

outputs:
  core:
    description: 'Whether core files changed'
    value: ${{ steps.filter.outputs.core_any_modified }}
  operator:
    description: 'Whether operator files changed'
    value: ${{ steps.filter.outputs.operator_any_modified }}
  deploy:
    description: 'Whether deploy files changed'
    value: ${{ steps.filter.outputs.deploy_any_modified }}
  vllm:
    description: 'Whether vllm files changed'
    value: ${{ steps.filter.outputs.vllm_any_modified }}
  sglang:
    description: 'Whether sglang files changed'
    value: ${{ steps.filter.outputs.sglang_any_modified }}
  trtllm:
    description: 'Whether trtllm files changed'
    value: ${{ steps.filter.outputs.trtllm_any_modified }}
  planner:
    description: 'Whether planner files changed'
    value: ${{ steps.filter.outputs.planner_any_modified }}
  frontend:
    description: 'Whether frontend files changed'
    value: ${{ steps.filter.outputs.frontend_any_modified }}
  rust:
    description: 'Whether rust files changed'
    value: ${{ steps.filter.outputs.rust_any_modified }}
  docs:
    description: 'Whether docs files changed'
    value: ${{ steps.filter.outputs.docs_any_modified }}

runs:
  using: "composite"
  steps:
    # Skipped for pull_request events — tj-actions uses the REST API instead.
    - name: Get merge base for push events
      id: merge-base
      if: github.event_name != 'pull_request'
      shell: bash
      env:
        GH_TOKEN: ${{ inputs.gh_token }}
      run: |
        # How changed-file detection works (callers use fetch-depth: 1):
        #
        #   pull_request events:
        #     tj-actions/changed-files calls the GitHub REST API (GET /pulls/{n}/files)
        #     to list changed files. No git history needed — works with shallow clones.
        #
        #   push to pull-request/N branches:
        #     The GitHub Compare API returns the true merge-base SHA. We fetch that
        #     single commit so tj-actions can run `git diff merge-base HEAD` locally.
        #
        #   push to main / release branches:
        #     base_sha is empty, skip_initial_fetch is false — tj-actions falls back
        #     to its default behaviour (deepens history and diffs against the previous
        #     commit). Callers short-circuit outputs to true for main/release anyway.
        if [[ "${{ github.ref_name }}" =~ ^pull-request/([0-9]+)$ ]]; then
          BASE=$(gh pr view "${BASH_REMATCH[1]}" --json baseRefName -q '.baseRefName')
          # Compare API returns the true merge-base (fork point), not the branch tip.
          BASE_SHA=$(gh api "repos/${{ github.repository }}/compare/${BASE}...${{ github.sha }}" \
            --jq '.merge_base_commit.sha')
          echo "Merge base: $BASE_SHA"
          # Fetch the merge-base commit so tj-actions can `git diff` against it.
          git fetch --no-tags --depth=1 origin "$BASE_SHA"
          echo "base_sha=$BASE_SHA" >> "$GITHUB_OUTPUT"
        else
          echo "base_sha=" >> "$GITHUB_OUTPUT"
        fi

    - name: Check for changes
      uses: tj-actions/changed-files@aa08304bd477b800d468db44fe10f6c61f7f7b11  # v42.1.0
      id: filter
      with:
        files_yaml_from_source_file: .github/filters.yaml
        token: ${{ inputs.gh_token }}
        # pull_request: calls GET /pulls/{n}/files — accurate, no git history needed.
        use_rest_api: ${{ github.event_name == 'pull_request' }}
        # push to pull-request/N: merge-base SHA from Compare API (set above).
        # push to main/release: empty — tj-actions uses default (previous commit).
        base_sha: ${{ steps.merge-base.outputs.base_sha || '' }}
        # When we provide base_sha we've already fetched everything needed.
        # When base_sha is empty, let tj-actions fetch history itself.
        skip_initial_fetch: ${{ steps.merge-base.outputs.base_sha != '' }}

    - name: Debug changed files
      shell: bash
      run: |
        echo "=== Base SHA Used ==="
        echo "base_sha: ${{ steps.merge-base.outputs.base_sha || 'default (previous commit)' }}"
        echo ""
        echo "=== All Modified Files (${{ steps.filter.outputs.all_all_modified_files_count }} total) ==="
        echo "${{ steps.filter.outputs.all_all_modified_files }}"
        echo ""
        echo "=== Filters That Matched ==="
        echo "changed_keys: ${{ steps.filter.outputs.changed_keys }}"
        echo ""
        echo "=== Filter Results ==="
        echo "docs: ${{ steps.filter.outputs.docs_any_modified }}"
        echo "ci: ${{ steps.filter.outputs.ci_any_modified }}"
        echo "core: ${{ steps.filter.outputs.core_any_modified }}"
        echo "operator: ${{ steps.filter.outputs.operator_any_modified }}"
        echo "deploy: ${{ steps.filter.outputs.deploy_any_modified }}"
        echo "planner: ${{ steps.filter.outputs.planner_any_modified }}"
        echo "vllm: ${{ steps.filter.outputs.vllm_any_modified }}"
        echo "sglang: ${{ steps.filter.outputs.sglang_any_modified }}"
        echo "trtllm: ${{ steps.filter.outputs.trtllm_any_modified }}"
        echo "frontend: ${{ steps.filter.outputs.frontend_any_modified }}"
        echo "rust: ${{ steps.filter.outputs.rust_any_modified }}"
        echo ""
        echo "=== Files Matching Each Filter ==="
        echo "docs: ${{ steps.filter.outputs.docs_all_modified_files }}"
        echo "ci: ${{ steps.filter.outputs.ci_all_modified_files }}"
        echo "core: ${{ steps.filter.outputs.core_all_modified_files }}"
        echo "operator: ${{ steps.filter.outputs.operator_all_modified_files }}"
        echo "deploy: ${{ steps.filter.outputs.deploy_all_modified_files }}"
        echo "planner: ${{ steps.filter.outputs.planner_all_modified_files }}"
        echo "vllm: ${{ steps.filter.outputs.vllm_all_modified_files }}"
        echo "sglang: ${{ steps.filter.outputs.sglang_all_modified_files }}"
        echo "trtllm: ${{ steps.filter.outputs.trtllm_all_modified_files }}"
        echo "frontend: ${{ steps.filter.outputs.frontend_all_modified_files }}"
        echo "rust: ${{ steps.filter.outputs.rust_all_modified_files }}"

    - name: Check for uncovered files
      shell: bash
      run: |
        # Combine all filter-specific files into one list
        COVERED_FILES=$(echo "${{ steps.filter.outputs.docs_all_modified_files }} ${{ steps.filter.outputs.examples_all_modified_files }} ${{ steps.filter.outputs.ignore_all_modified_files }} ${{ steps.filter.outputs.ci_all_modified_files }} ${{ steps.filter.outputs.core_all_modified_files }} ${{ steps.filter.outputs.operator_all_modified_files }} ${{ steps.filter.outputs.deploy_all_modified_files }} ${{ steps.filter.outputs.planner_all_modified_files }} ${{ steps.filter.outputs.vllm_all_modified_files }} ${{ steps.filter.outputs.sglang_all_modified_files }} ${{ steps.filter.outputs.trtllm_all_modified_files }} ${{ steps.filter.outputs.frontend_all_modified_files }} ${{ steps.filter.outputs.rust_all_modified_files }}" | tr ' ' '\n' | grep -v '^$' | sort -u)

        # Get all modified files
        ALL_FILES=$(echo "${{ steps.filter.outputs.all_all_modified_files }}" | tr ' ' '\n' | grep -v '^$' | sort -u)

        # Find files in ALL but not in COVERED
        UNCOVERED=$(comm -23 <(echo "$ALL_FILES") <(echo "$COVERED_FILES"))

        if [ -n "$UNCOVERED" ]; then
          echo "::error::The following files are not covered by any CI filter:"
          echo "$UNCOVERED"
          echo ""
          echo "Please add these paths to .github/filters.yaml"
          echo "See .github/FILTERS.md for documentation"
          exit 1
        fi
        echo "All modified files are covered by CI filters."

