name: Build supervisor

on:
  workflow_dispatch:
    inputs:
      channel:
        description: "Channel"
        required: true
        default: "dev"
      version:
        description: "Version"
        required: true
      publish:
        description: "Publish"
        required: true
        default: "false"
      stable:
        description: "Stable"
        required: true
        default: "false"
  pull_request:
    branches: ["main"]
  release:
    types: ["published"]
  push:
    branches: ["main"]
    paths:
      - "rootfs/**"
      - "supervisor/**"
      - build.yaml
      - Dockerfile
      - requirements.txt
      - setup.py

env:
  DEFAULT_PYTHON: 3.9
  BUILD_NAME: supervisor
  BUILD_TYPE: supervisor
  WHEELS_TAG: 3.9-alpine3.14

jobs:
  init:
    name: Initialize build
    runs-on: ubuntu-latest
    outputs:
      architectures: ${{ steps.info.outputs.architectures }}
      version: ${{ steps.version.outputs.version }}
      channel: ${{ steps.version.outputs.channel }}
      publish: ${{ steps.version.outputs.publish }}
      requirements: ${{ steps.requirements.outputs.changed }}
    steps:
      - name: Checkout the repository
        uses: actions/checkout@v3.0.2
        with:
          fetch-depth: 0

      - name: Get information
        id: info
        uses: home-assistant/actions/helpers/info@master

      - name: Get version
        id: version
        uses: home-assistant/actions/helpers/version@master
        with:
          type: ${{ env.BUILD_TYPE }}

      - name: Get changed files
        id: changed_files
        if: steps.version.outputs.publish == 'false'
        uses: jitterbit/get-changed-files@v1

      - name: Check if requirements files changed
        id: requirements
        run: |
          if [[ "${{ steps.changed_files.outputs.all }}" =~ (requirements.txt|build.json) ]]; then
            echo "::set-output name=changed::true"
          fi

  build:
    name: Build ${{ matrix.arch }} supervisor
    needs: init
    runs-on: ubuntu-latest
    strategy:
      matrix:
        arch: ${{ fromJson(needs.init.outputs.architectures) }}
    steps:
      - name: Checkout the repository
        uses: actions/checkout@v3.0.2
        with:
          fetch-depth: 0

      - name: Build wheels
        if: needs.init.outputs.requirements == 'true'
        uses: home-assistant/wheels@master
        with:
          tag: ${{ env.WHEELS_TAG }}
          arch: ${{ matrix.arch }}
          wheels-host: wheels.hass.io
          wheels-key: ${{ secrets.WHEELS_KEY }}
          wheels-user: wheels
          apk: "build-base;libffi-dev;openssl-dev;cargo"
          skip-binary: aiohttp
          requirements: "requirements.txt"

      - name: Set version
        if: needs.init.outputs.publish == 'true'
        uses: home-assistant/actions/helpers/version@master
        with:
          type: ${{ env.BUILD_TYPE }}

      - name: Login to DockerHub
        if: needs.init.outputs.publish == 'true'
        uses: docker/login-action@v2.0.0
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Login to GitHub Container Registry
        if: needs.init.outputs.publish == 'true'
        uses: docker/login-action@v2.0.0
        with:
          registry: ghcr.io
          username: ${{ github.repository_owner }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Set build arguments
        if: needs.init.outputs.publish == 'false'
        run: echo "BUILD_ARGS=--test" >> $GITHUB_ENV

      - name: Build supervisor
        uses: home-assistant/builder@2022.03.1
        with:
          args: |
            $BUILD_ARGS \
            --${{ matrix.arch }} \
            --target /data \
            --generic ${{ needs.init.outputs.version }}
        env:
          CAS_API_KEY: ${{ secrets.CAS_TOKEN }}

  codenotary:
    name: CAS signature
    needs: init
    runs-on: ubuntu-latest
    steps:
      - name: Checkout the repository
        if: needs.init.outputs.publish == 'true'
        uses: actions/checkout@v3.0.2
        with:
          fetch-depth: 0

      - name: Set up Python ${{ env.DEFAULT_PYTHON }}
        if: needs.init.outputs.publish == 'true'
        uses: actions/setup-python@v3.1.2
        with:
          python-version: ${{ env.DEFAULT_PYTHON }}

      - name: Set version
        if: needs.init.outputs.publish == 'true'
        uses: home-assistant/actions/helpers/version@master
        with:
          type: ${{ env.BUILD_TYPE }}

      - name: Install dirhash and calc hash
        if: needs.init.outputs.publish == 'true'
        id: dirhash
        run: |
          pip3 install dirhash
          dir_hash="$(dirhash "${{ github.workspace }}/supervisor" -a sha256 --match "*.py")"
          echo "::set-output name=dirhash::${dir_hash}"

      - name: Signing Source
        if: needs.init.outputs.publish == 'true'
        uses: home-assistant/actions/helpers/codenotary@master
        with:
          source: hash://${{ steps.dirhash.outputs.dirhash }}
          asset: supervisor-${{ needs.init.outputs.version }}
          token: ${{ secrets.CAS_TOKEN }}

  version:
    name: Update version
    needs: ["init", "run_supervisor"]
    runs-on: ubuntu-latest
    steps:
      - name: Checkout the repository
        if: needs.init.outputs.publish == 'true'
        uses: actions/checkout@v3.0.2

      - name: Initialize git
        if: needs.init.outputs.publish == 'true'
        uses: home-assistant/actions/helpers/git-init@master
        with:
          name: ${{ secrets.GIT_NAME }}
          email: ${{ secrets.GIT_EMAIL }}
          token: ${{ secrets.GIT_TOKEN }}

      - name: Update version file
        if: needs.init.outputs.publish == 'true'
        uses: home-assistant/actions/helpers/version-push@master
        with:
          key: ${{ env.BUILD_NAME }}
          version: ${{ needs.init.outputs.version }}
          channel: ${{ needs.init.outputs.channel }}

  run_supervisor:
    runs-on: ubuntu-latest
    name: Run the Supervisor
    needs: ["build", "codenotary", "init"]
    timeout-minutes: 60
    steps:
      - name: Checkout the repository
        uses: actions/checkout@v3.0.2

      - name: Build the Supervisor
        if: needs.init.outputs.publish != 'true'
        uses: home-assistant/builder@2022.03.1
        with:
          args: |
            --test \
            --amd64 \
            --target /data \
            --generic runner

      - name: Pull Supervisor
        if: needs.init.outputs.publish == 'true'
        run: |
          docker pull ghcr.io/home-assistant/amd64-hassio-supervisor:${{ needs.init.outputs.version }}
          docker tag ghcr.io/home-assistant/amd64-hassio-supervisor:${{ needs.init.outputs.version }} homeassistant/amd64-hassio-supervisor:runner

      - name: Create the Supervisor
        run: |
          mkdir -p /tmp/supervisor/data
          docker create --name hassio_supervisor \
            --privileged \
            --security-opt seccomp=unconfined \
            --security-opt apparmor=unconfined \
            -v /run/docker.sock:/run/docker.sock \
            -v /run/dbus:/run/dbus \
            -v /tmp/supervisor/data:/data \
            -v /etc/machine-id:/etc/machine-id:ro \
            -e SUPERVISOR_SHARE="/tmp/supervisor/data" \
            -e SUPERVISOR_NAME=hassio_supervisor \
            -e SUPERVISOR_DEV=1 \
            -e SUPERVISOR_MACHINE="qemux86-64" \
          homeassistant/amd64-hassio-supervisor:runner

      - name: Start the Supervisor
        run: docker start hassio_supervisor

      - name: Wait for Supervisor to come up
        run: |
          SUPERVISOR=$(docker inspect --format='{{.NetworkSettings.IPAddress}}' hassio_supervisor)
          ping="error"
          while [ "$ping" != "ok" ]; do
            ping=$(curl -sSL "http://$SUPERVISOR/supervisor/ping" | jq -r '.result')
            sleep 5
          done

      - name: Check the Supervisor
        run: |
          echo "Checking supervisor info"
          test=$(docker exec hassio_cli ha supervisor info --no-progress --raw-json | jq -r '.result')
          if [ "$test" != "ok" ]; then
            exit 1
          fi

          echo "Checking supervisor network info"
          test=$(docker exec hassio_cli ha network info --no-progress --raw-json | jq -r '.result')
          if [ "$test" != "ok" ]; then
            exit 1
          fi

      - name: Check the Store / Addon
        run: |
          echo "Install Core SSH Add-on"
          test=$(docker exec hassio_cli ha addons install core_ssh --no-progress --raw-json | jq -r '.result')
          if [ "$test" != "ok" ]; then
            exit 1
          fi

          # Make sure it actually installed
          test=$(docker exec hassio_cli ha addons info core_ssh --no-progress --raw-json | jq -r '.data.version')
          if [[ "$test" == "null" ]]; then
            exit 1
          fi

          echo "Start Core SSH Add-on"
          test=$(docker exec hassio_cli ha addons start core_ssh --no-progress --raw-json | jq -r '.result')
          if [ "$test" != "ok" ]; then
            exit 1
          fi

      - name: Check the Supervisor code sign
        if: needs.init.outputs.publish == 'true'
        run: |
          echo "Enable Content-Trust"
          test=$(docker exec hassio_cli ha security options --content-trust=true --no-progress --raw-json | jq -r '.result')
          if [ "$test" != "ok" ]; then
            exit 1
          fi

          echo "Run supervisor health check"
          test=$(docker exec hassio_cli ha resolution healthcheck --no-progress --raw-json | jq -r '.result')
          if [ "$test" != "ok" ]; then
            exit 1
          fi

          echo "Check supervisor unhealthy"
          test=$(docker exec hassio_cli ha resolution info --no-progress --raw-json | jq -r '.data.unhealthy[]')
          if [ "$test" != "" ]; then
            exit 1
          fi

          echo "Check supervisor supported"
          test=$(docker exec hassio_cli ha resolution info --no-progress --raw-json | jq -r '.data.unsupported[]')
          if [[ "$test" =~ source_mods ]]; then
            exit 1
          fi

      - name: Create full backup
        id: backup
        run: |
          test=$(docker exec hassio_cli ha backups new --no-progress --raw-json)
          if [ "$(echo $test | jq -r '.result')" != "ok" ]; then
            exit 1
          fi
          echo "::set-output name=slug::$(echo $test | jq -r '.data.slug')"

      - name: Uninstall SSH add-on
        run: |
          test=$(docker exec hassio_cli ha addons uninstall core_ssh --no-progress --raw-json | jq -r '.result')
          if [ "$test" != "ok" ]; then
            exit 1
          fi

      - name: Restart supervisor
        run: |
          test=$(docker exec hassio_cli ha supervisor restart --no-progress --raw-json | jq -r '.result')
          if [ "$test" != "ok" ]; then
            exit 1
          fi

      - name: Wait for Supervisor to come up
        run: |
          SUPERVISOR=$(docker inspect --format='{{.NetworkSettings.IPAddress}}' hassio_supervisor)
          ping="error"
          while [ "$ping" != "ok" ]; do
            ping=$(curl -sSL "http://$SUPERVISOR/supervisor/ping" | jq -r '.result')
            sleep 5
          done

      - name: Restore SSH add-on from backup
        run: |
          test=$(docker exec hassio_cli ha backups restore ${{ steps.backup.outputs.slug }} --addons core_ssh --no-progress --raw-json | jq -r '.result')
          if [ "$test" != "ok" ]; then
            exit 1
          fi

          # Make sure it actually installed
          test=$(docker exec hassio_cli ha addons info core_ssh --no-progress --raw-json | jq -r '.data.version')
          if [[ "$test" == "null" ]]; then
            exit 1
          fi

      - name: Restore SSL directory from backup
        run: |
          test=$(docker exec hassio_cli ha backups restore ${{ steps.backup.outputs.slug }} --folders ssl --no-progress --raw-json | jq -r '.result')
          if [ "$test" != "ok" ]; then
            exit 1
          fi

      - name: Get supervisor logs on failiure
        if: ${{ cancelled() || failure() }}
        run: docker logs hassio_supervisor
