告别“本地能跑线上崩”:深入解析TeamCity预测试提交功能
本文由 Adeyinka Adegbenro 和 draft.dev 呈现。
开发人员深知这种挫败感。代码在他们的笔记本电脑上运行完美,但一旦进入预发布或生产环境就会崩溃。很容易忽略细微的环境差异,这些差异隐藏了潜在的 bug,如竞态条件、平台特定的库加载失败、数据不一致(大数据集会暴露超时等隐藏的边缘情况)、网络变化、不稳定的测试以及内存问题。
即使经过仔细测试,有些问题也只会在共享环境中出现。仅靠本地检查无法捕捉到所有边缘情况,而且有时推送未验证的更改是不可避免的。这就是为什么在代码到达共享仓库之前捕获测试失败如此重要的原因。它可以防止集成问题,并确保只有通过测试的提交才能进入共享分支。
在本文中,您将了解 TeamCity 的预测试提交功能 如何阻止损坏的代码进入您的仓库。我们将解释什么是预测试(门控)提交,并演示如何使用远程运行和 IDE 集成来实现 TeamCity 的工作流程。
问题:未验证提交导致的构建失败
在软件开发中,未验证的提交很常见。它们加快了个人工作流程,但也增加了构建失败的风险。
通常,开发人员运行本地测试,提交更改,然后在同行评审或持续集成服务器验证之前推送到共享仓库。如果出现错误(特别是由本地和生产环境之间的差异引起的错误),可能会扰乱整个团队的工作流程。
以数据库连接为例。在本地,您可能将一个服务连接到您的数据库,并保持在数据库的最大连接限制内。但在生产环境中,多个工作进程连接到同一个数据库,很快就会达到最大连接数限制并触发超时。
当这些差异未被察觉时,结果往往是一连串的级联故障。任何以该分支为基准的人现在都拥有糟糕的代码。依赖该分支的其他开发人员可能也不得不花费时间调试代码,特别是如果引入该 bug 的原始开发人员不在场。这是对时间和资源的巨大浪费,而通过使用像 TeamCity 这样强大的 CI 服务器进行验证的预测试提交工作流程,本可以避免这种情况。
随着时间的推移,如果主分支经常损坏,开发人员会因为担心不稳定而犹豫是否拉取最新更改。这种信心的丧失会导致开发人员孤立工作,最终导致多次合并冲突,这违背了将版本控制作为协作开发工具的初衷。
TeamCity 中的预测试提交是什么?
TeamCity 的预测试提交,也称为门控提交或延迟提交,颠倒了“先提交后构建”的工作流程。它颠覆了典型的编辑、提交、推送和构建流程(在这个流程中,您只能祈祷构建在持续集成平台的服务器上通过)。您编辑代码,然后在提交之前在 TeamCity 服务器上构建更改。
CI 构建包括代码编译、测试、lint 检查以及在构建配置中定义的任何其他预定检查。如果构建失败,代码就不会被提交,开发人员可以在不影响整个团队流程的情况下修复问题。但如果代码构建通过了测试,TeamCity 或开发人员可以自动将更改提交到版本控制。
[LOADING...]
预测试提交工作流程
预测试提交工作流程通过在更改进入主分支之前运行完整的构建和测试周期来保证代码质量。其实现因所使用的版本控制系统 (VCS) 类型而有很大差异。
对于像 Git 这样的分布式系统,预测试提交围绕功能分支构建,因此无需直接将补丁应用到主分支。这通过本地隔离的测试和提交保持了并行开发的安全性。TeamCity 可以针对您在本地进行的暂存更改的临时补丁进行测试,但为了避免竞态条件,它不会执行最终的自动提交。相反,它通过所谓的分支远程运行使用专用的验证分支。
下面描述的工作流程基于 Git。
创建项目
一旦您拥有 TeamCity 实例,您需要手动创建项目,或者通过输入现有项目的仓库 URL(例如 git@github.com:your-org/your-repo.git)来创建。如果您选择仓库选项,系统会提示您登录到版本控制主机(例如 GitHub、GitLab 或 Bitbucket),并且您需要提供必要的身份验证凭据:
[LOADING...]
配置项目
接下来,您需要输入一些初步设置:构建名称、默认分支名称和分支规范。对于大多数 Git 项目,默认分支是 refs/heads/main 或 refs/heads/master。
在分支规范选项中,请确保至少输入一个分支,每个分支占一行。这会告诉 TeamCity 监控哪些分支的更改。这是一个示例分支规范:
[LOADING...]
点击 Proceed 继续进入构建步骤。
添加构建步骤
点击 Proceed 后,您必须通过单击构建页面上的 Build Steps 选项卡来添加构建步骤。构建步骤定义了验证代码所需的实际命令序列。无论分支类型(main 或 feature/*)如何,这些步骤都会运行。一个假设的 Node.js 项目的最小命令行构建配置可能如下所示:
[LOADING...]
不要忘记保存您的更改并运行构建,以确保它在默认分支上工作。
预测试验证
添加构建步骤后,开发人员需要在本地处理他们的隔离功能分支,频繁地进行和提交更改(例如名为 feature/login-flow 的分支)。为了启动预测试,开发人员将其本地功能分支推送到带有 remote-run/ 前缀的远程仓库。TeamCity 会自动监视任何带有 remote-run 前缀的分支,并在代码推送到那里后运行自动构建。
[LOADING...]
集成
一旦 remote-run/login-flow 构建完成,状态将决定下一步。如果失败,开发人员会查看构建日志,在他们的功能分支上本地修复问题,并重复推送到临时的 remote-run/login-flow 分支。
[LOADING...]
如果构建成功,开发人员将删除临时的远程分支。功能分支(feature/login-flow)现在已被证明是稳定的,并准备好进行最终操作:
[LOADING...]
开发人员现在可以提交并与主分支合并,或者从他们经过预测试的功能分支创建拉取请求。
在像 SVN 或 Perforce 这样的集中式版本控制系统中,TeamCity 的远程运行功能允许开发人员使用补丁(未提交更改的捆绑包)验证未提交的本地更改。开发人员使用像 IntelliJ IDEA 这样的 IDE 和 TeamCity 插件将补丁发送到构建服务器,然后 TeamCity 构建并测试该补丁。如果成功,TeamCity 会自动将更改提交到主仓库,从而完成预测试提交。
[LOADING...]
使用预测试提交的好处
预测试提交将验证从开发人员的机器转移到团队商定的 CI 环境。代码只有在通过指定的检查后才会被添加到主分支,因此失败的构建永远不会干扰其他人的工作。
这保持了集成的整洁,并及早发现回归。每个人都能获得一个稳定的分支基础。您知道最新版本确实有效,也不必花费数小时去追逐由其他人的构建引入的错误。
它还减少了挫败感。当团队不浪费时间修复别人的错误时,他们可以专注于自己的功能。而且因为您在预测试期间会立即获得反馈,所以您可以在问题变成别人的问题之前自己发现它们。
这些好处是累积的。您的提交历史保持专注于真正的进步,而不是充斥着诸如“修复拼写错误”、“修复 lint 问题”、“添加导致构建失败的缺失依赖项”或“添加类型检查”之类的提交信息。审查者可以专注于有意义的代码更改,而不是其他噪音。您的项目历史讲述的是代码如何演变的故事,而不是它经常崩溃的故事。
最终,预测试提交支持持续交付目标,特别是对于频繁发布并依赖稳定版本的敏捷团队。团队可以放心,因为他们的生产代码已经通过了自动化、强制的检查。
VCS 和配置注意事项
为了在 TeamCity 中顺利运行预测试提交,您需要注意一些版本控制和配置细节:
- 广泛的 VCS 集成: TeamCity 支持所有主要版本控制平台。Subversion、Perforce 和 TFVC 等集中式系统可以在 IDE 中使用远程运行,而 Git(GitHub、GitLab、Bitbucket 或 Azure DevOps)和 Mercurial 等分布式系统使用分支远程运行。
- IDE 插件设置: 在 IDE 中使用预测试提交功能(远程运行)取决于 TeamCity IDE 插件的安装。该插件允许您选择本地未提交的更改并将它们直接发送到 TeamCity 服务器进行验证。
- 分支规范: 您在 TeamCity UI 中的构建配置需要适当的分支规范(例如
+:refs/heads/*),以便 TeamCity 知道要自动监视和测试哪些分支。 - 参数和密钥: 在 TeamCity UI 的项目或构建配置级别定义所有构建参数(尤其是安全密钥)。TeamCity 将在个人构建期间安全地插入它们。这种分离确保代码不包含敏感配置细节。可以在仪表板右上角启用 Settings 模式后,在项目和构建设置中找到参数设置。
- 匹配的仓库 URL: 如果您在 IDE 中使用远程运行,请确保在 IntelliJ IDEA(或您的 IDE)中配置的仓库 URL 与 TeamCity 服务器站点中定义的 URL 完全匹配。即使是很小的差异(例如
https://github.com/acct/repo.git与https://github.com/acct/repo)也可能导致 TeamCity 无法将补丁识别为属于正确的 VCS 根目录。 - 构建触发器: 触发器允许您控制在您在设置中配置的特定情况/事件下运行构建的时间。例如,如果特定用户提交更改或提交消息中存在某个短语,您可以跳过触发构建。在构建设置的 Triggers 选项卡中配置此选项。
- 构建配置: 确保您的构建配置尽可能与您的分支/提交工作流程匹配,以保持一致性。这有助于确保用于测试开发人员更改的逻辑与用于合并到主分支的最终逻辑相似。例如,如果您的主分支运行数据库迁移,您的个人构建应该包括相同的设置。
何时使用预测试提交与替代方案
预测试提交很强大,但并不总是适合每个项目的工具。在将它们纳入您的工作流程之前,您需要考虑项目规模、分支稳定性以及测试运行所需的时间。
预测试提交最适合拥有单一稳定分支且稳定性很重要的团队。当您拥有可靠的自动化测试并且正在推动持续集成和交付时,它们也是一个很好的选择。
如果您的测试套件和检查很大,耗时很长(即 15 分钟),占用内存或使用生产级数据,远程运行预测试提交可以释放开发人员的机器并保持他们的生产力。
但是,如果您的团队严重依赖功能分支和长生命周期分支工作流程,拉取请求和合并门控可能更合适。而且如果您的测试套件不完整或不稳定,预测试提交不会有太大帮助;它们的有效性取决于支持它们的测试。
代码审查和预发布环境,与预测试提交一起使用,可能有助于探索性测试,以发现不稳定的测试套件无法覆盖的缺陷。对于小团队、独立开发人员或拥有小代码库的团队,带有快速反馈的手动提交可能更简单。
这并不总是一个非此即彼的选择。预测试提交可以分层在现有工作流程之上。例如,一个功能分支可能有多个开发人员贡献。每个开发人员使用预测试提交来确保只有通过的提交才能到达共享功能分支。一旦功能完成,团队仍然会打开一个 PR 以合并到 main(或 master)。在该阶段,PR 流程在最终合并之前提供了额外的代码审查和 CI 检查层。
结论
预测试提交为团队提供了一种保证只有经过测试且有效的代码才能进入主分支的方法。这将集成检查的责任转移到了 CI 服务器,使开发人员能够专注于编写功能,并信任系统会强制执行质量。
虽然这种工作流程并不适合每个团队,但对于优先考虑稳定性和持续交付的环境来说,它可能是变革性的。请记住,预测试提交工作流程的有效性取决于其测试和检查。如果您的测试不可靠,错误可能会漏网并导致问题。
JetBrains TeamCity 为团队提供了自动强制执行质量检查所需的一切,从允许您直接触发远程运行的 IDE 插件到灵活的分支远程运行。如果您目前正在使用 Jenkins 并想探索如何切换到 TeamCity,请查看我们的迁移规划工具包。如需深入了解平台功能,JetBrains 还提供详细的资源供您探索。