|
| 1 | +#!/usr/bin/env bash |
| 2 | +set -euo pipefail |
| 3 | + |
| 4 | +############################################ |
| 5 | +# Config — tweak if your gem/repo differs |
| 6 | +############################################ |
| 7 | +GEM_NAME="log_bench" |
| 8 | +DEFAULT_BRANCH="main" |
| 9 | +VERSION_FILE="lib/${GEM_NAME}/version.rb" |
| 10 | +GEMSPEC_FILE="${GEM_NAME}.gemspec" |
| 11 | +TAG_PREFIX="v" |
| 12 | + |
| 13 | +# Bump mode: patch | minor | major (default patch) |
| 14 | +BUMP="${BUMP:-patch}" |
| 15 | + |
| 16 | +############################################ |
| 17 | +# Arg parsing (optional) |
| 18 | +############################################ |
| 19 | +while [[ $# -gt 0 ]]; do |
| 20 | + case "$1" in |
| 21 | + --bump) BUMP="${2:-patch}"; shift 2 ;; |
| 22 | + --bump=*) BUMP="${1#*=}"; shift 1 ;; |
| 23 | + -h|--help) |
| 24 | + cat <<EOF |
| 25 | +Usage: $0 [--bump patch|minor|major] |
| 26 | +Defaults to --bump patch (e.g., 0.2.6 -> 0.2.7). |
| 27 | +EOF |
| 28 | + exit 0 ;; |
| 29 | + *) echo "Unknown arg: $1" >&2; exit 1 ;; |
| 30 | + esac |
| 31 | +done |
| 32 | + |
| 33 | +case "$BUMP" in |
| 34 | + patch|minor|major) : ;; |
| 35 | + *) echo "Invalid --bump: $BUMP (use patch|minor|major)"; exit 1 ;; |
| 36 | +esac |
| 37 | + |
| 38 | +############################################ |
| 39 | +# Pretty logging |
| 40 | +############################################ |
| 41 | +if command -v tput >/dev/null 2>&1 && [ -n "${TERM:-}" ]; then |
| 42 | + BOLD=$(tput bold) || BOLD="" |
| 43 | + RESET=$(tput sgr0) || RESET="" |
| 44 | + GREEN=$(tput setaf 2) || GREEN="" |
| 45 | + YELLOW=$(tput setaf 3) || YELLOW="" |
| 46 | + RED=$(tput setaf 1) || RED="" |
| 47 | + BLUE=$(tput setaf 4) || BLUE="" |
| 48 | +else |
| 49 | + BOLD=""; RESET=""; GREEN=""; YELLOW=""; RED=""; BLUE="" |
| 50 | +fi |
| 51 | + |
| 52 | +log() { printf "%b\n" "${BLUE}▸${RESET} $*"; } |
| 53 | +success() { printf "%b\n" "${GREEN}✔${RESET} $*"; } |
| 54 | +warn() { printf "%b\n" "${YELLOW}⚠${RESET} $*"; } |
| 55 | +error() { printf "%b\n" "${RED}✖${RESET} $*"; } |
| 56 | +die() { error "$*"; exit 1; } |
| 57 | + |
| 58 | +############################################ |
| 59 | +# Helpers |
| 60 | +############################################ |
| 61 | +require_cmd() { command -v "$1" >/dev/null 2>&1 || die "Missing required command: $1"; } |
| 62 | + |
| 63 | +current_version() { |
| 64 | + # Extract the first X.Y.Z from the VERSION file |
| 65 | + grep -Eo '[0-9]+\.[0-9]+\.[0-9]+' "$VERSION_FILE" | head -n1 |
| 66 | +} |
| 67 | + |
| 68 | +validate_semver() { [[ "$1" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; } |
| 69 | + |
| 70 | +bump_version() { |
| 71 | + local mode="$1" ver="$2" |
| 72 | + IFS='.' read -r MA MI PA <<<"$ver" |
| 73 | + case "$mode" in |
| 74 | + patch) echo "${MA}.${MI}.$((PA+1))" ;; |
| 75 | + minor) echo "${MA}.$((MI+1)).0" ;; |
| 76 | + major) echo "$((MA+1)).0.0" ;; |
| 77 | + esac |
| 78 | +} |
| 79 | + |
| 80 | +update_version_file() { |
| 81 | + local new="$1" |
| 82 | + # Robust sed (preserves indentation, quote style, optional .freeze) |
| 83 | + sed -E "s/^([[:space:]]*)VERSION[[:space:]]*=[[:space:]]*(['\"])[0-9]+\.[0-9]+\.[0-9]+(['\"])(\.freeze)?/\1VERSION = \2${new}\3\4/" \ |
| 84 | + "$VERSION_FILE" > "${VERSION_FILE}.tmp" |
| 85 | + mv "${VERSION_FILE}.tmp" "$VERSION_FILE" |
| 86 | +} |
| 87 | + |
| 88 | +ensure_clean_worktree() { |
| 89 | + if [ -n "$(git status --porcelain)" ]; then |
| 90 | + warn "Your working tree has uncommitted changes:" |
| 91 | + git status --short |
| 92 | + read -rp "$(printf '%b' "${YELLOW}Proceed anyway? [y/N] ${RESET}")" ans |
| 93 | + [[ "${ans:-}" =~ ^[Yy]$ ]] || die "Aborted due to dirty working tree." |
| 94 | + fi |
| 95 | +} |
| 96 | + |
| 97 | +############################################ |
| 98 | +# Checks |
| 99 | +############################################ |
| 100 | +require_cmd git |
| 101 | +require_cmd bundle |
| 102 | +require_cmd gem |
| 103 | +require_cmd sed |
| 104 | + |
| 105 | +if ! command -v gh >/dev/null 2>&1; then |
| 106 | + warn "GitHub CLI (gh) not found. GitHub release creation will be skipped." |
| 107 | + GH_AVAILABLE="false" |
| 108 | +else |
| 109 | + GH_AVAILABLE="true" |
| 110 | +fi |
| 111 | + |
| 112 | +[ -f "$VERSION_FILE" ] || die "Version file not found: $VERSION_FILE" |
| 113 | +[ -f "$GEMSPEC_FILE" ] || die "Gemspec not found: $GEMSPEC_FILE" |
| 114 | + |
| 115 | +############################################ |
| 116 | +# Start |
| 117 | +############################################ |
| 118 | +log "${BOLD}Publishing ${GEM_NAME}${RESET}" |
| 119 | +ensure_clean_worktree |
| 120 | + |
| 121 | +log "Pulling latest from origin/${DEFAULT_BRANCH}…" |
| 122 | +git checkout "$DEFAULT_BRANCH" >/dev/null 2>&1 || true |
| 123 | +git pull origin "$DEFAULT_BRANCH" |
| 124 | +success "Up to date." |
| 125 | + |
| 126 | +CURR_VER="$(current_version)" |
| 127 | +[ -n "$CURR_VER" ] || die "Could not determine current version from $VERSION_FILE" |
| 128 | + |
| 129 | +log "Current version detected in ${VERSION_FILE}: ${BOLD}${CURR_VER}${RESET}" |
| 130 | +PROPOSED="$(bump_version "$BUMP" "$CURR_VER")" |
| 131 | +read -rp "$(printf '%b' "${BLUE}Proposed next ${BOLD}${BUMP}${RESET}${BLUE} version is ${BOLD}${PROPOSED}${RESET}${BLUE}. Press Enter to accept or type a different semver (X.Y.Z): ${RESET}")" NEW_VER |
| 132 | +NEW_VER="${NEW_VER:-$PROPOSED}" |
| 133 | +validate_semver "$NEW_VER" || die "Invalid semver: $NEW_VER" |
| 134 | + |
| 135 | +TAG="${TAG_PREFIX}${NEW_VER}" |
| 136 | + |
| 137 | +# Guard against existing tag |
| 138 | +if git rev-parse -q --verify "refs/tags/${TAG}" >/dev/null; then |
| 139 | + die "Tag ${TAG} already exists." |
| 140 | +fi |
| 141 | + |
| 142 | +log "Updating version file to ${BOLD}${NEW_VER}${RESET}…" |
| 143 | +before_contents="$(cat "$VERSION_FILE")" |
| 144 | +update_version_file "$NEW_VER" |
| 145 | +after_contents="$(cat "$VERSION_FILE")" |
| 146 | + |
| 147 | +if [ "$before_contents" = "$after_contents" ]; then |
| 148 | + AFTER_VER="$(current_version || true)" |
| 149 | + if [ "$AFTER_VER" = "$NEW_VER" ]; then |
| 150 | + warn "Version file already at ${NEW_VER}; nothing to change." |
| 151 | + else |
| 152 | + die "Failed to update ${VERSION_FILE}. Ensure it has a line like: |
| 153 | + VERSION = \"${CURR_VER}\" |
| 154 | +(or with single quotes / optional .freeze)." |
| 155 | + fi |
| 156 | +else |
| 157 | + success "Updated ${VERSION_FILE}." |
| 158 | +fi |
| 159 | + |
| 160 | +log "Installing gems (bundle install)…" |
| 161 | +bundle install |
| 162 | +success "Dependencies installed." |
| 163 | + |
| 164 | +log "Committing version bump…" |
| 165 | +# Only commit if VERSION_FILE actually changed |
| 166 | +if git diff --quiet -- "$VERSION_FILE"; then |
| 167 | + warn "No changes to commit (version file already at ${NEW_VER})." |
| 168 | +else |
| 169 | + git add "$VERSION_FILE" |
| 170 | + git commit -m "Bump version to ${NEW_VER}" |
| 171 | + success "Committed." |
| 172 | +fi |
| 173 | + |
| 174 | +log "Creating tag ${BOLD}${TAG}${RESET}…" |
| 175 | +git tag "${TAG}" -m "Release version ${NEW_VER}" |
| 176 | +success "Tag created." |
| 177 | + |
| 178 | +log "Pushing branch & tag to origin…" |
| 179 | +git push origin "$DEFAULT_BRANCH" |
| 180 | +git push origin "${TAG}" |
| 181 | +success "Pushed." |
| 182 | + |
| 183 | +if [ "$GH_AVAILABLE" = "true" ]; then |
| 184 | + log "Creating GitHub release ${BOLD}${TAG}${RESET} with auto-generated notes…" |
| 185 | + gh release create "${TAG}" --title "Release version ${NEW_VER}" --generate-notes || warn "Failed to create GitHub release via gh." |
| 186 | + success "GitHub release created." |
| 187 | +else |
| 188 | + warn "Skipping GitHub release creation (gh not installed)." |
| 189 | +fi |
| 190 | + |
| 191 | +log "Building gem…" |
| 192 | +gem build "$GEMSPEC_FILE" |
| 193 | +GEM_FILE="${GEM_NAME}-${NEW_VER}.gem" |
| 194 | +[ -f "$GEM_FILE" ] || die "Gem file not found after build: $GEM_FILE" |
| 195 | +success "Built ${GEM_FILE}." |
| 196 | + |
| 197 | +log "Pushing gem to RubyGems…" |
| 198 | +gem push "$GEM_FILE" |
| 199 | +success "Gem pushed to RubyGems." |
| 200 | + |
| 201 | +success "${BOLD}${GEM_NAME} ${NEW_VER}${RESET} has been published! 🎉" |
| 202 | + |
0 commit comments