mcpField
S
semantic-versioning

semantic-versioning

github.com/bagelhole開発1.2k340 回インストール23 週間前に更新
リポジトリ

semantic-versioning について

semantic-versioning は、リリースワークフローやパッケージ管理向けのバージョン管理・変更履歴生成スキルです。SemVer の規則に加え、バージョン更新、変更履歴生成、複数パッケージの管理を扱います。

主な機能

  1. 01SemVer に沿ったバージョン更新
  2. 02Conventional Commits に基づく更新判定
  3. 03変更履歴の自動生成
  4. 04semantic-release と standard-version の設定
  5. 05複数パッケージのバージョン管理

活用シーン

  1. 01CI のリリース工程でバージョンを自動決定
  2. 02コミット履歴から変更履歴を生成
  3. 03複数パッケージのリポジトリでリリースを調整

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/bagelhole/devops-security-agent-skills --skill semantic-versioning

インストール後、Claude Code で/semantic-versioningを入力して読み込まれたか確認します。

詳細

セマンティックバージョニング

SemVer の原則に従って、バージョン管理と変更履歴の生成を自動化します。

このスキルを使うタイミング

次のような場合にこのスキルを使用します。

  • バージョン番号の標準を導入するとき
  • リリースのバージョン管理を自動化するとき
  • 変更履歴を自動生成するとき
  • リリースパイプラインを設定するとき
  • パッケージのバージョンを管理するとき

前提条件

  • コミット履歴のある Git リポジトリ
  • Node.js(多くのツールで必要)
  • Conventional Commits(推奨)

セマンティックバージョニングの基本

バージョン形式
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破壊的変更(互換性のない API 変更)
MINOR新機能(後方互換性あり)
PATCHバグ修正(後方互換性あり)
PRERELEASEプレリリースバージョン(alpha、beta、rc)
BUILDビルドメタデータ(優先順位には影響しません)
バージョンの優先順位
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 とバージョン

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

インストール
npm install --save-dev semantic-release \
  @semantic-release/changelog \
  @semantic-release/git
設定
// .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"
  ]
}
詳細設定
// 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 との統合
# .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

standard-version

インストール
npm install --save-dev standard-version
設定
// .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}}"
}
使用方法
# First release
npx standard-version --first-release

# Regular release (auto-detect version bump)
npx standard-version

# Specific version bump
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

# Dry run
npx standard-version --dry-run

# Skip specific steps
npx standard-version --skip.changelog
NPM スクリプト
// 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"
  }
}

変更履歴の生成

conventional-changelog
# Install
npm install -g conventional-changelog-cli

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

# Generate all history
conventional-changelog -p angular -i CHANGELOG.md -s -r 0
git-cliff
# Install
cargo install git-cliff

# Generate changelog
git cliff -o CHANGELOG.md
# cliff.toml
[changelog]
header = "# Changelog\n\n"
body = """
{% for group, commits in commits | group_by(attribute="group") %}
## {{ 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}](https://github.com/owner/repo/issues/${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]*"

バージョン更新スクリプト

Bash スクリプト
#!/bin/bash
# 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"

# Update package.json
npm version $NEW_VERSION --no-git-tag-version

# 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"
Python スクリプト
#!/usr/bin/env python3
# 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}'])

複数パッケージのバージョン管理

Lerna
// lerna.json
{
  "version": "independent",
  "npmClient": "npm",
  "command": {
    "version": {
      "conventionalCommits": true,
      "message": "chore(release): publish"
    },
    "publish": {
      "conventionalCommits": true
    }
  }
}
# Version all changed packages
npx lerna version

# Publish all changed packages
npx lerna publish
Changesets
# Initialize
npx @changesets/cli init

# Add changeset
npx changeset add

# Version packages
npx changeset version

# Publish
npx changeset publish

よくある問題

問題:バージョンが更新されない

問題:semantic-release がリリースを作成しない 解決策:コミット形式を確認し、ブランチ設定を検証します

問題:計算されたバージョンが正しくない

問題:メジャー、マイナー、パッチの判定が誤っている 解決策:コミットアナライザーのルールを確認し、プレフィックスの不足がないか確認します

問題:タグが重複している

問題:タグがすでに存在する 解決策:タグを整理し、該当バージョンがすでにリリースされていないか確認します

ベストプラクティス

  • Conventional Commits を一貫して使用する
  • CI でバージョン更新を自動化する
  • 変更履歴を自動生成する
  • Git でリリースにタグを付ける
  • テストにはプレリリースバージョンを使用する
  • 破壊的変更を明確に記述する
  • メジャーバージョンには移行ガイドを含める
  • 依存関係を正確なバージョンで固定する

関連スキル

コメント