From 3e1135ed2b6c97800a446f5d1ec5341a0c4c84a2 Mon Sep 17 00:00:00 2001 From: Rob Colbert Date: Sun, 17 May 2026 18:32:53 -0400 Subject: [PATCH] feat: add hardened release script with error handling and pre-flight checks --- release | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100755 release diff --git a/release b/release new file mode 100755 index 0000000..9b6045a --- /dev/null +++ b/release @@ -0,0 +1,89 @@ +#!/bin/bash +set -euo pipefail + +MAIN_BRANCH="master" +DEVELOP_BRANCH="develop" +VALID_LEVELS="major minor patch" + +# ── Validation ──────────────────────────────────────────────────────────────── +if [ -z "${1:-}" ]; then + echo "ERROR: Must specify the release level: major, minor, patch" >&2 + exit 1 +fi + +RELEASE_LEVEL="$1" + +if ! echo "$VALID_LEVELS" | grep -qw "$RELEASE_LEVEL"; then + echo "ERROR: Invalid release level '$RELEASE_LEVEL'. Must be one of: $VALID_LEVELS" >&2 + exit 1 +fi + +# ── Pre-flight checks ──────────────────────────────────────────────────────── +BRANCH=$(git rev-parse --abbrev-ref HEAD) + +# Ensure we're in a git repo +if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then + echo "ERROR: Not inside a git repository" >&2 + exit 1 +fi + +# Check for a clean working tree +if ! git diff --quiet || ! git diff --cached --quiet; then + echo "ERROR: Working tree has uncommitted changes. Commit or stash before releasing." >&2 + git status --short >&2 + exit 1 +fi + +# Verify both remotes exist +for remote in origin dtp; do + if ! git remote get-url "$remote" >/dev/null 2>&1; then + echo "ERROR: Git remote '$remote' not found" >&2 + exit 1 + fi +done + +# Verify branches exist locally +for branch in "$MAIN_BRANCH" "$DEVELOP_BRANCH"; do + if ! git rev-parse --verify "$branch" >/dev/null 2>&1; then + echo "ERROR: Branch '$branch' does not exist locally" >&2 + exit 1 + fi +done + +# ── Save starting branch for rollback ──────────────────────────────────────── +STARTING_BRANCH="$BRANCH" + +# ── Step 1: Update develop and bump version ────────────────────────────────── +echo ">>> Switching to $DEVELOP_BRANCH..." +git checkout "$DEVELOP_BRANCH" + +echo ">>> Bumping version ($RELEASE_LEVEL)..." +NEW_VERSION=$(npm version "$RELEASE_LEVEL" -m "release: %s") +echo " Version bumped to $NEW_VERSION" + +# ── Step 2: Push develop + tags to both remotes ────────────────────────────── +echo ">>> Pushing $DEVELOP_BRANCH to origin..." +git push origin "$DEVELOP_BRANCH" --follow-tags + +echo ">>> Pushing $DEVELOP_BRANCH to dtp..." +git push dtp "$DEVELOP_BRANCH" --follow-tags + +# ── Step 3: Merge develop into master ──────────────────────────────────────── +echo ">>> Switching to $MAIN_BRANCH..." +git checkout "$MAIN_BRANCH" + +echo ">>> Merging $DEVELOP_BRANCH into $MAIN_BRANCH..." +git merge "$DEVELOP_BRANCH" + +# ── Step 4: Push master + tags to both remotes ─────────────────────────────── +echo ">>> Pushing $MAIN_BRANCH to origin..." +git push origin "$MAIN_BRANCH" --follow-tags + +echo ">>> Pushing $MAIN_BRANCH to dtp..." +git push dtp "$MAIN_BRANCH" --follow-tags + +# ── Step 5: Return to develop ──────────────────────────────────────────────── +echo ">>> Switching back to $DEVELOP_BRANCH..." +git checkout "$DEVELOP_BRANCH" + +echo ">>> Release $NEW_VERSION complete!"