Teaching PBR about semver

https://blueprints.launchpad.net/oslo/+spec/pbr-semver

OpenStack 使用语义化版本控制(semver),但 pbr 目前并不理解它。教会它理解 semver 将允许开发者减少思考和增加自动化,以及为进行持续部署(CD)的 OpenStack 提供更好、更清晰的版本号。

问题描述

使用后版本化(默认情况下),给定一个 1.3.2 的标签,pbr 会生成每个提交的版本 1.3.2.N.g$sha。pip 目前认为这些版本无效,但如果 pip 了解构建标签 - g$sha,那么该版本将被认为是一个发布版本,但通常情况下,未标记提交的意图是成为用于本地安装/测试的开发者版本。因此,这不符合 PEP-440。与此冲突的是 CD 部署者和 CD 打包者希望拥有单调递增的版本,这些版本可用于在例如 Debian 系统上安装软件包,但后续的标签也可能与这些版本冲突。

PEP-440 版本并不完全映射到 version_info - 一个核心 python 概念,但 pbr 的一些用户在他们的模块中导出 version_info 元组,并且提供它将使他们能够使用 pbr。

最后,semver 提供了确定性规则来选择下一个版本号,而目前这仍然是一项开发人员的任务。开发人员很容易出错,因为他们需要思考“下一个版本是什么”,而不是将更改分类到版本中。

总结

  • pbr 生成的版本并不总是与 pip 兼容(PEP440)。

  • pbr 为未标记的提交生成版本时,将其标记为发布版本,而不是开发版本。这意味着如果推送到镜像,pip 默认会安装它们,而不是最新的发布版本。

  • 没有工具支持来调和开发人员的需求(给我一个可安装的版本)和 CD 部署者的需求(给我一个用于 trunk 中此提交的唯一版本号,该版本号可以与所有其他 trunk 版本正确排序)。

  • 没有工具支持来选择新的(后版本化)版本号。

提议的变更

提议的细节

  • 将标记版本从类似 1.2.3a4(一些项目正在使用)更改为 1.2.3.0a4,并更新 semver 文档以相应地指定前导 0。这使我们既能获得 PEP440 兼容性(允许 1.2.3a4 或 1.2.3.0a4,但不允许 1.2.3.a4),又能与 semver 的概念兼容性,在 semver 中,MAJOR.MINOR.PATCH 格式强烈预期仅为数字,而 3a4 结构破坏了这一点。我们需要继续接受当前格式的标签,因为它们已经存在于我们的分支中 - 但我们可以发出弃用警告。

  • 添加伪头到提交,以指示功能/api-break/deprecation/bugfix 更改。默认情况下将假定为 bugfix。如果提交未提及重要的更改,则可以通过仅包含元数据头和一些琐碎更改的提交来修复。如果提交错误地声称了重大更改,我们将接受结果 - CD 用户可以为已着陆到分支中的任何提交创建软件包。例如,要标记引入 api 不兼容性的新功能

    sem-ver: feature, api-break
    
  • 对于未标记的构建,生成 devN 版本 - 例如 x.y.z.dev4+gHASH,其中 x.y.z 是通过应用 semver 规则以及关于最新更改的元数据(每种类型)派生的。对于预版本化的构建,我们还将生成 devN 构建,但不会应用 semver 规则。+gHASH 由 PEP-440 驱动,将与上游 semver 略有语法不兼容,但我们认为这是正确的权衡。语义上它是兼容的。

  • 提供一个新的命令 (tag-release) 来为开发者执行 git 标记。它将接受参数以允许手动控制,但将强制执行 semver 正在被遵循(例如,如果在分支的历史记录中存在比正在标记的版本更新的标签,则会出错)。Alpha、beta 和 rc 标记也可以自动完成。

  • 提供一个 python API 来获取类似 version_info 的版本数据。例如:

    import pbr.version
    ...
    version_info = pbr.version.Version('MYPACKAGENAME').version_info()
    
  • 提供一个新的命令 (deb-version) 以输出与 Debian 软件包兼容的版本字符串。这主要涉及将 PEP-440 优先级规则转换为 Debian ~ 和 . 组件分隔符。

  • 提供一个新的命令 (rpm-version) 以输出 RPM 版本元数据,以便使用 ENVRA 格式将其合并到 RPM 版本中。由于 RPM 缺乏“之前”运算符 (~) ,主要翻译方法是将预发布和 dev 构建视为下一个最低版本的发布构建,以驱动排序高于所有实际版本。我们假设任何版本都不会有超过 9998 个补丁/次要版本。例如,1.2.0.dev5 将呈现为 1.1.9999.dev5。1.0.0.dev5 将呈现为 0.0.9999.dev5,最后 0.0.0.dev5 也将呈现为 0.0.0.dev5 以避免负版本号。

  • 提供一个新的命令 (next-version) 以输出下一个计算的 semver 版本。例如,如果上一个发布版本是 1.2.3 但发生了向后不兼容,则此命令将输出 2.0.0。此命令的预期用途是帮助开发者选择下一个要标记的版本。

  • sdist 和新的 Debian 和 RPM 版本字符串命令将接受一个 --no-rc 参数,该参数将告诉它们仅发出 a) 正常版本或 b) devN 构建的版本。这解决了 semver 中的排序歧义,其中 devN 低于 *任何* 预发布构建,但版本号可能同时具有预发布版本 *和* devN 版本组件。CD 部署者可以在他们的脚本中包含 –no-rc,并在从单调递增的分支中拉取时获得一致的时间线,而普通用户和打包者希望仅选择常规构建。

    这里有一个注意事项,那就是旧版本的点发布版本不得合并到 trunk 的历史记录中,否则 devN 版本将在此点重置。

新的配置标志

我们正在添加一个新的命令行选项。

  1. --no-rc 传递到输出版本的命令的命令行上。当传递时,这会导致从 git 标签计算版本时,绝不输出 alpha/beta/rc 版本 - 它将始终致力于正常版本(即 X.Y.Z 版本)。只要“最新发布版本”不会在移动到下一个 semver 强制版本的同时重置,这就可以为分支内的版本提供稳定的可比时间线。例如,使用 master,如果 semver 规定下一个版本将是 1.2,并且当前版本是 1.1.2,添加一个可从 master 访问的 1.1.3 标签将重置所有后续于 1.1.3 的提交的 devN 版本,从而使此时间线失效。添加 1.2 标签是可以的,因为下一个计算的版本将是(最低)1.2.1,并且任何剩余未发布的提交的 devN 版本将具有比先前的 1.2.0 devN 版本更高的优先级,在添加 1.2 标签之前。

新的后版本规则

版本中有四个可增量的组件:major、minor、patch 和 pre-release。以下是 pbr 将如何实现 semver.rst 中描述的分配规则的描述。对于预版本化的数字,用户指定要使用的目标版本,我们无法自动增量版本。未来的规范可能会考虑对 pbr 中的预版本化版本号进行 semver,但由于它们通常不是 semver 版本,并且因为它们的定义是“用户选择的”,因此目前超出范围。

  1. 如果一个提交有多个标签,那么最高的标签

    被认为是这些规则的“最后一个标签”。

  2. 如果最后一个标签是预发布标签,则会咨询 –no-rc 选项。如果设置,则在查找最后一个标签时不会考虑预发布标签。

  3. 可从提交访问的最高的合格标签被认为是“最后一个标签”。如果存在多个具有相等距离的标签(例如,单个提交上的 rc 和最终标签),则仍然使用最高的标签。如果根本无法访问任何标签,则假定 0.0.0 作为最后一个标签。如果标签是 PEP-440 版本字符串,可以从提交在 git 历史记录中访问,并且未被 –no-rc 选项排除,则标签是合格的。

  4. 如果与最后一个标签的距离为零,则该标签提供版本,并且过程停止。

  5. 否则,需要补丁级别版本,并且版本应为 devN 版本。

  6. 然后,我们要求 git 提供所有返回到最后一个标签的提交。

  7. 在每个提交中,我们查找 sem-ver: 伪头,如果找到则解析它(按“,”空格分隔并构建一个集合)。未知的符号不是错误(因此人们无法使 pbr 崩溃),但我们会对其发出警告,并且可能希望为 gate 创建一个 linter。已知的符号:featureapi-breakdeprecationbugfix。缺少 sem-ver 行等效于 sem-ver: bugfix

  8. 如果找到 deprecationfeature,则执行次要版本增量。

  9. 如果找到 api-break,则执行主要版本增量。

  10. 如果最后一个标签的主要组件为 0,则主要和次要增量右移。也就是说,主要增量变为次要增量,次要增量变为补丁级别增量。

使用这些规则,一些示例可能有助于阐明

# no tags, one commit, no sem-ver:
last_tag = 0.0.0
tag_distance = 1
version = 0.0.1.dev1+gHASH
Debian version = 0.0.1~dev1+gHASH
RPM version = 0.0.0.dev1+gHASH

# tag of 0.0.1 on a commit - tag sets version.
last_tag = 0.0.1
tag_distance = 0
version = 0.0.1
Debian version = 0.0.1
RPM version = 0.0.1

# tag of 0.0.1.0a4 on a commit, 5 commits since the start
# tag sets X.Y.Z of next version, and a devN version is emitted.
no_rc = True
last_tag = 0.0.0
tag_distance = 5 # distance to origin
version = 0.0.1.dev5+gHASH
Debian version = 0.0.1~dev5+gHASH
RPM version = 0.0.0.dev5+gHASH

# tag of 0.12.2, 2 commits ago with a sem-ver: deprecation line present
# in one of them.
# However since this is a 0.x.y version, we right shift the increment.
last_tag = 0.12.2
version = 0.12.3.dev2+gHASH
Debian version = 0.12.3~dev2+gHASH
RPM version = 0.12.2.dev2+gHASH

# tag of 1.12.2, 2 commits ago with a sem-ver: deprecation line present
# in one of them.
pbr_deprecation = 1.12
version = 1.13.0.dev2+gHASH
Debian version = 0.13.0~dev2+gHASH
RPM version = 0.12.9999.dev2+gHASH

备选方案

我们可以什么都不做,但现在人们正在以临时方式重新发明东西,并且无法以系统的方式重用他们的解决方案。

我们可以将已弃用等标记放在 setup.cfg 中(请参阅 gerrit 中此规范的先前迭代)。这被认为维护起来过于繁琐。

Impact on Existing APIs

没有公共 API 更改(因为 pbr 几乎没有公共 API)。

将添加我们需要支持的新公共 API。

安全影响

无。

性能影响

所需的计算是微不足道的,因此速度不会有显著的降低。读取发布版本之间的 git 历史记录已经为生成 changelog 完成,因此它将位于缓存中,并且从该历史记录中提取行是微不足道的。

Configuration Impact

无。

开发人员影响

开发者可以大致忽略这一点 - 它只会导致符合 PEP-440 兼容性的版本 *除非* 项目选择开始使用新功能。如果项目希望选择加入新功能,他们可以这样做:在他们的项目中导出 version_info 元组,记录他们进行破坏性更改的时间,并让 pbr 为他们强制执行适当的版本更改。这些选项都是选择加入的,有一个例外:生成的数字符合 PEP-440 兼容性,其中包括将未标记版本更改为 devN。

开发者工作流程

这构成了在 PBR 中使用 semver 的手册的开始。

  • 标记发布版本:从您想要成为发布版本的 git 提交运行 setup.py next-version。这将根据 semver 规则输出下一个版本。像往常一样使用该版本或您想要的任何更高版本创建 git 标签。

  • 对代码进行 API 兼容的 bugfix:像往常一样提交并推送到 gerrit。

  • 进行添加新功能的更改:像往常一样推送到 gerrit,但在提交中包含 sem-ver: feature

  • 进行弃用内容的更改:像往常一样推送到 gerrit,但在提交中包含 sem-ver: deprecation

  • 进行破坏兼容性的更改 - 无论是删除已弃用的代码还是删除尚未弃用的代码,或者添加用户之前不需要做的强制性操作:像往常一样推送到 gerrit,但在提交中包含 sem-ver: api-break

  • 对 master 进行每日/逐个提交构建,以便包含在软件包存储库中:使用 setup.py --no-rc 获取形成一致时间线的软件包版本号(因此 alpha/beta/rc 不会反映在版本中)。setup.py debian-version --no-rcsetup.py rpm-version --no-rc 命令将为您提供各自平台上使用的适当版本号。

实现

负责人

主要负责人

Robert Collins (lifeless)

其他贡献者

Joshua Harlow (harlowja) 任何人我能拉进来。

里程碑

完成目标里程碑

Juno-2

工作项

  • 清理 PBR 中的内部逻辑,以分离 setuptools 交互和版本建模。

  • 添加 devN 增量功能。

  • 添加历史抓取功能,用于增量主要和次要版本。

  • 添加标记功能。

  • 添加 debian-version 功能。

  • 添加 rpm-version 功能。

  • 添加 version_info 功能。

孵化

采用

全部。

pbr

预计 API 稳定

没有。它将是完美的。

文档影响

pbr 手册(在 pbr 树中)需要充实以涵盖此行为。

依赖项

参考资料

注意

本作品采用知识共享署名 3.0 非移植许可协议授权。 http://creativecommons.org/licenses/by/3.0/legalcode