pytest 补丁版发布公告模板解析:从 release.patch.rst 看 pytest 的自动化发版体系
【免费下载链接】pytestThe pytest framework makes it easy to write small tests, yet scales to support complex functional testing项目地址: https://gitcode.com/GitHub_Trending/py/pytest
本文以 pytest 仓库中的补丁版(bug-fix)发布公告模板 scripts/release.patch.rst 为切入点,完整解读其占位符语法与"drop-in replacement"语义,并结合 scripts/release.py、scripts/prepare-release-pr.py、tox.ini 以及 doc/en/announce/ 目录下的真实公告产物,还原 pytest 从模板文件到正式发布公告落盘的自动化链路。读完本文,你将掌握 pytest 四种发布公告模板的分工逻辑、模板占位符的填充机制、补丁版发布在整套发版流程中的位置,并能复刻这一"模板 + 脚本填充 + 自动落盘"的发布公告工程实践。
一、模板是什么:补丁版发布公告的标准话术
scripts/release.patch.rst全文仅 15 行,是 pytest 用于**补丁版(bug-fix release)**发布公告的 reStructuredText 模板。完整内容如下:
pytest-{version} ======================================= pytest {version} has just been released to PyPI. This is a bug-fix release, being a drop-in replacement. The full changelog is available at https://docs.pytest.org/en/stable/changelog.html. Thanks to all of the contributors to this release: {contributors} Happy testing, The pytest Development Team模板由三部分构成:
- 版本占位符
{version}:出现在标题行与正文首句,实际发布时被具体版本号替换,例如pytest 9.1.1 has just been released to PyPI; - 发布定位声明:明确标注 "This is a bug-fix release, being a drop-in replacement",即该版本只包含缺陷修复、不引入破坏性变更,用户可无感升级;
- 贡献者占位符
{contributors}:以 bullet 列表形式罗列本次发布的所有作者与共同作者,模板中预置了Thanks to all of the contributors to this release:引导语。
该模板的使用边界由模板名约定。scripts 目录下共维护四份同构模板,分别对应四类发布场景:
| 模板文件 | 适用场景 | 版本增量 | 关键话术差异 |
|---|---|---|---|
| scripts/release.patch.rst | 补丁版(bug-fix) | x.y.z+1 | "drop-in replacement",仅指向 changelog |
| scripts/release.minor.rst | 特性版(feature) | x.y+1.0 | "new features, improvements, and bug fixes",附pip install -U pytest |
| scripts/release.major.rst | 主版本 | x+1.0.0 | 额外提示 breaking changes,建议仔细阅读 CHANGELOG |
| scripts/release.pre.rst | 预发布版(rc) | 追加rcN后缀 | 声明 "not intended for production use",要求 issue 标题带[prerelease] |
其中 pre 模板还额外支持第三个占位符{doc_version},用于在 changelog 链接中指向特定文档版本(见 scripts/release.pre.rst 第 22 行),而 patch/minor/major 模板均指向en/stable的最新文档。
二、占位符如何被填充:release.py 的 announce() 机制
模板本身只是静态文本,真正让它"活"起来的是 scripts/release.py 中的announce()函数(scripts/release.py#L18-L76)。该函数负责读取模板、注入真实数据并落盘为正式公告,具体分四步:
第一步:通过 Git 历史收集贡献者。先执行git describe --abbrev=0 --tags取最近一个标签作为上一版本,构造提交区间{last_version}..HEAD;随后用两条git log分别提取区间内所有提交的作者(--format=%aN)与Co-authored-bytrailer(--format=%(trailers:key=Co-authored-by)),后者通过正则Co-authored-by: (.+?)<从 trailer 行中解析出共同作者姓名。
第二步:清洗贡献者名单。源码中用一个集合推导式对作者与共同作者去重,并过滤掉name.endswith("[bot]")与name == "pytest bot"的自动化账号,避免 bot 出现在人类贡献者致谢中。
第三步:格式化填充模板。将贡献者姓名按字典序排序后,逐行拼成* 姓名的 RST bullet 列表,再调用template_text.format(version=..., contributors=..., doc_version=...)完成占位符替换。
第四步:落盘并登记。生成的公告写入 doc/en/announce/release-{version}.rst,同时将新公告插入 doc/en/announce/index.rst 的 toctree 列表顶部(按release-前缀行定位插入点),最后执行git add将公告纳入暂存。
main()(scripts/release.py#L127-L144)以三个位置参数驱动整个流程:version、template_name、doc_version,另有--skip-check-links开关。这正是文章开头模板在真实发布中的完整调用语境。
三、何时用到补丁模板:prepare-release-pr.py 的模板决策逻辑
补丁模板并不是随意挑选的,其选择规则固化在 scripts/prepare-release-pr.py 的prepare_release_pr()中(scripts/prepare-release-pr.py#L95-L102):
if is_major: template_name = "release.major.rst" elif prerelease: template_name = "release.pre.rst" elif is_feature_release: template_name = "release.minor.rst" else: template_name = "release.patch.rst"判定is_feature_release的依据是对仓库 changelog/ 目录的扫描(scripts/prepare-release-pr.py#L63-L65):只要该目录中存在*.feature.rst或*.breaking.rst文件,即判定为特性版;否则就是纯 bug-fix 的补丁版,落入release.patch.rst分支。这与 changelog 目录中大量.bugfix.rst、.feature.rst、.breaking.rst碎片文件(如 changelog/14884.bugfix.rst、changelog/8593.breaking.rst)一一对应——正是这些碎片文件的类型,决定了公告采用哪种模板。
版本号则完全由 Git 标签推算(find_next_version(),scripts/prepare-release-pr.py#L144-L162):扫描所有形如x.y.z的标签取最大值后,主版本递增首位、特性版递增次位、补丁版递增末位,预发布再追加rcN后缀。也就是说,补丁版公告的版本号天然是(major, minor, patch+1)。
四、从模板到公告:补丁版发布的完整流水线
补丁模板的"消费端"是 tox 环境 tox.ini#L213-L222 中定义的[testenv:release]:
[testenv:release] description = do a release, required posarg of the version number usedevelop = True passenv = * deps = colorama pre-commit>=2.9.3 towncrier commands = python scripts/release.py {posargs}该环境声明了colorama(彩色日志输出)、pre-commit(格式化)、towncrier(changelog 聚合)三个依赖,然后转调release.py。pre_release()(scripts/release.py#L102-L119)将补丁模板串入一条完整流水线:
announce(version, "release.patch.rst", doc_version)——按上文机制生成补丁版公告并登记进 index;regen(version)——通过tox -e regen重新生成文档中的示例输出(借助SETUPTOOLS_SCM_PRETEND_VERSION_FOR_PYTEST注入版本);changelog(version, write_out=True)——调用towncrier build --yes --version {version},将 changelog 目录中的碎片合并进 CHANGELOG.rst 与 doc/en/changelog.rst(draft 模式则只预览不写盘,见 scripts/release.py#L122-L124);fix_formatting()——运行pre-commit run --all-files统一格式;check_links()——运行tox -e docs-checklinks校验文档链接(可用--skip-check-links跳过);- 最后
git commit -a -m "Prepare release version {version}"并提示推送分支、发起 PR。
整个流程可被prepare-release-pr环境(tox.ini#L224-L230)一键触发,其入口命令形如:
tox -e release -- 9.1.1 release.patch.rst release-9.1.1 --skip-check-links补丁版最终产出的公告实物可在 doc/en/announce/release-9.1.1.rst 中看到,其结构完全符合模板预期——标题为pytest-9.1.1,正文声明 "This is a bug-fix release, being a drop-in replacement",贡献者区列出人类维护者姓名;而 doc/en/announce/index.rst 顶部的 toctree 也按release-9.1.1在前的顺序登记了历次公告。发布完成后,scripts/generate-gh-release-notes.py 还会从doc/en/changelog.rst中按版本标题(正则pytest (\d\.\d+\.\d+\w*) \(\d{4}-\d{2}-\d{2}\))抽取对应条目,经 pandoc 从 RST 转为 GFM 格式,作为 GitHub Release 的正文——公告模板、changelog、发布说明三者由此构成完整闭环。
五、模板化设计的工程价值
回顾 scripts/release.patch.rst 这一不足 20 行的文件,可以提炼出 pytest 发版公告工程化的三个要点:
- 话术与逻辑分离:发布定位(bug-fix / feature / major / prerelease)被固化为四个模板,语义不会被临场改写;模板选择逻辑集中在 scripts/prepare-release-pr.py 一处,避免人为误判;
- 数据零手写:版本号取自 Git 标签推算,贡献者取自
git log自动提取与 bot 过滤,公告正文完全由脚本填充生成,杜绝遗漏或拼写错误; - 流程可编排:公告生成只是
[testenv:release]流水线的一环,与 regen、towncrier、pre-commit、链接检查串联,保证每次发布的公告、changelog、文档输出三者始终一致。
对于任何需要定期向用户发布版本的 Python 项目,这套"类型化模板 + 版本/贡献者占位符 + 脚本自动落盘"的模式都可以低成本复刻,是值得借鉴的发布工程样板。
【免费下载链接】pytestThe pytest framework makes it easy to write small tests, yet scales to support complex functional testing项目地址: https://gitcode.com/GitHub_Trending/py/pytest
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考