关于 conventional-changelog
conventional-changelog 是介绍 Conventional Commits 并比较变更日志生成与版本发布工具的技能。它帮助开发团队根据语言、仓库结构和发布流程选择适合的发布工作流。
主要功能
- 01Conventional Commits 指南
- 02变更日志工具比较
- 03发布工作流配置示例
- 04Semver 与标签实践
- 05Monorepo 发布指南
适用场景
- 01配置仓库发布流程
- 02自动生成变更日志
- 03为语言或 Monorepo 选择工具
- 04规划分阶段预发布
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ mizchi/ skills --skill conventional-changelog安装后在 Claude Code 中输入/conventional-changelog确认已加载。
附带文件README.mdSKILL-ja.md
详细介绍
Conventional Changelog
本指南介绍如何按照 Conventional Commits 规范编写提交消息,并据此自动生成 CHANGELOG 和 semver 标签。介绍 npm / cargo / python / rust / go 生态中的工具,避免手动编写 CHANGELOG。
适用场景
- 为新仓库搭建发布流程
- 现有仓库的提交消息格式不一致,希望引入 CHANGELOG 自动生成
- 试用过 release-please,但发现它不合适/想评估其他方案
- 寻找适合特定语言的变更日志工具(Rust / Python / Go 等)
- 标签和 CHANGELOG 手动维护时总出问题,希望将其自动化
Conventional Commits 格式
格式:<type>[optional scope]: <subject>
feat(api): add rate-limit middleware
fix(auth): reject expired tokens before DB lookup
docs: clarify OAuth flow diagram
chore: bump deps
类型列表及 semver 影响
| type | semver 升级级别 | 是否包含在 CHANGELOG 中 |
|---|---|---|
feat | minor | ✓(新增 / 功能) |
fix | patch | ✓(修复) |
perf | patch | ✓(变更) |
refactor | 无 | ×(默认) |
docs | 无 | × |
style | 无 | × |
test | 无 | × |
build | 无 | × |
ci | 无 | × |
chore | 无 | × |
revert | 取决于被撤销的提交 | ✓ |
破坏性变更(major 升级)
有两种方式:在页脚中添加 BREAKING CHANGE:,或在类型后添加 !:
feat(api)!: replace REST endpoint with GraphQL
BREAKING CHANGE: /api/v1/users is removed. Use GraphQL Query.user instead.
两种方式都写上更周全(标题一目了然,页脚则补充详细信息)。
scope
可选,但添加后能让 CHANGELOG 更易读。在 monorepo 中,可使用包名作为 scope:
feat(@pkg/auth): add oauth2 flow
feat(@pkg/billing): stripe migration
release-please 还会根据 scope 自动检测“变更的目标包”(在 manifest 模式下)。
subject
- 以小写字母开头(末尾不加句号)
- 使用祈使语气(“add X” / “fix Y”;避免使用“added”或“fixes”)
- 尽量控制在 50 个字符以内
正文 / 页脚
正文前空一行,页脚前再空一行(Closes #123、Co-authored-by: ...、BREAKING CHANGE: ...)。
CHANGELOG.md 格式(Keep a Changelog)
遵循 keepachangelog.com。版本按降序排列,使用 semver,并固定采用以下 6 个章节:
# Changelog
All notable changes to this project are documented here.
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]
### Added
- foo bar
## [1.2.0] - 2026-04-19
### Added
- New `--json` output flag for `foo scan` ([#42](https://github.com/owner/repo/pull/42))
### Changed
- `parse_config` now accepts YAML in addition to TOML
### Fixed
- Crash when config file is empty ([#40](https://github.com/owner/repo/issues/40))
### Deprecated
- `--legacy-mode` flag. Use `--mode=legacy` instead.
### Removed
- `foo bar --old-flag` (deprecated in 1.0.0)
### Security
- Upgrade `openssl` to 3.2.0 to address CVE-2025-XXXXX
## [1.1.0] - 2026-03-01
...
[Unreleased]: https://github.com/owner/repo/compare/v1.2.0...HEAD
[1.2.0]: https://github.com/owner/repo/compare/v1.1.0...v1.2.0
6 个章节:
Added:新增功能Changed:现有功能的变更Deprecated:计划移除(仍然存在)Removed:已移除Fixed:缺陷修复Security:安全漏洞修复
semver 标签实践
- 标签采用
v<MAJOR>.<MINOR>.<PATCH>格式(v1.2.3)。通常使用v前缀 - 预发布版本:
v1.0.0-beta.1/v1.0.0-rc.1 git tag -a v1.2.3 -m "Release v1.2.3"(附注标签)→git push origin v1.2.3- 通过 GitHub Release + Actions 和
on: release配置标签创建与部署
工具对比
| 工具 | 支持的语言 | 风格 | CHANGELOG 生成 | 版本升级 | 主要优势 | 缺点 |
|---|---|---|---|---|---|---|
| release-please | npm / python / rust / go / java / php / ruby | 自动创建 Release PR | ✓ | ✓(合并 PR 时打标签) | 支持 monorepo,由 Google 维护,稳定可靠 | 基于 PR,存在一定学习成本 |
| changesets | 仅 npm | 通过 changeset add 手动添加条目 | ✓ | ✓ | 适用于 monorepo,灵活(npm workspaces) | 不基于提交消息驱动 |
| conventional-changelog-cli | 与语言无关(命令行工具) | 根据提交一次性生成 | ✓ | × | 轻量,可追加到现有 CHANGELOG | 标签 / 版本升级需另外手动处理 |
| standard-version(已弃用) | npm | 提交 → 升级版本 + CHANGELOG | ✓ | ✓ | 简单 | 已停止维护;建议迁移到 release-please |
| git-cliff | 与语言无关(使用 Rust 编写) | 通过 TOML 配置高度自定义 | ✓ | × | 快速、模板灵活,在 Rust 生态中广受欢迎 | 标签 / 版本升级需单独处理 |
| towncrier | Python | 为每个 PR 编写一段新闻条目 | ✓ | × | 通过条目避免合并冲突 | 工作流较特别 |
| auto (intuit/auto) | 多种语言 | 基于标签 + CI 集成 | ✓ | ✓ | 由 GitHub 标签驱动 | 需要自定义标签约定 |
一句话建议
- 想自动化发布单个 npm 包 → release-please
- npm monorepo → release-please(清单模式)或 changesets(需要精细控制时)
- Rust → git-cliff
- Python → towncrier(或 release-please)
- Go / 通用场景 → git-cliff 或 release-please
- 只想生成 CHANGELOG(手动管理标签) → conventional-changelog-cli 或 git-cliff
release-please 设置(最推荐的路径)
在 npm 项目中进行全新设置:
// release-please-config.json
{
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
"release-type": "node",
"packages": {
".": { "package-name": "my-pkg", "changelog-path": "CHANGELOG.md" }
},
"include-v-in-tag": true,
"bump-minor-pre-major": true
}
// .release-please-manifest.json
{ ".": "0.0.0" }
# .github/workflows/release-please.yml
name: release-please
on:
push:
branches: [main]
permissions:
contents: write
pull-requests: write
jobs:
release-please:
runs-on: ubuntu-latest
steps:
- uses: googleapis/release-please-action@v5
with:
config-file: release-please-config.json
manifest-file: .release-please-manifest.json
按照 Conventional Commits 规范编写提交并推送到 main;系统会自动创建「Release PR」。合并后会触发标签创建、GitHub Release 发布和 CHANGELOG 更新。
与发布关联的两种工作流形式:
| 形式 | 触发条件 | 优势 | 劣势 |
|---|---|---|---|
在同一工作流中拆分作业(needs: release-please + if: outputs.release_created) | push: main | 集中在一个文件中,可直接引用 release-please 的输出,易于理解 | 如果 release-please 作业失败,发布也会随之失败;职责容易混杂 |
独立工作流(on: release) | release: { types: [published] } | 职责分离,手动发布时也会运行发布流程,重新运行互不影响 | 必须在两个地方声明 secrets / permissions;无法使用 release-please 的输出 |
选择建议:对于只有一个发布目标的单个包(仅 npm),使用单个工作流更简洁。如果有多个发布目标(npm + GitHub Container Registry 等),或也允许手动发布,则使用独立的 on: release 工作流。使用 OIDC Trusted Publishing 时,只需将 id-token: write 添加到发布作业,两种形式都可行。
详情请参阅 npm-release 技能中本地管理的发布流程。
预发布(beta / rc)分阶段发布
如果要按 beta → rc → 稳定版的顺序发布包含重大变更的 v2.0.0,请使用 release-please 的 prerelease / prerelease-type 标志:
// release-please-config.json
{
"packages": {
".": {
"package-name": "my-pkg",
"prerelease": true,
"prerelease-type": "beta"
}
}
}
流程:
- 使用
prerelease-type: "beta"累积提交 → 发布v2.0.0-beta.1 - 继续累积提交,版本依次变为
v2.0.0-beta.2/beta.3... - 升级为 RC:将配置改为
"prerelease-type": "rc"→ 发布v2.0.0-rc.1 - 发布稳定版:设置
"prerelease": false(或移除该字段)→ 发布v2.0.0
如何编写 CHANGELOG:release-please 会为每个 beta / rc 单独生成一个定稿章节(不会像 Keep a Changelog 那样累积到 [Unreleased] 中)。[2.0.0-beta.1] / [2.0.0-beta.2] / [2.0.0-rc.1] / [2.0.0] 都会保留。稳定版 2.0.0 章节会汇总这一期间的所有 feat! / BREAKING CHANGE。
Monorepo 与 workspace:* 的交互
在包含 @org/core、@org/ui、@org/cli 的 pnpm workspace 中,@org/ui 和 @org/cli 依赖于 @org/core:
// release-please-config.json
{
"plugins": [
{ "type": "node-workspace", "updatePeerDependencies": true }
],
"packages": {
"packages/core": {
"release-type": "node",
"package-name": "@org/core",
"include-component-in-tag": true
},
"packages/ui": {
"release-type": "node",
"package-name": "@org/ui",
"include-component-in-tag": true
},
"packages/cli": {
"release-type": "node",
"package-name": "@org/cli",
"include-component-in-tag": true
}
},
"separate-pull-requests": false,
"include-v-in-tag": true
}
include-component-in-tag: true→ 标签名称中会包含组件名,例如@org/core-v2.0.0(避免 monorepo 中的冲突)node-workspace插件 → 检测到@org/core的版本提升后,会重写@org/ui/@org/cli中的dependencies."@org/core",并在 PR 中将这两个包也进行补丁版本提升updatePeerDependencies: true→ 以相同方式重写对等依赖(库侧需要此设置)separate-pull-requests: false→ 将所有包的版本提升合并到一个 Release PR 中
在提交范围中包含包名:
feat(@org/core): add streaming parser
fix(@org/ui): button focus ring on safari
feat(@org/cli)!: rename --config to --profile
范围会自动确定变更的目标。条目只会写入相关包的 CHANGELOG。
workspace:* 替换机制:即使在 @org/ui/package.json 中写入 "@org/core": "workspace:*",pnpm 也会在发布时自动将其替换为 ^<actual version>。版本提升期间,release-please 会保留 workspace:* 源内容,不会对其进行修改。这一假设与 pnpm 端的行为完全吻合:workspace:^ → ^same-major,workspace:~ → ~same-minor。
git-cliff 设置(Rust / 通用)
cargo install git-cliff
# or
brew install git-cliff
# cliff.toml
[changelog]
header = "# Changelog\n"
body = """
{% if version %}## [{{ version }}] - {{ timestamp | date(format="%Y-%m-%d") }}{% endif %}
{% for group, commits in commits | group_by(attribute="group") %}
### {{ group | upper_first }}
{% for commit in commits %}
- {{ commit.message | upper_first }} ({{ commit.id | truncate(length=7, end="") }})
{% endfor %}
{% endfor %}
"""
[git]
conventional_commits = true
filter_unconventional = true # Exclude existing non-conforming commits from the CHANGELOG
protect_breaking_commits = true
commit_parsers = [
{ message = "^feat", group = "Added" },
{ message = "^fix", group = "Fixed" },
{ message = "^perf", group = "Changed" },
{ message = "^refactor", group = "Changed" },
{ message = "^deprecat", group = "Deprecated" },
{ message = "^remove", group = "Removed" },
{ message = "^security", group = "Security" },
{ message = "^revert", group = "Reverted" },
{ message = "^(docs|style|test|build|ci|chore)", skip = true },
{ message = ".*", skip = true }, # Bulk-exclude non-conforming commits (do not rewrite history)
]
tag_pattern = "v[0-9]*"
sort_commits = "newest"
对于与 cargo-release 配合使用的 Rust 项目,cargo-release 负责在 Cargo.toml 中升级版本并创建标签,而 git-cliff 只专注于生成 CHANGELOG(两者松散耦合)。这也是不应再叠加基于 PR 的自动版本升级工具(如 release-please)的原因。
git-cliff --output CHANGELOG.md # Regenerate from full history
git-cliff --unreleased --prepend CHANGELOG.md # Prepend only the unreleased section
git-cliff --tag v1.2.0 --output CHANGELOG.md # Generate for a specific tag
与 cargo-release 配合(具体示例)
将钩子放入 Cargo.toml 的 [package.metadata.release] 中。运行 cargo release <level> 会依次执行版本升级 → 生成 CHANGELOG → 提交 → 创建标签 → 推送:
# Cargo.toml
[package.metadata.release]
# After the version bump and before the commit, update CHANGELOG with git-cliff
pre-release-hook = ["git", "cliff", "--tag", "v{{version}}", "--unreleased", "--prepend", "CHANGELOG.md"]
# The tag itself is created by cargo-release
tag-name = "v{{version}}"
# Include CHANGELOG.md in the release commit
pre-release-commit-message = "chore(release): v{{version}}"
顺序:
cargo release minor --execute→ 在Cargo.toml中升级版本pre-release-hook运行git cliff,将下一个版本的[Unreleased]定稿为[v0.2.0] - 2026-04-19,并更新CHANGELOG.md- cargo-release 将
Cargo.toml和CHANGELOG.md一并纳入发布提交 tag-name创建v0.2.0注释标签cargo publish+git push --tags
注意:git-cliff 侧的 [changelog.bump] 部分(features_always_bump_minor 等)是 git-cliff 特有的功能,由 git-cliff 自行决定版本升级;与 cargo-release 配合使用时不要启用此功能(版本升级决策应由 cargo-release 负责)。<level>(patch / minor / major)由人工选择。
conventional-changelog-cli(轻量、与语言无关)
npm install -g conventional-changelog-cli
conventional-changelog -p angular -i CHANGELOG.md -s -r 0
# -p: preset (angular / atom / ember / eslint / jquery / jshint)
# -i: input, -s: same file, -r 0: full history
主要预设是 angular(Conventional Commits 的起源)。非常适合在现有 CHANGELOG 后追加内容。
使用 pre-commit 验证提交消息
在提交时拒绝不符合 Conventional Commits 规范的提交:
commitlint
npm install -D @commitlint/cli @commitlint/config-conventional
// commitlint.config.js
module.exports = { extends: ['@commitlint/config-conventional'] };
# .pre-commit-config.yaml (pre-commit framework)
- repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook
rev: v9.16.0
hooks:
- id: commitlint
stages: [commit-msg]
additional_dependencies: ['@commitlint/config-conventional']
注意事项:如果没有 additional_dependencies,钩子在运行时无法解析 config-conventional,并会报错 Cannot find module。仅在 commitlint.config.js 中使用 extends 并不足够;还必须在钩子的隔离环境中显式声明该依赖。
在 mizchi-default pkfire 生态系统中,commitlint 改为配置在 Taskfile.pkl 任务中——参见 chezmoi-management 和 pkfire 技能。
commitizen(交互式创建提交)
npm install -D commitizen cz-conventional-changelog
# package.json
"config": { "commitizen": { "path": "./node_modules/cz-conventional-changelog" } }
# usage
git cz
常见问题
- 中途才引入提交规范:现有历史记录仍会保留错误格式。release-please 扫描时会忽略“不符合规范的提交”。无需追溯重写,只需从现在开始统一规范。
- 所有内容都使用
chore:,导致没有发布 PR:为错误修复编写chore:会使其不会出现在 CHANGELOG 中,也不会触发版本升级。应根据“变更类型”而非重要性选择类型(fix 是用户可见的修复,chore 是内部维护工作)。 - 在主题行中写 BREAKING CHANGE,却省略页脚:release-please 会检查页脚中的
BREAKING CHANGE:或类型后面的!。在主题行中写“breaking: ...”不会触发 major 版本升级。 - 手动编辑 CHANGELOG,并同时使用工具管理:手动编辑由 release-please 管理的 CHANGELOG 会导致下一次生成时出现偏差。只能通过 PR 添加条目(使用 release-please 的“Edit”功能)。
- 先创建标签,再创建 release-please PR:release-please 会在 PR 合并时创建标签。手动创建的标签会与其冲突。若采用手动操作,应统一使用 conventional-changelog-cli / git-cliff + 手动创建标签。
常见误区
| 常见想法 | 实际情况 |
|---|---|
| “手动编写 CHANGELOG 会更精致” | 根据提交记录生成不会遗漏任何内容。手动编写难免会遗漏,措辞也不一致 |
| “先创建标签,再编写变更日志” | 顺序反了。应遵循:提交 → 工具生成 CHANGELOG → 标签 |
| “只要在主题中写明,破坏性变更就很清楚” | 工具会机械地读取页脚 / !。主题中的自然语言表述会被忽略 |
| “我们来添加项目专属类型吧” | 可以用 feat / fix / chore 表达。工具的预设不支持自定义类型 |
| “通过变基来修正不符合规范的提交” | 追溯性重写会污染历史记录。从现在开始保持规范即可;工具会忽略其余部分 |
相关内容
npm-release— 使用 release-please + OIDC Trusted Publishing 自动完成发布流程(特定会话的本地管理)apm-usagereferences/publishing.md — 发布技能时的 CHANGELOG 约定gh-fix-ci— 发布工作流在 CI 中失败时使用- Keep a Changelog:https://keepachangelog.com/
- Conventional Commits:https://www.conventionalcommits.org/
- release-please:https://github.com/googleapis/release-please
- git-cliff:https://git-cliff.org/