在一个有着上千开发技术人员的大型互联网公司,当研发到一定阶段,企业对CI/CD(持续集成/持续交付)的考虑会越来越重视,项目的快速上线交付能力考验着开发测试运维各个团队,但是实现CI/CD不是件容易的事情,肯定会对项目的开发上线有个规范过程,需要有个统一的应用框架,需要有规范化的部署,需要稳定可靠的基础设施,最近有对代码分支管理及其应用场景的讨论,看选择哪个可以方便CI。
两种代码分支开发模式
现在的代码分支管理一般按照上线流程成两个流派,一个是分支发布模式,还有一个是主支发布模式,下面是对两个分支模式的定义,
分支发布模式:每次上线都会开出一个分支,上线的发布包从这个分支构建。
主支发布模式:主支发布模式中,每次上线的发布包都会从代码主支构建。
需要说明的是,网上大家讨论比较多的gitflow流程是一种主支发布模式,其发布tag会在构建前预先提交到代码仓库,并且标记包的版本,这个和本文CI最佳实践中是有些出入,如何标记代码的tag,及其如何在CI平台中构建出包并确定包的版本,这个将在下面讨论。
分支发布流程
见如下图,
版本1.0要上线时,会先开出一个分支R1.0,然后使用这个分支构建打包上线,上线之后分支的提交权限关闭。当发现线上有紧急bug修复时,则直接提交hotfix到分支R1.0,然后继续从分支R1.0构建打包上线。
分支R1.0和R1.1对代码提交有权限控制。
主支发布流程
见如下图,
版本1.0要上线时,需要合并代码到master,然后从master构建打包上线,上线之后master的提交权限关闭。当发现线上有紧急bug需要修复时,从版本1.0的commit id拉出一个hotfix分支,修复代码提交到hotfix分支,然后从此分支构建打包上线。
master和hotfix对代码提交有权限控制。
两种代码分支管理的简单比较
1. 主支发布模式中,发布主支一般固定不变,这有助于CI的打包构建,不用每次上线都要在CI job中切换分支,这是比分支发布更好的地方,在这个模式下,CI流程中可以只以主支的代码打出来包上线,然后代码合并权限和审查流程也可以较好的进行工程化控制。
2. 分支发布模式中,每次大的版本上线都有一个对应的分支,这样做有个缺点是,分支会越来越多,分支名称如果管理的好,还可以分辨出各个分支开发的功能作用,但如果管理的不好,分支可以多到让自己看不清楚哪个分支是做什么。但是,这对紧急修复是非常有帮助的,一旦知道哪个版本有问题,可以快速切换到相应分支,进行修复。
CI的版本控制
包的版本可以在很多地方确定,有些使用代码仓库管理发布包版本,有些在CI平台上控制出包的版本,有些则在上线发布平台上控制。
我觉得在CI平台上来控制包的上线版本是最恰当的时候,因为每次构建的时候,都应该是不同的包,有不同的版本,都需要经过测试才能上线。我们不能因为代码一样,构建两次的包其版本都一样,这个在后续部署测试上线环节都容易混淆。
代码的tag功能和版本控制
有些项目团队使用tag功能来确定上线包的版本,一旦有新的版本tag标记,则触发CI平台上构建打包job,获取tag来每次进行构建,但是tag有一个缺点,其版本变化都需要预先手动更改,如果CI平台对同一个tag的代码进行多次构建,这是很有可能的,这个时候如何确定版本是个问题。
一般来说由CI平台来控制包版本是推荐的做法,而不是通过代码仓库中的tag或者配置文件来控制。在软件行业,几乎没有人没有听说过maven这个项目构建工具,其默认使用项目中的pom.xml进行构建,获取依赖包,并打出一个完整的项目包,包的版本会以pom.xml中版本来定,但是有多少了解 mvn中有个version:set插件,用于在CI构建的时候动态更新pom.xml的包版本?在pom.xml等这些构建配置文档里确定包版本,提交到代码仓库,通过这个方式来确定构建时候的包版本是不可取的。maven打出的包,如果是上传到snapshot仓库,是可以覆盖上传的,但如果上传到release仓库,则必须修改每次打包的版本,这个时候一般使用mvn version:set这个插件,在CI构建的时候动态确定包的版本,而不是把包版本提交代码仓库。
然后tag该如何添加,有什么用呢?我们知道tag一般用于表示代码快照,一般的commit id是不可读的,tag我们可以做些有意义的命名,来告诉我们这份代码快照是做什么的,很多时候tag就是包的上线版本。那么上线版本什么时候打到代码仓库中去?在我们的实践中,我们是在包正式上线后,才把上线的版本打到相应的commit id上,然后如果有紧急问题需要修复上线,能够通过tag快速拉取代码,打hotfix。
应用场合
两个分支开发模式是互有优缺点,至于选择哪个更好,其实和业务场景紧密相关的,不同的业务场景需要不同代码分支管理方式,项目开发起来才会方便开发。
我们将软件应用开发下面两大类,
1) 桌面应用
一些桌面安装应用,其特点是,应用的安装包分发和版本升级都由用户在其桌面系统中控制,应用的版本基本由用户的使用市场占有率决定,开发企业很难控制应用的版本升级,完全靠用户的评价和喜好,其版本控制是趋于分散开放模式。
这些应用包括普通的windows、mac应用,还包括操作系统本身,像微软xp和win7,IE的浏览器等等,这些应用的版本,企业是很难控制的。以微软开发的XP系统为例,在微软2014年4月全面停止支持服务后,到2014年11月份StatCounter的统计数据显示Windows XP的市场份额仍然高达10.71%,和Windows 8.1的10.86%占有率几乎持平。微软公司很早就说要关闭Windows XP,但是XP的市场占有率让微软公司无可奈何,谁让XP的用户那么坚定执着。
从中可以看到,桌面应用的版本控制是分散开放的,市场上有多少版本的应用,作为软件企业需要考虑市场占有率,然后决定维护多个版本的紧急修复和补丁升级,老版本的维护工作一点都不轻松。
2) web和移动应用
现在是互联网和移动应用的时代,这个时代有着鲜明的特征,快速开发发布和迭代,其应用的版本控制和升级也是和桌面应用完全不一样。web项目的前后端版本和升级,完全是软件公司控制的,如果后台支持灰度发布,那么在灰度发布期间,一般有两个版本的应用在运行,很少有三个版本以上的同一应用同时运行。移动应用的版本升级比web应用比较慢,但还是比桌面产品好,无论是android还是ios,都有相应应用市场,不断的推动用户升级至最新版本,现在移动应用本身也在加强自动升级功能。
可以看到,web和移动应用的版本是不断向前升级的,是一种趋于内敛的版本控制。
那么,现在回到刚刚问到的问题,在什么场合,我们该用什么样的分支管理模式呢?简单的回答如下,
1)软件产品的版本升级完全由用户来决定,版本控制趋于分散开放,这个时候建议使用分支发布开发模式,大部分桌面应用就是属于这个类型。
2)软件产品的版本完全受企业控制,趋于内敛聚合,可以不断升级到最新的版本,这个时候使用主支发布的开发模式,会使项目管理和开发更加方便。web和移动应用推荐使用这个模式。
具体原因,其实就是,如果版本不能控制,各个版本的代码最好能有单独的分支管理,其紧急hotfix开发和补丁程序能够较好的独立开发,代码的hotfix合并也比较清晰易懂。如果版本完全受控制,那么我们只要负责最近发布的几个版本,而且很多时候,我们只要负责最新一个版本,这个时候即使有紧急hotfix,我们也可以把紧急fix打到主支上,然后上线,完全没有维护N多个分支(N>无穷大)的烦恼。