血泪史
最近在使用Jenkins拉取Git工程编译代码时候遇到一个很奇怪的的问题:Jenkins的GitPlugin下载代码的版本不对(commitId不对)。由于线上部署和线下部署的编译产物是同一版本,导致最后发布到生产环境的代码版本也不对。这个问题在线上验证阶段才最终被发现,回顾整个job构建过程,控制台没有报错,也成功编译出来了上线包,那到底是哪里出了问题?
初步定位
我最开始怀疑是本地Git工程残留的问题,于是尝试删除jenkins对应job所运行的机器节点上的WORKSPACE目录,保证下次触发Jenkins构建能拉取到最新代码。
删除方式:
1. 登陆到运行这个job的节点的机器(在控制台中查看这个job运行的节点,在第一行有打印)。
2. 查看$WORKSPACE(在job的控制台中查看;如果找不到,直接在shell executor中加一行echo $WORKSPACE,重新执行job)
3. 删除对应的WORKSPACE信息。请谨慎操作,先看清楚WORKSPACE的值对不对(echo $WORKSPACE),别搞错导致删除了根目录!!!。
[ -d "$WORKSPACE" ] && rm -rf ${WORKSPACE}
4.删除后重新触发job
这么操作后,最终打包出来的编译产物还是版本不对。
既然不是本地代码缓存问题,那有没有可能在GIt拉取代码的时候版本就不对呢?
二次排错
打开Jenkins上job的配置详情,我在BranchSpecifier这里只填写了"*/qa",我的预期是匹配git特定工程(Repository URL)中的qa分支。
然后看看之前有问题那次构建的记录(打开job当次构建的详情页),点击Git Build Data(左侧):
这里显示了两个remote的branch,我在GitPlugin里面只配置了一个qa分支,这里应该只有一个叫做qa的分支被下载才对,怎么会有两个分支呢?
我开始怀疑可能是在配置Git Plugin中BranchName的规则匹配问题,导致在这里匹配了两个分支(一个叫做qa,一个叫origin/qa)。
再次打开Jenkins上对应job的配置,我们看到在BranchSpecifier这里填的是"*/qa",*是一个通配符,会不会在这个工程下正好有两个branch,如上图中的(origin/qa和qa),正好匹配到了*/qa呢?
这么一想就有点解释得通了,于是我点击了Branches Specifier这一栏右侧的问号查看帮助,可以看到使用帮助信息。
详情分析
//下面这句话的意思是:如果你不填写branch specifier(留空或者写的是any),那么任意分支都会被匹配,也就是你无法工程使用的是哪个分支。一般不建议这么用。
Specify the branches if you'd like to track a specific branch in a repository. If left blank, all branches will be examined for changes and built.
//下面这句话的意思是:最安全的方式是使用refs/heads/<branchName>这种语法
The safest way is to use the refs/heads/<branchName> syntax. This way the expected branch is unambiguous.
//如果你的分支里面包含‘/’(例如叫origin/qa),最好就用上面提到的语法:refs/heads/<branchName>
If your branch name has a / in it make sure to use the full reference above. When not presented with a full path the plugin will only use the part of the string right of the last slash. Meaning foo/bar will actually match bar.
If you use a wildcard branch specifier, with a slash (e.g. release/), you'll need to specify the origin repository in the branch names to make sure changes are picked up. So e.g. origin/release/
Possible options:
- <branchName> //branchName只是个通配符,比如realese可以匹配release分支,也可能匹配origin/release,一般不要这么写(否则会和我一样踩同样的坑)
//由于指定<branchName>存在二义性,建议使用refs/heads/<branchName>来明确指定具体的远程分支。
Tracks/checks out the specified branch. If ambiguous the first result is taken, which is not necessarily the expected one. Better use refs/heads/<branchName>.
E.g. master, feature1,...
Tracks/checks out the specified branch. //checkout远程的某个分支
E.g. refs/heads/master, refs/heads/feature1/master,...
- <remoteRepoName>/<branchName>
Tracks/checks out the specified branch. If ambiguous the first result is taken, which is not necessarily the expected one.
Better use refs/heads/<branchName>. //这种指定远程具体分支的方法可能存在二义性,建议使用refs/heads/<branchName>
E.g. origin/master
- remotes/<remoteRepoName>/<branchName>
Tracks/checks out the specified branch. //这种指定方式也是比较精确的。
E.g. remotes/origin/master
- refs/remotes/<remoteRepoName>/<branchName>
Tracks/checks out the specified branch.
E.g. refs/remotes/origin/master //checkout指定的tag,实际上这里tagName不会被当做tag,不建议这么写。
This does not work since the tag will not be recognized as tag.
Use refs/tags/<tagName> instead. .. //checkout指定的tag,这才是正确的语法。
E.g. git-2.3.0
Tracks/checks out the specified tag.
E.g. refs/tags/git-2.3.0
Checks out the specified commit. //checkout指定的commitid
E.g. 5062ac843f2b947733e6a3b105977056821bd352, 5062ac84, ...
It is also possible to use environment variables. In this case the variables are evaluated and the result is used as described above.
E.g. ${TREEISH}, refs/tags/${TAGNAME},...
The syntax is of the form: REPOSITORYNAME/BRANCH. In addition, BRANCH is recognized as a shorthand of */BRANCH, '*' is recognized as a wildcard, and '**' is recognized as wildcard that includes the separator '/'. Therefore, origin/branches* would match origin/branches-foo but not origin/branches/foo, while origin/branches** would match both origin/branches-foo and origin/branches/foo.
The syntax is of the form: :regexp. Regular expression syntax in branches to build will only build those branches whose names match the regular expression.
Examples:
- matches: origin or origin/master or origin/feature
- does not match: origin/prefix or origin/prefix_123 or origin/prefix-abc
- matches: origin/release-20150101
- does not match: origin/release-2015010 or origin/release-201501011 or origin/release-20150101-something
- :^(?!origin/master$|origin/develop$).*
- matches: origin/branch1 or origin/branch-2 or origin/master123 or origin/develop-123
- does not match: origin/master or origin/develop
所以对于想要指定特定分支进行拉取,最好的四种没有二义性的写法是:
- refs/heads/<branchName>
- refs/remotes/<remoteRepoName>/<branchName>
- refs/tags/<tagName>
- <commitId>
到这里基本问题就真相大白了,我去gitlab上看了下这个工程的分支列表,发现还真有一个叫做origin/qa的分支,经过和其他同事的确认,有人手抖创建了这个origin/qa的分支。
我把原来的*/qa换成refs/heads/qa,这样就精确地匹配到了具体的分支。尝试再次触发job构建,这次下载的Git代码版本也正确了。
本文为原创踩坑史,如果对你有帮助,辛苦手下留赞,谢谢!
博主:测试生财(一个不为996而996的测开码农)
座右铭:专注测试开发与自动化运维,努力读书思考写作,为内卷的人生奠定财务自由。
内容范畴:技术提升,职场杂谈,事业发展,阅读写作,投资理财,健康人生。
csdn:https://blog.csdn.net/ccgshigao
博客园:https://www.cnblogs.com/qa-freeroad/
51cto:https://blog.51cto.com/14900374
微信公众号:测试生财(定期分享独家内容和资源)