nickschuetz/o3de-release-notes-generator
GitHub: nickschuetz/o3de-release-notes-generator
一个自动化生成O3DE发布说明的工具,通过整合GitHub拉取请求数据来简化发布文档的创建。
Stars: 1 | Forks: 0
# O3DE 发布说明生成器
一个独立工具,用于生成 [Open 3D Engine (O3DE)](https://o3de.org) 的发布说明。它通过从 GitHub 提取已合并的拉取请求,按 SIG (特殊兴趣小组) 分类,并以既定的发布说明格式渲染 Markdown。
旨在预发布周期内增量运行,以便发布团队在 PR 合并时跟踪进展。
## 前置条件
- Python 3.10+
- 已安装并认证的 [GitHub CLI (`gh`)](https://cli.github.com/) (`gh auth login`)
- O3DE 仓库的本地克隆(只读参考)
- (可选) 用于自动生成叙述性摘要的 LLM:[Ollama](https://ollama.com/) (本地,开源) 或 [Claude CLI](https://claude.ai/claude-code) (云端)
## 项目结构
```
o3de-release-notes-generator/
├── README.md # This file
├── ARCHITECTURE.md # Architecture, security model, data flow
├── CHANGELOG.md # Version history (Keep a Changelog format)
├── CONTRIBUTING.md # Dev workflow, dual-license, SHA-pin policy
├── SECURITY.md # Vulnerability disclosure
├── AGENTS.md # AI agent instructions for this repo
├── release_notes.py # Main script (zero external dependencies)
├── generate_sbom.py # CycloneDX 1.5 SBOM generator
├── sbom.cdx.json # Generated SBOM (auto-updated via CI)
├── pyproject.toml # pytest / ruff / mypy config
├── Makefile # test / sbom / lint / typecheck targets
├── tests/
│ └── test_release_notes.py # Unit tests
├── reports/ # Sample rendered release notes (committed)
├── .github/
│ └── workflows/
│ ├── sbom.yml # Auto-regenerates SBOM on push
│ └── test.yml # Runs pytest on push & PR
├── LICENSE.txt # Dual-license overview
├── LICENSE_APACHE2.TXT # Apache License 2.0
├── LICENSE_MIT.TXT # MIT License
└── .gitignore
```
## CLI 参考
该工具具有三个子命令:`fetch`、`render` 和 `generate`。
### `fetch` - 从 GitHub 提取 PR 数据到 JSON
| 标志 | 是否必需 | 默认值 | 描述 |
|------|----------|---------|------|
| `--from-ref` | 是 | - | 起始 git 引用 (标签或提交) |
| `--to-ref` | 是 | - | 结束 git 引用 (分支或标签) |
| `--default-repo-path` | 否 | `.` | 对于没有显式映射的仓库,默认的本地克隆路径 |
| `--repo-path` | 否 | - | 按仓库指定的克隆路径,格式为 `owner/repo=/path/to/clone` (可重复) |
| `--output-json` | 是 | - | 输出 JSON 文件路径 |
| `--repos` | 否 | `o3de/o3de` | PR 所在的 GitHub 仓库,格式为 `owner/repo` |
| `--dry-run` | 否 | 关闭 | 打印将要获取哪些 PR(根据 git 日志),而不调用 GitHub API 或写入文件 |
| `--no-pointrelease-audit` | 否 | 关闭 | 即使 `--from-ref` 看起来像一个点发布标签 (`X.Y.N`, `N>0`),也跳过点发布审计附属文件 |
| `--log-file` | 否 | - | 除了输出到 stderr,还将日志追加到此文件 |
| `-v` | 否 | - | 详细日志记录 |
### `render` - 从 JSON 生成 Markdown
```
python release_notes.py render \
--input-json \
--output-md \
--release-version \
[--include-uncategorized] \
[--include-release-machinery] \
[--generate-summary] \
[--summary-cmd ] \
[--summary-hint ] \
[--summary-timeout ] \
[--log-file PATH]
```
| 标志 | 是否必需 | 默认值 | 描述 |
|------|----------|---------|------|
| `--input-json` | 是 | - | `fetch` 生成的 JSON 文件路径 |
| `--output-md` | 是 | - | 输出 Markdown 文件路径 |
| `--release-version` | 是 | - | 发布版本字符串 (例如,`26.05.0`) |
| `--include-uncategorized` | 否 | 关闭 | 显示无法分类的 PR |
| `--include-release-machinery` | 否 | 关闭 | 在渲染输出中包含发布工程相关 PR (版本号提升、SBOM 自动更新、cherry-pick 到点发布包装器等)。对于主发布默认关闭;对于点发布说明,其中“机械性”工作即是主要内容时,请开启此项 |
| `--generate-summary` | 否 | 关闭 | 使用 LLM 生成叙述性摘要 |
| `--summary-cmd` | 否 | `ollama run --nowordwrap qwen2.5:14b` | 生成摘要的命令 |
| `--summary-hint` | 否 | - | 叙述引导:内联文本或 `@文件路径` (从文件读取) |
| `--summary-timeout` | 否 | `300` | 摘要命令的超时时间(秒)(范围:10–3600) |
| `--log-file` | 否 | - | 除了输出到 stderr,还将日志追加到此文件 |
### `generate` - 一步完成获取和渲染
组合 `fetch` 和 `render`。接受这两个子命令的所有标志。
## 示例
### 为特定版本生成说明
```
python release_notes.py generate \
--from-ref 2510.0 \
--to-ref development \
--default-repo-path ~/PROJECTS/o3de \
--output-json release_data.json \
--output-md 26050_release_notes.md \
--release-version 26.05.0
```
### 预发布期间增量更新
重新运行相同命令。新的 PR 会被获取;JSON 中现有的数据和任何手动编辑都会被保留。
```
# 第一周
python release_notes.py generate --from-ref 2510.0 --to-ref development \
--default-repo-path ~/PROJECTS/o3de --output-json release_data.json \
--output-md notes.md --release-version 26.05.0
# 第二周(相同命令 - 仅获取新PR)
python release_notes.py generate --from-ref 2510.0 --to-ref development \
--default-repo-path ~/PROJECTS/o3de --output-json release_data.json \
--output-md notes.md --release-version 26.05.0
```
### 多仓库并使用独立的本地克隆
```
python release_notes.py generate \
--from-ref 2510.0 --to-ref development \
--repos o3de/o3de o3de/o3de-extras \
--default-repo-path ~/PROJECTS/o3de \
--repo-path o3de/o3de-extras=~/PROJECTS/o3de-extras \
--output-json release_data.json \
--output-md notes.md \
--release-version 26.05.0
```
每个仓库都针对其自己的本地克隆运行 `git log`。对于没有显式 `--repo-path` 映射的任何仓库,将使用 `--default-repo-path`。
### 使用自动化叙述摘要生成
```
python release_notes.py generate \
--from-ref 2510.0 --to-ref development \
--default-repo-path ~/PROJECTS/o3de \
--output-json release_data.json \
--output-md notes.md \
--release-version 26.05.0 \
--generate-summary
```
此操作会从分类的 PR 数据构建结构化提示,并通过 stdin 管道传递给摘要命令(默认:`ollama run --nowordwrap qwen2.5:14b`)。生成的叙述将替换 Markdown 输出中的占位符简介。
要使用不同的模型或工具:
```
# Claude CLI(云服务,最高质量)
--generate-summary --summary-cmd "claude -p"
# 为显存更大的机器准备的较大本地模型
--generate-summary --summary-cmd "ollama run --nowordwrap qwen2.5:32b"
# 或任何从stdin读取提示并写入stdout的工具
--generate-summary --summary-cmd "my-llm-tool --flag"
# 为较慢模型/硬件增加超时时间
--generate-summary --summary-timeout 900
```
该命令必须从 **stdin** 读取提示,并将其响应写入 **stdout**。
### 使用提示引导叙述
使用 `--summary-hint` 引导 LLM 关注特定主题或语调:
```
python release_notes.py generate \
--from-ref 2510.0 --to-ref development \
--default-repo-path ~/PROJECTS/o3de \
--output-json release_data.json \
--output-md notes.md \
--release-version 26.05.0 \
--generate-summary \
--summary-hint "This is a major platform expansion release. Emphasize Wayland support, Mac ARM64, and Emscripten. Note that PhysX4 deprecation is a breaking change."
```
该提示作为“发布管理者的额外指导”注入到 LLM 提示中,并塑造叙述,而不会覆盖结构化的 PR 数据。
要从文件加载提示,请在路径前加上 `@`:
```
--summary-hint @release_briefing.txt
```
这对于较长的指导或在增量运行中重用相同的叙述方向非常有用。
### 仅获取(供 AI 代理消费)
```
python release_notes.py fetch \
--from-ref 2510.0 --to-ref development \
--default-repo-path ~/PROJECTS/o3de \
--output-json release_data.json
```
### 包含未分类 PR 以供分类处理
```
python release_notes.py generate \
--from-ref 2510.0 --to-ref development \
--default-repo-path ~/PROJECTS/o3de \
--output-json release_data.json \
--output-md notes.md \
--release-version 26.05.0 \
--include-uncategorized
```
### 演练运行(预览将获取哪些 PR)
```
python release_notes.py fetch \
--from-ref 2510.0 --to-ref development \
--default-repo-path ~/PROJECTS/o3de \
--output-json /tmp/unused.json \
--dry-run
```
在本地读取 `git log` 并打印将要获取的 PR 编号。不调用 GitHub API;不写入文件。在长时间运行前验证引用和克隆路径非常有用。
### 当点发布已在前一主线上发布时生成说明
当点发布已在上一个主版本和当前周期之间发布时(例如,`2510.0` → `2510.1` → `2510.2`),请将**最新的点发布标签**作为 `--from-ref` 传递:
```
python release_notes.py generate \
--from-ref 2510.2 \
--to-ref origin/stabilization/26050 \
--repos o3de/o3de o3de/o3de-extras \
--repo-path o3de/o3de=~/PROJECTS/o3de \
--repo-path o3de/o3de-extras=~/PROJECTS/o3de-extras \
--output-json reports/release_data.json \
--output-md reports/26050_release_notes.md \
--release-version 26.05.0
```
该工具自动检测点发布模式并:
1. 发出一行 `INFO` 日志,指出 `2510.0` 和 `2510.2` 相对于 `--to-ref` 的 merge-base 是相同的(点发布 cherry-pick 被正确排除;它们捆绑的修复通过开发侧的合并计数)。
2. 在 `reports/26050_release_notes_pointrelease_audit.md` 写入一个**点发布审计附属文件**,列出在之前的稳定分支上找到的每个 cherry-pick 容器 PR,其中每个捆绑的 PR 显示为 ✓ (存在于渲染的报告中) 或 ✗ (缺失;需要调查)。将手动的“我们是否丢失了任何修复?”检查转变为一目了然的清单。使用 `--no-pointrelease-audit` 可以抑制此行为。
3. 在 JSON 中用 `release_machinery: true` 标记发布工程相关的 PR(版本号提升、SBOM 自动更新、cherry-pick 包装器、“将点发布合并到主分支”的合并等),并默认将其从渲染输出中排除。使用 `--include-release-machinery` 可以重新包含它们;这对于点发布说明非常有用,因为这些工程相关 PR 本身就是主要内容。
## 示例输出
一个针对 `o3de/o3de` 25.10.0 → 26.05.0 的实际运行(228 个 PR)渲染效果如下:
```
# 26.05.0版本发布说明
The O3DE 26.05.0 release includes bug fixes, performance enhancements,
and new features across the engine.
# 完整变更列表
## SIG-Build
- Remove system cmake dependency from the Linux installer. [o3de#19704](https://github.com/o3de/o3de/pull/19704)
- Update vcpkg baseline for clang-19 builds. [o3de#19712](https://github.com/o3de/o3de/pull/19712)
- ...
## SIG-Graphics-Audio
- Fix shader compilation error in Atom on dx12. [o3de#19651](https://github.com/o3de/o3de/pull/19651)
- ...
## SIG-Platform
- Initial Wayland support for Linux. [o3de#19589](https://github.com/o3de/o3de/pull/19589)
- ...
```
使用 `--generate-summary` 时,`` 占位符会被替换为真实的叙述内容。完整的示例运行已签入到 [`reports/`](reports/) 目录下(一个完整的发布;可根据需要手动刷新)。
## JSON 模式
中间 JSON 是主要的数据格式。它可以由人类编辑,也可以被 AI 代理消费。
```
{
"metadata": {
"generated_at": "2026-04-21T10:00:00+00:00",
"from_ref": "2510.0",
"to_ref": "development",
"repos": ["o3de/o3de", "o3de/o3de-extras"],
"repo_paths": {
"o3de/o3de": "/home/user/PROJECTS/o3de",
"o3de/o3de-extras": "/home/user/PROJECTS/o3de-extras"
},
"schema_version": 3,
"pr_count": 228,
"categorization_summary": {
"label": 152,
"heuristic_title": 55,
"heuristic_files": 17,
"uncategorized": 4
},
"release_machinery_count": 1,
"merge_bases": {
"o3de/o3de": {
"sha": "57680ee42f18d5952e4d4fa5ab52750edefb878e",
"committer_date": "2025-07-29T11:12:47-07:00"
},
"o3de/o3de-extras": {
"sha": "3038e4ac7b566b8b0ab7360acc67d6280eb68eba",
"committer_date": "2025-09-08T14:48:13+02:00"
}
},
"effective_window": {
"start": "2025-07-29T11:12:47-07:00",
"end": "2026-05-20T16:55:32+00:00"
}
},
"pull_requests": [
{
"number": 19709,
"repo": "o3de/o3de",
"title": "Fix for choppy mouse movement in FlyCameraInputComponent",
"url": "https://github.com/o3de/o3de/pull/19709",
"author": "contributor",
"merged_at": "2026-04-20T17:14:14Z",
"labels": ["sig/content"],
"files": ["Gems/AtomLyIntegration/.../FlyCameraInputComponent.cpp"],
"sig_category": "sig/content",
"categorization_source": "label",
"description": "Fix for choppy mouse movement in FlyCameraInputComponent.",
"flags": [],
"release_machinery": false,
"manual_override_sig": null,
"manual_override_description": null
}
]
}
```
### 关键字段
| 字段 | 描述 |
|------|------|
| `sig_category` | 已分配的 SIG。自动设置,或通过 `manual_override_sig` 设置。 |
| `categorization_source` | SIG 的分配方式:`label`(标签)、`heuristic_title`(启发式标题)、`heuristic_files`(启发式文件)、`uncategorized`(未分类)、`manual_override`(手动覆盖)。 |
| `flags` | 自动检测的标志:`cherry-pick`、`stabilization-sync`。被标记的 PR 会从渲染的 Markdown 中排除。 |
| `release_machinery` | 自动检测的布尔值,用于标记发布工程相关 PR(版本号提升、SBOM 自动更新、cherry-pick 到点发布包装器、仅涉及 `engine.json`/`sbom.cdx.json`/`version.txt` 的差异)。默认从渲染的 Markdown 和摘要提示中排除;使用 `--include-release-machinery` 可重新包含。 |
| `manual_override_sig` | 设置此项可将 PR 重新分配给不同的 SIG。在重新运行时保留。 |
| `manual_override_description` | 设置此项可覆盖自动生成的描述。在重新运行时保留。 |
| `metadata.merge_bases` | 按仓库的 `{sha, committer_date}`,记录 `from_ref` 和 `to_ref` 的 merge-base。锚定实际的分叉点。 |
| `metadata.effective_window` | 差异所覆盖的 `{start, end}` 时间窗口。`start` 是所有仓库中最早 merge-base 的提交者日期;`end` 是 `generated_at` 的时间。 |
| `metadata.release_machinery_count` | 本次运行中标记为 `release_machinery: true` 的 PR 数量。 |
## SIG 分类
PR 使用三种方法按优先级顺序进行分类:
1. **GitHub 标签** - 具有 `sig/*` 标签(例如 `sig/build`、`sig/graphics-audio`)的 PR 直接分类。置信度最高。
2. **标题关键词** - PR 标题与每个 SIG 的关键词列表进行匹配。
3. **文件路径** - 更改的文件路径与目录到 SIG 的映射进行匹配。
如果都不匹配,则将 PR 标记为 `uncategorized` 以便手动分类。
### 更新启发式规则
分类数据作为四个数据驱动的结构体位于 `release_notes.py` 文件顶部:
| 常量 | 用途 |
|------|------|
| `SIG_CANONICAL_ORDER` | 规范的 SIG 列表。定义 Markdown 输出中的章节顺序,**并且**当 PR 具有多个 SIG 标签或其标题匹配多个 SIG 的关键词时,作为确定性的决胜因素。 |
| `SIG_DISPLAY_NAMES` | 从 `sig/foo` 到 `SIG-Foo` 的映射(渲染的 Markdown 中出现的标题)。 |
| `SIG_TITLE_KEYWORDS` | 针对标题启发式分类器的每个 SIG 的关键词列表。 |
| `SIG_FILE_PATH_PATTERNS` | 针对文件启发式分类器的每个 SIG 的文件路径前缀列表(最长匹配优先)。 |
要**调整**现有 SIG 的启发式规则,请编辑 `SIG_TITLE_KEYWORDS` 和/或 `SIG_FILE_PATH_PATTERNS`。要**添加新的 SIG**,您必须更新*所有四个*;否则新的 SIG 要么不会被渲染(缺少显示名称),要么根本不会被识别(不在规范顺序中)。
## 叙述性摘要生成
当启用 `--generate-summary` 时,该工具会从分类的 PR 数据构建结构化提示,并将其发送到可配置的 LLM 命令。
**工作原理:**
1. PR 按 SIG 分组,每组最多包含 15 个标题(对于大章节会进行截断)。
2. Cherry-pick 和未分类的 PR 被排除在提示之外。
3. 如果提供了 `--summary-hint`(内联文本或 `@文件路径`),它将作为“发布管理者的额外指导”注入。
4. 提示要求生成 2-3 段类似以往 O3DE 发布说明风格的叙述。
5. LLM 的输出经过清理(去除前言/分隔符)并替换 `` 占位符。
**默认命令:** `ollama run --nowordwrap qwen2.5:14b`([Ollama](https://ollama.com/) 搭配 Qwen 2.5 14B)。使用 `--summary-cmd` 覆盖。默认设置针对约 12GB 显存的典型工作站;如果有余量,可以升级到 `qwen2.5:32b`,或者使用 `claude -p` 以获得最高质量。
**支持的 LLM 选项:**
| 命令 | 类型 | 质量 | 要求 |
|------|------|------|------|
| `claude -p` | 云端 | 最高 | [Claude CLI](https://claude.ai/claude-code) 已认证 |
| `ollama run --nowordwrap qwen2.5:32b` | 本地 | 最高本地 | [Ollama](https://ollama.com/),约 24GB 显存 |
| `ollama run --nowordwrap qwen2.5:14b` | 本地 | 高 | [Ollama](https://ollama.com/),约 12GB 显存 (默认) |
| `ollama run --nowordwrap mistral` | 本地 | 良好 | [Ollama](https://ollama.com/),约 6GB 显存 |
**自定义命令的要求:** 必须从 stdin 读取提示,并将响应写入 stdout。LLM 的前言文本(例如 "Here's the summary:")和 `---` 分隔符会自动从输出中剥离。
**禁用时(默认):** 会插入一个占位符简介和 `` 注释供手动编写。
## SBOM (软件物料清单)
CycloneDX 1.5 SBOM 维护在 `sbom.cdx.json` 中。它由 GitHub Action 在每次推送到 `main` 分支且更改了 Python 源文件时自动重新生成。
要在本地重新生成:
```
python generate_sbom.py
```
SBOM 包含:
- 项目元数据(名称、版本、许可证、仓库 URL)
- 用作依赖项的 Python 标准库模块(13 个模块)
- 所有源文件的 SHA-256 哈希值,用于完整性验证
- 显式声明零外部依赖项
## 运行测试
```
python -m pytest tests/ -v
```
224 个单元测试,涵盖输入验证(包括路径遍历边缘情况)、多仓库路径解析、SIG 分类(包括标题和文件启发式的确定性决胜)、GraphQL 变量结构、摘要提示构建、摘要生成(含超时边界验证)、LLM 输出清理、Markdown 渲染(包括发布工程过滤)、增量合并(带丢弃警告行为)、演练运行、原子 I/O、stderr 令牌编辑、PR 正文大小限制、点发布标签解析、同级标签发现、merge-base 提取、cherry-pick 容器解析、点发布审计附属文件生成、发布工程分类、点发布感知日志记录和安全控制。
提供了一个 `Makefile` 用于常见目标:
```
make test # run pytest
make sbom # regenerate sbom.cdx.json
make lint # ruff (if installed)
make typecheck # mypy (if installed)
```
## 安全
此工具设计遵循 OWASP 和 NIST SP 800-53 安全控制。完整的安全模型、威胁分析、信任边界和输入验证规范请参见 [ARCHITECTURE.md](ARCHITECTURE.md)。要报告漏洞,请参见 [SECURITY.md](SECURITY.md)。
关键亮点:
- 零外部依赖项(仅 Python 标准库)
- 所有子进程调用均使用列表参数(无 `shell=True`)
- 所有子进程输出使用 `encoding='utf-8', errors='replace'` 解码
- 所有用户输入在使用前均通过正则表达式验证
- GraphQL 查询使用服务器端变量 (`$owner`, `$name`);无字符串插值
- GitHub 认证委托给 `gh` CLI;日志记录前清理 stderr 中的令牌格式
- 原子文件写入防止数据损坏
- PR 标题已消毒以防止 Markdown 注入;提取前 PR 正文限制为 64KB
- 摘要命令运行时间有界 (`--summary-timeout`,默认 300 秒,范围 10–3600 秒)
- 带有源文件哈希的 CycloneDX SBOM 确保供应链透明度
- GitHub Actions 固定到提交 SHA(而非浮动标签)
## 贡献
开发工作流程、双重许可证政策和 GitHub Actions SHA 固定政策请参见 [CONTRIBUTING.md](CONTRIBUTING.md)。
## 许可证
Apache-2.0 OR MIT (参见 [LICENSE.txt](LICENSE.txt))
标签:AI辅助, AI风险缓解, DNS解析, GitHub Actions, GitHub集成, Markdown生成, O3DE, Open 3D Engine, PR分类, Python编程, SBOM生成, SOC Prime, 代码审查, 发布管理, 团队协作, 增量更新, 安全物料清单, 开发工具, 开源项目, 文档自动化, 自动化文档, 自动笔记, 逆向工具