Laminas项目在主项目、LaminasAPI工具和Mezzio之间有近200个存储库。需要维护的东西很多,并且掌握传入的补丁可能是一项艰巨的任务,更不用说创建版本了。
这就是为什么在过去的一年里,我们花了很多时间来简化我们的流程;我们希望能够快速、自信地审查、合并和发布更改。为此,我们开发了一些GitHubActions,以使这些过程对我们的维护人员来说尽可能简单。
自动发布
第一个是MarcoPivetta(又名Ocramius)的创意。他想要一种使发布尽可能简单的方法。
在此之前,我们有一个比较复杂的过程:
- 如果拉取请求针对我们的“master”分支:
- 合并到“master”
- 合并到“develop”(这通常会导致合并冲突,由于
CHANGELOG.md
文件中分支之间的差异) - 从“master”创建一个分支以设置发布版本
- 将发布版本修改为
CHANGELOG.md
- 将发布分支合并到“master”
- 将发布分支合并到“develop”(同样,合并冲突)
- 标记发布,将相关的
CHANGELOG.md
条目复制到标记描述中 - 推送发布
- 从GitHub上创建发布标记,然后再次将
CHANGELOG.md
条目复制到描述中
- 如果拉取请求针对我们的“开发”分支:
- 合并到“develop”
- 合并“develop”到“master”
- 从“master”创建一个分支来设置发布版本
- 在
CHANGELOG.md
中修改发布版本 - 将发布分支合并到“master”
- 将发布分支合并到“develop”
- 在
CHANGELOG.md“develop”分支中的文件到下一个次要版本
- 标记来自“master”的版本,复制相关的
CHANGELOG.md
条目到标记描述中 - 推送发布
- 从标签在GitHub上创建发布,然后再次将
CHANGELOG.md
条目复制到描述中
很多围绕标记和创建GitHub版本的工作都是由我的keep-a-changelog工具处理的,但它仍然有效,并且涉及很多样板文件和繁琐的工作。
Marco的伟大想法:如果我们将问题和拉取请求分配给GitHub里程碑,并且在里程碑关闭时自动创建发布会怎样?
这导致我们创建了自动发布GitHub操作。
要使用它,您需要在存储库中创建发布分支,以语义版本命名,格式为{MAJOR}.{MINOR}.x
(那是最后的文字“.x”)。(这也有一个很好的附带好处,即从我们的分支中删除“master”措辞。)然后您创建里程碑,为您要创建的下一个版本命名:1.2.3、1.3.0
、2.0.0
。从那里开始,您可以向您的应用程序添加一个小工作流,以及一些秘密(aGPG签名密钥、Git作者和用于标记版本的电子邮件,以及可能允许创建合并请求的特权GitHub令牌;稍后会详细介绍)。
在您分类时,将您的问题和拉取请求分配给里程碑。当与里程碑相关的所有问题和拉取请求都完成后,您关闭里程碑,工作流从那里获取它。
工作流的作用:
- 它提取里程碑描述。
- 它提取问题和拉取请求列表以及创建它们的人员,以创建详细说明问题/拉取请求的发行说明列表
- 如果您有KeepAChangelog格式的
CHANGELOG.md
,它将更新版本的条目以附加里程碑描述和之前提取的版本说明步骤,以及设置发布日期,将更改推送回分支。 - 它使用签名密钥和git作者/电子邮件创建标签,将描述设置为更改日志条目,或来自前两个步骤的信息,在完成时推送标签。
- 它使用标签描述中提供的相同注释在GitHub上创建一个版本。
- 如果存在更新的版本分支(例如,如果您的版本是1.2.3,并且存在1.3.x分支),它会创建一个对该分支的“合并”请求,其中包含两个分支之间的差异。
- 如果没有无如果发布分支存在,它会创建一个新的次要发布分支(例如,如果您要发布1.2.3,它将创建1.3.x分支)。
- 它将默认分支切换到下一个发布分支.
- 如果您有一个
CHANGELOG.md
文件,并且刚刚创建了一个新的发布分支,它会为下一个次要版本添加一个条目。 - 它为下一个补丁、次要和主要版本创建里程碑(如果尚不存在的话)。
从本质上讲,该操作将繁忙的工作自动化,以便维护人员可以轻松并经常发布。
我们在此过程中发现的一件事:合并请求仍然会导致问题,因为CHANGELOG.md
文件中会有差异。因此我们想出了一个解决方案,因为它已经在工作流程中得到支持:我们现在使用相同的格式将过去保存在CHANGELOG.md
文件里程碑描述中的信息保留下来。description被action提取并用在标签和发行说明中,它具有相同的效果(将信息语义化地放在它所属的位置),同时消除合并冲突(因为可以消除文件)。
这个新的工作流程非常成功。我们发现我们大部分时间都花在分类上(确定我们是否要解决它,如果是,修复或功能应该针对哪个分支),以及更多的时间来提供反馈。一旦我们能够接受补丁,合并和发布之间的时间只需要关闭里程碑和运行操作所需的时间(我们已经减少到不到一分钟!).
在任何地方使用!
虽然此操作使用PHP来完成其工作,但它可以在任何存储库中使用!我们甚至使用它来创建版本我们的其他GitHubActions,主要是用JavaScript和Bash编写的。
在任何地方使用!
虽然此操作使用PHP来完成其工作,但它可以在任何存储库中使用!我们甚至使用它来创建版本我们的其他GitHubActions,主要是用JavaScript和Bash编写的。
持续集成
为了合并拉取请求,我们需要确保它通过我们的QA检查,通常包括单元测试、编码标准验证和静态分析。我们还希望增加我们的QA检查以包括文档等内容linting和文档链接检查。
我们传统上为此使用Travis-CI,但从去年年底开始,Travis对OSS使用进行了一些政策更改,我们发现我们在月中的时间用完了。因此,我们需要一个新的解决方案,我们决定是时候弄清楚我们真正想要从CI得到什么了。
我们得出的结果:
- 我们绝对讨厌每次有新的PHP版本要支持时都必须更新矩阵。
- 我们也不喜欢在添加新的QA工具时必须更新矩阵;这是一个很容易忘记的步骤。
- 最重要的是,对给定的矩阵项目运行多种类型的检查意味着我们最终会玩打地鼠游戏:我们修复了一个QA项目,却发现下一次检查失败;我们修复它只是为了发现下一个失败;等。如果可能的话,我们希望它们独立运行,设置它们所需的条件最少。
- 我们真的希望尽可能多的作业并行运行,而不是受到限制在存储库和组织级别(Travis限制我们在任何给定时间在整个组织中进行5个连续的工作,这有时会导致排长队。)
- 我们想要最少的配置,零配置是可实现的目标。
为了帮助解决这个问题,我与Marco和Cees-JanKiewiet(又名Wyrihaximus)合作,以确定存在哪些可能性以及哪些方法可能有效。LuísCobucci还提供了一些有关创建和发布Docker镜像的有用信息以支持GitHub操作。
结果是两个GitHub操作:
- laminas-ci-matrix-action
- laminas-continuous-integration-action
然后通过两个步骤在单个工作流程中引用这些内容。
第一个动作laminas-ci-matrix-action分析您的提交以发现可能需要运行的已知QA工具:
- PHPUnit(通过
phpunit.xml
或phpunit.xml.dist
之一或两者的存在) - phpcs(通过存在
phpcs.xml
或phpcs.xml.dist
之一或两者) - Psalm(通过
psalm之一或两者的存在。xml
或psalm.xml.dist
) - yamllint和markdownlint(通过存在
mkdocs.yml
和/或docs?/book/**/*.md
文件)
它还会检查您的composer.json
以确定要运行的PHP版本,以及要安装的依赖项集(它始终包括支持的最低和最新版本,如果是composer.lock
存在,已锁定)。从那里,它构建了一个矩阵。
- 每个PHP版本的每个依赖集一个PHPUnit作业。
- 一个作业在当前“稳定的PHP”(当前7.4)上用于发现的每个其他QA检查。
您可以提供要安装的扩展、要使用的php.ini
值、要排除的QA检查以及要通过配置运行的额外检查。
当动作检测到它是由拉取请求触发时,它还会与目标分支执行差异,并且仅根据更改的文件运行检查;例如,如果仅更改了文档文件,它只会执行markdownlinting。
该操作然后创建一个JSON格式的“输出”字符串,其中包含要运行的所有检查,随后的步骤或作业可以使用它。比如laminas-continuous-integation-action!
laminas-continuous-integration-action执行实际的提升。它接受一个JSON格式的“作业”,其中包含:
- 要使用的PHP版本。
- 要安装的任何其他扩展。
- 要使用的任何
php.ini
设置。 - 使用哪个依赖集(最低、锁定或最新)
- 要运行的命令(实际QA检查)
当它运行时,它会检查指定的存储库引用,设置默认的PHP版本,安装任何需要的扩展,添加php.ini
配置,然后运行Composer安装依赖项,基于依赖集。从那里,它运行指定为非特权用户的命令,从而执行QA检查。
该操作还允许您在存储库中提供运行前/运行后脚本,这对于播种数据库或缓存之类的事情非常方便。
在这两个操作和简化的工作流程之间,我们已经能够创建一个CI解决方案,该解决方案将随着我们的需求而增长,而无需定期更新。事实上,我们已经多次扩展了它们的功能,而无需更改已经在使用它们的存储库。自从添加工作流以来,我实际上已经向几个存储库添加了新的QA工具,并且惊喜地看到操作选择它并开始使用它执行作业!
最重要的是,GHA基础架构使得工作流可以并行运行,并且大多数作业要么全部并行运行,要么大量并行运行。最终结果是CI运行的作业通常需要3-5分钟现在,Travis上的GHA在一分钟内完成。这种快速反馈使补丁迭代变得更加容易,并使我们的维护人员能够专注于补丁正在完成的工作,而不是它是否符合QA指南。
要点
作为项目负责人,许多人认为我的工作是推动新功能。然而,由于我们拥有如此多的存储库,而且其中许多都需要大量的专业知识,我的工作是促成贡献。我发现我们一直在开发的工具在帮助维护者(包括我自己)引导新的贡献并快速发布给世界方面是一个巨大的福音。因为这经常是对ZendFramework的批评过去和现在的Laminas,我非常希望我们能够扭转这个局面,为世界各地的PHP用户提供高质量的代码。
谈到:我们一直正在为我们的软件包寻找更多的维护者。如果您有兴趣,请自己提名!