mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 02:54:26 +00:00
The wait job picks the newest privileged run on the head commit of the PR. Two fork PRs that share a head repository, branch and commit but target different bases each start such a run, and the commit alone cannot tell them apart. A failed run of the other PR would then end the wait of this one while its images are still being built. The run name of the workflow carries the PR number, so the match now requires it next to the head commit. A run without that name matches nothing, and the wait does what it does whenever no run is found: it keeps polling for the images until they exist or the wait times out. Raised by the CodeRabbit review of this PR. Refs #784 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
180 lines
5.4 KiB
Bash
Executable File
180 lines
5.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
: "${GITHUB_REPOSITORY:?Missing GITHUB_REPOSITORY}"
|
|
: "${GH_TOKEN:?Missing GH_TOKEN}"
|
|
: "${WORKFLOW_FILE:?Missing WORKFLOW_FILE}"
|
|
: "${TARGET_EVENT:?Missing TARGET_EVENT}"
|
|
: "${PR_NUMBER:?Missing PR_NUMBER}"
|
|
: "${IMAGE_TAG:?Missing IMAGE_TAG}"
|
|
: "${INFINITO_DISTROS:?Missing INFINITO_DISTROS}"
|
|
: "${WAIT_ATTEMPTS:?Missing WAIT_ATTEMPTS}"
|
|
: "${WAIT_SLEEP_SECONDS:?Missing WAIT_SLEEP_SECONDS}"
|
|
|
|
for bin in docker gh jq; do
|
|
if ! command -v "${bin}" >/dev/null 2>&1; then
|
|
echo "ERROR: ${bin} not found." >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
repo_owner="$(scripts/meta/resolve/repository/owner.sh)"
|
|
repo_name="$(scripts/meta/resolve/repository/name.sh)"
|
|
pr_head_sha="${PR_HEAD_SHA:-}"
|
|
pr_updated_at="${PR_UPDATED_AT:-}"
|
|
pr_updated_at_floor=""
|
|
last_run_state=""
|
|
last_run_id=""
|
|
lookup_failure_limit=30
|
|
lookup_failures=0
|
|
|
|
if [[ -n "${pr_updated_at}" ]]; then
|
|
if ! pr_updated_at_floor="$(date -u -d "${pr_updated_at} - 300 seconds" '+%Y-%m-%dT%H:%M:%SZ' 2>/dev/null)"; then
|
|
echo "WARN: Could not relax PR_UPDATED_AT=${pr_updated_at}; using the raw timestamp." >&2
|
|
pr_updated_at_floor="${pr_updated_at}"
|
|
fi
|
|
fi
|
|
|
|
read -r -a distros_raw <<<"${INFINITO_DISTROS}"
|
|
distros=()
|
|
for distro in "${distros_raw[@]}"; do
|
|
[[ -n "${distro}" ]] || continue
|
|
distros+=("${distro}")
|
|
done
|
|
|
|
if [[ "${#distros[@]}" -eq 0 ]]; then
|
|
echo "ERROR: INFINITO_DISTROS did not contain any entries." >&2
|
|
exit 1
|
|
fi
|
|
|
|
build_image_refs() {
|
|
local distro
|
|
|
|
for distro in "${distros[@]}"; do
|
|
printf 'ghcr.io/%s/%s/%s:%s\n' "${repo_owner}" "${repo_name}" "${distro}" "${IMAGE_TAG}"
|
|
done
|
|
}
|
|
|
|
mapfile -t image_refs < <(build_image_refs)
|
|
|
|
find_matching_run() {
|
|
local query="event=${TARGET_EVENT}&per_page=100"
|
|
|
|
if [[ -n "${pr_head_sha}" ]]; then
|
|
query+="&head_sha=${pr_head_sha}"
|
|
fi
|
|
|
|
gh api --paginate \
|
|
-H "Accept: application/vnd.github+json" \
|
|
"/repos/${GITHUB_REPOSITORY}/actions/workflows/${WORKFLOW_FILE}/runs?${query}" |
|
|
jq -sc \
|
|
--argjson pr_number "${PR_NUMBER}" \
|
|
--arg pr_head_sha "${pr_head_sha}" \
|
|
--arg pr_updated_at_floor "${pr_updated_at_floor}" '
|
|
[
|
|
.[]
|
|
| .workflow_runs[]?
|
|
| select(
|
|
if $pr_head_sha != "" then
|
|
(.head_sha // "") == $pr_head_sha
|
|
and ((.display_title // "") | test("PR #\($pr_number)( |$)"))
|
|
else
|
|
any(.pull_requests[]?; (.number // -1) == $pr_number)
|
|
end
|
|
)
|
|
| select($pr_updated_at_floor == "" or .created_at >= $pr_updated_at_floor)
|
|
]
|
|
| sort_by(.created_at)
|
|
| last // empty
|
|
'
|
|
}
|
|
|
|
find_missing_images() {
|
|
local ref
|
|
|
|
missing_images=()
|
|
for ref in "${image_refs[@]}"; do
|
|
if ! docker manifest inspect "${ref}" >/dev/null 2>&1; then
|
|
missing_images+=("${ref}")
|
|
fi
|
|
done
|
|
}
|
|
|
|
echo "Waiting for fork CI images for PR #${PR_NUMBER}"
|
|
echo "Expecting ${#image_refs[@]} image refs with tag ${IMAGE_TAG}"
|
|
if [[ -n "${pr_head_sha}" ]]; then
|
|
echo "Matching privileged runs on PR head.sha=${pr_head_sha}"
|
|
fi
|
|
if [[ -n "${pr_updated_at_floor}" ]]; then
|
|
echo "Ignoring privileged runs created before ${pr_updated_at_floor} (relaxed from PR_UPDATED_AT=${pr_updated_at})"
|
|
fi
|
|
|
|
for attempt in $(seq 1 "${WAIT_ATTEMPTS}"); do
|
|
find_missing_images
|
|
|
|
if [[ "${#missing_images[@]}" -eq 0 ]]; then
|
|
echo "All required CI images are available."
|
|
exit 0
|
|
fi
|
|
|
|
if run_json="$(find_matching_run)"; then
|
|
lookup_failures=0
|
|
else
|
|
lookup_failures=$((lookup_failures + 1))
|
|
if [[ "${lookup_failures}" -ge "${lookup_failure_limit}" ]]; then
|
|
echo "The lookup of the privileged workflow run failed ${lookup_failures} times in a row." >&2
|
|
exit 1
|
|
fi
|
|
echo "[${attempt}/${WAIT_ATTEMPTS}] Lookup of the privileged workflow run failed (${lookup_failures}/${lookup_failure_limit}). Waiting ${WAIT_SLEEP_SECONDS}s..."
|
|
sleep "${WAIT_SLEEP_SECONDS}"
|
|
continue
|
|
fi
|
|
|
|
if [[ -n "${run_json}" && "${run_json}" != "null" ]]; then
|
|
run_id="$(jq -r '.id' <<<"${run_json}")"
|
|
status="$(jq -r '.status' <<<"${run_json}")"
|
|
conclusion="$(jq -r '.conclusion // ""' <<<"${run_json}")"
|
|
url="$(jq -r '.html_url // ""' <<<"${run_json}")"
|
|
created_at="$(jq -r '.created_at // ""' <<<"${run_json}")"
|
|
state="${run_id}:${status}:${conclusion}"
|
|
|
|
if [[ "${state}" != "${last_run_state}" || "${run_id}" != "${last_run_id}" ]]; then
|
|
echo "[${attempt}/${WAIT_ATTEMPTS}] Privileged run ${run_id} created_at=${created_at} status=${status} conclusion=${conclusion:-<pending>}"
|
|
if [[ -n "${url}" ]]; then
|
|
echo "Run URL: ${url}"
|
|
fi
|
|
last_run_state="${state}"
|
|
last_run_id="${run_id}"
|
|
fi
|
|
|
|
if [[ "${status}" == "completed" && "${conclusion}" != "success" ]]; then
|
|
echo "Privileged run ${run_id} finished with conclusion=${conclusion} before all CI images became available." >&2
|
|
if [[ -n "${url}" ]]; then
|
|
echo "Run URL: ${url}" >&2
|
|
fi
|
|
echo "Still missing (first 10):" >&2
|
|
for ref in "${missing_images[@]:0:10}"; do
|
|
echo " - ${ref}" >&2
|
|
done
|
|
exit 1
|
|
fi
|
|
else
|
|
if [[ "${last_run_state}" != "missing" ]]; then
|
|
echo "[${attempt}/${WAIT_ATTEMPTS}] No matching privileged workflow run found yet."
|
|
last_run_state="missing"
|
|
last_run_id=""
|
|
fi
|
|
fi
|
|
|
|
echo "[${attempt}/${WAIT_ATTEMPTS}] Missing ${#missing_images[@]}/${#image_refs[@]} CI images. Waiting ${WAIT_SLEEP_SECONDS}s..."
|
|
echo "Example missing: ${missing_images[0]}"
|
|
sleep "${WAIT_SLEEP_SECONDS}"
|
|
done
|
|
|
|
echo "Timed out waiting for CI images tagged ${IMAGE_TAG}." >&2
|
|
echo "Still missing (first 10):" >&2
|
|
for ref in "${missing_images[@]:0:10}"; do
|
|
echo " - ${ref}" >&2
|
|
done
|
|
exit 1
|