diff --git a/.gitea/workflows/cd-dev.yaml b/.gitea/workflows/cd-dev.yaml
index c1ccf2cd6..525f1158b 100644
--- a/.gitea/workflows/cd-dev.yaml
+++ b/.gitea/workflows/cd-dev.yaml
@@ -43,10 +43,19 @@ jobs:
├ 📝 ${{ steps.commit.outputs.message }}
├ 🔖 ${{ steps.commit.outputs.short_sha }}
└ 🌿 dev branch"
- printf '%b' "$MSG" | curl -fS -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
- -d "chat_id=${{ env.TELEGRAM_ALERT_CHAT_ID }}" \
- -d "parse_mode=HTML" \
- --data-urlencode "text@-"
+ if AWOOI_CICD_STATUS=running \
+ AWOOI_CICD_STAGE=dev-deploy \
+ AWOOI_CICD_JOB_NAME="[DEV] 部署開始" \
+ AWOOI_CICD_COMMIT_SHA="${GITHUB_SHA}" \
+ AWOOI_CICD_SUMMARY="${{ steps.commit.outputs.message }}" \
+ scripts/ci/notify-awoooi-cicd.sh; then
+ echo "Dev deploy start notification mirrored through AWOOI API"
+ else
+ printf '%b' "$MSG" | curl -fS -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
+ -d "chat_id=${{ env.TELEGRAM_ALERT_CHAT_ID }}" \
+ -d "parse_mode=HTML" \
+ --data-urlencode "text@-"
+ fi
# API 測試 (同 prod CI,確保 dev 也通過)
- name: Run API Tests
@@ -184,10 +193,20 @@ jobs:
├ 🔖 ${{ steps.commit.outputs.short_sha }}
├ ⏱️ 耗時: ${MINUTES}m ${SECONDS}s
└ 🩺 http://192.168.0.125:32344/api/v1/health"
- printf '%b' "$MSG" | curl -fS -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
- -d "chat_id=${{ env.TELEGRAM_ALERT_CHAT_ID }}" \
- -d "parse_mode=HTML" \
- --data-urlencode "text@-"
+ if AWOOI_CICD_STATUS=success \
+ AWOOI_CICD_STAGE=dev-deploy \
+ AWOOI_CICD_JOB_NAME="[DEV] 部署完成" \
+ AWOOI_CICD_COMMIT_SHA="${GITHUB_SHA}" \
+ AWOOI_CICD_DURATION_SECONDS="${DURATION}" \
+ AWOOI_CICD_SUMMARY="${{ steps.commit.outputs.message }}" \
+ scripts/ci/notify-awoooi-cicd.sh; then
+ echo "Dev deploy success notification mirrored through AWOOI API"
+ else
+ printf '%b' "$MSG" | curl -fS -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
+ -d "chat_id=${{ env.TELEGRAM_ALERT_CHAT_ID }}" \
+ -d "parse_mode=HTML" \
+ --data-urlencode "text@-"
+ fi
- name: Notify Dev Deploy Failure
if: failure()
@@ -196,7 +215,16 @@ jobs:
├ 📝 ${{ steps.commit.outputs.message }}
├ 🔖 ${{ steps.commit.outputs.short_sha }}
└ 🔗 查看日誌"
- printf '%b' "$MSG" | curl -fS -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
- -d "chat_id=${{ env.TELEGRAM_ALERT_CHAT_ID }}" \
- -d "parse_mode=HTML" \
- --data-urlencode "text@-"
+ if AWOOI_CICD_STATUS=failed \
+ AWOOI_CICD_STAGE=dev-deploy \
+ AWOOI_CICD_JOB_NAME="[DEV] 部署失敗" \
+ AWOOI_CICD_COMMIT_SHA="${GITHUB_SHA}" \
+ AWOOI_CICD_SUMMARY="${{ steps.commit.outputs.message }}" \
+ scripts/ci/notify-awoooi-cicd.sh; then
+ echo "Dev deploy failure notification mirrored through AWOOI API"
+ else
+ printf '%b' "$MSG" | curl -fS -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
+ -d "chat_id=${{ env.TELEGRAM_ALERT_CHAT_ID }}" \
+ -d "parse_mode=HTML" \
+ --data-urlencode "text@-"
+ fi
diff --git a/.gitea/workflows/cd.yaml b/.gitea/workflows/cd.yaml
index 753799c57..14a5a35e4 100644
--- a/.gitea/workflows/cd.yaml
+++ b/.gitea/workflows/cd.yaml
@@ -92,10 +92,20 @@ jobs:
MSG=$(printf '🚀 AWOOOI 部署開始\n├ 📝 %s\n├ 🔖 %s\n└ 👤 %s' "${COMMIT_ESC}" "${SHORT_SHA}" "${ACTOR}")
# 2026-05-02 Claude Opus 4.7 + 統帥 ogt: notify 失敗不該擋整條 CI(鐵證:
# curl 400 從 5/1 起連續炸 14 個 commit 的 build-and-deploy)— 對齊 line 922 既有 pattern
- curl -fS -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
- -d "chat_id=${{ env.TELEGRAM_ALERT_CHAT_ID }}" \
- -d "parse_mode=HTML" \
- --data-urlencode "text=${MSG}" || echo "TG notify failed (non-fatal): exit=$?"
+ if AWOOI_CICD_STATUS=running \
+ AWOOI_CICD_STAGE=tests \
+ AWOOI_CICD_JOB_NAME="AWOOOI 部署開始" \
+ AWOOI_CICD_COMMIT_SHA="${GITHUB_SHA}" \
+ AWOOI_CICD_TRIGGERED_BY="${ACTOR}" \
+ AWOOI_CICD_SUMMARY="${COMMIT_MSG}" \
+ scripts/ci/notify-awoooi-cicd.sh; then
+ echo "✅ CI/CD start notification mirrored through AWOOI API"
+ else
+ curl -fS -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
+ -d "chat_id=${{ env.TELEGRAM_ALERT_CHAT_ID }}" \
+ -d "parse_mode=HTML" \
+ --data-urlencode "text=${MSG}" || echo "TG notify failed (non-fatal): exit=$?"
+ fi
@@ -255,10 +265,20 @@ jobs:
ACTOR="${{ github.actor }}"
COMMIT_ESC=$(echo "$COMMIT_MSG" | sed 's/&/\&/g; s/\</g; s/>/\>/g')
MSG=$(printf '❌ AWOOOI 部署失敗\n├ 📝 %s\n├ 🔖 %s\n├ 👤 %s\n├ 🧪 Stage: tests\n└ 🔗 http://192.168.0.110:3001/wooo/awoooi/actions' "${COMMIT_ESC}" "${SHORT_SHA}" "${ACTOR}")
- curl -fS -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
- -d "chat_id=${{ env.TELEGRAM_ALERT_CHAT_ID }}" \
- -d "parse_mode=HTML" \
- --data-urlencode "text=${MSG}" || echo "TG notify failed (non-fatal): exit=$?"
+ if AWOOI_CICD_STATUS=failed \
+ AWOOI_CICD_STAGE=tests \
+ AWOOI_CICD_JOB_NAME="AWOOOI 部署失敗" \
+ AWOOI_CICD_COMMIT_SHA="${GITHUB_SHA}" \
+ AWOOI_CICD_TRIGGERED_BY="${ACTOR}" \
+ AWOOI_CICD_SUMMARY="${COMMIT_MSG}" \
+ scripts/ci/notify-awoooi-cicd.sh; then
+ echo "✅ CI/CD tests failure notification mirrored through AWOOI API"
+ else
+ curl -fS -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
+ -d "chat_id=${{ env.TELEGRAM_ALERT_CHAT_ID }}" \
+ -d "parse_mode=HTML" \
+ --data-urlencode "text=${MSG}" || echo "TG notify failed (non-fatal): exit=$?"
+ fi
build-and-deploy:
# 2026-04-30 Codex: Docker builds run on the host runner. Long docker build
@@ -861,10 +881,20 @@ jobs:
ACTOR="${{ github.actor }}"
COMMIT_ESC=$(echo "$COMMIT_MSG" | sed 's/&/\&/g; s/\</g; s/>/\>/g')
MSG=$(printf '❌ AWOOOI 部署失敗\n├ 📝 %s\n├ 🔖 %s\n├ 👤 %s\n├ 🏗️ Stage: build-and-deploy\n└ 🔗 http://192.168.0.110:3001/wooo/awoooi/actions' "${COMMIT_ESC}" "${SHORT_SHA}" "${ACTOR}")
- curl -fS -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
- -d "chat_id=${{ env.TELEGRAM_ALERT_CHAT_ID }}" \
- -d "parse_mode=HTML" \
- --data-urlencode "text=${MSG}" || echo "TG notify failed (non-fatal): exit=$?"
+ if AWOOI_CICD_STATUS=failed \
+ AWOOI_CICD_STAGE=build-and-deploy \
+ AWOOI_CICD_JOB_NAME="AWOOOI 部署失敗" \
+ AWOOI_CICD_COMMIT_SHA="${GITHUB_SHA}" \
+ AWOOI_CICD_TRIGGERED_BY="${ACTOR}" \
+ AWOOI_CICD_SUMMARY="${COMMIT_MSG}" \
+ scripts/ci/notify-awoooi-cicd.sh; then
+ echo "✅ CI/CD build failure notification mirrored through AWOOI API"
+ else
+ curl -fS -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
+ -d "chat_id=${{ env.TELEGRAM_ALERT_CHAT_ID }}" \
+ -d "parse_mode=HTML" \
+ --data-urlencode "text=${MSG}" || echo "TG notify failed (non-fatal): exit=$?"
+ fi
post-deploy-checks:
needs: build-and-deploy
@@ -1024,9 +1054,19 @@ jobs:
COMMIT_MSG="${{ steps.commit.outputs.message }}"
SHORT_SHA="${{ steps.commit.outputs.short_sha }}"
TG_MSG="✅ AWOOOI 部署完成\n├ 📝 ${COMMIT_MSG}\n├ 🔖 ${SHORT_SHA}\n├ ⏱️ 耗時: ${MINUTES}m ${SECONDS}s\n├ 📦 API: ✅ Web: ✅\n├ 🩺 Health: ✅\n├ 🔗 Alert Chain: ${ALERT_CHAIN_RESULT}\n├ 📊 Monitoring: ${MONITORING_RESULT}\n└ 🎭 Smoke: ${SMOKE_RESULT}"
- printf '%b' "$TG_MSG" | curl -fS -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
- -d "chat_id=${{ env.TELEGRAM_ALERT_CHAT_ID }}" \
- --data-urlencode "text@-" || echo "TG notify warning (non-fatal)"
+ if AWOOI_CICD_STATUS=success \
+ AWOOI_CICD_STAGE=post-deploy \
+ AWOOI_CICD_JOB_NAME="AWOOOI 部署完成" \
+ AWOOI_CICD_COMMIT_SHA="${GITHUB_SHA}" \
+ AWOOI_CICD_DURATION_SECONDS="${DURATION}" \
+ AWOOI_CICD_SUMMARY="API=✅; Web=✅; AlertChain=${ALERT_CHAIN_RESULT}; Monitoring=${MONITORING_RESULT}; Smoke=${SMOKE_RESULT}" \
+ scripts/ci/notify-awoooi-cicd.sh; then
+ echo "✅ CI/CD success notification mirrored through AWOOI API"
+ else
+ printf '%b' "$TG_MSG" | curl -fS -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
+ -d "chat_id=${{ env.TELEGRAM_ALERT_CHAT_ID }}" \
+ --data-urlencode "text@-" || echo "TG notify warning (non-fatal)"
+ fi
- name: Notify Pipeline Failure
# 2026-04-16 ogt + Claude Sonnet 4.6: 改用 HTML 結構化格式
@@ -1037,7 +1077,17 @@ jobs:
ACTOR="${{ github.actor }}"
COMMIT_ESC=$(echo "$COMMIT_MSG" | sed 's/&/\&/g; s/\</g; s/>/\>/g')
MSG=$(printf '❌ AWOOOI 部署失敗\n├ 📝 %s\n├ 🔖 %s\n├ 👤 %s\n├ 🩺 Stage: post-deploy-checks\n└ 🔗 http://192.168.0.110:3001/wooo/awoooi/actions' "${COMMIT_ESC}" "${SHORT_SHA}" "${ACTOR}")
- curl -fS -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
- -d "chat_id=${{ env.TELEGRAM_ALERT_CHAT_ID }}" \
- -d "parse_mode=HTML" \
- --data-urlencode "text=${MSG}" || echo "TG notify failed (non-fatal): exit=$?"
+ if AWOOI_CICD_STATUS=failed \
+ AWOOI_CICD_STAGE=post-deploy-checks \
+ AWOOI_CICD_JOB_NAME="AWOOOI 部署失敗" \
+ AWOOI_CICD_COMMIT_SHA="${GITHUB_SHA}" \
+ AWOOI_CICD_TRIGGERED_BY="${ACTOR}" \
+ AWOOI_CICD_SUMMARY="${COMMIT_MSG}" \
+ scripts/ci/notify-awoooi-cicd.sh; then
+ echo "✅ CI/CD post-deploy failure notification mirrored through AWOOI API"
+ else
+ curl -fS -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
+ -d "chat_id=${{ env.TELEGRAM_ALERT_CHAT_ID }}" \
+ -d "parse_mode=HTML" \
+ --data-urlencode "text=${MSG}" || echo "TG notify failed (non-fatal): exit=$?"
+ fi
diff --git a/.gitea/workflows/code-review.yaml b/.gitea/workflows/code-review.yaml
index 54d090531..4ba1e2cad 100644
--- a/.gitea/workflows/code-review.yaml
+++ b/.gitea/workflows/code-review.yaml
@@ -110,18 +110,29 @@ jobs:
FILES_DISPLAY: ${{ steps.ctx.outputs.files_display }}
run: |
set -euo pipefail
- if [ -z "${TG_BOT_TOKEN:-}" ] || [ -z "${TG_CHAT_ID:-}" ]; then
- echo "Telegram secret missing; skip start notification"
- exit 0
- fi
html_escape() { sed 's/&/\&/g; s/\</g; s/>/\>/g'; }
COMMIT_ESC="$(printf '%s' "$COMMIT_MSG" | html_escape)"
FILES_ESC="$(printf '%s\n' "$FILES_DISPLAY" | html_escape)"
MSG="$(printf '🔍 Code Review 啟動\n──────────────────────\n📦 Commit %s 🌿 %s\n📝 %s\n📁 變更檔案:\n%s\n──────────────────────\n🤖 Hermes → OpenClaw → Elephant Alpha → NemoTron\n📊 即時進度:%s' "$SHORT_SHA" "$BRANCH" "$COMMIT_ESC" "$FILES_ESC" "$REPORT_URL" "$REPORT_URL")"
- curl -fsS -X POST "https://api.telegram.org/bot${TG_BOT_TOKEN}/sendMessage" \
- -H "Content-Type: application/json" \
- -d "$(jq -n --arg c "$TG_CHAT_ID" --arg t "$MSG" '{chat_id:$c,text:$t,parse_mode:"HTML",disable_web_page_preview:true}')" \
- >/dev/null
+ if AWOOI_CICD_STATUS=running \
+ AWOOI_CICD_STAGE=code-review \
+ AWOOI_CICD_JOB_NAME="Code Review 啟動" \
+ AWOOI_CICD_COMMIT_SHA="${GITHUB_SHA}" \
+ AWOOI_CICD_TRIGGERED_BY="${GITHUB_ACTOR:-CI}" \
+ AWOOI_CICD_SUMMARY="${COMMIT_MSG}" \
+ AWOOI_CICD_WORKFLOW_URL="${REPORT_URL}" \
+ scripts/ci/notify-awoooi-cicd.sh; then
+ echo "Code review start notification mirrored through AWOOI API"
+ else
+ if [ -z "${TG_BOT_TOKEN:-}" ] || [ -z "${TG_CHAT_ID:-}" ]; then
+ echo "Telegram secret missing and AWOOI API notify failed; skip start notification"
+ exit 0
+ fi
+ curl -fsS -X POST "https://api.telegram.org/bot${TG_BOT_TOKEN}/sendMessage" \
+ -H "Content-Type: application/json" \
+ -d "$(jq -n --arg c "$TG_CHAT_ID" --arg t "$MSG" '{chat_id:$c,text:$t,parse_mode:"HTML",disable_web_page_preview:true}')" \
+ >/dev/null
+ fi
- name: Run Deterministic Review
if: steps.stale.outputs.skip != 'true'
@@ -144,10 +155,6 @@ jobs:
SHORT_SHA: ${{ steps.ctx.outputs.short_sha }}
run: |
set -euo pipefail
- if [ -z "${TG_BOT_TOKEN:-}" ] || [ -z "${TG_CHAT_ID:-}" ]; then
- echo "Telegram secret missing; skip completion notification"
- exit 0
- fi
REPORT=/tmp/code-review-report.json
if [ ! -s "$REPORT" ]; then
cat > "$REPORT" <<'JSON'
@@ -180,7 +187,25 @@ jobs:
TOP_ESC="$(printf '%s' "$TOP_ISSUE" | html_escape)"
MSG="$(printf '%s Code Review 完成・%s\n──────────────────────\n🔴 CRITICAL %s 🟠 HIGH %s 🟡 MEDIUM %s 🟢 LOW %s\n──────────────────────\n⚠️ 主要問題\n%s\n\n🔍 整體風險等級\n%s:%s\n\n⚠️ 最高關注問題\n1. %s\n──────────────────────\n🤖 Elephant Alpha:%s ✅ %s\n📊 完整報告:%s' "$STATUS" "$SHORT_SHA" "$CRITICAL" "$HIGH" "$MEDIUM" "$LOW" "$ISSUE_LINE" "$RISK" "$SUMMARY_ESC" "$TOP_ESC" "$RISK" "$ACTION_ESC" "$REPORT_URL" "$REPORT_URL")"
- curl -fsS -X POST "https://api.telegram.org/bot${TG_BOT_TOKEN}/sendMessage" \
- -H "Content-Type: application/json" \
- -d "$(jq -n --arg c "$TG_CHAT_ID" --arg t "$MSG" '{chat_id:$c,text:$t,parse_mode:"HTML",disable_web_page_preview:true}')" \
- >/dev/null
+ CICD_STATUS=success
+ if [ "$RISK" = "MEDIUM" ]; then CICD_STATUS=pending; fi
+ if [ "$RISK" = "HIGH" ] || [ "$RISK" = "CRITICAL" ]; then CICD_STATUS=failed; fi
+ if AWOOI_CICD_STATUS="${CICD_STATUS}" \
+ AWOOI_CICD_STAGE=code-review \
+ AWOOI_CICD_JOB_NAME="Code Review 完成・${RISK}" \
+ AWOOI_CICD_COMMIT_SHA="${GITHUB_SHA}" \
+ AWOOI_CICD_TRIGGERED_BY="${GITHUB_ACTOR:-CI}" \
+ AWOOI_CICD_SUMMARY="CRITICAL=${CRITICAL}; HIGH=${HIGH}; MEDIUM=${MEDIUM}; LOW=${LOW}; ${SUMMARY}" \
+ AWOOI_CICD_WORKFLOW_URL="${REPORT_URL}" \
+ scripts/ci/notify-awoooi-cicd.sh; then
+ echo "Code review completion notification mirrored through AWOOI API"
+ else
+ if [ -z "${TG_BOT_TOKEN:-}" ] || [ -z "${TG_CHAT_ID:-}" ]; then
+ echo "Telegram secret missing and AWOOI API notify failed; skip completion notification"
+ exit 0
+ fi
+ curl -fsS -X POST "https://api.telegram.org/bot${TG_BOT_TOKEN}/sendMessage" \
+ -H "Content-Type: application/json" \
+ -d "$(jq -n --arg c "$TG_CHAT_ID" --arg t "$MSG" '{chat_id:$c,text:$t,parse_mode:"HTML",disable_web_page_preview:true}')" \
+ >/dev/null
+ fi
diff --git a/.gitea/workflows/deploy-alerts.yaml b/.gitea/workflows/deploy-alerts.yaml
index 30e9bfb79..11af3273d 100644
--- a/.gitea/workflows/deploy-alerts.yaml
+++ b/.gitea/workflows/deploy-alerts.yaml
@@ -50,6 +50,17 @@ jobs:
SHORT_SHA="${{ github.sha }}"
SHORT_SHA="${SHORT_SHA:0:7}"
MSG="${EMOJI} Prometheus 告警規則部署 ${STATUS} (${SHORT_SHA})"
- curl -fS -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
- -d "chat_id=${{ env.TELEGRAM_ALERT_CHAT_ID }}" \
- --data-urlencode "text=${MSG}" || true
+ CICD_STATUS="success"
+ [ "$STATUS" != "success" ] && CICD_STATUS="failed"
+ if AWOOI_CICD_STATUS="${CICD_STATUS}" \
+ AWOOI_CICD_STAGE=deploy-alerts \
+ AWOOI_CICD_JOB_NAME="Prometheus 告警規則部署" \
+ AWOOI_CICD_COMMIT_SHA="${{ github.sha }}" \
+ AWOOI_CICD_SUMMARY="${MSG}" \
+ scripts/ci/notify-awoooi-cicd.sh; then
+ echo "Alert rule deploy notification mirrored through AWOOI API"
+ else
+ curl -fS -X POST "https://api.telegram.org/bot${{ secrets.TELEGRAM_BOT_TOKEN }}/sendMessage" \
+ -d "chat_id=${{ env.TELEGRAM_ALERT_CHAT_ID }}" \
+ --data-urlencode "text=${MSG}" || true
+ fi
diff --git a/.gitea/workflows/e2e-health.yaml b/.gitea/workflows/e2e-health.yaml
index db05a5d03..c92859a34 100644
--- a/.gitea/workflows/e2e-health.yaml
+++ b/.gitea/workflows/e2e-health.yaml
@@ -54,7 +54,17 @@ jobs:
- name: Notify Telegram on Failure
if: failure()
run: |
- curl -s -X POST "https://api.telegram.org/bot${{ secrets.OPENCLAW_TG_BOT_TOKEN }}/sendMessage" \
- -d chat_id="${{ env.TELEGRAM_ALERT_CHAT_ID }}" \
- -d parse_mode="HTML" \
- -d text="🔴 [E2E Health Check] 失敗%0A%0A📅 $(TZ=Asia/Taipei date '+%Y-%m-%d %H:%M')%0A🔗 API 健康檢查未通過%0A%0A請檢查 K3s 叢集狀態"
+ MSG="E2E Health Check 失敗;API 健康檢查未通過"
+ if AWOOI_CICD_STATUS=failed \
+ AWOOI_CICD_STAGE=e2e-health \
+ AWOOI_CICD_JOB_NAME="E2E Health Check" \
+ AWOOI_CICD_COMMIT_SHA="${{ github.sha }}" \
+ AWOOI_CICD_SUMMARY="${MSG}" \
+ scripts/ci/notify-awoooi-cicd.sh; then
+ echo "E2E failure notification mirrored through AWOOI API"
+ else
+ curl -s -X POST "https://api.telegram.org/bot${{ secrets.OPENCLAW_TG_BOT_TOKEN }}/sendMessage" \
+ -d chat_id="${{ env.TELEGRAM_ALERT_CHAT_ID }}" \
+ -d parse_mode="HTML" \
+ -d text="🔴 [E2E Health Check] 失敗%0A%0A📅 $(TZ=Asia/Taipei date '+%Y-%m-%d %H:%M')%0A🔗 API 健康檢查未通過%0A%0A請檢查 K3s 叢集狀態"
+ fi
diff --git a/.gitea/workflows/run-migration.yml b/.gitea/workflows/run-migration.yml
index ba32b2ec3..38c9ba3f8 100644
--- a/.gitea/workflows/run-migration.yml
+++ b/.gitea/workflows/run-migration.yml
@@ -179,8 +179,19 @@ jobs:
TG_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
TG_CHAT: ${{ env.TELEGRAM_ALERT_CHAT_ID }}
run: |
+ STATUS="${{ job.status }}"
+ CICD_STATUS="success"
+ [ "$STATUS" != "success" ] && CICD_STATUS="failed"
+ if AWOOI_CICD_STATUS="${CICD_STATUS}" \
+ AWOOI_CICD_STAGE=run-migration \
+ AWOOI_CICD_JOB_NAME="Migration CI" \
+ AWOOI_CICD_COMMIT_SHA="${{ github.sha }}" \
+ AWOOI_CICD_SUMMARY="Migration CI: ${STATUS}" \
+ scripts/ci/notify-awoooi-cicd.sh; then
+ echo "Migration notification mirrored through AWOOI API"
+ exit 0
+ fi
if [ -n "$TG_TOKEN" ] && [ -n "$TG_CHAT" ]; then
- STATUS="${{ job.status }}"
MSG="🗄️ Migration CI: \`${STATUS}\` — commit ${{ github.sha }}"
curl -s -X POST "https://api.telegram.org/bot${TG_TOKEN}/sendMessage" \
-d chat_id="${TG_CHAT}" \
diff --git a/apps/api/src/api/v1/webhooks.py b/apps/api/src/api/v1/webhooks.py
index b38bbc2b0..20a846e42 100644
--- a/apps/api/src/api/v1/webhooks.py
+++ b/apps/api/src/api/v1/webhooks.py
@@ -1439,6 +1439,39 @@ class AlertmanagerPayload(BaseModel):
alerts: list[AlertmanagerAlert]
+_CICD_JOB_STATUSES = frozenset({"running", "success", "failed", "pending"})
+
+
+def _cicd_job_status_from_alert(alert: AlertmanagerAlert) -> str:
+ """將 CI/CD Alertmanager label 轉成 TelegramGateway 支援的狀態。
+
+ 2026-05-12 Codex: Gitea workflow 先送進 AWOOI API,不能只靠
+ severity=info 推 success,否則 failed/pending 事件進 AwoooP 後語義會失真。
+ """
+ labels = alert.labels or {}
+ for key in ("status", "job_status", "ci_status"):
+ value = str(labels.get(key) or "").strip().lower()
+ if value in _CICD_JOB_STATUSES:
+ return value
+
+ severity = str(labels.get("severity") or "").strip().lower()
+ if severity == "info":
+ return "success"
+ if severity in {"critical", "error"}:
+ return "failed"
+ return "running"
+
+
+def _cicd_duration_seconds_from_alert(alert: AlertmanagerAlert) -> int:
+ labels = alert.labels or {}
+ raw = labels.get("duration_seconds") or labels.get("duration") or 0
+ try:
+ value = int(str(raw).strip())
+ except (TypeError, ValueError):
+ return 0
+ return max(value, 0)
+
+
def is_internal_ip(client_ip: str) -> bool:
"""檢查是否為內網 IP"""
import ipaddress
@@ -2182,11 +2215,12 @@ async def alertmanager_webhook(
telegram = get_telegram_gateway()
# 解析 CI/CD 狀態
stage = alert.labels.get("stage", "")
- job_status = "success" if alert.labels.get("severity") == "info" else "running"
+ job_status = _cicd_job_status_from_alert(alert)
commit_sha = alert.labels.get("commit", "")
triggered_by = alert.labels.get("triggered_by", "CI")
workflow_url = alert.annotations.get("workflow_url", "")
summary = alert.annotations.get("summary", alertname)
+ detail_message = alert.annotations.get("description", "")
await telegram.send_cicd_progress(
job_name=summary,
@@ -2194,6 +2228,8 @@ async def alertmanager_webhook(
stage=stage,
commit_sha=commit_sha,
triggered_by=triggered_by,
+ duration_seconds=_cicd_duration_seconds_from_alert(alert),
+ message=detail_message,
workflow_url=workflow_url,
)
@@ -2425,7 +2461,7 @@ async def alertmanager_webhook(
record_alert_chain_success("alertmanager")
return AlertResponse(
success=True,
- message=f"✅ TYPE-1 純資訊告警已通知 (no LLM)",
+ message="✅ TYPE-1 純資訊告警已通知 (no LLM)",
alert_id=alert_id,
approval_created=False,
)
diff --git a/apps/api/src/services/telegram_gateway.py b/apps/api/src/services/telegram_gateway.py
index 0a1e619f7..1c64227d0 100644
--- a/apps/api/src/services/telegram_gateway.py
+++ b/apps/api/src/services/telegram_gateway.py
@@ -43,7 +43,6 @@ from src.services.security_interceptor import (
get_security_interceptor,
)
from src.services.chat_manager import get_chat_manager
-from src.services.notification_matrix import resolve_chat_ids # ADR-093 路由矩陣
# =============================================================================
# Snooze/Silence Redis Keys (2026-03-27 P1 優化)
@@ -996,12 +995,18 @@ class CICDProgressMessage:
safe_url = html.escape(self.workflow_url, quote=True)
workflow_link = f"\n🔗 Workflow"
+ detail = ""
+ if self.message:
+ safe_message = html.escape(self.message[:240])
+ detail = f"\n📝 {safe_message}"
+
# 簡潔訊息
stage_label = f" | {safe_stage}" if safe_stage else ""
message = (
f"{status_emoji} [AWOOOI CI/CD]{stage_label}\n"
f"📦 {safe_job}{duration_str}"
f"{commit_info}"
+ f"{detail}"
f"{workflow_link}"
)
@@ -6168,7 +6173,7 @@ class TelegramGateway:
s = await k8s.get_pod_status_summary(namespace="awoooi-prod")
running, total = s.get("running", 0), s.get("total", 0)
problems = s.get("problem_pods", [])
- lines = [f"🖥 Cluster 狀態", f"• Pods: {running}/{total} Running"]
+ lines = ["🖥 Cluster 狀態", f"• Pods: {running}/{total} Running"]
if problems:
lines.append(f"• 異常: {len(problems)} 個")
for p in problems[:5]:
diff --git a/apps/api/tests/test_cicd_alertmanager_mapping.py b/apps/api/tests/test_cicd_alertmanager_mapping.py
new file mode 100644
index 000000000..14a873930
--- /dev/null
+++ b/apps/api/tests/test_cicd_alertmanager_mapping.py
@@ -0,0 +1,62 @@
+from __future__ import annotations
+
+from src.api.v1.webhooks import (
+ AlertmanagerAlert,
+ _cicd_duration_seconds_from_alert,
+ _cicd_job_status_from_alert,
+)
+from src.services.telegram_gateway import CICDProgressMessage
+
+
+def test_cicd_alert_status_label_overrides_severity() -> None:
+ alert = AlertmanagerAlert(
+ status="firing",
+ labels={"status": "failed", "severity": "info"},
+ annotations={},
+ )
+
+ assert _cicd_job_status_from_alert(alert) == "failed"
+
+
+def test_cicd_alert_legacy_info_severity_remains_success() -> None:
+ alert = AlertmanagerAlert(
+ status="firing",
+ labels={"severity": "info"},
+ annotations={},
+ )
+
+ assert _cicd_job_status_from_alert(alert) == "success"
+
+
+def test_cicd_alert_duration_is_sanitized() -> None:
+ assert _cicd_duration_seconds_from_alert(
+ AlertmanagerAlert(
+ status="firing",
+ labels={"duration_seconds": "91"},
+ annotations={},
+ )
+ ) == 91
+ assert _cicd_duration_seconds_from_alert(
+ AlertmanagerAlert(
+ status="firing",
+ labels={"duration_seconds": "-5"},
+ annotations={},
+ )
+ ) == 0
+
+
+def test_cicd_progress_message_includes_safe_detail() -> None:
+ message = CICDProgressMessage(
+ job_name="Code Review",
+ status="failed",
+ stage="review",
+ commit_sha="abcdef1234567890",
+ message="最高風險