Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .pr_trigger
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Last run: 2025-12-03 19:21:19
Last run: 2025-12-03 20:10:02
231 changes: 161 additions & 70 deletions pr_automation.sh
Original file line number Diff line number Diff line change
@@ -1,101 +1,192 @@
#!/bin/bash
#!/usr/bin/env bash
# pr_automation.sh - safer automated PR creation/updating
# Usage: ./pr_automation.sh (interactive)
# Optional env:
# DEFAULT_BASE_BRANCH (default: gh-pages)
# ALLOW_EMPTY_COMMIT (default: "no") -> set to "yes" to auto-create empty commits when nothing changed
set -o errexit
set -o pipefail
set -o nounset

# --- Configuration ---
# File used to force a commit and update the PR
TRIGGER_FILE=".pr_trigger"
# Prefix for the new branch name
BRANCH_PREFIX="feature/auto-"
# Hard-coded base branch
DEFAULT_BRANCH="gh-pages"
DEFAULT_BASE_BRANCH="${DEFAULT_BRANCH:-gh-pages}"
ALLOW_EMPTY_COMMIT="${ALLOW_EMPTY_COMMIT:-no}" # set to "yes" to allow empty commits automatically

# Get the current branch
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2> /dev/null)
if [ "$?" -ne 0 ]; then
echo "❌ Error: Not in a Git repository."
exit 1
log() { printf '\033[1;34m%s\033[0m\n' "$*"; } # blue
success() { printf '\033[1;32m%s\033[0m\n' "$*"; } # green
warn() { printf '\033[1;33m%s\033[0m\n' "$*"; } # yellow
err() { printf '\033[1;31m%s\033[0m\n' "$*"; } # red

# --- sanity checks ---
command -v git >/dev/null 2>&1 || { err "git not found in PATH. Install git."; exit 1; }
command -v gh >/dev/null 2>&1 || { err "GitHub CLI 'gh' not found in PATH. Install and authenticate."; exit 1; }

# Ensure we're in a git repo
if ! git rev-parse --git-dir >/dev/null 2>&1; then
err "Not a git repository (or no .git found). cd into a repo and retry."
exit 1
fi
# --- Helper Functions ---

# Function to generate a unique branch name
# Get current branch
CURRENT_BRANCH="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || true)"
if [ -z "$CURRENT_BRANCH" ]; then
err "Couldn't detect current git branch."
exit 1
fi

# generate branch name
generate_branch_name() {
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
counter=$(( (RANDOM % 26) ))
identifier=$(printf %c $((counter + 97)))
echo "${BRANCH_PREFIX}${identifier}-${TIMESTAMP}"
TIMESTAMP="$(date +%Y%m%d-%H%M%S)"
# randomized letter suffix (a..z)
letter=$(printf "%c" $((97 + (RANDOM % 26))))
echo "${BRANCH_PREFIX}${letter}-${TIMESTAMP}"
}

# determine sensible remote base branch (fall back if DEFAULT_BASE_BRANCH not present remote)
determine_remote_base() {
local preferred="$1"
# check remote branch existence
if git ls-remote --heads origin "refs/heads/${preferred}" | grep -q 'refs/heads/' ; then
echo "$preferred"
return 0
fi
# try main/master
for fallback in main master; do
if git ls-remote --heads origin "refs/heads/${fallback}" | grep -q 'refs/heads/'; then
warn "Preferred base branch 'origin/${preferred}' not found; using 'origin/${fallback}' instead."
echo "$fallback"
return 0
fi
done
warn "Preferred base branch 'origin/${preferred}' not found and no 'main'/'master' found. Using '$preferred' locally (may fail if remote missing)."
echo "$preferred"
return 0
}

# --- Main Script Logic ---
# interactive menu
PS3="Select action: "
options=("Create New Pull Request (New Branch)" "Update Existing Pull Request (Current Branch)")
echo
select opt in "${options[@]}"; do
case $REPLY in
1) ACTION="CREATE"; break;;
2) ACTION="UPDATE"; break;;
*) echo "Invalid option. Choose 1 or 2.";;
esac
done

# ensure git user config exists (helpful failure point)
if ! git config user.email >/dev/null 2>&1 || ! git config user.name >/dev/null 2>&1; then
warn "Git user.name or user.email not set. Commits may fail. Set with 'git config user.name \"Your Name\"' and 'git config user.email [email protected]'."
fi

# create/update branch if requested
if [ "$ACTION" = "CREATE" ]; then
NEW_BRANCH_NAME="$(generate_branch_name)"
log "Creating and switching to new branch: $NEW_BRANCH_NAME"
git checkout -b "$NEW_BRANCH_NAME"
BRANCH_TO_USE="$NEW_BRANCH_NAME"
else
BRANCH_TO_USE="$CURRENT_BRANCH"
log "Using current branch: $BRANCH_TO_USE"
fi

# 1. Force a change by updating a hidden file (silent)
CURRENT_TIME=$(date +'%Y-%m-%d %H:%M:%S')
# 1) update trigger file so there's always something to commit if desired
CURRENT_TIME="$(date +'%Y-%m-%d %H:%M:%S')"
echo "Last run: $CURRENT_TIME" > "$TRIGGER_FILE"

# 2. Stage and commit all changes (silent)
git add -A > /dev/null 2>&1
# 2) Stage all changes
log "Staging changes..."
git add -A

# 3) Try commit; if nothing to commit and ALLOW_EMPTY_COMMIT=yes, create empty commit
COMMIT_MESSAGE="Auto-commit: $CURRENT_TIME - Automated PR update."
git commit -m "$COMMIT_MESSAGE" > /dev/null 2>&1

# 3. Interactive Menu (The ONLY prompt shown before the URL)
echo ""
PS3="Select action: "
options=("Create New Pull Request (New Branch)" "Update Existing Pull Request (Current Branch)")
select opt in "${options[@]}"
do
case $opt in
"Create New Pull Request (New Branch)")
# Create New PR flow: Create and switch to a new branch (silent)
NEW_BRANCH_NAME=$(generate_branch_name)
git checkout -b "$NEW_BRANCH_NAME" > /dev/null 2>&1
BRANCH_TO_USE="$NEW_BRANCH_NAME"
ACTION="CREATE"
break
;;
"Update Existing Pull Request (Current Branch)")
# Update Existing PR flow: Stay on current branch
BRANCH_TO_USE="$CURRENT_BRANCH"
ACTION="UPDATE"
break
;;
*) echo "Invalid option $REPLY. Please select 1 or 2.";;
esac
done
if git commit -m "$COMMIT_MESSAGE"; then
success "Commit created."
else
# commit failed; check if it's because nothing to commit
if git status --porcelain | grep -q '^'; then
warn "Warning: commit returned non-zero. Check hooks or configuration."
fi
# detect "nothing to commit" case by checking status
if [ -z "$(git status --porcelain)" ]; then
warn "Nothing to commit."
if [ "${ALLOW_EMPTY_COMMIT}" = "yes" ]; then
log "Creating an empty commit because ALLOW_EMPTY_COMMIT=yes..."
git commit --allow-empty -m "${COMMIT_MESSAGE} (empty)"
success "Empty commit created."
else
warn "No commit created and ALLOW_EMPTY_COMMIT is not enabled. Continuing without new commit."
fi
else
# There are staged/unstaged changes but commit failed (likely hook): show last commit error and exit
err "git commit failed. Hooks may have rejected the commit. Aborting."
# give hint and show git status
git status --short || true
exit 1
fi
fi

# 4. Push the changes to the remote repository
echo "🚀 Pushing changes to remote branch 'origin/$BRANCH_TO_USE'..."
# The redirection to /dev/null has been removed to allow the SSH passphrase prompt to be visible.
# 4) Push
log "Pushing to remote branch 'origin/${BRANCH_TO_USE}'..."
if ! git push -u origin "$BRANCH_TO_USE"; then
echo "----------------------------------------------"
echo "❌ **FATAL ERROR: Git push failed.**"
echo "Please ensure your SSH key is loaded (try 'ssh-add' if prompted for a passphrase)."
echo "----------------------------------------------"
exit 1
err "git push failed. Make sure remote 'origin' is reachable and you have permission."
exit 1
fi
success "Pushed branch to origin/${BRANCH_TO_USE}."

# 5. Execute PR Action (Create or Update)
# 5) Prepare PR creation/update
BASE_BRANCH="$(determine_remote_base "$DEFAULT_BASE_BRANCH")"
PR_TITLE="[Auto-PR] ${COMMIT_MESSAGE}"
PR_BODY="**Automated Pull Request**\n\nTriggered: ${CURRENT_TIME}"

PR_URL=""

if [ "$ACTION" == "CREATE" ]; then
# Create PR (silent)
PR_URL=$(gh pr create --title "$PR_TITLE" --body "**Automated Pull Request**" --head "$BRANCH_TO_USE" --base "$DEFAULT_BRANCH" --fill)

elif [ "$ACTION" == "UPDATE" ]; then
# Get existing PR URL (silent)
PR_URL=$(gh pr view "$BRANCH_TO_USE" --json url -q .url 2>/dev/null)

if [ "$ACTION" = "CREATE" ]; then
log "Creating Pull Request (head: ${BRANCH_TO_USE} -> base: ${BASE_BRANCH})..."
# Use explicit title/body to avoid relying on gh --fill
PR_URL="$(gh pr create --head "${BRANCH_TO_USE}" --base "${BASE_BRANCH}" --title "$PR_TITLE" --body "$PR_BODY" 2>/dev/null || true)"
if [ -z "$PR_URL" ]; then
# Try verbose mode to show gh error
warn "gh pr create failed; showing gh diagnostics:"
gh pr create --head "${BRANCH_TO_USE}" --base "${BASE_BRANCH}" --title "$PR_TITLE" --body "$PR_BODY" || true
# After printing diagnostics, attempt to find an existing PR or fail
PR_URL="$(gh pr view --head "${BRANCH_TO_USE}" --json url -q .url 2>/dev/null || true)"
fi

elif [ "$ACTION" = "UPDATE" ]; then
log "Looking for an existing PR for branch '${BRANCH_TO_USE}'..."
PR_URL="$(gh pr view --head "${BRANCH_TO_USE}" --json url -q .url 2>/dev/null || true)"
if [ -n "$PR_URL" ]; then
success "Found PR: $PR_URL"
# Optionally add a comment to indicate update
gh pr comment --body "Automated update: ${CURRENT_TIME}" "${PR_URL}" >/dev/null 2>&1 || true
else
log "No existing PR found; creating one..."
PR_URL="$(gh pr create --head "${BRANCH_TO_USE}" --base "${BASE_BRANCH}" --title "$PR_TITLE" --body "$PR_BODY" 2>/dev/null || true)"
if [ -z "$PR_URL" ]; then
# Create new PR if none found (silent)
PR_URL=$(gh pr create --title "$PR_TITLE" --body "**Automated Pull Request**" --head "$BRANCH_TO_USE" --base "$DEFAULT_BRANCH" --fill)
warn "gh pr create failed; showing gh diagnostics:"
gh pr create --head "${BRANCH_TO_USE}" --base "${BASE_BRANCH}" --title "$PR_TITLE" --body "$PR_BODY" || true
PR_URL="$(gh pr view --head "${BRANCH_TO_USE}" --json url -q .url 2>/dev/null || true)"
fi
fi
fi

# 6. Display Result (Only the URL)
# 6) Output result
echo "----------------------------------------------"
if [ -n "$PR_URL" ]; then
echo "🔗 **Pull Request URL:** $PR_URL"
success "🔗 Pull Request URL: $PR_URL"
echo
# print short helpful info
echo "Branch: ${BRANCH_TO_USE}"
echo "Base: ${BASE_BRANCH}"
echo "Title: ${PR_TITLE}"
else
echo "❌ **Action Failed.** Could not determine or create the Pull Request URL."
err "❌ Action Failed. Could not determine or create the Pull Request URL."
warn "Useful diagnostics:"
git status --short || true
git log --oneline "origin/${BASE_BRANCH}..${BRANCH_TO_USE}" || true
fi
echo "----------------------------------------------"

Loading