mcpField
G
gen-release-notes

gen-release-notes

github.com/existential-birds开发8562 次安装11 周前更新
代码仓库

关于 gen-release-notes

根据指定标签以来的变更生成符合 Keep a Changelog 格式的发行说明。它指导变更分析、分类、版本建议、更新 changelog,并验证比较链接。

主要功能

  1. 01从前一标签收集变更
  2. 02按 Keep a Changelog 分类
  3. 03提供 Semantic Versioning 版本建议
  4. 04CHANGELOG.md 更新步骤
  5. 05页脚比较链接验证关卡

适用场景

  1. 01准备标签版本的发行说明
  2. 02按用户影响整理变更
  3. 03建议发行版本号
  4. 04检查 changelog 比较链接

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/existential-birds/beagle --skill gen-release-notes

安装后在 Claude Code 中输入/gen-release-notes确认已加载。

详细介绍

发布说明生成器

按照 Keep a Changelog 标准生成专业的发布说明。

输入:上一个标签(例如,v0.0.1)

$ARGUMENTS

生成发布说明前,请彻底分析变更。

门槛

不要编造标签、PR 编号或链接。每一行都必须通过检查,才能进行依赖于该检查的工作。

时机通过条件(证据)未通过时
执行 git log / git diff 前git tag -l "$PREV_TAG" 恰好输出一行,且与 PREV_TAG 匹配停止;报告标签缺失——不要编写更新日志条目
分类前git rev-parse "$PREV_TAG^{commit}" 以状态码 0 退出停止;修正 PREV_TAG 或仓库检出状态
使用 gh pr list 时命令以状态码 0 退出,且 JSON 有效回退到提交主题和合并提交 URL;不要编造 PR 编号
第 5 步页脚编辑后第 6 步页脚门槛以状态码 0 退出(针对暂存的 CHANGELOG.md 的两次 grep -q 检查均通过)重新执行第 5 步的页脚编辑,然后重新执行第 6 步,直到以状态码 0 退出

第 1 步:收集变更

运行以下命令,收集自提供的标签以来的变更信息:

# Store the previous tag
PREV_TAG="$ARGUMENTS"

# Gate: tag must exist (output must be non-empty and match PREV_TAG)
git tag -l "$PREV_TAG"
# If the line above prints nothing, STOP — do not continue below.

# Get the repo URL for PR links
git remote get-url origin

# List commits since last tag
git log ${PREV_TAG}..HEAD --pretty=format:"%h %s" --no-merges

# Get detailed diff stats
git diff ${PREV_TAG}..HEAD --stat

# List changed files by directory
git diff ${PREV_TAG}..HEAD --name-only | sort | uniq

另行收集 PR 信息:

# Get merged PRs since the tag (requires gh CLI)
gh pr list --state merged --search "merged:>=$(git log -1 --format=%ci $PREV_TAG | cut -d' ' -f1)" --json number,title,author,labels

第 2 步:分析并分类

将每项变更归入以下类别之一,且只能归入一个类别(按此顺序):

类别包含排除
Added(新增)新功能、新的公开 API、新的 CLI 命令未向用户公开的内部工具
Changed(变更)行为修改、性能改进、对用户有影响的依赖更新不改变行为的重构
Deprecated(弃用)标记为将来移除的功能-
Removed(移除)删除的功能、移除的公开 API移除的内部代码
Fixed(修复)Bug 修复、错误处理改进仅涉及测试的修复
Security(安全)漏洞补丁、安全加固-

完全排除:

  • CI/CD 配置变更(除非会影响用户)
  • 仅涉及文档的变更(除非揭示了新功能)
  • 代码风格/格式变更
  • 仅涉及测试的变更
  • 不影响用户体验的内部重构
  • 合并提交

第 3 步:确定版本号

根据变更,按照语义化版本规范建议下一个版本:

  • MAJOR(X.0.0):破坏性变更影响公开 API
  • MINOR(x.Y.0):新增功能,且向后兼容
  • PATCH(x.y.Z):仅修复 Bug

根据现有标签检测标签格式(带或不带 v 前缀)。

第 4 步:编写发布说明

使用以下精确格式生成 CHANGELOG.md 条目:

## [VERSION] - YYYY-MM-DD

### Added

- **scope:** Add new feature description ([#54](REPO_URL/pull/54))

### Changed

- **Breaking:** Rename `oldName()` to `newName()` for consistency ([#145](REPO_URL/pull/145))

  **Migration:** Replace all calls to `oldName()` with `newName()`.

### Deprecated

- **scope:** Deprecate `legacy_function()` in favor of `new_function()` ([#143](REPO_URL/pull/143))

### Removed

- **Breaking:** Remove deprecated `old_function()` ([#141](REPO_URL/pull/141))

### Fixed

- **scope:** Fix race condition when multiple workers access shared state ([#139](REPO_URL/pull/139))

### Security

- **deps:** Update vulnerable package to patched version ([#49](REPO_URL/pull/49))
编写规则

格式要求:

  • 每个条目均以祈使动词开头:Add、Fix、Remove、Update、Improve、Rename、Deprecate、Patch
  • 存在作用范围前缀时,使用粗体:**server:**、**cli:**、**api:**
  • 每项变更占一行(破坏性变更除外,需附迁移说明)
  • 在行末添加 PR/issue 链接
  • 在每个类别内按重要性排序条目(影响最大的排在最前)
  • 完全省略空类别

破坏性变更:

  • 添加粗体前缀 **Breaking:**
  • 在所属类别中排在首位
  • 在下一行添加 **Migration:** 区块,准确说明用户必须做哪些更改
  • 对 API 签名变更提供更改前后的代码示例

语气:

  • 面向库的使用者,而非维护者
  • 关注用户有哪些变化,而非实现方式
  • 具体明确——绝不要写“各种改进”或“Bug 修复”
  • 每个条目都应无需阅读 PR 即可理解

应避免的不良示例:

# BAD - Too vague
- Fixed bugs
- Performance improvements
- Updated dependencies

# BAD - Implementation-focused
- Refactored the internal state machine to use async/await

# BAD - Missing context
- Fixed #234

应遵循的良好示例:

# GOOD - Specific and user-focused
- **server:** Fix timeout errors when processing files larger than 100MB ([#234](URL))
- **cli:** Add `--dry-run` flag to preview changes before execution ([#235](URL))
- **api:** Improve cold-start latency from 2.3s to 0.8s by lazy-loading plugins ([#236](URL))

步骤 5:更新 CHANGELOG.md

  1. 如果存在 CHANGELOG.md:

    • 在 ## [Unreleased] 部分后插入新版本(如果没有 Unreleased,则插入到顶部)
  2. 如果不存在 CHANGELOG.md,则使用以下标题创建:

# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

  1. 更新文件底部的页脚引用链接。 此步骤为必做项——如果缺少这些链接,CodeRabbit 和其他审查者会将变更日志标记为不完整。需要进行两项编辑:

    a. 推进 [Unreleased],使其与新版本进行比较,而非与之前的版本比较。 b. 在 [Unreleased] 正下方插入一行 [NEW_VERSION],比较之前的标签与新版本。

    示例差异(在 3.1.0 之后发布 3.2.0):

    -[Unreleased]: https://github.com/OWNER/REPO/compare/v3.1.0...HEAD
    +[Unreleased]: https://github.com/OWNER/REPO/compare/v3.2.0...HEAD
    +[3.2.0]: https://github.com/OWNER/REPO/compare/v3.1.0...v3.2.0
     [3.1.0]: https://github.com/OWNER/REPO/compare/v3.0.0...v3.1.0
    

    编辑后,运行步骤 6,验证两行页脚均存在且正确。不要跳过步骤 6——它是硬性门槛,可防止反复出现“缺少页脚比较链接”的审查反馈。

步骤 6:验证 CHANGELOG 页脚比较链接(硬性门槛)

这是针对页脚比较链接的强制检查。必须在 gen-release-notes 报告成功之前,以及发布工作流中的任何提交步骤之前运行。此检查并非建议项——两个 grep -q 检查都必须以 0 退出,否则检查失败并指出缺失的行。

请原样运行以下代码块:

# Read the staged CHANGELOG.md blob (index), not the working tree, so unstaged
# edits cannot make the gate pass when the commit would actually fail.
STAGED_CHANGELOG=$(git show :CHANGELOG.md)

# Extract NEW and PREV versions from the staged CHANGELOG.md.
# We only match numeric `## [X.Y.Z]` headings so `## [Unreleased]` is skipped.
NEW_VERSION=$(printf '%s\n' "$STAGED_CHANGELOG" | grep -m1 -E '^## \[[0-9]+\.[0-9]+\.[0-9]+\]' | sed -E 's/^## \[([0-9]+\.[0-9]+\.[0-9]+)\].*/\1/')
PREV_VERSION=$(printf '%s\n' "$STAGED_CHANGELOG" | grep -m2 -E '^## \[[0-9]+\.[0-9]+\.[0-9]+\]' | sed -n '2p' | sed -E 's/^## \[([0-9]+\.[0-9]+\.[0-9]+)\].*/\1/')

if [ -z "$NEW_VERSION" ] || [ -z "$PREV_VERSION" ]; then
  echo "GATE FAIL: could not extract NEW_VERSION ($NEW_VERSION) or PREV_VERSION ($PREV_VERSION) from staged CHANGELOG.md"
  exit 1
fi

# Infer optional tag prefix (`v` or empty) from the existing [Unreleased] footer.
# Step 3 explicitly allows tags with or without `v`; never hardcode `v` here.
# Fall back to `v` only when no [Unreleased] footer exists yet (first release).
TAG_PREFIX=$(printf '%s\n' "$STAGED_CHANGELOG" | sed -nE 's|^\[Unreleased\]: .*/compare/(v?)[0-9]+\.[0-9]+\.[0-9]+\.\.\.HEAD$|\1|p' | head -1)
if ! printf '%s\n' "$STAGED_CHANGELOG" | grep -qE '^\[Unreleased\]:'; then
  TAG_PREFIX="v"
fi

echo "Gating footer compare links: NEW=${TAG_PREFIX}${NEW_VERSION}, PREV=${TAG_PREFIX}${PREV_VERSION}"

# Escape dots so they match literally inside the regex.
NEW_RE=${NEW_VERSION//./\\.}
PREV_RE=${PREV_VERSION//./\\.}

# Check 1: the new [NEW_VERSION] footer line exists and points PREV->NEW.
printf '%s\n' "$STAGED_CHANGELOG" | grep -qE "^\[${NEW_RE}\]: .*compare/${TAG_PREFIX}${PREV_RE}\.\.\.${TAG_PREFIX}${NEW_RE}\$" \
  || { echo "GATE FAIL: missing footer line: [${NEW_VERSION}]: .../compare/${TAG_PREFIX}${PREV_VERSION}...${TAG_PREFIX}${NEW_VERSION}"; exit 1; }

# Check 2: the [Unreleased] footer line is advanced to compare from the new tag.
printf '%s\n' "$STAGED_CHANGELOG" | grep -qE "^\[Unreleased\]: .*compare/${TAG_PREFIX}${NEW_RE}\.\.\.HEAD\$" \
  || { echo "GATE FAIL: [Unreleased] is not advanced; expected: [Unreleased]: .../compare/${TAG_PREFIX}${NEW_VERSION}...HEAD"; exit 1; }

echo "Footer compare links verified: [${NEW_VERSION}] and [Unreleased] both present and correct."

通过条件(客观标准): 上方两个 grep -q 调用针对暂存的 CHANGELOG.md 均以 0 退出。代码块必须打印 Footer compare links verified: ... 并以 0 退出。

失败时: 检查会指出缺失或错误的行。返回步骤 5,编辑 CHANGELOG.md 以添加或更正该行,然后重新运行此代码块。在代码块以 0 退出之前,不要继续执行步骤 7,也不要报告成功。

步骤 7:输出摘要

更新变更日志后,提供:

  1. 建议的版本号及理由
  2. 分类变更摘要
  3. 需要特别注意的任何重大变更
  4. 确认已更新 CHANGELOG.md

Conventional Commits 映射

将提交前缀映射到变更日志类别:

提交前缀变更日志类别
feat(scope):新增
feat!(scope):新增(带有 Breaking 前缀)
fix(scope):修复
perf(scope):变更
security(scope):安全
docs:, chore:, ci:, test:, style:排除

评论

支持 Markdown