Skip to content

Commit e94c3ed

Browse files
committed
Add release_gem script
1 parent 51d9ad3 commit e94c3ed

1 file changed

Lines changed: 202 additions & 0 deletions

File tree

‎release_gem.sh‎

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
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

Comments
 (0)