All checks were successful
CICD Start / Sanity and Base Decision (pull_request) Successful in 1m2s
Signed-off-by: copilotcoder <copilotcoder@darkhelm.org>
248 lines
5.5 KiB
Bash
Executable File
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
|