From 0e575e2948013180ef419a6a78de096504bb2dfc Mon Sep 17 00:00:00 2001 From: HyxiaoGe <18889592303@163.com> Date: Sat, 6 Jun 2026 16:01:22 +0800 Subject: [PATCH] =?UTF-8?q?ci:=20=E9=83=A8=E7=BD=B2=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E9=A3=9E=E4=B9=A6=E4=BA=A4=E4=BA=92=E5=BC=8F=E5=AF=8C=E5=8D=A1?= =?UTF-8?q?=E7=89=87=E9=80=9A=E7=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 部署 job 末尾追加飞书 interactive 卡片通知,与 audio 两仓同款: - 成功绿头/失败红头,标题含仓库名 - 提交信息(前3非空行/截240字)+ 分支/提交/结果/时间四宫格 - 「查看运行日志」按钮;关键词 UptimeKuma 在脚注 note - 提交信息只经 env→python(os.environ→json),绝不进 shell,杜绝注入 - 空 webhook 守卫 + continue-on-error,未配 secret/飞书故障都不影响部署 Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/deploy.yml | 70 ++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index af4598a..3afa42f 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -71,3 +71,73 @@ jobs: - name: Push deploy metrics if: always() run: bash ~/scripts/push-deploy-metrics.sh chrono master ${{ job.status }} ${{ env.BUILD_START }} + + - name: 通知飞书(部署结果) + if: always() + continue-on-error: true + env: + FEISHU_INFRA_WEBHOOK: ${{ secrets.FEISHU_INFRA_WEBHOOK }} + JOB_STATUS: ${{ job.status }} + REPO: ${{ github.repository }} + REF_NAME: ${{ github.ref_name }} + RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + # 提交信息可含任意字符,只经 env 传入 python(os.environ→json),绝不进 shell,杜绝注入。 + COMMIT_MSG: ${{ github.event.head_commit.message }} + run: | + if [ -z "$FEISHU_INFRA_WEBHOOK" ]; then + echo "FEISHU_INFRA_WEBHOOK 未配置,跳过飞书通知" + exit 0 + fi + export SHORT_SHA="${GITHUB_SHA:0:7}" + export DEPLOY_TS="$(date -u '+%Y-%m-%d %H:%M UTC')" + python3 - <<'PY' + import json, os, urllib.request + ok = os.environ.get("JOB_STATUS") == "success" + repo = os.environ.get("REPO", "") + ref = os.environ.get("REF_NAME", "") + sha = os.environ.get("SHORT_SHA", "") + status = os.environ.get("JOB_STATUS", "") + ts = os.environ.get("DEPLOY_TS", "") + run_url = os.environ.get("RUN_URL", "") + raw = os.environ.get("COMMIT_MSG", "") or "" + lines = [ln for ln in raw.splitlines() if ln.strip()] + msg = "\n".join(lines[:3]) if lines else "(无提交信息)" + if len(msg) > 240: + msg = msg[:240] + "…" + card = { + "msg_type": "interactive", + "card": { + "config": {"wide_screen_mode": True}, + "header": { + "template": "green" if ok else "red", + "title": {"tag": "plain_text", + "content": ("✅ 部署成功 · " if ok else "❌ 部署失败 · ") + repo}, + }, + "elements": [ + {"tag": "div", "text": {"tag": "lark_md", "content": f"**提交信息**\n{msg}"}}, + {"tag": "hr"}, + {"tag": "div", "fields": [ + {"is_short": True, "text": {"tag": "lark_md", "content": f"**分支**\n{ref}"}}, + {"is_short": True, "text": {"tag": "lark_md", "content": f"**提交**\n`{sha}`"}}, + {"is_short": True, "text": {"tag": "lark_md", "content": f"**结果**\n{status}"}}, + {"is_short": True, "text": {"tag": "lark_md", "content": f"**时间**\n{ts}"}}, + ]}, + {"tag": "action", "actions": [ + {"tag": "button", "type": "primary", + "text": {"tag": "plain_text", "content": "查看运行日志"}, + "url": run_url}]}, + {"tag": "note", "elements": [ + {"tag": "plain_text", "content": "🤖 UptimeKuma · CI/CD 部署告警"}]}, + ], + }, + } + data = json.dumps(card).encode() + req = urllib.request.Request( + os.environ["FEISHU_INFRA_WEBHOOK"], data=data, + headers={"Content-Type": "application/json"}) + try: + with urllib.request.urlopen(req, timeout=15) as r: + print(f"飞书响应 http={r.status} body={r.read().decode()}") + except Exception as e: + print(f"飞书通知失败(忽略): {e!r}") + PY