#!/usr/bin/env bash set -euo pipefail # Query Gitea Actions for an exact SHA via curl. Output STATUS= keys. # Auth: GITEA_TOKEN in the environment or .env.builder. GITEA_API_URL optional. sha= while (($#)); do case "$1" in --sha) sha=$2; shift 2 ;; *) echo "Usage: ci-status.sh --sha SHA" >&2; exit 2 ;; esac done [[ -n "$sha" ]] || { echo "Usage: ci-status.sh --sha SHA" >&2; exit 2; } if ! command -v curl >/dev/null 2>&1; then echo "Error: curl is required" >&2 exit 3 fi root=${BUILDER_PROJECT_ROOT:-$(git rev-parse --show-toplevel)} origin=$(git -C "$root" remote get-url origin 2>/dev/null || true) [[ -n "$origin" ]] || { echo "Error: no origin remote" >&2; exit 3; } read -r host path base <<<"$(WAIT_CI_ORIGIN=$origin python3 -c ' import os, re url = os.environ["WAIT_CI_ORIGIN"].strip() url = re.sub(r"^git@([^:]+):", r"ssh://git@\1/", url) url = re.sub(r"^ssh://git@([^/]+):", r"ssh://git@\1/", url) if re.match(r"^https?://", url): m = re.match(r"^(https?)://([^/]+)(?:/(.*))?$", url) scheme, host, path = (m.group(1), m.group(2), m.group(3) or "") if m else ("", "", "") else: m = re.match(r"^ssh://git@([^/]+)/(.+)$", url) scheme, host, path = ("https", m.group(1), m.group(2)) if m else ("", "", "") path = re.sub(r"\.git$", "", path) print(host, path, (scheme or "https") + "://" + host) ')" api=${GITEA_API_URL:-$base/api/v1} api=${api%/} [[ "$api" == */api/v1 ]] || api+=/api/v1 repo=$(WAIT_CI_PATH=$path python3 -c 'import os,urllib.parse; print(urllib.parse.quote(os.environ["WAIT_CI_PATH"], safe="/"))') gitea_curl() { if [[ -n "${GITEA_TOKEN:-}" ]]; then curl -fsS --retry 2 --connect-timeout 10 --max-time 30 \ --config <(printf 'header = "Authorization: token %s"\n' "$GITEA_TOKEN") "$1" else curl -fsS --retry 2 --connect-timeout 10 --max-time 30 "$1" fi } set +e runs=$(gitea_curl "${api}/repos/${repo}/actions/runs?head_sha=${sha}&limit=20&page=1") rc=$? set -e if [[ $rc -ne 0 ]]; then echo "Error: curl cannot read Gitea Actions for $path" >&2 exit 3 fi python3 -c ' import json, sys sha = sys.argv[1] try: data = json.loads(sys.stdin.read()) except json.JSONDecodeError: print("STATUS=error") raise SystemExit(0) if isinstance(data, dict) and data.get("message") and not (data.get("workflow_runs") or data.get("runs")): print("STATUS=error") raise SystemExit(0) runs = data.get("workflow_runs", data.get("runs", [])) if isinstance(data, dict) else data if not isinstance(runs, list) or not runs: print("STATUS=empty") raise SystemExit(0) matched = [] for run in runs: head = run.get("head_sha") or run.get("headSha") or (run.get("head_commit") or {}).get("id") if head and head.lower() == sha.lower() and (run.get("id") or run.get("database_id")): matched.append(run) if not matched: print("STATUS=empty") raise SystemExit(0) run = matched[0] run_id = run.get("id") or run.get("database_id") url = run.get("html_url") or run.get("web_url") or run.get("url") or "-" status = str(run.get("status") or "").lower() conclusion = str(run.get("conclusion") or run.get("result") or "").lower() if conclusion in ("failure", "failed", "cancelled", "canceled", "skipped", "timed_out"): state = "failed" elif status in ("completed", "success") and conclusion in ("", "success", "succeeded"): state = "success" elif status == "completed" and conclusion: state = "failed" else: state = "running" print("STATUS=%s ID=%s PIPELINE=%s" % (state, run_id, url)) ' "$sha" <<<"$runs"