name: Quibble and Phan

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

jobs:
  test:
    name: "${{ matrix.mw }} | PHP ${{ matrix.php }} (${{ matrix.stage }})"

    strategy:
      matrix:
        include:
          # Latest stable MediaWiki - PHP 8.1 (phan)
          - mw: 'REL1_43'
            php: 8.1
            php-docker: 81
            experimental: false
            stage: phan

          # Latest MediaWiki master - PHP 8.1 (phan)
          - mw: 'master'
            php: 8.1
            php-docker: 81
            experimental: true
            stage: phan

          # Latest MediaWiki master - PHP 7.4 (coverage)
          - mw: 'master'
            php: 7.4
            php-docker: 74
            experimental: false
            stage: coverage

          # Latest stable MediaWiki - PHP 8.1 (phpunit)
          - mw: 'REL1_43'
            php: 8.1
            php-docker: 81
            experimental: false
            stage: phpunit

          # Latest MediaWiki master - PHP 8.1 (phpunit)
          - mw: 'master'
            php: 8.1
            php-docker: 81
            experimental: true
            stage: phpunit

          # Latest MediaWiki master - PHP 8.1 (selenium)
          - mw: 'master'
            php: 8.1
            php-docker: 81
            experimental: true
            stage: selenium

          # Latest MediaWiki master - PHP 8.1 (qunit)
          - mw: 'master'
            php: 8.1
            php-docker: 81
            experimental: true
            stage: qunit

          # Latest stable MediaWiki - PHP 8.1 (composer-test)
          - mw: 'REL1_43'
            php: 8.1
            php-docker: 81
            experimental: false
            stage: composer-test

    runs-on: ubuntu-latest

    env:
      DOCKER_REGISTRY: docker-registry.wikimedia.org
      DOCKER_ORG: releng
      QUIBBLE_DOCKER_IMAGE: quibble-buster-php${{ matrix.php-docker }}
      COVERAGE_DOCKER_IMAGE: quibble-buster-php${{ matrix.php-docker }}-coverage
      PHAN_DOCKER_IMAGE: mediawiki-phan-php${{ matrix.php-docker }}
      MEDIAWIKI_VERSION: ${{ matrix.mw }}

    steps:
      - name: Cancel Previous Runs
        uses: styfle/cancel-workflow-action@0.12.1
        with:
          access_token: ${{ github.token }}

      - uses: actions/checkout@v4

      # /home/runner/cache/                               Cache
      # /home/runner/src/                                 Mediawiki installation
      # /home/runner/src/extensions/EXTENSION_NAME/       Clone of the extension repository
      # /home/runner/docker-images/                       Docker images which exported with docker-save command
      # $GITHUB_WORKSPACE/.github/workflows/dependencies  Necessary dependencies - YAML syntax
      # $GITHUB_WORKSPACE/.github/workflows/globals.php   Add global configuration options for MediaWiki
      - name: Set up
        run: |
          if [ "${{ matrix.stage }}" == 'phan' ]; then
            export DOCKER_IMAGE="${PHAN_DOCKER_IMAGE}"
          elif [ "${{ matrix.stage }}" == coverage ]; then
            export DOCKER_IMAGE="${COVERAGE_DOCKER_IMAGE}"
          else
            export DOCKER_IMAGE="${QUIBBLE_DOCKER_IMAGE}"
          fi
          echo "DOCKER_IMAGE=${DOCKER_IMAGE}" >> $GITHUB_ENV

          # Get the latest docker tag (Ref: https://github.com/thcipriani/dockerregistry)
          DOCKER_LATEST_TAG="$(curl -sL "https://${DOCKER_REGISTRY}/v2/${DOCKER_ORG}/${DOCKER_IMAGE}/tags/list" |
            python3 -c 'import json;print("\n".join(json.loads(input())["tags"]))' |
            grep -v latest | sort -Vr | head -1)"
          echo "DOCKER_LATEST_TAG=${DOCKER_LATEST_TAG}" >> $GITHUB_ENV
          if [ "${{ matrix.stage }}" == 'phan' ] || [ "${{ matrix.stage }}" == 'coverage' ]; then
            echo "QUIBBLE_DOCKER_LATEST_TAG=$(curl -sL "https://${DOCKER_REGISTRY}/v2/${DOCKER_ORG}/${QUIBBLE_DOCKER_IMAGE}/tags/list" |
              python3 -c 'import json;print("\n".join(json.loads(input())["tags"]))' |
              grep -v latest | sort -Vr | head -1)" >> $GITHUB_ENV
          fi

          # Resolve dependencies
          if [ -e .github/workflows/dependencies ] && [ "${{ matrix.stage }}" == 'phan' ]; then
            cd .github/workflows
            curl -sL https://raw.githubusercontent.com/wikimedia/integration-config/master/zuul/dependencies.yaml -o dependencies.yaml
            curl -sL https://raw.githubusercontent.com/wikimedia/integration-config/master/zuul/phan_dependencies.yaml -o phan_dependencies.yaml
            curl -sL https://raw.githubusercontent.com/wikimedia/integration-config/master/zuul/parameter_functions.py -o pf.py
            curl -sL https://raw.githubusercontent.com/miraheze/ci-scripts/master/mediawiki/resolve_dependencies.py -o rd.py
            if [ -e dependencies ]; then
              echo "DEPENDENCIES=$(python3 rd.py dependencies)" >> $GITHUB_ENV
            fi
          fi

      - name: Cache docker image
        uses: actions/cache@v4
        with:
          path: /home/runner/docker-images/${{ env.DOCKER_IMAGE }}
          key: ${{ env.DOCKER_IMAGE }}:${{ env.DOCKER_LATEST_TAG }}

      - name: Load or pull docker image
        run: |
          docker load -i /home/runner/docker-images/"${DOCKER_IMAGE}" || \
            docker pull "${DOCKER_REGISTRY}/${DOCKER_ORG}/${DOCKER_IMAGE}:${DOCKER_LATEST_TAG}"

      - name: Cache quibble docker image
        if: ${{ matrix.stage == 'coverage' || matrix.stage == 'phan' }}
        uses: actions/cache@v4
        with:
          path: /home/runner/docker-images/${{ env.QUIBBLE_DOCKER_IMAGE }}
          key: ${{ env.QUIBBLE_DOCKER_IMAGE }}:${{ env.QUIBBLE_DOCKER_LATEST_TAG }}
      - name: Load or pull quibble docker image
        if: ${{ matrix.stage == 'coverage' || matrix.stage == 'phan' }}
        run: |
          docker load -i /home/runner/docker-images/"${QUIBBLE_DOCKER_IMAGE}" || \
            docker pull "${DOCKER_REGISTRY}/${DOCKER_ORG}/${QUIBBLE_DOCKER_IMAGE}:${QUIBBLE_DOCKER_LATEST_TAG}"

      - name: Download MediaWiki and extensions
        run: |
          cd /home/runner
          if [ ! -d src ]; then
            git clone -b "${MEDIAWIKI_VERSION}" --depth 1 https://gerrit.wikimedia.org/r/mediawiki/core src
            git clone --recurse-submodules -b "${MEDIAWIKI_VERSION}" --depth 1 https://gerrit.wikimedia.org/r/mediawiki/skins/Vector src/skins/Vector
            for dep in $DEPENDENCIES; do
              dependency=$(echo $dep | cut -d'|' -f1)
              repository=$(echo $dep | cut -sd'|' -f2)
              branch=$(echo $dep | rev | cut -sd'|' -f1 | rev)

              if [ $repository == $branch ]; then
                repository=""
              fi

              git clone --recurse-submodules -b "${branch:-${MEDIAWIKI_VERSION}}" --depth 1 "${repository:-https://gerrit.wikimedia.org/r/${dependency}}" src/"$(echo $dependency | cut -d'/' -f2,3)"
            done
          fi

          if [ -e "$GITHUB_WORKSPACE"/.github/workflows/globals.php ]; then
            echo 'require_once __DIR__ . "/../extensions/${{ github.event.repository.name }}/.github/workflows/globals.php";' >> src/includes/DevelopmentSettings.php
          fi

          git -C src/ log -n 1 --format="%H"

      - name: Cache dependencies (composer and npm)
        uses: actions/cache@v4
        with:
          path: /home/runner/cache
          key: ${{ runner.os }}-${{ env.MEDIAWIKI_VERSION }}-${{ hashFiles('**/*.json') }}

      - name: Setup PHP Action
        if: ${{ matrix.stage == 'phan' }}
        uses: shivammathur/setup-php@v2
        with:
          php-version: ${{ matrix.php }}
          tools: composer:v2

      - name: Composer install
        if: ${{ matrix.stage == 'phan' }}
        run: |
          if [ -e composer.json ]; then
            composer install --prefer-dist --no-progress --no-interaction
          fi

      - name: Fix PHPCS violations
        continue-on-error: true
        if: ${{ github.event_name == 'pull_request' && matrix.stage == 'composer-test' }}
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          if [ -e composer.json ]; then
            composer install --prefer-dist --no-progress --no-interaction
            composer fix
            rm composer.lock
            if ! git diff --exit-code --quiet; then
              git config --global user.name "github-actions"
              git config --global user.email "github-actions@users.noreply.github.com"
              git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
              git checkout -b ${GITHUB_HEAD_REF}
              git add .
              git commit -am "CI: lint code to MediaWiki standards" -m "Check commit and GitHub actions for more details"
              git pull origin ${GITHUB_HEAD_REF} --rebase
              git push --set-upstream origin ${GITHUB_HEAD_REF}
            else
              echo "No changes to commit"
            fi
          fi

      - name: Main Test
        continue-on-error: ${{ matrix.experimental }}
        run: |
          cd /home/runner
          # Move our extension
          sudo cp -r "${GITHUB_WORKSPACE}" src/extensions/
          mkdir -p cache cover
          chmod 777 src cache cover
          sudo chown -R nobody:nogroup src cache
          sudo chown $(id -u):$(id -g) src cache
          # Composer install
          if [ "${{ matrix.stage }}" == 'phan' ] || [ "${{ matrix.stage }}" == 'coverage' ]; then
            docker run \
              -e ZUUL_PROJECT=mediawiki/extensions/"${{ github.event.repository.name }}" \
              -v "$(pwd)"/cache:/cache \
              -v "$(pwd)"/src:/workspace/src \
              "${DOCKER_REGISTRY}/${DOCKER_ORG}/${QUIBBLE_DOCKER_IMAGE}:${QUIBBLE_DOCKER_LATEST_TAG}" \
              --skip-zuul \
              --packages-source composer \
              --skip-install \
              --skip all \
              $DEPENDENCIES
          fi

          if [ "${{ matrix.stage }}" == 'phan' ]; then
            docker run \
              -e THING_SUBNAME=extensions/"${{ github.event.repository.name }}" \
              -v "$(pwd)"/src:/mediawiki \
              "${DOCKER_REGISTRY}/${DOCKER_ORG}/${DOCKER_IMAGE}:${DOCKER_LATEST_TAG}" \
              --color
          elif [ "${{ matrix.stage }}" == 'coverage' ] && [ -d src/extensions/"${{ github.event.repository.name }}"/tests/phpunit ]; then
            docker run \
              --entrypoint quibble-with-supervisord \
              -e ZUUL_PROJECT=mediawiki/extensions/"${{ github.event.repository.name }}" \
              -v "$(pwd)"/cache:/cache \
              -v "$(pwd)"/src:/workspace/src \
              -v "$(pwd)"/cover:/workspace/cover \
              "${DOCKER_REGISTRY}/${DOCKER_ORG}/${DOCKER_IMAGE}:${DOCKER_LATEST_TAG}" \
              --skip-zuul \
              --skip-deps \
              -c mwext-phpunit-coverage
          elif [ "${{ matrix.stage }}" != 'coverage' ]; then
            docker run \
              --entrypoint quibble-with-supervisord \
              -e ZUUL_PROJECT=mediawiki/extensions/"${{ github.event.repository.name }}" \
              -v "$(pwd)"/cache:/cache \
              -v "$(pwd)"/src:/workspace/src \
              "${DOCKER_REGISTRY}/${DOCKER_ORG}/${DOCKER_IMAGE}:${DOCKER_LATEST_TAG}" \
              --skip-zuul \
              --packages-source composer \
              --run "${{ matrix.stage }}" \
              $DEPENDENCIES
            fi

      - name: Upload coverage to Codecov
        if: ${{ matrix.stage == 'coverage' }}
        uses: codecov/codecov-action@v5
        with:
          directory: /home/runner/cover

      - name: Tear down
        run: |
          cd /home/runner
          sudo rm -rf src/extensions/"${{ github.event.repository.name }}"
          # See https://doc.wikimedia.org/quibble/index.html#remove-localsettings-php-between-runs
          rm "$(pwd)"/src/LocalSettings.php || true
          mkdir -p docker-images
          docker save -o "$(pwd)/docker-images/${DOCKER_IMAGE}" \
            "${DOCKER_REGISTRY}/${DOCKER_ORG}/${DOCKER_IMAGE}:${DOCKER_LATEST_TAG}"
