|
| 1 | +#!/bin/bash |
| 2 | +# |
| 3 | +# Bump CLI Version - Automated Version Management |
| 4 | +# |
| 5 | +# This script automatically bumps the version in both: |
| 6 | +# 1. VERSION_CLI file |
| 7 | +# 2. workspace.package.version in Cargo.toml |
| 8 | +# |
| 9 | +# Usage: |
| 10 | +# ./scripts/bump-version.sh patch # 0.0.6 -> 0.0.7 |
| 11 | +# ./scripts/bump-version.sh minor # 0.0.6 -> 0.1.0 |
| 12 | +# ./scripts/bump-version.sh major # 0.0.6 -> 1.0.0 |
| 13 | +# ./scripts/bump-version.sh 1.2.3 # Set exact version |
| 14 | +# ./scripts/bump-version.sh 1.0.0-beta.1 # Prerelease version |
| 15 | +# |
| 16 | +# Options: |
| 17 | +# --dry-run Show what would be changed without making changes |
| 18 | +# --help Show this help message |
| 19 | +# |
| 20 | + |
| 21 | +set -euo pipefail |
| 22 | + |
| 23 | +# Get the repository root |
| 24 | +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" |
| 25 | + |
| 26 | +# Colors for output |
| 27 | +RED='\033[0;31m' |
| 28 | +GREEN='\033[0;32m' |
| 29 | +YELLOW='\033[1;33m' |
| 30 | +BLUE='\033[0;34m' |
| 31 | +NC='\033[0m' # No Color |
| 32 | + |
| 33 | +# Files to update |
| 34 | +VERSION_FILE="$REPO_ROOT/VERSION_CLI" |
| 35 | +CARGO_TOML="$REPO_ROOT/Cargo.toml" |
| 36 | + |
| 37 | +DRY_RUN=false |
| 38 | +BUMP_TYPE="" |
| 39 | + |
| 40 | +# Parse options |
| 41 | +while [[ $# -gt 0 ]]; do |
| 42 | + case $1 in |
| 43 | + --dry-run) |
| 44 | + DRY_RUN=true |
| 45 | + shift |
| 46 | + ;; |
| 47 | + --help|-h) |
| 48 | + echo "Usage: $0 [options] <patch|minor|major|X.Y.Z>" |
| 49 | + echo "" |
| 50 | + echo "Bump the CLI version automatically." |
| 51 | + echo "" |
| 52 | + echo "Arguments:" |
| 53 | + echo " patch Increment patch version (0.0.6 -> 0.0.7)" |
| 54 | + echo " minor Increment minor version (0.0.6 -> 0.1.0)" |
| 55 | + echo " major Increment major version (0.0.6 -> 1.0.0)" |
| 56 | + echo " X.Y.Z Set exact version (e.g., 1.2.3 or 1.0.0-beta.1)" |
| 57 | + echo "" |
| 58 | + echo "Prerelease versions:" |
| 59 | + echo " Supports semver prerelease format: X.Y.Z-<prerelease>" |
| 60 | + echo " Examples: 1.0.0-alpha, 1.0.0-beta.1, 2.0.0-rc.2" |
| 61 | + echo "" |
| 62 | + echo "Options:" |
| 63 | + echo " --dry-run Show what would be changed without making changes" |
| 64 | + echo " --help Show this help message" |
| 65 | + echo "" |
| 66 | + echo "Examples:" |
| 67 | + echo " $0 patch # Bump patch version" |
| 68 | + echo " $0 minor # Bump minor version" |
| 69 | + echo " $0 major # Bump major version" |
| 70 | + echo " $0 2.0.0 # Set version to 2.0.0" |
| 71 | + echo " $0 1.0.0-beta.1 # Set prerelease version" |
| 72 | + echo " $0 --dry-run patch # Preview patch bump" |
| 73 | + exit 0 |
| 74 | + ;; |
| 75 | + -*) |
| 76 | + echo -e "${RED}ERROR: Unknown option: $1${NC}" |
| 77 | + echo "Use --help for usage information" |
| 78 | + exit 1 |
| 79 | + ;; |
| 80 | + *) |
| 81 | + if [ -n "$BUMP_TYPE" ]; then |
| 82 | + echo -e "${RED}ERROR: Multiple version arguments provided: '$BUMP_TYPE' and '$1'${NC}" |
| 83 | + echo "Only one version argument (patch, minor, major, or X.Y.Z) is allowed" |
| 84 | + exit 1 |
| 85 | + fi |
| 86 | + BUMP_TYPE="$1" |
| 87 | + shift |
| 88 | + ;; |
| 89 | + esac |
| 90 | +done |
| 91 | + |
| 92 | +if [ -z "$BUMP_TYPE" ]; then |
| 93 | + echo -e "${RED}ERROR: Please specify bump type (patch, minor, major) or exact version (X.Y.Z)${NC}" |
| 94 | + echo "Use --help for usage information" |
| 95 | + exit 1 |
| 96 | +fi |
| 97 | + |
| 98 | +# Read current version |
| 99 | +if [ ! -f "$VERSION_FILE" ]; then |
| 100 | + echo -e "${RED}ERROR: VERSION_CLI file not found at $VERSION_FILE${NC}" |
| 101 | + exit 1 |
| 102 | +fi |
| 103 | + |
| 104 | +CURRENT_VERSION=$(cat "$VERSION_FILE" | tr -d '[:space:]') |
| 105 | +echo -e "${BLUE}Current version:${NC} $CURRENT_VERSION" |
| 106 | + |
| 107 | +# Strip prerelease suffix for parsing (e.g., 1.0.0-beta.1 -> 1.0.0) |
| 108 | +BASE_VERSION="${CURRENT_VERSION%%-*}" |
| 109 | + |
| 110 | +# Parse version components |
| 111 | +IFS='.' read -r MAJOR MINOR PATCH <<< "$BASE_VERSION" |
| 112 | + |
| 113 | +# Validate current version format |
| 114 | +if ! [[ "$MAJOR" =~ ^[0-9]+$ ]] || ! [[ "$MINOR" =~ ^[0-9]+$ ]] || ! [[ "$PATCH" =~ ^[0-9]+$ ]]; then |
| 115 | + echo -e "${RED}ERROR: Invalid current version format: $CURRENT_VERSION${NC}" |
| 116 | + echo "Expected base format: X.Y.Z (e.g., 0.0.6 or 1.0.0-beta.1)" |
| 117 | + exit 1 |
| 118 | +fi |
| 119 | + |
| 120 | +# Version comparison function |
| 121 | +# Returns: 0 if equal, 1 if v1 > v2, 2 if v1 < v2 |
| 122 | +compare_versions() { |
| 123 | + local v1_base="${1%%-*}" |
| 124 | + local v2_base="${2%%-*}" |
| 125 | + |
| 126 | + local v1_major v1_minor v1_patch |
| 127 | + local v2_major v2_minor v2_patch |
| 128 | + |
| 129 | + IFS='.' read -r v1_major v1_minor v1_patch <<< "$v1_base" |
| 130 | + IFS='.' read -r v2_major v2_minor v2_patch <<< "$v2_base" |
| 131 | + |
| 132 | + if [ "$v1_major" -gt "$v2_major" ]; then return 1; fi |
| 133 | + if [ "$v1_major" -lt "$v2_major" ]; then return 2; fi |
| 134 | + if [ "$v1_minor" -gt "$v2_minor" ]; then return 1; fi |
| 135 | + if [ "$v1_minor" -lt "$v2_minor" ]; then return 2; fi |
| 136 | + if [ "$v1_patch" -gt "$v2_patch" ]; then return 1; fi |
| 137 | + if [ "$v1_patch" -lt "$v2_patch" ]; then return 2; fi |
| 138 | + return 0 |
| 139 | +} |
| 140 | + |
| 141 | +# Calculate new version |
| 142 | +case $BUMP_TYPE in |
| 143 | + patch) |
| 144 | + NEW_VERSION="$MAJOR.$MINOR.$((PATCH + 1))" |
| 145 | + ;; |
| 146 | + minor) |
| 147 | + NEW_VERSION="$MAJOR.$((MINOR + 1)).0" |
| 148 | + ;; |
| 149 | + major) |
| 150 | + NEW_VERSION="$((MAJOR + 1)).0.0" |
| 151 | + ;; |
| 152 | + *) |
| 153 | + # Assume it's an exact version - validate format |
| 154 | + if [[ "$BUMP_TYPE" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?$ ]]; then |
| 155 | + NEW_VERSION="$BUMP_TYPE" |
| 156 | + # Check for potential version downgrade |
| 157 | + cmp_result=0 |
| 158 | + compare_versions "$CURRENT_VERSION" "$NEW_VERSION" || cmp_result=$? |
| 159 | + if [ $cmp_result -eq 1 ]; then |
| 160 | + echo -e "${YELLOW}⚠ WARNING: You are DOWNGRADING from $CURRENT_VERSION to $NEW_VERSION${NC}" |
| 161 | + fi |
| 162 | + else |
| 163 | + echo -e "${RED}ERROR: Invalid version format: $BUMP_TYPE${NC}" |
| 164 | + echo "Expected: patch, minor, major, or X.Y.Z (e.g., 1.2.3 or 1.0.0-beta.1)" |
| 165 | + exit 1 |
| 166 | + fi |
| 167 | + ;; |
| 168 | +esac |
| 169 | + |
| 170 | +echo -e "${BLUE}New version:${NC} $NEW_VERSION" |
| 171 | +echo "" |
| 172 | + |
| 173 | +if [ "$DRY_RUN" = true ]; then |
| 174 | + echo -e "${YELLOW}=== DRY RUN MODE - No changes will be made ===${NC}" |
| 175 | + echo "" |
| 176 | +fi |
| 177 | + |
| 178 | +# Update VERSION_CLI |
| 179 | +echo -e "${BLUE}Updating VERSION_CLI...${NC}" |
| 180 | +if [ "$DRY_RUN" = true ]; then |
| 181 | + echo " Would write '$NEW_VERSION' to $VERSION_FILE" |
| 182 | +else |
| 183 | + # Use atomic file operations: write to temp file first, then move |
| 184 | + VERSION_FILE_TMP=$(mktemp "${VERSION_FILE}.tmp.XXXXXX") |
| 185 | + echo "$NEW_VERSION" > "$VERSION_FILE_TMP" |
| 186 | + |
| 187 | + # Verify the temp file was written correctly |
| 188 | + if [ ! -s "$VERSION_FILE_TMP" ] || [ "$(cat "$VERSION_FILE_TMP")" != "$NEW_VERSION" ]; then |
| 189 | + rm -f "$VERSION_FILE_TMP" 2>/dev/null || true |
| 190 | + echo -e "${RED}ERROR: Failed to write VERSION_CLI temp file${NC}" |
| 191 | + exit 1 |
| 192 | + fi |
| 193 | + |
| 194 | + # Atomically replace the original file |
| 195 | + mv "$VERSION_FILE_TMP" "$VERSION_FILE" |
| 196 | + echo -e " ${GREEN}✓ Updated VERSION_CLI${NC}" |
| 197 | +fi |
| 198 | + |
| 199 | +# Update Cargo.toml workspace version |
| 200 | +echo -e "${BLUE}Updating Cargo.toml workspace version...${NC}" |
| 201 | + |
| 202 | +# Use awk to update the version in [workspace.package] section |
| 203 | +if [ "$DRY_RUN" = true ]; then |
| 204 | + echo " Would update version to \"$NEW_VERSION\" in $CARGO_TOML" |
| 205 | +else |
| 206 | + # Use atomic file operations: write to temp file first, then move |
| 207 | + CARGO_TOML_TMP=$(mktemp "${CARGO_TOML}.tmp.XXXXXX") |
| 208 | + |
| 209 | + # Cleanup function for trap |
| 210 | + cleanup() { |
| 211 | + rm -f "$CARGO_TOML_TMP" 2>/dev/null || true |
| 212 | + } |
| 213 | + trap cleanup EXIT |
| 214 | + |
| 215 | + # Use awk to update the version only in [workspace.package] section |
| 216 | + awk -v new_ver="$NEW_VERSION" ' |
| 217 | + /^\[workspace\.package\]/ { in_section = 1 } |
| 218 | + /^\[/ && !/^\[workspace\.package\]/ { in_section = 0 } |
| 219 | + in_section && /^version[[:space:]]*=/ { |
| 220 | + sub(/"[^"]*"/, "\"" new_ver "\"") |
| 221 | + } |
| 222 | + { print } |
| 223 | + ' "$CARGO_TOML" > "$CARGO_TOML_TMP" |
| 224 | + |
| 225 | + # Verify the temp file is not empty and has expected content |
| 226 | + if [ ! -s "$CARGO_TOML_TMP" ]; then |
| 227 | + echo -e "${RED}ERROR: Failed to update Cargo.toml - awk produced empty output${NC}" |
| 228 | + exit 1 |
| 229 | + fi |
| 230 | + |
| 231 | + # Verify the new version is in the temp file |
| 232 | + if ! grep -q "version = \"$NEW_VERSION\"" "$CARGO_TOML_TMP"; then |
| 233 | + echo -e "${RED}ERROR: Failed to update version in Cargo.toml${NC}" |
| 234 | + exit 1 |
| 235 | + fi |
| 236 | + |
| 237 | + # Atomically replace the original file |
| 238 | + mv "$CARGO_TOML_TMP" "$CARGO_TOML" |
| 239 | + |
| 240 | + # Clear trap since file was successfully moved |
| 241 | + trap - EXIT |
| 242 | + |
| 243 | + echo -e " ${GREEN}✓ Updated Cargo.toml${NC}" |
| 244 | +fi |
| 245 | + |
| 246 | +echo "" |
| 247 | + |
| 248 | +# Verify consistency |
| 249 | +echo -e "${BLUE}Verifying version consistency...${NC}" |
| 250 | +if [ "$DRY_RUN" = true ]; then |
| 251 | + echo " Would run: ./scripts/check-cli-version.sh" |
| 252 | +else |
| 253 | + if "$REPO_ROOT/scripts/check-cli-version.sh"; then |
| 254 | + echo "" |
| 255 | + echo -e "${GREEN}=== Version bump complete! ===${NC}" |
| 256 | + echo "" |
| 257 | + echo "Next steps:" |
| 258 | + echo " 1. Review the changes: git diff" |
| 259 | + echo " 2. Commit: git commit -am \"chore: bump version to $NEW_VERSION\"" |
| 260 | + echo " 3. Tag for release: git tag v$NEW_VERSION" |
| 261 | + echo " 4. Push: git push && git push --tags" |
| 262 | + else |
| 263 | + echo "" |
| 264 | + echo -e "${RED}ERROR: Version consistency check failed!${NC}" |
| 265 | + exit 1 |
| 266 | + fi |
| 267 | +fi |
0 commit comments