◆

Jenkins 更新 Git submodule 报错 404 的解决办法

2023-02-02 01:370

项目里用 git submodule 管理公共组件库时,本地开发一切正常,一放到 jenkins 上构建就拉不下来。Jenkins 默认只 clone 主仓库,不会递归拉取子模块,所以第一步是在任务里显式打开子模块支持;打开之后我这边又撞上了 HTTP Code 404,最终原因是构建机上的 git 版本太老,不认省略 .git 后缀的仓库地址。

这篇按顺序记录两件事:Jenkins 侧的子模块配置怎么开,以及 404 报错怎么定位到 .gitmodules 上。

jenkins 配置 submodule

如图所示,打开 jenkins 对于 submodule 的配置

通过jenkins更新git submodule遇到的问题

这张图是 Jenkins 任务的 Git 插件配置区,重点看 Additional Behaviours 里新增的那条 Advanced sub-modules behaviours。勾上 Recursively update submodules 之后,Jenkins 在 checkout 阶段才会等价于执行 git submodule update --init --recursive;如果子模块本身又嵌套了子模块,这个递归选项是必需的。旁边的超时时间建议按仓库体积调大一点,子模块多的项目很容易在默认超时内拉不完而被判失败。

执行编译遇到的问题

但是当我们执行编译的时间,jenkins 拉取 submodule 代码出现如下报错。

通过jenkins更新git submodule遇到的问题

这张图是构建控制台的输出。要看的不是最上面那一大段 Java 异常堆栈,而是堆栈里被包裹的那行原始 git 命令的返回信息,也就是 HTTP Code 404。Jenkins 的 Git 插件只是把子进程的失败包装成了自己的异常,真正的失败原因始终在被执行的那条 git 命令的 stderr 里。

可以看到里面有个报错信息是 HTTP Code 404,说明压根没有找到此 submodule 地址。

这里有个容易走弯路的地方:404 和 403 表达的意思完全不同。403(或者要求输入用户名密码)是认证问题,得去检查凭据;404 是服务端明确回答"这个路径下没有仓库",说明请求的 URL 本身就不对。既然同样的地址在本地能拉通,那就是构建机在处理这个地址时和本地不一致——要么是地址被改写过,要么是客户端行为不同。

解决方法

服务器的 git 版本过低无法识别不带 .git 的仓库 url,将 .gitmodules 中的 url 加入 .git 结尾即可

通过jenkins更新git submodule遇到的问题

这张图是改完之后的 .gitmodules,看每个 [submodule] 段里 url 那一行的结尾,都补上了 .git。path 不用动,它是子模块在工作区里的落地目录,改了会导致已有的检出关系错乱。

之所以只在构建机上炸,是因为新版本的 git 在 URL 不带 .git 时会自己补一次再重试,老版本不做这个兜底,服务端直接按原样返回 404。所以本地和 CI 表现不同,本质是两端 git 版本行为不一致。先在构建机上确认一下版本:

git --version

改完 .gitmodules 之后别忘了让已有的检出跟着更新,.gitmodules 只是模板文件,实际生效的配置在 .git/config 里,需要重新同步一次:

git submodule sync --recursive
git submodule update --init --recursive

Jenkins 那边如果工作区里还留着上次失败的残留状态,最省事的做法是在任务里勾上 Clean before checkout,或者直接删掉该任务的 workspace 目录重新构建。删 workspace 属于不可恢复操作,动手前确认那个目录里没有只存在于构建机上的产物或临时文件。

顺手把根因也修掉

改 .gitmodules 是最快见效的办法,但它治的是症状。更彻底的做法是把构建机的 git 升级到较新的版本,顺带能解决一批老版本的其他坑(比如不支持 --recurse-submodules、部分 HTTP 认证方式失效)。如果构建机是团队共用的,升级前先和使用方对齐时间窗口,避免升级过程中有任务正在跑。

💬留言板0

还没有留言,来发表第一条吧~

PAGE LOAD / LIVEJenkins 更新 Git submodule 报错 404 的解决办法加载中