使用语义版本控制或 Lerna Publish 从 CI/CD 部署时如何确保 Master 和 Dev 分支保持同步

问题描述

设置

我有几个 gitlab 存储库,其中一般设置包括一个 master 分支、一个 stage(预发布)分支和一个 dev 分支。

所有 3 个分支的推送权限都被禁用。

工作流程是从 dev 分支分叉任何热修复、错误修复和功能。当您对发布感到满意时,您可以向 dev 提交合并请求。最终,当一个稳定的构建在 dev 内准备就绪时;将为 stage 分支提交合并请求。最后,当您对预发布感到满意时,您可以为 master 分支提交合并请求。

我已配置 CI/CD,以便自动masterstage 分支执行测试、构建和部署,并自动生成 CHANGELOG.md 文件stage 分支部署到 UAT s3 Bucket,master 部署到生产 s3 Bucket。

部署是通过 Semantic Versioning 2.0.0 处理的,它负责提升版本、生成变更日志和部署。

我有一个与上面刚刚描述的类似的设置,除了它是一个 monorepo,所以我使用 Lerna 来处理发布(部署)和 {"conventionalCommits": true} 来复制 Semantic Versioning 2.0.0行为。我在 monorepo 中使用独立版本控制。

Semantic Versioning 2.0.0Lerna 设置都强制 master 分支始终落后于或等于 stagedev 分支;并且 stage 分支总是落后于或等于 dev 分支,有点像级联效应。

dev >= stage >= master

问题

Lerna PublishSemantic Versioning 在发布/部署时都会对文件进行一些更改。其中一些更改包括更新 CHANGELOG.md 文件和在 package.json 文件增加版本。

Lerna 和语义版本控制最终都将这些更改推送到通过 CI/CD 运行它们的分支。

这意味着,如果我从 dev 合并到 stage,stage 将通过语义版本控制或 Lerna Publish 执行将颠簸的版本号和新的变更日志推送到其中。这会导致 stage 分支在 dev 分支之前,并且会导致来自 dev 分支的所有未来分支与 stage 分支分离,这意味着下一个分支当我从 dev 合并到 stage 时,它不会是一个简单的 fast-forward 合并,因为它很可能会遇到冲突,这会阻止任何未来的合并或可能会失败CI/CD。

我的解决方法

对于语义版本控制:

  • 我已禁用推送功能,以便不再提交和推送新的、更改的文件(仍然创建和推送标签
  • 我创建了一个脚本,可将 CHANGELOG.md 文件转换为 PDF 并将其发送到我的团队电子邮件

这很有效,因为语义版本控制使用标签来确定更改的文件并决定如何修改版本。因此,尽管存储库中的版本保持不变,例如 1.0.0,但语义版本控制足够智能,可以从最新标签而不是 package.json

中的内容增加版本

不幸的是,这不适用于 Lerna,它仍然使用标签来确定更改的文件,但随后从 package.json 内的版本中颠簸,这意味着通过不将更新的 package.json 推送到新版本, Lerna 总是让我从 1.0.01.0.11.1.02.0.0

所以我被 Lerna 难住了。

问题

我应该如何设置我的 CI/CD 以避免这个问题?我知道我的 repo 的结构很常见,尽管 Lerna 和 Semantic Versioning 的无数用户告诉我,我显然错过了一些东西,因为它不是一个普遍存在的问题,但我还没有找到任何人解决这个问题。>

可能的解决方

在我写这个问题时,我突然想到,也许我应该只在 dev增加版本,然后从 stagemaster 部署。这将阻止 stagemaster 永远领先于 dev,这是正确的做法吗?

解决方法

在 repo 中维护包版本信息,不会扩展。尽管如此,那里的所有工具都在不断努力使其发挥作用。除了这么说之外,我没有什么可以提供替代方案(还);发布数据应通过其他方式进行管理,而不是将其存储在源存储库中。我们在这里真正谈论的是异步流程、开发、构建和发布。这些系统的分布式特性意味着我们不能将存储库视为文件共享并期望它们能够很好地扩展。

my other rant on this topic。我还没有时间就此写一篇好文章。我想补充一点,Git 标签是方便的里程碑标记,供开发人员找到要返回的正确 git 哈希值,并从中创建修复分支。提交消息用于更改日志,并且只是决定从哪个构建发布哪个版本的输入之一。

在多开发、多分支、分布式环境中工作的开发人员不可能预测适当的语义版本以在未来的某个随机点应用。至少在没有对每个开发、分支和构建/发布环境进行完全独裁控制的情况下。即便如此,他们也很难让它发挥作用。当前的工具所暗示的就是这种控制,但在实践中,它永远不会扩展。


考虑到您的软件包提要服务可能拥有您发布历史的全部或足够部分。只要您只有一个提要服务,您就可以使用它来确定下一个版本的版本层。处理您的语义提交,查找与您的流程所针对的标签匹配的最新版本(daily、beta、RC、none 或其他),计算下一个适当的版本,更新源代码中的适当版本字段,然后构建和测试。如果提要服务不允许您在查询中包含隐藏或删除的版本,则您必须使用自己的数据库。不要签入修改过的文件!这些字段应该在您的存储库中归零,有效地将本地开发版本标记为 0.0.-dev 或类似的内容。

预发布版本的自动发布是可以的,但发布版本应该有人参与。如果以上所有步骤都成功,请为您刚刚成功构建的 git hash 应用一个标签。


我梦想的 CI/CD 系统,通过测试构建和单元测试运行对我的发布分支的每次提交,检测现有测试用例是否被修改(自动检测重大更改!),分析提交消息故意破坏的迹象,并根据需要将所有这些信息呈现给发布构建系统。我的发布构建系统生成一个 -alpha.build.### 并针对它运行所有验收测试。

如果没有已知的损坏,并且预期目标是预发布,则它会更新包含版本信息的文件,并在自动发布之前运行增量构建,并进行一次最终冒烟测试。这里有一些灰色区域。一些预发布目标不应该包括破损,没有人为干预,而对于其他人来说没关系。所以我会有一组特殊的预发布目标,不允许自动发布破损,例如某些级别的狗食者,或针对我内部长期运行的测试基础设施的位。

如果它是一个未标记的发布目标,那么我更喜欢它构建和打包所有内容以供我在测试的最后阶段使用。这是自动化验证包裹是否符合政策的地方,确保它可以正确解包,并在发布之前收集区域所有者、部门/部门负责人等的签字。如果我们的目标是实时系统,它可能包括一些随机测试部署。

以上所有内容实际上只是过于简化的描述。它掩盖的比澄清的要多,因为生产者和消费者的实际需求差异太大。

回到工具和控制。 DevOps 世界中有很多人会告诉你,重点是围绕工具进行标准化。这让我想起了this xkcd commic