AgentSkillsCN

changelog-automation

按照 Keep a Changelog 格式,自动从提交、拉取请求与发布中生成变更日志。适用于设置发布工作流、生成发布说明,或统一提交规范时使用。

SKILL.md
--- frontmatter
name: changelog-automation
description: Automate changelog generation from commits, PRs, and releases following Keep a Changelog format. Use when setting up release workflows, generating release notes, or standardizing commit conventions.

Changelog Automation

Conventional Commits

code
<type>[optional scope]: <description>

[optional body]

[optional footer(s)]
TypeChangelog SectionSemver Bump
featAddedMINOR
fixFixedPATCH
perfChangedPATCH
refactorChangedPATCH
revertRemovedPATCH
feat! / BREAKING CHANGEBreakingMAJOR
docs, style, test, chore, ci, build(excluded)none

Method 1: Commitlint + Husky

bash
npm install -D @commitlint/cli @commitlint/config-conventional husky

# commitlint.config.js
module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'subject-max-length': [2, 'always', 72],
  },
};

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

Method 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', hidden: true }, { type: 'style', hidden: true },
    { type: 'chore', hidden: true }, { type: 'refactor', hidden: true },
    { type: 'test', hidden: true }, { type: 'build', hidden: true },
    { type: 'ci', hidden: true },
  ],
  releaseCommitMessageFormat: 'chore(release): {{currentTag}}',
};
json
{ "scripts": {
    "release": "standard-version",
    "release:minor": "standard-version --release-as minor",
    "release:major": "standard-version --release-as major",
    "release:dry": "standard-version --dry-run"
} }

Method 3: semantic-release (Full Automation)

javascript
// release.config.js
module.exports = {
  branches: ['main', { name: 'beta', 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

yaml
name: Release
on:
  push: { branches: [main] }
  workflow_dispatch:
    inputs:
      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
      - run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
      - run: npx semantic-release
        env: { GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}', NPM_TOKEN: '${{ secrets.NPM_TOKEN }}' }

Method 5: git-cliff (Rust-based)

toml
# cliff.toml
[changelog]
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 }}
    {% endfor %}
{% endfor %}
"""
trim = true

[git]
conventional_commits = true
filter_unconventional = true
commit_parsers = [
    { message = "^feat", group = "Features" },
    { message = "^fix", group = "Bug Fixes" },
    { message = "^perf", group = "Performance" },
    { message = "^refactor", group = "Refactoring" },
    { message = "^chore\\(release\\)", skip = true },
    { message = "^chore", group = "Miscellaneous" },
]
tag_pattern = "v[0-9]*"
sort_commits = "oldest"
bash
git cliff -o CHANGELOG.md
git cliff v1.0.0..v2.0.0 -o RELEASE_NOTES.md
git cliff --unreleased --dry-run

Method 6: Python (commitizen)

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
bash
cz commit        # Interactive commit
cz bump --changelog  # Bump version + changelog
cz check --rev-range HEAD~5..HEAD  # Validate commits

Commit Message Examples

bash
feat(auth): add OAuth2 support for Google login
fix(checkout): resolve race condition in payment processing
Closes #123

feat(api)!: change user endpoint response format
BREAKING CHANGE: user endpoint returns `userId` instead of `id`.