1addcffeba
CI/CD Pipeline / Check if frontend-only change (push) Has been skipped
CI/CD Pipeline / PR Build Web Image (push) Has been skipped
CI/CD Pipeline / PR Build API Image (push) Has been skipped
CI/CD Pipeline / PR Build Worker Image (push) Has been skipped
CI/CD Pipeline / Frontend Lint (push) Successful in 1m14s
CI/CD Pipeline / Validate - Type Check (mypy) (push) Successful in 1m16s
CI/CD Pipeline / Build Production Worker Image (push) Successful in 1m54s
CI/CD Pipeline / Validate - Migration (alembic) (push) Successful in 1m57s
CI/CD Pipeline / Frontend Unit Tests (push) Failing after 1m56s
CI/CD Pipeline / Build Staging Worker Image (push) Successful in 2m8s
CI/CD Pipeline / Build Production Web Image (push) Successful in 2m50s
CI/CD Pipeline / Build Staging Web Image (push) Successful in 2m53s
CI/CD Pipeline / Validate - Code Quality (push) Successful in 5m2s
CI/CD Pipeline / Unit Tests (push) Successful in 5m2s
CI/CD Pipeline / Integration Tests (push) Successful in 2m30s
CI/CD Pipeline / CI Gate (push) Has been skipped
CI/CD Pipeline / Build Production API Image (push) Successful in 14m30s
CI/CD Pipeline / Deploy Production (push) Has been skipped
CI/CD Pipeline / Production Browser E2E (push) Has been skipped
CI/CD Pipeline / Build Staging API Image (push) Successful in 14m37s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (push) Failing after 4m13s
CI/CD Pipeline / Staging E2E Tests (push) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (push) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (push) Has been skipped
CI/CD Pipeline / Canary Release to Production (push) Has been skipped
- Bug4(P0): 新增scripts/ci_staging_deploy.sh - Staging环境部署脚本(pull镜像+migration+重启) - Bug4(P0): 新增scripts/ci_staging_healthcheck.sh - Staging健康检查脚本(API/Worker/Web) - Bug5(P1): 新增scripts/ci_notify.py - 飞书通知脚本(start/success/failure模式)
136 lines
4.1 KiB
Python
Executable File
136 lines
4.1 KiB
Python
Executable File
#!/usr/bin/env python3
|
||
"""
|
||
CI 通知脚本 - 发送飞书消息通知
|
||
用法:
|
||
NOTIFY_MODE=start JOB_NAME="Build API" python3 scripts/ci_notify.py
|
||
NOTIFY_MODE=success JOB_NAME="Deploy Staging" python3 scripts/ci_notify.py
|
||
NOTIFY_MODE=failure JOB_NAME="Unit Tests" python3 scripts/ci_notify.py
|
||
|
||
环境变量:
|
||
NOTIFY_MODE - 通知类型: start/success/failure
|
||
JOB_NAME - Job名称
|
||
CI_NOTIFY_WEBHOOK - 飞书Webhook地址
|
||
GITHUB_SHA - Commit SHA (可选)
|
||
GITHUB_REF_NAME - 分支名 (可选)
|
||
GITHUB_RUN_ID - Run ID (可选)
|
||
GITHUB_REPOSITORY - 仓库名 (可选)
|
||
GITHUB_SERVER_URL - Gitea地址 (可选)
|
||
|
||
设计原则:
|
||
通知失败永远不阻断主流程(永远返回0)
|
||
"""
|
||
|
||
import json
|
||
import os
|
||
import sys
|
||
import urllib.request
|
||
from datetime import datetime
|
||
|
||
|
||
def get_env(name, default=""):
|
||
return os.environ.get(name, default)
|
||
|
||
|
||
def send_feishu_notify(webhook_url, title, content, color="blue"):
|
||
"""发送飞书通知(简单卡片格式)"""
|
||
if not webhook_url:
|
||
print("[INFO] 未配置 CI_NOTIFY_WEBHOOK,跳过通知")
|
||
return True
|
||
|
||
# 状态颜色映射
|
||
color_map = {
|
||
"green": "green",
|
||
"red": "red",
|
||
"blue": "blue",
|
||
"yellow": "yellow",
|
||
}
|
||
header_color = color_map.get(color, "blue")
|
||
|
||
# 构造卡片
|
||
card = {
|
||
"config": {"wide_screen_mode": True},
|
||
"header": {
|
||
"title": {"tag": "plain_text", "content": title},
|
||
"template": header_color,
|
||
},
|
||
"elements": [
|
||
{"tag": "div", "text": {"tag": "lark_md", "content": content}},
|
||
],
|
||
}
|
||
|
||
payload = {"msg_type": "interactive", "card": card}
|
||
data = json.dumps(payload).encode("utf-8")
|
||
|
||
req = urllib.request.Request(
|
||
webhook_url,
|
||
data=data,
|
||
headers={"Content-Type": "application/json"},
|
||
method="POST",
|
||
)
|
||
try:
|
||
with urllib.request.urlopen(req, timeout=10) as resp:
|
||
resp_body = resp.read().decode("utf-8")
|
||
result = json.loads(resp_body)
|
||
if result.get("code", 0) != 0:
|
||
print(f"[WARN] 飞书通知返回错误: {result.get('msg', resp_body)}", file=sys.stderr)
|
||
return False
|
||
print("[INFO] 飞书通知发送成功")
|
||
return True
|
||
except Exception as e:
|
||
print(f"[WARN] 飞书通知发送失败: {e}", file=sys.stderr)
|
||
return False
|
||
|
||
|
||
def build_message():
|
||
"""根据环境变量构造通知消息"""
|
||
notify_mode = get_env("NOTIFY_MODE", "info").lower()
|
||
job_name = get_env("JOB_NAME", "未知Job")
|
||
branch = get_env("GITHUB_REF_NAME", "未知分支")
|
||
sha = get_env("GITHUB_SHA", "")[:8]
|
||
run_id = get_env("GITHUB_RUN_ID", "")
|
||
repo = get_env("GITHUB_REPOSITORY", "")
|
||
server_url = get_env("GITHUB_SERVER_URL", "https://git.xiaoxiajianji.com")
|
||
|
||
# 状态映射
|
||
status_map = {
|
||
"start": ("🔔 CI 任务开始", "blue", "开始执行"),
|
||
"success": ("✅ CI 任务成功", "green", "执行成功"),
|
||
"failure": ("❌ CI 任务失败", "red", "执行失败"),
|
||
"info": ("ℹ️ CI 通知", "blue", "通知"),
|
||
}
|
||
title, color, status_text = status_map.get(notify_mode, status_map["info"])
|
||
|
||
# 构造内容
|
||
content_lines = [
|
||
f"**任务**: {job_name}",
|
||
f"**状态**: {status_text}",
|
||
f"**分支**: {branch}",
|
||
]
|
||
if sha:
|
||
content_lines.append(f"**Commit**: `{sha}`")
|
||
if run_id and repo and server_url:
|
||
run_url = f"{server_url}/{repo}/actions/runs/{run_id}"
|
||
content_lines.append(f"**详情**: [点击查看]({run_url})")
|
||
|
||
content_lines.append(f"**时间**: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||
|
||
content = "\n".join(content_lines)
|
||
return title, content, color
|
||
|
||
|
||
def main():
|
||
webhook = get_env("CI_NOTIFY_WEBHOOK", "")
|
||
title, content, color = build_message()
|
||
|
||
print(f"[CI Notify] 模式: {get_env('NOTIFY_MODE')}")
|
||
print(f"[CI Notify] 任务: {get_env('JOB_NAME')}")
|
||
|
||
send_feishu_notify(webhook, title, content, color)
|
||
|
||
# 永远返回0,不阻断主流程
|
||
return 0
|
||
|
||
|
||
if __name__ == "__main__":
|
||
sys.exit(main())
|