name: Tests and delivery
on:
  schedule:
    - cron: '0 8 * * *' # Run at 8AM UTC.
  push:
    branches:
      - '3.x'
      - '3.x-**'
    tags:
      - '3.*'
  pull_request:
    branches:
      - '3.x'

jobs:
  build:
    name: Build Docker images
    runs-on: ubuntu-latest
    steps:
      - name: Checkout the repository
        uses: actions/checkout@v3
      - name: Set default FARMOS_REPO and FARMOS_VERSION.
        run: |
          echo "FARMOS_REPO=${GITHUB_REPOSITORY}" >> $GITHUB_ENV
          echo "FARMOS_VERSION=3.x" >> $GITHUB_ENV
      - name: Set FARMOS_VERSION for branch push event.
        if: github.event_name == 'push' && github.ref_type == 'branch'
        run: echo "FARMOS_VERSION=${GITHUB_REF:11}" >> $GITHUB_ENV
      - name: Set FARMOS_VERSION for tag push event.
        if: github.event_name == 'push' && github.ref_type == 'tag'
        run: echo "FARMOS_VERSION=${GITHUB_REF:10}" >> $GITHUB_ENV
      - name: Set FARMOS_VERSION and FARMOS_REPO for pull request event.
        if: github.event_name == 'pull_request'
        run: |
          echo "FARMOS_VERSION=${GITHUB_HEAD_REF}" >> $GITHUB_ENV
          echo "FARMOS_REPO=${{ github.event.pull_request.head.repo.full_name }}" >> $GITHUB_ENV
      - name: Build and save farmOS Docker image
        run: |
          docker build --build-arg FARMOS_REPO=https://github.com/${FARMOS_REPO} --build-arg FARMOS_VERSION=${FARMOS_VERSION} -t farmos/farmos:3.x docker
          docker save farmos/farmos:3.x > /tmp/farmos.tar
      - name: Cache farmOS Docker image
        uses: actions/cache@v3
        with:
          path: /tmp/farmos.tar
          key: farmos-${{ github.run_id }}
      # This builds the dev Docker image using the specified FARMOS_VERSION,
      # but notably it does NOT override the default PROJECT_VERSION, so the
      # farmOS Composer project 3.x branch is always used.
      - name: Build and save farmOS dev Docker image
        run: |
          docker build --build-arg FARMOS_REPO=https://github.com/${FARMOS_REPO} --build-arg FARMOS_VERSION=${FARMOS_VERSION} -t farmos/farmos:3.x-dev docker/dev
          docker save farmos/farmos:3.x-dev > /tmp/farmos-dev.tar
      - name: Cache farmOS dev Docker image
        uses: actions/cache@v3
        with:
          path: /tmp/farmos-dev.tar
          key: farmos-dev-${{ github.run_id }}
    outputs:
      farmos_version: ${{ env.FARMOS_VERSION }}
  sniff:
    name: Run PHP Codesniffer and PHPStan
    runs-on: ubuntu-latest
    needs: build
    steps:
      - name: Checkout the repository
        uses: actions/checkout@v3
      - name: Restore farmOS dev Docker image from cache
        uses: actions/cache@v3
        with:
          path: /tmp/farmos-dev.tar
          key: farmos-dev-${{ github.run_id }}
      - name: Load farmos/farmos:3.x-dev image
        run: docker load < /tmp/farmos-dev.tar
      - name: Run PHP CodeSniffer
        run: docker run farmos/farmos:3.x-dev phpcs /opt/drupal/web/profiles/farm
      - name: Run PHPStan
        run: docker run farmos/farmos:3.x-dev phpstan analyze --memory-limit 1G /opt/drupal/web/profiles/farm
      - name: Check PHP compatibility of contrib modules and themes (ignore warnings).
        run: |
          docker run farmos/farmos:3.x-dev phpcs --standard=PHPCompatibility --runtime-set testVersion 8.2- --warning-severity=0 /opt/drupal/web/modules
          docker run farmos/farmos:3.x-dev phpcs --standard=PHPCompatibility --runtime-set testVersion 8.2- --warning-severity=0 /opt/drupal/web/themes
  test:
    name: Run PHPUnit tests
    runs-on: ubuntu-latest
    needs: build
    strategy:
      matrix:
        dbms:
         - pgsql
         - mariadb
         - sqlite
        include:
          - dbms: pgsql
            DB_URL: pgsql://farm:farm@db/farm
            processes: auto
          - dbms: mariadb
            DB_URL: mysql://farm:farm@db/farm
            processes: auto
          - dbms: sqlite
            DB_URL: sqlite://localhost/sites/default/files/db.sqlite
            processes: 1
    steps:
      - name: Print test matrix variables
        run: echo "matrix.dbms=${{ matrix.dbms }}, matrix.DB_URL=${{ matrix.DB_URL }}"
      - name: Checkout the repository
        uses: actions/checkout@v3
      - name: Restore farmOS dev Docker image from cache
        uses: actions/cache@v3
        with:
          path: /tmp/farmos-dev.tar
          key: farmos-dev-${{ github.run_id }}
      - name: Load farmos/farmos:3.x-dev image
        run: docker load < /tmp/farmos-dev.tar
      # Build a new docker-compose.yml file from docker-compose.testing.common + docker-compose.testing.{dbms}.yml.
      # Copy to the current directory so that farmOS volume mounts don't change to the docker/www folder.
      - name: Create docker-compose.yml
        env:
          DB_URL: ${{ matrix.DB_URL }}
        run: |
          cp docker/docker-compose.testing.* .
          docker compose -f docker-compose.testing.common.yml -f docker-compose.testing.${{ matrix.dbms }}.yml config > docker-compose.yml
      - name: Start containers
        run: docker compose up -d
      - name: Wait until www container is ready
      # The www-container-fs-ready file is only created once we expect the containers to be online
      # so waiting for that lets us know it is safe to start the tests
        run: until [ -f ./www/www-container-fs-ready ]; do sleep 0.1; done
      - name: Install pg_trgm PostgreSQL extension
      # This avoids race conditions when trying to automatically install it in concurrently run tests.
        if: matrix.dbms == 'pgsql'
        run: docker compose exec -T db psql -U farm -c 'CREATE EXTENSION IF NOT EXISTS pg_trgm;'
      - name: Run PHPUnit tests
        run: docker compose exec -u www-data -T www paratest -vv --processes=${{ matrix.processes }} /opt/drupal/web/profiles/farm
      - name: Test Drush site install with all modules
        run: docker compose exec -u www-data -T www drush site-install --db-url=${{ matrix.DB_URL }} farm farm.modules='all'
  release:
    name: Create release
    # We only create a release if this is a tag push event to the official
    # repository.
    if: github.repository == 'farmOS/farmOS' && github.event_name == 'push' && github.ref_type == 'tag'
    runs-on: ubuntu-latest
    needs:
      - build
      - sniff
      - test
    steps:
      - name: Set FARMOS_VERSION from previous output.
        run: echo "FARMOS_VERSION=${{ needs.build.outputs.farmos_version }}" >> $GITHUB_ENV
      - name: Restore farmOS Docker image from cache
        uses: actions/cache@v3
        with:
          path: /tmp/farmos.tar
          key: farmos-${{ github.run_id }}
      - name: Load farmos/farmos:3.x image
        run: docker load < /tmp/farmos.tar
      - name: Run farmOS Docker container
        run: docker run --rm -v /tmp/farmOS:/opt/drupal farmos/farmos:3.x true
      - name: Create artifact
        run: cd /tmp && tar -czf farmOS-${FARMOS_VERSION}.tar.gz farmOS
      - name: Create GitHub release
        uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 #0.1.15
        with:
          body: |
            For full release notes, see [CHANGELOG.md](https://github.com/farmOS/farmOS/blob/${{ env.FARMOS_VERSION }}/CHANGELOG.md).
          files: /tmp/farmOS-${{ env.FARMOS_VERSION }}.tar.gz
          draft: false
          prerelease: false
  publish:
    name: Publish to Docker Hub
    # We only publish to Docker Hub if this is a tag or 3.x branch push event
    # to the official repository.
    if: github.repository == 'farmOS/farmOS' && github.event_name == 'push' && (github.ref_type == 'tag' || (github.ref_type == 'branch' && needs.build.outputs.farmos_version == '3.x'))
    runs-on: ubuntu-latest
    needs:
      - build
      - sniff
      - test
    steps:
      - name: Set FARMOS_VERSION from previous output.
        run: echo "FARMOS_VERSION=${{ needs.build.outputs.farmos_version }}" >> $GITHUB_ENV
      - name: Restore farmOS Docker image from cache
        uses: actions/cache@v3
        with:
          path: /tmp/farmos.tar
          key: farmos-${{ github.run_id }}
      - name: Load farmos/farmos:3.x image
        run: docker load < /tmp/farmos.tar
      - name: Restore farmOS dev Docker image from cache
        uses: actions/cache@v3
        with:
          path: /tmp/farmos-dev.tar
          key: farmos-dev-${{ github.run_id }}
      - name: Load farmos/farmos:3.x-dev image
        run: docker load < /tmp/farmos-dev.tar
      - name: Login to Docker Hub.
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}
      # If the 3.x branch was pushed...
      - name: Publish farmos/farmos:3.x image to Docker Hub.
        if: github.ref_type == 'branch' && env.FARMOS_VERSION == '3.x'
        run: docker push farmos/farmos:3.x
      - name: Publish farmos/farmos:3.x-dev image to Docker Hub.
        if: github.ref_type == 'branch' && env.FARMOS_VERSION == '3.x'
        run: docker push farmos/farmos:3.x-dev
      # If a tag was pushed, tag the Docker image and push to Docker Hub.
      # If the tag is a valid semantic versioning string, also tag "latest".
      # Semver regex from https://github.com/semver/semver/issues/199#issuecomment-43640395
      # If "latest" is tagged, we will also announce the release in a followup job.
      - name: Tag and publish farmos/farmos:{tag} image to Docker Hub.
        if: github.ref_type == 'tag'
        run: |
          docker tag farmos/farmos:3.x farmos/farmos:${{ env.FARMOS_VERSION }}
          docker push farmos/farmos:${{ env.FARMOS_VERSION }}
          if echo ${{ env.FARMOS_VERSION }} | grep -Pq '^(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$'; then
            docker tag farmos/farmos:3.x farmos/farmos:latest
            docker push farmos/farmos:latest
            echo "ANNOUNCE_RELEASE=1" >> $GITHUB_ENV
          else
            echo "ANNOUNCE_RELEASE=0" >> $GITHUB_ENV
          fi
    outputs:
      announce: ${{ env.ANNOUNCE_RELEASE }}
  announce-microblog:
    name: Announce new release on farmOS-microblog
    if: needs.publish.outputs.announce
    needs:
      - build
      - release
      - publish
    uses: farmOS/farmOS-microblog/.github/workflows/create-message.yml@main
    with:
      message: '#farmOS ${{ needs.build.outputs.farmos_version }} has been released! https://github.com/farmOS/farmOS/releases/${{ needs.build.outputs.farmos_version }}'
    secrets:
      MICROBLOG_DEPLOY_KEY: ${{ secrets.MICROBLOG_DEPLOY_KEY }}
  announce-discourse:
    name: Announce new release on farmOS.discourse.group
    if: needs.publish.outputs.announce
    runs-on: ubuntu-latest
    needs:
      - build
      - release
      - publish
    steps:
      - name: Discourse API request
        env:
          DISCOURSE_API_KEY: ${{ secrets.DISCOURSE_API_KEY }}
        run: |
          curl --fail-with-body -X POST "https://farmos.discourse.group/posts/" \
            -H "Content-Type: application/json" \
            -H "Api-Key: ${DISCOURSE_API_KEY}" \
            -H "Api-Username: mstenta" \
            -d '{
              "title": "farmOS ${{ needs.build.outputs.farmos_version }} has been released",
              "raw": "farmOS [${{ needs.build.outputs.farmos_version }}](https://github.com/farmOS/farmOS/releases/${{ needs.build.outputs.farmos_version }}) has been released.\n\nFor the full release notes, see [CHANGELOG.md](https://github.com/farmOS/farmOS/blob/${{ needs.build.outputs.farmos_version }}/CHANGELOG.md).",
              "category": 7
            }'
