#!/usr/bin/env bash set -euo pipefail # ----------------------------- # Inputs # ----------------------------- TOKEN="${TOKEN:?Missing TOKEN}" API_URL="${API_URL:?Missing API_URL}" REPO_OWNER="${REPO_OWNER:?Missing REPO_OWNER}" REPO_NAME="${REPO_NAME:?Missing REPO_NAME}" APPROVERS="${APPROVERS:?Missing APPROVERS}" APPROVAL_KEYWORDS="${APPROVAL_KEYWORDS:?Missing APPROVAL_KEYWORDS}" DENIAL_KEYWORDS="${DENIAL_KEYWORDS:?Missing DENIAL_KEYWORDS}" POLL_INTERVAL="${POLL_INTERVAL:-10}" REMINDER_INTERVAL="${REMINDER_INTERVAL:-0}" IFS=',' read -r -a approver_list <<< "$APPROVERS" IFS=',' read -r -a approved_kws <<< "$APPROVAL_KEYWORDS" IFS=',' read -r -a denied_kws <<< "$DENIAL_KEYWORDS" last_reminder=$(date +%s) # ----------------------------- # Helper: Escape regex special chars # ----------------------------- escape_regex() { sed -e 's/[]\/$*.^|[]/\\&/g' <<< "$1" } # ----------------------------- # Helper: Notifications # ----------------------------- notify_func() { local msg="$1" echo "::notice::$msg" # Local notify script if [[ -x "$GITHUB_ACTION_PATH/notify" ]]; then bash "$GITHUB_ACTION_PATH/notify" "$msg" || true fi # Apprise CLI if command -v apprise >/dev/null 2>&1; then apprise -b "$msg" >/dev/null 2>&1 || true fi # Apprise API if [[ -n "${APPRISE_API_URL:-}" ]]; then curl -s -X POST \ -H "Content-Type: application/json" \ -d "{\"body\":\"$msg\"}" \ "$APPRISE_API_URL/notify" >/dev/null 2>&1 || true fi } # ----------------------------- # Step 1: Create the issue # ----------------------------- title="Manual approval required" body="Workflow requires manual approval. Approvers: ${APPROVERS} Reply with: • ${APPROVAL_KEYWORDS} • ${DENIAL_KEYWORDS}" # Build assignees JSON assignees_json=$(jq -nc --arg csv "$APPROVERS" '$csv|split(",")') json=$(jq -n \ --arg title "$title" \ --arg body "$body" \ --argjson assignees "$assignees_json" \ '{title:$title, body:$body, assignees:$assignees}') resp=$(curl -s -X POST \ -H "Authorization: token $TOKEN" \ -H "Content-Type: application/json" \ -d "$json" \ "$API_URL/repos/$REPO_OWNER/$REPO_NAME/issues") ISSUE=$(echo "$resp" | jq -r '.number // .index // .id') if [[ -z "$ISSUE" || "$ISSUE" == "null" ]]; then echo "❌ Failed to create issue: $resp" exit 1 fi notify_func "Approval required on issue #$ISSUE" # ----------------------------- # Step 2: Poll for comments # ----------------------------- while true; do comments=$(curl -s \ -H "Authorization: token $TOKEN" \ "$API_URL/repos/$REPO_OWNER/$REPO_NAME/issues/$ISSUE/comments") declare -A state for u in "${approver_list[@]}"; do state["$u"]="pending" done count=$(echo "$comments" | jq 'length') for ((i=0; i> "$GITHUB_OUTPUT" exit 1 fi done # Full approval check all_ok=true for u in "${!state[@]}"; do [[ "${state[$u]}" != "approved" ]] && all_ok=false done if $all_ok; then notify_func "All approvers approved issue #$ISSUE" echo "approved=true" >> "$GITHUB_OUTPUT" exit 0 fi # Send reminders if (( REMINDER_INTERVAL > 0 )); then now=$(date +%s) if (( now - last_reminder >= REMINDER_INTERVAL )); then pending="" for u in "${!state[@]}"; do [[ "${state[$u]}" == "pending" ]] && pending+="$u " done notify_func "Reminder: approval pending for [${pending}] on issue #$ISSUE" last_reminder=$now fi fi sleep "$POLL_INTERVAL" done