使用 Bors 自动化 pull request 合并
我们如何开始使用 Bors 为 Meilisearch 的贡献者创造更好的环境。

Meilisearch 目前在 GitHub 上维护着超过 50 个开源项目。仅在过去的六个月里,我们就合并了 1000 多个 Pull Request (PR),其中 160 个来自外部贡献者。
为了确保代码库保持稳定,并且代码质量始终一流,我们在合并新的 PR 时必须非常谨慎。随着贡献数量的增长,为了使贡献者操作更简单,并保证 Meilisearch 代码库的安全,自动化此过程变得势在必行。
在本文中,我将描述我们为什么决定使用 Bors,一个用于 Github pull request 的合并机器人,为维护者和贡献者创造更好的环境。
我们的合并流程
在开源贡献期间,任何更改都必须在他们自己的独立分支上完成。之后,当在这个分支上的工作完成后,我们在合并之前的主要要求之一是新分支与主分支保持同步——在我们的大多数仓库中称为 main
。
如果 PR 没有与 main
同步更新,你可以说大部分工作已经完成,但是对我们来说,大部分工作是不够的。即使所有自动化测试在 PR 本身通过,如果 PR 分支和 main
没有对齐,也不能保证在合并时也能通过。
在使用 Bors 之前,这意味着我们经常不得不要求 PR 作者 rebase 他们的分支,例如,如果在此期间合并了不同的 PR。 这使得贡献比我们预期的更复杂,因为并非每个人都熟悉 Git 的 rebase
命令。
为什么 rebase?
在 Meilisearch,我们认为保持清晰的 Git 历史记录非常重要,原因有两点:1 它允许每个人跟踪对项目所做的所有更改 2 在调试时,清晰的历史记录有助于识别哪个 commit 引入了问题
rebase 的目标是将 PR 分支中的 commit 移动到 main 上最后一个 commit 之后。 因此,rebase 使您的 Git 历史记录保持清晰,并与您合并 PR 的顺序保持一致。
Rebase 不是使分支与 main
分支同步更新的唯一方法:例如,您也可以将 main 中的更改 pull 到 PR 分支。 这种方法的问题是 pull 会向 PR 分支添加一个 merge commit,这将无法保持仓库历史记录的清晰。
考虑到维护清晰的 commit 树是多么重要,rebase 是保证即将合并的工作符合当前生产环境工作的唯一方法。 如果测试在最新的分支上通过,那么一旦合并到 main
中,它们也会通过,从而确保您的生产分支不会崩溃。
找到完美的工具
考虑到我们的 rebase 要求,我开始寻找完美的工具。 我希望提高效率,所以我缩小了范围,只评估了以下工具:
- 免费且开源: 该工具必须符合 Meilisearch 及其社区的价值观
- 评分高: 该工具必须稳定可靠,因为在处理 PR 时犯任何错误都必然会产生重大影响
- 文档齐全且易于设置: 良好的文档可以节省时间,我们可以用这些时间来改进 Meilisearch
不幸的是,在这个范围内,我没有找到像我预期的那么多解决方案。
我尝试的第一个工具是 Shipits,这是我通过 Shopify 工程博客上的这篇文章 发现的。 虽然 Shipits 看起来非常可靠和有前途,但我感觉它更适合复杂的集成流程,而不是仅仅试图保持 Git 历史记录清晰的开源项目。
我测试的另一个工具是 Kodiak。 设置非常容易,这在很大程度上归功于他们出色的文档。 它非常适合我们的用例,但是自动合并过程基于我们需要管理的标签,这实际上并不符合我们的工作流程。 例如,我们在合并 PR 时的最低要求是获得一位审阅者的批准; 但是,有时我希望两个人审阅一个 PR,并且很容易忘记将此传达给 Kodiak。 一旦收到第一个批准,Kodiak 就会自动合并 PR,因为一位审阅者的要求已得到满足。
我们注意到的另一件事是,Kodiak 不会通过 rebase 更新发起 PR 的分支,而是通过将 main 分支 pull 到 PR 分支来创建一个 Git commit,正如我们所见,这会混淆仓库的历史记录。 最终,Kodiak 并不符合我们的需求,即使它的设置和配置提供了无缝且轻松的体验!
与我研究的所有其他工具相反,Bors 的构建遵循了 Bors 原始创建者在 这篇文章 中描述的独特而强大的原则
软件工程的非火箭科学规则:自动维护一个始终通过所有测试的代码仓库。
这个“规则”完美地符合我们的要求,并使 Bors 成为我们的最终选择。
Bors 是如何工作的?
Bors 是一个合并机器人——意味着它是一个自动化 PR 合并过程某些方面的应用程序。 它的设置很简单,并且像 Kodiak 一样,文档非常齐全。 实际上,配置可以是包含我们要在合并之前执行的测试名称的单个 bors.toml
文件。
所有 Bors 命令都必须通过 PR 中的注释给出。 要合并 PR,我们只需要使用 bors merge
。 Bors 首先应用 rebase,然后再运行测试,如果测试成功,则将它们合并回 main。 更准确地说,Bors 实际上并没有 rebase PR 的分支:它将 PR 的 commit 合并到另一个分支(称为 staging)中,该分支已经与 main
同步更新。 如果测试在 staging 上未通过,Bors 将返回失败并且不合并 PR。
锦上添花的是:Bors 还能够同时管理多个 PR 的合并,这意味着我们不需要一个一个地合并它们。 每个 PR 都被添加到队列中,并将定期使用 main 上的最新版本进行检查。
如果 Bors 发现 PR 和 main 分支之间存在冲突,它无法决定应用哪些最终更改。 在这种情况下,贡献者必须手动 rebase 分支——如果您遇到这种情况,我编写了这个关于如何从 fork 仓库进行 rebase 的 快速教程。
结论:我们解决了哪些问题?
我们 Meilisearch 已经使用 Bors 几个月了,并且非常满意。 我们知道,在提供稳定代码库的同时保持 Git 历史记录清晰可能非常复杂,但到目前为止,Bors 已帮助我们极大地优化了我们的工作流程。 通过将 Bors 集成到我们的工作流程中,
- 无需请求和等待 PR 作者 rebase
- 无需逐个测试和合并 PR
- 我们的贡献者不会感到沮丧:他们都做了出色的工作,但是他们中的一些人不太熟悉 Git 的 rebase 功能,这确实并不总是容易使用——尤其是在从 fork 仓库工作时!
Bors 使维护者和贡献者都更快乐; 因此,我们可以为具有与我们相同用例的项目推荐它。