mirror of
https://github.com/BagelHole/DevOps-Security-Agent-Skills.git
synced 2026-08-22 12:49:53 +02:00
478 lines
10 KiB
Markdown
478 lines
10 KiB
Markdown
---
|
|||
|
|
name: semantic-versioning
|
||
|
|
description: Automate versioning and changelog generation using semantic versioning principles. Configure release automation, version bumping, and changelog tools. Use when implementing version management or automating release processes.
|
||
|
|
license: MIT
|
||
|
|
metadata:
|
||
|
|
author: devops-skills
|
||
|
|
version: "1.0"
|
||
|
|
---
|
||
|
|
|
||
|
|
# Semantic Versioning
|
||
|
|
|
||
|
|
Automate version management and changelog generation following SemVer principles.
|
||
|
|
|
||
|
|
## 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)
|
||
|
|
|
||
|
|
## 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
|
||
|
|
```
|
||
|
|
|
||
|
|
### Version Components
|
||
|
|
|
||
|
|
| Component | When to Increment |
|
||
|
|
|-----------|-------------------|
|
||
|
|
| MAJOR | Breaking changes (incompatible API changes) |
|
||
|
|
| MINOR | New features (backward compatible) |
|
||
|
|
| PATCH | Bug fixes (backward compatible) |
|
||
|
|
| PRERELEASE | Pre-release versions (alpha, beta, rc) |
|
||
|
|
| BUILD | Build metadata (ignored in precedence) |
|
||
|
|
|
||
|
|
### 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
|
||
|
|
```
|
||
|
|
|
||
|
|
## 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
|
||
|
|
```
|
||
|
|
|
||
|
|
## semantic-release
|
||
|
|
|
||
|
|
### Installation
|
||
|
|
|
||
|
|
```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"
|
||
|
|
]
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### 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'
|
||
|
|
]
|
||
|
|
};
|
||
|
|
```
|
||
|
|
|
||
|
|
### GitHub Actions Integration
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
# .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
|
||
|
|
|
||
|
|
### Installation
|
||
|
|
|
||
|
|
```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}}"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Usage
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# 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 Scripts
|
||
|
|
|
||
|
|
```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
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# 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
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Install
|
||
|
|
cargo install git-cliff
|
||
|
|
|
||
|
|
# Generate changelog
|
||
|
|
git cliff -o CHANGELOG.md
|
||
|
|
```
|
||
|
|
|
||
|
|
```toml
|
||
|
|
# 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]*"
|
||
|
|
```
|
||
|
|
|
||
|
|
## Version Bumping Scripts
|
||
|
|
|
||
|
|
### Bash Script
|
||
|
|
|
||
|
|
```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 Script
|
||
|
|
|
||
|
|
```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}'])
|
||
|
|
```
|
||
|
|
|
||
|
|
## Multi-Package Versioning
|
||
|
|
|
||
|
|
### Lerna
|
||
|
|
|
||
|
|
```json
|
||
|
|
// lerna.json
|
||
|
|
{
|
||
|
|
"version": "independent",
|
||
|
|
"npmClient": "npm",
|
||
|
|
"command": {
|
||
|
|
"version": {
|
||
|
|
"conventionalCommits": true,
|
||
|
|
"message": "chore(release): publish"
|
||
|
|
},
|
||
|
|
"publish": {
|
||
|
|
"conventionalCommits": true
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Version all changed packages
|
||
|
|
npx lerna version
|
||
|
|
|
||
|
|
# Publish all changed packages
|
||
|
|
npx lerna publish
|
||
|
|
```
|
||
|
|
|
||
|
|
### Changesets
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Initialize
|
||
|
|
npx @changesets/cli init
|
||
|
|
|
||
|
|
# Add changeset
|
||
|
|
npx changeset add
|
||
|
|
|
||
|
|
# Version packages
|
||
|
|
npx changeset version
|
||
|
|
|
||
|
|
# Publish
|
||
|
|
npx changeset publish
|
||
|
|
```
|
||
|
|
|
||
|
|
## Common Issues
|
||
|
|
|
||
|
|
### Issue: No Version Bump
|
||
|
|
**Problem**: semantic-release not creating release
|
||
|
|
**Solution**: Check commit format, verify branch configuration
|
||
|
|
|
||
|
|
### Issue: Wrong Version Calculated
|
||
|
|
**Problem**: Major/minor/patch incorrectly determined
|
||
|
|
**Solution**: Review commit analyzer rules, check for missing prefixes
|
||
|
|
|
||
|
|
### 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
|
||
|
|
|
||
|
|
## Related Skills
|
||
|
|
|
||
|
|
- [git-workflow](../git-workflow/) - Branching strategies
|
||
|
|
- [github-actions](../../ci-cd/github-actions/) - CI automation
|
||
|
|
- [feature-flags](../feature-flags/) - Progressive rollout
|