#!/usr/bin/env bash set -euo pipefail readonly ALLOWED_MAIN_BRANCHES=("main" "master" "develop") readonly BRANCH_TYPES=( "feat For new features" "hotfix For urgent fixes" "fix For fixes" "release For preparing releases" "chore For non-code tasks" ) error() { echo "Error: $1" >&2 exit 1 } warn() { echo "Warning: $1" >&2 } check_dependencies() { local missing=() for cmd in git fzf; do if ! command -v "$cmd" &> /dev/null; then missing+=("$cmd") fi done if [[ ${#missing[@]} -gt 0 ]]; then error "Missing required commands: ${missing[*]}" fi } check_git_repo() { if ! git rev-parse --git-dir &> /dev/null; then error "Not in a git repository" fi } check_current_branch() { local current_branch current_branch=$(git branch --show-current) local is_main_branch=false for branch in "${ALLOWED_MAIN_BRANCHES[@]}"; do if [[ "$current_branch" == "$branch" ]]; then is_main_branch=true break fi done if [[ "$is_main_branch" == false ]]; then warn "Not branching from a main branch (current: $current_branch)" read -rp "Continue anyway? [y/N] " response if [[ ! "$response" =~ ^[Yy]$ ]]; then exit 0 fi fi } get_user_email() { local email email=$(git config --get user.email 2>/dev/null) if [[ -z "$email" ]]; then error "Git user email not configured. Run: git config user.email 'your@email.com'" fi echo "$email" } select_branch_type() { local selected selected=$(printf '%s\n' "${BRANCH_TYPES[@]}" | \ fzf --prompt="Select branch type: " \ --height=40% \ --border \ --info=inline) || error "Branch type selection cancelled" echo "${selected%% *}" } select_jira_ticket() { local email=$1 if ! command -v jira &> /dev/null; then warn "Jira CLI not found. Proceeding without ticket ID." return 0 fi echo "Fetching Jira tickets for $email..." >&2 local jira_data jira_data=$(jira issue list --assignee="$email" --order-by=priority --plain --no-headers 2>/dev/null) || { warn "Could not fetch Jira tickets. Proceeding without ticket ID." return 0 } if [[ -z "$jira_data" ]]; then warn "No Jira tickets found. Proceeding without ticket ID." return 0 fi echo "$jira_data" >&2 echo "" >&2 local formatted_tickets formatted_tickets=$(echo "$jira_data" | awk '{ ticket_id = $2 $1 = $2 = "" description = $0 gsub(/^[ \t]+/, "", description) if (length(description) > 60) { description = substr(description, 1, 57) "..." } print ticket_id " - " description }') if [[ -z "$formatted_tickets" ]]; then warn "No tickets to display. Proceeding without ticket ID." return 0 fi local selected_ticket selected_ticket=$(echo -e "SKIP - Create branch without ticket ID\n$formatted_tickets" | \ fzf --prompt="Select Jira ticket (or skip): " \ --height=40% \ --border \ --info=inline) || error "Ticket selection cancelled" if [[ "$selected_ticket" != "SKIP"* ]]; then echo "${selected_ticket%% -*}" fi } get_branch_description() { local ticket_id=$1 local editor="${EDITOR:-vi}" local tmpfile tmpfile=$(mktemp) trap "rm -f '$tmpfile'" EXIT if [[ -n "$ticket_id" ]]; then cat > "$tmpfile" << EOF # Selected ticket: $ticket_id # Enter your branch description below in kebab-case (e.g., my-description): # The ticket ID will be automatically included in the branch name. # Lines starting with # will be ignored. EOF else cat > "$tmpfile" << 'EOF' # Enter your branch description below in kebab-case (e.g., my-description): # Lines starting with # will be ignored. EOF fi "$editor" "$tmpfile" < /dev/tty > /dev/tty local desc desc=$(grep -v '^#' "$tmpfile" | tr -d '\n' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') echo "$desc" } validate_description() { local desc=$1 if [[ -z "$desc" ]]; then error "No description provided" fi if [[ ! "$desc" =~ ^[a-z0-9]+(-[a-z0-9]+)*$ ]]; then error "Invalid branch description format.\nUse lowercase letters, numbers, and hyphens only.\nNo trailing or consecutive hyphens allowed.\nExample: my-feature-description" fi } create_branch() { local type=$1 local ticket_id=$2 local desc=$3 local branch if [[ -n "$ticket_id" ]]; then branch="$type/$ticket_id-$desc" else branch="$type/$desc" fi if git show-ref --verify --quiet "refs/heads/$branch"; then error "Branch '$branch' already exists" fi echo "" echo "Creating branch: $branch" git checkout -b "$branch" } main() { check_dependencies check_git_repo check_current_branch local email email=$(get_user_email) local type type=$(select_branch_type) echo "About to call select_jira_ticket" >&2 local ticket_id="" ticket_id=$(select_jira_ticket "$email") local desc desc=$(get_branch_description "$ticket_id") validate_description "$desc" create_branch "$type" "$ticket_id" "$desc" } main "$@"