关于我,你需要了解一些东西。
我是一个持续集成的超级粉。
如果你把我放在一个新的软件开发团队,你没有一套自动化构建过程和持续集成设施,你可以相信,我要做的第一件事就是把自动化持续集成所有的事情都做完。
我喜欢自动化的想法。
只要有可能,我喜欢让事情更加高效和自动化。
对于我来说,这就是持续集成。
软件集成是一个漫长、困难、乏味、易错的过程,持续集成就是自动化完成这个过程并运行、测试,打包和部署。
但是它能做的远远不止这些。
持续集成,或者称为Cl,还提高了各开发人员所负责部分的代码合并的便利性,因此你不需要向我在源头控制那个章节提到的那样,频繁地进行烦人的代码合并过程。
集成的速度越快,集成过程给你带来烦恼的机会就越少,你也能更快地发现集成的问题。
最后,持续集成为整个团队快速提供反馈。
当你检查一些代码,在两分钟内知道代码是否编译,五分钟内知道是否代码被破坏——同时在一个中央的位置看到结果——那么你便拥有一个非常快、非常有效的反馈循环。
反馈周期越快,软件的发展也就越快,整体质量也提高越多——敏捷开发中极其重要的元素。
在这一点上,你可能会想,“嗯,约翰,听起来真的像是你想卖给我CI这个东西,但是它到底是什么呢?
我的意思是,这听起来不错。
我喜欢自动化,我喜欢反馈,但我不认为我对集成感兴趣。
”但你说的这个词Cl究竟是什么?“
嗯,我认为让你了解持续集成的最好办法是带你回到过去,让你知道这些事在以前是如何完成,持续化是如何随时间发展的,最后带你体验在Cl系统的启动和运行下,现代软件开发环境的工作流。
让我们开始旅程吧,好吗?
构建代码曾经是如何工作的

我虽然不是很老,但也老到在各种花哨的自动化工具出现之前就开始构建代码了。
在我的职业生涯早期,我们谈论的是像早期的2000年代 - 在一个软件开发团队工作是相当正常的,每个开发人员负责确定如何创建自己的软件构建。
这是什么意思?
那么,在任何足够大的应用程序中,将会有相当多的组件可能进入正在处理的软件的构建中。
当然会有大量的、必须编译的源代码文件。
通常,会有一些其他资源,如外部库,需要存储在开发人员的机器上,以构建最终的软件解决方案。
在编译代码以获得成品之前或之后可能会有额外的步骤。
过去,当你作为开发人员工作时,你会得到一个源代码的副本。 在过去五年中一直在使用该软件的一些大师将向你展示构建该软件所需要做的神奇咒语,接下来就靠你自己了。
个别开发人员开发了自己的方法用于在他们的机器上建立软件。
当是时候生产一个准备测试或部署到客户的构建,其中一个开发者会杀一只鸡,在一个圆圈中倒着走,并在五芒星周围点燃蜡烛,按CTRL+SHIFT+F5,然后会弹出一个完成了的软件版本。(译者注:作者的用意可能是想说明个别开发人员通过召唤魔法等各种各样的方式进行开发或部署)
但是,这种开发和构建软件的方式有一些大问题。
最大的问题是,由于每个开发人员都在自己的机器上构建软件 - 并且以略有不同的方式 - 也极有可能发生两个拥有相同版本代码的开发人员产生两个完全不同的软件版本的事件。
这怎么可能,你可能会问?
当失去一致性时,大量的事情可能会出错,每个人都在遵循自己的方式做事。
开发人员可能在其机器上安装不同版本的外部库。
开发人员可能认为它们具有相同的源代码,但实际上忘记从源代码控制中获取最新的文件,或者对文件进行了本地更改,这是他们没有意识到的,并且这些行为阻止了代码的更新。
文件或文件夹结构可能不同,这又会导致软件实际运行方式的差异。
很多事情可能会出错。
另一个主要问题是,由于开发人员都在本地构建,如果有人检入了一些甚至没有编译的代码,没有人会发现,直到他们拉下该代码,并试图构建软件时。
这似乎没什么了不起,但它将变得非常有趣,当多个开发人员检入糟糕的代码超过了一段时间,甚至几个星期,然后当终于有人试图建立一切,却发现它是有问题的,他们不知道更改了什么或者更改导致了哪些问题。
另外,我曾在一些地方花了几个小时才创建了一个单一的软件构建。
没有什么比在你的机器上运行了4-5个小时的构建,然后发现它是有问题的更糟糕了。
然后构建服务器出现了
解决这些问题的早期方法之一是引入构建器服务器。
想法是,不是让每个开发人员在自己的机器上构建软件,而是有一个中央构建服务器,它被正确配置,并具有所有正确的版本的库等。
开发人员可以在构建服务器上启动构建,或者构建服务器每晚都会自动构建软件。
起初,开始于每周构建。
所以,你至少有一个官方的每周构建的软件,该软件将所有的开发人员在那一周的软件变化结合在一起,并以统一的方式构建。
然而,每周构建的一个问题是,经常(特别是对于大型团队),当尝试创建每周构建时,会出现集成地狱问题。
通常,有一个开发人员或IT人员负责每周构建工作,他们会手动修复所有打破构建的问题,并寻找具有冲突更改的开发人员,尝试并解决它们。
这是一种对没有构建服务器的改进,但改进不是很大。
最后,夜间构建的想法变得流行起来。
想法是,如果我们每天将代码与构建服务器集成,每天晚上创建一个新的构建,那么大问题堆积和复合的时间就会减少,我们也可以更早地捕获问题。
起初,这个想法似乎很疯狂。
你不会相信,当我在曾经工作的一家公司中首次建议使用每晚构建时遇到了多大的阻力。
但是,最终它成为了规范,它确实解决了很多问题。
每晚从一个中央构建服务器构建,帮助大家在同一页面上同步,可以这么说,如果夜间构建被打破,每个人都优先考虑让它再次工作。
每晚构建的想法推动了构建过程本身的实际自动化的需要。
这是非常好的。
为了每天晚上一致地构建软件,我们需要一些自动化的方法来将所有代码集合在一起,编译它,并执行创建一个可用的构建所需的任何其他步骤。
这是为了实现自动化构建软件的过程而创建大量脚本的时代,至少在构建机器上。 开发人员仍然有自己的本地构建过程,这仍然是正常的。
只用于编译代码的Makefile脚本,开始变得更复杂,以处理构建服务器的完整构建过程,基于XML的构建自动化工具(如Ant)被创建并变得流行。
生活越来越好,但仍有重大问题。
随着敏捷变得越来越流行,单元测试的想法越来越受到支持,只是编译代码并打包它的夜间构建变得不够好了。
我们需要更短的反馈周期。
如果有人检入了糟糕的代码,整个团队可能会脱离正规,直到第二天早上之前都不会发现它们。
我们需要一些方法来构建代码,可靠,每天多次,除了“它能够编译”外,还需要运行一些其他的代码质量检查。
最后,持续集成

图片由SEI Insights - 卡内基梅隆大学提供
夜间构建已经是一个困难的构建间距了,但没有什么比试图让管理层购买持续集成,或者每次有人检入新代码时就构建软件的想法更困难的了。
你为什么想要这样做? 我们有每夜构建了。
我不明白; 你想不断地构建代码吗?
等一下,让我直说吧。 你想让我告诉所有的开发人员他们需要每天多次检入他们的代码? 你在跟我开玩笑吗?
有很大的阻力,但随着敏捷变得更受欢迎,都被慢慢地克服了。 持续集成并不仅是一个美好的梦想,而且是一个必须品,以在每次迭代时具有足够短的完成工作项的反馈周期。
然而,主要的问题是如何做到这一点。
我们如何在每次将新代码检入源代码控制时真正构建代码?
答案是使用持续集成服务器。
开发可以在构建服务器上运行的特定软件,它具有检测源代码控制变化,下拉最新代码和运行构建的能力。
很快,开发人员正在努力缩短代码库的构建时间,以使反馈更快。
既然我们有了这个能力,除了构建代码以外做更多的事情是可行的。
持续集成演变为包含运行单元测试,运行代码质量指标,如静态代码分析器,这些都是通过初始检入启动的。
最大的障碍是 - 并且仍然是在大多数情况下 - 让开发人员尽早检入代码并经常检入,以便我们能够快速获得反馈。
现在,使用CI,我们不仅能够在几分钟内知道代码更改是否阻止了整个项目的编译,而且我们可以找出是否有任何单元测试被破坏,甚至做自动运行回归测试寻找回归这样的事情。
啊,生活是这样美好。
一个持续集成工作流的示例
好了,在关于什么是持续集成这一点上,你可能会通过听到它解决的问题和它是如何演变的对其有了一个直观的理解。
但你可能仍然没有“明白它是什么” - 没关系。
让我们演示一个使用持续集成的示例工作流程,也许你会感觉稍微好一点。
检入代码
循环从检入你的代码就开始了。
当然,在你敢将代码检入到主存储库,并冒着破坏每个人的构建的风险前,你需要在本地机器上运行构建并运行所有的单元测试... ...对吧?
新构建启动
安装在构建服务器上的CI软件检测到它正在监视的源代码控制分支的更改。
这是你的代码! 哦,好运!
CI服务器启动新的构建作业。
代码检出
新构建作业做的第一件事是获取最新的更改。
它将你的代码更改和分支上的其他代码下拉到工作目录中。
编译代码
在这一点上,某种类型的构建脚本通常被启动,以实际编译和构建代码。
构建脚本将运行命令来构建源代码。
它还将链接到任何外部库或编译代码所需的任何东西。
如果代码编译失败,构建将立即停止,并且将报告错误。
这被称为打断构建 - 这是不好的。
我以为你说你在你的机器上编译代码之前检查它?
耻辱!
运行静态分析仪
假设代码正确构建,运行静态分析器以测量某些代码质量度量。
如果你不知道这些是什么,好的。
基本上,它们是可以查看代码并查找可能的错误或违反最佳实践的工具。
来自这些分析器的结果构建完成时生成报告被存储。
如果对于从静态分析器导出的代码质量度量不满足某些阈值,则某些构建实际上可以被设置为失败。
运行单元测试
假设一切都还是好的,CI工作开始单元测试。
单元测试针对编译的代码运行,并且结果被记录用于以后。
通常如果任何单元测试失败,这将导致整个构建失败。 我强烈推荐这种方法。
报告结果
最后,报告实际构建的结果。
报告将包含有关构建是否通过或失败,运行时间,代码质量指标,运行的单元测试以及任何其他相关数据的数据。
在这一点上,文档也可以由构建自动生成。
结果可以设置为通过电子邮件发送给团队 - 尤其是在失败的情况下 - 大多数CI软件程序都有一个Web界面,任何人都可以看到最新构建的结果。
软件打包
现在构建软件被打包成一个可以部署或安装的形式。
这通常涉及获取编译的代码和任何外部资源或依赖性,并将其包装到任何具有可部署或可安装单元所需的结构中。
例如,可能会创建一个包含所有正确文件的文件结构,然后所有文件可能会被压缩。
在这一点上,构建作业还可以在源代码控制中应用某种标记来标记软件的版本。
可选地部署代码(持续部署)
这最后一步是可选的 - 实际上,我认为上一步也是可选的。
但是更多的团队正在进行持续部署,将代码直接部署到环境中以便进行测试,或者如果他们真那么勇敢,可以将代码直接部署到生产环境。
完成
就是这样。
当然,这些步骤有一些变化,当然,可能还有一些额外的步骤,但基本的想法是构建代码,检查问题,并为部署准备好代码,每当新代码检入时自动部署。
这使我们能够很快知道系统的新更改是否导致错误,从而可以立即修复。
虽然我轻轻松松地讲了这些内容,但我不想让它听起来太简单了。
构建工程师会花费相当多的时间来构建一个良好的持续集成过程,并且关于如何做和什么是最好的做法有各种各样的讨论。
CI 服务器和软件

持续集成的一个关键组件是CI软件。
没有CI软件,我们必须编写自定义脚本,并亲自编写自己的构建服务器。
幸运的是,许多聪明的开发人员很快意识到构建CI软件的价值,这有助于自动化大多数持续集成的常见任务。
大多数CI软件以非常相似的方式工作,这使得它很容易或者至少更容易实现像上面描述的那样的工作流。
实际上有相当多的CI服务器和软件可用,但我只想在这里强调一些在编写本书时最常用的。
Jenkins
它是一个Java程序,最初被创建用于在Java环境中做CI,但它变得如此受欢迎,并且易于使用,它已经扩展为可用于任何技术。
Jenkins非常容易安装和运行,因为它包含自己的内置Web服务器。
它还有大量 的插件。
如果你想利用Jenkins做一些事情,有很大可能有人已经写好了一个插件实现你的需求。 这是我喜欢和使用Jenkins的主要原因之一。
(实际上我有一个Pluralsight的课程,讲述Jenkins的基础知识。)
Hudson
关于Hudson和Jenkins分家的狗血故事,我也就不赘述了,这里只是简要介绍下。
在Jenkins出现之前,只有Hudson。
然而有一些争吵, 因此Jenkins就从Hudson分离了出来,而Hudson继续单独发展。
Hudson由Oracle所控制,而且我个人认为它不如Jenkins好些,因为Hudson的创始人Kohsuke Kawaguchi和最初的Hudson团队的大部分人都迁移到了Jenkins。
老实说,我并不知道Hudson是否还存在且有人使用。
Travis CI
Travis是另外一个流行的CI软件,但是它操作起来有点不一样。
Travis CI 实际上是被托管的并且作为一个服务使用。
换句话说,你不用安装Travis CI,只是需要注册。
实际上它最初设计是用来给托管在Github上的项目提供CI服务的。
Travis逐渐流行起来,因为大量项目都是托管在GitHub上的,而且它设计良好且易用。
再加上你无须维护自己的的构建服务器,这一点非常棒。
TFS
如果你是专门工作在一个使用微软产品的地方,那么TFS (Team Foundation Server) 可以提供持续集成的支持,但是以我的经验来看,它相当的简单并且不那么健壮,无法与其他流行的软件竞争。
但是,我想如果你想要简单些的东西—而且必须是微软的解决方案—那么这个东西就可以为你所用。
TeamCity
TeamCity是另外一个流行的持续集成服务器,它是由商业公司JetBrains所创建。
它有一个免费版本,同时也是一个许可的产品。
因此,如果你在寻找一个更加专业的产品,这会是个不错的选择。
很多.NET 的团队都在使用TeamCity来解决他们的持续集成需求。
更多?
我这里只是给你们介绍了持续集成服务器中比较流行的一小部分样例,但确实还是存在着不少其他的。
如果你想查看所有的选项,请查阅即将完成和更新的列表。
