name: Artifact from Cirrus

description: Waits for a specific Cirrus CI run to complete, then downloads the artifact and uploads it to the current workflow. This will silently succeed if Cirrus CI did not schedule a task within 2 minutes.

inputs:
  download:
    description: Name of Artifact to download from Cirrus CI
    required: true
  task:
    description: Name of Cirrus Task
    required: true
  token:
    description: GitHub Token
    required: true
  upload:
    description: Name of Artifact to upload on GitHub Actions
    required: true

runs:
  using: composite
  steps:
    - shell: bash
      run: echo "GH_TOKEN=${{ inputs.token }}" >> "$GITHUB_ENV"
    - name: Wait for Check Suite to be created
      id: check-suite
      env:
        # GITHUB_SHA does weird things for pull request, so we roll our own:
        COMMIT: ${{ github.event.pull_request.head.sha || github.sha }}
      shell: bash
      run: |
        get_check_runs_url() {
          gh api "repos/{owner}/{repo}/commits/${COMMIT}/check-suites" \
            | jq -r '.check_suites[] | select(.app.slug == "cirrus-ci") | .check_runs_url'
        }
        for _ in $(seq 1 12); do
          check_runs_url="$(get_check_runs_url)"
          if [ -z "$check_runs_url" ]; then
            echo "Cirrus CI task has not started, yet. Waiting..."
            sleep 10
          else
            echo "check_runs_url=$check_runs_url" >> "$GITHUB_OUTPUT"
            exit 0
          fi
        done
        >&2 echo "Cirrus CI check suite not found. Is Cirrus CI enabled for this repo?"
    - name: Find task by name
      id: find-task
      if: steps.check-suite.outputs.check_runs_url
      shell: bash
      run: |
        get_number_of_tasks() {
          gh api "${{ steps.check-suite.outputs.check_runs_url }}" \
            | jq -r '.check_runs | map(select(.name == "${{ inputs.task }}")) | length'
        }
        tasks="$(get_number_of_tasks)"
        case "$tasks" in
          0)
            echo "Task not found, assuming it's skipped intentionally..."
            exit 0
            ;;
          1)
            echo "task_found=1" >> "$GITHUB_OUTPUT"
            exit 0
            ;;
          *)
            >&2 echo "More than 1 task with the same name found. Don't know what to do..."
            exit 1
            ;;
        esac
    - name: Wait for Cirrus CI to complete task
      if: steps.find-task.outputs.task_found
      shell: bash
      run: |
        get_conclusion() {
          gh api "${{ steps.check-suite.outputs.check_runs_url }}" \
            | jq -r '.check_runs[] | select(.name == "${{ inputs.task }}" and .status == "completed") | .conclusion'
        }
        while true; do
          conclusion="$(get_conclusion)"
          if [ -z "$conclusion" ]; then
            echo "Cirrus CI task has not completed, yet. Waiting..."
            sleep 30
          else
            if [ "$conclusion" == "success" ]; then
              break
            else
              exit 1
            fi
          fi
        done
    - name: Download artifact from Cirrus CI
      if: steps.find-task.outputs.task_found
      id: download
      shell: bash
      run: |
        get_external_id() {
          gh api "${{ steps.check-suite.outputs.check_runs_url }}" \
            | jq -er '.check_runs[] | select(.name == "${{ inputs.task }}") | .external_id'
        }
        archive="$(mktemp)"
        artifacts="$(mktemp -d)"
        until curl --no-progress-meter --fail -o "${archive}" \
                "https://api.cirrus-ci.com/v1/artifact/task/$(get_external_id)/${{ inputs.download }}.zip"
        do
          # This happens when a tag is pushed on the same commit. In this case the
          # job is immediately marked as "completed" for us, so we end up here after a few
          # seconds - but the actual Cirrus CI task is still running and didn't produce its artifact, yet.
          echo "Artifact not found on Cirrus CI, yet. Waiting..."
          sleep 30
        done
        unzip "${archive}" -d "${artifacts}"
        echo "artifacts=${artifacts}" >> "$GITHUB_OUTPUT"
    - name: Save artifact to GitHub Actions
      if: steps.find-task.outputs.task_found
      uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
      with:
        name: ${{ inputs.upload }}
        path: ${{ steps.download.outputs.artifacts }}
        if-no-files-found: error
