| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772 |
- #!/bin/bash
- # ============================================================================
- # 禅道 Bug 追踪模块 - DEV 开发环境专用版本
- # ============================================================================
- # ⚠️ 重要提示:
- # 此文件仅供 Jenkinsfile.dev 使用
- # Jenkinsfile.release 严禁使用此文件,必须使用 zentao_dev.sh
- #
- # 功能说明:
- # 1. 从 Git 提交历史中提取 Bug 修复信息
- # 2. 调用禅道 API 自动更新 Bug 状态为 "resolved"
- # 3. 发送飞书(Lark)通知 - 每个 Bug 单独发送绿色卡片
- # 4. 发送飞书版本发布汇总通知 - 蓝色卡片汇总所有已解决Bug
- # 5. 发送 Telegram 汇总通知
- #
- # 使用场景:
- # - dev 环境: 每次代码推送自动执行
- # - 分析范围: 从上次成功构建到当前 HEAD 的所有提交
- # - 自动更新: 将 active/wait 状态的 Bug 更新为 resolved
- # - 即时通知: 每解决一个 Bug 立即发送飞书通知
- #
- # 环境隔离:
- # ✅ 仅用于开发环境 (Jenkinsfile.dev)
- # ❌ 不得用于生产发布 (Jenkinsfile.release)
- # ============================================================================
- # ----------------------------------------------------------------------------
- # 配置部分
- # ----------------------------------------------------------------------------
- # ⚠️ 重要提示:
- # 所有配置项必须从 Jenkinsfile 的 environment 部分传入
- # 此脚本不包含任何硬编码配置,确保配置集中管理
- #
- # 必需的环境变量:
- # - ZENTAO_PRODUCT_ID: 禅道产品 ID
- # - ZENTAO_API_URL: 禅道 API 地址
- # - ZENTAO_BUG_BASE_URL: 禅道 Bug 详情页基础 URL
- # - COMMIT_BASE_URL: Git 提交详情页基础 URL
- # - LARK_WEBHOOK_URL: Lark Webhook URL
- # ----------------------------------------------------------------------------
- # 验证必需的环境变量
- if [ -z "${ZENTAO_PRODUCT_ID}" ] || [ -z "${ZENTAO_API_URL}" ] ||
- [ -z "${ZENTAO_BUG_BASE_URL}" ] || [ -z "${COMMIT_BASE_URL}" ] ||
- [ -z "${LARK_WEBHOOK_URL}" ]; then
- echo "错误: 缺少必需的环境变量配置" >&2
- echo "请确保在 Jenkinsfile 的 environment 部分设置了所有必需变量" >&2
- exit 1
- fi
- # ----------------------------------------------------------------------------
- # 函数: 发送单个 Bug 解决通知到飞书
- # ----------------------------------------------------------------------------
- # 参数:
- # $1: bug_id - Bug ID (例如: 2262)
- # $2: bug_title - Bug 标题 (例如: "登录页面404错误")
- # $3: old_status - 旧状态 (例如: "active")
- # $4: commit_info - 提交信息 (例如: "[abc123] 修复登录问题")
- # $5: bug_url - Bug 详情页 URL
- #
- # 功能:
- # 发送一个绿色的飞书卡片,显示单个 Bug 的解决信息,包含查看详情按钮
- # ----------------------------------------------------------------------------
- send_bug_resolved_notification() {
- local bug_id="$1"
- local bug_title="$2"
- local old_status="$3"
- local commit_info="$4"
- local bug_url="$5"
- local code_url="$6"
- # 发送 POST 请求到飞书 Webhook
- curl -s -X POST "${LARK_WEBHOOK_URL}" \
- -H "Content-Type: application/json" \
- -d "{
- \"msg_type\": \"interactive\",
- \"card\": {
- \"header\": {
- \"title\": {
- \"tag\": \"plain_text\",
- \"content\": \"🐛 pwtk管端端Bug更新\"
- },
- \"template\": \"green\"
- },
- \"elements\": [
- {
- \"tag\": \"div\",
- \"text\": {
- \"tag\": \"lark_md\",
- \"content\": \"**Bug #${bug_id}**: ${bug_title}\"
- }
- },
- {
- \"tag\": \"action\",
- \"actions\": [
- {
- \"tag\": \"button\",
- \"text\": {
- \"tag\": \"plain_text\",
- \"content\": \"查看代码\"
- },
- \"type\": \"primary\",
- \"url\": \"${code_url}\"
- }
- ]
- }
- ]
- }
- }" >/dev/null
- }
- # ----------------------------------------------------------------------------
- # 函数: 发送版本发布汇总通知到飞书
- # ----------------------------------------------------------------------------
- # 参数:
- # $1: previous_tag - 上一个版本标签 (例如: "v1.2.30" 或 "dev")
- # $2: current_tag - 当前版本标签 (例如: "v1.2.31" 或 "abc123")
- # $3: resolved_count - 已解决的 Bug 数量
- # $4: resolved_bugs - 已解决的 Bug 列表 (格式: "bug_info|bug_url" 每行一个)
- #
- # 功能:
- # 发送一个蓝色的飞书卡片,汇总本次发布解决的所有 Bug
- # ----------------------------------------------------------------------------
- send_release_summary_notification() {
- local previous_tag="$1"
- local current_tag="$2"
- local resolved_count="$3"
- local resolved_bugs="$4"
- # 构建已解决 Bug 链接列表
- local resolved_links=""
- if [ -n "$resolved_bugs" ]; then
- # 逐行读取 Bug 信息,格式化为 Markdown 链接
- while IFS='|' read -r bug_info bug_url; do
- if [ -n "$bug_info" ]; then
- resolved_links="${resolved_links}• [${bug_info}](${bug_url})\\n"
- fi
- done <<<"$resolved_bugs"
- fi
- # 构建完整消息内容
- local content="**版本**: ${previous_tag} → ${current_tag}\\n\\n"
- if [ ${resolved_count} -gt 0 ]; then
- content="${content}**已解决Bug** (${resolved_count}个):\\n${resolved_links}"
- else
- content="${content}**本次发布未解决Bug**"
- fi
- # 发送 POST 请求到飞书 Webhook
- curl -s -X POST "${LARK_WEBHOOK_URL}" \
- -H "Content-Type: application/json" \
- -d "{
- \"msg_type\": \"interactive\",
- \"card\": {
- \"header\": {
- \"title\": {
- \"tag\": \"plain_text\",
- \"content\": \"🚀 pwtk管端端版本发布\"
- },
- \"template\": \"blue\"
- },
- \"elements\": [
- {
- \"tag\": \"div\",
- \"text\": {
- \"tag\": \"lark_md\",
- \"content\": \"${content}\"
- }
- }
- ]
- }
- }" >/dev/null
- }
- # ----------------------------------------------------------------------------
- # 函数: 禅道 Bug 追踪主流程
- # ----------------------------------------------------------------------------
- # 参数:
- # $1: start_ref - 起始 Git 引用 (例如: "HEAD~10" 或 "v1.2.30")
- # $2: end_ref - 结束 Git 引用 (例如: "HEAD" 或 "v1.2.31")
- # $3: version_info - 版本信息 (例如: "dev (abc123)" 或 "v1.2.30 → v1.2.31")
- # $4: update_status - 是否更新禅道状态 ("true"=更新, "false"=只汇总)
- #
- # 功能:
- # 1. 从 Git 提交历史中提取符合格式的 Bug 修复 (fix:2262, fix-2262, fix: 2262 2265)
- # 2. 查询禅道获取 Bug 状态和标题
- # 3. 如果 update_status=true,调用禅道 API 更新 Bug 状态为 "resolved"
- # 4. 发送飞书通知 (每个 Bug 单独发送 + 最后发送汇总)
- # 5. 发送 Telegram 汇总通知
- #
- # 提交格式支持:
- # - fix:2262 修复登录问题
- # - fix-2262 修复登录问题
- # - fix: 2262 修复登录问题
- # - fix: 2262 2265 修复多个问题 (一次提交修复多个 Bug)
- # ----------------------------------------------------------------------------
- process_zentao_bugs() {
- local start_ref="$1"
- local end_ref="$2"
- local version_info="$3"
- local update_status="${4:-false}" # 默认为 false,只汇总不更新
- # 输出开始信息
- if type -t log_message >/dev/null 2>&1; then
- log_message "=============== 禅道 Bug 追踪开始 ==============="
- log_message "分析范围: ${start_ref}..${end_ref}"
- log_message "版本信息: ${version_info}"
- log_message "更新禅道状态: ${update_status}"
- fi
- # 初始化 bug 列表关联数组
- # 格式: BUG_MAP[bug_id] = "commit_hash|author|message\ncommit_hash|author|message"
- declare -A BUG_MAP
- # 获取所有符合格式的提交 (保存到变量以避免管道子shell问题)
- # 格式: commit_hash|author|subject
- COMMITS=$(git log "${start_ref}..${end_ref}" --pretty=format:"%h|%an|%s")
- # 遍历每个提交,提取 Bug ID
- while IFS='|' read -r commit_hash author commit_message; do
- # 检查提交信息是否包含 fix: 或 fix- 格式
- if echo "$commit_message" | grep -qE "fix[:-]\s*[0-9]"; then
- # 提取所有 Bug ID (支持多个 Bug ID,用空格分隔)
- # 例如: "fix: 2262 2265" 提取出 "2262 2265"
- # 步骤1: 提取 fix: 或 fix- 后面的所有数字
- # 步骤2: 移除 fix: 或 fix-
- # 步骤3: 使用 xargs 自动trim前后空格
- bug_ids=$(echo "$commit_message" | grep -oE "fix[:-]\s*([0-9]+(\s+[0-9]+)*)" | sed -E 's/fix[:-]//' | xargs)
- if [ -n "$bug_ids" ]; then
- if type -t log_message >/dev/null 2>&1; then
- log_message "发现 Bug 修复: [$commit_hash] $author - $commit_message"
- fi
- # 分割多个 Bug ID,逐个处理
- for bug_id in $bug_ids; do
- if [ -n "$bug_id" ]; then
- # 存储到关联数组 (如果已存在则追加,支持一个 Bug 多次提交修复)
- if [ -n "${BUG_MAP[$bug_id]}" ]; then
- BUG_MAP[$bug_id]="${BUG_MAP[$bug_id]}
- ${commit_hash}|${author}|${commit_message}"
- else
- BUG_MAP[$bug_id]="${commit_hash}|${author}|${commit_message}"
- fi
- fi
- done
- fi
- fi
- done <<<"$COMMITS"
- # 统计发现的 Bug 数量
- BUG_COUNT=${#BUG_MAP[@]}
- if type -t log_message >/dev/null 2>&1; then
- log_message "共发现 ${BUG_COUNT} 个 Bug"
- fi
- # 如果没有发现 Bug,直接返回
- if [ $BUG_COUNT -eq 0 ]; then
- if type -t log_message >/dev/null 2>&1; then
- log_message "本次构建未发现符合格式的 Bug 修复"
- log_message "=============== 禅道 Bug 追踪结束 ==============="
- fi
- return 0
- fi
- # 获取禅道 API Token
- if type -t log_message >/dev/null 2>&1; then
- log_message "正在获取禅道 API Token..."
- fi
- # 调用禅道登录接口获取 Token
- ZENTAO_TOKEN_RESPONSE=$(curl -s -X POST "${ZENTAO_API_URL}/tokens" \
- -H "Content-Type: application/json" \
- -d "{\"account\":\"${ZENTAO_USERNAME}\",\"password\":\"${ZENTAO_PASSWORD}\"}")
- # 从响应中提取 Token
- ZENTAO_TOKEN=$(echo "$ZENTAO_TOKEN_RESPONSE" | grep -o '"token":"[^"]*"' | cut -d'"' -f4)
- # 检查 Token 是否获取成功
- if [ -z "$ZENTAO_TOKEN" ]; then
- if type -t log_message >/dev/null 2>&1; then
- log_message "警告: 无法获取禅道 Token,仅生成链接列表"
- log_message "响应: ${ZENTAO_TOKEN_RESPONSE}"
- fi
- # Token 获取失败,只生成链接列表不更新状态
- BUG_LINKS=""
- for bug_id in $(echo "${!BUG_MAP[@]}" | tr ' ' '\n' | sort -n); do
- COMMIT_INFO=""
- # 遍历该 Bug 的所有相关提交
- while IFS= read -r commit_line; do
- commit_hash=$(echo "$commit_line" | cut -d'|' -f1)
- author=$(echo "$commit_line" | cut -d'|' -f2)
- COMMIT_INFO="${COMMIT_INFO} 修复人: ${author} [${commit_hash}]
- "
- done <<<"${BUG_MAP[$bug_id]}"
- BUG_LINKS="${BUG_LINKS}#${bug_id}
- ${COMMIT_INFO} ${ZENTAO_BUG_BASE_URL}${bug_id}.html
- "
- done
- # 发送 Telegram 通知 (包含警告信息)
- if type -t send_telegram_message >/dev/null 2>&1; then
- send_telegram_message "🐛 Bug 修复汇总
- 版本: ${version_info}
- ${BUG_LINKS}
- 📊 总计: ${BUG_COUNT} 个 Bug 已修复
- ⚠️ 禅道状态未更新(Token获取失败)"
- fi
- return 1
- fi
- if type -t log_message >/dev/null 2>&1; then
- log_message "成功获取禅道 Token"
- fi
- # 初始化汇总信息
- BUG_LINKS="" # Telegram 消息用的链接列表
- RESOLVED_COUNT=0 # 已解决的 Bug 数量
- ERROR_COUNT=0 # 失败的 Bug 数量
- RESOLVED_BUGS_LIST="" # 飞书汇总用的 Bug 列表
- # 处理每个 Bug (按 Bug ID 排序)
- for bug_id in $(echo "${!BUG_MAP[@]}" | tr ' ' '\n' | sort -n); do
- if type -t log_message >/dev/null 2>&1; then
- log_message "----------------------------------------"
- log_message "处理 Bug #${bug_id}"
- fi
- # 查询 Bug 状态
- BUG_INFO=$(curl -s -X GET "${ZENTAO_API_URL}/bugs/${bug_id}" \
- -H "Token: ${ZENTAO_TOKEN}")
- # 使用 Python 解析 JSON,正确处理中文
- BUG_STATUS=$(echo "$BUG_INFO" | python3 -c "import sys, json; data=json.load(sys.stdin); print(data.get('status', ''))" 2>/dev/null)
- BUG_TITLE=$(echo "$BUG_INFO" | python3 -c "import sys, json; data=json.load(sys.stdin); print(data.get('title', ''))" 2>/dev/null)
- BUG_OPENED_BY=$(echo "$BUG_INFO" | python3 -c "import sys, json; data=json.load(sys.stdin); openedBy=data.get('openedBy', {}); print(openedBy.get('account', '') if isinstance(openedBy, dict) else '')" 2>/dev/null)
- # 检查是否成功获取 Bug 信息
- if [ -z "$BUG_STATUS" ]; then
- if type -t log_message >/dev/null 2>&1; then
- log_message "警告: 无法获取 Bug #${bug_id} 的状态信息"
- fi
- ERROR_COUNT=$((ERROR_COUNT + 1))
- # 添加到失败列表
- COMMIT_INFO=""
- while IFS= read -r commit_line; do
- commit_hash=$(echo "$commit_line" | cut -d'|' -f1)
- author=$(echo "$commit_line" | cut -d'|' -f2)
- COMMIT_INFO="${COMMIT_INFO} 修复人: ${author} [${commit_hash}]
- "
- done <<<"${BUG_MAP[$bug_id]}"
- BUG_LINKS="${BUG_LINKS}❌ #${bug_id} (查询失败)
- ${COMMIT_INFO} ${ZENTAO_BUG_BASE_URL}${bug_id}.html
- "
- continue
- fi
- if type -t log_message >/dev/null 2>&1; then
- log_message "Bug #${bug_id} 状态: ${BUG_STATUS}"
- log_message "Bug #${bug_id} 标题: ${BUG_TITLE}"
- log_message "Bug #${bug_id} 创建者: ${BUG_OPENED_BY}"
- fi
- # 获取提交信息 (所有情况都需要)
- COMMIT_INFO=""
- while IFS= read -r commit_line; do
- commit_hash=$(echo "$commit_line" | cut -d'|' -f1)
- author=$(echo "$commit_line" | cut -d'|' -f2)
- COMMIT_INFO="${COMMIT_INFO} 修复人: ${author} [${commit_hash}]
- "
- done <<<"${BUG_MAP[$bug_id]}"
- # 检查是否为未解决状态 (active 或 wait 状态才需要处理)
- if [ "$BUG_STATUS" = "active" ] || [ "$BUG_STATUS" = "wait" ]; then
- # 根据 update_status 参数决定是否更新禅道状态
- if [ "$update_status" = "true" ]; then
- if type -t log_message >/dev/null 2>&1; then
- log_message "Bug #${bug_id} 状态为未解决,准备更新为已解决..."
- fi
- # 准备评论内容 (包含所有相关 commit 信息)
- COMMENT="已修复该问题
- 相关提交:"
- while IFS= read -r commit_info; do
- commit_hash=$(echo "$commit_info" | cut -d'|' -f1)
- author=$(echo "$commit_info" | cut -d'|' -f2)
- message=$(echo "$commit_info" | cut -d'|' -f3)
- COMMENT="${COMMENT}
- - [${commit_hash}] ${author}: ${message}"
- done <<<"${BUG_MAP[$bug_id]}"
- COMMENT="${COMMENT}
- 代码提交: <a href=\"${COMMIT_BASE_URL}${commit_hash}\" target=\"_blank\">点击查看</a>"
- # 生成解决 Bug 的 JSON 临时文件
- RESOLVE_JSON_FILE="/tmp/zentao_resolve_${bug_id}_$$.json"
- # 确定指派对象:优先使用创建者,如果获取失败则使用当前用户
- ASSIGNEE="${BUG_OPENED_BY}"
- if [ -z "$ASSIGNEE" ]; then
- if type -t log_message >/dev/null 2>&1; then
- log_message "警告: 无法获取 Bug #${bug_id} 创建者,将指派给当前用户 ${ZENTAO_USERNAME}"
- fi
- ASSIGNEE="${ZENTAO_USERNAME}"
- fi
- # 使用 Python 生成 JSON (确保正确处理中文和特殊字符)
- python3 -c "import json; print(json.dumps({
- 'resolution': 'fixed',
- 'resolvedBuild': 'trunk',
- 'resolvedDate': '$(date "+%Y-%m-%d %H:%M:%S")',
- 'assignedTo': '${ASSIGNEE}',
- 'comment': '''${COMMENT}'''
- }, ensure_ascii=False, indent=2))" >"$RESOLVE_JSON_FILE"
- # 调用禅道 API 解决 Bug
- RESOLVE_RESPONSE=$(curl -s -X POST "${ZENTAO_API_URL}/bugs/${bug_id}/resolve" \
- -H "Token: ${ZENTAO_TOKEN}" \
- -H "Content-Type: application/json" \
- -d @"$RESOLVE_JSON_FILE")
- # 清理临时文件
- rm -f "$RESOLVE_JSON_FILE"
- # 检查是否成功 (响应包含 "id" 字段表示成功)
- if echo "$RESOLVE_RESPONSE" | grep -q '"id"'; then
- if type -t log_message >/dev/null 2>&1; then
- log_message "✅ 成功解决 Bug #${bug_id}"
- fi
- RESOLVED_COUNT=$((RESOLVED_COUNT + 1))
- BUG_LINKS="${BUG_LINKS}✅ #${bug_id} - ${BUG_TITLE}
- ${COMMIT_INFO} ${ZENTAO_BUG_BASE_URL}${bug_id}.html
- "
- # 添加到已解决 Bug 列表 (用于飞书汇总通知)
- RESOLVED_BUGS_LIST="${RESOLVED_BUGS_LIST}#${bug_id} ${BUG_TITLE}|${ZENTAO_BUG_BASE_URL}${bug_id}.html
- "
- # 发送飞书单个 Bug 通知
- BUG_URL="${ZENTAO_BUG_BASE_URL}${bug_id}.html"
- CODE_URL="${COMMIT_BASE_URL}${commit_hash}"
- COMMIT_SUMMARY=$(echo "${BUG_MAP[$bug_id]}" | head -1 | awk -F'|' '{print "[" $1 "] " $3}')
- send_bug_resolved_notification "${bug_id}" "${BUG_TITLE}" "${BUG_STATUS}" "${COMMIT_SUMMARY}" "${BUG_URL}" "${CODE_URL}"
- if type -t log_message >/dev/null 2>&1; then
- log_message "飞书通知已发送 (Bug #${bug_id})"
- fi
- else
- # 更新失败
- if type -t log_message >/dev/null 2>&1; then
- log_message "❌ 解决 Bug #${bug_id} 失败: ${RESOLVE_RESPONSE}"
- fi
- ERROR_COUNT=$((ERROR_COUNT + 1))
- BUG_LINKS="${BUG_LINKS}❌ #${bug_id} - ${BUG_TITLE} (更新失败)
- ${COMMIT_INFO} ${ZENTAO_BUG_BASE_URL}${bug_id}.html
- "
- fi
- else
- # update_status = false: 只汇总,不更新禅道状态
- if type -t log_message >/dev/null 2>&1; then
- log_message "Bug #${bug_id} 状态为未解决,但只汇总不更新禅道状态"
- fi
- RESOLVED_COUNT=$((RESOLVED_COUNT + 1))
- BUG_LINKS="${BUG_LINKS}📋 #${bug_id} - ${BUG_TITLE}
- ${COMMIT_INFO} ${ZENTAO_BUG_BASE_URL}${bug_id}.html
- "
- # 添加到已解决 Bug 列表
- RESOLVED_BUGS_LIST="${RESOLVED_BUGS_LIST}#${bug_id} ${BUG_TITLE}|${ZENTAO_BUG_BASE_URL}${bug_id}.html
- "
- fi
- elif [ "$BUG_STATUS" = "resolved" ] || [ "$BUG_STATUS" = "closed" ]; then
- # Bug 已经是 resolved 或 closed 状态,跳过处理
- if type -t log_message >/dev/null 2>&1; then
- log_message "Bug #${bug_id} 已经是 ${BUG_STATUS} 状态,跳过更新(不显示在通知中)"
- fi
- # 不添加到 BUG_LINKS,不计数,不发通知
- else
- # 其他状态 (例如: testing),跳过处理
- if type -t log_message >/dev/null 2>&1; then
- log_message "Bug #${bug_id} 状态为 ${BUG_STATUS},跳过更新(不显示在通知中)"
- fi
- # 不添加到 BUG_LINKS,不计数,不发通知
- fi
- done
- # 输出汇总信息
- if type -t log_message >/dev/null 2>&1; then
- log_message "=============== 禅道 Bug 追踪完成 ==============="
- log_message "总计: ${BUG_COUNT} 个 Bug"
- log_message "已解决: ${RESOLVED_COUNT} 个"
- log_message "失败: ${ERROR_COUNT} 个"
- fi
- # 发送 Telegram 汇总通知
- if type -t send_telegram_message >/dev/null 2>&1; then
- send_telegram_message "🐛 Bug 修复汇总
- 版本: ${version_info}
- ${BUG_LINKS}
- 📊 统计:
- - 已解决: ${RESOLVED_COUNT} 个
- - 失败: ${ERROR_COUNT} 个"
- fi
- # 发送飞书版本发布汇总通知 (仅在有解决 Bug 时发送)
- if [ ${RESOLVED_COUNT} -gt 0 ] || [ ${ERROR_COUNT} -gt 0 ]; then
- if type -t log_message >/dev/null 2>&1; then
- log_message "发送飞书版本发布汇总通知..."
- fi
- send_release_summary_notification "${start_ref}" "${end_ref}" "${RESOLVED_COUNT}" "${RESOLVED_BUGS_LIST}"
- if type -t log_message >/dev/null 2>&1; then
- log_message "飞书版本发布通知已发送"
- fi
- else
- if type -t log_message >/dev/null 2>&1; then
- log_message "本次构建无Bug状态变更,跳过飞书版本汇总通知"
- fi
- fi
- if type -t log_message >/dev/null 2>&1; then
- log_message "=============== 禅道 Bug 追踪结束 ==============="
- fi
- }
- resolve_zentao_bugs() {
- local workspace_dir="${1:-${WORKSPACE:-.}}"
- local last_success_file="${workspace_dir}/.last_success_commit"
- if type -t log_message >/dev/null 2>&1; then
- log_message "=============== 禅道 Bug 自动解决开始 ==============="
- fi
- # 确定分析范围
- LAST_SUCCESS_HASH=$(cat "$last_success_file" 2>/dev/null || echo "")
- if [ -z "$LAST_SUCCESS_HASH" ]; then
- if type -t log_message >/dev/null 2>&1; then
- log_message "首次构建,分析最近 20 个包含 fix: 的提交"
- fi
- # 获取最近 20 个包含 fix: 的提交
- FIX_COMMITS=$(git log -20 --pretty=format:"%h|%an|%s" | grep -E "fix[:-]\s*[0-9]" || true)
- else
- if type -t log_message >/dev/null 2>&1; then
- log_message "上次成功构建: ${LAST_SUCCESS_HASH}"
- log_message "分析从 ${LAST_SUCCESS_HASH} 到 HEAD 的所有 fix: 提交"
- fi
- # 获取从上次成功构建到 HEAD 的所有包含 fix: 的提交
- FIX_COMMITS=$(git log ${LAST_SUCCESS_HASH}..HEAD --pretty=format:"%h|%an|%s" | grep -E "fix[:-]\s*[0-9]" || true)
- fi
- if [ -z "$FIX_COMMITS" ]; then
- if type -t log_message >/dev/null 2>&1; then
- log_message "未发现包含 Bug 修复标记 (fix:) 的提交"
- log_message "=============== 禅道 Bug 自动解决结束 ==============="
- fi
- return 0
- fi
- # 统计找到的提交数
- COMMIT_COUNT=$(echo "$FIX_COMMITS" | wc -l | tr -d ' ')
- if type -t log_message >/dev/null 2>&1; then
- log_message "找到 ${COMMIT_COUNT} 个包含 fix: 的提交"
- fi
- # 初始化 bug 映射数组(bug_id => 最新的提交信息)
- # 使用关联数组存储每个 bug 的最新提交(最上面的提交是最新的)
- declare -A BUG_MAP
- # 解析提交,提取 bug IDs(从新到旧的顺序)
- while IFS='|' read -r commit_hash author commit_message; do
- # 提取所有 bug IDs
- if echo "$commit_message" | grep -qE "fix[:-]\s*[0-9]"; then
- bug_ids=$(echo "$commit_message" | grep -oE "fix[:-]\s*([0-9]+(\s+[0-9]+)*)" | sed -E 's/fix[:-]\s*//')
- for bug_id in $bug_ids; do
- if [ -n "$bug_id" ]; then
- # 只在 bug 第一次出现时记录(即最新的提交)
- if [ -z "${BUG_MAP[$bug_id]}" ]; then
- BUG_MAP[$bug_id]="${commit_hash}|${author}|${commit_message}"
- if type -t log_message >/dev/null 2>&1; then
- log_message "发现 Bug #${bug_id}: [$commit_hash] $commit_message"
- fi
- fi
- fi
- done
- fi
- done <<<"$FIX_COMMITS"
- # 统计发现的 bug 数量
- BUG_COUNT=${#BUG_MAP[@]}
- if type -t log_message >/dev/null 2>&1; then
- log_message "共发现 ${BUG_COUNT} 个唯一的 Bug"
- fi
- if [ $BUG_COUNT -eq 0 ]; then
- if type -t log_message >/dev/null 2>&1; then
- log_message "未能提取到有效的 Bug ID"
- log_message "=============== 禅道 Bug 自动解决结束 ==============="
- fi
- return 0
- fi
- # 获取禅道 Token
- if type -t log_message >/dev/null 2>&1; then
- log_message "正在获取禅道 API Token..."
- fi
- ZENTAO_TOKEN_RESPONSE=$(curl -s -X POST "${ZENTAO_API_URL}/tokens" \
- -H "Content-Type: application/json" \
- -d "{\"account\":\"${ZENTAO_USERNAME}\",\"password\":\"${ZENTAO_PASSWORD}\"}")
- ZENTAO_TOKEN=$(echo "$ZENTAO_TOKEN_RESPONSE" | grep -o '"token":"[^"]*"' | cut -d'"' -f4)
- if [ -z "$ZENTAO_TOKEN" ]; then
- if type -t log_message >/dev/null 2>&1; then
- log_message "错误: 无法获取禅道 Token"
- log_message "响应: ${ZENTAO_TOKEN_RESPONSE}"
- log_message "=============== 禅道 Bug 自动解决结束 ==============="
- fi
- return 1
- fi
- if type -t log_message >/dev/null 2>&1; then
- log_message "成功获取禅道 Token"
- fi
- # 初始化统计计数器
- RESOLVED_COUNT=0
- SKIPPED_COUNT=0
- ERROR_COUNT=0
- # 处理每个 bug(按 bug ID 排序)
- for bug_id in $(echo "${!BUG_MAP[@]}" | tr ' ' '\n' | sort -n); do
- if type -t log_message >/dev/null 2>&1; then
- log_message "----------------------------------------"
- log_message "处理 Bug #${bug_id}"
- fi
- # 获取该 bug 的最新提交信息
- commit_info="${BUG_MAP[$bug_id]}"
- commit_hash=$(echo "$commit_info" | cut -d'|' -f1)
- author=$(echo "$commit_info" | cut -d'|' -f2)
- commit_message=$(echo "$commit_info" | cut -d'|' -f3)
- # 查询 bug 详情
- BUG_INFO=$(curl -s -X GET "${ZENTAO_API_URL}/bugs/${bug_id}" \
- -H "Token: ${ZENTAO_TOKEN}")
- # 使用 Python 解析 JSON
- BUG_STATUS=$(echo "$BUG_INFO" | python3 -c "import sys, json; data=json.load(sys.stdin); print(data.get('status', ''))" 2>/dev/null)
- BUG_TITLE=$(echo "$BUG_INFO" | python3 -c "import sys, json; data=json.load(sys.stdin); print(data.get('title', ''))" 2>/dev/null)
- BUG_OPENED_BY=$(echo "$BUG_INFO" | python3 -c "import sys, json; data=json.load(sys.stdin); openedBy=data.get('openedBy', {}); print(openedBy.get('account', '') if isinstance(openedBy, dict) else '')" 2>/dev/null)
- if [ -z "$BUG_STATUS" ]; then
- if type -t log_message >/dev/null 2>&1; then
- log_message "警告: 无法获取 Bug #${bug_id} 的状态信息"
- fi
- ERROR_COUNT=$((ERROR_COUNT + 1))
- continue
- fi
- if type -t log_message >/dev/null 2>&1; then
- log_message "Bug #${bug_id} 状态: ${BUG_STATUS}"
- log_message "Bug #${bug_id} 标题: ${BUG_TITLE}"
- log_message "Bug #${bug_id} 创建者: ${BUG_OPENED_BY}"
- log_message "最新修复提交: [${commit_hash}] ${author}"
- fi
- # 检查是否为未解决状态
- if [ "$BUG_STATUS" = "active" ] || [ "$BUG_STATUS" = "wait" ]; then
- if type -t log_message >/dev/null 2>&1; then
- log_message "Bug #${bug_id} 状态为未解决,准备解决..."
- fi
- # 准备 comment(只包含最新的提交)
- COMMENT="已修复该问题
- 相关提交:
- - [${commit_hash}] ${author}: ${commit_message}
- 代码详情: ${COMMIT_BASE_URL}${commit_hash}"
- # 生成解决 bug 的 JSON payload
- RESOLVE_JSON_FILE="/tmp/zentao_resolve_${bug_id}_$$.json"
- # 确定指派对象:优先使用创建者,如果获取失败则使用当前用户
- ASSIGNEE="${BUG_OPENED_BY}"
- if [ -z "$ASSIGNEE" ]; then
- if type -t log_message >/dev/null 2>&1; then
- log_message "警告: 无法获取 Bug #${bug_id} 创建者,将指派给当前用户 ${ZENTAO_USERNAME}"
- fi
- ASSIGNEE="${ZENTAO_USERNAME}"
- fi
- python3 -c "import json; print(json.dumps({
- 'resolution': 'fixed',
- 'resolvedBuild': 'trunk',
- 'resolvedDate': '$(date "+%Y-%m-%d %H:%M:%S")',
- 'assignedTo': '${ASSIGNEE}',
- 'comment': '''${COMMENT}'''
- }, ensure_ascii=False, indent=2))" >"$RESOLVE_JSON_FILE"
- # 调用 API 解决 bug
- RESOLVE_RESPONSE=$(curl -s -X POST "${ZENTAO_API_URL}/bugs/${bug_id}/resolve" \
- -H "Token: ${ZENTAO_TOKEN}" \
- -H "Content-Type: application/json" \
- -d @"$RESOLVE_JSON_FILE")
- # 清理临时文件
- rm -f "$RESOLVE_JSON_FILE"
- # 检查是否成功
- if echo "$RESOLVE_RESPONSE" | grep -q '"id"'; then
- if type -t log_message >/dev/null 2>&1; then
- log_message "✅ 成功解决 Bug #${bug_id}"
- fi
- RESOLVED_COUNT=$((RESOLVED_COUNT + 1))
- # 发送飞书通知(每个 bug 单独通知)
- BUG_URL="${ZENTAO_BUG_BASE_URL}${bug_id}.html"
- CODE_URL="${COMMIT_BASE_URL}${commit_hash}"
- COMMIT_SUMMARY="[${commit_hash}] ${commit_message}"
- send_bug_resolved_notification "${bug_id}" "${BUG_TITLE}" "${BUG_STATUS}" "${COMMIT_SUMMARY}" "${BUG_URL}" "${CODE_URL}"
- if type -t log_message >/dev/null 2>&1; then
- log_message "飞书通知已发送 (Bug #${bug_id})"
- fi
- else
- if type -t log_message >/dev/null 2>&1; then
- log_message "❌ 解决 Bug #${bug_id} 失败: ${RESOLVE_RESPONSE}"
- fi
- ERROR_COUNT=$((ERROR_COUNT + 1))
- fi
- elif [ "$BUG_STATUS" = "resolved" ] || [ "$BUG_STATUS" = "closed" ]; then
- if type -t log_message >/dev/null 2>&1; then
- log_message "Bug #${bug_id} 已经是 ${BUG_STATUS} 状态,跳过处理"
- fi
- SKIPPED_COUNT=$((SKIPPED_COUNT + 1))
- else
- if type -t log_message >/dev/null 2>&1; then
- log_message "Bug #${bug_id} 状态为 ${BUG_STATUS},跳过处理"
- fi
- SKIPPED_COUNT=$((SKIPPED_COUNT + 1))
- fi
- done
- if type -t log_message >/dev/null 2>&1; then
- log_message "=============== 禅道 Bug 自动解决完成 ==============="
- log_message "分析提交数: ${COMMIT_COUNT}"
- log_message "发现 Bug 数: ${BUG_COUNT}"
- log_message "已解决: ${RESOLVED_COUNT} 个"
- log_message "已跳过: ${SKIPPED_COUNT} 个"
- log_message "失败: ${ERROR_COUNT} 个"
- log_message "=============== 禅道 Bug 自动解决结束 ==============="
- fi
- return 0
- }
|