···19192020clean:
2121 @rm -rf ./__snapshots__
2222+2323+# Determine next version from conventional commits and tag both modules
2424+release:
2525+ @./scripts/version.sh
2626+2727+# Preview version bump without creating tags
2828+release-dry:
2929+ @./scripts/version.sh --dry-run
+87
scripts/version.sh
···11+#!/usr/bin/env bash
22+set -euo pipefail
33+44+# Determine next semver based on conventional commits since main.
55+# Usage: ./scripts/version.sh [--dry-run]
66+77+DRY_RUN=false
88+if [[ "${1:-}" == "--dry-run" ]]; then
99+ DRY_RUN=true
1010+fi
1111+1212+# Get the latest root module tag (ignore cmd/shutter/ prefixed tags)
1313+LATEST_TAG=$(jj tag list | grep -E '^v[0-9]' | sort -V -t: -k1,1 | tail -1 | awk '{print $1}' | tr -d ':')
1414+1515+if [[ -z "$LATEST_TAG" ]]; then
1616+ echo "error: no existing version tags found"
1717+ exit 1
1818+fi
1919+2020+echo "Current version: $LATEST_TAG"
2121+2222+# Parse current version
2323+VERSION="${LATEST_TAG#v}"
2424+IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION"
2525+2626+# Get commit messages since main
2727+COMMITS=$(jj log -r 'main..@' --no-graph -T 'description ++ "---\n"' 2>/dev/null)
2828+2929+if [[ -z "$COMMITS" || "$COMMITS" == $'---\n' ]]; then
3030+ echo "No commits since main."
3131+ exit 0
3232+fi
3333+3434+echo ""
3535+echo "Commits since main:"
3636+echo "$COMMITS" | grep -v '^---$' | grep -v '^$' | sed 's/^/ /'
3737+echo ""
3838+3939+# Determine bump type from conventional commits
4040+BUMP="patch"
4141+4242+while IFS= read -r line; do
4343+ # Skip empty lines and delimiters
4444+ [[ -z "$line" || "$line" == "---" ]] && continue
4545+4646+ # Check for breaking changes
4747+ if echo "$line" | grep -qiE '^[a-z]+(\(.+\))?!:|BREAKING CHANGE'; then
4848+ BUMP="major"
4949+ break
5050+ fi
5151+5252+ # Check for feat -> minor
5353+ if echo "$line" | grep -qE '^feat(\(.+\))?:'; then
5454+ BUMP="minor"
5555+ fi
5656+done <<< "$COMMITS"
5757+5858+# Calculate new version
5959+case "$BUMP" in
6060+ major) MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0 ;;
6161+ minor) MINOR=$((MINOR + 1)); PATCH=0 ;;
6262+ patch) PATCH=$((PATCH + 1)) ;;
6363+esac
6464+6565+NEW_VERSION="v${MAJOR}.${MINOR}.${PATCH}"
6666+6767+echo "Bump type: $BUMP"
6868+echo "New version: $NEW_VERSION"
6969+echo "Tags: $NEW_VERSION, cmd/shutter/$NEW_VERSION"
7070+7171+if $DRY_RUN; then
7272+ echo ""
7373+ echo "(dry run — no tags created)"
7474+ exit 0
7575+fi
7676+7777+echo ""
7878+read -p "Create tags and push? [y/N] " -n 1 -r
7979+echo ""
8080+8181+if [[ $REPLY =~ ^[Yy]$ ]]; then
8282+ jj tag set "$NEW_VERSION" "cmd/shutter/$NEW_VERSION"
8383+ jj git push --tags
8484+ echo "Done. Tagged and pushed $NEW_VERSION"
8585+else
8686+ echo "Aborted."
8787+fi