Skip to content
Open
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
147 changes: 147 additions & 0 deletions .github/workflows/merged-stale-branch-deletion.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
name: Stale Branch Deletion Approval
permissions:
contents: write
pull-requests: write
on:
schedule:
- cron: '00 22 1 * *' # 10PM on 1st of every month
workflow_dispatch:
inputs:
min_age_days:
description: "Minimum age in days since merge"
required: true
default: 27
type: number
jobs:
identify-branches:
if: github.repository_owner == 'mendix'
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
persist-credentials: true

- name: Fetch all branches
run: |
echo "Fetching all branches from remote..."
git fetch origin '+refs/heads/*:refs/remotes/origin/*' --prune
echo "Fetched branches:"
git branch -r

- name: Process branches
id: branch-data
run: |
set -e
echo "Finding all branches for processing..."
ALL_BRANCHES=$(git branch -r | grep -v "origin/HEAD" | sed 's/origin\///')

echo "All branches found:"
printf "%s\n" "${ALL_BRANCHES[@]}"

MIN_AGE_DAYS=${{ github.event.inputs.min_age_days || 27 }}

# Arrays to hold branches
PROTECTED_BRANCHES=()
MERGED_BRANCHES_TO_PROCESS=()
BRANCHES_TO_DELETE=()
BRANCHES_TOO_RECENT=()

CURRENT_DATE=$(date +%Y%m%d)
echo "CURRENT_DATE=$CURRENT_DATE" >> $GITHUB_ENV

# Check branches
for BRANCH in $ALL_BRANCHES; do
branch_lower=$(echo "$BRANCH" | tr '[:upper:]' '[:lower:]')
# Identify protected branches
if [[ $branch_lower =~ (backup|development|main|master|production) ]]; then
if git branch -r --merged origin/development | grep -q "origin/$BRANCH"; then
PROTECTED_BRANCHES+=("$BRANCH (protected name, merged)")
else
PROTECTED_BRANCHES+=("$BRANCH (protected name, not merged)")
fi
else
# Process non-protected merged branches
if git branch -r --merged origin/development | grep -q "origin/$BRANCH"; then
MERGED_BRANCHES_TO_PROCESS+=("$BRANCH")
else
UNMERGED_BRANCHES+=("$BRANCH (not merged)")
fi
fi
done

# Process potential deletion
for BRANCH in "${MERGED_BRANCHES_TO_PROCESS[@]}"; do
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")
[ -z "$MERGE_HASH" ] && MERGE_HASH=$(git log -n 1 origin/$BRANCH --pretty=format:"%H")

MERGE_DATE=$(git show -s --format=%ct $MERGE_HASH)
DAYS_AGO=$(( ($(date +%s) - MERGE_DATE) / 86400 ))

if [[ $DAYS_AGO -ge $MIN_AGE_DAYS ]]; then
BRANCHES_TO_DELETE+=("$BRANCH ($DAYS_AGO days)")
else
BRANCHES_TOO_RECENT+=("$BRANCH ($DAYS_AGO days)")
fi
done

# Display non-deleted branches for logging
ALL_NON_DELETED_BRANCHES=("${PROTECTED_BRANCHES[@]}" "${BRANCHES_TOO_RECENT[@]}" "${UNMERGED_BRANCHES[@]}")
IFS=$'\n' ALL_NON_DELETED_BRANCHES=($(sort <<<"${ALL_NON_DELETED_BRANCHES[*]}"))
unset IFS

if [ ${#BRANCHES_TO_DELETE[@]} -eq 0 ]; then
echo "No branches found for deletion."
echo "NO_BRANCHES=true" >> $GITHUB_ENV
exit 0
else
echo "NO_BRANCHES=false" >> $GITHUB_ENV
fi

# Create report
echo "# Branch Cleanup Report - $(date +%Y-%m-%d)" > branch-report.branchreport
echo "## Branches for deletion (merged >=${MIN_AGE_DAYS} days ago):" >> branch-report.branchreport
echo '```' >> branch-report.branchreport
printf "%s\n" "${BRANCHES_TO_DELETE[@]}" >> branch-report.branchreport
echo '```' >> branch-report.branchreport
echo "## Branches not eligible for deletion:" >> branch-report.branchreport
echo '```' >> branch-report.branchreport
printf "%s\n" "${ALL_NON_DELETED_BRANCHES[@]}" >> branch-report.branchreport
echo '```' >> branch-report.branchreport

echo "BRANCHES_TO_DELETE<<EOF" >> $GITHUB_ENV
printf "%s\n" "${BRANCHES_TO_DELETE[@]}" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
echo "ALL_NON_DELETED_BRANCHES<<EOF" >> $GITHUB_ENV
printf "%s\n" "${ALL_NON_DELETED_BRANCHES[@]}" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV

- name: Create Deletion PR
if: env.NO_BRANCHES != 'true'
uses: peter-evans/create-pull-request@v6
with:
commit-message: "Branch cleanup proposal"
title: "[AUTO] Branch Deletion Candidates - ${{ env.CURRENT_DATE }}"
body: |
## Branches for deletion (merged to development)
```
${{ env.BRANCHES_TO_DELETE }}
```

## Branches not eligible for deletion
```
${{ env.ALL_NON_DELETED_BRANCHES }}
```
## ⚠️ Warning
Merging this PR will:
1. Delete the branches listed in the "Branches for deletion" section.
2. Remove the temporary branch-report.branchreport file.
branch: branch-cleanup-${{ env.CURRENT_DATE }}
assignees: MarkvanMents,OlufunkeMoronfolu
reviewers: MarkvanMents,OlufunkeMoronfolu
labels: Internal WIP
add-paths: |
branch-report.branchreport
96 changes: 96 additions & 0 deletions .github/workflows/process-branch-deletion.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
name: Process Branch Deletion
on:
pull_request:
types:
- closed
branches:
- 'development'
paths:
- '**.branchreport'
permissions:
contents: write
jobs:
delete-branches-and-cleanup:
if: |
github.event.pull_request.merged == true &&
startsWith(github.event.pull_request.title, '[AUTO] Branch Deletion Candidates')
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
ref: ${{ github.event.repository.default_branch }}
- name: Delete branch report file
run: |
# Check if file exists and delete it
if [ -f "branch-report.branchreport" ]; then
# Configure Git with your username
git config --global user.name "github-actions[bot]"
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"

git rm branch-report.branchreport
git commit -m "Remove temporary branch report file"
git push
echo "Removed branch-report.branchreport file"
else
echo "branch-report.branchreport file not found"
fi
- name: Extract branches and delete
env:
PR_BODY: ${{ github.event.pull_request.body }}
run: |
echo "PR Body Content:"
echo "$PR_BODY"
echo "-----------------------------------"

echo "Extracting branch names for deletion..."

# Extract lines between the markers using awk
DELETION_LIST=$(echo "$PR_BODY" | awk '
BEGIN { print_lines = 0; }
/Branches for deletion/ { print_lines = 1; next; }
/Branches not eligible for deletion/ { print_lines = 0; }
print_lines == 1 && !/^```/ && NF > 0 {
print $1;
}
')

echo "Branches identified for deletion:"
echo "$DELETION_LIST"
echo "-----------------------------------"

if [ -z "$DELETION_LIST" ]; then
echo "No branches found for deletion"
exit 0
fi
# Configure Git with your username
git config --global user.name "github-actions[bot]"
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"

# Process each branch
echo "$DELETION_LIST" | while read -r BRANCH; do
# Skip empty lines
[ -z "$BRANCH" ] && continue

echo "Processing branch: '$BRANCH'"

# Final protection check
branch_lower=$(echo "$BRANCH" | tr '[:upper:]' '[:lower:]')
if [[ $branch_lower =~ (backup|development|main|master|production) ]]; then
echo "Skipping protected branch: $BRANCH"
continue
fi
echo "Attempting to delete branch: $BRANCH"
# Check if branch exists before trying to delete
if git ls-remote --heads origin "$BRANCH" | grep -q "$BRANCH"; then
echo "Branch exists, proceeding with deletion"
git push origin --delete "$BRANCH" || {
echo "Failed to delete branch: $BRANCH"
echo "Error code: $?"
}
else
echo "Branch $BRANCH does not exist or is not accessible"
fi
done