-
Notifications
You must be signed in to change notification settings - Fork 51
187 lines (158 loc) · 6.92 KB
/
cleanup-lint-branches.yml
File metadata and controls
187 lines (158 loc) · 6.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
name: "🧹 Cleanup Lint Branches"
# This workflow automatically cleans up lint/* branches that have been merged or closed.
# It helps maintain a clean repository by removing branches created by the linter workflow
# after their associated pull requests are no longer active.
on:
# Run daily at 00:00 UTC
schedule:
- cron: '0 0 * * *'
# Allow manual triggering with optional dry-run mode
workflow_dispatch:
inputs:
dry_run:
description: 'Dry run mode (preview deletions without actually deleting)'
required: false
type: boolean
default: true
permissions:
contents: write
pull-requests: read
jobs:
cleanup:
name: Clean up merged/closed lint branches
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # Fetch all branches and history
token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup GitHub CLI
run: |
# Verify gh CLI is available (pre-installed on ubuntu-latest)
gh --version
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Cleanup lint branches
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Default to true (dry-run mode) unless explicitly set to false
# This ensures safe operation for both scheduled and manual triggers
DRY_RUN: ${{ github.event.inputs.dry_run == 'false' && 'false' || 'true' }}
BRANCH_AGE_DAYS: 7
run: |
set -e
set -o pipefail
echo "🧹 Starting lint branch cleanup..."
echo "Dry run mode: $DRY_RUN"
echo "Branch age threshold for orphaned branches: $BRANCH_AGE_DAYS days"
echo ""
# Get current date in seconds since epoch
current_date=$(date +%s)
age_threshold_seconds=$((BRANCH_AGE_DAYS * 86400))
# Track statistics
total_branches=0
deleted_branches=0
skipped_branches=0
error_branches=0
# Get all remote branches matching the pattern lint/style-fixes-*
echo "📋 Fetching lint branches..."
lint_branches=$(git for-each-ref --format='%(refname:short)' 'refs/remotes/origin/lint/style-fixes-*' | sed 's|^origin/||' || echo "")
if [ -z "$lint_branches" ]; then
echo "✅ No lint branches found matching 'lint/style-fixes-*'"
exit 0
fi
echo "Found $(echo "$lint_branches" | wc -l) lint branches"
echo ""
# Process each branch (using while read to handle special characters safely)
while IFS= read -r branch; do
[ -z "$branch" ] && continue
total_branches=$((total_branches + 1))
echo "🔍 Processing: $branch"
# Validate branch name matches expected pattern
if [[ ! "$branch" =~ ^lint/style-fixes-[0-9]+$ ]]; then
echo " ⚠️ Warning: Branch name doesn't match expected pattern, skipping"
skipped_branches=$((skipped_branches + 1))
echo ""
continue
fi
# Check if there's a PR for this branch
pr_number=$(gh pr list --state all --head "$branch" --json number --jq '.[0].number // empty' 2>/dev/null || echo "")
should_delete=false
delete_reason=""
if [ -n "$pr_number" ]; then
# PR exists, check its state
echo " Found PR #$pr_number"
pr_state=$(gh pr view "$pr_number" --json state --jq '.state' 2>/dev/null || echo "")
pr_merged=$(gh pr view "$pr_number" --json merged --jq '.merged' 2>/dev/null || echo "false")
if [ "$pr_merged" = "true" ]; then
should_delete=true
delete_reason="PR #$pr_number was merged"
elif [ "$pr_state" = "CLOSED" ]; then
should_delete=true
delete_reason="PR #$pr_number was closed"
else
echo " ⏭️ Skipping: PR #$pr_number is still open"
skipped_branches=$((skipped_branches + 1))
fi
else
# No PR found - check if branch is old enough to delete
echo " No associated PR found (orphaned branch)"
# Get the last commit date on this branch
last_commit_date=$(git log -1 --format=%ct "origin/$branch" 2>/dev/null || echo "0")
if [ "$last_commit_date" != "0" ]; then
branch_age_seconds=$((current_date - last_commit_date))
branch_age_days=$((branch_age_seconds / 86400))
echo " Branch age: $branch_age_days days"
if [ $branch_age_seconds -gt $age_threshold_seconds ]; then
should_delete=true
delete_reason="Orphaned branch older than $BRANCH_AGE_DAYS days (age: $branch_age_days days)"
else
echo " ⏭️ Skipping: Orphaned branch is only $branch_age_days days old (threshold: $BRANCH_AGE_DAYS days)"
skipped_branches=$((skipped_branches + 1))
fi
else
echo " ⚠️ Warning: Could not determine branch age"
skipped_branches=$((skipped_branches + 1))
fi
fi
# Delete the branch if appropriate
if [ "$should_delete" = "true" ]; then
if [ "$DRY_RUN" = "true" ]; then
echo " 🔍 [DRY RUN] Would delete: $delete_reason"
deleted_branches=$((deleted_branches + 1))
else
echo " 🗑️ Deleting: $delete_reason"
# Use git push for safer deletion with validated branch name
delete_error=$(git push origin --delete "$branch" 2>&1)
exit_code=$?
if [ $exit_code -eq 0 ]; then
echo " ✅ Successfully deleted"
deleted_branches=$((deleted_branches + 1))
else
echo " ❌ Failed to delete branch: $delete_error"
error_branches=$((error_branches + 1))
fi
fi
fi
echo ""
done <<< "$lint_branches"
# Print summary
echo "📊 Cleanup Summary"
echo "=================="
echo "Total lint branches processed: $total_branches"
if [ "$DRY_RUN" = "true" ]; then
echo "Branches that would be deleted: $deleted_branches"
else
echo "Branches deleted: $deleted_branches"
fi
echo "Branches skipped: $skipped_branches"
if [ $error_branches -gt 0 ]; then
echo "Branches with errors: $error_branches"
fi
echo ""
if [ "$DRY_RUN" = "true" ]; then
echo "✅ Dry run completed - no branches were actually deleted"
else
echo "✅ Cleanup completed"
fi