mcpField
C
conventional-changelog

conventional-changelog

github.com/mizchi开发41668 次安装1 周前更新
代码仓库

关于 conventional-changelog

conventional-changelog 是介绍 Conventional Commits 并比较变更日志生成与版本发布工具的技能。它帮助开发团队根据语言、仓库结构和发布流程选择适合的发布工作流。

主要功能

  1. 01Conventional Commits 指南
  2. 02变更日志工具比较
  3. 03发布工作流配置示例
  4. 04Semver 与标签实践
  5. 05Monorepo 发布指南

适用场景

  1. 01配置仓库发布流程
  2. 02自动生成变更日志
  3. 03为语言或 Monorepo 选择工具
  4. 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 影响
typesemver 升级级别是否包含在 CHANGELOG 中
featminor✓(新增 / 功能)
fixpatch✓(修复)
perfpatch✓(变更)
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-pleasenpm / 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 生态中广受欢迎标签 / 版本升级需单独处理
towncrierPython为每个 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"
    }
  }
}

流程:

  1. 使用 prerelease-type: "beta" 累积提交 → 发布 v2.0.0-beta.1
  2. 继续累积提交,版本依次变为 v2.0.0-beta.2 / beta.3 ...
  3. 升级为 RC:将配置改为 "prerelease-type": "rc" → 发布 v2.0.0-rc.1
  4. 发布稳定版:设置 "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}}"

顺序:

  1. cargo release minor --execute → 在 Cargo.toml 中升级版本
  2. pre-release-hook 运行 git cliff,将下一个版本的 [Unreleased] 定稿为 [v0.2.0] - 2026-04-19,并更新 CHANGELOG.md
  3. cargo-release 将 Cargo.toml 和 CHANGELOG.md 一并纳入发布提交
  4. tag-name 创建 v0.2.0 注释标签
  5. 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 表达。工具的预设不支持自定义类型
“通过变基来修正不符合规范的提交”追溯性重写会污染历史记录。从现在开始保持规范即可;工具会忽略其余部分

相关内容

评论

支持 Markdown