Skip to content

Commit d5c4a4e

Browse files
feat: update to merged branch deletion action using PR method
1 parent 9c00267 commit d5c4a4e

File tree

2 files changed

+207
-131
lines changed

2 files changed

+207
-131
lines changed
Lines changed: 111 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -1,167 +1,147 @@
1-
name: Merged stale branch deletion
2-
1+
name: Stale Branch Deletion Approval
2+
permissions:
3+
contents: write
4+
pull-requests: write
35
on:
46
schedule:
5-
# Runs every two weeks (on the 1st and 15th of each month)
6-
- cron: "0 0 1,15 * *"
7-
8-
# Manual trigger
7+
- cron: '00 22 1 * *' # 10PM on 1st of every month
98
workflow_dispatch:
109
inputs:
11-
dry_run:
12-
description: "Dry run (no actual deletions)"
13-
required: true
14-
default: "true"
15-
type: choice
16-
options:
17-
- "true"
18-
- "false"
1910
min_age_days:
2011
description: "Minimum age in days since merge"
2112
required: true
22-
default: 14
13+
default: 27
2314
type: number
24-
2515
jobs:
26-
cleanup-branches:
27-
16+
identify-branches:
2817
if: github.repository_owner == 'mendix'
29-
3018
runs-on: ubuntu-latest
31-
3219
steps:
3320
- name: Checkout code
3421
uses: actions/checkout@v4
3522
with:
3623
fetch-depth: 0
3724
token: ${{ secrets.GITHUB_TOKEN }}
38-
39-
- name: Set up Git
25+
persist-credentials: true
26+
27+
- name: Fetch all branches
4028
run: |
41-
git config --global user.name 'GitHub Actions'
42-
git config --global user.email '[email protected]'
29+
echo "Fetching all branches from remote..."
30+
git fetch origin '+refs/heads/*:refs/remotes/origin/*' --prune
31+
echo "Fetched branches:"
32+
git branch -r
4333
4434
- name: Process branches
35+
id: branch-data
4536
run: |
46-
echo "FETCHING ALL BRANCHES"
47-
git fetch --all
48-
49-
# Checks if it is a dry run
50-
if [[ "${{ github.event_name }}" == "schedule" ]]; then
51-
IS_DRY_RUN="false" # Scheduled runs do actual deletions
52-
MIN_AGE_DAYS=14
53-
else
54-
IS_DRY_RUN="${{ github.event.inputs.dry_run }}"
55-
MIN_AGE_DAYS=${{ github.event.inputs.min_age_days }}
56-
fi
57-
58-
echo "Mode: $([ "$IS_DRY_RUN" == "true" ] && echo "DRY RUN (no deletions)" || echo "ACTUAL DELETION")"
59-
echo "Using minimum age of $MIN_AGE_DAYS days since merge"
60-
61-
echo "FINDING MERGED BRANCHES"
62-
# Gets list of merged branches
63-
MERGED_BRANCHES=$(git branch -r --merged origin/development | grep -v "origin/development" | grep -v "origin/main" | grep -v "origin/production" | grep -v "origin/master" | sed 's/origin\///')
64-
65-
CURRENT_DATE=$(date +%s)
66-
echo "Current date: $(date -d @$CURRENT_DATE)"
67-
68-
# Creates arrays to track results
37+
set -e
38+
echo "Finding all branches for processing..."
39+
ALL_BRANCHES=$(git branch -r | grep -v "origin/HEAD" | sed 's/origin\///')
40+
41+
echo "All branches found:"
42+
printf "%s\n" "${ALL_BRANCHES[@]}"
43+
44+
MIN_AGE_DAYS=${{ github.event.inputs.min_age_days || 27 }}
45+
46+
# Arrays to hold branches
47+
PROTECTED_BRANCHES=()
48+
MERGED_BRANCHES_TO_PROCESS=()
6949
BRANCHES_TO_DELETE=()
7050
BRANCHES_TOO_RECENT=()
71-
BRANCHES_SKIPPED=()
7251
73-
echo "CHECKING BRANCH MERGE DATES:"
74-
echo "=================================="
75-
for BRANCH in $MERGED_BRANCHES; do
76-
# Skips branches with "backup" in their name
77-
if [[ $BRANCH == *backup* ]]; then
78-
BRANCHES_SKIPPED+=("$BRANCH (backup branch)")
79-
continue
80-
fi
81-
82-
# Finds when branch was merged to development
83-
# Gets the commit hash where the branch was merged
84-
MERGE_HASH=$(git log --grep="Merge branch.*$BRANCH" origin/development -n 1 --pretty=format:"%H" || git log --grep="Merge pull request.*$BRANCH" origin/development -n 1 --pretty=format:"%H")
85-
86-
# If no merge commit found, do:
87-
if [[ -z "$MERGE_HASH" ]]; then
88-
# Get the last commit of the branch
89-
LAST_COMMIT=$(git log -n 1 origin/$BRANCH --pretty=format:"%H")
90-
# Find when this commit or its equivalent appeared in development
91-
MERGE_HASH=$(git log origin/development --pretty=format:"%H" --grep="$(git log -n 1 $LAST_COMMIT --pretty=format:"%s")" -n 1)
92-
fi
93-
94-
# If still no merge info, use the last commit on the branch
95-
if [[ -z "$MERGE_HASH" ]]; then
96-
MERGE_HASH=$(git log -n 1 origin/$BRANCH --pretty=format:"%H")
52+
CURRENT_DATE=$(date +%Y%m%d)
53+
echo "CURRENT_DATE=$CURRENT_DATE" >> $GITHUB_ENV
54+
55+
# Check branches
56+
for BRANCH in $ALL_BRANCHES; do
57+
branch_lower=$(echo "$BRANCH" | tr '[:upper:]' '[:lower:]')
58+
# Identify protected branches
59+
if [[ $branch_lower =~ (backup|development|main|master|production) ]]; then
60+
if git branch -r --merged origin/development | grep -q "origin/$BRANCH"; then
61+
PROTECTED_BRANCHES+=("$BRANCH (protected name, merged)")
62+
else
63+
PROTECTED_BRANCHES+=("$BRANCH (protected name, not merged)")
64+
fi
65+
else
66+
# Process non-protected merged branches
67+
if git branch -r --merged origin/development | grep -q "origin/$BRANCH"; then
68+
MERGED_BRANCHES_TO_PROCESS+=("$BRANCH")
69+
else
70+
UNMERGED_BRANCHES+=("$BRANCH (not merged)")
71+
fi
9772
fi
98-
99-
# Get the commit date
73+
done
74+
75+
# Process potential deletion
76+
for BRANCH in "${MERGED_BRANCHES_TO_PROCESS[@]}"; do
77+
MERGE_HASH=$(git log --grep="Merge branch.*$BRANCH" origin/development -n 1 --pretty=format:"%H" ||
78+
git log --grep="Merge pull request.*$BRANCH" origin/development -n 1 --pretty=format:"%H")
79+
[ -z "$MERGE_HASH" ] && MERGE_HASH=$(git log -n 1 origin/$BRANCH --pretty=format:"%H")
80+
10081
MERGE_DATE=$(git show -s --format=%ct $MERGE_HASH)
101-
MERGE_DATE_HUMAN=$(git show -s --format=%ci $MERGE_HASH)
102-
103-
# Calculate days since merge
104-
DAYS_AGO=$(( (CURRENT_DATE - MERGE_DATE) / 86400 ))
105-
106-
echo "Branch: $BRANCH - merged approximately $DAYS_AGO days ago ($MERGE_DATE_HUMAN)"
107-
108-
# Check if the branch is old enough to be deleted
82+
DAYS_AGO=$(( ($(date +%s) - MERGE_DATE) / 86400 ))
83+
10984
if [[ $DAYS_AGO -ge $MIN_AGE_DAYS ]]; then
110-
BRANCHES_TO_DELETE+=("$BRANCH ($DAYS_AGO days since merge)")
85+
BRANCHES_TO_DELETE+=("$BRANCH ($DAYS_AGO days)")
11186
else
112-
BRANCHES_TOO_RECENT+=("$BRANCH ($DAYS_AGO days since merge)")
87+
BRANCHES_TOO_RECENT+=("$BRANCH ($DAYS_AGO days)")
11388
fi
11489
done
11590
116-
# Display results
117-
echo ""
118-
echo "BRANCHES RECOMMENDED FOR DELETION (MERGED >= $MIN_AGE_DAYS DAYS AGO):"
119-
echo "=================================="
120-
if [ ${#BRANCHES_TO_DELETE[@]} -eq 0 ]; then
121-
echo "No branches found that meet deletion criteria"
122-
else
123-
for BRANCH_INFO in "${BRANCHES_TO_DELETE[@]}"; do
124-
echo "$BRANCH_INFO"
125-
done
126-
fi
91+
# Display non-deleted branches for logging
92+
ALL_NON_DELETED_BRANCHES=("${PROTECTED_BRANCHES[@]}" "${BRANCHES_TOO_RECENT[@]}" "${UNMERGED_BRANCHES[@]}")
93+
IFS=$'\n' ALL_NON_DELETED_BRANCHES=($(sort <<<"${ALL_NON_DELETED_BRANCHES[*]}"))
94+
unset IFS
12795
128-
echo ""
129-
echo "BRANCHES TOO RECENT TO DELETE (MERGED < $MIN_AGE_DAYS DAYS AGO):"
130-
echo "=================================="
131-
if [ ${#BRANCHES_TOO_RECENT[@]} -eq 0 ]; then
132-
echo "No recently merged branches found"
133-
else
134-
for BRANCH_INFO in "${BRANCHES_TOO_RECENT[@]}"; do
135-
echo "$BRANCH_INFO"
136-
done
137-
fi
138-
139-
echo ""
140-
echo "BRANCHES SKIPPED:"
141-
echo "=================================="
142-
if [ ${#BRANCHES_SKIPPED[@]} -eq 0 ]; then
143-
echo "No branches skipped"
96+
if [ ${#BRANCHES_TO_DELETE[@]} -eq 0 ]; then
97+
echo "No branches found for deletion."
98+
echo "NO_BRANCHES=true" >> $GITHUB_ENV
99+
exit 0
144100
else
145-
for BRANCH_INFO in "${BRANCHES_SKIPPED[@]}"; do
146-
echo "$BRANCH_INFO"
147-
done
101+
echo "NO_BRANCHES=false" >> $GITHUB_ENV
148102
fi
149103
150-
# Perform actual deletion if not a dry run
151-
if [[ "$IS_DRY_RUN" == "false" ]]; then
152-
echo ""
153-
echo "=================================="
154-
echo "PROCEEDING WITH ACTUAL DELETION"
155-
echo "=================================="
104+
# Create report
105+
echo "# Branch Cleanup Report - $(date +%Y-%m-%d)" > branch-report.branchreport
106+
echo "## Branches for deletion (merged >=${MIN_AGE_DAYS} days ago):" >> branch-report.branchreport
107+
echo '```' >> branch-report.branchreport
108+
printf "%s\n" "${BRANCHES_TO_DELETE[@]}" >> branch-report.branchreport
109+
echo '```' >> branch-report.branchreport
110+
echo "## Branches not eligible for deletion:" >> branch-report.branchreport
111+
echo '```' >> branch-report.branchreport
112+
printf "%s\n" "${ALL_NON_DELETED_BRANCHES[@]}" >> branch-report.branchreport
113+
echo '```' >> branch-report.branchreport
114+
115+
echo "BRANCHES_TO_DELETE<<EOF" >> $GITHUB_ENV
116+
printf "%s\n" "${BRANCHES_TO_DELETE[@]}" >> $GITHUB_ENV
117+
echo "EOF" >> $GITHUB_ENV
118+
echo "ALL_NON_DELETED_BRANCHES<<EOF" >> $GITHUB_ENV
119+
printf "%s\n" "${ALL_NON_DELETED_BRANCHES[@]}" >> $GITHUB_ENV
120+
echo "EOF" >> $GITHUB_ENV
121+
122+
- name: Create Deletion PR
123+
if: env.NO_BRANCHES != 'true'
124+
uses: peter-evans/create-pull-request@v6
125+
with:
126+
commit-message: "Branch cleanup proposal"
127+
title: "[AUTO] Branch Deletion Candidates - ${{ env.CURRENT_DATE }}"
128+
body: |
129+
## Branches for deletion (merged to development)
130+
```
131+
${{ env.BRANCHES_TO_DELETE }}
132+
```
156133
157-
# Extract just the branch names from BRANCHES_TO_DELETE
158-
for BRANCH_INFO in "${BRANCHES_TO_DELETE[@]}"; do
159-
BRANCH=$(echo $BRANCH_INFO | cut -d' ' -f1)
160-
echo "Deleting branch: $BRANCH"
161-
git push origin --delete $BRANCH
162-
done
163-
else
164-
echo ""
165-
echo "=================================="
166-
echo "This is just a report - no branches were actually deleted."
167-
fi
134+
## Branches not eligible for deletion
135+
```
136+
${{ env.ALL_NON_DELETED_BRANCHES }}
137+
```
138+
## ⚠️ Warning
139+
Merging this PR will:
140+
1. Delete the branches listed in the "Branches for deletion" section.
141+
2. Remove the temporary branch-report.branchreport file.
142+
branch: branch-cleanup-${{ env.CURRENT_DATE }}
143+
assignees: MarkvanMents,OlufunkeMoronfolu
144+
reviewers: MarkvanMents,OlufunkeMoronfolu
145+
labels: Internal WIP
146+
add-paths: |
147+
branch-report.branchreport
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
name: Process Branch Deletion
2+
on:
3+
pull_request:
4+
types:
5+
- closed
6+
branches:
7+
- 'development'
8+
paths:
9+
- '**.branchreport'
10+
permissions:
11+
contents: write
12+
jobs:
13+
delete-branches-and-cleanup:
14+
if: |
15+
github.event.pull_request.merged == true &&
16+
startsWith(github.event.pull_request.title, '[AUTO] Branch Deletion Candidates')
17+
runs-on: ubuntu-latest
18+
steps:
19+
- name: Checkout code
20+
uses: actions/checkout@v4
21+
with:
22+
fetch-depth: 0
23+
token: ${{ secrets.GITHUB_TOKEN }}
24+
ref: ${{ github.event.repository.default_branch }}
25+
- name: Delete branch report file
26+
run: |
27+
# Check if file exists and delete it
28+
if [ -f "branch-report.branchreport" ]; then
29+
# Configure Git with your username
30+
git config --global user.name "github-actions[bot]"
31+
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
32+
33+
git rm branch-report.branchreport
34+
git commit -m "Remove temporary branch report file"
35+
git push
36+
echo "Removed branch-report.branchreport file"
37+
else
38+
echo "branch-report.branchreport file not found"
39+
fi
40+
- name: Extract branches and delete
41+
env:
42+
PR_BODY: ${{ github.event.pull_request.body }}
43+
run: |
44+
echo "PR Body Content:"
45+
echo "$PR_BODY"
46+
echo "-----------------------------------"
47+
48+
echo "Extracting branch names for deletion..."
49+
50+
# Extract lines between the markers using awk
51+
DELETION_LIST=$(echo "$PR_BODY" | awk '
52+
BEGIN { print_lines = 0; }
53+
/Branches for deletion/ { print_lines = 1; next; }
54+
/Branches not eligible for deletion/ { print_lines = 0; }
55+
print_lines == 1 && !/^```/ && NF > 0 {
56+
print $1;
57+
}
58+
')
59+
60+
echo "Branches identified for deletion:"
61+
echo "$DELETION_LIST"
62+
echo "-----------------------------------"
63+
64+
if [ -z "$DELETION_LIST" ]; then
65+
echo "No branches found for deletion"
66+
exit 0
67+
fi
68+
# Configure Git with your username
69+
git config --global user.name "github-actions[bot]"
70+
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
71+
72+
# Process each branch
73+
echo "$DELETION_LIST" | while read -r BRANCH; do
74+
# Skip empty lines
75+
[ -z "$BRANCH" ] && continue
76+
77+
echo "Processing branch: '$BRANCH'"
78+
79+
# Final protection check
80+
branch_lower=$(echo "$BRANCH" | tr '[:upper:]' '[:lower:]')
81+
if [[ $branch_lower =~ (backup|development|main|master|production) ]]; then
82+
echo "Skipping protected branch: $BRANCH"
83+
continue
84+
fi
85+
echo "Attempting to delete branch: $BRANCH"
86+
# Check if branch exists before trying to delete
87+
if git ls-remote --heads origin "$BRANCH" | grep -q "$BRANCH"; then
88+
echo "Branch exists, proceeding with deletion"
89+
git push origin --delete "$BRANCH" || {
90+
echo "Failed to delete branch: $BRANCH"
91+
echo "Error code: $?"
92+
}
93+
else
94+
echo "Branch $BRANCH does not exist or is not accessible"
95+
fi
96+
done

0 commit comments

Comments
 (0)