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, 代码审查, 发布管理, 团队协作, 增量更新, 安全物料清单, 开发工具, 开源项目, 文档自动化, 自动化文档, 自动笔记, 逆向工具