版本管理
一个健康的项目通常有一个长期的、合理的版本演变过程,如junit有3.7、3.8等版本,而有了版本的定义,那么我们就可以针对版本做控制和管理。
那么,版本管理和版本控制又是什么,版本管理是指项目整体版本的演变过程管理,如从1.0到1.1-SNAPSHOT在到1.1,而版本控制是指借助版本控制工具追踪代码的每一个变更。这里将要讲述的是版本管理,但版本管理通常也是涉及到一些版本控制系统的操作及感念,请注意区分。
为了方便团队的合作,在项目开发的过程中,大家都应该使用快照版本(SNAPSHOT),maven能够很智能的处理这种特殊的版本,解析项目各个模块的最新快照,快照版本机制促进团队之间的内部交流,而当项目需要对外发布的时候,我们需要提供一个稳定的版本,使用该版本永远只能够定位到唯一的无变化的构件,而快照版本定位的构件可能随时发生变化,对应的我们称这种稳定的版本为发布版本,当项目发布了稳定版本之后,自然就进入了下一个开发周期,也就是一个新的快照版本。
版本管理关心的问题之一就是快照版本和发布版本之间的转换,快照版本对应了项目的开发过程,因此往往对应了很长时间,而正式版本对应了项目的发布,因此仅仅代表某个时间项目的状态。如图
理想的发布版本应该对应项目某个时刻的稳定状态,这包括源代码的状态以及构建的状态,因此这个时候项目的构建应该满足一下条件:
1.所有自动化测试应该全部通过:如果有没通过的测试,说明有问题需要修复,这是不稳定的、有问题的体现。
2.项目没有依赖任何快照版本的依赖:快照版本的依赖,意味着不同时间点,构建的结果会有不同,会得到不同的构件,这会导致应用的不稳定或错误。
3.项目没有配置任何快照版本的插件:不同时间会引入不同的插件快照,这回影响项目构建的稳定性。
4.项目所包含的代码已经全部提交到版本控制系统中:避免代码的丢失,项目某一个时刻状态的丢失。
只有上述条件都满足了,才可以修改项目的版本为发布版本。
这里我们应该合理的利用版本控制系统的标签(Tag),正常使用中,项目所有的变化都记录在主干(Trunk)中,这样我们找项目的某个时刻的状态会比较麻烦,而使用标签,我们就可以明确的把项目的某个时刻的状态明确的标记出来,这里说的是某个时刻的状态,也就是项目的各个历史发布版本,这样我们就可以很轻松的找到项目某个时刻的状态,方便的进行比较,甚至是重新构建历史版本的构件。
因此,将项目的快照版本更新为发布版本后,应该在进行一次构建,以确保项目状态是健康的,然后将这一变更提交到版本控制系统的主干中,接着在为当前主干的状态打上标签。
一个项目发布版本的大致过程如下,当项目满足更新为发布版本的条件后,更改项目版本为发布版本,做一次发布版本的构建来确认项目的健康状态,提交版本更新至主干,为当前主干打一个标签(Tag),这就是一个发布版本的过程。接下来就是更新发布版本至快照版本,进入下一个开发周期。
maven的版本号定义约定
例如:1.3.4-bate-2这个版本号,这往往表示了这个项目或产品的第一个重大版本的第三个次要版本的第四次增量版本的beta-2里程碑,也就是说maven的版本号定义约定是这样的:<主版本>.<次版本>.<增量版本>-<里程碑版本> 。
主版本:表示了项目的重大架构变更,如maven1和maven2想去甚远。
次版本:表示较大范围的功能增加和变化,及bug修复。
增量版本:一般表示重大bug的修复,如1.4.0的bug修复后,版本为1.4.1。
里程碑版本:往往指某一版本的里程碑,如maven3已经发布了很多里程碑版本,如3.0-alpha-1、3.0-alpha-2,这样的版本与正式的3.0相比,往往表示不是非常稳定,还需要很多测试。
注意:不是每个版本号都一定要包含这四个部分,一般声明有主次两个部分就可以,当添加依赖的时候,如果没有声明版本,那么maven会根据版本号约定,自动解析最新版本,这个时候就需要对版本号进行排序,对于主、次、增量版本号是基于数字的比较好做出比较结果,而里程碑版本maven只会做简单的字符串比较,因此会有1.2-beta-3 > 1.2-beta-11这样的结果,这需要特别留意。
主干(trunk)、标签(tag)与分支(branch)
主干:项目开发代码的主体,是从项目开始到当前都处于活动的状态,从这里可以获得项目最新的代码,以及几乎所有的变更历史。
分支:从主干某个点分离出来的代码拷贝,通常可以在不影响主干的情况下在这里做重大bug的修复,或者做一些实验性质的开发,如果达到预期的目的,就把变更合并到主干中。
标签:用来标识主干或者分支的某个点的状态,以代表项目的某个稳定状态,这通常就是版本发布时的状态。
使用maven管理版本的时候,也涉及了很多版本控制系统的操作,如图:
图中展示的是一个典型的项目版本变化过程,项目从1.0.0-SNAPSHOT快照版本进行开发,1.0.0发布并打一个标签,然后进入1.1.0-SNAPSHOT快照版本开发,1.1.0版本发布,1.1.0打一个标签,进入1.2.0-SNAPSHOT快照版本开发,这个时候,发现1.1.0版本有个重大的bug,但是主干1.2.0-SNAPSHOT已经有了很多变化,这这个主干上修复bug是不利于快速测试修复的验证的,所以我们在1.1.0的标签上拉取一个分支1.1.1-SNAPSHOT快照版本,并在这个分支上做bug修复和测试验证,当验证通过,发布1.1.1版本,并打一个标签,同时把修复所做的改动合并到主干1.2.0-SNAPSHOT上,然后1.2.0发布并打标签,进入1.3.0的开发阶段,这里涉及了快照版本和发布版本的切换,maven版本号约定的应用,以及版本控制系统主干、标签和分支的使用,这其实也是一个不成文的行业标准,理解这个过程之后,不仅可以更方便的学习开源项目,也能对项目的版本管理更加的标准和清晰。
自动化版本发布
使用maven-release-plugin插件,自动完成版本发布所做的操作,只要提供一些必要的信息,该插件主要有三个目标,如下:
release : prepare 准备版本发布,依次执行下列操作:
检查项目是否有未提交的代码
检查项目是否有快照版本的依赖
根据用户的输入将快照版本升级为发布版本
想pom中的scm信息更新为标签地址
基于修改后的pom执行maven构建
提交pom
基于用户输入为代码打标签
将代码从发布版升级为新的快照版
提交pom变更
release : rollback 回退 release : prepare 所执行的操作,将pom回退至 release : prepare之前的状态,并提交。需要注意的是,该动作不会删除 release : prepare生成的标签,所以需要用户手动删除。
release : perform 执行版本发布。签出release : prepare生成的标签中的源代码,并在次基础上mvn deploy命令,打包并部署构件至仓库。
要为项目发布版本,首先需要为其添加正确的版本控制系统信息,这是因为maven-release-plugin插件需要知道版本控制系统的主干、标签等地址信息后才能执行相关的操作。
一般配置项目scm信息如下:
<!--源码仓库信息--> <scm> <!--只读的scm地址--> <connection>scm:git:https://github.com/123456/test-parent.git</connection> <!--可写的scm地址--> <developerConnection>scm:git:https://github.com/123456/test-parent.git</developerConnection> <!--可以在浏览器访问的scm地址--> <url>https://github.com/123456/test-parent.git</url> </scm>
这里的配置只是告诉maven当前项目的仓库位置,而版本控制还需要涉及标签操作。配置如下:
<plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-release-plugin</artifactId> <version>2.5.3</version> <configuration>
<username>username</username> <password>password</password>tag地址,标签的基础目录
<tagBase>https://github.com/123456/test-parent.git</tagBase> </configuration> </plugin></plugins>
在执行mvn release:prepare之前,要确保两点:
1.系统必须提供scm版本控制软件的命令行工具,maven需要命令行工具执行相应的操作。
2.pom必须配置了可用的部署仓库,因为mvn release:perform会签出标签的代码,执行deploy操作将构件发布到仓库中。
另外,mvn release:prepare -DautoVersionSubmodules=true 这个带参数的命令的作用是,当我们管理聚合项目的时候,忽略个模块的版本确认,使各个模块都使用与父模块一致的版本。
在版本管理的过程中,生成的配置文件,我们不需要动,当我们执行perform命令之后,会自动消失。
在maven构建的过程中,一些功能我们并没有进行配置,但是确自动生效了,这是因为我们继承了超级pom中的配置,perfile配置的属性,会在条件达成的时候被**使用,而我们使用版本管理插件的时候,该插件**了某些配置,所以才有这个效果。具体超级pom配置请看maven的超级pom文件。
自动化创建分支
使用maven-release-plugin插件的branch目标可以帮我们实现自动创建分支,他会做这些操作:
检查本地有无未提交的代码
为分支更改pom的版本,例如从1.1.0-SNAPSHOT改为1.1.1-SNAPSHOT
将POM中的scm信息更新为分支地址
提交以上更改
将主干的代码复制到分支中
修改本地代码,使其回退到修改之前的版本(用户可以指定新的版本)
提交本地更改
当然,我们还需要配置分支的目录地址,
<plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-release-plugin</artifactId> <version>2.5.3</version> <configuration> <username>sunhongwu</username> <password>shwsunhongwu5</password> <!--标签--> <tagBase>https://github.com/123456/test-parent.git</tagBase> <!--分支--> <branchBase>https://github.com/123456/test-parent.git</branchBase> </configuration> </plugin> </plugins>
mvn release:branch 生成分支
-DbranchName=test-parent-beta-1 设置分支的目录名
-DupdateBranchVersions=true 设置分支使用新的版本
-DupdateWorkingCopyVersions=false 不更新(本地代码)主干版本
-DautoVersionSubmodules=true 多模块和父模块使用相同的版本
GPG签名
当我们获取一个构件的时候,我们希望可以校验这个构件是否是我们指定的组织发布的构件,并且校验是否被篡改,我们自己帆布的构件,可能使用着也希望可以做这样的校验。
PGP(pretty good privacy)就是这样一个帮助提高安全性的技术,pgp最长用来给电子邮件进行加密、解密以及提供签名,以提高电子邮件交流的安全性,这里介绍下如何使用pgp技术为我们发布的构件签名,为项目增强安全性。
GnuPG简称GPG,是PGP标准的一个免费实现,无论是类unix平台还是windows平台都能使用,GPG能够帮助我们为文件生成签名、管理**,以及验证签名等。
maven-gpg-plugin插件可以帮助我们自动做这个工作,避免复杂无聊的手工操作,签名、部署到仓库自动完成,此处暂不做说明,日常使用中,很少需要签名,而且使用插件也需要在相应的平台安装gpg的分发包。