diff --git a/automation/ci-issue-reporter.sh b/automation/ci-issue-reporter.sh new file mode 100644 index 0000000..65c47ba --- /dev/null +++ b/automation/ci-issue-reporter.sh @@ -0,0 +1,237 @@ +#!/usr/bin/env bash +# ============================================================================ +# Copyright (C) 2026 Moko Consulting +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +# FILE INFORMATION +# DEFGROUP: Automation.CI +# INGROUP: moko-platform.Automation +# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform +# PATH: /automation/ci-issue-reporter.sh +# VERSION: 09.23.00 +# BRIEF: Creates or updates a Gitea issue when a CI gate fails. +# Deduplicates by searching open issues with the "ci-auto" label +# whose title matches the gate. If a matching issue exists, a comment +# is appended instead of opening a duplicate. +# ============================================================================ + +set -euo pipefail + +# ── Defaults ──────────────────────────────────────────────────────────────── +GITEA_URL="${GITEA_URL:-https://git.mokoconsulting.tech}" +GITEA_TOKEN="${GITEA_TOKEN:-}" +REPO="${GITHUB_REPOSITORY:-}" +RUN_URL="${GITHUB_SERVER_URL:-${GITEA_URL}}/${REPO}/actions/runs/${GITHUB_RUN_ID:-0}" +LABEL_NAME="ci-auto" +LABEL_COLOR="#e11d48" + +GATE="" +DETAILS="" +SEVERITY="error" +WORKFLOW="" + +# ── Parse arguments ───────────────────────────────────────────────────────── +usage() { + cat </dev/null || echo "000") + + if [[ "$exists" == "200" ]]; then + # Check if label already exists + local found + found=$(curl -sf \ + -H "Authorization: token ${GITEA_TOKEN}" \ + "${API}/labels" 2>/dev/null \ + | grep -o "\"name\":\"${LABEL_NAME}\"" || true) + + if [[ -z "$found" ]]; then + curl -sf -X POST \ + -H "Authorization: token ${GITEA_TOKEN}" \ + -H "Content-Type: application/json" \ + "${API}/labels" \ + -d "{\"name\":\"${LABEL_NAME}\",\"color\":\"${LABEL_COLOR}\",\"description\":\"Auto-created by CI issue reporter\"}" \ + > /dev/null 2>&1 || true + fi + fi +} + +# ── Search for existing open issue ────────────────────────────────────────── +find_existing_issue() { + # URL-encode the gate name for the query + local query + query=$(printf '%s' "[CI] ${GATE}" | sed 's/ /%20/g; s/\[/%5B/g; s/\]/%5D/g') + + local response + response=$(curl -sf \ + -H "Authorization: token ${GITEA_TOKEN}" \ + "${API}/issues?type=issues&state=open&labels=${LABEL_NAME}&q=${query}&limit=5" \ + 2>/dev/null || echo "[]") + + # Extract the first matching issue number + echo "$response" \ + | grep -oP '"number":\s*\K[0-9]+' \ + | head -1 +} + +# ── Build issue body ──────────────────────────────────────────────────────── +build_body() { + local severity_badge + if [[ "$SEVERITY" == "error" ]]; then + severity_badge="**Severity:** Error" + else + severity_badge="**Severity:** Warning" + fi + + cat </dev/null) + + HTTP=$(curl -sf -o /dev/null -w '%{http_code}' -X POST \ + -H "Authorization: token ${GITEA_TOKEN}" \ + -H "Content-Type: application/json" \ + "${API}/issues/${EXISTING}/comments" \ + -d "${COMMENT_JSON}" 2>/dev/null || echo "000") + + if [[ "$HTTP" == "201" ]]; then + echo "Commented on existing issue #${EXISTING}" + else + echo "WARNING: Failed to comment on issue #${EXISTING} (HTTP ${HTTP})" + fi +else + # Create new issue + ISSUE_BODY=$(build_body) + ISSUE_JSON=$(python3 -c " +import sys, json +body = sys.stdin.read() +print(json.dumps({ + 'title': sys.argv[1], + 'body': body, + 'labels': [] +}))" "$TITLE" <<< "$ISSUE_BODY" 2>/dev/null) + + # Create the issue + RESPONSE=$(curl -sf -X POST \ + -H "Authorization: token ${GITEA_TOKEN}" \ + -H "Content-Type: application/json" \ + "${API}/issues" \ + -d "${ISSUE_JSON}" 2>/dev/null || echo "{}") + + ISSUE_NUM=$(echo "$RESPONSE" | grep -oP '"number":\s*\K[0-9]+' | head -1) + + if [[ -n "$ISSUE_NUM" ]]; then + # Apply label (separate call — more reliable across Gitea versions) + LABEL_ID=$(curl -sf \ + -H "Authorization: token ${GITEA_TOKEN}" \ + "${API}/labels" 2>/dev/null \ + | grep -oP "\"id\":\s*\K[0-9]+(?=[^}]*\"name\":\s*\"${LABEL_NAME}\")" \ + | head -1 || true) + + if [[ -n "$LABEL_ID" ]]; then + curl -sf -X POST \ + -H "Authorization: token ${GITEA_TOKEN}" \ + -H "Content-Type: application/json" \ + "${API}/issues/${ISSUE_NUM}/labels" \ + -d "{\"labels\":[${LABEL_ID}]}" \ + > /dev/null 2>&1 || true + fi + + echo "Created issue #${ISSUE_NUM}: ${TITLE}" + else + echo "WARNING: Failed to create issue" + echo "Response: ${RESPONSE}" + fi +fi