Saltar al contenido
mypipelines
Pipelines Actions Gradle Buscar
Shared (cross-cutting)· Reusable workflow ·on: workflow_call

Shared Delete Branch

Shared - Delete Merged Branch

.github/workflows/shared-delete-branch.yml

.github/workflows/shared-delete-branch.yml
name: Shared - Delete Merged Branch
on:
workflow_call:
inputs:
branch_name:
description: 'Branch name to delete (if empty, detects from merge commit)'
required: false
type: string
default: ''
dry_run:
description: 'Only log what would be deleted, do not actually delete'
required: false
type: boolean
default: false
fail_on_protected:
description: 'Hard-fail when target branch is protected (develop/main/master/release/*). Default true for explicit branch_name inputs.'
required: false
type: boolean
default: true
outputs:
deleted_branch:
description: 'Name of deleted branch (empty if not deleted)'
value: ${{ jobs.delete.outputs.branch }}
status:
description: 'Status: deleted, skipped, protected, not_found'
value: ${{ jobs.delete.outputs.status }}
jobs:
delete:
name: Delete Branch
runs-on: ubuntu-latest
outputs:
branch: ${{ steps.delete.outputs.branch }}
status: ${{ steps.delete.outputs.status }}
steps:
- name: Checkout
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Detect and delete branch
id: delete
env:
GH_TOKEN: ${{ github.token }}
INPUT_BRANCH: ${{ inputs.branch_name }}
DRY_RUN: ${{ inputs.dry_run }}
FAIL_ON_PROTECTED: ${{ inputs.fail_on_protected }}
run: |
# Protected branches - never delete
PROTECTED_BRANCHES="main master develop"
# Determine branch to delete
EXPLICIT_INPUT=false
if [ -n "$INPUT_BRANCH" ]; then
BRANCH="$INPUT_BRANCH"
EXPLICIT_INPUT=true
else
COMMIT_SHA=$(git rev-parse HEAD)
COMMIT_MSG=$(git log -1 --pretty=%B)
# Method 1: Merge commit message
if echo "$COMMIT_MSG" | grep -q "Merge pull request"; then
BRANCH=$(echo "$COMMIT_MSG" | grep -oP "from [^/]+/\K[^\s]+" | head -1)
elif echo "$COMMIT_MSG" | grep -q "Merge branch"; then
BRANCH=$(echo "$COMMIT_MSG" | grep -oP "Merge branch '\K[^']+" | head -1)
fi
# Method 2: GitHub API (handles squash merges)
if [ -z "$BRANCH" ]; then
echo "Trying GitHub API to find PR for commit $COMMIT_SHA"
BRANCH=$(gh api "repos/${{ github.repository }}/commits/${COMMIT_SHA}/pulls" \
--jq '.[0].head.ref // empty' 2>/dev/null || echo "")
fi
if [ -z "$BRANCH" ]; then
echo "Could not detect source branch from commit"
echo "status=not_found" >> $GITHUB_OUTPUT
echo "branch=" >> $GITHUB_OUTPUT
exit 0
fi
fi
echo "Detected branch: $BRANCH"
# Check if branch is protected
IS_PROTECTED=false
for protected in $PROTECTED_BRANCHES; do
if [ "$BRANCH" = "$protected" ]; then
IS_PROTECTED=true
break
fi
done
if echo "$BRANCH" | grep -q "^release/"; then
IS_PROTECTED=true
fi
if [ "$IS_PROTECTED" = "true" ]; then
echo "::warning::Branch '$BRANCH' is protected (main/master/develop/release/*) — refusing to delete."
echo "status=protected" >> $GITHUB_OUTPUT
echo "branch=$BRANCH" >> $GITHUB_OUTPUT
if [ "$EXPLICIT_INPUT" = "true" ] && [ "$FAIL_ON_PROTECTED" = "true" ]; then
echo "::error::Refusing to delete protected branch '$BRANCH' (explicit input + fail_on_protected=true)."
exit 1
fi
exit 0
fi
# Check if branch exists
if ! git ls-remote --heads origin "$BRANCH" | grep -q .; then
echo "Branch '$BRANCH' does not exist on remote"
echo "status=not_found" >> $GITHUB_OUTPUT
echo "branch=$BRANCH" >> $GITHUB_OUTPUT
exit 0
fi
# Delete branch
if [ "$DRY_RUN" = "true" ]; then
echo "[DRY RUN] Would delete branch: $BRANCH"
echo "status=skipped" >> $GITHUB_OUTPUT
else
echo "Deleting branch: $BRANCH"
gh api -X DELETE "repos/${{ github.repository }}/git/refs/heads/$BRANCH" || true
echo "status=deleted" >> $GITHUB_OUTPUT
fi
echo "branch=$BRANCH" >> $GITHUB_OUTPUT
# Summary
echo "### Branch Cleanup" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "$DRY_RUN" = "true" ]; then
echo "**[DRY RUN]** Would delete: \`$BRANCH\`" >> $GITHUB_STEP_SUMMARY
else
echo "Deleted branch: \`$BRANCH\`" >> $GITHUB_STEP_SUMMARY
fi