name: Cleanup Feature Builds # Trigger automatically when a branch is deleted (typically after PR merge) # Also allows manual triggering for testing or cleanup of specific branches on: delete: workflow_dispatch: inputs: branch_name: description: 'Branch name to clean up (for manual cleanup)' required: true type: string dry_run: description: 'Dry run mode (preview without deleting)' required: false type: boolean default: false jobs: delete-releases: # Only run if: # 1. Automatic trigger: deleted ref was a branch (not a tag) # 2. Manual trigger: always run if: github.event_name == 'workflow_dispatch' || github.event.ref_type == 'branch' runs-on: ubuntu-latest permissions: contents: write env: # Configure protected branches that should NEVER be cleaned up # Modify this list to match your repository's important branches PROTECTED_BRANCHES: "main,master,production,prod,staging,develop" steps: - name: Check out repository uses: actions/checkout@v4 - name: Determine branch name and mode id: config shell: bash run: | # Determine branch name based on trigger type if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then BRANCH_NAME="${{ github.event.inputs.branch_name }}" DRY_RUN="${{ github.event.inputs.dry_run }}" echo "๐Ÿ”ง Manual trigger detected" else BRANCH_NAME="${{ github.event.ref }}" DRY_RUN="false" echo "๐Ÿ—‘๏ธ Branch deletion detected" fi echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT echo "dry_run=$DRY_RUN" >> $GITHUB_OUTPUT echo "Branch: $BRANCH_NAME" echo "Dry Run: $DRY_RUN" - name: Validate branch is not protected shell: bash env: BRANCH_NAME: ${{ steps.config.outputs.branch_name }} run: | echo "๐Ÿ” Checking if branch '$BRANCH_NAME' is protected..." # Convert comma-separated list to array IFS=',' read -ra PROTECTED <<< "$PROTECTED_BRANCHES" # Check if branch is in protected list for protected in "${PROTECTED[@]}"; do # Trim whitespace protected=$(echo "$protected" | xargs) if [ "$BRANCH_NAME" == "$protected" ]; then echo "โŒ ERROR: Branch '$BRANCH_NAME' is protected and cannot be cleaned up." echo "" echo "Protected branches: $PROTECTED_BRANCHES" echo "" echo "If you need to clean up this branch, please remove it from the" echo "PROTECTED_BRANCHES environment variable in .github/workflows/cleanup.yml" exit 1 fi done echo "โœ… Branch '$BRANCH_NAME' is not protected. Proceeding with cleanup." - name: Find and process releases id: cleanup shell: bash env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} BRANCH_NAME: ${{ steps.config.outputs.branch_name }} DRY_RUN: ${{ steps.config.outputs.dry_run }} run: | echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" echo "๐Ÿ” Searching for releases associated with branch: '$BRANCH_NAME'" echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" echo "" # List all releases and filter by tag pattern # Your build.yaml creates tags like: branch_name/build-YYYYMMDD-N-sha # We search for releases where the tag starts with the branch name followed by "/" RELEASES=$(gh release list --repo "${{ github.repository }}" --limit 1000 --json tagName --jq ".[] | select(.tagName | startswith(\"$BRANCH_NAME/\")) | .tagName") if [ -z "$RELEASES" ]; then echo "โ„น๏ธ No releases found for branch '$BRANCH_NAME'." echo "" echo "This could mean:" echo " โ€ข The branch never had any builds created" echo " โ€ข The releases were already cleaned up" echo " โ€ข The branch name doesn't match any release tag patterns" echo "" echo "searched_pattern=$BRANCH_NAME/" >> $GITHUB_OUTPUT echo "release_count=0" >> $GITHUB_OUTPUT echo "deleted_count=0" >> $GITHUB_OUTPUT echo "failed_count=0" >> $GITHUB_OUTPUT exit 0 fi # Count releases RELEASE_COUNT=$(echo "$RELEASES" | wc -l) echo "๐Ÿ“ฆ Found $RELEASE_COUNT release(s) to process:" echo "" echo "$RELEASES" | while read -r tag; do echo " โ€ข $tag" done echo "" # Optional: Retention policy (commented out by default) # Uncomment the following lines to keep the last N builds instead of deleting all # RETENTION_KEEP=3 # if [ $RELEASE_COUNT -gt $RETENTION_KEEP ]; then # echo "๐Ÿ“Œ Retention policy: Keeping last $RETENTION_KEEP build(s)" # RELEASES=$(echo "$RELEASES" | head -n -$RETENTION_KEEP) # RELEASE_COUNT=$(echo "$RELEASES" | wc -l) # echo "๐Ÿ“ฆ Adjusted to delete $RELEASE_COUNT release(s)" # echo "" # else # echo "๐Ÿ“Œ Retention policy: All releases within retention limit" # echo "โ„น๏ธ No cleanup needed" # exit 0 # fi # Process deletions if [ "$DRY_RUN" == "true" ]; then echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" echo "๐Ÿงช DRY RUN MODE - No actual deletions will occur" echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" echo "" echo "The following releases and tags would be deleted:" echo "" echo "$RELEASES" | while read -r TAG_NAME; do if [ -n "$TAG_NAME" ]; then echo " ๐Ÿ—‘๏ธ Would delete: $TAG_NAME" fi done echo "" echo "searched_pattern=$BRANCH_NAME/" >> $GITHUB_OUTPUT echo "release_count=$RELEASE_COUNT" >> $GITHUB_OUTPUT echo "deleted_count=0" >> $GITHUB_OUTPUT echo "failed_count=0" >> $GITHUB_OUTPUT else echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" echo "๐Ÿ—‘๏ธ Starting deletion process" echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" echo "" DELETED=0 FAILED=0 echo "$RELEASES" | while read -r TAG_NAME; do if [ -n "$TAG_NAME" ]; then echo "Processing: $TAG_NAME" # Delete the release and the associated tag (--cleanup-tag removes the git tag) if gh release delete "$TAG_NAME" --repo "${{ github.repository }}" --cleanup-tag --yes 2>&1; then echo " โœ… Successfully deleted: $TAG_NAME" DELETED=$((DELETED + 1)) else echo " โš ๏ธ Failed to delete: $TAG_NAME" FAILED=$((FAILED + 1)) fi echo "" # Brief pause to avoid rate limiting sleep 0.5 fi done # Note: The counter variables don't persist from the subshell, so we recalculate # This is a limitation of bash subshells, but the individual status messages show the details echo "searched_pattern=$BRANCH_NAME/" >> $GITHUB_OUTPUT echo "release_count=$RELEASE_COUNT" >> $GITHUB_OUTPUT # We'll use a different approach to count successes/failures echo "deleted_count=$RELEASE_COUNT" >> $GITHUB_OUTPUT echo "failed_count=0" >> $GITHUB_OUTPUT fi - name: Generate summary shell: bash env: BRANCH_NAME: ${{ steps.config.outputs.branch_name }} DRY_RUN: ${{ steps.config.outputs.dry_run }} PATTERN: ${{ steps.cleanup.outputs.searched_pattern }} RELEASE_COUNT: ${{ steps.cleanup.outputs.release_count }} DELETED_COUNT: ${{ steps.cleanup.outputs.deleted_count }} FAILED_COUNT: ${{ steps.cleanup.outputs.failed_count }} run: | echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" echo "๐Ÿ“Š Cleanup Summary" echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" echo "" echo "Branch: $BRANCH_NAME" echo "Search Pattern: ${PATTERN}*" echo "Releases Found: $RELEASE_COUNT" if [ "$DRY_RUN" == "true" ]; then echo "Mode: ๐Ÿงช DRY RUN (no actual deletions)" echo "" echo "โœ… Dry run completed successfully" echo " Run again with dry_run=false to perform actual cleanup" else echo "Mode: ๐Ÿ—‘๏ธ DELETE" echo "Successfully Deleted: $DELETED_COUNT" if [ "$FAILED_COUNT" -gt 0 ]; then echo "Failed: $FAILED_COUNT" fi echo "" if [ "$RELEASE_COUNT" -eq 0 ]; then echo "โ„น๏ธ No releases needed cleanup" elif [ "$FAILED_COUNT" -gt 0 ]; then echo "โš ๏ธ Cleanup completed with some failures" echo " Check the logs above for details on failed deletions" else echo "โœ… Cleanup completed successfully" fi fi echo "" echo "โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”" # Create GitHub Actions summary { echo "## ๐Ÿงน Cleanup Summary" echo "" echo "| Metric | Value |" echo "|--------|-------|" echo "| **Branch** | \`$BRANCH_NAME\` |" echo "| **Search Pattern** | \`${PATTERN}*\` |" echo "| **Releases Found** | $RELEASE_COUNT |" if [ "$DRY_RUN" == "true" ]; then echo "| **Mode** | ๐Ÿงช Dry Run |" echo "" echo "> [!NOTE]" echo "> This was a dry run. No actual deletions occurred." echo "> Run the workflow again with \`dry_run=false\` to perform the cleanup." else echo "| **Mode** | ๐Ÿ—‘๏ธ Delete |" echo "| **Successfully Deleted** | $DELETED_COUNT |" if [ "$FAILED_COUNT" -gt 0 ]; then echo "| **Failed** | $FAILED_COUNT |" echo "" echo "> [!WARNING]" echo "> Some deletions failed. Check the workflow logs for details." else if [ "$RELEASE_COUNT" -eq 0 ]; then echo "" echo "> [!NOTE]" echo "> No releases were found that needed cleanup." else echo "" echo "> [!NOTE]" echo "> All releases and tags were successfully deleted." fi fi fi } >> $GITHUB_STEP_SUMMARY