如何开始持续集成

2025-05-25

如何开始持续集成

现实生活中的持续集成

持续集成入门所需了解的一切。包括分支策略、测试自动化、工具和最佳实践。

目标:快速安全地交付工作代码

持续集成的目标是将代码交付到存储库的主分支:

  • 快速:从将新代码推送到存储库到将其合并到主分支,应该在几分钟内完成
  • 安全:我们如何知道新代码有效?持续集成旨在设置正确的检查,以便完全信任地自动合并代码。

持续集成一方面与工具有关,另一方面则与团队的思维方式和文化息息相关。您希望您的开发流程能够促进新代码的快速集成,同时始终保持一个可运行的主分支。这个可运行的主分支将在未来支持持续交付或持续部署。不过这些内容留待以后再讨论。我们先来聊聊持续集成。

实现持续集成有两大支柱。

分成小块进行

想象一下,一个由 5 人组成的团队正在开发一款 SaaS 产品。每个人都开发一个单独的新功能。每个功能的工作量大约需要 1 到 2 周。有两种方法可以实现这一点。

  • 团队可以采用功能分支。每个开发人员在“功能分支”上完成自己的工作。一旦每个人都对自己的工作满意,这些分支就会合并到主分支。
  • 团队可以继续使用分支(仍然),但每次推送时都会将工作集成到主分支。即使工作仍在进行中!正在进行的工作对于主分支的任何最终用户或测试人员都是不可见的。

您认为哪种方法最有效?

第一种方法最终会导致“不可预测的发布综合症”。长期存在的功能分支会给每个开发人员带来一种虚假的安全感和舒适感。随着分支长期分离,无法衡量合并所有分支的难度。最好的情况是出现一些小的代码冲突,最坏的情况是基本的设计假设会受到挑战,最终不得不重新设计……这真是艰难的尝试。

时间压力之下,返工必然会导致质量下降,技术债也会不断累积,形成恶性循环。

请参阅关于为什么您不应该使用功能分支来处理肮脏细节的帖子。

第二种方法正是我们实现持续集成所需要的。每个开发人员都在自己的分支上工作。区别在于:

每次推送时,更改都会合并到主分支,并且每个开发人员每天都会将其分支与最新的主分支版本同步几次。

这样,团队就能更快、更轻松地解决冲突并协调设计假设。早期发现的 5 个小问题远比发布前发现的 1 个大问题要好得多。查看下方的“功能切换”部分,了解如何将“正在进行的工作”集成到主分支。

自动检查带来安全

传统的软件开发流程基于构建周期和测试周期。这可能仍然适用于上述“功能分支”方法。如果我们每天集成和合并数十次代码,那么手动测试就毫无意义,因为这会耗费太长时间。我们需要自动化检查来验证代码是否正常运行。我们需要一个 CI 工具,能够自动接收每个开发人员的推送、运行构建和测试。

测试类型和内容应该是:

  • 速度足够快,可以在几分钟内向开发人员提供反馈
  • 足够彻底,可以满怀信心地将代码合并到主分支

遗憾的是,没有一种测试类型和内容适合所有情况。正确的平衡点取决于你的项目。在持续集成 (CI) 阶段,不要运行大型且耗时的测试套件。这类测试虽然安全性更高,但代价是延迟向开发人员提供反馈。这会导致上下文切换,纯粹是浪费时间。

优化开发人员时间并减少上下文切换

冗长的 CI 检查(我指的是超过 3 分钟)会给团队中每位开发人员带来时间的浪费。
让我们比较一下“好”和“坏”的工作流程。“好”的工作流程:

  • 您提交并推送您的代码
  • CI 构建和测试运行 1 到 3 分钟
  • 在 1 到 3 分钟内,您可以检查手头的任务,在某些管理工具中调整状态,或再次检查代码
  • 3分钟内您将获得成功状态:您可以继续执行任务的下一部分。如果构建失败:您可以立即修复问题

“糟糕”的工作流程:

  • 您提交并推送您的代码
  • CI 构建和测试运行 15 分钟
  • 这15分钟你做什么呢?
    • 你可以和团队一起喝杯咖啡。这很正常,但你一天能喝几次呢?
    • 你可能会专注于流程中的下一个任务
  • 15分钟后,你收到了构建失败的通知。你需要切换回上一个任务,尝试修复问题……然后继续下一个15分钟的循环……
  • 那时你会想:我是否应该再次回到下一个任务,或者只是等待 15 分钟,然后安心地认为我确实已经完成了当前的任务......

糟糕的工作流程不仅浪费时间,还会让开发人员感到沮丧。高效的开发人员才是快乐的开发人员。

您需要调整您的工具和工作流程以使您的开发人员满意。

工具

分枝

持续集成是指将来自不同开发者分支的代码集成到配置管理系统中的公共分支。您很可能正在使用 git。在 git 中,代码库的默认主分支称为“master”。有些团队会创建一个名为“develop”的分支作为持续集成的主分支。他们使用“master”来跟踪交付和部署(develop 分支正在合并到 master)。

你可能已经有一个团队推送或合并代码的主分支了。坚持下去。

每个开发人员都应该在自己的分支上工作。如果同时处理多个不同的主题,可以使用多个分支。但这充其量只能说明工作“不专注”。
一旦代码的一致部分准备就绪,就立即推送代码库。持续集成 (CI) 检查将启动,如果成功,会将代码合并到主分支。如果检查失败,您仍然在自己的分支上,可以修复任何需要修复的问题,然后再次推送。

上述过程中,一个重要的词是“代码的一致性”。如何知道它是一致的?很简单。

如果您可以轻松地想出一个好的提交信息,那么它就是一致的。

另一方面,如果你的提交信息只有三个要点,而且充斥着许多形容词和副词,那可能不太好。建议将你的工作拆分成多个一致的提交,然后再推送代码。
一致的提交有助于代码审查,并使代码库历史记录更易于追踪。

不要因为一天结束了就强行做任何事!

拉取请求

什么是拉取请求?拉取请求是指你请求团队将你的分支合并到主分支。请求的接受需要通过 CI 工具提供的状态信息,并可能进行代码审查。最终,需要由负责合并拉取请求的人员手动接受。

拉取请求 (Pull Request) 诞生于开源项目中。维护者需要一种结构化的方式来评估贡献,然后再将其合并。拉取请求并非 Git 的一部分。不过,所有 Git 提供商(GitHub、BitBucket、GitLab 等)都支持拉取请求。

请注意,拉取请求 (Pull Request) 对于持续集成来说并非强制要求。它们的主要作用是支持代码审查流程,而该流程在设计上无法实现自动化。

如果您使用拉取请求,则适用相同的原则或“小块工作”和“优化开发人员时间”:

  • 保持每个拉取请求较小且具有一个明确的目的(这将使代码审查更加容易)
  • 保持 CI 检查快速

自动检查

持续流程的核心是自动化检查。它们确保代码合并后,主分支代码能够正常运行。如果检查失败,代码将无法合并。代码至少应该能够编译、转译,或者进行任何技术栈上的操作,以使其为运行时做好准备。

除了编译之外,你还应该运行自动化测试,以确保软件正常运行。测试覆盖率越高,你对合并到主分支的新代码就越有信心。不过要小心!更高的覆盖率意味着更多的测试和更长的执行时间。你需要找到合适的平衡点。

当你没有任何测试或需要减少一些长时间运行的测试时,你该从哪里开始呢?专注于对你的项目或产品至关重要的事情。

如果您正在构建 SaaS 应用,则应检查用户是否能够注册或登录,并执行 SaaS 提供的最基本操作。除非您正在开发 Salesforce 的竞争对手,否则您应该能够在几分钟甚至几秒钟内运行测试。如果您正在构建数据处理量很大的后端:请使用有限的数据集来测试不同的构建模块。不要将大型数据集上的长时间运行纳入持续集成。代码合并后即可触发长时间运行的测试。

专业提示

功能切换

持续集成的核心理念是尽快将代码迁移到主分支。即使是正在进行的工作,即使是那些尚未完全运行,或者您不想暴露给测试人员或最终用户的功能。实现这一点的方法是使用功能切换开关。将新功能置于启用/禁用的切换​​开关下。切换开关可以是编译时布尔标志、环境变量或运行时变量。正确的方法取决于您想要实现的目标。

功能切换的首要优势在于,您可以将其部署到生产环境中,并根据需要启用/禁用新功能。您可以重启服务器并更改环境变量,或者启用/禁用新的 UI 仪表板布局。这样,您就可以充分灵活地部署该功能。或者,如果在生产环境中出现意外问题,也可以禁用该功能。或者,允许最终用户选择启用或禁用该功能(在 UI 切换的情况下)。

功能切换的第二个主要好处是,它迫使你思考你正在做的事情和现有代码之间的界限。这是一个很好的练习,每次你对现有系统进行添加时,都应该从这里开始。功能切换步骤使流程的这一步更加清晰可见。

功能切换的唯一缺点是您需要定期从环境和代码中清理它们。一旦某个功能经过严格测试并被用户采用,它就应该成为默认功能。切换的代码和旧版本(如果有)都应该清理。不要落入“配置即切换”系统的陷阱。这样做的陷阱是,您将永远无法维护和测试所有切换的组合,最终导致架构脆弱。

将 CI 构建时间保持在 3 分钟以内

记住文章第一部分中提到的“好”和“坏”工作流程。我们希望避免开发人员频繁切换工作环境。拿起你的手机,设置一个 3 分钟的计时器。看看你在等待某件事时会花多长时间!3 分钟应该是你集中精力、安全高效地从一项任务切换到另一项任务的绝对最大值。

3分钟以内的构建速度对一些团队来说可能看起来很疯狂,但这绝对是可以实现的。这更多地取决于你如何组织工作,而不是你使用的工具。优化构建的方法如下:

  • 使用更多的构建能力:如果您的 CI 工具上没有足够的并发构建,并且构建排队,开发人员就会浪费时间
  • 利用缓存:大多数技术栈在运行全新构建时都需要安装和配置依赖项。您的 CI 工具应该能够在依赖项保持不变的情况下缓存这些步骤,以优化构建时间。
  • 检查你的测试:检查你的测试是否针对时间进行了优化。删除超时和“安全的长时间”等待步骤。如果你有繁重的测试套件需要运行,请考虑将它们移到单独的构建中,并在合并到主分支后运行。它们将不再是持续集成保障的一部分,但繁重的测试无论如何都不应该成为其中的一部分。
  • 拆分代码库:是否必须将所有内容都放在一个存储库中?是否即使只有很小一部分发生变化,也必须对所有内容进行构建和运行测试?这样做或许能带来好处。
  • 有条件地运行测试:仅当某些目录发生更改时才运行测试。如果你的代码库组织良好,这将是一个巨大的优势。

强制对 CI 检查设置短时间限制的好处是,它要求您从根本上改进整个开发过程。

正如吉姆·罗恩所说:

“成为百万富翁不是为了获得百万美元,而是为了实现这一目标后你能获得什么”

虚拟合并:你的代码本身并不重要

大多数持续集成工具都会在你的分支上运行 CI 构建,以判断是否可以合并。但这不是我们感兴趣的。如果你知道自己在做什么,那么你刚刚推送的代码很可能已经可以正常工作了!你的 CI 工具应该验证的是,与主分支合并后的分支是否正常工作。

您的 CI 工具应该将您的分支本地合并到主分支,并针对主分支运行构建和测试。如果主分支在此期间没有更改,您的分支就可以自动合并。如果主分支确实更改,则应再次运行 CI 检查,直到您的代码可以安全合并。如果您的 CI 工具不支持此类工作流程,请更换您的工具。

邪恶的任务管理器

有一种误解认为,能够在敏捷开发板或类似 JIRA 的 bug 跟踪器中追踪与任务相关的代码是一件很酷的事情。虽然理论上这是一个很好的概念,但它对开发过程的影响肯定不值得付出努力。任务管理器提供了一个“功能和 bug”的视角。代码的结构和分层方式截然不同。尝试将任务管理器中的某个项目与一组提交进行协调是毫无意义的。如果你想知道一段代码的编写原因,你应该能够从代码上下文和注释中获取信息。

最后的想法

工具只是工具而已。设置工具可能只需要一个小时。但如果使用不当,就无法获得预期的结果。

请记住我们为持续集成设定的目标:

  • 快速安全地交付工作代码
  • 优化开发人员时间并减少上下文切换

真正的重点是转变你的思维方式,为你的项目或产品“持续创造价值”。

将您的软件开发流程想象成一个硬件生产设施。开发人员的代码代表着移动部件。主分支是组装好的产品。

您越快将各个部分集成在一起并检查其是否正常运行,最终获得可正常运行的产品就越安全。

一些实际的例子:

  • 您正在开发一项新功能,需要更改其他人很可能会用到的底层组件。请为该通用组件创建一个专门的提交,并将其合并。然后继续开发其余功能。其他开发者将能够立即基于您的更改开展工作。
  • 您正在开发一个需要大量时间和代码的大型功能?请使用功能切换开关。永远不要孤立地工作!
  • 您正在等待代码审查,但没有人可以做。如果您的代码通过了 CI 检查,那么只需合并它,然后再进行代码审查即可。如果这听起来像是在破坏流程,请记住“完成胜于完美”。如果它运行良好,那么它在主分支上的价值将远高于搁置数日。

感谢阅读!

本文最初于 2019 年 4 月 9 日发表于fire.ci。

文章来源:https://dev.to/jpdelimat/how-to-get-started-with-continuous-integration-1o9b
PREV
什么是 CORS?
NEXT
使用 Github 组织你的生活 问题 精彩瞬间 我是如何做到的