Git Hooks如何仅在提交仓库特定文件夹内容时触发Jenkins任务?
Git钩子按文件夹判断触发逻辑的实操方案
你要实现的核心逻辑非常直接:钩子执行时先拿到本次提交/推送涉及的所有改动文件路径,只要有任意一个文件属于指定的触发文件夹,就执行原来的Jenkins触发逻辑;如果所有改动都属于另一个不需要触发的文件夹,就直接退出流程,不发起构建。
第一步:在钩子环境中获取所有改动文件的路径
Git钩子的执行环境是完全隔离的,最稳定的获取文件列表的方式是用git diff-tree命令,拿到的路径都是相对仓库根目录的纯路径,不存在额外的路径拼接问题,适配所有钩子场景:
- 如果你用的是Bitbucket服务器端的
post-receive类钩子(收到git push请求后自动执行):这类钩子会从标准输入逐行读取本次推送对应的老commit hash、新commit hash、分支名,可以配合循环拿到本次推送全量的改动文件列表:
命令参数说明:while read oldrev newrev refname; do # 输出两个版本之间所有改动的文件路径,每行一个 git diff-tree --no-commit-id --name-only -r $newrev done--no-commit-id:隐藏输出开头的commit hash,仅保留文件路径--name-only:仅输出纯文件路径,不携带文件改动的具体内容-r:递归遍历所有子目录下的改动文件
- 如果你用的是本地电脑上的
post-commit类钩子(本地完成commit后触发):只需要把上面的diff命令参数换成当前最新的commit hash即可,即git diff-tree --no-commit-id --name-only -r HEAD,后续判断逻辑完全一致。
第二步:添加路径匹配的判断逻辑
假设你需要触发Jenkins的目标文件夹是service/,另一个docs/文件夹的改动不需要触发,按照下面的结构改你的现有脚本即可:
# 定义需要触发构建的目标文件夹路径(末尾记得加斜杠,避免同前缀文件夹误判) TRIGGER_FOLDER="service/" # 触发标记位,默认状态为不触发 NEED_TRIGGER=0 while read oldrev newrev refname; do # 遍历本次推送的所有改动文件 for changed_file in $(git diff-tree --no-commit-id --name-only -r $newrev); do # 判断当前改动文件是否属于目标文件夹 if [[ "$changed_file" == "$TRIGGER_FOLDER"* ]]; then NEED_TRIGGER=1 # 匹配到目标文件后直接跳出循环,不需要继续遍历剩余文件 break fi done done # 根据标记位执行后续逻辑 if [ "$NEED_TRIGGER" -eq 1 ]; then # 把你原来写好的Jenkins触发代码放到这里(比如调用Jenkins构建接口的curl命令) echo "检测到目标文件夹改动,启动Jenkins构建流程" else echo "本次提交无目标文件夹改动,跳过构建流程" exit 0 fi
新手易踩的注意事项
- 路径匹配时一定要在目标文件夹名末尾加斜杠:如果不带斜杠,比如把触发文件夹定义成
service,会把service-backup/这类同前缀的其他文件夹的改动误判为需要触发 - 不需要在脚本里额外拼接路径:Git钩子执行时默认的工作目录就是仓库根目录,拿到的文件路径已经是可以直接匹配的相对路径,手动拼接路径反而容易出错
- 新建分支的推送场景不需要额外处理:哪怕是第一次推全新的空分支,
git diff-tree命令也能正常拿到分支下所有文件的路径,不会出现报错 - 脚本权限问题:如果是部署在Bitbucket服务器上的钩子,记得给脚本加可执行权限,运行
chmod +x 钩子文件路径即可,不然脚本会无法执行
内容的提问来源于stack exchange,提问作者Jessica Grahm
相关产品推荐
相关产品推荐

