---
name: CI

# yamllint disable-line rule:truthy
on:
  push:
    branches:
      - main
  pull_request: ~
  workflow_dispatch:

env:
  DEFAULT_PYTHON: "3.13"
  PRE_COMMIT_CACHE: ~/.cache/pre-commit

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

jobs:
  # Separate job to pre-populate the base dependency cache
  # This prevent upcoming jobs to do the same individually
  prepare:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["3.12", "3.13"]
    outputs:
      python-version: ${{ matrix.python-version }}
    name: Prepare Python ${{ matrix.python-version }} dependencies
    steps:
      - name: Check out code from GitHub
        uses: actions/checkout@v5.0.0
      - name: Set up Python
        id: python
        uses: actions/setup-python@v6.0.0
        with:
          python-version: ${{ matrix.python-version }}
      - name: Restore Python virtual environment
        id: cache-venv
        uses: actions/cache@v4.3.0
        with:
          path: venv
          key: >
            ${{ runner.os }}-venv-${{ matrix.python-version }}-
            ${{ hashFiles('pyproject.toml') }}
      - name: Create Python virtual environment
        if: steps.cache-venv.outputs.cache-hit != 'true'
        run: |
          python -m venv venv
          . venv/bin/activate
          pip install -U pip setuptools
          pip install -e ".[dev]"
      - name: Restore pre-commit environment from cache
        id: cache-precommit
        uses: actions/cache@v4.3.0
        with:
          path: ${{ env.PRE_COMMIT_CACHE }}
          lookup-only: true
          key: >
            ${{ runner.os }}-pre-commit-${{ matrix.python-version }}-
            ${{ hashFiles('.pre-commit-config.yaml') }}
          restore-keys: |
            ${{ runner.os }}-pre-commit-
      - name: Install pre-commit dependencies
        if: steps.cache-precommit.outputs.cache-hit != 'true'
        run: |
          . venv/bin/activate
          pre-commit install-hooks

  lint-ruff-format:
    name: Check ruff-format Python ${{ matrix.python-version }}
    runs-on: ubuntu-latest
    needs: prepare
    strategy:
      matrix:
        python-version: ["3.12", "3.13"]
    steps:
      - name: Check out code from GitHub
        uses: actions/checkout@v5.0.0
      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v6.0.0
        id: python
        with:
          python-version: ${{ matrix.python-version }}
      - name: Restore Python virtual environment
        id: cache-venv
        uses: actions/cache@v4.3.0
        with:
          path: venv
          key: >
            ${{ runner.os }}-venv-${{ matrix.python-version }}-
            ${{ hashFiles('pyproject.toml') }}
      - name: Fail job if Python cache restore failed
        if: steps.cache-venv.outputs.cache-hit != 'true'
        run: |
          echo "Failed to restore Python virtual environment from cache"
          exit 1
      - name: Restore pre-commit environment from cache
        id: cache-precommit
        uses: actions/cache@v4.3.0
        with:
          path: ${{ env.PRE_COMMIT_CACHE }}
          key: >
            ${{ runner.os }}-pre-commit-${{ matrix.python-version }}-
            ${{ hashFiles('.pre-commit-config.yaml') }}
      - name: Fail job if cache restore failed
        if: steps.cache-venv.outputs.cache-hit != 'true'
        run: |
          echo "Failed to restore Python virtual environment from cache"
          exit 1
      - name: Run ruff-format
        run: |
          . venv/bin/activate
          pre-commit run \
            --hook-stage manual ruff-format --all-files --show-diff-on-failure
        env:
          RUFF_OUTPUT_FORMAT: github

  lint-ruff:
    name: Check ruff Python ${{ matrix.python-version }}
    runs-on: ubuntu-latest
    needs: prepare
    strategy:
      matrix:
        python-version: ["3.12", "3.13"]
    steps:
      - name: Check out code from GitHub
        uses: actions/checkout@v5.0.0
      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v6.0.0
        id: python
        with:
          python-version: ${{ matrix.python-version }}
      - name: Restore Python virtual environment
        id: cache-venv
        uses: actions/cache@v4.3.0
        with:
          path: venv
          key: >
            ${{ runner.os }}-venv-${{ matrix.python-version }}-
            ${{ hashFiles('pyproject.toml') }}
      - name: Fail job if Python cache restore failed
        if: steps.cache-venv.outputs.cache-hit != 'true'
        run: |
          echo "Failed to restore Python virtual environment from cache"
          exit 1
      - name: Restore pre-commit environment from cache
        id: cache-precommit
        uses: actions/cache@v4.3.0
        with:
          path: ${{ env.PRE_COMMIT_CACHE }}
          key: >
            ${{ runner.os }}-pre-commit-${{ matrix.python-version }}-
            ${{ hashFiles('.pre-commit-config.yaml') }}
      - name: Fail job if cache restore failed
        if: steps.cache-venv.outputs.cache-hit != 'true'
        run: |
          echo "Failed to restore Python virtual environment from cache"
          exit 1
      - name: Run ruff
        run: |
          . venv/bin/activate
          pre-commit run \
            --hook-stage manual ruff --all-files --show-diff-on-failure
        env:
          RUFF_OUTPUT_FORMAT: github

  lint-other:
    name: Check other linters Python ${{ matrix.python-version }}
    runs-on: ubuntu-24.04
    needs: prepare
    strategy:
      matrix:
        python-version: ["3.12", "3.13"]
    steps:
      - name: Check out code from GitHub
        uses: actions/checkout@v5.0.0
      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v6.0.0
        id: python
        with:
          python-version: ${{ matrix.python-version }}
      - name: Restore Python virtual environment
        id: cache-venv
        uses: actions/cache@v4.3.0
        with:
          path: venv
          key: >
            ${{ runner.os }}-venv-${{ matrix.python-version }}-
            ${{ hashFiles('pyproject.toml') }}
      - name: Fail job if Python cache restore failed
        if: steps.cache-venv.outputs.cache-hit != 'true'
        run: |
          echo "Failed to restore Python virtual environment from cache"
          exit 1
      - name: Restore pre-commit environment from cache
        id: cache-precommit
        uses: actions/cache@v4.3.0
        with:
          path: ${{ env.PRE_COMMIT_CACHE }}
          key: >
            ${{ runner.os }}-pre-commit-${{ matrix.python-version }}-
            ${{ hashFiles('.pre-commit-config.yaml') }}
      - name: Fail job if cache restore failed
        if: steps.cache-venv.outputs.cache-hit != 'true'
        run: |
          echo "Failed to restore Python virtual environment from cache"
          exit 1

      - name: Register yamllint problem matcher
        run: |
          echo "::add-matcher::.github/workflows/matchers/yamllint.json"
      - name: Run yamllint
        run: |
          . venv/bin/activate
          pre-commit run \
            --hook-stage manual yamllint --all-files --show-diff-on-failure

      - name: Register check-json problem matcher
        run: |
          echo "::add-matcher::.github/workflows/matchers/check-json.json"
      - name: Run check-json
        run: |
          . venv/bin/activate
          pre-commit run --hook-stage manual check-json --all-files

      - name: Run prettier
        run: |
          . venv/bin/activate
          pre-commit run --hook-stage manual prettier --all-files

      - name: Register check executables problem matcher
        # yamllint disable rule:line-length
        run: |
          echo "::add-matcher::.github/workflows/matchers/check-executables-have-shebangs.json"
        # yamllint enable rule:line-length
      - name: Run executables check
        run: |
          . venv/bin/activate
          pre-commit run \
            --hook-stage manual check-executables-have-shebangs --all-files

      - name: Register codespell problem matcher
        run: |
          echo "::add-matcher::.github/workflows/matchers/codespell.json"
      - name: Run codespell
        run: |
          . venv/bin/activate
          pre-commit run \
            --show-diff-on-failure --hook-stage manual codespell --all-files

      - name: Register hadolint problem matcher
        run: |
          echo "::add-matcher::.github/workflows/matchers/hadolint.json"
      - name: Check Dockerfile.dev
        uses: docker://hadolint/hadolint:v1.18.2
        with:
          args: hadolint Dockerfile.dev

  pytest:
    runs-on: ubuntu-latest
    needs: prepare
    strategy:
      matrix:
        python-version: ["3.12", "3.13"]
    name: Run tests Python ${{ matrix.python-version }}
    steps:
      - name: Check out code from GitHub
        uses: actions/checkout@v5.0.0
      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v6.0.0
        id: python
        with:
          python-version: ${{ matrix.python-version }}
      - name: Restore Python virtual environment
        id: cache-venv
        uses: actions/cache@v4.3.0
        with:
          path: venv
          key: >
            ${{ runner.os }}-venv-${{ matrix.python-version }}-
            ${{ hashFiles('pyproject.toml') }}
      - name: Fail job if Python cache restore failed
        if: steps.cache-venv.outputs.cache-hit != 'true'
        run: |
          echo "Failed to restore Python virtual environment from cache"
          exit 1
      - name: Register Python problem matcher
        run: |
          echo "::add-matcher::.github/workflows/matchers/python.json"
      - name: Install Pytest Annotation plugin
        run: |
          . venv/bin/activate
          # Ideally this should be part of our dependencies
          # However this plugin is fairly new and doesn't run correctly
          # on a non-GitHub environment.
          pip install pytest-github-actions-annotate-failures
      - name: Run pytest
        run: |
          . venv/bin/activate
          pytest \
            -qq \
            --timeout=10 \
            --durations=10 \
            --cov aiohasupervisor \
            -o console_output_style=count \
            tests
      - name: Upload coverage artifact
        uses: actions/upload-artifact@v4.3.6
        with:
          name: coverage-${{ matrix.python-version }}
          path: .coverage

  coverage:
    name: Process test coverage
    runs-on: ubuntu-latest
    needs: ["pytest", "prepare"]
    steps:
      - name: Check out code from GitHub
        uses: actions/checkout@v5.0.0
      - name: Set up Python ${{ env.DEFAULT_PYTHON }}
        uses: actions/setup-python@v6.0.0
        id: python
        with:
          python-version: ${{ env.DEFAULT_PYTHON }}
      - name: Restore Python virtual environment
        id: cache-venv
        uses: actions/cache@v4.3.0
        with:
          path: venv
          key: >
            ${{ runner.os }}-venv-${{ env.DEFAULT_PYTHON }}-
            ${{ hashFiles('pyproject.toml') }}
      - name: Fail job if Python cache restore failed
        if: steps.cache-venv.outputs.cache-hit != 'true'
        run: |
          echo "Failed to restore Python virtual environment from cache"
          exit 1
      - name: Download all coverage artifacts
        uses: actions/download-artifact@v5.0.0
      - name: Combine coverage results
        run: |
          . venv/bin/activate
          coverage combine coverage*/.coverage*
          coverage report
          coverage xml
      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v5.5.1
