Julia 版本发布流程

2019 年 8 月 28 日 | Stefan Karpinski (JuliaHub)

参与项目日常开发的人员往往对项目的节奏和流程非常熟悉,他们会将流程内化,感觉每个人都应该知道每个阶段是如何展开的。当然,从外部看进来就不那么明显了。因此,我认为将 Julia 的版本发布流程记录下来,包括以下内容,可能会对更广泛的 Julia 社区(甚至其他编程语言社区)有所帮助:

这些信息收集自 discourse 上的一些帖子和 Slack 上的对话,因此信息是“存在的”,但这篇博客文章将所有内容整合到一个地方。如果反响良好,我们可能会将这篇文章变成一份正式文档。Julia 遵循 SemVer 标准中指定的“语义化版本控制”,但 SemVer 为解释留下了相当大的空间,对流程的描述也很少,因此这篇文章旨在补充这些细节。

  1. 补丁版本
  2. 次要版本
  3. 主要版本
  4. 长期支持
  5. 风险承受能力角色
  6. 发布流程
  7. 为什么需要预发布版本?
  8. 发布维护
  9. 结论

补丁版本

次要版本

主要版本

长期支持

一些用户乐于经常升级 Julia,以便尽快获得最新的功能。有些人甚至乐于每天构建 Julia 的 master 分支,并在功能完全成熟之前尝试使用它们。另一些人则不希望一年内升级 Julia 超过一次,如果有的话。理想情况下,我们希望永远为我们发布的每一个 Julia 次要版本提供 bug 修复。如果我们有无限的资源,我们会将每个 bug 修复移植到它适用的每一个旧发布分支。然而,现实情况是,我们没有能力同时维护多个活跃的移植分支。因此,我们决定妥协,在任何时候最多有四个活跃的分支:

每次功能冻结时,都会创建一个新的不稳定发布分支,一旦该分支上发布了第一个稳定版本,它就会成为新的稳定发布分支:也就是说,当我们发布 1.3.0 最终版时,release-1.3 分支将成为新的稳定发布分支,release-1.2 将不再维护,并且直到下一个功能冻结之前,不会有任何当前的不稳定发布分支。

最关键的问题是何时更改长期支持分支。release-1.0 分支是我们有史以来唯一的 LTS 分支。它已经发布了四个补丁版本,并且变得非常稳定,得到了广泛的支持。然而,在某个时候,在 master 上做出的 bug 修复补丁将越来越少地适用于 release-1.0,越来越少的包的当前版本将支持 Julia 的 1.0.x 版本——它们将使用太多新功能。当这种情况发生时——正确的时间需要判断——我们将不得不选择一个新的长期支持分支,并宣布 1.0.x 系列不再维护。新的 LTS 分支最终可能是 1.41.8——也可能要等到 2.0 才会发生。我们不确定,但这会在某个时候发生。幸运的是,即使这样也不会强迫使用 Julia 1.0.x 的用户进行升级:他们可以继续使用最后一个 1.0.x 版本以及与其兼容的包。到那时,它将成为现存的 Julia 最稳定、经过最彻底测试、打过补丁的版本,因此,如果不需要更新的功能,可以无限期地安全使用。此外,如果某个人或组织对维护任何特定的旧发布分支有既得利益,并且愿意为实现这一点付出努力(精选移植和启动 PkgEval 运行以确保一切正常),我们很乐意接受这种帮助并发布更多版本。因此,您始终可以通过自己进行维护(或付费让其他人来做)来获得更长时间的支持。目前,release-1.0 仍然是一个优秀的、稳定的 LTS 分支,在我们更改 LTS 分支之前,会有足够的警告时间。

风险承受能力角色

不同语言用户对风险的承受能力差别很大。有些用户完全可以接受发现并报告偶尔出现的 bug,并帮助找出为什么某些包无法与新版本一起使用。另一些用户只希望使用经过多次 bug 修复并且每个包都已在很长一段时间内完美运行的语言版本。在这两种情况下,风险承受能力之间存在一个连续谱。大多数用户将属于以下四种风险承受能力类别之一

  1. 高风险承受能力:“YOLO,我在 master 上生活。当然,现在没有以前那么冒险了,因为 master 上一段时间内不会出现破坏性更改,所以即使在 master 上,包也应该继续工作,但 bug 总会发生,你知道吗?我愿意帮助找到它们。”

  2. 正常风险承受能力:“我喜欢事情正常工作,不想处理 master 上的瞬时 bug。所以我会坚持使用最新的稳定版本,并在可用时升级到该版本的最新补丁版本,因为这很安全,并且我会获得 bug 修复和性能改进。唯一令人恼火的是,当我使用的包出现问题时,因为它依赖于一些 Julia 内部代码,并且可能需要一段时间才能发布新版本。”

  3. 低风险承受能力:“我比较保守,对风险厌恶。我会关注当前的 LTS 分支,因为它已经经过了大量的测试。当 LTS 分支更改时,我会升级,因为到它成为新的 LTS 分支时,它已经发布了第四个或第五个补丁版本,所以 bug 已经被解决,最初可能存在的任何包故障也早就被解决了。”

  4. 极低风险承受能力: "我极度厌恶风险。我从不升级 Julia(或任何东西),除非是关键的错误修复和安全问题。我运行的是不再获得积极支持的 Julia 版本,但它是以前 LTS 分支的最后一个版本,所以有一个两位数的补丁号,并且经过了非常彻底的调试。如果在这个古老的发布分支上需要新的错误修复,我会自己回溯它并帮助发布一个新的、更可靠的补丁版本。"

这些配置文件更清楚地表明了长期支持分支的主要标准是该分支具有以下属性

如果一个新的 LTS 分支满足这两个标准,那么 "低风险承受能力" 类别的用户将能够升级到新的 LTS 分支,因为他们已经可以确定它将是可靠的、经过良好调试的,并且他们需要的包将可以使用(尽管他们可能需要升级他们的包版本)。我们将需要从经验中学习 LTS 分支应该比稳定发布分支滞后多少个版本。

发布流程

我们已经讨论过各种类型的发布意味着什么,以及哪些类型的更改可以进入它们,但我们并没有过多地讨论一个发布是如何实际完成的。在本节中,我将概述我们如何从在 master 分支上开发功能,到标记发布的最终版本,以及之后制作该版本的补丁。我将使用 "bug" 一词来指代通常意义上的错误代码,以及 "性能错误" - 即运行速度低于我们认为可以接受的代码。在 Julia 中,性能是一个至关重要的属性,我们通常将性能问题视为阻塞性错误。以下是围绕 x.y.0 次要版本的一系列事件概述

你只需看一眼就能明白这可能是一个漫长而不可预测的过程。特别是,稳定化阶段可能需要非常不确定的时间 - 从几周到几个月不等。这在确保质量和希望拥有可预测的发布速度之间造成了紧张关系。一方面,我们不希望仓促发布候选版本流程,因为它在很大程度上确保了每个 Julia 版本都具有你期望的质量和稳定性。另一方面,我们不希望总体发布速度成为调试需要多长时间的变幻莫测的牺牲品 - 我们都知道,对于任何项目来说,这可能是一个漫长而痛苦的过程,对于像编程语言一样复杂的东西来说尤其如此。

我们通过将一个版本的稳定化与下一个版本的开发重叠来解决在确保发布质量和保持可预测发布速度之间存在的紧张关系。每个版本的开发阶段都设定了四个月的时间,x.(y+1) 的开发阶段在 x.y 的开发阶段结束后立即开始。无论风雨,我们每四个个月就会有一次新的功能冻结:我们会选择一个日期,你必须在那天之前将你的功能合并。如果新的功能没有合并,它们就不会进入该版本。但这没关系,它们会在下一个版本中进入。这种方法还意味着 master 始终对新功能开放,而不是在稳定化期间被冻结。

由于开发和稳定化重叠,如果发布候选版本流程花费的时间异常长,那么 x.y.0 的最终版本可能会与 x.(y+1).0 的功能冻结同时发生。例如,这种情况发生在 1.2.01.3.0 上。在讨论区上对此表达了一些困惑和不安,但这正是为了保持可预测发布速度而付出的不可避免的代价。1.2 的稳定化阶段异常长,这种情况有时会发生。我们一直在检查我们的流程,并思考如何改进它。一个可能有所帮助的改变是更频繁地以完全自动化的方式运行 PkgEval,以便我们能更早地知道开发过程中的更改何时会破坏包。尽早频繁地运行 PkgEval 使得更容易缩小导致破坏的更改范围。如果有人想参与进来并帮助改进 Julia 的发布流程,帮助使用 PkgEval 将是一个非常有影响力的工作,它不需要深入的技术知识。

有一点需要注意,因为有些人对此感到困惑:功能冻结只影响新功能 - 错误可以在任何分支的任何时间修复。修复错误永远不会太晚。唯一不会在发布分支上修复错误的时间是当它不再维护时。即使那时,如果其他人想要修复错误并完成发布新版本的流程,我们也会很乐意提供帮助,只是我们不会自己去做。

为什么需要预发布版本?

尽管它们是发布流程的标准部分,但人们可能不清楚 alpha 和 beta 版本的目的是什么,或者 "发布候选版本" 是什么。为什么存在这些 "预发布" 版本?我知道,在我开始尝试实际制作软件版本之前,这一点对我来说并不完全清楚。这些版本都是为了与依赖你的软件的人进行沟通。它们充当一个信号,表示 "请现在测试它"。每个版本都要求不同类型的用户提供不同类型的反馈

因此,当你看到 alpha 版本或 beta 版本或发布候选版本时,请尝试一下!让我们知道它是否在任何方面都不适合你。这样做将有助于确保最终版本尽可能地平稳、高质量,为你

发布维护

关于错误修复:当 x.y.0 被标记时,一个版本的生命周期并没有结束 - 还有可能存在许多 x.y.z 错误修复版本也会被标记。这个过程是如何运作的?所有活动分支都会修复错误,但通常会先在具有该错误的最新分支上修复错误,然后 "回溯" 到所有仍然活动的早期分支。因此,例如,如果 master 上存在错误,它将被修复在 master 上,并且修复它的拉取请求 (PR) 在 GitHub 上用 backport x.y 标记,用于所有具有该错误的活动分支。由于当前的活动分支是 masterrelease-1.3(不稳定)、release-1.2(稳定)和 release-1.0(LTS),因此针对 master 上的错误的修复将用 backport 1.3backport 1.2backport 1.0 标记。然后,更改将被 cherry-pick(使用 git cherry-pick -x)到这些分支中的每一个,用于该分支的下一个补丁版本。如果修复干净地应用并通过测试,那就太好了。如果不是,则可能需要额外的手动工作才能做出适用于该分支的修复。

一旦发布分支积累了足够的错误修复,并且经过了足够的时间,就会发布一个新的错误修复版本 x.y.z。这将在讨论区上提前五天宣布,以便人们可以测试新版本。我们目前没有带宽或资源来为补丁版本制作二进制文件或发布候选版本 - 它们太多了。因此,为了测试,你需要使用 nightly 版本或从源代码构建 Julia。帮助自动化和简化补丁版本流程是任何希望参与该项目的人的另一个高影响力领域。

结论

希望你发现这篇关于 Julia 发布流程和策略的概述很有启发。我们能希望的最好的事情是,你们中的一些人阅读后会觉得它很有趣并想参与进来,通过消除误解,我们帮助让成为 Julia 开发人员更容易一些。

[1] PkgEval 是一个用于运行所有 Julia 包的测试套件的工具,它帮助我们确保我们没有无意中破坏任何东西。每个失败都会在发布版本时进行检查:我们会验证失败是否不是由于违反了 SemVer 造成的,并尝试提出拉取请求来修复包,无论失败的原因是什么。