semantic-versioning

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Semantic Versioning

语义化版本控制(Semantic Versioning)

Automate version management and changelog generation following SemVer principles.
遵循SemVer原则,实现版本管理与变更日志生成的自动化。

When to Use This Skill

适用场景

Use this skill when:
  • Implementing version numbering standards
  • Automating release versioning
  • Generating changelogs automatically
  • Setting up release pipelines
  • Managing package versions
适用于以下场景:
  • 实施版本编号标准
  • 自动化发布版本控制
  • 自动生成变更日志
  • 搭建发布流水线
  • 管理包版本

Prerequisites

前置条件

  • Git repository with commit history
  • Node.js (for most tools)
  • Conventional commits (recommended)
  • 带有提交历史的Git仓库
  • Node.js(适用于大多数工具)
  • 规范化提交(Conventional commits,推荐使用)

Semantic Versioning Basics

语义化版本控制基础

Version Format

版本格式

MAJOR.MINOR.PATCH[-PRERELEASE][+BUILD]

Examples:
1.0.0
2.1.3
1.0.0-alpha.1
1.0.0-beta.2+build.123
MAJOR.MINOR.PATCH[-PRERELEASE][+BUILD]

Examples:
1.0.0
2.1.3
1.0.0-alpha.1
1.0.0-beta.2+build.123

Version Components

版本组成部分

ComponentWhen to Increment
MAJORBreaking changes (incompatible API changes)
MINORNew features (backward compatible)
PATCHBug fixes (backward compatible)
PRERELEASEPre-release versions (alpha, beta, rc)
BUILDBuild metadata (ignored in precedence)
组成部分升级时机
MAJOR破坏性变更(不兼容的API变更)
MINOR新增功能(向后兼容)
PATCHBug修复(向后兼容)
PRERELEASE预发布版本(alpha、beta、rc)
BUILD构建元数据(版本优先级中忽略)

Version Precedence

版本优先级

1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta
< 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11
< 1.0.0-rc.1 < 1.0.0 < 2.0.0
1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-alpha.beta
< 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11
< 1.0.0-rc.1 < 1.0.0 < 2.0.0

Conventional Commits to Version

规范化提交与版本映射

yaml
Commit Type → Version Bump:
  feat:     → MINOR
  fix:      → PATCH
  docs:     → PATCH (or no release)
  style:    → PATCH (or no release)
  refactor: → PATCH
  perf:     → PATCH
  test:     → No release
  chore:    → No release
  
  BREAKING CHANGE: → MAJOR
  feat!:   → MAJOR
  fix!:    → MAJOR
yaml
Commit Type → Version Bump:
  feat:     → MINOR
  fix:      → PATCH
  docs:     → PATCH (or no release)
  style:    → PATCH (or no release)
  refactor: → PATCH
  perf:     → PATCH
  test:     → No release
  chore:    → No release
  
  BREAKING CHANGE: → MAJOR
  feat!:   → MAJOR
  fix!:    → MAJOR

semantic-release

semantic-release

Installation

安装

bash
npm install --save-dev semantic-release \
  @semantic-release/changelog \
  @semantic-release/git
bash
npm install --save-dev semantic-release \
  @semantic-release/changelog \
  @semantic-release/git

Configuration

配置

json
// .releaserc.json
{
  "branches": ["main"],
  "plugins": [
    "@semantic-release/commit-analyzer",
    "@semantic-release/release-notes-generator",
    ["@semantic-release/changelog", {
      "changelogFile": "CHANGELOG.md"
    }],
    ["@semantic-release/npm", {
      "npmPublish": true
    }],
    ["@semantic-release/git", {
      "assets": ["CHANGELOG.md", "package.json", "package-lock.json"],
      "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
    }],
    "@semantic-release/github"
  ]
}
json
// .releaserc.json
{
  "branches": ["main"],
  "plugins": [
    "@semantic-release/commit-analyzer",
    "@semantic-release/release-notes-generator",
    ["@semantic-release/changelog", {
      "changelogFile": "CHANGELOG.md"
    }],
    ["@semantic-release/npm", {
      "npmPublish": true
    }],
    ["@semantic-release/git", {
      "assets": ["CHANGELOG.md", "package.json", "package-lock.json"],
      "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
    }],
    "@semantic-release/github"
  ]
}

Advanced Configuration

高级配置

javascript
// release.config.js
module.exports = {
  branches: [
    'main',
    { name: 'beta', prerelease: true },
    { name: 'alpha', prerelease: true }
  ],
  plugins: [
    ['@semantic-release/commit-analyzer', {
      preset: 'angular',
      releaseRules: [
        { type: 'docs', release: 'patch' },
        { type: 'refactor', release: 'patch' },
        { type: 'style', release: 'patch' },
        { type: 'perf', release: 'patch' },
        { breaking: true, release: 'major' }
      ]
    }],
    ['@semantic-release/release-notes-generator', {
      preset: 'angular',
      writerOpts: {
        commitsSort: ['subject', 'scope']
      }
    }],
    '@semantic-release/changelog',
    '@semantic-release/npm',
    '@semantic-release/git',
    '@semantic-release/github'
  ]
};
javascript
// release.config.js
module.exports = {
  branches: [
    'main',
    { name: 'beta', prerelease: true },
    { name: 'alpha', prerelease: true }
  ],
  plugins: [
    ['@semantic-release/commit-analyzer', {
      preset: 'angular',
      releaseRules: [
        { type: 'docs', release: 'patch' },
        { type: 'refactor', release: 'patch' },
        { type: 'style', release: 'patch' },
        { type: 'perf', release: 'patch' },
        { breaking: true, release: 'major' }
      ]
    }],
    ['@semantic-release/release-notes-generator', {
      preset: 'angular',
      writerOpts: {
        commitsSort: ['subject', 'scope']
      }
    }],
    '@semantic-release/changelog',
    '@semantic-release/npm',
    '@semantic-release/git',
    '@semantic-release/github'
  ]
};

GitHub Actions Integration

GitHub Actions 集成

yaml
undefined
yaml
undefined

.github/workflows/release.yml

.github/workflows/release.yml

name: Release
on: push: branches: [main]
jobs: release: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 persist-credentials: false
  - uses: actions/setup-node@v4
    with:
      node-version: '20'

  - run: npm ci

  - name: Release
    env:
      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
    run: npx semantic-release
undefined
name: Release
on: push: branches: [main]
jobs: release: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 persist-credentials: false
  - uses: actions/setup-node@v4
    with:
      node-version: '20'

  - run: npm ci

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

standard-version

standard-version

Installation

安装

bash
npm install --save-dev standard-version
bash
npm install --save-dev standard-version

Configuration

配置

json
// .versionrc.json
{
  "types": [
    { "type": "feat", "section": "Features" },
    { "type": "fix", "section": "Bug Fixes" },
    { "type": "docs", "section": "Documentation" },
    { "type": "style", "section": "Styling" },
    { "type": "refactor", "section": "Code Refactoring" },
    { "type": "perf", "section": "Performance" },
    { "type": "test", "section": "Tests" },
    { "type": "chore", "section": "Maintenance" }
  ],
  "skip": {
    "bump": false,
    "changelog": false,
    "commit": false,
    "tag": false
  },
  "commitUrlFormat": "https://github.com/owner/repo/commit/{{hash}}",
  "compareUrlFormat": "https://github.com/owner/repo/compare/{{previousTag}}...{{currentTag}}"
}
json
// .versionrc.json
{
  "types": [
    { "type": "feat", "section": "Features" },
    { "type": "fix", "section": "Bug Fixes" },
    { "type": "docs", "section": "Documentation" },
    { "type": "style", "section": "Styling" },
    { "type": "refactor", "section": "Code Refactoring" },
    { "type": "perf", "section": "Performance" },
    { "type": "test", "section": "Tests" },
    { "type": "chore", "section": "Maintenance" }
  ],
  "skip": {
    "bump": false,
    "changelog": false,
    "commit": false,
    "tag": false
  },
  "commitUrlFormat": "https://github.com/owner/repo/commit/{{hash}}",
  "compareUrlFormat": "https://github.com/owner/repo/compare/{{previousTag}}...{{currentTag}}"
}

Usage

使用方法

bash
undefined
bash
undefined

First release

首次发布

npx standard-version --first-release
npx standard-version --first-release

Regular release (auto-detect version bump)

常规发布(自动检测版本升级)

npx standard-version
npx standard-version

Specific version bump

指定版本升级类型

npx standard-version --release-as minor npx standard-version --release-as 1.1.0
npx standard-version --release-as minor npx standard-version --release-as 1.1.0

Pre-release

预发布版本

npx standard-version --prerelease alpha npx standard-version --prerelease beta
npx standard-version --prerelease alpha npx standard-version --prerelease beta

Dry run

试运行

npx standard-version --dry-run
npx standard-version --dry-run

Skip specific steps

跳过指定步骤

npx standard-version --skip.changelog
undefined
npx standard-version --skip.changelog
undefined

NPM Scripts

NPM 脚本

json
// package.json
{
  "scripts": {
    "release": "standard-version",
    "release:minor": "standard-version --release-as minor",
    "release:major": "standard-version --release-as major",
    "release:alpha": "standard-version --prerelease alpha",
    "release:beta": "standard-version --prerelease beta",
    "release:dry": "standard-version --dry-run"
  }
}
json
// package.json
{
  "scripts": {
    "release": "standard-version",
    "release:minor": "standard-version --release-as minor",
    "release:major": "standard-version --release-as major",
    "release:alpha": "standard-version --prerelease alpha",
    "release:beta": "standard-version --prerelease beta",
    "release:dry": "standard-version --dry-run"
  }
}

Changelog Generation

变更日志生成

conventional-changelog

conventional-changelog

bash
undefined
bash
undefined

Install

安装

npm install -g conventional-changelog-cli
npm install -g conventional-changelog-cli

Generate changelog

生成变更日志

conventional-changelog -p angular -i CHANGELOG.md -s
conventional-changelog -p angular -i CHANGELOG.md -s

Generate all history

生成全部历史日志

conventional-changelog -p angular -i CHANGELOG.md -s -r 0
undefined
conventional-changelog -p angular -i CHANGELOG.md -s -r 0
undefined

git-cliff

git-cliff

bash
undefined
bash
undefined

Install

安装

cargo install git-cliff
cargo install git-cliff

Generate changelog

生成变更日志

git cliff -o CHANGELOG.md

```toml
git cliff -o CHANGELOG.md

```toml

cliff.toml

cliff.toml

[changelog] header = "# Changelog\n\n" body = """ {% for group, commits in commits | group_by(attribute="group") %}
[changelog] header = "# Changelog\n\n" body = """ {% for group, commits in commits | group_by(attribute="group") %}

{{ group | upper_first }}

{{ group | upper_first }}

{% for commit in commits %}
  • {{ commit.message | upper_first }}
    {% endfor %} {% endfor %} """ trim = true
[git] conventional_commits = true filter_unconventional = true commit_preprocessors = [ { pattern = '((\w+)\s#([0-9]+))', replace = "(#${2})" }, ] 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", group = "Miscellaneous" }, ] filter_commits = true tag_pattern = "v[0-9]*"
undefined
{% for commit in commits %}
  • {{ commit.message | upper_first }}
    {% endfor %} {% endfor %} """ trim = true
[git] conventional_commits = true filter_unconventional = true commit_preprocessors = [ { pattern = '((\w+)\s#([0-9]+))', replace = "(#${2})" }, ] 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", group = "Miscellaneous" }, ] filter_commits = true tag_pattern = "v[0-9]*"
undefined

Version Bumping Scripts

版本号升级脚本

Bash Script

Bash 脚本

bash
#!/bin/bash
bash
#!/bin/bash

bump-version.sh

bump-version.sh

CURRENT_VERSION=$(cat package.json | jq -r '.version') echo "Current version: $CURRENT_VERSION"
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_VERSION"
case $1 in major) NEW_VERSION="$((MAJOR + 1)).0.0" ;; minor) NEW_VERSION="$MAJOR.$((MINOR + 1)).0" ;; patch) NEW_VERSION="$MAJOR.$MINOR.$((PATCH + 1))" ;; *) echo "Usage: $0 {major|minor|patch}" exit 1 ;; esac
echo "New version: $NEW_VERSION"
CURRENT_VERSION=$(cat package.json | jq -r '.version') echo "Current version: $CURRENT_VERSION"
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_VERSION"
case $1 in major) NEW_VERSION="$((MAJOR + 1)).0.0" ;; minor) NEW_VERSION="$MAJOR.$((MINOR + 1)).0" ;; patch) NEW_VERSION="$MAJOR.$MINOR.$((PATCH + 1))" ;; *) echo "Usage: $0 {major|minor|patch}" exit 1 ;; esac
echo "New version: $NEW_VERSION"

Update package.json

Update package.json

npm version $NEW_VERSION --no-git-tag-version
npm version $NEW_VERSION --no-git-tag-version

Create git tag

Create git tag

git add package.json package-lock.json git commit -m "chore: bump version to $NEW_VERSION" git tag -a "v$NEW_VERSION" -m "Version $NEW_VERSION"
undefined
git add package.json package-lock.json git commit -m "chore: bump version to $NEW_VERSION" git tag -a "v$NEW_VERSION" -m "Version $NEW_VERSION"
undefined

Python Script

Python 脚本

python
#!/usr/bin/env python3
python
#!/usr/bin/env python3

bump_version.py

bump_version.py

import re import sys import subprocess
def get_current_version(): with open('setup.py', 'r') as f: content = f.read() match = re.search(r"version='"['"]", content) return match.group(1) if match else None
def bump_version(current, bump_type): major, minor, patch = map(int, current.split('.'))
if bump_type == 'major':
    return f'{major + 1}.0.0'
elif bump_type == 'minor':
    return f'{major}.{minor + 1}.0'
elif bump_type == 'patch':
    return f'{major}.{minor}.{patch + 1}'
else:
    raise ValueError(f'Invalid bump type: {bump_type}')
def update_version(old_version, new_version): with open('setup.py', 'r') as f: content = f.read()
content = content.replace(f"version='{old_version}'", f"version='{new_version}'")

with open('setup.py', 'w') as f:
    f.write(content)
if name == 'main': bump_type = sys.argv[1] if len(sys.argv) > 1 else 'patch' current = get_current_version() new = bump_version(current, bump_type)
print(f'Bumping version: {current} → {new}')
update_version(current, new)

subprocess.run(['git', 'add', 'setup.py'])
subprocess.run(['git', 'commit', '-m', f'chore: bump version to {new}'])
subprocess.run(['git', 'tag', '-a', f'v{new}', '-m', f'Version {new}'])
undefined
import re import sys import subprocess
def get_current_version(): with open('setup.py', 'r') as f: content = f.read() match = re.search(r"version='"['"]", content) return match.group(1) if match else None
def bump_version(current, bump_type): major, minor, patch = map(int, current.split('.'))
if bump_type == 'major':
    return f'{major + 1}.0.0'
elif bump_type == 'minor':
    return f'{major}.{minor + 1}.0'
elif bump_type == 'patch':
    return f'{major}.{minor}.{patch + 1}'
else:
    raise ValueError(f'Invalid bump type: {bump_type}')
def update_version(old_version, new_version): with open('setup.py', 'r') as f: content = f.read()
content = content.replace(f"version='{old_version}'", f"version='{new_version}'")

with open('setup.py', 'w') as f:
    f.write(content)
if name == 'main': bump_type = sys.argv[1] if len(sys.argv) > 1 else 'patch' current = get_current_version() new = bump_version(current, bump_type)
print(f'Bumping version: {current} → {new}')
update_version(current, new)

subprocess.run(['git', 'add', 'setup.py'])
subprocess.run(['git', 'commit', '-m', f'chore: bump version to {new}'])
subprocess.run(['git', 'tag', '-a', f'v{new}', '-m', f'Version {new}'])
undefined

Multi-Package Versioning

多包版本管理

Lerna

Lerna

json
// lerna.json
{
  "version": "independent",
  "npmClient": "npm",
  "command": {
    "version": {
      "conventionalCommits": true,
      "message": "chore(release): publish"
    },
    "publish": {
      "conventionalCommits": true
    }
  }
}
bash
undefined
json
// lerna.json
{
  "version": "independent",
  "npmClient": "npm",
  "command": {
    "version": {
      "conventionalCommits": true,
      "message": "chore(release): publish"
    },
    "publish": {
      "conventionalCommits": true
    }
  }
}
bash
undefined

Version all changed packages

为所有变更包升级版本

npx lerna version
npx lerna version

Publish all changed packages

发布所有变更包

npx lerna publish
undefined
npx lerna publish
undefined

Changesets

Changesets

bash
undefined
bash
undefined

Initialize

初始化

npx @changesets/cli init
npx @changesets/cli init

Add changeset

添加变更记录

npx changeset add
npx changeset add

Version packages

升级包版本

npx changeset version
npx changeset version

Publish

发布包

npx changeset publish
undefined
npx changeset publish
undefined

Common Issues

常见问题

Issue: No Version Bump

问题:未触发版本升级

Problem: semantic-release not creating release Solution: Check commit format, verify branch configuration
问题描述:semantic-release未创建发布版本 解决方法:检查提交格式,验证分支配置

Issue: Wrong Version Calculated

问题:版本计算错误

Problem: Major/minor/patch incorrectly determined Solution: Review commit analyzer rules, check for missing prefixes
问题描述:MAJOR/MINOR/PATCH版本号判断错误 解决方法:检查提交分析规则,确认是否缺少前缀

Issue: Duplicate Tags

问题:标签重复

Problem: Tag already exists Solution: Clean up tags, verify version wasn't already released
问题描述:标签已存在 解决方法:清理重复标签,确认版本未被发布过

Best Practices

最佳实践

  • Use conventional commits consistently
  • Automate version bumping in CI
  • Generate changelogs automatically
  • Tag releases in Git
  • Use pre-release versions for testing
  • Document breaking changes clearly
  • Include migration guides for major versions
  • Lock dependencies with exact versions
  • 持续使用规范化提交
  • 在CI中自动化版本号升级
  • 自动生成变更日志
  • 在Git中标记发布版本
  • 使用预发布版本进行测试
  • 清晰记录破坏性变更
  • 为大版本提供迁移指南
  • 使用精确版本锁定依赖

Related Skills

相关技能

  • git-workflow - Branching strategies
  • github-actions - CI automation
  • feature-flags - Progressive rollout
  • git-workflow - 分支策略
  • github-actions - CI自动化
  • feature-flags - 渐进式发布