name: Upload Petri Test Results

on:
  workflow_run:
    types:
      - completed
    workflows:
      - 'OpenVMM CI'
      - 'OpenVMM PR'
      - '\[Optional\] OpenVMM Release PR'

permissions:
  id-token: write
  contents: read
  actions: read  # Needed to download artifacts from other runs
  pull-requests: write # Needed to create PR comments

jobs:
  upload-logs:
    # Only attempt upload if the workflow run could have actually produced some artifacts
    if: >-
      ${{
        github.event.workflow_run.conclusion == 'success' ||
        github.event.workflow_run.conclusion == 'failure' ||
        github.event.workflow_run.conclusion == 'timed_out'
      }}
    runs-on: ubuntu-latest
    env:
      BASE_URL: https://openvmmghtestresults.blob.core.windows.net

    steps:
      - name: Free Disk Space (Ubuntu)
        uses: jlumbroso/free-disk-space@v1.3.1
        with:
          # Azure CLI is considered a large package
          large-packages: false

      - name: Authenticate to Azure
        uses: azure/login@v1
        with:
          client-id: ${{ secrets.OPENVMM_TEST_RESULT_CLIENT_ID }}
          tenant-id: ${{ secrets.OPENVMM_TENANT_ID }}
          subscription-id: ${{ secrets.OPENVMM_SUBSCRIPTION_ID }}

      - name: Download matching artifacts
        run: |
          if [ "$event" = "pull_request" ]; then
            # Get the associated PR number. Work around GitHub Actions often not
            # populating the `pull_requests` array. See
            # <https://github.com/orgs/community/discussions/25220>.
            pr=$(gh pr view --repo "${repo}" "${head_branch}" --json number --jq .number)
            echo "SOURCE_PR=$pr" >> "$GITHUB_ENV"
          else
            echo "SOURCE_PR=" >> "$GITHUB_ENV"
          fi
          mkdir -p results
          gh run \
            -R "$repo" \
            download "$run_id" \
            -p "*-vmm-tests-logs" \
            -D results
          test_count=$(find results -name petri.jsonl | wc -l)
          PASS_COUNT=$(find results -name petri.passed | wc -l)
          FAIL_UNSTABLE_COUNT=$(find results -name petri.failed_unstable | wc -l)
          FAIL_COUNT=$((test_count - PASS_COUNT))
          echo "FAIL_COUNT=$FAIL_COUNT" >> "$GITHUB_ENV"
          echo "PASS_COUNT=$PASS_COUNT" >> "$GITHUB_ENV"
          echo "FAIL_UNSTABLE_COUNT=$FAIL_UNSTABLE_COUNT" >> "$GITHUB_ENV"
          echo "SOURCE_BRANCH=$head_branch" >> "$GITHUB_ENV"
        env:
          event: ${{ github.event.workflow_run.event }}
          repo: ${{ github.event.workflow_run.repository.full_name }}
          run_id: ${{ github.event.workflow_run.id }}
          # If the PR is from a fork, prefix it with `<owner-login>:`.
          head_branch: |-
            ${{
              (github.event.workflow_run.head_repository.owner.login != github.event.workflow_run.repository.owner.login)
                && format('{0}:{1}', github.event.workflow_run.head_repository.owner.login, github.event.workflow_run.head_branch)
                || github.event.workflow_run.head_branch
            }}
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Upload to Azure Blob Storage
        run: |
          az storage blob upload-batch \
            --destination "$BASE_URL/results" \
            --destination-path "$run_id" \
            --source results \
            --auth-mode login

          # Store a metadata blob in a separate portion of the hierarchy so that
          # it can be cheaply queried.
          az storage blob upload \
            --blob-url "$BASE_URL/results/runs/$run_id" \
            --file /dev/null \
            --metadata \
              "petrifailed=$FAIL_COUNT" \
              "petripassed=$PASS_COUNT" \
              "ghbranch=$SOURCE_BRANCH" \
              "ghpr=$SOURCE_PR" \
            --auth-mode login
        env:
          run_id: ${{ github.event.workflow_run.id }}

      - name: Upload to memory validation results to Azure Blob Storage
        run: |
          # ignore this job for builds that we don't run memory validation on to keep
          # storage account clean
          if ! find results -path '*/*memory_validation*/petri.jsonl' | grep -q .; then
            exit 0
          fi

          az storage blob upload-batch \
            --destination "$BASE_URL/memoryvalidation" \
            --destination-path "$run_id" \
            --source results \
            --pattern '*/*memory_validation*/petri.jsonl' \
            --auth-mode login

          # Store the PR number in the storage account as well to associate memory validation
          # results with their code changes. We want to include PR events as well to collect
          # results from architectures that can't have their meminfo queried for release builds
          az storage blob upload \
            --blob-url "$BASE_URL/memoryvalidation/runs/$run_id" \
            --file /dev/null \
            --metadata \
              "ghpr=$SOURCE_PR" \
              "ghbranch=$SOURCE_BRANCH" \
            --auth-mode login
        env:
          run_id: ${{ github.event.workflow_run.id }}

      - name: Report failing tests
        if: ${{ env.SOURCE_PR && env.FAIL_COUNT > 0 }}
        run: |
          gh pr comment \
            -R "$repo" \
            "$SOURCE_PR" \
            -F - <<EOF
          [$FAIL_COUNT Petri tests failed ($FAIL_UNSTABLE_COUNT unstable)](https://openvmm.dev/test-results/#/runs/$run_id)
          EOF
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          repo: ${{ github.event.workflow_run.repository.full_name }}
          run_id: ${{ github.event.workflow_run.id }}
