作者:Andrew Bayer

从开发的角度看,Digg4令人兴奋的一件事情是持续部署:当开发修复了一个bug或者新增了一个功能的时候,不需要去等待排队发布,而是立刻就能将变更上线。这简直太棒了,因为变更周转时间极大的缩短了。当然,由于缺少手工测试以及正式签字通过,这也会使得错误的变更出现在线上。如何平衡速度和应需求持续部署的敏捷性以达到足够的稳定性和可靠性曾经是、并且继续是一个最主要的挑战。在过去的几个月里,我们实现了一种在不牺牲稳定性或者敏捷性的前提下可以促成这种平衡的流程。

此流程有几个要点。第一,工具的支持:git源代码控制,Hudson持续集成和构建管理,Selenium UI测试(由Sauce Labs提供的Sauce OnDemand服务的极为方便的支持),以及puppet服务器管理。另外,又增加了Gerrit,一个git代码评审系统,发源自Google的Android开发团队。对所有变更代码的强制评审感觉会比较痛苦,但是要想发现自动化测试无法发现的设计缺陷,除了由另一个开发来阅读你的代码还能有其它方法吗?

然而,我们使用Gerrit还有其他的目的。除了代码评审以外,Gerrit还提供了一个“已经验证”标志,用来记录相关的变更正确的创建和测试。结合Hudson即可实现预先测试的代码提交:除非经过一系列的单元测试,成功的打成Debian包,以及前端代码安装在一台测试机上并经过Selenium冒烟测试,任何变更都不能进入主干。这个流程由Gerrit的Hudson Trigger插件来完成,该插件是由索尼-爱立信的Robert Sandell及其它Hudson团队来开发和维护的。

Gerrit Trigger插件利用了Gerrit的事件流功能来监听新补丁的创建。每当它看到一个新补丁出现,就检查是否有被配置来构建补丁所属Gerrit项目的Hudson项目,如果有的话,就启动此Hudson项目的构建,并确保该构建包含了待验证的变更。在所有构建完成以后,Gerrit Trigger监听器会发送一条带结果的消息到Gerrit。具体的命令、值等是可配置的。在我们的流程里,我们只是使用“已经验证”标志,-1表示构建失败,+1表示通过。我们还对Gerrit Trigger插件做了微小的调整以支持补丁在除测试失败外的构建失败时能够重新构建。索尼-爱立信团队有一个更加正式的解决方案会在将来发布。

为什么我们在预测试的提交的构建中只运行一部分单元测试和Seleinum测试呢?最主要是时间原因。我们希望预测试的提交过程尽可能快,因为每个变更的每个版本都会经过它。持续部署流程的下一步将会跑的少一些,所以可以花的时间长一些并且更严格一些。当一个变更经过了另外一个开发的代码评审以及Hudson构建的验证,就可以合并到主干了。当一个变更进入主干时,一个完整的构建被执行,所有单元测试例会被运行。接下来,代码被打包、安装在内部的预发环境里,然后运行全部Seleinum测试例。如果全部测试例成功通过,我们就会把经过测试的代码部署到生产环境。

现在,是不是很完美了?当然没有。现在,由于部分测试或外部因素的不稳定性,我们必须手工的重新构建更多的事先测试的提交;并且,还有不少部分可以调整,使整个流程更加平滑和可靠。但总体上来看,已经不错了,因为没有变更在被另一个开发评审而且经过了特定的测试之前会进入主干。没有一个变更会在经过了完整的单元测试之前会从内部进入生产环境,也不会有一个变更会在经过了额外的Selenium测试之前从内部进入生产环境。这是否意味着线上不再出现bug了?当然不是,但这可以使得遗漏的bug尽可能少,并且使得bug修复更容易和更快的上线。

原文:Continuous Deployment, Code Review and Pre-Tested Commits on Digg4

欢迎参加iTran乐译4期