Skip to content
Merged
Show file tree
Hide file tree
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
41 changes: 41 additions & 0 deletions .claude/commands/release.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Release

Prepare a release PR for initium. This command handles version bumping, changelog updates, and PR creation. The release is published automatically when the PR merges (CI detects the version bump and creates the tag, which triggers the release workflow).

## Determine the next version number

Follow semantic versioning (MAJOR.MINOR.PATCH):

1. Read `CHANGELOG.md` under `## [Unreleased]` to see what has changed since the last release.
2. Read recent commits since the last tag: `git log $(git describe --tags --abbrev=0)..HEAD --oneline`
3. Determine the version bump:
- **PATCH** (x.y.Z): Only bug fixes, documentation, or internal changes with no user-facing behavior change.
- **MINOR** (x.Y.0): New features, new CLI flags, new configuration options, or backward-compatible enhancements.
- **MAJOR** (X.0.0): Breaking changes — removed features, changed defaults, incompatible schema/config changes, or renamed CLI flags.
4. Read the current version from `Cargo.toml` and compute the next version.

## Confirmation phase

Before making any changes, present to the user:
- The **current version** and the **proposed next version** with reasoning.
- A **summary of changes** that will go into the release (from Unreleased changelog + commit log).
- Ask: "Proceed with version X.Y.Z?" and wait for confirmation.
- If the user suggests a different version, use that instead.

## Execute the release

Once confirmed:

1. Fetch origin and create a branch: `release/vX.Y.Z` from `origin/main`.
2. Bump version in `Cargo.toml` (the `version = "..."` field under `[package]`).
3. Run `cargo check` to update `Cargo.lock`.
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This step says cargo check will update Cargo.lock, but cargo check typically won't change the lockfile unless dependency resolution changes; bumping the package version alone won't do that. If the goal is just validation, reword to reflect that; if the goal is to ensure a lockfile exists/updates, use the appropriate Cargo command (e.g., generating/updating the lockfile) instead.

Suggested change
3. Run `cargo check` to update `Cargo.lock`.
3. Run `cargo check` to ensure the project builds successfully after the version bump.

Copilot uses AI. Check for mistakes.
4. Update `CHANGELOG.md`:
- Move everything under `## [Unreleased]` into a new `## [X.Y.Z] - YYYY-MM-DD` section (use today's date).
- Leave `## [Unreleased]` empty (with just the heading).
5. Run `cargo test` to verify nothing is broken.
6. Run `cargo clippy -- -D warnings` and `cargo fmt -- --check`.
7. Commit: `release: vX.Y.Z`
8. Push the branch and create a PR with title `release: vX.Y.Z`.
9. The PR body should include the changelog entries for this version.

When the PR merges, the auto-tag workflow detects the version bump in `Cargo.toml` and creates the `vX.Y.Z` tag, which triggers the release workflow (Docker build + crates.io publish).
40 changes: 40 additions & 0 deletions .github/workflows/auto-tag.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Auto Tag
on:
push:
branches: [main]
paths:
- Cargo.toml
permissions:
contents: write
jobs:
tag:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 2
- name: Check for version bump
id: version
run: |
CURRENT=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/')
PREVIOUS=$(git show HEAD~1:Cargo.toml | grep '^version' | head -1 | sed 's/.*"\(.*\)"/\1/')
Comment on lines +15 to +20
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HEAD~1 only compares against the immediately previous commit. On pushes that include multiple commits (or merges), this can miss a version bump that happened earlier in the push (or compare against the wrong base). Use the push event's before SHA (e.g., ${{ github.event.before }}) or explicitly diff Cargo.toml between ${{ github.event.before }} and ${{ github.sha }} to reliably detect version changes for the whole push.

Suggested change
fetch-depth: 2
- name: Check for version bump
id: version
run: |
CURRENT=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/')
PREVIOUS=$(git show HEAD~1:Cargo.toml | grep '^version' | head -1 | sed 's/.*"\(.*\)"/\1/')
fetch-depth: 0
- name: Check for version bump
id: version
run: |
CURRENT=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/')
PREVIOUS=$(git show ${{ github.event.before }}:Cargo.toml | grep '^version' | head -1 | sed 's/.*"\(.*\)"/\1/')

Copilot uses AI. Check for mistakes.
echo "current=$CURRENT" >> "$GITHUB_OUTPUT"
echo "previous=$PREVIOUS" >> "$GITHUB_OUTPUT"
if [ "$CURRENT" != "$PREVIOUS" ]; then
echo "changed=true" >> "$GITHUB_OUTPUT"
else
echo "changed=false" >> "$GITHUB_OUTPUT"
fi
- name: Create and push tag
if: steps.version.outputs.changed == 'true'
run: |
VERSION="${{ steps.version.outputs.current }}"
TAG="v${VERSION}"
if git rev-parse "$TAG" >/dev/null 2>&1; then
echo "Tag $TAG already exists, skipping"
exit 0
fi
git tag "$TAG"
git push origin "$TAG"
Comment on lines +33 to +38
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tag existence check uses git rev-parse "$TAG", which only checks locally fetched tags. In a shallow checkout this may not include existing remote tags, causing git push to fail when the tag already exists on origin. Prefer checking the remote (git ls-remote --tags origin "refs/tags/$TAG") or fetching tags before the check, and handle a push rejection gracefully.

Suggested change
if git rev-parse "$TAG" >/dev/null 2>&1; then
echo "Tag $TAG already exists, skipping"
exit 0
fi
git tag "$TAG"
git push origin "$TAG"
# Check if the tag already exists on the remote
if git ls-remote --tags origin "refs/tags/$TAG" | grep -q .; then
echo "Tag $TAG already exists on origin, skipping"
exit 0
fi
# Also check if the tag exists locally
if git rev-parse "$TAG" >/dev/null 2>&1; then
echo "Tag $TAG already exists locally, skipping"
exit 0
fi
git tag "$TAG"
# Push the tag; if it fails, re-check the remote to handle concurrent creation
if ! git push origin "$TAG"; then
if git ls-remote --tags origin "refs/tags/$TAG" | grep -q .; then
echo "Tag $TAG was created concurrently on origin, skipping"
exit 0
fi
echo "Failed to push tag $TAG"
exit 1
fi

Copilot uses AI. Check for mistakes.
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- Auto-tag workflow: CI automatically creates a git tag when `Cargo.toml` version changes on main, triggering the release workflow.
- `/release` skill for Claude Code: guided release preparation with version determination, confirmation, and PR creation.

## [1.2.0] - 2026-03-11

### Added
Expand Down
Loading