← 返回主页
Jenkins CI/CD GitLab Webhook PowerShell 企业微信机器人

Jenkins 打包通知体系搭建实录:从 GitLab 推送 到 企业微信群消息,全程无人工

2026.09 · by Easin

一条 push 进去,五分钟后两个企业微信群分别收到带下载二维码的结果通知——这条链路上真正难的不是"打通",而是让每一次打的包都永远是对的。这篇是完整的搭建实录与踩坑手册(敏感信息已脱敏)。

一、体系总览

代码从 GitLab 推送开始,到两个企业微信群收到消息为止,全链路自动完成,平均耗时 4~5 分钟(编译约 4 分钟 + 上传通知约 1 分钟)。

开发者 push
   │
   ▼
GitLab(私有部署)
   │  ① Webhook(push 事件,全部分支)
   ▼
Jenkins(Windows 打包机,打包任务)
   │  ② 强制检出本次推送的确切提交
   │  ③ merge 提交 → 自动跳过打包
   │  ④ 按分支选 flavor → Gradle 编译
   │  ⑤ 产物落盘 D:\ApkOutput\build-N\{debug,release}
   ▼
上传 + 通知编排脚本
   ├─ ⑥ 上传蒲公英(debug→测试通道,release→正式通道)
   └─ ⑦ 企业微信消息
        ├─ 主打包群:所有消息(成功 + 失败)
        └─ 上线验收群:仅需求整合分支的出包成功消息

另有三个旁路机制保证"打的包永远是对的":

机制解决的问题
强制检出推送提交几秒内多人同时推送时,构建的 cause 和实际代码交叉错乱
merge 提交跳过merge 操作触发无意义的重复打包
同分支去重守护(每分钟定时任务)同分支连续多次 push 打出多个重复包,只保留最后一次

二、基础环境

系统用途凭据存放
Jenkins 2.580打包任务(FreeStyle,串行执行)专用账号,凭据存打包机
GitLab 私有部署代码仓库、webhook 配置具备 webhook 管理权限的账号
蒲公英APK 托管、二维码API Key 存本地密钥文件(严禁入库)
企业微信 AI 机器人群消息推送(MCP 接口)MCP 接口地址存本地密钥文件(严禁入库)

三、Jenkins 任务配置

FreeStyle 任务,串行执行(不勾选 Execute concurrent builds),三个 Batch 构建步骤:

触发器:只开 Webhook,千万别开轮询

三个构建步骤

:: 步骤一(准备):写 SDK 路径 + 强制检出本次推送的提交
chcp 65001 >nul
echo sdk.dir=C:/Users/<用户>/AppData/Local/Android/Sdk> local.properties
powershell -NoProfile -ExecutionPolicy Bypass -File D:\jenkins-setup\checkout-pushed.ps1
if errorlevel 1 (powershell ... upload-notify.ps1 -Failed & exit /b 1)

:: 步骤二(编译):开头两行是 merge 跳过
powershell ... is-merge-tip.ps1
if not errorlevel 1 (echo === SKIP: merge commit tip === & exit /b 0)
...按分支选 flavor,gradlew 打包,产物拷贝到 D:\ApkOutput\build-%BUILD_NUMBER%...
powershell ... prune-apkoutput.ps1 -Keep 20   :: 只保留最近 20 次产物

:: 步骤三(上传+通知)
chcp 65001 >nul
powershell -NoProfile -ExecutionPolicy Bypass -File D:\jenkins-setup\upload-notify.ps1

构建后设置两个关键项:

四、GitLab Webhook 打通(最容易踩坑的一段)

  1. 项目 webhook:URL 指向 Jenkins 任务地址,Secret token 与 Jenkins 触发器一致;触发事件仅 Push events,SSL 校验关闭;
  2. 本地网络放行(关键坑):GitLab 默认禁止向内网地址发 webhook。要在实例级 Settings → Network → Outbound requests 的 allowlist 中加入 Jenkins 的内网地址,否则 webhook 永远到不了 Jenkins——表现就是推了代码毫无反应;
  3. 注意 allowlist 的参数名是带 _raw 后缀的那个才是可写的;
  4. 验证顺序:先在 webhook 页点 Test → Push events 确认返回 200,再观察 Jenkins 出构建。

五、打包正确性三件套(本文核心)

5.1 强制检出推送提交

问题:Jenkins git 插件配通配分支时,检出的是"所有分支中最新 tip"。两人几秒内各推一次,会出现构建 A 的推送人/分支来自甲、实际检出代码却是乙的——消息全部错乱(历史事故:推送记录显示某同学,打包出来的却是别人的提交)。

方案:每个构建开始时,从本构建的 build.xml 里读取 GitLabWebHookCause 的 after(本次推送的最后一个提交 SHA),git checkout -f <sha> 强制对齐。于是打包人、分支、最新提交、merge 判断永远来自同一次推送。手动构建(无 webhook cause)不干预。

5.2 merge 提交跳过

git log -1 --pretty=%P HEAD 父提交数 >1 即 merge → 直接跳过编译、上传、通知(配合 allowEmptyArchive,构建安静地 SUCCESS)。业务规则:"merge 不要打包"。

5.3 同分支去重守护

需求:同分支多个 commit 一次 push 本来就只出一个包(webhook 的 after = 最后一个提交);但短时间多次 push 会出多个重复包,业务上只要最后一个。

实现(Windows 计划任务,SYSTEM 账户,每分钟):

  1. 用 scriptText Groovy 只读查询队列项的推送分支(坑:队列 REST API 的 cause 只有一个序列化不出来的空 data:{},分支信息拿不到,只能走服务端脚本);
  2. 队列里同分支多个待构建 → 先取消旧的(POST /queue/cancelItem?id=N——注意不是 /queue/item/N/cancel,那个在本版本返回 404),只留最新;
  3. 再判断:运行中的构建若被同分支新推送取代 → POST /job/xxx/<N>/stop(顺序不能反!先 stop 会让旧队列项立刻出队复活);
  4. Fail-safe:识别不出推送来源的构建/队列项一律不碰;跨分支互不影响;动作全记录审计日志。

边界:旧构建已进入上传的最后一分钟时新推送才到,旧包仍可能上传(约 1 分钟窗口,正常推送间隔都能拦住)。

六、蒲公英上传:build 号的口径坑

七、企业微信消息通道

7.1 通道原理

机器人是企业微信 AI 机器人,对外提供 MCP 接口(streamable-http),脚本直接 POST tools/call:sessions_list 发现群 chat_id,send 以 markdown 格式发消息。

7.2 双群路由:配置文件即路由表

配置文件作用投递规则
wecom-main-group.txt主打包群 chat_id所有成功 + 失败消息
wecom-accept-group.txt上线验收群 chat_id仅需求整合分支 + 打包成功
wecom-dm-userid.txt私聊兜底无群会话时用

固定 chat_id 的另一个好处:机器人加入多个群后,不会因为"取最近会话"而发错群。

7.3 换群 / 关群

7.4 消息格式(示例)

### ✅ APK 打包完成,已上传蒲公英,请尽快测试验证
**打包人**: ×××  **分支**: main_dev_xx  **构建号**: #60
> **[debug] 测试通道** v1.x.x · [蒲公英build #18xx](下载页链接) · 41.2MB
[点击下载](链接)  [二维码](链接)
> **[release] 正式通道** v1.x.x · [蒲公英build #18xx](下载页链接) · 38.9MB
**最新提交**: `a1b2c3d` ×××:提交说明……

### ❌ APK 打包失败
**打包人**: ×××  **分支**: ×××  **构建号**: #N
请相关同学检查代码,[点击查看构建日志](链接)

字段来源的可靠性设计:

八、运维 SOP:改 Jenkins 配置的三个坑

通过 REST API 提交 config.xml,三件事缺一不可:

# 1. 取 crumb(xpath 方式的 crumbIssuer 已被禁用,必须用 JSON)
curl -s -c cookies.txt -u <user>:<pass> -o crumb.json \
     http://<jenkins>:8080/crumbIssuer/api/json
# 2. 提交配置(必须带 charset,否则中文配置按 ISO-8859-1 解码 → 乱码写坏线上配置!)
curl -s -b cookies.txt -u <user>:<pass> -H "Jenkins-Crumb:<crumb>" \
     -H "Content-Type: text/xml; charset=utf-8" \
     --data-binary @new-config.xml -X POST \
     http://<jenkins>:8080/job/<job>/config.xml

PowerShell 5.1 守则

验证纪律

九、踩坑速查表

坑症状解法
GitLab 禁止内网 webhook推代码无反应实例级 allowlist 加 Jenkins 地址
config.xml 提交 500HTTP 500XML 声明 1.1 → 1.0
提交后中文分支乱码main_dev_???POST 必须带 charset=utf-8
crumb 接口 403primitive XPath forbidden改用 /crumbIssuer/api/json
队列 REST 拿不到推送分支cause 的 data 为空scriptText Groovy 服务端查询
队列取消 404cancel 不生效用 POST /queue/cancelItem?id=N
先 stop 后 cancel旧队列项复活成新构建先去重队列,再停运行中构建
无产物构建 FAILUREmerge 跳过被标红ArtifactArchiver 勾 allowEmptyArchive
蒲公英 build 号错误显示应用内序号用 buildBuildVersion 字段
轮询引发老分支补打蒲公英出现老版本关闭 Poll SCM
中文 .ps1 语法错误莫名解析失败UTF-8 带 BOM 保存
双 push 交叉错乱打包人/分支/提交对不上强制检出推送提交

十、快速上手清单(接手的人照此走一遍)

  1. 推一条提交到任一需求分支 → 约 5 分钟内主群收到 ✅ 消息,蒲公英出现新 build,消息里打包人/分支/提交一致;
  2. 推一条需求整合分支 → 主群收到双包消息 + 上线验收群收到同款消息;
  3. 在 GitLab 上做一次普通 merge 到该分支 → Jenkins 构建显示 SUCCESS 但无消息(跳过);
  4. 同一分支 1 分钟内连推两次 → 只出一个包(旧构建 ABORTED,见 dedup 日志);
  5. 看一次历史失败构建 → 主群收到 ❌ 消息,验收群无消息。

以上五条全过,说明体系各环节运转正常。

Easin's Code Lab · 作者:Easin ← 返回主页 · 上一篇:Harness Engineering · GitHub ↗