ansible-core 变更日志(Changelog)片段机制:从 fragments 目录到 CHANGELOG-vX.Y.rst 的完整流程
本文以 changelogs/README.md 为核心,讲解 ansible-core 项目如何基于 changelogs/fragments/ 目录下的 YAML 变更片段(changelog fragments)在发布时自动生成版本级变更日志文件 CHANGELOG-vX.Y.rst。读完后,你将理解 devel 分支与发布分支在 changelog 上的差异、changelogs/config.yaml 中各配置项与分节规则的含义,以及发布脚本 packaging/release.py 中 changelog 生成的真实调用链,从而能在向 ansible-core 贡献代码时正确编写变更片段。
核心机制:片段驱动的版本级变更日志
changelogs/README.md 给出的核心说明可以归纳为三点:
- 发布流程的一部分:作为发布流程的一环,版本专属的
CHANGELOG-vX.Y.rst文件由fragments目录下的片段聚合生成; - 发布分支看版本文件:在发布分支上,一旦某个版本已经创建,应查阅该分支上版本专属的 changelog 文件,来了解该分支上已经发生的变更;
- devel 分支只有片段:
devel分支上不存在已生成的 changelog,只有 changelog 片段。
也就是说,ansible-core 采用"片段累积、发布时聚合"的变更日志模型:日常开发中,每个 PR 往 changelogs/fragments/ 提交一个小的 YAML 片段;只有执行发布动作时才把所有片段合并进 RST 格式的正式 changelog,并同步到 changelogs/changelog.yaml 中。
目录结构与文件角色
changelogs/ 目录下的关键文件各司其职:
| 路径 | 作用 |
|---|---|
| changelogs/README.md | 说明 changelog 生成规则(本文主体) |
| changelogs/config.yaml | 定义 changelog 的标题、版本正则、分节顺序与生成行为 |
| changelogs/changelog.yaml | 记录已发布版本的累积变更(机器可读的发布状态文件) |
| changelogs/fragments/ | 存放所有待发布的变更片段(YAML 文件) |
当前 changelogs/changelog.yaml 的内容为:
ancestor: 2.21.0
releases: {}
这表示 devel 分支的"变更基线"是 2.21.0:所有 2.21.0 之后合入、尚未发布进版本文件的变更,都以片段形式积累在 fragments/ 中。releases: {} 印证了 README 的说明——devel 分支没有已生成的版本条目。
changelog 生成配置逐项解读
changelogs/config.yaml 是生成器的行为定义,当前仓库中的完整配置为:
---
title: ansible-core
release_tag_re: '(v(?:[\d.ab\-]|rc)+)'
pre_release_tag_re: '(?P<pre_release>(?:[ab]|rc)+\d*)$'
changes_file: changelog.yaml
changes_format: combined
keep_fragments: true
always_refresh: true
ignore_other_fragment_extensions: true
mention_ancestor: false
notesdir: fragments
prelude_section_name: release_summary
new_plugins_after_name: removed_features
sections:
- ['major_changes', 'Major Changes']
- ['minor_changes', 'Minor Changes']
- ['breaking_changes', 'Breaking Changes / Porting Guide']
- ['deprecated_features', 'Deprecated Features']
- ['removed_features', 'Removed Features (previously deprecated)']
- ['security_fixes', 'Security Fixes']
- ['bugfixes', 'Bugfixes']
- ['known_issues', 'Known Issues']
结合 packaging/release.py 中实际使用的生成工具(antsibull-changelog)与各字段的语义,可以逐项理解:
title: ansible-core:生成 changelog 的文档标题;release_tag_re/pre_release_tag_re:识别正式版本 tag(如v2.22.0)与预发布 tag(如rc1、a1、b2)的正则,决定哪些 git tag 会被视为发布节点;changes_file: changelog.yaml与changes_format: combined:累积的变更历史写入 changelogs/changelog.yaml,且历史以"combined"(合并式)格式组织;keep_fragments: true:发布生成后保留片段文件,不自动清理,便于回溯每个变更对应的原始提交;always_refresh: true:生成时刷新既有内容,保证 changelog.yaml 与片段保持一致;ignore_other_fragment_extensions: true:只处理约定扩展名的片段文件,忽略其他无关文件;notesdir: fragments:片段目录即 changelogs/fragments/;prelude_section_name: release_summary:每个版本的"引言"取自片段中的release_summary字段(见下文发布脚本部分);new_plugins_after_name: removed_features:插件增删记录紧跟在"已移除特性"分节之后;sections:定义 changelog 正文的八个分节及其显示顺序,从 Major Changes 到 Known Issues。这也是每个片段文件必须使用的合法 key 集合。
变更片段(Fragments)的格式与真实示例
片段就是 fragments/ 目录下的一个小 YAML 文件,文件内容是一个以分节名为 key 的映射,value 为该分节下的条目列表。当前 changelogs/fragments/ 中存在数十个真实片段,例如:
- 87304-rpm_key-armor-trailing-newline.yml、86937-git-rm-redundant-err-checks.yml:以 issue/PR 编号命名的 bugfix 类片段;
- 87235-is_netmask-contiguous.yml、87044-user-alpine-move-home-ordering.yml:模块行为修正类片段;
- 86957-uniontechos-server-redhat-family.yml:facts 识别范围调整类片段;
- alpine_user_mod.yml、ssh-agent-hardening.yml、mask_url.yml:不以编号命名的主题式片段。
命名上可见两种惯例:<issue号>-<简述>.yml 与纯主题名 <简述>.yml。片段内容的合法 key 只能是 config.yaml 的 sections 中列出的分节名,例如一个典型的 bugfix 片段形如:
bugfixes:
- rpm_key - fix handling of trailing newline in armored keys (https://github.com/ansible/ansible/issues/87304).
另有一个特殊的发布占位片段 v2.22.0-initial-commit.yaml,其内容为空映射 {},用于标记版本线起点而不携带任何变更条目。
项目对片段的格式约束在 context/documentation-standards.md 中被进一步明确:变更需要在 changelogs/fragments/ 下提供 YAML 条目;片段结构遵循 changelogs/config.yaml 中 sections 定义的分节;提交前应验证片段使用的分节是合法分节。此外 context/coding-style.md 建议 changelog 片段中的文本保持"每行一句话"的风格,便于生成 RST 时排版整齐。
发布时如何生成:release 脚本中的调用链
README 所说的"release process"在仓库中由 packaging/release.py 实现。该脚本的 prepare 命令按固定顺序执行五个步骤(见 packaging/release.py):
@command
def prepare(final: bool = False, pre: str | None = None, version: str | None = None, setuptools: bool | None = None) -> None:
"""Prepare a release."""
command.run(
update_version,
update_setuptools,
check_state,
generate_summary,
generate_changelog,
create_release_pr,
)
其中与 changelog 直接相关的两个步骤值得展开:
1. generate_summary——生成版本摘要片段
generate_summary 会向 changelogs/fragments/ 写入一个 {version}_summary.yaml 文件,内容为 release_summary 字段的表格,包含 Release Date 与 Porting Guide 链接:
content = f"""
release_summary: |
| Release Date: {release_date}
| `Porting Guide <...porting_guide_core_{major_minor}.html>`__
"""
summary_path.write_text(content.lstrip())
这正是 config.yaml 中 prelude_section_name: release_summary 的消费端——每个版本的 changelog 开头的表格就是由这个片段渲染出来的。
2. generate_changelog——调用 antsibull-changelog 聚合并校验
generate_changelog 先基于 test/lib/ansible_test/_data/requirements/sanity.changelog.txt 与主 requirements 构建独立 venv,然后依次执行:
run("antsibull-changelog", "release", "-vv", "--use-ansible-doc", env=env, cwd=CHECKOUT_DIR)
run("antsibull-changelog", "generate", "-vv", "--use-ansible-doc", env=env, cwd=CHECKOUT_DIR)
run("ansible-test", "sanity", CHANGELOGS_DIR, ANSIBLE_RELEASE_FILE, env=env, cwd=CHECKOUT_DIR)
即先以 release 动作把累积片段归并进版本化 changelog,再 generate 渲染最终内容,最后用 ansible-test sanity 对 changelogs/ 目录做一致性校验。源码中还留有一行注释表明当前从原内置生成器切换到了 antsibull-changelog(# TODO: consider switching back to the original changelog generator...)。
生成完成后,create_release_pr 会提交 changelogs/ 目录、lib/ansible/release.py 与 pyproject.toml 的变更并发起发布 PR(见 packaging/release.py)——这也解释了为什么 README 说发布分支上"已创建的版本"才有版本专属文件:这些文件正是在该 PR 合入后才出现在对应发布分支中。
devel 与发布分支的差异及实用建议
对照 README 的结论,可以形成如下实操对照:
| 场景 | 应查看的内容 |
|---|---|
在 devel 分支上了解"即将发生的变更" |
changelogs/fragments/ 下的所有 YAML 片段(devel 没有已生成的 changelog) |
| 在发布分支上核对某版本包含的变更 | 该分支上已生成的版本级 CHANGELOG-vX.Y.rst 与更新后的 changelogs/changelog.yaml |
| 为自身 PR 记录变更 | 按 changelogs/config.yaml 的 sections 选用合法分节,在 changelogs/fragments/ 新增一个 YAML 片段 |
编写片段时的几条要点(均有仓库依据):分节 key 必须取自 sections 列表;文件放在 changelogs/fragments/;文本一行一句;生成阶段由 antsibull-changelog 统一聚合,因此片段保持小粒度、单主题即可。
小结
ansible-core 的变更日志机制是"片段优先、发布聚合":changelogs/README.md 界定了 devel 分支只存片段、发布分支才有版本文件的基本原则;changelogs/config.yaml 定义了八大分节与生成行为;packaging/release.py 的 prepare 流程负责在发布时生成 release_summary 摘要片段、调用 antsibull-changelog 完成聚合渲染并通过 ansible-test sanity 校验。理解了这条链路,你在阅读任何 ansible-core 发布分支的 changelog、或为自己的修改补充变更片段时,都能准确判断内容从何而来、应放到何处。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00