changelog-automation

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Changelog Automation

变更日志自动化

Patterns and tools for automating changelog generation, release notes, and version management following industry standards.
遵循行业标准的变更日志生成、发布说明和版本管理的模式与工具。

When to Use This Skill

适用场景

  • Setting up automated changelog generation
  • Implementing Conventional Commits
  • Creating release note workflows
  • Standardizing commit message formats
  • Generating GitHub/GitLab release notes
  • Managing semantic versioning
  • 设置自动化变更日志生成
  • 实施Conventional Commits规范
  • 创建发布说明工作流
  • 标准化提交消息格式
  • 生成GitHub/GitLab发布说明
  • 管理语义化版本控制

Core Concepts

核心概念

1. Keep a Changelog Format

1. Keep a Changelog格式

markdown
undefined
markdown
undefined

Changelog

Changelog

All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[Unreleased]

[Unreleased]

Added

Added

  • New feature X
  • New feature X

[1.2.0] - 2024-01-15

[1.2.0] - 2024-01-15

Added

Added

  • User profile avatars
  • Dark mode support
  • User profile avatars
  • Dark mode support

Changed

Changed

  • Improved loading performance by 40%
  • Improved loading performance by 40%

Deprecated

Deprecated

  • Old authentication API (use v2)
  • Old authentication API (use v2)

Removed

Removed

  • Legacy payment gateway
  • Legacy payment gateway

Fixed

Fixed

  • Login timeout issue (#123)
  • Login timeout issue (#123)

Security

Security

  • Updated dependencies for CVE-2024-1234
undefined
  • Updated dependencies for CVE-2024-1234
undefined

2. Conventional Commits

2. Conventional Commits规范

<type>[optional scope]: <description>

[optional body]

[optional footer(s)]
TypeDescriptionChangelog Section
feat
New featureAdded
fix
Bug fixFixed
docs
Documentation(usually excluded)
style
Formatting(usually excluded)
refactor
Code restructureChanged
perf
PerformanceChanged
test
Tests(usually excluded)
chore
Maintenance(usually excluded)
ci
CI changes(usually excluded)
build
Build system(usually excluded)
revert
Revert commitRemoved
<type>[optional scope]: <description>

[optional body]

[optional footer(s)]
类型描述变更日志章节
feat
新功能Added
fix
修复BugFixed
docs
文档更新(通常不包含)
style
代码格式调整(通常不包含)
refactor
代码重构Changed
perf
性能优化Changed
test
测试相关(通常不包含)
chore
维护任务(通常不包含)
ci
CI流程变更(通常不包含)
build
构建系统变更(通常不包含)
revert
回滚提交Removed

3. Semantic Versioning

3. 语义化版本控制(Semantic Versioning)

MAJOR.MINOR.PATCH

MAJOR: Breaking changes (feat! or BREAKING CHANGE)
MINOR: New features (feat)
PATCH: Bug fixes (fix)
MAJOR.MINOR.PATCH

MAJOR: Breaking changes (feat! or BREAKING CHANGE)
MINOR: New features (feat)
PATCH: Bug fixes (fix)

Implementation

实现方法

Method 1: Conventional Changelog (Node.js)

方法1:Conventional Changelog(Node.js)

bash
undefined
bash
undefined

Install tools

Install tools

npm install -D @commitlint/cli @commitlint/config-conventional npm install -D husky npm install -D standard-version
npm install -D @commitlint/cli @commitlint/config-conventional npm install -D husky npm install -D standard-version

or

or

npm install -D semantic-release
npm install -D semantic-release

Setup commitlint

Setup commitlint

cat > commitlint.config.js << 'EOF' module.exports = { extends: ['@commitlint/config-conventional'], rules: { 'type-enum': [ 2, 'always', [ 'feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'chore', 'ci', 'build', 'revert', ], ], 'subject-case': [2, 'never', ['start-case', 'pascal-case', 'upper-case']], 'subject-max-length': [2, 'always', 72], }, }; EOF
cat > commitlint.config.js << 'EOF' module.exports = { extends: ['@commitlint/config-conventional'], rules: { 'type-enum': [ 2, 'always', [ 'feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'chore', 'ci', 'build', 'revert', ], ], 'subject-case': [2, 'never', ['start-case', 'pascal-case', 'upper-case']], 'subject-max-length': [2, 'always', 72], }, }; EOF

Setup husky

Setup husky

npx husky init echo "npx --no -- commitlint --edit $1" > .husky/commit-msg
undefined
npx husky init echo "npx --no -- commitlint --edit $1" > .husky/commit-msg
undefined

Method 2: standard-version Configuration

方法2:standard-version配置

javascript
// .versionrc.js
module.exports = {
  types: [
    { type: "feat", section: "Features" },
    { type: "fix", section: "Bug Fixes" },
    { type: "perf", section: "Performance Improvements" },
    { type: "revert", section: "Reverts" },
    { type: "docs", section: "Documentation", hidden: true },
    { type: "style", section: "Styles", hidden: true },
    { type: "chore", section: "Miscellaneous", hidden: true },
    { type: "refactor", section: "Code Refactoring", hidden: true },
    { type: "test", section: "Tests", hidden: true },
    { type: "build", section: "Build System", hidden: true },
    { type: "ci", section: "CI/CD", hidden: true },
  ],
  commitUrlFormat: "{{host}}/{{owner}}/{{repository}}/commit/{{hash}}",
  compareUrlFormat:
    "{{host}}/{{owner}}/{{repository}}/compare/{{previousTag}}...{{currentTag}}",
  issueUrlFormat: "{{host}}/{{owner}}/{{repository}}/issues/{{id}}",
  userUrlFormat: "{{host}}/{{user}}",
  releaseCommitMessageFormat: "chore(release): {{currentTag}}",
  scripts: {
    prebump: 'echo "Running prebump"',
    postbump: 'echo "Running postbump"',
    prechangelog: 'echo "Running prechangelog"',
    postchangelog: 'echo "Running postchangelog"',
  },
};
json
// package.json scripts
{
  "scripts": {
    "release": "standard-version",
    "release:minor": "standard-version --release-as minor",
    "release:major": "standard-version --release-as major",
    "release:patch": "standard-version --release-as patch",
    "release:dry": "standard-version --dry-run"
  }
}
javascript
// .versionrc.js
module.exports = {
  types: [
    { type: "feat", section: "Features" },
    { type: "fix", section: "Bug Fixes" },
    { type: "perf", section: "Performance Improvements" },
    { type: "revert", section: "Reverts" },
    { type: "docs", section: "Documentation", hidden: true },
    { type: "style", section: "Styles", hidden: true },
    { type: "chore", section: "Miscellaneous", hidden: true },
    { type: "refactor", section: "Code Refactoring", hidden: true },
    { type: "test", section: "Tests", hidden: true },
    { type: "build", section: "Build System", hidden: true },
    { type: "ci", section: "CI/CD", hidden: true },
  ],
  commitUrlFormat: "{{host}}/{{owner}}/{{repository}}/commit/{{hash}}",
  compareUrlFormat:
    "{{host}}/{{owner}}/{{repository}}/compare/{{previousTag}}...{{currentTag}}",
  issueUrlFormat: "{{host}}/{{owner}}/{{repository}}/issues/{{id}}",
  userUrlFormat: "{{host}}/{{user}}",
  releaseCommitMessageFormat: "chore(release): {{currentTag}}",
  scripts: {
    prebump: 'echo "Running prebump"',
    postbump: 'echo "Running postbump"',
    prechangelog: 'echo "Running prechangelog"',
    postchangelog: 'echo "Running postchangelog"',
  },
};
json
// package.json scripts
{
  "scripts": {
    "release": "standard-version",
    "release:minor": "standard-version --release-as minor",
    "release:major": "standard-version --release-as major",
    "release:patch": "standard-version --release-as patch",
    "release:dry": "standard-version --dry-run"
  }
}

Method 3: semantic-release (Full Automation)

方法3:semantic-release(全自动化)

javascript
// release.config.js
module.exports = {
  branches: [
    "main",
    { name: "beta", prerelease: true },
    { name: "alpha", prerelease: true },
  ],
  plugins: [
    "@semantic-release/commit-analyzer",
    "@semantic-release/release-notes-generator",
    [
      "@semantic-release/changelog",
      {
        changelogFile: "CHANGELOG.md",
      },
    ],
    [
      "@semantic-release/npm",
      {
        npmPublish: true,
      },
    ],
    [
      "@semantic-release/github",
      {
        assets: ["dist/**/*.js", "dist/**/*.css"],
      },
    ],
    [
      "@semantic-release/git",
      {
        assets: ["CHANGELOG.md", "package.json"],
        message:
          "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}",
      },
    ],
  ],
};
javascript
// release.config.js
module.exports = {
  branches: [
    "main",
    { name: "beta", prerelease: true },
    { name: "alpha", prerelease: true },
  ],
  plugins: [
    "@semantic-release/commit-analyzer",
    "@semantic-release/release-notes-generator",
    [
      "@semantic-release/changelog",
      {
        changelogFile: "CHANGELOG.md",
      },
    ],
    [
      "@semantic-release/npm",
      {
        npmPublish: true,
      },
    ],
    [
      "@semantic-release/github",
      {
        assets: ["dist/**/*.js", "dist/**/*.css"],
      },
    ],
    [
      "@semantic-release/git",
      {
        assets: ["CHANGELOG.md", "package.json"],
        message:
          "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}",
      },
    ],
  ],
};

Method 4: GitHub Actions Workflow

方法4:GitHub Actions工作流

yaml
undefined
yaml
undefined

.github/workflows/release.yml

.github/workflows/release.yml

name: Release
on: push: branches: [main] workflow_dispatch: inputs: release_type: description: "Release type" required: true default: "patch" type: choice options: - patch - minor - major
permissions: contents: write pull-requests: write
jobs: release: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 token: ${{ secrets.GITHUB_TOKEN }}
  - uses: actions/setup-node@v4
    with:
      node-version: "20"
      cache: "npm"

  - run: npm ci

  - name: Configure Git
    run: |
      git config user.name "github-actions[bot]"
      git config user.email "github-actions[bot]@users.noreply.github.com"

  - name: Run semantic-release
    env:
      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
    run: npx semantic-release

Alternative: manual release with standard-version

manual-release: if: github.event_name == 'workflow_dispatch' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0
  - uses: actions/setup-node@v4
    with:
      node-version: "20"

  - run: npm ci

  - name: Configure Git
    run: |
      git config user.name "github-actions[bot]"
      git config user.email "github-actions[bot]@users.noreply.github.com"

  - name: Bump version and generate changelog
    run: npx standard-version --release-as ${{ inputs.release_type }}

  - name: Push changes
    run: git push --follow-tags origin main

  - name: Create GitHub Release
    uses: softprops/action-gh-release@v1
    with:
      tag_name: ${{ steps.version.outputs.tag }}
      body_path: RELEASE_NOTES.md
      generate_release_notes: true
undefined
name: Release
on: push: branches: [main] workflow_dispatch: inputs: release_type: description: "Release type" required: true default: "patch" type: choice options: - patch - minor - major
permissions: contents: write pull-requests: write
jobs: release: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 token: ${{ secrets.GITHUB_TOKEN }}
  - uses: actions/setup-node@v4
    with:
      node-version: "20"
      cache: "npm"

  - run: npm ci

  - name: Configure Git
    run: |
      git config user.name "github-actions[bot]"
      git config user.email "github-actions[bot]@users.noreply.github.com"

  - name: Run semantic-release
    env:
      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
    run: npx semantic-release

Alternative: manual release with standard-version

manual-release: if: github.event_name == 'workflow_dispatch' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0
  - uses: actions/setup-node@v4
    with:
      node-version: "20"

  - run: npm ci

  - name: Configure Git
    run: |
      git config user.name "github-actions[bot]"
      git config user.email "github-actions[bot]@users.noreply.github.com"

  - name: Bump version and generate changelog
    run: npx standard-version --release-as ${{ inputs.release_type }}

  - name: Push changes
    run: git push --follow-tags origin main

  - name: Create GitHub Release
    uses: softprops/action-gh-release@v1
    with:
      tag_name: ${{ steps.version.outputs.tag }}
      body_path: RELEASE_NOTES.md
      generate_release_notes: true
undefined

Method 5: git-cliff (Rust-based, Fast)

方法5:git-cliff(基于Rust,速度快)

toml
undefined
toml
undefined

cliff.toml

cliff.toml

[changelog] header = """
[changelog] header = """

Changelog

Changelog

All notable changes to this project will be documented in this file.
""" body = """ {% if version %}
## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }} {% else %}
## [Unreleased] {% endif %}
{% for group, commits in commits | group_by(attribute="group") %} ### {{ group | upper_first }} {% for commit in commits %} - {% if commit.scope %}{{ commit.scope }}: {% endif %}
{{ commit.message | upper_first }}
{% if commit.github.pr_number %} ([#{{ commit.github.pr_number }}](https://github.com/owner/repo/pull/{{ commit.github.pr_number }})){% endif %}
{% endfor %} {% endfor %} """ footer = """ {% for release in releases -%} {% if release.version -%} {% if release.previous.version -%} [{{ release.version | trim_start_matches(pat="v") }}]:
https://github.com/owner/repo/compare/{{ release.previous.version }}...{{ release.version }} {% endif -%} {% else -%} [unreleased]: https://github.com/owner/repo/compare/{{ release.previous.version }}...HEAD {% endif -%} {% endfor %} """ trim = true
[git] conventional_commits = true filter_unconventional = true split_commits = false commit_parsers = [ { message = "^feat", group = "Features" }, { message = "^fix", group = "Bug Fixes" }, { message = "^doc", group = "Documentation" }, { message = "^perf", group = "Performance" }, { message = "^refactor", group = "Refactoring" }, { message = "^style", group = "Styling" }, { message = "^test", group = "Testing" }, { message = "^chore\(release\)", skip = true }, { message = "^chore", group = "Miscellaneous" }, ] filter_commits = false tag_pattern = "v[0-9]*" skip_tags = "" ignore_tags = "" topo_order = false sort_commits = "oldest"
[github] owner = "owner" repo = "repo"

```bash
All notable changes to this project will be documented in this file.
""" body = """ {% if version %}
## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }} {% else %}
## [Unreleased] {% endif %}
{% for group, commits in commits | group_by(attribute="group") %} ### {{ group | upper_first }} {% for commit in commits %} - {% if commit.scope %}{{ commit.scope }}: {% endif %}
{{ commit.message | upper_first }}
{% if commit.github.pr_number %} ([#{{ commit.github.pr_number }}](https://github.com/owner/repo/pull/{{ commit.github.pr_number }})){% endif %}
{% endfor %} {% endfor %} """ footer = """ {% for release in releases -%} {% if release.version -%} {% if release.previous.version -%} [{{ release.version | trim_start_matches(pat="v") }}]:
https://github.com/owner/repo/compare/{{ release.previous.version }}...{{ release.version }} {% endif -%} {% else -%} [unreleased]: https://github.com/owner/repo/compare/{{ release.previous.version }}...HEAD {% endif -%} {% endfor %} """ trim = true
[git] conventional_commits = true filter_unconventional = true split_commits = false commit_parsers = [ { message = "^feat", group = "Features" }, { message = "^fix", group = "Bug Fixes" }, { message = "^doc", group = "Documentation" }, { message = "^perf", group = "Performance" }, { message = "^refactor", group = "Refactoring" }, { message = "^style", group = "Styling" }, { message = "^test", group = "Testing" }, { message = "^chore\(release\)", skip = true }, { message = "^chore", group = "Miscellaneous" }, ] filter_commits = false tag_pattern = "v[0-9]*" skip_tags = "" ignore_tags = "" topo_order = false sort_commits = "oldest"
[github] owner = "owner" repo = "repo"

```bash

Generate changelog

Generate changelog

git cliff -o CHANGELOG.md
git cliff -o CHANGELOG.md

Generate for specific range

Generate for specific range

git cliff v1.0.0..v2.0.0 -o RELEASE_NOTES.md
git cliff v1.0.0..v2.0.0 -o RELEASE_NOTES.md

Preview without writing

Preview without writing

git cliff --unreleased --dry-run
undefined
git cliff --unreleased --dry-run
undefined

Method 6: Python (commitizen)

方法6:Python(commitizen)

toml
undefined
toml
undefined

pyproject.toml

pyproject.toml

[tool.commitizen] name = "cz_conventional_commits" version = "1.0.0" version_files = [ "pyproject.toml:version", "src/init.py:version", ] tag_format = "v$version" update_changelog_on_bump = true changelog_incremental = true changelog_start_rev = "v0.1.0"
[tool.commitizen.customize] message_template = "{{change_type}}{% if scope %}({{scope}}){% endif %}: {{message}}" schema = "<type>(<scope>): <subject>" schema_pattern = "^(feat|fix|docs|style|refactor|perf|test|chore)(\(\w+\))?:\s.*" bump_pattern = "^(feat|fix|perf|refactor)" bump_map = {"feat" = "MINOR", "fix" = "PATCH", "perf" = "PATCH", "refactor" = "PATCH"}

```bash
[tool.commitizen] name = "cz_conventional_commits" version = "1.0.0" version_files = [ "pyproject.toml:version", "src/init.py:version", ] tag_format = "v$version" update_changelog_on_bump = true changelog_incremental = true changelog_start_rev = "v0.1.0"
[tool.commitizen.customize] message_template = "{{change_type}}{% if scope %}({{scope}}){% endif %}: {{message}}" schema = "<type>(<scope>): <subject>" schema_pattern = "^(feat|fix|docs|style|refactor|perf|test|chore)(\(\w+\))?:\s.*" bump_pattern = "^(feat|fix|perf|refactor)" bump_map = {"feat" = "MINOR", "fix" = "PATCH", "perf" = "PATCH", "refactor" = "PATCH"}

```bash

Install

Install

pip install commitizen
pip install commitizen

Create commit interactively

Create commit interactively

cz commit
cz commit

Bump version and update changelog

Bump version and update changelog

cz bump --changelog
cz bump --changelog

Check commits

Check commits

cz check --rev-range HEAD~5..HEAD
undefined
cz check --rev-range HEAD~5..HEAD
undefined

Release Notes Templates

发布说明模板

GitHub Release Template

GitHub发布模板

markdown
undefined
markdown
undefined

What's Changed

What's Changed

🚀 Features

🚀 Features

{{ range .Features }}
  • {{ .Title }} by @{{ .Author }} in #{{ .PR }} {{ end }}
{{ range .Features }}
  • {{ .Title }} by @{{ .Author }} in #{{ .PR }} {{ end }}

🐛 Bug Fixes

🐛 Bug Fixes

{{ range .Fixes }}
  • {{ .Title }} by @{{ .Author }} in #{{ .PR }} {{ end }}
{{ range .Fixes }}
  • {{ .Title }} by @{{ .Author }} in #{{ .PR }} {{ end }}

📚 Documentation

📚 Documentation

{{ range .Docs }}
  • {{ .Title }} by @{{ .Author }} in #{{ .PR }} {{ end }}
{{ range .Docs }}
  • {{ .Title }} by @{{ .Author }} in #{{ .PR }} {{ end }}

🔧 Maintenance

🔧 Maintenance

{{ range .Chores }}
  • {{ .Title }} by @{{ .Author }} in #{{ .PR }} {{ end }}
{{ range .Chores }}
  • {{ .Title }} by @{{ .Author }} in #{{ .PR }} {{ end }}

New Contributors

New Contributors

{{ range .NewContributors }}
  • @{{ .Username }} made their first contribution in #{{ .PR }} {{ end }}
Full Changelog: https://github.com/owner/repo/compare/v{{ .Previous }}...v{{ .Current }}
undefined
{{ range .NewContributors }}
  • @{{ .Username }} made their first contribution in #{{ .PR }} {{ end }}
Full Changelog: https://github.com/owner/repo/compare/v{{ .Previous }}...v{{ .Current }}
undefined

Internal Release Notes

内部发布说明

markdown
undefined
markdown
undefined

Release v2.1.0 - January 15, 2024

Release v2.1.0 - January 15, 2024

Summary

Summary

This release introduces dark mode support and improves checkout performance by 40%. It also includes important security updates.
This release introduces dark mode support and improves checkout performance by 40%. It also includes important security updates.

Highlights

Highlights

🌙 Dark Mode

🌙 Dark Mode

Users can now switch to dark mode from settings. The preference is automatically saved and synced across devices.
Users can now switch to dark mode from settings. The preference is automatically saved and synced across devices.

⚡ Performance

⚡ Performance

  • Checkout flow is 40% faster
  • Reduced bundle size by 15%
  • Checkout flow is 40% faster
  • Reduced bundle size by 15%

Breaking Changes

Breaking Changes

None in this release.
None in this release.

Upgrade Guide

Upgrade Guide

No special steps required. Standard deployment process applies.
No special steps required. Standard deployment process applies.

Known Issues

Known Issues

  • Dark mode may flicker on initial load (fix scheduled for v2.1.1)
  • Dark mode may flicker on initial load (fix scheduled for v2.1.1)

Dependencies Updated

Dependencies Updated

PackageFromToReason
react18.2.018.3.0Performance improvements
lodash4.17.204.17.21Security patch
undefined
PackageFromToReason
react18.2.018.3.0Performance improvements
lodash4.17.204.17.21Security patch
undefined

Commit Message Examples

提交消息示例

bash
undefined
bash
undefined

Feature with scope

Feature with scope

feat(auth): add OAuth2 support for Google login
feat(auth): add OAuth2 support for Google login

Bug fix with issue reference

Bug fix with issue reference

fix(checkout): resolve race condition in payment processing
Closes #123
fix(checkout): resolve race condition in payment processing
Closes #123

Breaking change

Breaking change

feat(api)!: change user endpoint response format
BREAKING CHANGE: The user endpoint now returns
userId
instead of
id
. Migration guide: Update all API consumers to use the new field name.
feat(api)!: change user endpoint response format
BREAKING CHANGE: The user endpoint now returns
userId
instead of
id
. Migration guide: Update all API consumers to use the new field name.

Multiple paragraphs

Multiple paragraphs

fix(database): handle connection timeouts gracefully
Previously, connection timeouts would cause the entire request to fail without retry. This change implements exponential backoff with up to 3 retries before failing.
The timeout threshold has been increased from 5s to 10s based on p99 latency analysis.
Fixes #456 Reviewed-by: @alice
undefined
fix(database): handle connection timeouts gracefully
Previously, connection timeouts would cause the entire request to fail without retry. This change implements exponential backoff with up to 3 retries before failing.
The timeout threshold has been increased from 5s to 10s based on p99 latency analysis.
Fixes #456 Reviewed-by: @alice
undefined

Best Practices

最佳实践

Do's

建议做法

  • Follow Conventional Commits - Enables automation
  • Write clear messages - Future you will thank you
  • Reference issues - Link commits to tickets
  • Use scopes consistently - Define team conventions
  • Automate releases - Reduce manual errors
  • 遵循Conventional Commits规范 - 支持自动化流程
  • 编写清晰的提交消息 - 方便未来回溯
  • 关联问题工单 - 将提交与任务关联
  • 一致使用作用域 - 定义团队规范
  • 自动化发布流程 - 减少人为错误

Don'ts

避免做法

  • Don't mix changes - One logical change per commit
  • Don't skip validation - Use commitlint
  • Don't manual edit - Generated changelogs only
  • Don't forget breaking changes - Mark with
    !
    or footer
  • Don't ignore CI - Validate commits in pipeline
  • 不要混合变更 - 每次提交只包含一个逻辑变更
  • 不要跳过验证 - 使用commitlint进行校验
  • 不要手动编辑 - 仅使用自动生成的变更日志
  • 不要遗漏破坏性变更 - 使用
    !
    或脚注标记
  • 不要忽略CI校验 - 在流水线中验证提交

Resources

参考资源