Files
zt-closet-backend/.codebuddy/plans/cicd补充分级钉钉通知_68dac9d1.md
2026-09-14 00:04:55 +08:00

141 lines
9.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
name: cicd补充分级钉钉通知
overview: 在 3 个 Gitea Actions 工作流中补充分级钉钉通知(成功/失败/危险操作警告),webhook 走 Secret,采用 markdown 富文本样式。
todos:
- id: notify-deploy-and-mysqlup
content: 为 deploy.yaml 与 mysqlup.yaml 新增开始计时步骤及成功/失败钉钉通知步骤
status: completed
- id: notify-mysqldown
content: 为 mysqldown.yaml 新增开始计时、危险操作警告(执行前)及成功/失败钉钉通知步骤
status: completed
- id: verify-workflows
content: 校验三个工作流 YAML 合法、缩进正确、无明文 webhook、变量与条件引用无误
status: completed
dependencies:
- notify-deploy-and-mysqlup
- notify-mysqldown
---
## 产品概述
为仓库现有的 3 条 Gitea Actions 流水线(构建部署、数据库迁移升级、数据库迁移回滚)补齐钉钉机器人通知能力。通知按事件级别分级呈现,采用钉钉 markdown 富文本卡片样式,让团队在钉钉群里一眼看清「哪条流水线、什么结果、涉及哪个版本/提交、耗时多久、从哪看日志」。
## 核心功能
- **分级通知(3 级)**:成功、失败、危险操作警告。成功与失败在流水线结束时发送;危险操作警告在「数据库回滚」实际执行前发送,结束后再补一条结果通知。
- **覆盖范围**:全部 3 条流水线——部署、迁移升级、迁移回滚;失败通知不做任何 @(不 @所有人、不 @指定人)。
- **富文本样式**:使用钉钉 markdown 消息,含三级标题、引用块、加粗、无序列表与可点击链接;不同级别使用不同图标与标题文案,视觉上可快速区分。
- **信息摘要**:每条通知包含仓库、分支或标签、提交短哈希、触发者、镜像版本、耗时、发生时间,并附「查看运行详情/失败日志」链接;失败通知额外给出定位指引。
- **安全存放**:webhook 地址(含 access_token)以 Gitea 仓库 Secret 形式引用,不出现在流水线文件中,也不打印到日志。
- **不干扰主流程**:通知发送失败不会导致流水线失败。
## 技术选型
- 流水线引擎:Gitea Actions(GitHub Actions 兼容语法),工作流文件为 YAML。
- 通知通道:钉钉自定义机器人 webhook,`msgtype: markdown`(原生支持标题、引用、加粗、列表、链接,是当前钉钉机器人中样式表达力最好的纯文本消息类型;`text` 无样式,`actionCard` 无法体现级别差异)。
- 发送方式:`curl` 直连 webhook,纯 shell 构造 JSON。**不引入任何外部 action**——与「checkout 已替换为 Gitee 镜像、避免依赖 GitHub action」的既定方向保持一致,避免新增拉取依赖。
- 无新增语言/框架/依赖:仅使用 runner 自带的 `bash`、`curl`、`date`、`cut`。
## 实现方式
在 3 个工作流中新增「通知类」步骤,不动任何既有业务步骤(docker 构建/推送、SSH 部署、迁移执行)及其语义。
**分级触发条件**
| 级别 | 触发条件 | 位置 |
| --- | --- | --- |
| 成功 | `if: success()` | 作业最后(结果步骤之后) |
| 失败 | `if: failure()` | 作业最后(结果步骤之后) |
| 危险警告 | `if: success()` | 仅 mysqldown:置于「校验确认词」通过之后、拉取代码之前 |
**耗时统计**:在每条作业的**第一步**新增「记录开始时间」,把 epoch 秒写入 `$GITHUB_ENV` 的 `JOB_START`;通知步骤用 `date +%s` 求差并格式化为「X 分 Y 秒」。该步骤位于拉取代码之前,可保证即便后续 checkout 失败也能正确计时。
**关键决策与理由**
1. **内联步骤而非本地 composite action**:本地 action 需要仓库先 checkout 成功才能加载,而本仓库恰好存在 checkout 拉取的现实风险(用户刚做过 checkout 替换),一旦 checkout 失败则失败通知自身也无法发送。内联步骤只依赖 runner 与工作流文件,可在 checkout 失败时照常发出失败通知,可靠性更高。代价是 3 个文件存在少量重复,可接受。
2. **纯 shell 构造 JSON,不依赖 jq**:Gitea act runner 镜像中 jq 不保证存在;改为 bash 变量拼接 + 字面量 `\n` 转义,再以 heredoc 写入临时文件用 `curl -d @file` 发送。正文刻意不使用双引号、反引号与 `,从根本上规避 JSON 破坏与 shell 命令替换。
3. **Secret 而非变量**:access_token 属凭据,走 `secrets`;通知步骤禁用 `set -x`,且不 echo webhook,避免凭据进入构建日志。
4. **`continue-on-error: true`**:通知是旁路能力,任何发送异常都不得影响部署/迁移结果。
## 实现注意
- **前置人工操作**:需在 Gitea 仓库 Settings → Actions → Secrets 中新建 Secret `DINGTALK_WEBHOOK`,值为完整 webhook URL(含 `access_token=`)。若未创建,通知步骤会因空 URL 而失败,但被 `continue-on-error` 吞掉,不影响流水线。
- **钉钉机器人安全设置需匹配**:本方案按「自定义关键词」或「IP 白名单」模式发送;若机器人配置的是「加签」模式,需在 URL 后追加 `&timestamp=...&sign=...`,当前未实现该签名逻辑(需另行补充)。消息正文已包含 `zt-closet-backend` 等关键词,可满足常见关键词配置。
- **表达式兼容性**:优先使用既有文件已证实的 `gitea.*` 上下文(`gitea.repository/ref/sha/actor/run_id/server_url`)。其中 `gitea.server_url` 若在该 Gitea 版本不可用,回退为仓库已知主机 `https://git.lzcwy.cn`(与 `env.REGISTRY` 一致)。
- **不影响既有行为**:`deploy.yaml` 的「部署结果」步骤(`if: always()`)保留;`mysqldown.yaml` 的确认词校验逻辑与失败退出保持不变。
- **限流**:钉钉机器人限 20 条/分钟;本方案单次运行最多 3 条,无风险。
- **日志**:仅打印钉钉返回体中的 `errcode`/`errmsg` 便于排障,绝不输出 URL。
## 架构设计
通知能力作为「旁路观测层」附加在各作业末尾,与业务步骤零耦合:
```mermaid
flowchart LR
A["记录开始时间<br/>写入 JOB_START"] --> B["拉取代码 / 业务步骤"]
B --> C{"作业结果"}
C -- success --> D["钉钉通知(成功)<br/>if: success()"]
C -- failure --> E["钉钉通知(失败)<br/>if: failure()"]
D --> F["钉钉 markdown 机器人"]
E --> F
G["危险操作警告<br/>仅 mysqldown,执行前"] --> F
```
## 目录结构
```
zt-closet-backend/
└── .gitea/
└── workflows/
├── deploy.yaml # [MODIFY] 新增「记录开始时间」+「钉钉通知(成功)」「钉钉通知(失败)」三步
├── mysqlup.yaml # [MODIFY] 新增「记录开始时间」+「钉钉通知(成功)」「钉钉通知(失败)」三步
└── mysqldown.yaml # [MODIFY] 新增「记录开始时间」+「钉钉通知(危险操作警告)」+「钉钉通知(成功)」「钉钉通知(失败)」四步
```
- **deploy.yaml**:在 `steps` 首部插入计时步骤;在「部署结果」之后追加成功/失败两个通知步骤,通知中携带镜像 `${{ env.IMAGE }}:${{ steps.meta.outputs.tag }}`。
- **mysqlup.yaml**:结构同上,镜像字段使用 `${{ env.IMAGE }}:${{ env.MIGRATE_TAG }}`。
- **mysqldown.yaml**:在「校验确认词」之后追加危险操作警告步骤(`if: success()`,正文强调「删除所有迁移创建的表及其数据,不可恢复」),末尾再追加成功/失败通知。
## 关键代码结构
通知步骤统一形态(以 deploy.yaml 成功通知为例,失败/警告仅替换图标、文案、条件与链接文案):
```
- name: 记录开始时间
run: echo "JOB_START=$(date +%s)" >> "$GITHUB_ENV"
# ... 既有业务步骤保持不变 ...
- name: 钉钉通知(成功)
if: success()
continue-on-error: true
env:
WEBHOOK: ${{ secrets.DINGTALK_WEBHOOK }}
REPO: ${{ gitea.repository }}
REF: ${{ gitea.ref }}
SHA: ${{ gitea.sha }}
ACTOR: ${{ gitea.actor }}
RUN_URL: ${{ gitea.server_url }}/${{ gitea.repository }}/actions/runs/${{ gitea.run_id }}
IMAGE_REF: ${{ env.IMAGE }}:${{ steps.meta.outputs.tag }}
run: |
set -uo pipefail
REF_NAME="${REF#refs/heads/}"; REF_NAME="${REF_NAME#refs/tags/}"
SHORT_SHA="$(printf '%s' "$SHA" | cut -c1-7)"
COST=$(( $(date +%s) - ${JOB_START:-$(date +%s)} ))
TEXT="### <状态图标> 构建并部署成功\n\n"
TEXT="${TEXT}> **zt-closet-backend** · 构建并部署\n\n"
TEXT="${TEXT}- **仓库**:$REPO\n"
TEXT="${TEXT}- **分支/标签**:$REF_NAME\n"
TEXT="${TEXT}- **提交**:$SHORT_SHA\n"
TEXT="${TEXT}- **触发者**:$ACTOR\n"
TEXT="${TEXT}- **镜像**:$IMAGE_REF\n"
TEXT="${TEXT}- **耗时**:$((COST/60)) 分 $((COST%60)) 秒\n"
TEXT="${TEXT}- **时间**:$(date '+%Y-%m-%d %H:%M:%S')\n\n"
TEXT="${TEXT}[查看运行详情]($RUN_URL)"
printf '%s' "{\"msgtype\":\"markdown\",\"markdown\":{\"title\":\"<状态图标> 构建并部署成功 · zt-closet-backend\",\"text\":\"$TEXT\"}}" > /tmp/dingtalk.json
RESP="$(curl -sS -X POST "$WEBHOOK" -H 'Content-Type: application/json' -d @/tmp/dingtalk.json)"
echo "钉钉响应: $RESP"
```
其中 `<状态图标>` 分别取「对勾」「叉」「警示」三类图标 emoji(实现时填入对应字符),使三级通知在钉钉列表中可被颜色与图形同时区分。失败通知额外增加一行「- **失败定位**:请点击下方链接查看失败步骤与日志」,链接文案为「查看失败日志」。