概述/背景
項(xiàng)目在開發(fā)過(guò)程中的版本,和在發(fā)布時(shí)的版本應(yīng)該有所區(qū)別。發(fā)布的版本應(yīng)該保證一個(gè)版本號(hào)對(duì)應(yīng)唯一的內(nèi)容。
之前遇到過(guò)一個(gè)項(xiàng)目,因?yàn)閜om中的版本號(hào)沒(méi)有-SNAPSHOT
,導(dǎo)致兩個(gè)環(huán)境依賴的項(xiàng)目雖然版本號(hào)相同,其實(shí)不是同一個(gè)。當(dāng)時(shí)得出結(jié)論:相同的版本號(hào)只應(yīng)該對(duì)應(yīng)一個(gè)內(nèi)容。
后來(lái)系統(tǒng)學(xué)習(xí)了下Maven,才具體了解到Maven體系中關(guān)于項(xiàng)目版本的標(biāo)準(zhǔn),這里分享出來(lái),希望大家在維護(hù)項(xiàng)目版本的時(shí)候有據(jù)可依。
另外,Maven中的版本管理需要結(jié)合軟件版本控制工具使用,如Git、SVN等。雖然目前Git的使用已經(jīng)很普遍了,我的工作環(huán)境使用的是SVN,就以SVN為例進(jìn)行介紹。
Maven范圍的版本和SVN范圍的版本有不同的含義。前者面向的是項(xiàng)目,代碼多次修改,項(xiàng)目的版本號(hào)可以不變;后者面向的是代碼,每一次修改都是一個(gè)不同的版本。下文所說(shuō)的版本一般是指項(xiàng)目的版本,特殊情況下會(huì)說(shuō)明是代碼版本。
快照版本和發(fā)布版本
快照版本對(duì)應(yīng)開發(fā)過(guò)程中的版本,特點(diǎn)是快速的迭代更新,同樣的版本號(hào)對(duì)應(yīng)的是項(xiàng)目在一段時(shí)間內(nèi)的開發(fā)過(guò)程。
發(fā)布版本應(yīng)該是一個(gè)穩(wěn)定的版本,它應(yīng)該對(duì)應(yīng)項(xiàng)目在某個(gè)時(shí)刻的狀態(tài) —— 它對(duì)應(yīng)唯一的代碼版本。
快照版本使用-SNAPSHOT
后綴。
在Maven體系中的區(qū)別
Maven解析依賴的一般機(jī)制是:先從本地倉(cāng)庫(kù)找,找不到再?gòu)倪h(yuǎn)程倉(cāng)庫(kù)找。換言之,在本地倉(cāng)庫(kù)找到了,就不會(huì)再去找遠(yuǎn)程倉(cāng)庫(kù)。
如果有2個(gè)項(xiàng)目并行開發(fā),其中一個(gè)項(xiàng)目B依賴于另一個(gè)項(xiàng)目A。想象一下A和B都是持續(xù)更新的,項(xiàng)目B的開發(fā)人員如何確保能實(shí)時(shí)獲取項(xiàng)目A的最新內(nèi)容?
方案一:項(xiàng)目B的開發(fā)人員,每次構(gòu)建之前都手動(dòng)從本地倉(cāng)庫(kù)刪除項(xiàng)目A,這樣構(gòu)建的時(shí)候就會(huì)從遠(yuǎn)程倉(cāng)庫(kù)下載最新的A。
方案二:項(xiàng)目B的開發(fā)人員,遷出項(xiàng)目A的代碼,自己將A項(xiàng)目安裝到本地倉(cāng)庫(kù)。
這兩個(gè)個(gè)方案都不是Maven體系推薦的做法,方案二讓項(xiàng)目B的開發(fā)人員不得不去搭建項(xiàng)目A的開發(fā)環(huán)境,而且項(xiàng)目A如果出現(xiàn)代碼問(wèn)題,項(xiàng)目B的開發(fā)人員可能就無(wú)能為力了。方案一增加了需要人工介入的工作量。
Maven使用SNAPSHOT
來(lái)解決這個(gè)問(wèn)題。如果項(xiàng)目依賴的是一個(gè)SNAPSHOT版本的依賴,Maven會(huì)定期從遠(yuǎn)程倉(cāng)庫(kù)獲取最新的內(nèi)容,默認(rèn)頻率是一天一次。也可以使用-U參數(shù)來(lái)強(qiáng)制更新。
所以,項(xiàng)目A使用SNAPSHOT版本,項(xiàng)目B就可以定期或者實(shí)時(shí)獲取項(xiàng)目A的最新內(nèi)容。
反之,如果項(xiàng)目A沒(méi)有使用SNAPSHOT
,項(xiàng)目B在第一次從遠(yuǎn)程倉(cāng)庫(kù)下載項(xiàng)目A到本地倉(cāng)庫(kù)之后,就一直使用本地倉(cāng)庫(kù)的這個(gè)備份。即使項(xiàng)目A更新了,項(xiàng)目B也不會(huì)知道,除非人工刪除了項(xiàng)目A在本地倉(cāng)庫(kù)的副本。
所以,開發(fā)過(guò)程中的版本,應(yīng)該使用SNAPSHOT
版本。
發(fā)布版本的版本號(hào)約定
一般的情況下是下面這種結(jié)構(gòu):
<主版本>.<次版本>.<增量版本> 比如 1.1.2
各版本的作用如下:
- 主版本
項(xiàng)目的重大架構(gòu)變更 - 次版本
較大范圍的功能變化 - 增量版本
大量或緊急bug修復(fù)
這只是一個(gè)粗略的劃分,大家可以根據(jù)自己的情況討論每一個(gè)版本的使用場(chǎng)景。
通常主版本、次版本都會(huì)有,增量版本視情況而定。
更進(jìn)一步的情況,可以添加里程碑版本,版本的結(jié)構(gòu)如下:
<主版本>.<次版本>.<增量版本>—<里程碑版本> 如 3.1.2-alpha-1
里程碑版本表示達(dá)成項(xiàng)目的某個(gè)里程碑,但通常不夠穩(wěn)定。大部分的公司應(yīng)該不會(huì)使用里程碑版本,在此略過(guò)不表。
版本維護(hù)流程
主干、標(biāo)簽和分支
- 主干
無(wú)需多說(shuō) - 標(biāo)簽
標(biāo)記某個(gè)有意義的代碼版本,每一個(gè)發(fā)布版本都應(yīng)該打一個(gè)標(biāo)簽。 - 分支
用于并行開發(fā),通常用于已有版本的bug修復(fù)。
流程demo
- 在主干進(jìn)行1.0.0-SNAPSHOT的開發(fā),開發(fā)完成之后提交修改的代碼到主干。修改版本為1.0.0,再次提交,并添加標(biāo)簽。
- 隨后將版本改為1.1.0-SNAPSHOT,提交。然后開始新一輪的開發(fā),完成之后提交代碼,修改版本為1.1.0,再次提交并打標(biāo)簽。
- 接著將版本號(hào)改為1.2.0-SNAPSHOT,提交,并開始新一輪的開發(fā),在開發(fā)過(guò)程中1.1.0遇到一個(gè)緊急的bug,新建分支并1.1.1-SNAPSHOT,修改版本號(hào)為1.1.1-SNAPSHOT,提交并開始1.1.1-SNAPSHOT的開發(fā)。
- 1.1.1-SNAPSHOT開發(fā)完成,提交代碼。修改版本號(hào)至1.1.1,提交并打上標(biāo)簽。此時(shí)可以及時(shí)把1.1.1發(fā)布出去。同步分支到主干。
- 隨后,1.2.0-SNAPSHOT開發(fā)完成..
版本號(hào)的修改應(yīng)該作為一次單獨(dú)的提交。在發(fā)布的時(shí)候,項(xiàng)目版本號(hào)初始為SNAPSHOT版本,提交完所有的代碼之后,修改版本號(hào)為發(fā)布版本,再提交;這次提交只修改版本號(hào)。
在開始新一輪的開發(fā)過(guò)程時(shí),修改版本號(hào)為新的SNAPSHOT,然后立即提交而不是修改代碼,版本號(hào)提交之后再開始開發(fā),這次提交只修改版本號(hào)。
SVN相關(guān)操作
SVN簡(jiǎn)單介紹
SVN是一個(gè)很簡(jiǎn)單的版本控制工具,你可以下載VisualSVN Server來(lái)搭建自己的SVN服務(wù)器。
SVN的代碼主要是在倉(cāng)庫(kù)里管理的,如上圖demo就是一個(gè)倉(cāng)庫(kù)。完整的倉(cāng)庫(kù)目錄結(jié)構(gòu)如下:
倉(cāng)庫(kù)目錄(Repositories)
|-- 倉(cāng)庫(kù)1
|-- 項(xiàng)目1
|-- 主干(trunk)
|-- 項(xiàng)目代碼
|-- 標(biāo)簽?zāi)夸洠╰ags)
|-- 標(biāo)簽1
|-- 項(xiàng)目代碼
|-- 分支(branches)
|-- 分支1
|-- 項(xiàng)目代碼
可以看到,SVN的每一個(gè)標(biāo)簽或者分支,都是一份完整的代碼副本。
與倉(cāng)庫(kù)同一個(gè)級(jí)別的是用戶等其他,可以控制對(duì)倉(cāng)庫(kù)的讀寫權(quán)限,具體沒(méi)有深入研究,大家有需要自己研究下。
SVN操作
這里介紹上文版本維護(hù)流程中涉及到的操作。這里以命令的方式介紹,使用客戶端界面的自己找一下操作的位置。
初始化項(xiàng)目
初始化項(xiàng)目首先要在VisulSVN Server的具體倉(cāng)庫(kù)下新建一個(gè)項(xiàng)目結(jié)構(gòu):
倉(cāng)庫(kù)-> 右鍵 -> 新建 –> Project Structure …
之后會(huì)生成一個(gè)標(biāo)準(zhǔn)的項(xiàng)目結(jié)構(gòu),包含了trunk、branches、tags,沒(méi)有代碼。
獲取trunk的url地址,通過(guò)trunk -> 右鍵 -> Copy URL…
在本地項(xiàng)目所在目錄,輸入以下命令:
svn import -m "initial import" . https://PC-LIJINLONG9.hikvision.com:444/svn/demo/demo-version/trunk
添加標(biāo)簽
版本號(hào)修改為發(fā)布版本之后,應(yīng)該立即對(duì)當(dāng)前狀態(tài)添加一個(gè)標(biāo)簽:
svn copy https://PC-LIJINLONG9.hikvision.com:444/svn/demo/demo-version/trunk https://PC-LIJINLONG9.hikvision.com:444/svn/demo/demo-version/tags/1.0-RELEASE -m "tag 1.0-RELEASE"
創(chuàng)建分支
svn copy https://PC-LIJINLONG9.hikvision.com:444/svn/demo/demo-version/tags/1.0-RELEASE https://PC-LIJINLONG9.hikvision.com:444/svn/demo/demo-version/branches/1.0.1-SNAPSHOT -m "create branch 1.0.1-SNAPSHOT"
可以看到,添加標(biāo)簽和創(chuàng)建分支本質(zhì)上都是復(fù)制。
切換分支
svn switch https://PC-LIJINLONG9.hikvision.com:444/svn/demo/demo-version/branches/1.0.1-SNAPSHOT
合并分支
svn merge https://PC-LIJINLONG9.hikvision.com:444/svn/demo/demo-version/branches/1.0.1-SNAPSHOT
合并分支的時(shí)候,pom文件中的版本號(hào)會(huì)有沖突。一般選擇使用當(dāng)前的版本號(hào)。
Maven的release插件
在使用自動(dòng)化的插件之前,建議先手動(dòng)執(zhí)行上面的流程,熟悉版本維護(hù)的流程之后,再去使用自動(dòng)化插件。
配置pom文件
pom文件需要添加scm配置和插件配置。scm是Software configuration management的縮寫。
<project>
...
<scm>
<connection>scm:svn:https://PC-LIJINLONG9.hikvision.com:444/svn/demo/demo-version/trunk</connection>
<developerConnection>scm:svn:https://PC-LIJINLONG9.hikvision.com:444/svn/demo/demo-version/trunk</developerConnection>
<url>https://PC-LIJINLONG9.hikvision.com:444/svn/demo/demo-version/trunk</url>
</scm>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<version>2.5.3</version>
<configuration>
<tagBase>https://PC-LIJINLONG9.hikvision.com:444/svn/demo/demo-version/tags</tagBase>
<branchBase>https://PC-LIJINLONG9.hikvision.com:444/svn/demo/demo-version/branches</branchBase>
</configuration>
</plugin>
</plugins>
</build>
</project>
scm中配置了trunk的url,connection和developerConnection的值都以scm開頭,之后跟著版本控制工具的類型svn。除了svn之外,Maven還支持其他的工具,如git。
插件里配置了標(biāo)簽的url和分支的url,其實(shí)如果項(xiàng)目的結(jié)構(gòu)是標(biāo)準(zhǔn)的,trunk、tags和branches在同一級(jí),可以不用配置這兩項(xiàng)。
配置完之后,就可以通過(guò)mvn命令來(lái)自動(dòng)發(fā)布、打標(biāo)簽、創(chuàng)建分支了。
自動(dòng)發(fā)布
插件把發(fā)布分為2步,第1步執(zhí)行上文中版本維護(hù)流程中介紹的發(fā)布流程,第2步使用mvn deploy
部署構(gòu)建至倉(cāng)庫(kù)。第1步由插件的prepare
來(lái)完成,第2步由插件的perform
目標(biāo)來(lái)完成。
如果prepare
執(zhí)行失敗,可以通過(guò)rollback
目標(biāo)來(lái)回滾,但是prepare
生成的標(biāo)簽需要手動(dòng)刪除。
release:prepare
prepare負(fù)責(zé)預(yù)發(fā)布,在prepare之前,需要確保所有的修改都已經(jīng)提交了,而當(dāng)前的pom文件中的版本號(hào)還沒(méi)有升級(jí)為發(fā)布版本號(hào)。
prepare所做的工作有:
- 檢查系統(tǒng)是否有未提交的代碼
- 詢問(wèn)用戶發(fā)布版本號(hào)、標(biāo)簽和下一個(gè)開發(fā)版本號(hào),并提供默認(rèn)值。
What is the release version for "demo-version"? (com.hikvision.demo:demo-version) 1.2: : What is SCM release tag or label for "demo-version"? (com.hikvision.demo:demo-version) demo-version-1.2: : What is the new development version for "demo-version"? (com.hikvision.demo:demo-version) 1.3-SNAPSHOT: :
- 修改pom-根據(jù)用戶的輸入,將快照版本升級(jí)為發(fā)布版本
- 修改pom-根據(jù)用戶的輸入,將scm中的地址改為標(biāo)簽對(duì)應(yīng)的地址
- 執(zhí)行Maven構(gòu)建
- 提交pom變更
- 根據(jù)用戶的輸入,新建標(biāo)簽
- 修改pom-根據(jù)用戶的輸入將version升級(jí)為新的快照版本,將scm信息更新。
- 提交pom變更。
上述的內(nèi)容,正是我們?cè)谏?jí)項(xiàng)目版本需要做的事情。
使用命令:mvn release:prepare
。
release:rollback
回退release:prepare
的所執(zhí)行的操作,并提交。但是歷史記錄會(huì)保留,創(chuàng)建的標(biāo)簽頁(yè)不會(huì)刪除。
使用命令:mvn release:rollback
。
release:perform
執(zhí)行版本發(fā)布。是基于發(fā)布版本標(biāo)簽地址所對(duì)應(yīng)的代碼進(jìn)行mvn deploy的結(jié)果。
使用命令:mvn release:perform
。
release:branch
自動(dòng)創(chuàng)建分支。
使用命令:
mvn release:branch -DbranchName=1.1.1 -DupdateBranchVersions=true -DupdateWorkingCopyVersions=false
參考
- 《Maven實(shí)戰(zhàn)》 - 許曉斌