# prior to running this script.
#
# Usage:
-# ./scripts/release.sh [major|minor|patch] [--dry-run]
+# ./scripts/release.sh prepare [major|minor|patch] [--dry-run]
+# ./scripts/release.sh finalize [--dry-run]
#
-# Example usage:
-# $ ./scripts/release.sh minor --dry-run
+# Two-stage release process:
#
-# This will show what the actions that would be taken to increment the minor
-# version of the project.
+# Stage 1 - Prepare:
+# $ ./scripts/release.sh prepare minor
+# This creates a release candidate branch with version bump and removes -dev suffix.
+# The branch should then be manually pushed and a PR created, reviewed, and merged.
#
-# This script:
-# 1. Updates version and removes -dev suffix
-# 2. Commits the version bump
-# 3. Creates a git tag
-# 4. Prepares for next development cycle
+# Stage 2 - Finalize:
+# $ ./scripts/release.sh finalize
+# After the RC PR is merged, this reads the current version from CMakeLists.txt,
+# creates the release tag, and prepares the next development cycle.
+#
+# Prepare stage:
+# 1. Creates release candidate branch
+# 2. Updates version and removes -dev suffix
+# 3. Commits the version bump
+#
+# Finalize stage:
+# 1. Reads current release version from CMakeLists.txt
+# 2. Creates signed git tag on master
+# 3. Adds -dev suffix back for next development cycle
+# 4. Creates branch and commit for development version
#
set -e
fi
# Parse command line arguments
+COMMAND=""
VERSION_TYPE=""
DRY_RUN=false
+# First argument should be the command
+if [ $# -eq 0 ]; then
+ echo "Error: Missing command"
+ echo "Usage: $0 prepare [major|minor|patch] [--dry-run]"
+ echo " $0 finalize [--dry-run]"
+ exit 1
+fi
+
+COMMAND="$1"
+shift
+
+# Parse remaining arguments
for arg in "$@"; do
case $arg in
--dry-run)
DRY_RUN=true
- shift
;;
major|minor|patch)
- VERSION_TYPE="$arg"
- shift
+ if [ "$COMMAND" = "prepare" ]; then
+ VERSION_TYPE="$arg"
+ else
+ echo "Error: Version type only valid for 'prepare' command"
+ exit 1
+ fi
;;
*)
echo "Error: Unknown argument '$arg'"
- echo "Usage: $0 [major|minor|patch] [--dry-run]"
+ echo "Usage: $0 prepare [major|minor|patch] [--dry-run]"
+ echo " $0 finalize [--dry-run]"
exit 1
;;
esac
done
-# Default to patch if no version type specified
-VERSION_TYPE="${VERSION_TYPE:-patch}"
-
-if [[ ! "$VERSION_TYPE" =~ ^(major|minor|patch)$ ]]; then
- echo "Error: Version type must be 'major', 'minor', or 'patch'"
- echo "Usage: $0 [major|minor|patch] [--dry-run]"
+# Validate command
+if [[ ! "$COMMAND" =~ ^(prepare|finalize)$ ]]; then
+ echo "Error: Command must be 'prepare' or 'finalize'"
+ echo "Usage: $0 prepare [major|minor|patch] [--dry-run]"
+ echo " $0 finalize [--dry-run]"
exit 1
fi
-if [ "$DRY_RUN" = true ]; then
- echo "[dry-run] - No changes will be made"
- echo ""
-else
- echo "Starting automated release process..."
+# For prepare command, default to patch if no version type specified
+if [ "$COMMAND" = "prepare" ]; then
+ VERSION_TYPE="${VERSION_TYPE:-patch}"
+ if [[ ! "$VERSION_TYPE" =~ ^(major|minor|patch)$ ]]; then
+ echo "Error: Version type must be 'major', 'minor', or 'patch'"
+ echo "Usage: $0 prepare [major|minor|patch] [--dry-run]"
+ exit 1
+ fi
fi
-# Check for uncommitted changes (skip in dry-run)
-if [ "$DRY_RUN" = false ] && ! git diff-index --quiet HEAD --; then
- echo "Error: You have uncommitted changes. Please commit or stash them first."
- exit 1
-fi
+# Common validation functions
+check_git_status() {
+ # Check for uncommitted changes (skip in dry-run)
+ if [ "$DRY_RUN" = false ] && ! git diff-index --quiet HEAD --; then
+ echo "Error: You have uncommitted changes. Please commit or stash them first."
+ exit 1
+ fi
+}
+
+check_master_branch() {
+ # Ensure we're on master branch
+ CURRENT_BRANCH=$(git branch --show-current)
+ if [ "$CURRENT_BRANCH" != "master" ]; then
+ if [ "$DRY_RUN" = true ]; then
+ echo "[dry run] Warning: Not on master branch (currently on: $CURRENT_BRANCH). Continuing with dry-run..."
+ echo ""
+ else
+ echo "Error: Must be on master branch. Currently on: $CURRENT_BRANCH"
+ exit 1
+ fi
+ fi
+}
+
+check_master_up_to_date() {
+ # Check if we have the latest from master (skip in dry-run)
+ if [ "$DRY_RUN" = false ]; then
+ echo "Checking if local master is up-to-date with remote..."
+ git fetch origin master
+ LOCAL=$(git rev-parse HEAD)
+ REMOTE=$(git rev-parse origin/master)
+
+ if [ "$LOCAL" != "$REMOTE" ]; then
+ echo "Error: Your local master branch is not up-to-date with origin/master."
+ echo "Please run 'git pull origin master' first."
+ exit 1
+ fi
+ echo "✓ Local master is up-to-date with remote"
+ echo ""
+ elif [ "$(git branch --show-current)" = "master" ]; then
+ echo "[dry run] Warning: Dry-run mode - not checking if master is up-to-date with remote"
+ echo ""
+ fi
+}
+
+prepare_release() {
+ if [ "$DRY_RUN" = true ]; then
+ echo "[dry-run] Preparing release (no changes will be made)"
+ else
+ echo "Starting release preparation..."
+ fi
+ echo ""
+
+ check_git_status
+ check_master_branch
+ check_master_up_to_date
+
+ # Extract current version from CMakeLists.txt
+ echo "Step 1: Reading current version..."
+ MAJOR=$(grep "set(GGML_VERSION_MAJOR" CMakeLists.txt | sed 's/.*MAJOR \([0-9]*\).*/\1/')
+ MINOR=$(grep "set(GGML_VERSION_MINOR" CMakeLists.txt | sed 's/.*MINOR \([0-9]*\).*/\1/')
+ PATCH=$(grep "set(GGML_VERSION_PATCH" CMakeLists.txt | sed 's/.*PATCH \([0-9]*\).*/\1/')
+
+ echo "Current version: $MAJOR.$MINOR.$PATCH-dev"
+
+ # Calculate new version
+ case $VERSION_TYPE in
+ major)
+ NEW_MAJOR=$((MAJOR + 1))
+ NEW_MINOR=0
+ NEW_PATCH=0
+ ;;
+ minor)
+ NEW_MAJOR=$MAJOR
+ NEW_MINOR=$((MINOR + 1))
+ NEW_PATCH=0
+ ;;
+ patch)
+ NEW_MAJOR=$MAJOR
+ NEW_MINOR=$MINOR
+ NEW_PATCH=$((PATCH + 1))
+ ;;
+ esac
+
+ NEW_VERSION="$NEW_MAJOR.$NEW_MINOR.$NEW_PATCH"
+ RC_BRANCH="ggml-rc-v$NEW_VERSION"
+ echo "New release version: $NEW_VERSION"
+ echo "Release candidate branch: $RC_BRANCH"
+ echo ""
-# Ensure we're on master branch
-CURRENT_BRANCH=$(git branch --show-current)
-if [ "$CURRENT_BRANCH" != "master" ]; then
+ # Create release candidate branch
+ echo "Step 2: Creating release candidate branch..."
if [ "$DRY_RUN" = true ]; then
- echo "[dry run] Warning: Not on master branch (currently on: $CURRENT_BRANCH). Continuing with dry-run..."
+ echo " [dry-run] Would create branch: $RC_BRANCH"
+ else
+ git checkout -b "$RC_BRANCH"
+ echo "✓ Created and switched to branch: $RC_BRANCH"
+ fi
+ echo ""
+
+ # Update CMakeLists.txt for release
+ echo "Step 3: Updating version in CMakeLists.txt..."
+ if [ "$DRY_RUN" = true ]; then
+ echo " [dry-run] Would update GGML_VERSION_MAJOR to $NEW_MAJOR"
+ echo " [dry-run] Would update GGML_VERSION_MINOR to $NEW_MINOR"
+ echo " [dry-run] Would update GGML_VERSION_PATCH to $NEW_PATCH"
+ echo " [dry-run] Would remove -dev suffix"
+ else
+ sed -i'' -e "s/set(GGML_VERSION_MAJOR [0-9]*)/set(GGML_VERSION_MAJOR $NEW_MAJOR)/" CMakeLists.txt
+ sed -i'' -e "s/set(GGML_VERSION_MINOR [0-9]*)/set(GGML_VERSION_MINOR $NEW_MINOR)/" CMakeLists.txt
+ sed -i'' -e "s/set(GGML_VERSION_PATCH [0-9]*)/set(GGML_VERSION_PATCH $NEW_PATCH)/" CMakeLists.txt
+ sed -i'' -e 's/set(GGML_VERSION_DEV "-dev")/set(GGML_VERSION_DEV "")/' CMakeLists.txt
+ fi
+ echo ""
+
+ # Commit version bump
+ echo "Step 4: Committing version bump..."
+ if [ "$DRY_RUN" = true ]; then
+ echo " [dry-run] Would commit: 'ggml : bump version to $NEW_VERSION'"
+ else
+ git add CMakeLists.txt
+ git commit -m "ggml : bump version to $NEW_VERSION"
+ fi
+ echo ""
+
+ echo ""
+ if [ "$DRY_RUN" = true ]; then
+ echo "[dry-run] Summary (no changes were made):"
+ echo " • Would have created branch: $RC_BRANCH"
+ echo " • Would have updated version to: $NEW_VERSION"
+ else
+ echo "Release preparation completed!"
+ echo "Summary:"
+ echo " • Created branch: $RC_BRANCH"
+ echo " • Updated version to: $NEW_VERSION"
echo ""
+ echo "Next steps:"
+ echo " • Push branch to remote: git push origin $RC_BRANCH"
+ echo " • Create a Pull Request from $RC_BRANCH to master"
+ echo " • After PR is merged, run: ./scripts/release.sh finalize"
+ fi
+}
+
+finalize_release() {
+ if [ "$DRY_RUN" = true ]; then
+ echo "[dry-run] Finalizing release (no changes will be made)"
else
- echo "Error: Must be on master branch to create release. Currently on: $CURRENT_BRANCH"
- exit 1
+ echo "Starting release finalization..."
fi
-fi
+ echo ""
-# Check if we have the latest from master (skip in dry-run)
-if [ "$DRY_RUN" = false ]; then
- echo "Checking if local master is up-to-date with remote..."
- git fetch origin master
- LOCAL=$(git rev-parse HEAD)
- REMOTE=$(git rev-parse origin/master)
+ check_git_status
+ check_master_branch
+ check_master_up_to_date
- if [ "$LOCAL" != "$REMOTE" ]; then
- echo "Error: Your local master branch is not up-to-date with origin/master."
- echo "Please run 'git pull origin master' first."
+ # Read current version from CMakeLists.txt (should not have -dev suffix)
+ echo "Step 1: Reading current release version..."
+ MAJOR=$(grep "set(GGML_VERSION_MAJOR" CMakeLists.txt | sed 's/.*MAJOR \([0-9]*\).*/\1/')
+ MINOR=$(grep "set(GGML_VERSION_MINOR" CMakeLists.txt | sed 's/.*MINOR \([0-9]*\).*/\1/')
+ PATCH=$(grep "set(GGML_VERSION_PATCH" CMakeLists.txt | sed 's/.*PATCH \([0-9]*\).*/\1/')
+ DEV_SUFFIX=$(grep "set(GGML_VERSION_DEV" CMakeLists.txt | sed 's/.*DEV "\([^"]*\)".*/\1/')
+
+ if [ "$DEV_SUFFIX" = "-dev" ]; then
+ echo "Error: Current version still has -dev suffix. Make sure the release candidate PR has been merged."
exit 1
fi
- echo "✓ Local master is up-to-date with remote"
+
+ RELEASE_VERSION="$MAJOR.$MINOR.$PATCH"
+ echo "Release version: $RELEASE_VERSION"
echo ""
-elif [ "$CURRENT_BRANCH" = "master" ]; then
- echo "[dry run] Warning: Dry-run mode - not checking if master is up-to-date with remote"
+
+ # Create git tag
+ echo "Step 2: Creating signed git tag..."
+ if [ "$DRY_RUN" = true ]; then
+ echo " [dry-run] Would create signed tag: v$RELEASE_VERSION with message 'Release version $RELEASE_VERSION'"
+ else
+ git tag -s "v$RELEASE_VERSION" -m "Release version $RELEASE_VERSION"
+ echo "✓ Created signed tag: v$RELEASE_VERSION"
+ fi
echo ""
-fi
-# Extract current version from CMakeLists.txt
-echo "Step 1: Reading current version..."
-MAJOR=$(grep "set(GGML_VERSION_MAJOR" CMakeLists.txt | sed 's/.*MAJOR \([0-9]*\).*/\1/')
-MINOR=$(grep "set(GGML_VERSION_MINOR" CMakeLists.txt | sed 's/.*MINOR \([0-9]*\).*/\1/')
-PATCH=$(grep "set(GGML_VERSION_PATCH" CMakeLists.txt | sed 's/.*PATCH \([0-9]*\).*/\1/')
+ # Create branch for next development version
+ DEV_BRANCH="ggml-dev-v$RELEASE_VERSION"
+ echo "Step 3: Creating development branch..."
+ if [ "$DRY_RUN" = true ]; then
+ echo " [dry-run] Would create branch: $DEV_BRANCH"
+ else
+ git checkout -b "$DEV_BRANCH"
+ echo "✓ Created and switched to branch: $DEV_BRANCH"
+ fi
+ echo ""
-echo "Current version: $MAJOR.$MINOR.$PATCH-dev"
+ # Add -dev suffix back (no version increment)
+ NEXT_DEV_VERSION="$RELEASE_VERSION-dev"
+ echo "Step 4: Adding -dev suffix for next development cycle ($NEXT_DEV_VERSION)..."
+ if [ "$DRY_RUN" = true ]; then
+ echo " [dry-run] Would add -dev suffix"
+ else
+ sed -i'' -e 's/set(GGML_VERSION_DEV "")/set(GGML_VERSION_DEV "-dev")/' CMakeLists.txt
+ fi
+ echo ""
-# Calculate new version
-case $VERSION_TYPE in
- major)
- NEW_MAJOR=$((MAJOR + 1))
- NEW_MINOR=0
- NEW_PATCH=0
- ;;
- minor)
- NEW_MAJOR=$MAJOR
- NEW_MINOR=$((MINOR + 1))
- NEW_PATCH=0
- ;;
- patch)
- NEW_MAJOR=$MAJOR
- NEW_MINOR=$MINOR
- NEW_PATCH=$((PATCH + 1))
- ;;
-esac
+ # Commit development version
+ echo "Step 5: Committing development version..."
+ if [ "$DRY_RUN" = true ]; then
+ echo " [dry-run] Would commit: 'ggml : prepare for development of $NEXT_DEV_VERSION'"
+ else
+ git add CMakeLists.txt
+ git commit -m "ggml : prepare for development of $NEXT_DEV_VERSION"
+ fi
+ echo ""
-NEW_VERSION="$NEW_MAJOR.$NEW_MINOR.$NEW_PATCH"
-RC_BRANCH="ggml-rc-v$NEW_VERSION"
-echo "New release version: $NEW_VERSION"
-echo "Release candidate branch: $RC_BRANCH"
-echo ""
-
-# Create release candidate branch
-echo "Step 2: Creating release candidate branch..."
-if [ "$DRY_RUN" = true ]; then
- echo " [dry-run] Would create branch: $RC_BRANCH"
-else
- git checkout -b "$RC_BRANCH"
- echo "✓ Created and switched to branch: $RC_BRANCH"
-fi
-echo ""
-
-# Update CMakeLists.txt for release
-echo "Step 3: Updating version in CMakeLists.txt..."
-if [ "$DRY_RUN" = true ]; then
- echo " [dry-run] Would update GGML_VERSION_MAJOR to $NEW_MAJOR"
- echo " [dry-run] Would update GGML_VERSION_MINOR to $NEW_MINOR"
- echo " [dry-run] Would update GGML_VERSION_PATCH to $NEW_PATCH"
- echo " [dry-run] Would remove -dev suffix"
-else
- sed -i'' -e "s/set(GGML_VERSION_MAJOR [0-9]*)/set(GGML_VERSION_MAJOR $NEW_MAJOR)/" CMakeLists.txt
- sed -i'' -e "s/set(GGML_VERSION_MINOR [0-9]*)/set(GGML_VERSION_MINOR $NEW_MINOR)/" CMakeLists.txt
- sed -i'' -e "s/set(GGML_VERSION_PATCH [0-9]*)/set(GGML_VERSION_PATCH $NEW_PATCH)/" CMakeLists.txt
- sed -i'' -e 's/set(GGML_VERSION_DEV "-dev")/set(GGML_VERSION_DEV "")/' CMakeLists.txt
-fi
-echo ""
-
-# Commit version bump
-echo "Step 4: Committing version bump..."
-if [ "$DRY_RUN" = true ]; then
- echo " [dry-run] Would commit: 'ggml : bump version to $NEW_VERSION'"
-else
- git add CMakeLists.txt
- git commit -m "ggml : bump version to $NEW_VERSION"
-fi
-echo ""
-
-# Create git tag
-echo "Step 5: Creating signed git tag..."
-if [ "$DRY_RUN" = true ]; then
- echo " [dry-run] Would create signed tag: v$NEW_VERSION with message 'Release version $NEW_VERSION'"
-else
- git tag -s "v$NEW_VERSION" -m "Release version $NEW_VERSION"
- echo "✓ Created signed tag: v$NEW_VERSION"
-fi
-echo ""
-
-# Prepare for next development cycle
-echo "Step 6: Preparing for next development cycle..."
-case $VERSION_TYPE in
- major|minor)
- NEXT_DEV_MINOR=$((NEW_MINOR))
- NEXT_DEV_VERSION="$NEW_MAJOR.$NEXT_DEV_MINOR.0-dev"
- if [ "$DRY_RUN" = true ]; then
- echo " [dry-run] Would update GGML_VERSION_MINOR to $NEXT_DEV_MINOR"
- else
- sed -i'' -e "s/set(GGML_VERSION_MINOR [0-9]*)/set(GGML_VERSION_MINOR $NEXT_DEV_MINOR)/" CMakeLists.txt
- fi
+ echo ""
+ if [ "$DRY_RUN" = true ]; then
+ echo "[dry-run] Summary (no changes were made):"
+ echo " • Would have created tag: v$RELEASE_VERSION"
+ echo " • Would have created branch: $DEV_BRANCH"
+ echo " • Would have prepared next development version: $NEXT_DEV_VERSION"
+ else
+ echo "Release finalization completed!"
+ echo "Summary:"
+ echo " • Created signed tag: v$RELEASE_VERSION"
+ echo " • Created branch: $DEV_BRANCH"
+ echo " • Prepared next development version: $NEXT_DEV_VERSION"
+ echo ""
+ echo "Next steps:"
+ echo " • Push tag to remote: git push origin v$RELEASE_VERSION"
+ echo " • Push development branch: git push origin $DEV_BRANCH"
+ echo " • Create PR for development version manually"
+ echo " • The release is now complete!"
+ fi
+}
+
+# Execute the appropriate command
+case $COMMAND in
+ prepare)
+ prepare_release
;;
- patch)
- NEXT_DEV_PATCH=$((NEW_PATCH))
- NEXT_DEV_VERSION="$NEW_MAJOR.$NEW_MINOR.$NEXT_DEV_PATCH-dev"
- if [ "$DRY_RUN" = true ]; then
- echo " [dry-run] Would update GGML_VERSION_PATCH to $NEXT_DEV_PATCH"
- else
- sed -i'' -e "s/set(GGML_VERSION_PATCH [0-9]*)/set(GGML_VERSION_PATCH $NEXT_DEV_PATCH)/" CMakeLists.txt
- fi
+ finalize)
+ finalize_release
;;
esac
-
-if [ "$DRY_RUN" = true ]; then
- echo " [dry-run] Would add -dev suffix back"
-else
- sed -i'' -e 's/set(GGML_VERSION_DEV "")/set(GGML_VERSION_DEV "-dev")/' CMakeLists.txt
-fi
-echo ""
-
-# Commit development version
-echo "Step 7: Committing development version..."
-if [ "$DRY_RUN" = true ]; then
- echo " [dry-run] Would commit: 'ggml : prepare for development of $NEXT_DEV_VERSION'"
-else
- git add CMakeLists.txt
- git commit -m "ggml : prepare for development of $NEXT_DEV_VERSION"
-fi
-
-echo ""
-if [ "$DRY_RUN" = true ]; then
- echo "[dry-run] Summary (no changes were made):"
- echo " • Would have created branch: $RC_BRANCH"
- echo " • Would have created tag: v$NEW_VERSION"
- echo " • Would have set next development version: $NEXT_DEV_VERSION"
-else
- echo "Release process completed!"
- echo "Summary:"
- echo " • Created branch: $RC_BRANCH"
- echo " • Created tag: v$NEW_VERSION"
- echo " • Next development version: $NEXT_DEV_VERSION"
-fi
-if [ "$DRY_RUN" = false ]; then
- echo "Next steps:"
- echo " • Review the commits and tag on branch $RC_BRANCH"
- echo " • Push branch to remote: git push origin $RC_BRANCH"
- echo " • Create a Pull Request from $RC_BRANCH to master"
- echo " • After PR is merged, push the tag: git push origin v$NEW_VERSION"
- echo " • The release will be completed once the tag is pushed"
- echo ""
-fi