Files
plex-playlist/scripts/dispatch-workflow.sh
copilotcoder 509b402edf
All checks were successful
CICD Start / Sanity and Base Decision (pull_request) Successful in 1m2s
More CICD fixes again again again again again again again again again again again again again again again again again
Signed-off-by: copilotcoder <copilotcoder@darkhelm.org>
2026-06-06 19:34:38 -04:00

248 lines
5.5 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
TOKEN=""
REPO_FULL=""
WORKFLOW_FILE=""
TARGET_REF=""
HEAD_SHA=""
SOURCE_WORKFLOW=""
BASE_NEEDED=""
BASE_HASH=""
TRACE_ID=""
MAX_ROUNDS=3
declare -a API_BASES=()
declare -a EXTRA_INPUTS=()
usage() {
cat <<'EOF'
Usage:
dispatch-workflow.sh \
--token <token> \
--repo <owner/repo> \
--workflow <workflow-file> \
--ref <branch-or-ref> \
--head-sha <sha> \
--source-workflow <name> \
[--api-base <base-url>]... \
[--base-needed <true|false>] \
[--base-hash <hash>] \
[--trace-id <id>] \
[--input <key=value>]... \
[--max-rounds <n>]
EOF
}
ensure_curl() {
if command -v curl >/dev/null 2>&1; then
return 0
fi
if command -v apt-get >/dev/null 2>&1; then
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq curl ca-certificates
fi
if command -v curl >/dev/null 2>&1; then
return 0
fi
echo "curl is required for dispatch and could not be installed" >&2
return 1
}
build_default_api_bases() {
if [[ -n "${GITHUB_SERVER_URL:-}" ]]; then
API_BASES+=("${GITHUB_SERVER_URL%/}/api/v1")
fi
if [[ -n "${GITEA_SERVER_URL:-}" ]]; then
API_BASES+=("${GITEA_SERVER_URL%/}/api/v1")
fi
if [[ -n "${GITEA_SSH_HOST:-}" ]]; then
API_BASES+=("http://${GITEA_SSH_HOST}:3001/api/v1")
fi
if [[ -n "${GITEA_REGISTRY_HOST:-}" ]]; then
API_BASES+=("http://${GITEA_REGISTRY_HOST}:3001/api/v1")
fi
if [[ -n "${GITEA_REGISTRY_IP:-}" ]]; then
API_BASES+=("http://${GITEA_REGISTRY_IP}:3001/api/v1")
fi
if [[ ${#API_BASES[@]} -eq 0 ]]; then
API_BASES+=("http://kankali.darkhelm.lan:3001/api/v1")
fi
}
append_input_field() {
local key="$1"
local value="$2"
[[ -z "$value" ]] && return 0
PAYLOAD_INPUTS+=$',\n'
PAYLOAD_INPUTS+=" \"${key}\": \"${value}\""
}
while [[ $# -gt 0 ]]; do
case "$1" in
--api-base)
API_BASES+=("$2")
shift 2
;;
--token)
TOKEN="$2"
shift 2
;;
--repo)
REPO_FULL="$2"
shift 2
;;
--workflow)
WORKFLOW_FILE="$2"
shift 2
;;
--ref)
TARGET_REF="$2"
shift 2
;;
--head-sha)
HEAD_SHA="$2"
shift 2
;;
--source-workflow)
SOURCE_WORKFLOW="$2"
shift 2
;;
--base-needed)
BASE_NEEDED="$2"
shift 2
;;
--base-hash)
BASE_HASH="$2"
shift 2
;;
--trace-id)
TRACE_ID="$2"
shift 2
;;
--input)
EXTRA_INPUTS+=("$2")
shift 2
;;
--max-rounds)
MAX_ROUNDS="$2"
shift 2
;;
--help|-h)
usage
exit 0
;;
*)
echo "Unknown argument: $1" >&2
usage
exit 2
;;
esac
done
if [[ -z "$TOKEN" || -z "$REPO_FULL" || -z "$WORKFLOW_FILE" || -z "$TARGET_REF" || -z "$HEAD_SHA" || -z "$SOURCE_WORKFLOW" ]]; then
echo "Missing required arguments" >&2
usage
exit 2
fi
if ! [[ "$MAX_ROUNDS" =~ ^[0-9]+$ ]] || [[ "$MAX_ROUNDS" -lt 1 ]]; then
echo "--max-rounds must be a positive integer" >&2
exit 2
fi
REPO_OWNER="${REPO_FULL%/*}"
REPO_NAME="${REPO_FULL#*/}"
if [[ ${#API_BASES[@]} -eq 0 ]]; then
build_default_api_bases
fi
if [[ -z "$TRACE_ID" ]]; then
TRACE_ID="dispatch-${GITHUB_RUN_ID:-local}-${GITHUB_RUN_ATTEMPT:-1}-${HEAD_SHA:0:8}"
fi
PAYLOAD_INPUTS=$' "head_sha": "'"${HEAD_SHA}"$'",\n "source_workflow": "'"${SOURCE_WORKFLOW}"$'"'
append_input_field "trace_id" "$TRACE_ID"
append_input_field "base_needed" "$BASE_NEEDED"
append_input_field "base_hash" "$BASE_HASH"
for kv in "${EXTRA_INPUTS[@]}"; do
if [[ "$kv" != *=* ]]; then
echo "Invalid --input value: ${kv} (expected key=value)" >&2
exit 2
fi
key="${kv%%=*}"
value="${kv#*=}"
append_input_field "$key" "$value"
done
PAYLOAD=$(cat <<EOF
{
"ref": "${TARGET_REF}",
"inputs": {
${PAYLOAD_INPUTS}
}
}
EOF
)
ensure_curl
echo "Dispatching ${WORKFLOW_FILE} for ${HEAD_SHA} on ${TARGET_REF}"
echo "trace_id=${TRACE_ID}"
dispatched=false
attempt=0
total_attempts=$((MAX_ROUNDS * ${#API_BASES[@]}))
for round in $(seq 1 "${MAX_ROUNDS}"); do
for api_base in "${API_BASES[@]}"; do
attempt=$((attempt + 1))
dispatch_url="${api_base}/repos/${REPO_OWNER}/${REPO_NAME}/actions/workflows/${WORKFLOW_FILE}/dispatches"
response_file="/tmp/dispatch-response.txt"
: > "${response_file}"
echo "dispatch_attempt=${attempt}/${total_attempts}"
echo "dispatch_round=${round}/${MAX_ROUNDS}"
echo "dispatch_api_base=${api_base}"
dispatch_start_ms=$(date +%s%3N)
http_code=$(curl -sS --connect-timeout 5 --max-time 20 -o "${response_file}" -w "%{http_code}" -X POST \
-H "Authorization: token ${TOKEN}" \
-H "Content-Type: application/json" \
"${dispatch_url}" \
-d "${PAYLOAD}" || true)
dispatch_end_ms=$(date +%s%3N)
dispatch_elapsed_ms=$((dispatch_end_ms - dispatch_start_ms))
echo "dispatch_elapsed_ms=${dispatch_elapsed_ms}"
echo "dispatch_http_code=${http_code}"
if [[ "${http_code}" =~ ^[0-9]+$ ]] && [[ "${http_code}" -ge 200 ]] && [[ "${http_code}" -lt 300 ]]; then
echo "Dispatch succeeded"
dispatched=true
break 2
fi
echo "Dispatch attempt failed via ${api_base} (HTTP ${http_code})"
if [[ -s "${response_file}" ]]; then
echo "Response body:"
cat "${response_file}" || true
fi
done
sleep_seconds=$((2 ** round))
echo "Dispatch round ${round} failed; backing off for ${sleep_seconds}s"
sleep "${sleep_seconds}"
done
if [[ "${dispatched}" != "true" ]]; then
echo "Dispatch failed on all endpoints after ${MAX_ROUNDS} rounds" >&2
exit 1
fi