160 lines
4.5 KiB
Bash
Executable File
160 lines
4.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
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}"
|
|
INITIAL_COMMENT="${INITIAL_COMMENT:-}"
|
|
|
|
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)
|
|
|
|
escape_regex() {
|
|
sed -e 's/[]\/$*.^|[]/\\&/g' <<< "$1"
|
|
}
|
|
|
|
notify_func() {
|
|
local msg="$1"
|
|
echo "::notice::$msg"
|
|
|
|
if [[ -x "$GITHUB_ACTION_PATH/notify" ]]; then
|
|
bash "$GITHUB_ACTION_PATH/notify" "$msg" || true
|
|
fi
|
|
|
|
if command -v apprise >/dev/null 2>&1; then
|
|
apprise -b "$msg" >/dev/null 2>&1 || true
|
|
fi
|
|
|
|
if [[ -n "${APPRISE_API_URL:-}" ]]; then
|
|
curl -s -X POST \
|
|
-H "Content-Type: application/json" \
|
|
-F "{\"body\":\"$msg\"}" \
|
|
-F "tags=all" \
|
|
"$APPRISE_API_URL" >/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}"
|
|
|
|
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 1b: Post initial comment if provided
|
|
# -----------------------------
|
|
if [[ -n "$INITIAL_COMMENT" ]]; then
|
|
comment_json=$(jq -n --arg body "$INITIAL_COMMENT" '{body:$body}')
|
|
curl -s -X POST \
|
|
-H "Authorization: token $TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
-d "$comment_json" \
|
|
"$API_URL/repos/$REPO_OWNER/$REPO_NAME/issues/$ISSUE/comments" >/dev/null
|
|
fi
|
|
|
|
# -----------------------------
|
|
# 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<count; i++)); do
|
|
user=$(echo "$comments" | jq -r ".[$i].user.login")
|
|
body=$(echo "$comments" | jq -r ".[$i].body" | tr '[:upper:]' '[:lower:]')
|
|
|
|
body_esc=$(escape_regex "$body")
|
|
|
|
for k in "${approved_kws[@]}"; do
|
|
k_esc=$(escape_regex "$k")
|
|
[[ "$body_esc" =~ ^${k_esc}[.!]?$ ]] && state["$user"]="approved"
|
|
done
|
|
|
|
for k in "${denied_kws[@]}"; do
|
|
k_esc=$(escape_regex "$k")
|
|
[[ "$body_esc" =~ ^${k_esc}[.!]?$ ]] && state["$user"]="denied"
|
|
done
|
|
done
|
|
|
|
# Denial check
|
|
for u in "${!state[@]}"; do
|
|
if [[ "${state[$u]}" == "denied" ]]; then
|
|
notify_func "Approval denied by $u on issue #$ISSUE"
|
|
echo "approved=false" >> "$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
|