Created
October 30, 2025 09:04
-
-
Save szeyu/eb45fad02802a0cf316f724c0fd85117 to your computer and use it in GitHub Desktop.
A script to quickly discover stale branch and manage it (delete)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # Fail fast and show errors | |
| set -euo pipefail | |
| # --- User-editable parameters --- | |
| REPO="${REPO:-rytbank/data-schedules}" | |
| DAYS_OLD="${DAYS_OLD:-30}" | |
| EXCLUDE_PATTERN="${EXCLUDE_PATTERN:-^(main|dev-env|uat-env)$}" | |
| DEFAULT_BRANCH="${DEFAULT_BRANCH:-main}" | |
| DRY_RUN="${DRY_RUN:-true}" | |
| FORCE_DELETE="${FORCE_DELETE:-false}" | |
| # --- Check for dependencies --- | |
| if ! command -v gh >/dev/null 2>&1; then | |
| echo "'gh' CLI not found; please install from https://cli.github.com/" | |
| exit 1 | |
| fi | |
| # --- Confirm authentication --- | |
| if ! gh auth status >/dev/null 2>&1; then | |
| echo "GitHub CLI not authenticated; run 'gh auth login'." | |
| exit 1 | |
| fi | |
| # --- Calculate cutoff date --- | |
| if date -v-1d +%Y-%m-%dT%H:%M:%SZ >/dev/null 2>&1; then | |
| # macOS/BSD date | |
| CUTOFF_DATE=$(date -u -v-"$DAYS_OLD"d +%Y-%m-%dT%H:%M:%SZ) | |
| else | |
| # GNU date (Linux) | |
| CUTOFF_DATE=$(date -u -d "$DAYS_OLD days ago" +%Y-%m-%dT%H:%M:%SZ) | |
| fi | |
| echo "Fetching branches for $REPO..." | |
| branches=($(gh api "repos/$REPO/branches" --paginate --jq '.[].name')) | |
| echo "Analyzing branches older than $CUTOFF_DATE..." | |
| for branch in "${branches[@]}"; do | |
| if echo "$branch" | grep -Eq "$EXCLUDE_PATTERN"; then | |
| continue | |
| fi | |
| last_commit_date=$(gh api "repos/$REPO/commits/$branch" --jq '.commit.committer.date' 2>/dev/null || true) | |
| if [[ -n "$last_commit_date" && "$last_commit_date" < "$CUTOFF_DATE" ]]; then | |
| echo "Branch $branch is stale since $last_commit_date" | |
| if git branch --contains "origin/$branch" | grep -q "$DEFAULT_BRANCH"; then | |
| if [[ "$DRY_RUN" == "false" ]]; then | |
| echo "[DELETE] $branch" | |
| echo "$branch,$last_commit_date,DELETED" >> stale_branches.csv | |
| # gh api -X DELETE "repos/$REPO/git/refs/heads/$branch" | |
| else | |
| echo "[DRY RUN] Would delete: $branch" | |
| echo "$branch,$last_commit_date,DRY_DELETED" >> stale_branches.csv | |
| fi | |
| else | |
| if [[ "$FORCE_DELETE" == "true" ]]; then | |
| if [[ "$DRY_RUN" == "false" ]]; then | |
| echo "[FORCE DELETE] $branch" | |
| echo "$branch,$last_commit_date,FORCE_DELETED" >> stale_branches.csv | |
| # gh api -X DELETE "repos/$REPO/git/refs/heads/$branch" | |
| else | |
| echo "[DRY RUN] Would force delete: $branch" | |
| echo "$branch,$last_commit_date,DRY_FORCE_DELETED" >> stale_branches.csv | |
| fi | |
| else | |
| echo "[SKIP] $branch not merged into $DEFAULT_BRANCH" | |
| echo "$branch,$last_commit_date,SKIPPED" >> stale_branches.csv | |
| fi | |
| fi | |
| fi | |
| done | |
| echo "Done" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment