Maven最佳实践:版本管理

什么是版本管理

首先,这里说的版本管理(version management)不是指版本控制(version control),可是本文假设你拥有基本的版本控制的知识,了解subversion的基本用法。版本管理中说得版本是指构件(artifact)的版本,而非源码的版本(如subversion中常见的rXXX,或者git中一次提交都有个sha1的commit号)。git

好比我有一个项目,其artifactId为myapp,随着项目的进展,咱们会生成这样一些jar:myapp-1.0-SNAPSHOT.jar,myapp-1.0.jar,myapp-1.1-SNAPSHOT.jar,myapp-1.0.1.jar等等。你可能会说,这很简单啊,我在POM中改个version,mvn clean install不就完了?但这只是表面,本文我将讲述,snapshot和release版本的区别,如何自动化版本发布(若是你的项目有几十个module,你就会以为手工改POM来升级版本是很痛苦的事情),结合自动化发布的过程,这里还会介绍maven-release-plugin。此外,一些scm概念也会被涉及到,好比tag和branch。程序员

 

前提:版本控制

无论怎样,咱们都须要创建一个项目并提交到SCM中,这里我以subversion为例。你得有一个配置好的subversion repository,这里我创建了一个空的svn仓库,其地址为:https://192.168.1.100:8443/svn/myapp/ 如今,该目录下只有三个空的典型的子目录:/trunk/, branches/, tags/。分别用来存放主干,分支,以及标签。apache

接着将项目导入到svn仓库中,到项目根目录,运行以下命令:windows

svn import -m 'project initialization' https://192.168.1.100:8443/svn/myapp/trunk 
(注意,这么作你会将目录下全部文件导入到svn库中,可是这其中某些目录和文件是不该该被导入的,如/target目录,以及eclipse相关的项目文件)架构

目前,咱们将项目的版本设置为1.0-SNAPSHOT。app

 

为何用SNAPSHOT?

我先说说若是没有SNAPSHOT会是什么样子。假设你的项目有2个模块,A,B,其中A依赖B。这三个模块分别由甲,乙两个我的负责开发。在开发过程当中,由于A是依赖于B的,所以乙每次作一个改动都会影响到甲,因而,乙提交了一些更改后,须要让甲看到。这个时候,怎么作呢?乙对甲说,“你签出个人代码,build一下就OK了”,甲有点不情愿,但仍是照作了,签出代码,svn clean install,而后,发现build出错了,有个测试没有pass。甲郁闷了,对乙说,“你的代码根本不能用,我不想build,你build好了给我”,乙看了看确实本身的代码build不过,因而回去解决了,而后打了个jar包,扔给甲,甲对了对groupId,artifactId,放到了本身的.m2/repository/目录下,OK,能用了。eclipse

因而乙每次更新都这样作,打包,复制,而后甲粘贴,使用……渐渐的,你们发现这是个很笨的办法,这是纯手工劳动阿,程序员最BS的就是重复劳动。一天,甲对乙说,“你知道nexus么?你把你的jar发布到nexus上就能够了,我要用就自动去下载,这多棒!”乙说“哦?有这好东西,我去看看”因而乙发现了nexus这块新大陆,并成功的发布了B到nexus上。(见,Nexus入门指南,(图文) )。maven

可是,请注意,咱们这里的一切都假设没有SNAPSHOT,所以若是乙不更改版本,甲下载一次如B-1.0.jar以后,maven认为它已经有了正确的B的版本,就不会再从新下载。甲发现了这个问题,对乙说“你的更新我看不到,你更新了么?”乙说“不可能!我看看”,因而检查一下甲下载的C-1.0.jar,发现那是几天前的。乙一拍脑壳,说“这简单,我更新一下个人版本就行了,我发布个B-1.1.jar上去,你更新下依赖版本”,甲照作了,彷佛这么作是可行的。svn

这里有一个问题,一次提交就更新一个版本,这明显不是正确的管理办法,此外,乙得不停的通知甲更新对B的依赖版本,累不累阿?1.0,或者说1.1,2.0,都表明了稳定,这样随随便便的改版本,能稳定么?测试

因此Maven有SNAPSHOT版本的概念,它与release版本对应,后者是指1.0,1.1,2.0这样稳定的发布版本。

如今乙能够将B的版本设置成1.0-SNAPSHOT,每次更改后,都mvn deploy到nexus中,每次deploy,maven都会将SNAPSHOT改为一个当前时间的timestamp,好比B-1.0-SNAPSHOT.jar到nexus中后,会成为这个样子:B-1.0-20081017-020325-13.jar。Maven在处理A中对于B的SNAPSHOT依赖时,会根据这样的timestamp下载最新的jar,默认Maven天天 更新一次,若是你想让Maven强制更新,可使用-U参数,如:mvn clean install -U 。

如今事情简化成了这个样子:乙作更改,而后mvn deploy,甲用最简单的maven命令就能获得最新的B。

 

从1.0-SNAPSHOT到1.0到1.1-SNAPSHOT

SNAPSHOT是快照的意思,项目到一个阶段后,就须要发布一个正式的版本(release版本)。一次正式的发布须要这样一些工做:

  1. 在trunk中,更新pom版本从1.0-SNAPSHOT到1.0
  2. 对1.0打一个svn tag
  3. 针对tag进行mvn deploy,发布正式版本
  4. 更新trunk从1.0到1.1-SNAPSHOT

你能够手工一步步的作这些事情,无非就是一些svn操做,一些pom编辑,还有一些mvn操做。可是你应该明白,手工作这些事情,一来繁琐,而来容易出错。所以这里我介绍使用maven插件来自动化这一系列动做。

SCM

首先咱们须要在POM中加入scm信息,这样Maven才可以替你完成svn操做,这里个人配置以下:

Xml代码   收藏代码
  1. <scm>  
  2.   <connection>scm:svn:http://192.168.1.100:8443/svn/myapp/trunk/</connection>  
  3.   <developerConnection>scm:svn:https://192.168.1.100:8443/svn/myapp/trunk/</developerConnection>  
  4. </scm>  

须要注意的是,不少windows使用的tortoiseSVN客户端,而没有svn命令行客户端,这会致使Maven全部svn相关的工做失败,所以,你首先确保svn --version可以运行。

分发仓库

想要让Maven帮咱们自动发布,首先咱们须要配置好分发仓库。关于这一点,见Maven最佳实践:Maven仓库——分发构件至远程仓库。

maven-release-plugin

紧接着,咱们须要配置maven-release-plugin,这个插件会帮助咱们升级pom版本,提交,打tag,而后再升级版本,再提交,等等。基本配置以下:

Xml代码   收藏代码
  1. <plugin>  
  2.   <groupId>org.apache.maven.plugins</groupId>  
  3.   <artifactId>maven-release-plugin</artifactId>  
  4.   <version>2.0-beta-7</version>  
  5.   <configuration>  
  6.     <tagBase>https://192.168.1.100:8443/svn/myapp/tags/</tagBase>  
  7.   </configuration>  
  8. </plugin>  

GAV我就很少解释了,这里咱们须要注意的是configuration元素下的tagBase元素,它表明了咱们svn中的tag目录,也就是说,maven-release-plugin帮咱们打tag的时候,其基础目录是什么。这里,我填写了svn仓库中的标准的tags目录。

提交代码

接着,确保你的全部代码都提交了,若是你有未提交代码,release插件会报错,既然你要发布版本了,就表示代码是稳定的,因此要么要么把代码提交了,要么把本地的更改抛弃了。

开始工做

如今,屏住呼吸,执行:

mvn release:prepare

执行过程当中,你会遇到这样的提示:

What is the release version for "Unnamed - org.myorg:myapp:jar:1.0-SNAPSHOT"? (org.myorg:myapp) 1.0: :

——“你想将1.0-SNAPSHOT发布为何版本?默认是1.0。”我要的就是1.0,直接回车。

What is SCM release tag or label for "Unnamed - org.myorg:myapp:jar:1.0-SNAPSHOT"? (org.myorg:myapp) myapp-1.0: :

——“发布的tag标签名称是什么?默认为myapp-1.0。”我仍是要默认值,直接回车。

What is the new development version for "Unnamed - org.myorg:myapp:jar:1.0-SNAPSHOT"? (org.myorg:myapp) 1.1-SNAPSHOT: :

——“主干上新的版本是什么?默认为1.1-SNAPSHOT。”哈,release插件会自动帮我更新版本到1.1-SNAPSHOT,很好,直接回车。

而后屏幕刷阿刷,maven在build咱们的项目,并进行了一些svn操做,你能够仔细查看下日志。

那么结果是什么呢?你能够浏览下svn仓库:

  • 咱们多了一个tag:https://192.168.1.100:8443/svn/myapp/tags/myapp-1.0/,这就是须要发布的版本1.0。
  • 再看看trunk中的POM,其版本自动升级成了1.1-SNAPSHOT。

这不正是咱们想要的么?等等,好像缺了点什么,对了,1.0尚未发布到仓库中呢。

再一次屏住呼吸,执行:

mvn release:perform

maven-release-plugin会自动帮咱们签出刚才打的tag,而后打包,分发到远程Maven仓库中,至此,整个版本的升级,打标签,发布等工做所有完成。咱们能够在远程Maven仓库中看到正式发布的1.0版本。

这但是自动化的 ,正式的 版本发布!

 

Maven的版本规则

前面咱们提到了SNAPSHOT和Release版本的区别,如今看一下,为何要有1.0,1.1,1.1.1这样的版本,这里的规则是什么。

Maven主要是这样定义版本规则的:

<主版本>.<次版本>.<增量版本>

好比说1.2.3,主版本是1,次版本是2,增量版本是3。

主版本通常来讲表明了项目的重大的架构变动,好比说Maven 1和Maven 2,在架构上已经两样了,未来的Maven 3和Maven 2也会有很大的变化。次版本通常表明了一些功能的增长或变化,但没有架构的变化,好比说Nexus 1.3较之于Nexus 1.2来讲,增长了一系列新的或者改进的功能(仓库镜像支持,改进的仓库管理界面等等),但从大的架构上来讲,1.3和1.2没什么区别。至于增量版本,通常是一些小的bug fix,不会有重大的功能变化。

通常来讲,在咱们发布一次重要的版本以后,随之会开发新的版本,好比说,myapp-1.1发布以后,就着手开发myapp-1.2了。因为myapp-1.2有新的主要功能的添加和变化,在发布测试前,它会变得不稳定,而myapp-1.1是一个比较稳定的版本,如今的问题是,咱们在myapp-1.1中发现了一些bug(固然在1.2中也存在),为了可以在段时间内修复bug并仍然发布稳定的版本,咱们就会用到分支(branch),咱们基于1.1开启一个分支1.1.1,在这个分支中修复bug,并快速发布。这既保证了版本的稳定,也可以使bug获得快速修复,也不一样中止1.2的开发。只是,每次修复分支1.1.1中的bug后,须要merge代码到1.2(主干)中。

上面讲的就是咱们为何要用增量版本。

 

实战分支

目前咱们trunk的版本是1.1-SNAPSHOT,其实按照前面解释的版本规则,应该是1.1.0-SNAPSHOT。

如今咱们想要发布1.1.0,而后将主干升级为1.2.0-SNAPSHOT,同时开启一个1.1.x的分支,用来修复1.1.0中的bug。

首先,在发布1.1.0以前,咱们建立1.1.x分支,运行以下命令:

mvn release:branch -DbranchName=1.1.x -DupdateBranchVersions=true -DupdateWorkingCopyVersions=false

这是maven-release-plugin的branch目标,咱们指定branch的名称为1.1.x,表示这里会有版本1.1.1, 1.1.2等等。updateBranchVersions=true的意思是在分支中更新版本,而updateWorkingCopyVersions=false是指不更改当前工做目录(这里是trunk)的版本。

在运行该命令后,咱们会遇到这样的提示:

What is the branch version for "Unnamed - org.myorg:myapp:jar:1.1-SNAPSHOT"? (org.myorg:myapp) 1.1-SNAPSHOT: :

——"分支中的版本号是多少?默认为1.1-SNAPSHOT" 这时咱们想要的版本是1.1.1-SNAPSHOT,所以输入1.1.1-SNAPSHOT,回车,maven继续执行直至结束。

接着,咱们浏览svn仓库,会看到这样的目录:https://192.168.1.100:8443/svn/myapp/branches/1.1.x/,打开其中的POM文件,其版本已是1.1.1-SNAPSHOT。

分支建立好了,就可使用release:prepare和release:perform为1.1.0打标签,升级trunk至1.2.0-SNAPSHOT,而后分发1.1.0。

至此,一切OK。

 

小结

本文讲述了如何使用Maven结合svn进行版本管理。解释了Maven中SNAPSHOT版本的来由,以及Maven管理版本的规则。并结合SCM的tag和branch概念展现了如何使用maven-release-plugin发布版本,以及建立分支。本文涉及的内容比较多,且略显复杂,不过掌握版本管理的技巧对于项目的正规化管理来讲十分重要。Maven为咱们提供了一些一套比较成熟的机制,值得掌握。