使用约定式提交进行语义化版本控制
全文机翻,有小幅语法调整,不会或无法翻译的部分保留原文。
原文: Semantic Versioning with Conventional Commits
使用约定式提交进行语义化版本控制
版本控制很重要。我不必告诉你这个。然而,我看到它一遍又一遍地做得不好。
我经常遇到的缺陷是缺乏明确的开发和发布流程以及较差的工具支持。如果你的开发人员坐在那里想知道如何完成某些任务,那么这个过程就被打破了。他们应该知道如何发布新版本或如何在主干移动时修复生产。您的 CI/CD 流程也应该 支持这些场景!
许多人依靠他们的 CI/CD 工具来确定他们应用程序的下一个版本。我也为此感到内疚,但在某些时候,我意识到这是只有开发人员才能做出的决定。我们的工具还不够智能,无法查看代码更改并告诉我它是功能、修复还是其他什么。
这让我产生了改变我们做事方式的强烈愿望。我的目标是拥有一个涵盖所有这些用例的明确流程,对开发人员友好,并在我们倾向于跨项目使用的各种 CI/CD 工具中得到支持。
这篇文章记录了这种方法。
语义版本控制
我们将在这里使用语义版本控制(semver)。这可能是当今软件中使用的最突出的版本控制方案。
我们构建的大多数应用程序和库都倾向于公开一个 API,无论是 REST API、接口等。Semver 就是对这个 API 进行版本控制。
语法是众所周知的:
<MAJOR>.<MINOR>.<PATCH>
MAJOR | MINOR | PATCH |
---|---|---|
引入新的向后不兼容更改 | 引入新的向后兼容更改 | 在保持向后兼容性的同时修复一个错误 |
1.0.0 -> 2.0.0 | 1.0.0 -> 1.1.0 | 1.0.0 -> 1.0.1 |
如表所示,向后兼容性是版本冲突的一大区别。
有些人仍然没有搞懂,所以在这里我将使用 JSON REST API 来说明什么构成补丁、功能或重大更改。
让我们假设一个称职的开发人员已经实现了这个 REST API,并且它成功地遵循了 Postel 定律:
Major
- 删除一个操作,比如删除 HTTP 动词/路径组合
- 向某个请求添加必填字段
- 向某个请求添加/删除字段
- 如果所有用户被认为足够宽容,那么添加一个字段可能不会是一个重大变化
Minor
- 添加一个新操作,即新的 HTTP 动词/路径组合
- 向请求添加可选字段
Patch
- 没有接口变化
约定式提交
如今,尽管工具非常好,但它们还无法识别代码更改的本质。通过机器学习的那一天将会到来,这将是可能的,但就目前而言,我们必须依靠良好的老式人类智能。
约定式提交提供了在开发人员和 CI/CD 工具之间传达提交中更改的性质的机制。
简而言之,开发人员提供了一个提交消息,该消息明确地标识了更改的性质。然后 CI/CD 工具可以扫描自上一个版本以来的所有提交消息,并确定如何更新版本。
除了这种自动化之外,这种方法还为其他团队成员提供了清晰的变更沟通,甚至让我们自动生成发布 release note 和 changelog。
约定是提交的官方总结和示例非常简洁,我没有必要在这里重复它们。来看看吧。我会在这里等你。
以下是我们的 JSON REST API 的一些示例提交:
Major
-
feat: disable deletion of records
BREAKING CHANGE: removed and endpoint
-
feat: expiry date must be provided by the user
BREAKING CHANGE: new mandatory field in create operation
Minor
- feat(my-operation): allow users to provide an optional name to override the default
- feat: add operation to retrieve sub-records
Patch
- fix: remove the infinite loop
- fix(my-operation): handle null input field
N/A
- test: refactor user management test cases
- ci: point to the new registry
- docs: add missing method documentation in create operation
如果您的团队更“有趣”,您可以随时尝试这些替代信号:✨ (feat), 🐛 (fix), 📚 (docs), 💎 (style), ♻️ (refactor), 🚀 (perf), ✅ (test), 📦 (build), 👷 (ci), 🔧 (chore)
你在构建什么?
如果您曾在软件开发中花费过任何时间,您就会知道提倡“一刀切”的人需要滚蛋了。因此,在这篇文章中,我想研究两种截然不同的软件发布方法,以及如何将这种技术应用于这两种方法。
合并后发布
- 描述
- 主干分支上的每个提交都是具有新语义版本的发布
- 新版本没什么大不了的,版本不被视为神圣的有限资源
- 一个应用程序每天可能会经历多个版本
- 修改需要通过 QA 关卡,比如单元测试、同事评审、MR 构建甚至 review app
- 优势
- 无预先发版计划。在需要时选择要发布的版本
- 对测试人员和最终用户的反馈循环更短
- 使用时机
- 供应商/顾问交付的项目
- 有多个非生产环境可用于快速部署和测试更改的项目
预发布后正式发布
- 描述
- 每个版本之前都有一个或多个 alpha 和 beta 版本形式的预发布
- 版本更新被认为是珍贵的
- 从预发布版本中删除版本时需要重新测试
- 优势
- 通过在引入稳定版本之前发布预发布来获得充分的信心
- 您的用户不必像在其他方法中那样想知道预发布版本发生了什么
- 使用时机
- 库、软件产品和开源项目
- 稳定发布节奏较慢的项目
我已经在很多地方看到了这两种方法的许多变体,因此我们将在这里讨论的内容也应该同样适用于这些方法。 虽然我应该提到我倾向于避免破坏版本不变性的样式,例如 Maven 中的 SNAPSHOT 版本或 NPM 中的 @next disributaion channel。
Pull Requests
无论您如何发布,我都希望您通过 PR(有时称为 MR)引入新功能。 如果你不是,我们有比版本控制更大的问题。
理想情况下,每个拉取请求都应包含一个功能或修复。 作为拉取请求审查的一部分,开发人员可能需要提交更多更改以解决审查意见。 但是这些额外的提交并不是主干上的新 功能或代码修复。
我的解决方案是使用 squash merge 策略。 这样,开发人员可以在功能/修复分支上对他们的提交消息做任何他们喜欢的事情。 这些提交将全部消失,开发人员可以在合并点为整个拉取请求提供约定是提交消息。
大多数体面的 Git 存储库还允许您使用您的拉取请求名称作为您的 squash 提交消息。 如果您希望从开发人员那里看到一致的拉取请求名称并让审阅者甚至在批准之前审阅 squash 提交消息,这很好。
合并后发布的分支策略
不管发布风格如何,我倾向于基于主干(主线)的分支。我避免使用 Gitflow,因为我足够关心我的开发人员,以免他们每天都花在解决合并冲突上。更不用说,仅仅因为你从 develop 合并到 master 就重新构建相同版本的应用程序,面对“一次构建,多次部署”的CI/CD实践。
现在我已经完成了我对 GitFlow 的每日一喷,接下来让我们来谈谈我们在每次合并时发布时如何进行分支。
这很简单:创建一个功能/修复分支并遵循上面的拉取请求过程。
作为 CI/CD 流程设计师,您的主要目标之一应该是:开发人员最常做的任何事情都应该是最容易做到的。我觉得上面符合这个标准。
不太常见的情况也没有那么困难。假设开发人员正在构建应用程序的新主要版本,但之前的主要版本存在生产缺陷,需要热修复。这是执行此修补程序的剧本:
- 找出生产中的次要版本。比方说v1.3。