name: CI

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

env:
  DEFAULT_PYTHON: 3.9
  PRE_COMMIT_HOME: ~/.cache/pre-commit
  DEFAULT_CAS: v1.0.2

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.9]
    name: Prepare Python ${{ matrix.python-version }} dependencies
    steps:
      - name: Check out code from GitHub
        uses: actions/checkout@v3.0.2
      - name: Set up Python ${{ matrix.python-version }}
        id: python
        uses: actions/setup-python@v3.1.2
        with:
          python-version: ${{ matrix.python-version }}
      - name: Restore Python virtual environment
        id: cache-venv
        uses: actions/cache@v3.0.2
        with:
          path: venv
          key: |
            ${{ runner.os }}-venv-${{ steps.python.outputs.python-version }}-${{ hashFiles('requirements.txt') }}-${{ hashFiles('requirements_tests.txt') }}
      - 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 -r requirements.txt -r requirements_tests.txt
      - name: Restore pre-commit environment from cache
        id: cache-precommit
        uses: actions/cache@v3.0.2
        with:
          path: ${{ env.PRE_COMMIT_HOME }}
          key: |
            ${{ runner.os }}-pre-commit-${{ 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-black:
    name: Check black
    runs-on: ubuntu-latest
    needs: prepare
    steps:
      - name: Check out code from GitHub
        uses: actions/checkout@v3.0.2
      - name: Set up Python ${{ env.DEFAULT_PYTHON }}
        uses: actions/setup-python@v3.1.2
        id: python
        with:
          python-version: ${{ env.DEFAULT_PYTHON }}
      - name: Restore Python virtual environment
        id: cache-venv
        uses: actions/cache@v3.0.2
        with:
          path: venv
          key: |
            ${{ runner.os }}-venv-${{ steps.python.outputs.python-version }}-${{ hashFiles('requirements.txt') }}-${{ hashFiles('requirements_tests.txt') }}
      - 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: Run black
        run: |
          . venv/bin/activate
          black --target-version py38 --check supervisor tests setup.py

  lint-dockerfile:
    name: Check Dockerfile
    runs-on: ubuntu-latest
    needs: prepare
    steps:
      - name: Check out code from GitHub
        uses: actions/checkout@v3.0.2
      - name: Register hadolint problem matcher
        run: |
          echo "::add-matcher::.github/workflows/matchers/hadolint.json"
      - name: Check Dockerfile
        uses: docker://hadolint/hadolint:v1.18.0
        with:
          args: hadolint Dockerfile

  lint-executable-shebangs:
    name: Check executables
    runs-on: ubuntu-latest
    needs: prepare
    steps:
      - name: Check out code from GitHub
        uses: actions/checkout@v3.0.2
      - name: Set up Python ${{ env.DEFAULT_PYTHON }}
        uses: actions/setup-python@v3.1.2
        id: python
        with:
          python-version: ${{ env.DEFAULT_PYTHON }}
      - name: Restore Python virtual environment
        id: cache-venv
        uses: actions/cache@v3.0.2
        with:
          path: venv
          key: |
            ${{ runner.os }}-venv-${{ steps.python.outputs.python-version }}-${{ hashFiles('requirements.txt') }}-${{ hashFiles('requirements_tests.txt') }}
      - 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@v3.0.2
        with:
          path: ${{ env.PRE_COMMIT_HOME }}
          key: |
            ${{ runner.os }}-pre-commit-${{ 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 check executables problem matcher
        run: |
          echo "::add-matcher::.github/workflows/matchers/check-executables-have-shebangs.json"
      - name: Run executables check
        run: |
          . venv/bin/activate
          pre-commit run --hook-stage manual check-executables-have-shebangs --all-files

  lint-flake8:
    name: Check flake8
    runs-on: ubuntu-latest
    needs: prepare
    steps:
      - name: Check out code from GitHub
        uses: actions/checkout@v3.0.2
      - name: Set up Python ${{ env.DEFAULT_PYTHON }}
        uses: actions/setup-python@v3.1.2
        id: python
        with:
          python-version: ${{ env.DEFAULT_PYTHON }}
      - name: Restore Python virtual environment
        id: cache-venv
        uses: actions/cache@v3.0.2
        with:
          path: venv
          key: |
            ${{ runner.os }}-venv-${{ steps.python.outputs.python-version }}-${{ hashFiles('requirements.txt') }}-${{ hashFiles('requirements_tests.txt') }}
      - 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 flake8 problem matcher
        run: |
          echo "::add-matcher::.github/workflows/matchers/flake8.json"
      - name: Run flake8
        run: |
          . venv/bin/activate
          flake8 supervisor tests

  lint-isort:
    name: Check isort
    runs-on: ubuntu-latest
    needs: prepare
    steps:
      - name: Check out code from GitHub
        uses: actions/checkout@v3.0.2
      - name: Set up Python ${{ env.DEFAULT_PYTHON }}
        uses: actions/setup-python@v3.1.2
        id: python
        with:
          python-version: ${{ env.DEFAULT_PYTHON }}
      - name: Restore Python virtual environment
        id: cache-venv
        uses: actions/cache@v3.0.2
        with:
          path: venv
          key: |
            ${{ runner.os }}-venv-${{ steps.python.outputs.python-version }}-${{ hashFiles('requirements.txt') }}-${{ hashFiles('requirements_tests.txt') }}
      - 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@v3.0.2
        with:
          path: ${{ env.PRE_COMMIT_HOME }}
          key: |
            ${{ runner.os }}-pre-commit-${{ 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 isort
        run: |
          . venv/bin/activate
          pre-commit run --hook-stage manual isort --all-files --show-diff-on-failure

  lint-json:
    name: Check JSON
    runs-on: ubuntu-latest
    needs: prepare
    steps:
      - name: Check out code from GitHub
        uses: actions/checkout@v3.0.2
      - name: Set up Python ${{ env.DEFAULT_PYTHON }}
        uses: actions/setup-python@v3.1.2
        id: python
        with:
          python-version: ${{ env.DEFAULT_PYTHON }}
      - name: Restore Python virtual environment
        id: cache-venv
        uses: actions/cache@v3.0.2
        with:
          path: venv
          key: |
            ${{ runner.os }}-venv-${{ steps.python.outputs.python-version }}-${{ hashFiles('requirements.txt') }}-${{ hashFiles('requirements_tests.txt') }}
      - 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@v3.0.2
        with:
          path: ${{ env.PRE_COMMIT_HOME }}
          key: |
            ${{ runner.os }}-pre-commit-${{ 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 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

  lint-pylint:
    name: Check pylint
    runs-on: ubuntu-latest
    needs: prepare
    steps:
      - name: Check out code from GitHub
        uses: actions/checkout@v3.0.2
      - name: Set up Python ${{ env.DEFAULT_PYTHON }}
        uses: actions/setup-python@v3.1.2
        id: python
        with:
          python-version: ${{ env.DEFAULT_PYTHON }}
      - name: Restore Python virtual environment
        id: cache-venv
        uses: actions/cache@v3.0.2
        with:
          path: venv
          key: |
            ${{ runner.os }}-venv-${{ steps.python.outputs.python-version }}-${{ hashFiles('requirements.txt') }}-${{ hashFiles('requirements_tests.txt') }}
      - 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 pylint problem matcher
        run: |
          echo "::add-matcher::.github/workflows/matchers/pylint.json"
      - name: Run pylint
        run: |
          . venv/bin/activate
          pylint supervisor tests

  lint-pyupgrade:
    name: Check pyupgrade
    runs-on: ubuntu-latest
    needs: prepare
    steps:
      - name: Check out code from GitHub
        uses: actions/checkout@v3.0.2
      - name: Set up Python ${{ env.DEFAULT_PYTHON }}
        uses: actions/setup-python@v3.1.2
        id: python
        with:
          python-version: ${{ env.DEFAULT_PYTHON }}
      - name: Restore Python virtual environment
        id: cache-venv
        uses: actions/cache@v3.0.2
        with:
          path: venv
          key: |
            ${{ runner.os }}-venv-${{ steps.python.outputs.python-version }}-${{ hashFiles('requirements.txt') }}-${{ hashFiles('requirements_tests.txt') }}
      - 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@v3.0.2
        with:
          path: ${{ env.PRE_COMMIT_HOME }}
          key: |
            ${{ runner.os }}-pre-commit-${{ 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 pyupgrade
        run: |
          . venv/bin/activate
          pre-commit run --hook-stage manual pyupgrade --all-files --show-diff-on-failure

  pytest:
    runs-on: ubuntu-latest
    needs: prepare
    strategy:
      matrix:
        python-version: [3.9]
    name: Run tests Python ${{ matrix.python-version }}
    steps:
      - name: Check out code from GitHub
        uses: actions/checkout@v3.0.2
      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v3.1.2
        id: python
        with:
          python-version: ${{ matrix.python-version }}
      - name: Install CAS tools
        uses: home-assistant/actions/helpers/cas@master
        with:
          version: ${{ env.DEFAULT_CAS }}
      - name: Restore Python virtual environment
        id: cache-venv
        uses: actions/cache@v3.0.2
        with:
          path: venv
          key: |
            ${{ runner.os }}-venv-${{ steps.python.outputs.python-version }}-${{ hashFiles('requirements.txt') }}-${{ hashFiles('requirements_tests.txt') }}
      - 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: Install additional system dependencies
        run: |
          sudo apt-get update
          sudo apt-get install -y --no-install-recommends libpulse0 libudev1
      - 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 supervisor \
            -o console_output_style=count \
            tests
      - name: Upload coverage artifact
        uses: actions/upload-artifact@v3.0.0
        with:
          name: coverage-${{ matrix.python-version }}
          path: .coverage

  coverage:
    name: Process test coverage
    runs-on: ubuntu-latest
    needs: pytest
    steps:
      - name: Check out code from GitHub
        uses: actions/checkout@v3.0.2
      - name: Set up Python ${{ env.DEFAULT_PYTHON }}
        uses: actions/setup-python@v3.1.2
        id: python
        with:
          python-version: ${{ env.DEFAULT_PYTHON }}
      - name: Restore Python virtual environment
        id: cache-venv
        uses: actions/cache@v3.0.2
        with:
          path: venv
          key: |
            ${{ runner.os }}-venv-${{ steps.python.outputs.python-version }}-${{ hashFiles('requirements.txt') }}-${{ hashFiles('requirements_tests.txt') }}
      - 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@v3
      - 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@v3.1.0
