自动化处理开源与私有分支间的 Cherry-Pick
引言:保持同步的挑战
大型公司在为其特定使用场景定制开源项目时,通常面临的一个主要挑战是如何与上游变更保持一致。在 PlanetScale,我们在管理开源 Vitess 项目的持续演进与我们的私有定制修改时深刻体会到了这一问题。
初期阶段:手动处理
起初,当我们的私有更改还相对较小时,我们采用了一种简单的方式来维护这些差异。每周会有一个基于 GitHub Action 的定时任务运行,它会将所有私有更改 cherry-pick 到开源项目主分支的最新代码上。虽然这种方法在开始时可以工作,但随着 PlanetScale 的私有差异随着需求扩展而变得复杂,这种方法迅速变得不可持续。
特别是当我们决定与 Vitess 稳定发布版本保持同步,而非主分支最新代码时,挑战更为严重。这又新增了一项任务:不仅要维护主分支,还需维护多个发布分支的私有差异。
第一次尝试:Git-replay
在处理私有提交到多个发布分支时,我们频繁遇到相同的冲突。为了应对这一问题,我们开发了一种工具,可以按顺序处理所有相关提交,并在开源分支上重放这些提交。这一工具被命名为 **git-replay**,它能够记录在 cherry-pick 冲突中指定的解决方式,并在未来遇到类似冲突时复用这些信息。
虽然 git-replay 是对我们手动流程的一大提升,但它仍有局限性:
- 仍然需要人工运行工具,解决未识别的冲突并验证所有代码是否正确地被 cherry-pick。
- 为确保没有提交遗漏,需生成开源分支和私有分支的代码差异,然后由代码所有者对仓库的不同部分进行令人费神的手动审查。
尽管我们在多个版本中使用了这个工具,但随着私有差异集规模进一步增长,git-replay 的缺点变得越来越明显。我们意识到需要一种更全面的解决方案。这一需求促使我们开发了 Vitess Cherry-Pick Bot,实现管理私有差异集的重大改进。
新方法的开始:需求分析
随着私有差异集持续增长,我们认识到需要一个更连续、更加高效的流程。我们希望不再每次需要新版本时进行大量的 cherry-pick,而是建立一个系统,使开源项目的变更能无缝流向私有分支。因此,我们定义了一个模型:
- 持续跟踪开源 Vitess 主分支(OSS main)的变更,并使其与我们私有分支中的对应 “upstream” 分支保持同步;
- 设置 Vitess 发布分支的私有版本,如
release-x.0
对应latest-x.0
分支; - 这些私有分支还需包含我们的私有差异集。
开源分支 | 私有对应分支 |
main | upstream |
release-22.0 | latest-22.0 |
release-x.0 | latest-x.0 |
关键需求
为实现目标,我们制定了以下需求:
- 任何合并到开源主分支的 PR 应被 cherry-pick 到私有
upstream
中; - 开源主分支的 PR 被回溯到发布分支
release-x.0
时,应触发对应的 PR 被回溯到私有发布分支latest-x.0
; - 私有更改继续进入
upstream
,并根据需要被 cherry-pick 到各私有最新分支; - 每当开源发布分支从主分支创建,应对应生成一个私有分支;
- 尽量自动化整个过程,减少人工操作。
自动化的价值
新流程带来了以下显著益处:
- 不再需要显式维护私有差异集;
- 开源 PR 会持续流入私有分支,确保其保持同步。
因此,我们开始开发一个自动化的 bot,以尽可能优化和简化这一工作流,这便是 Vitess Cherry-Pick Bot 的诞生。
Vitess Cherry-Pick Bot 的设计
在定义需求后,我们开始探索如何开发 bot。过程中我们遇到了几个关键性设计问题:
- 是否应该将 bot 部署在专用服务器上,还是使用 GitHub Actions 与应用集成?
- bot 是否应该是无状态的,将所有信息存储在 PR 中,还是有状态的,并使用专用数据库?
经过深入讨论,我们选择使用 GitHub Actions 的时间触发功能,并将状态存储在 PlanetScale 数据库中。bot 每小时运行一次,任务分为两大核心模块:**Cherry-Picking** 和 **Backporting**。
Cherry-Picking 模块
- 识别需要 Cherry-Picking 的 PR
- bot 使用
go-github
库与 GitHub API 交互,从 Vitess 仓库获取最近关闭的 PR。 - 过滤未合并即关闭的 PR,仅保留合并的内容并插入数据库。
- bot 停止历史回溯的时间点为数据库中现有的最早 PR 日期。
- bot 使用
- 执行 Cherry-Picking
- cherry-pick 和 PR 创建完全在工作流内完成,bot 使用 GitHub Token 验证并操作私有仓库。
- 处理冲突
- 如果出现冲突,工作流仍会创建一个草稿 PR(status为
do not merge
),并在 PR 中标明冲突详细信息。 - bot 会注释冲突文件并提醒原始 PR 作者解决问题。
- 如果出现冲突,工作流仍会创建一个草稿 PR(status为
- 完成 Cherry-Picking
- 流程完成后,bot 将 PR 标记为已 cherry-picked。
Backporting 模块
Backporting 流程类似于 Cherry-Picking,主要区别是:
- Backports 不会自动触发,而是依赖于
vitess-private
仓库中的标签,例如Backport to: latest-x.0
。 - 一旦标签被应用,bot 按 Cherry-Pick 的流程执行 Backporting 操作。
构建可靠性
为了确保流程的可靠性,我们为 bot 添加了完整性检查,每周运行一次。这些检查生成差异性摘要,供人工干预。
每周完整性检查
- 上游同步检查
- 确保
upstream
分支与开源 Vitess 变更保持一致; - 标记以下问题:
- 未合并到
upstream
的 PR; - 未被 cherry-pick 的内容;
- 不正确直接合并到最新分支的 PR。
- 未合并到
- 确保
- 最新分支检查
- 检查最新分支(例如
latest-21.0
)的一致性; - 标记以下问题:
- 最新分支未完成的 Backport PR;
- 合并到最新分支但未通过 Backport 的 PR;
- 未同步到所有相关最新分支的 Backport PR。
- 检查最新分支(例如
结果与未来
有了这些保障措施后,我们谨慎地推出了新流程,并开始使用 Vitess Cherry-Pick Bot。经过一年多的运行,结果令人满意:
- bot 节省了开发团队大量时间,使我们能够专注于为用户构建创新功能,而不是繁琐的手动 cherry-pick。
Vitess Cherry-Pick Bot 的自动化流程,让我们能够高效管理私有分支与开源项目的同步,成为工作流优化的典范。
关注公众号:程序新视界,一个让你软实力、硬技术同步提升的平台
除非注明,否则均为程序新视界原创文章,转载必须以链接形式标明本文链接