release-and-changelog

verified

8c37d9b9-4deb-4e36-ba95-b54d7e0ef1e6

Use when cutting a release — semver decision table (breaking/feature/fix → major/minor/patch), Keep a Changelog format, and the full release checklist from version bump to announce.

Metadata

Skill ID
8c37d9b9-4deb-4e36-ba95-b54d7e0ef1e6
Version
1
Owner
387274b7-2891-478b-81b8-e11d5adb9319
Tags
releasesemverchangelogversioningdeployment
Signature
verified
Integrity
OK
Content hash
18577d5ab776363b3f5368ca25adf0592115b471ff5db1b583d55b2808ee78da
Created
2026-08-15T05:27:11Z

Skill file

Raw skill file (markdown source)
# Release and Changelog

**Use when** cutting a release — deciding the version number, writing the changelog, tagging, building, and announcing. A release without a changelog is an untracked deployment.

## Semver Decision Table

```
MAJOR.MINOR.PATCH
  |     |     └─ PATCH: backward-compatible bug fixes
  |     └─────── MINOR: backward-compatible new features
  └───────────── MAJOR: breaking changes
```

| Change type | Bump | Example |
|-------------|------|---------|
| Breaking API change | **MAJOR** | Removed param, renamed endpoint, dropped Python 3.8 |
| New feature (backward-compatible) | **MINOR** | New endpoint, new optional param |
| Bug fix (backward-compatible) | **PATCH** | Fixed null crash, corrected rounding |
| Docs only | (usually) **PATCH** | README typo, comment fix |
| Deprecation (not removal) | **MINOR** | Added `@deprecated` warning |

### Pre-1.0 Pragmatics

```text
0.x.y is special: anything may break, so:
- 0.1.0 → 0.2.0 can have breaking changes (MINOR acts like MAJOR)
- 0.2.0 → 0.2.1 is a bug fix (PATCH behaves normally)
Rule: before 1.0, don't promise stability.
```

### Quick decision: what changed?

```bash
# Inspect commits since the last tag
git log --oneline v1.2.0..HEAD

# Categorize:
# - "feat:"      -> MINOR
# - "fix:"       -> PATCH
# - "refactor:"  -> PATCH (usually)
# - "BREAKING"   -> MAJOR
```

## Changelog Format (Keep a Changelog)

Follow https://keepachangelog.com — sections by version, newest first:

```markdown
# Changelog

All notable changes to this project are documented here.
The format follows [Keep a Changelog](https://keepachangelog.com).

## [Unreleased]

## [1.3.0] - 2026-08-15
### Added
- Bulk order cancellation endpoint (`POST /v1/orders/bulk-cancel`)

### Changed
- Rate limiting now returns 429 with a `Retry-After` header

### Fixed
- Null `user_id` no longer crashes the refresh flow (#1234)

### Deprecated
- `GET /v1/orders` will be removed in 2.0; use `GET /v2/orders`

## [1.2.0] - 2026-07-20
### Added
- CSV export for order history
```

### Categories

```text
Added      — new features
Changed    — changes to existing functionality
Deprecated — soon-to-be-removed features
Removed    — removed features
Fixed      — bug fixes
Security   — vulnerability fixes
```

## Deriving Changelog from Conventional Commits

```bash
# If you use Conventional Commits, you can auto-generate a changelog draft
# Using git-cliff (https://github.com/orhun/git-cliff)
git cliff --tag v1.3.0

# Or generate a simple log grouped by type
git log --oneline v1.2.0..HEAD | grep "^[a-f0-9]* feat"  # features
git log --oneline v1.2.0..HEAD | grep "^[a-f0-9]* fix"   # fixes
```

## The Release Checklist

```bash
# 1. Version bump (in pyproject.toml, package.json, etc.)
# pyproject.toml:  version = "1.3.0"
# package.json:    "version": "1.3.0"

# 2. Update the changelog (move [Unreleased] -> [1.3.0])
# CHANGELOG.md

# 3. Commit the bump + changelog
git add pyproject.toml CHANGELOG.md
git commit -m "chore: release 1.3.0"

# 4. Tag it
git tag -a v1.3.0 -m "Release 1.3.0"

# 5. Push the tag
git push origin main --tags

# 6. Build the artifact
python -m build              # or: make build, npm run build

# 7. Publish (PyPI example)
python -m twine upload dist/*

# 8. Announce (GitHub release + notes)
gh release create v1.3.0 --title "1.3.0" --notes "$(cat CHANGELOG.md | sed -n '/## \[1.3.0\]/,/## \[1.2.0\]/p')"
```

## Git Tag Conventions

```bash
# Annotated tag (recommended — stores metadata)
git tag -a v1.3.0 -m "Release 1.3.0"

# Lightweight tag (not recommended for releases)
git tag v1.3.0

# Tag naming: v-prefix is conventional for semver
# v1.3.0  (yes)   1.3.0 (also fine, but v-prefix is clearer)
```

## Guardrails

- **Never bump without a changelog.** A version with no changelog is an untracked deployment.
- **Never mix breaking changes into a patch bump.** That's how you break downstream consumers silently.
- **Don't leave `[Unreleased]` with released changes.** Every merged change belongs in a versioned section.
- **Tag BEFORE building**, so the build embeds the correct version.
- **Untagged releases don't exist.** If it's not tagged, you can't roll back to it.

## Pitfalls

| Pitfall | Fix |
|---------|-----|
| Breaking change in a patch bump | Categorize commits; breaking → MAJOR |
| Changelog not updated | Update `[Unreleased]` → versioned section EVERY release |
| Untagged release | Always `git tag -a vX.Y.Z` |
| Changelog generated but not human-reviewed | Auto-gen is a draft; clean it up and add context |
| Version bump in only some places | Grep for the old version everywhere (`grep -rn "1.2.0" .`) |

## Verify / Checklist

- [ ] Semver decision matches the changes (breaking/feature/fix → major/minor/patch)
- [ ] Changelog has an entry for this version with correct categories
- [ ] Version bumped in ALL locations (`grep -rn "<old>" .` finds nothing stale)
- [ ] Annotated git tag created (`git tag -a vX.Y.Z`)
- [ ] Tag pushed (`git push --tags`)
- [ ] Artifact built and published
- [ ] GitHub release / announcement created with notes

Attached files

No attached files.