血淚史
最近在使用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
微信公眾號:測試生財(定期分享獨家內容和資源)

