161 lines
4.9 KiB
Bash
Executable File
161 lines
4.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# 2026-05-12 Codex: CI/CD 通知先走 AWOOI Alertmanager 入口,讓 TelegramGateway
|
||
# 鏡像出站訊息到 AwoooP;呼叫端保留直接 Telegram fallback。
|
||
set -euo pipefail
|
||
|
||
API_BASE="${AWOOOI_API_URL:-http://192.168.0.125:32334}"
|
||
ALERTMANAGER_URL="${AWOOOI_ALERTMANAGER_URL:-${API_BASE%/}/api/v1/webhooks/alertmanager}"
|
||
|
||
JOB_NAME="${AWOOI_CICD_JOB_NAME:-${AWOOOI_CICD_JOB_NAME:-CI/CD Pipeline}}"
|
||
STATUS_RAW="${AWOOI_CICD_STATUS:-${AWOOOI_CICD_STATUS:-running}}"
|
||
STAGE="${AWOOI_CICD_STAGE:-${AWOOOI_CICD_STAGE:-ci}}"
|
||
COMMIT_SHA="${AWOOI_CICD_COMMIT_SHA:-${AWOOOI_CICD_COMMIT_SHA:-${GITHUB_SHA:-}}}"
|
||
TRIGGERED_BY="${AWOOI_CICD_TRIGGERED_BY:-${AWOOOI_CICD_TRIGGERED_BY:-${GITHUB_ACTOR:-CI}}}"
|
||
WORKFLOW_URL="${AWOOI_CICD_WORKFLOW_URL:-${AWOOOI_CICD_WORKFLOW_URL:-http://192.168.0.110:3001/wooo/awoooi/actions}}"
|
||
DURATION_SECONDS="${AWOOI_CICD_DURATION_SECONDS:-${AWOOOI_CICD_DURATION_SECONDS:-0}}"
|
||
SUMMARY="${AWOOI_CICD_SUMMARY:-${AWOOOI_CICD_SUMMARY:-}}"
|
||
|
||
payload_file="$(mktemp)"
|
||
trap 'rm -f "$payload_file"' EXIT
|
||
|
||
export JOB_NAME
|
||
export STATUS_RAW
|
||
export STAGE
|
||
export COMMIT_SHA
|
||
export TRIGGERED_BY
|
||
export WORKFLOW_URL
|
||
export DURATION_SECONDS
|
||
export SUMMARY
|
||
|
||
build_payload_with_python() {
|
||
python3 - <<'PY'
|
||
from __future__ import annotations
|
||
|
||
import datetime as dt
|
||
import json
|
||
import os
|
||
import re
|
||
|
||
status = (os.environ.get("STATUS_RAW") or "running").strip().lower()
|
||
if status not in {"running", "success", "failed", "pending"}:
|
||
status = "running"
|
||
|
||
severity = "info"
|
||
if status == "failed":
|
||
severity = "critical"
|
||
elif status == "pending":
|
||
severity = "warning"
|
||
|
||
stage = (os.environ.get("STAGE") or "ci").strip()[:40]
|
||
safe_stage = re.sub(r"[^A-Za-z0-9_]+", "_", stage).strip("_") or "ci"
|
||
alertname = f"CI_{safe_stage}_{status}"
|
||
|
||
payload = {
|
||
"version": "4",
|
||
"status": "firing",
|
||
"receiver": "awoooi-cicd",
|
||
"groupLabels": {"alertname": alertname},
|
||
"commonLabels": {"alertname": alertname, "severity": severity},
|
||
"commonAnnotations": {},
|
||
"externalURL": os.environ.get("WORKFLOW_URL", ""),
|
||
"alerts": [
|
||
{
|
||
"status": "firing",
|
||
"labels": {
|
||
"alertname": alertname,
|
||
"severity": severity,
|
||
"stage": stage,
|
||
"status": status,
|
||
"commit": os.environ.get("COMMIT_SHA", ""),
|
||
"triggered_by": os.environ.get("TRIGGERED_BY", "CI"),
|
||
"duration_seconds": os.environ.get("DURATION_SECONDS", "0"),
|
||
},
|
||
"annotations": {
|
||
"summary": os.environ.get("JOB_NAME", "CI/CD Pipeline"),
|
||
"description": os.environ.get("SUMMARY", ""),
|
||
"workflow_url": os.environ.get("WORKFLOW_URL", ""),
|
||
},
|
||
"startsAt": dt.datetime.now(dt.timezone.utc).isoformat().replace("+00:00", "Z"),
|
||
}
|
||
],
|
||
}
|
||
print(json.dumps(payload, ensure_ascii=False))
|
||
PY
|
||
}
|
||
|
||
build_payload_with_node() {
|
||
node <<'JS'
|
||
const statusCandidates = new Set(["running", "success", "failed", "pending"]);
|
||
let status = (process.env.STATUS_RAW || "running").trim().toLowerCase();
|
||
if (!statusCandidates.has(status)) {
|
||
status = "running";
|
||
}
|
||
|
||
let severity = "info";
|
||
if (status === "failed") {
|
||
severity = "critical";
|
||
} else if (status === "pending") {
|
||
severity = "warning";
|
||
}
|
||
|
||
const stage = (process.env.STAGE || "ci").trim().slice(0, 40);
|
||
const safeStage = stage.replace(/[^A-Za-z0-9_]+/g, "_").replace(/^_+|_+$/g, "") || "ci";
|
||
const alertname = `CI_${safeStage}_${status}`;
|
||
|
||
const payload = {
|
||
version: "4",
|
||
status: "firing",
|
||
receiver: "awoooi-cicd",
|
||
groupLabels: { alertname },
|
||
commonLabels: { alertname, severity },
|
||
commonAnnotations: {},
|
||
externalURL: process.env.WORKFLOW_URL || "",
|
||
alerts: [
|
||
{
|
||
status: "firing",
|
||
labels: {
|
||
alertname,
|
||
severity,
|
||
stage,
|
||
status,
|
||
commit: process.env.COMMIT_SHA || "",
|
||
triggered_by: process.env.TRIGGERED_BY || "CI",
|
||
duration_seconds: process.env.DURATION_SECONDS || "0",
|
||
},
|
||
annotations: {
|
||
summary: process.env.JOB_NAME || "CI/CD Pipeline",
|
||
description: process.env.SUMMARY || "",
|
||
workflow_url: process.env.WORKFLOW_URL || "",
|
||
},
|
||
startsAt: new Date().toISOString(),
|
||
},
|
||
],
|
||
};
|
||
|
||
process.stdout.write(JSON.stringify(payload));
|
||
JS
|
||
}
|
||
|
||
if command -v python3 >/dev/null 2>&1; then
|
||
build_payload_with_python > "$payload_file"
|
||
elif command -v node >/dev/null 2>&1; then
|
||
build_payload_with_node > "$payload_file"
|
||
else
|
||
echo "python3 and node missing; cannot build Alertmanager JSON payload" >&2
|
||
exit 2
|
||
fi
|
||
|
||
if [ "${AWOOI_CICD_DRY_RUN:-${AWOOOI_CICD_DRY_RUN:-0}}" = "1" ]; then
|
||
cat "$payload_file"
|
||
exit 0
|
||
fi
|
||
|
||
curl -fsS \
|
||
--connect-timeout "${AWOOI_CICD_CONNECT_TIMEOUT:-5}" \
|
||
--max-time "${AWOOI_CICD_MAX_TIME:-12}" \
|
||
-H "Content-Type: application/json" \
|
||
--data-binary "@${payload_file}" \
|
||
"$ALERTMANAGER_URL" >/dev/null
|
||
|
||
echo "AwoooP-mirrored CI/CD notification sent via ${ALERTMANAGER_URL}"
|