Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 50 additions & 13 deletions .agents/skills/version-bump/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ MAJOR.MINOR.PATCH
- **MINOR**: New features, backwards compatible
- **PATCH**: Bug fixes, backwards compatible

_This format is referenced as `X.Y.Z` in this document._

## Version Bump Decision Tree

### MAJOR (X.0.0) - Breaking Changes
Expand Down Expand Up @@ -67,7 +69,10 @@ For versions < 1.0.0 (like this project):

## Instructions

1. Review all changes since last release: `git log --oneline $(git describe --tags --abbrev=0)..HEAD`
1. Review all changes since last release:
`git log --oneline "$(git describe --tags --abbrev=0 2>/dev/null || git rev-list --max-parents=0 HEAD)"..HEAD`
The fallback covers the first release, when `git describe` has no tag to find
and would otherwise fail.
Comment thread
shadowhand marked this conversation as resolved.
2. Check for breaking changes:
- Removed or renamed public APIs?
- Changed default behaviors?
Expand All @@ -76,21 +81,54 @@ For versions < 1.0.0 (like this project):
4. If new features exist -> MINOR bump
5. If only fixes/refactoring -> PATCH bump

## Preparing the Release

When `CHANGELOG.md` has a non-empty `## [Unreleased]` section, prepare the
release on a branch rather than committing to `main`:

1. Create the branch from `main`: `chore/prepare-release-X.Y.Z`
2. Make the changelog and version commits there (see *Version Update Locations* below)
3. Push the branch and open a pull request
4. Stop here and wait for the pull request to be merged

## Version Update Locations

When bumping version, update `CHANGELOG.md`:

1. Add `## [X.Y.Z] - YYYY-MM-DD` section
2. Update comparison URLs at bottom of CHANGELOG.md
3. Update the `Unreleased` comparison URL at the bottom of CHANGELOG.md
1. Rename the accumulated `## [Unreleased]` heading to `## [X.Y.Z] - YYYY-MM-DD`
2. Add a fresh, empty `## [Unreleased]` heading above it, so the next change has
somewhere to go
3. Reconcile the section's contents with what actually shipped — entries written
while the work was in flight often still call a feature deferred that landed
later in the same cycle
4. Add a `[X.Y.Z]: https://github.com/<org>/<repo>/releases/tag/X.Y.Z` link
definition at the bottom of CHANGELOG.md
5. Repoint the `[Unreleased]` link at the version just cut —
`.../compare/X.Y.Z...main`. It is the one comparison link the changelog
keeps, and leaving it behind makes the empty `[Unreleased]` section link to
a diff full of released work

## Release Process

After the changelog is updated and committed:

1. Create a signed tag with no `v` prefix: `git tag -s X.Y.Z -m "X.Y.Z"`
2. Push the tag: `git push origin X.Y.Z`
3. Create the GitHub release from the changelog section for that version:
Tag only when `## [Unreleased]` is empty — the top of the changelog is then the
version to release. Otherwise, create a pull request as described in
*Preparing the Release*.

1. Check whether the tag already exists and which commit it points at.
Resolve it before comparing: `git rev-parse X.Y.Z^{commit}` for a local tag,
or `git ls-remote origin 'refs/tags/X.Y.Z' 'refs/tags/X.Y.Z^{}'` for the remote,
where the `^{}` line is the peeled commit of an annotated tag. Compare that
commit with the commit being released.
- No tag: continue to step 2.
- Remote tag at the release commit: a prior run already created and pushed
it, so skip to step 4. `--verify-tag` checks the remote tag, not a local
one.
- Local tag at the release commit, not yet on the remote: a prior run
created the signed tag but did not push it — skip to step 3 to push it.
- Tag pointing anywhere else: stop. Never move or force an existing tag.
2. Create a signed tag with no `v` prefix: `git tag -s X.Y.Z -m "X.Y.Z"`
3. Push the tag: `git push origin X.Y.Z`
4. Create the GitHub release from the changelog section for that version:

```sh
awk -v ver="X.Y.Z" '
Expand All @@ -99,14 +137,13 @@ After the changelog is updated and committed:
inside { print }
' CHANGELOG.md > /tmp/release-notes.md

printf '\n**Full Changelog**: %s\n' \
"$(grep -E '^\[X\.Y\.Z\]: ' CHANGELOG.md | sed 's/^\[X\.Y\.Z\]: //')" \
>> /tmp/release-notes.md
url=$(sed -n 's/^\[X\.Y\.Z\]: //p' CHANGELOG.md)
[ -n "$url" ] && printf '\n**Full Changelog**: %s\n' "$url" >> /tmp/release-notes.md

Comment thread
shadowhand marked this conversation as resolved.
gh release create X.Y.Z --title X.Y.Z --notes-file /tmp/release-notes.md --verify-tag
```

4. Verify the release exists and is marked latest: `gh release list`
5. Verify the release exists and is marked latest: `gh release list`

Release notes are the changelog section verbatim, so the changelog stays the single source
of truth. `--verify-tag` fails the command rather than creating a tag that was never pushed.
Loading