Skip to content
On this page

title: Jenkins 构建失败自动分析 description:

Jenkins 构建失败自动分析(v1.5.0)

概述

当 Jenkins trigger job 构建失败时,自动完成:

  1. 解析构建日志,定位失败根因(第一个 FAILURE 的 sub-job)
  2. 获取编译服务器 IP、workspace 路径、错误文件
  3. SSH 到编译服务器,fetch 完整 git 历史,git log 找到引入错误的 commit
  4. git show 获取 commit 作者、邮箱、时间、message、Change-Id
  5. 发送企业微信 webhook 通知
  6. 输出结构化分析报告

触发条件

Jenkins trigger job 构建失败后,PostBuildScript 发 webhook → Hermes 自动执行分析。

新项目接入(无需修改 Hermes 配置)

新项目只需要在 Jenkins trigger job 里配置 PostBuildScript:

配置位置: Jenkins job → 配置 → 构建后操作 → PostBuildScript → 勾选「所有结果(Always)」

curl 命令(通用,任何项目都能用):

bash
HERMES_URL="http://192.168.100.206:8644/webhooks/jenkins-monitor"
curl -s -X POST "${HERMES_URL}" \
  -H "Content-Type: application/json" \
  -d "{\"job_name\":\"${JOB_NAME}\",\"build_number\":\"${BUILD_NUMBER}\",\"build_result\":\"${BUILD_RESULT}\",\"build_url\":\"${BUILD_URL}\",\"build_user\":\"${BUILD_USER:-unknown}\",\"project\":\"${J_PROJECT:-}\",\"branch\":\"${GIT_BRANCH:-}\",\"timestamp\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}"

路径信息自动从 consoleText 提取,Hermes 不需要任何配置修改:

  • 编译服务器 IP:从 sub-job consoleText 第一行 Building remotely on <IP> 提取
  • workspace 路径:从 sub-job consoleText 的 in workspace /path 提取
  • 代码子目录:从报错文件绝对路径中拆分

Webhook 传入参数

参数说明来源
job_nameJenkins job 名称${JOB_NAME}
build_number构建号${BUILD_NUMBER}
build_resultFAILURE / SUCCESS${BUILD_RESULT}
build_url构建 URL${BUILD_URL}
build_user触发者${BUILD_USER:-unknown}
project项目名(可选)${J_PROJECT:-}
branchGit 分支(可选)${GIT_BRANCH:-}
timestampISO 时间戳$(date -u +%Y-%m-%dT%H:%M:%SZ)

环境配置速查

配置项
Jenkins 地址http://192.168.100.207:8080
Jenkins 认证-u jenkins:ontim123!
Hermes Webhookhttp://192.168.100.206:8644/webhooks/jenkins-monitor
编译服务器 SSHontim / ontim123!,必须用 /usr/bin/python3
企业微信 Webhookhttps://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=806d31d0-352e-447a-90d9-d8a624ad589f

完整分析流程

第 1 步:获取 trigger consoleText,提取基础信息

bash
curl -s -k --noproxy '*' -u jenkins:ontim123! \
  "http://192.168.100.207:8080/job/{job_name}/{build_number}/consoleText"

提取字段:

字段搜索关键词示例
项目名J_Project=SM68B
发布版本J_RLSVer=25131
触发者Started by userzhangwanliang
触发时间console 头部时间戳Apr 29, 2026, 3:27 PM
构建节点Building remotely on192.168.100.157 (trigger)
分支名remotes/origin/b/sm6650/do_25131
第一个 FAILUREFinished Build.*FAILUREsm6650_bp → FAILURE

关键规则:第一个 FAILURE 的 sub-job 是根因,后续 ABORTED 是被牵连的。

trigger consoleText 示例(尾部):

Finished Build : [... Job: sm6650_bp] of Job : sm6650_bp with status : FAILURE at 15:35:31
Finished Build : [... Job: sm6650_sys_userdebug] of Job : sm6650_sys_userdebug with status : ABORTED at 15:35:33
Finished Build : [... Job: sm6650_vnd_userdebug] of Job : sm6650_vnd_userdebug with status : ABORTED at 15:35:33

第 2 步:获取 sub-job consoleText,获取编译服务器信息

bash
curl -s -k --noproxy '*' -u jenkins:ontim123! \
  "http://192.168.100.207:8080/job/{sub_job_name}/{sub_build_number}/consoleText"

sub-job consoleText 第一行包含关键信息:

Building remotely on 192.168.100.167 (amss) in workspace /home/ontim/BP_SPACE

⚠️ 编译服务器 IP 和 workspace 路径必须以 sub-job consoleText 为准,不要用记忆里的旧 IP。

第 3 步:从 sub-job consoleText 尾部提取编译错误

错误模式:

fatal error: 'xxx.h' file not found
make: *** [...path.../File.obj] Error 1
error 7000: Failed to execute command
error F002: Failed to build module
- Failed -

提取三个关键信息:

  1. 报错文件完整路径
  2. 错误类型(如 fatal error: 'Uefixx.h' file not found
  3. 行号

第 4 步:SSH 到编译服务器,fetch 完整历史 + git log

⚠️ 重要:构建时 repo 可能使用 --depth=1 shallow clone,直接 git log 只能看到 1-2 条历史。需要先 fetch 完整历史。

SSH 参数:

  • IP:从 sub-job consoleText 获取
  • 用户:ontim
  • 密码:ontim123!
  • Python:必须用 /usr/bin/python3
  • SSH 超时:10-30s,git fetch 超时:60-90s

Step 4a:获取分支名

从 sub-job consoleText 搜索 remotes/origin/ 获取当前分支。

Step 4b:定位正确的 git repo

⚠️ 关键陷阱:BP workspace 下有多个独立的 .git 仓库,路径不能搞错!

从报错文件绝对路径反推 git root:

/home/ontim/BP_SPACE/SM6650_do/BP/BOOT.MXF.2.1/boot_images/boot/QcomPkg/.../ChargerLibCommon.c

git root = /home/ontim/BP_SPACE/SM6650_do/BP/BOOT.MXF.2.1/boot_images.git 在 boot_images 目录下)

验证 git repo 是否正确:

python
stdin, stdout, stderr = client.exec_command(f'test -d "{git_root}/.git" && echo "GIT OK" || echo "NOT A REPO"')

Step 4c:fetch 完整历史

python
cmds = [
    f'test -d "{git_root}/.git" && echo "GIT_OK" || echo "NOT_GIT"',
    f"cd {git_root} && rm -f .git/index.lock",
    f"cd {git_root} && timeout 90 git fetch --depth=500 origin {branch}",
    f"cd {git_root} && git log --oneline | wc -l",
]

⚠️ 关键点:

  • 必须用具体分支名(如 b/sm6650/do_25131),不能用 HEAD
  • git fetch --depth=500git fetch --unshallow 快很多

Step 4d:git log 查找目标 commit

python
cmd = f"cd {git_root} && git log --oneline -10 -- {rel_filepath}"

如果文件 git log 为空(文件可能是生成的):

python
cmd = f"cd {git_root} && git log --oneline -10 -- {dirname}"

第 5 步:git show 获取 commit 详情

python
cmd = f"cd {git_root} && git show {commit_hash} --format='%H%n%an%n%ae%n%ad%n%s%n%B' -s"

输出格式:

9bf7c5c25077bd55c3853792ad445cdc24c0307c   ← 完整 commit hash
wanliang.zhang                                  ← 作者名
wanliang.zhang@chino-e.com                     ← 作者邮箱
Tue Feb 10 09:52:44 2026 +0800                 ← 提交时间
[Bsp][25131][Charger]...                       ← commit message
[Description]
Force charger related temp normal
Change-Id: I6b0789f498b1d97c204ac17a7bd230f066f04601

第 6 步:发送企业微信 Webhook 通知

python
import requests

webhook_url = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=806d31d0-352e-447a-90d9-d8a624ad589f"

content = f"""🚨 **构建失败分析**

**基本信息**
- Trigger:{job_name}
- 项目名:{j_project}
- 构建号:#{build_number}
- 触发者:{build_user}
- 触发时间:{trigger_time}
- Build URL:http://192.168.100.207:8080/job/{job_name}/{build_number}/

**失败范围**
{failed_list}

**编译错误**
- 报错文件:{error_file}
- 错误类型:{error_type}

**根因定位**
- Commit:{commit_hash}
- 作者:{author_name}{author_email}
- 提交时间:{commit_time}
- Commit Message:{commit_msg}

**修复建议**
{fix_suggestion}"""

payload = {"msgtype": "markdown", "markdown": {"content": content}}
r = requests.post(webhook_url, json=payload)
print(r.json())  # 期望 {"errcode": 0, "errmsg": "ok"}

第 7 步:输出结构化分析报告

🚨 **构建失败分析**

**基本信息**
- Trigger:SM68B_do_smt_trigger
- 项目名:SM68B
- 构建号:#283
- 触发者:zhangwanliang
- 触发时间:2026-04-29 15:27
- Build URL: http://192.168.100.207:8080/job/SM68B_do_smt_trigger/283/

**失败范围**
- sm6650_bp → FAILURE(根因)
- sm6650_sys_userdebug → ABORTED(被牵连)
- sm6650_vnd_userdebug → ABORTED(被牵连)

**编译错误**
- 报错文件:ChargerLibCommon.c 第 88 行
- 错误类型:fatal error: 'Uefixx.h' file not found

**根因定位**
- Commit:9bf7c5c25
- 作者:wanliang.zhang(wanliang.zhang@chino-e.com)
- 提交时间:2026-02-10 09:52
- Commit Message:[Bsp][25131][Charger]...

**修复建议**
将 ChargerLibCommon.c 第 88 行的 #include <Uefixx.h> 改回 #include <Uefi.h>,或确认该 include 是否应删除。

注意事项

  • 编译服务器 IP 以 sub-job consoleText 第一行 Building remotely on <IP> 为准
  • workspace 路径以 sub-job consoleText 中的 in workspace /path 为准
  • git fetch 必须用具体分支名,不能用 HEAD
  • 如果 git log 结果少于 10 条,说明 fetch 没生效,重新执行 Step 4c
  • 如果报错文件 git log 为空,对文件所在目录执行 git log
  • SSH 使用 Python paramiko,不要用 sshpass
  • Jenkins API 端口:trigger 和 sub-job 都用 192.168.100.207:8080
  • 企业微信 webhook errcode: 0 表示发送成功
  • 构建成功时(build_result=SUCCESS):只返回简短确认,不需要分析

常见问题处理

问题解决方案
git fetchCouldn't find remote ref HEAD直接用具体分支名,如 b/sm6650/do_25131
git fetchindex.lock 冲突rm -f .git/index.lock
git log 只有 1-2 条执行 git fetch --depth=500 origin <branch>
git 命令报 not a git repositorygit root 路径不对,需从报错文件路径反推 .git 所在目录
PostBuildScript funclib.sh 找不到工作目录导致相对路径解析失败,文件实际存在,不影响分析流程