|
| 1 | +#!/usr/bin/env bash |
| 2 | +# Polls the GitHub REST API until the `changes` job of the CI workflow run |
| 3 | +# matching $HEAD_SHA has finished. Exits 0 on success (and writes the run id |
| 4 | +# to $GITHUB_OUTPUT as `run_id`), non-zero on failure or timeout. |
| 5 | +# |
| 6 | +# Expected environment variables: |
| 7 | +# GH_TOKEN - token used by `gh api`. |
| 8 | +# REPO - "owner/name" of the repository. |
| 9 | +# HEAD_SHA - PR head SHA to match CI runs against. |
| 10 | +# GITHUB_OUTPUT - standard GitHub Actions output file. |
| 11 | +# MAX_ATTEMPTS - (optional, default 40) number of polling attempts. |
| 12 | +# INTERVAL_SECONDS - (optional, default 15) seconds between attempts. |
| 13 | +# CI_WORKFLOW_NAME - (optional, default "CI") name of the workflow to wait for. |
| 14 | +# CI_JOB_NAME - (optional, default "changes") name of the job to wait for. |
| 15 | +set -euo pipefail |
| 16 | + |
| 17 | +: "${GH_TOKEN:?GH_TOKEN is required}" |
| 18 | +: "${REPO:?REPO is required}" |
| 19 | +: "${HEAD_SHA:?HEAD_SHA is required}" |
| 20 | +: "${GITHUB_OUTPUT:?GITHUB_OUTPUT is required}" |
| 21 | + |
| 22 | +max_attempts="${MAX_ATTEMPTS:-40}" |
| 23 | +interval="${INTERVAL_SECONDS:-15}" |
| 24 | +workflow_name="${CI_WORKFLOW_NAME:-CI}" |
| 25 | +job_name="${CI_JOB_NAME:-changes}" |
| 26 | + |
| 27 | +attempt=0 |
| 28 | +while [ "$attempt" -lt "$max_attempts" ]; do |
| 29 | + attempt=$((attempt + 1)) |
| 30 | + |
| 31 | + # Find the most recent matching workflow run for this head SHA. The run |
| 32 | + # may not be discoverable for a few seconds after the PR event dispatches. |
| 33 | + run_id=$(gh api \ |
| 34 | + "repos/$REPO/actions/runs?event=pull_request&head_sha=$HEAD_SHA&per_page=30" \ |
| 35 | + --jq "[.workflow_runs[] | select(.name==\"$workflow_name\")] | sort_by(.run_number) | last | .id // empty") |
| 36 | + |
| 37 | + if [ -z "$run_id" ]; then |
| 38 | + echo "$workflow_name run not yet discoverable for $HEAD_SHA (attempt $attempt/$max_attempts)" |
| 39 | + sleep "$interval" |
| 40 | + continue |
| 41 | + fi |
| 42 | + |
| 43 | + job=$(gh api "repos/$REPO/actions/runs/$run_id/jobs" \ |
| 44 | + --jq ".jobs[] | select(.name==\"$job_name\") | {status: .status, conclusion: .conclusion}") |
| 45 | + status=$(echo "$job" | jq -r '.status // empty') |
| 46 | + conclusion=$(echo "$job" | jq -r '.conclusion // empty') |
| 47 | + |
| 48 | + if [ "$status" = "completed" ]; then |
| 49 | + if [ "$conclusion" = "success" ]; then |
| 50 | + echo "$job_name job completed in $workflow_name run $run_id" |
| 51 | + echo "run_id=$run_id" >> "$GITHUB_OUTPUT" |
| 52 | + exit 0 |
| 53 | + fi |
| 54 | + echo "::error::$job_name job in $workflow_name run $run_id finished with conclusion=$conclusion" |
| 55 | + exit 1 |
| 56 | + fi |
| 57 | + |
| 58 | + echo "$workflow_name run $run_id, $job_name job status=$status (attempt $attempt/$max_attempts)" |
| 59 | + sleep "$interval" |
| 60 | +done |
| 61 | + |
| 62 | +echo "::error::Timed out after $max_attempts attempts waiting for the $job_name job in $workflow_name" |
| 63 | +exit 1 |
0 commit comments