GitHub Actions PR 通知系统完整指南
📋 系统概览
PR 检查通知系统是一个智能化的自动化工具,当 Pull Request 的所有检查完成后自动发送通知,支持 GitHub 评论和钉钉机器人等多种通知渠道。
关联资源:
- Issue: #49 实现 PR 检查完成通知系统
- 实现 PR: #57 实施基于 workflow_run 的改进 PR 通知系统
- 权限修复 PR: #53 修复 PR 通知系统权限问题
- 钉钉修复 PR: #61 修复钉钉机器人通知签名验证问题
🎯 核心功能
功能特性
- 智能状态检测: 自动监控所有 PR 相关的检查项(测试、代码规范等)
- 多渠道通知: 支持 GitHub 评论通知和钉钉/Slack/企业微信等 Webhook 通知
- 丰富的状态信息: 提供详细的检查结果、失败原因和修复建议
- 实时状态更新: 检查状态变化时自动更新通知内容
工作流架构
🔧 配置指南
快速配置
使用 GitHub CLI 快速配置:
bash
# 1. 配置钉钉机器人 URL
gh variable set WEBHOOK_URL --body "https://oapi.dingtalk.com/robot/send?access_token=your_access_token"
# 2. 设置通知模式
gh variable set NOTIFICATION_MODE --body "both" # webhook | comment | both
# 3. 配置钉钉机器人加签密钥
gh secret set WEBHOOK_SECRET --body "SECyour_dingtalk_secret_key"
# 4. 验证配置
gh variable list
gh secret list支持的通知平台
🤖 钉钉机器人
配置要求:
bash
# Webhook URL
WEBHOOK_URL: "https://oapi.dingtalk.com/robot/send?access_token=YOUR_TOKEN"
# 加签密钥(⚠️ 必须启用加签验证)
WEBHOOK_SECRET: "SECyour_dingtalk_secret_key"重要提醒:
- ✅ 必须启用钉钉机器人的"加签"安全设置
- ✅ 签名通过 URL 参数传递,不是请求体
- ✅ 使用 HMAC-SHA256 + Base64 签名算法
💬 Slack 机器人
bash
# Slack Webhook URL
WEBHOOK_URL: "https://hooks.slack.com/services/T00/B00/XXX"
WEBHOOK_SECRET: "your_slack_signing_secret"📱 企业微信群机器人
bash
# 企业微信 Webhook URL
WEBHOOK_URL: "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=your_key"权限配置
工作流已配置必要权限,无需额外设置:
yaml
permissions:
contents: read # 读取仓库内容
pull-requests: write # 创建和更新 PR 评论
issues: write # 管理 Issue 评论
checks: read # 获取检查状态(⚠️ 必需!)📨 通知格式
GitHub 评论通知
markdown
## 🔍 PR 检查状态汇总
**整体状态**: ✅ 可以审核
**检查完成时间**: 2025-06-23 10:30:00 UTC
**总检查数**: 4 | 通过: 4 | 关键失败: 0 | 关键跳过: 0
### 检查结果详情
| 检查项 | 状态 | 结果 | 链接 |
|--------|------|------|------|
| Test & Quality Check | ✅ | 成功 | [查看](https://github.com/repo/actions/runs/123) |
| Check Conventional Commits | ✅ | 成功 | [查看](https://github.com/repo/actions/runs/124) |
| Go Performance Tests | ✅ | 成功 | [查看](https://github.com/repo/actions/runs/125) |
| Cloudflare Pages | ✅ | 成功 | [查看](https://github.com/repo/actions/runs/126) |
### 触发信息
- **触发工作流**: CI Unified
- **分支**: feat/new-feature
- **提交**: abc123d
---
*🤖 基于 workflow_run 事件和 alls-green 逻辑自动生成*钉钉机器人通知
json
{
"msgtype": "markdown",
"markdown": {
"title": "PR 检查完成通知",
"text": "## 🔍 PR 检查状态汇总\n\n**PR #61**: fix: 修复钉钉机器人通知签名验证问题\n**整体状态**: ✅ 可以审核\n**检查完成时间**: 2025-06-23 10:30:00 UTC\n**总检查数**: 4 | 通过: 4 | 关键失败: 0 | 关键跳过: 0\n\n### 检查结果详情\n- ✅ Test & Quality Check: 成功\n- ✅ Check Conventional Commits: 成功\n- ✅ Go Performance Tests: 成功\n- ✅ Cloudflare Pages: 成功\n\n**PR 链接**: [查看详情](https://github.com/repo/pull/61)\n**触发工作流**: CI Unified\n\n---\n*🤖 基于 workflow_run 事件和 alls-green 逻辑自动生成*"
}
}🛠️ 故障排除
常见问题诊断
1. 通知没有发送
检查清单:
- ✅ 确认 PR 处于 open 状态
- ✅ 检查 CI 工作流是否正常完成
- ✅ 查看 PR Ready Notification 工作流日志
- ✅ 验证权限配置是否正确
2. 权限不足导致静默失败 🚨
问题症状:
- ✅ 工作流正常触发 (
workflow_run事件) - ✅ Job 显示执行成功
- ❌ 但没有创建 GitHub 评论
- ❌ 没有发送 Webhook 通知
错误日志示例:
❌ Notification failed: RequestError [HttpError]: Resource not accessible by integration
status: 403,
response: {
message: 'Resource not accessible by integration',
documentation_url: 'https://docs.github.com/rest/checks/runs#list-check-runs-for-a-git-reference'
}解决方案: 确保工作流包含 checks: read 权限
3. 钉钉机器人通知失败
钉钉机器人故障排除详解
错误码 310000 - 签名不匹配 🔴
错误响应:
json
{
"errcode": 310000,
"errmsg": "description:机器人发送签名不匹配;solution:请确认签名和生成签名的时间戳必须都放在调用的网址中"
}根本原因: 钉钉机器人的签名验证算法与其他平台不同
解决步骤:
验证密钥配置:
bashgh secret list | grep WEBHOOK_SECRET检查签名算法:
javascript// 正确的钉钉签名算法 const timestampMs = Date.now(); const stringToSign = timestampMs.toString() + '\n' + webhookSecret; const signature = crypto .createHmac('sha256', webhookSecret) .update(stringToSign, 'utf8') .digest('base64'); // 关键:签名通过 URL 参数传递 const url = new URL(webhookUrl); url.searchParams.set('timestamp', timestampMs.toString()); url.searchParams.set('sign', signature);钉钉机器人设置检查:
- ✅ 确认已启用"加签"安全设置
- ✅ 复制正确的加签密钥到 GitHub Secrets
- ✅ 检查机器人发送权限
手动测试:
bash# 使用测试脚本验证 node .act-artifacts/test-dingtalk-direct.js
其他钉钉错误码
| 错误码 | 错误信息 | 原因 | 解决方案 |
|---|---|---|---|
| 310000 | 签名不匹配 | 签名算法错误或密钥不匹配 | 检查签名算法和密钥配置 |
| 40035 | 缺少参数 json | JSON 格式错误 | 验证消息格式 |
| 300001 | 关键词不匹配 | 消息内容不包含关键词 | 添加必要关键词或调整设置 |
| 300002 | webhook 地址无效 | access_token 错误 | 检查 webhook URL |
| 300013 | 关键词不匹配 | 关键词过滤 | 在消息中添加机器人关键词 |
调试技巧
查看工作流日志
bash
# 列出最近的通知工作流执行
gh run list --workflow="PR Ready Notification"
# 查看特定执行的详细日志
gh run view <run-id> --log
# 查看钉钉相关日志
gh run view <run-id> --log | grep -A5 -B5 "钉钉\|dingtalk"手动触发测试
bash
# 手动触发通知工作流测试
gh workflow run pr-ready-notification.yml --ref master -f pr_number=61🏗️ 技术实现
工作流触发机制
yaml
name: PR Ready Notification
on:
workflow_run:
workflows: ["CI Unified", "Go Performance Tests"]
types: [completed]
workflow_dispatch:
inputs:
pr_number:
description: 'PR number to check'
required: false
type: number
permissions:
contents: read
pull-requests: write
issues: write
checks: read
jobs:
aggregate-and-notify:
if: |
(github.event_name == 'workflow_run' && github.event.workflow_run.event == 'pull_request') ||
github.event_name == 'workflow_dispatch'检查聚合逻辑
javascript
// 定义关键检查项
const criticalChecks = [
'Test & Quality Check', // 来自 CI Unified
'Check Conventional Commits', // 来自 CI Unified
'Go Performance Tests', // 来自 Performance workflow
'Cloudflare Pages' // 外部服务
];
// 允许失败和跳过的检查
const allowedFailures = ['Go Performance Tests']; // 性能测试允许失败
const allowedSkips = ['Cloudflare Pages']; // 外部服务允许跳过
// alls-green 逻辑:只有关键失败为 0 且关键跳过为 0 才能审核
const canReview = failedChecks === 0 && skippedChecks === 0 && totalChecks > 0;钉钉签名算法实现
javascript
// 钉钉机器人签名算法: HMAC-SHA256(timestamp + "\n" + secret)
// 注意:钉钉的签名需要通过URL参数传递,而不是放在请求体中
if (webhookSecret && webhookSecret !== '') {
const timestampMs = Date.now();
// 确保时间戳转换为字符串,这很重要!
const stringToSign = timestampMs.toString() + '\n' + webhookSecret;
const signature = crypto
.createHmac('sha256', webhookSecret)
.update(stringToSign, 'utf8') // 明确指定UTF-8编码
.digest('base64');
// 修改URL以包含签名参数
const url = new URL(webhookUrl);
url.searchParams.set('timestamp', timestampMs.toString());
url.searchParams.set('sign', signature);
webhookUrl = url.toString();
}📊 问题修复历史
Issue #49 完整解决历程
第一阶段:基础实现 (PR #57)
- ✅ 实施基于
workflow_run的通知系统 - ✅ 支持 GitHub 评论和 Webhook 通知
- ✅ 实现智能检查聚合逻辑
第二阶段:权限修复 (PR #53)
- 🐛 问题: 403 权限错误导致静默失败
- 🔧 修复: 添加
checks: read权限 - ✅ 结果: GitHub 评论通知正常工作
第三阶段:钉钉签名修复 (PR #61)
- 🐛 问题: 钉钉机器人返回 310000 签名不匹配错误
- 🔍 诊断: 发现签名算法中时间戳和编码问题
- 🔧 第一次修复: 修正时间戳转字符串和 UTF-8 编码
- 🐛 后续问题: 签名应通过 URL 参数传递而非请求体
- 🔧 最终修复: 实现正确的钉钉签名参数传递方式
- ✅ 结果: 钉钉机器人通知正常工作
第四阶段:JavaScript 变量赋值错误修复 (Issue #62)
- 🐛 问题: PR #62 钉钉通知仍然失败,GitHub Comment 正常
- 🔍 详细诊断: 通过 workflow 日志发现
TypeError: Assignment to constant variable. - 🎯 根本原因: 第270行声明
const webhookUrl,第331行尝试重新赋值导致错误 - 🔧 修复方案: 将
const webhookUrl改为let webhookUrl,允许签名过程中重新赋值 - 🧪 验证方法: 创建本地测试脚本,使用 act 工具验证修复
- ✅ 结果: JavaScript 错误完全消除,钉钉通知系统恢复正常
关键技术发现
钉钉签名特殊性:
- 签名通过 URL 参数传递,不是请求体
- 使用 Base64 编码,不是 hex
- 需要明确指定 UTF-8 编码
GitHub Actions 权限陷阱:
- 缺少
checks: read权限会导致静默失败 - 工作流显示成功但功能完全不工作
- 缺少
调试策略重要性:
- 详细的日志输出帮助快速定位问题
- 本地测试脚本验证修复方案
- 错误码专门处理提供具体指导
🚀 最佳实践
部署策略
- 工作流更新: 由于
workflow_run事件限制,必须先将工作流文件合并到 master 分支 - 功能验证: 只有合并后才能在后续 PR 中验证新功能
- 谨慎修改: 由于无法在合并前验证,工作流修改需要格外谨慎
监控和维护
- 定期检查配置: 确保 webhook URL 和密钥的有效性
- 监控日志: 定期查看工作流日志,及时发现问题
- 测试验证: 在重要更改后通过测试 PR 验证功能
- 文档同步: 配置变更时及时更新团队文档
错误预防
- 配置验证: 建立配置变更的验证流程
- 测试覆盖: 包含端到端的通知测试
- 监控告警: 设置通知系统失效的监控
- 回滚准备: 保持能够快速回滚的能力
📚 参考资源
官方文档
项目资源
- Issue #49: PR 检查完成通知系统
- PR #57: 实施基于 workflow_run 的改进 PR 通知系统
- PR #53: 修复 PR 通知系统权限问题
- PR #61: 修复钉钉机器人通知签名验证问题
测试工具
test-dingtalk-direct.js- 钉钉机器人直接测试test-dingtalk-fixed.js- 修复后的签名测试- GitHub CLI 工作流手动触发
本文档综合了 Issue #49 及相关 PR 的完整技术实现和故障排除经验,为项目提供完整的 PR 通知系统解决方案。