Cleanup Merged Branches #67
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
| name: Cleanup Merged Branches | |
| on: | |
| pull_request: | |
| types: [closed] | |
| schedule: | |
| # Run weekly on Sunday at 00:00 UTC to clean up stale branches | |
| - cron: '0 0 * * 0' | |
| workflow_dispatch: | |
| inputs: | |
| dry_run: | |
| description: 'Dry run (show branches without deleting)' | |
| required: false | |
| type: boolean | |
| default: true | |
| permissions: | |
| contents: write | |
| pull-requests: read | |
| jobs: | |
| cleanup-pr-branch: | |
| name: Delete merged PR branch | |
| # Only run for closed PRs that were merged | |
| if: | | |
| github.event_name == 'pull_request' && | |
| github.event.pull_request.merged == true | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Delete branch | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| BRANCH_NAME: ${{ github.event.pull_request.head.ref }} | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| REPO: ${{ github.repository }} | |
| run: | | |
| # Only delete auto-update branches | |
| if [[ ! "$BRANCH_NAME" =~ ^auto-update/ ]]; then | |
| echo "Branch '$BRANCH_NAME' is not an auto-update branch, skipping" | |
| exit 0 | |
| fi | |
| echo "PR #$PR_NUMBER was merged" | |
| echo "Deleting branch: $BRANCH_NAME" | |
| gh api --method DELETE \ | |
| "repos/$REPO/git/refs/heads/$BRANCH_NAME" \ | |
| && echo "✓ Branch deleted successfully" \ | |
| || echo "⚠ Branch may have been already deleted" | |
| cleanup-stale-branches: | |
| name: Cleanup all stale merged branches | |
| # Run on manual dispatch or scheduled runs | |
| if: github.event_name == 'workflow_dispatch' || github.event_name == 'schedule' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Find and delete stale auto-update branches | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # For scheduled runs, default to dry_run=false (actually delete) | |
| DRY_RUN: ${{ github.event_name == 'schedule' && 'false' || inputs.dry_run }} | |
| REPO: ${{ github.repository }} | |
| run: | | |
| echo "Searching for merged auto-update/* branches..." | |
| # Get all auto-update branches | |
| BRANCHES=$(gh api "repos/$REPO/branches" \ | |
| --paginate \ | |
| --jq '.[] | select(.name | startswith("auto-update/")) | .name') | |
| if [ -z "$BRANCHES" ]; then | |
| echo "No auto-update branches found" | |
| exit 0 | |
| fi | |
| DELETED=0 | |
| SKIPPED=0 | |
| while IFS= read -r BRANCH; do | |
| echo "" | |
| echo "Checking branch: $BRANCH" | |
| # Check via PR API (handles squash, rebase, and merge-commit strategies) | |
| MERGED_PR=$(gh pr list --repo "$REPO" --head "$BRANCH" --state merged --json number --jq '.[0].number') | |
| if [ -n "$MERGED_PR" ]; then | |
| echo " ✓ PR #$MERGED_PR was merged" | |
| if [ "$DRY_RUN" = "true" ]; then | |
| echo " [DRY RUN] Would delete: $BRANCH" | |
| DELETED=$((DELETED + 1)) | |
| else | |
| if gh api --method DELETE "repos/$REPO/git/refs/heads/$BRANCH"; then | |
| echo " ✓ Deleted: $BRANCH" | |
| DELETED=$((DELETED + 1)) | |
| else | |
| echo " ✗ Failed to delete: $BRANCH" | |
| SKIPPED=$((SKIPPED + 1)) | |
| fi | |
| fi | |
| else | |
| echo " ⊘ No merged PR found for this branch, keeping it" | |
| SKIPPED=$((SKIPPED + 1)) | |
| fi | |
| sleep 0.5 # Rate limiting | |
| done <<< "$BRANCHES" | |
| echo "" | |
| echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" | |
| if [ "$DRY_RUN" = "true" ]; then | |
| echo "DRY RUN SUMMARY:" | |
| echo " Would delete: $DELETED branches" | |
| echo " Would keep: $SKIPPED branches" | |
| echo "" | |
| echo "Run with dry_run=false to actually delete branches" | |
| else | |
| echo "CLEANUP SUMMARY:" | |
| echo " Deleted: $DELETED branches" | |
| echo " Skipped: $SKIPPED branches" | |
| fi |