大型公司为特定用例定制开源项目时面临的主要挑战之一,是与上游变更保持同步。在 PlanetScale,我们在管理私有修改的同时,还要应对开源 Vitess 项目的持续演进,这让我们亲身遇到了这个问题。
早期,当我们的私有变更相对较小时,我们使用一种简单的方法来维护差异。每周,一个通过 cron 运行的 GitHub Action 会将所有私有变更 cherry-pick 到主分支的最新更新之上。虽然这种方法最初有效,但随着 PlanetScale 的私有差异随着我们日益复杂的需求而增长,它很快就变得不可持续。
当我们决定与 Vitess 的稳定发布版本对齐,而不是使用主分支的最新代码时,情况变得更加复杂。这引入了一个额外的挑战:不仅要在主分支上维护私有差异,还要在多个发布分支上维护。
我们早期的观察之一是,当将私有提交 cherry-pick 到多个发布分支时,我们经常反复遇到相同的冲突。为了解决这个问题,我们开发了一个工具,可以按顺序处理所有相关提交,并将它们重放到开源(OSS)分支之上。这个工具被恰当地命名为 git-replay,它可以存储 cherry-pick 过程中特定冲突的解决方式,并在未来复用这些信息来解决类似的冲突。
虽然 git-replay 相比我们之前的手动流程有了显著改进,但它并非没有局限性。仍然需要有人运行该工具,手动解决任何未识别的冲突,并验证所有变更是否被正确 cherry-pick。为了确保没有遗漏任何提交,我们生成 OSS 分支与变基后的私有分支之间的代码差异,然后由仓库不同部分的代码负责人进行艰苦的手动审查。
我们在多个发布版本中使用了这个工具,但随着我们的私有差异集比以往任何时候都大,git-replay 的局限性变得越来越明显。显然,需要一个更全面的解决方案。这一认识促成了 Vitess cherry-pick bot 的诞生,标志着我们管理私有差异集方式的重大变革。
随着我们的私有 diffset 持续增长,我们意识到需要一种更连续、更高效的流程。与其每次需要新版本时都执行一次大规模的 cherry-pick 操作,我们设想了一个系统,让开源(OSS)变更能够无缝流入我们的私有 fork。这促使我们定义了一个模型,用于持续跟踪 OSS Vitess 主分支中的变更,并将其与私有 fork 中对应的 upstream 分支同步。我们还建立了 Vitess 发布分支的私有对应分支。这些私有分支也将包含我们的私有 diff。
| OSS 分支 | 私有对应分支 | | --- | --- | | main | upstream | | release-22.0 | latest-22.0 | | release-x.0 | latest-x.0 |
为了实现这一点,我们为新流程设定了以下需求:
任何合并到 OSS main 的 PR 都应被 cherry-pick 到私有 upstream。
任何从 OSS main 回溯移植到 OSS 发布分支 release-x.0 的 PR 都应触发将对应的 cherry-pick PR 回溯移植到私有分支(latest-x.0)。
私有变更将继续进入私有 upstream,并根据需要 cherry-pick 到私有 latest 分支。
每当从 OSS main 切出新的 OSS 发布分支时,应从私有 upstream 创建对应的私有 latest 分支。
尽可能自动化此流程,以尽量减少手动操作。
这个新流程带来了几个明显的好处:
我们不再需要显式维护私有 diffset。
来自 OSS 的 PR 将连续流入私有 fork,确保其保持最新状态。
带着这些目标,我们开始开发一个 bot,以尽可能自动化和简化此工作流。这标志着 Vitess cherry-pick bot 的诞生。
需求确定后,我们开始探索如何让这个 bot 成为现实。出现了几个关键的设计问题:
bot 应该托管在专用服务器上,还是利用 GitHub Actions 和 applications?
它应该是无状态的,将所有信息存储在 PR 中,还是有状态的,使用专用数据存储?
经过深入讨论,我们选择了一种使用 GitHub Actions 按 cron 计划定期运行的解决方案,其状态存储在 PlanetScale 数据库实例中。该机器人按每小时一次的 cron 计划在 GitHub Actions 中运行,并执行两项核心任务:Cherry-Picking 和 Backporting。
该机器人使用 go-github 库与 GitHub API(应用程序编程接口) 交互,从 Vitess 仓库获取最近关闭的 PR。
它会过滤掉未合并即关闭的 PR,并将剩余的 PR 插入数据库以进行 cherry-picking。
为了确定需要回溯多远的历史,机器人一旦遇到早于数据库中已有任何 PR 的 PR 就会停止。
Cherry-picking 和 PR 创建完全在工作流内完成。
该机器人使用 GitHub token 进行身份验证,以检出 vitess-private 仓库并针对其创建 PR。
即使 cherry-picking 过程中出现冲突,工作流也会创建 PR,以确保不会遗漏任何 PR。
PR 继承原始 PR 的标题和描述。原始作者或合并该 PR 的人被指定为受理人,除非他们是非 PlanetScale 贡献者,在这种情况下不指定任何人。
如果存在冲突,机器人会创建一个草稿 PR,并带有 do not merge 和 Conflict 等标签。
它会在 PR 上评论 Git status 输出,突出显示存在冲突的文件,并标记原始 PR 作者以解决问题。
流程完成后,机器人会在数据库中将该 PR 标记为已 cherry-picked。
将 PR 从上游 backport 到最新分支的过程与 cherry-picking 类似,但有几个关键区别:
Backports 不会自动触发。相反,它们依赖于在 vitess-private 仓库中应用于 PR 的标签。
像 Backport to: latest-x.0 这样的标签会通知机器人启动 backport。
一旦应用了标签,机器人就会执行 backport 步骤,遵循类似于 cherry-picking 的工作流程。
这种方法使我们能够自动化和简化大量手动工作,以保持我们的私有 fork 与 OSS 变更同步。机器人的有状态设计和 GitHub Actions 的使用提供了一个健壮、可扩展的解决方案,以管理我们工作流程中日益增长的复杂性。
与任何关键流程的重大重构或彻底改革一样,实施保障措施以确保可靠性至关重要。为此,我们直接在机器人中构建了完整性检查,每周运行一次。这些检查提供任何差异的摘要,允许在必要时进行手动干预。
机器人每周执行两项关键的对账测试:
上游对账测试 此测试确保上游分支与 OSS 变更保持同步。它会标记以下问题:针对上游的开放 cherry-pick PR。来自 main 但未 cherry-pick 到上游的 PR。直接合并到 latest 分支而不是从上游 backport 的 PR。
Latest 分支对账测试 此测试确保 latest 分支(例如 latest-21.0)保持一致。它会识别:针对 latest 分支的开放 backport PR。合并到 latest 但不是 backport 的 PR。backport 到 latest-x.0 但未 backport 到其他更高编号 latest 分支(如果有)的 PR。
机器人每周将这些检查的摘要发布到一个专用的 GitHub issue,提供对可能需要手动检查或操作的任何问题的可见性。
有了这些安全措施,我们谨慎地推出了新流程并开始使用 Vitess cherry-pick 机器人。一年半后,结果非常显著。该机器人节省了无数小时的工程时间,使我们的团队能够专注于为用户构建创新功能,而不是手动 cherry-pick PR!
转载声明:本文为转载整理,原文版权归原作者及原站点所有。
原文来源:PlanetScale Blog
原创地址:https://planetscale.com/blog/automating-cherry-picks-between-oss-and-private-forks
原文作者:PlanetScale Blog;如有侵权,请联系管理员删除。