Skip to content

Cleanup Old Releases #14

Cleanup Old Releases

Cleanup Old Releases #14

# This workflow automatically cleans up old releases to maintain a manageable number of releases per tool.
#
# Purpose:
# - Fetches all releases from the repository
# - Groups releases by tool name (e.g., "HTML Sample Tool", "PCF Builder", "Data Migrator")
# - Keeps only the top 3 most recent releases for each tool
# - Deletes older releases automatically
#
# Release Name Format:
# - Positive matches: "HTML Sample Tool v0.0.1", "HTML Sample Tool v0.0.2"
# - These are grouped as "HTML Sample Tool" and the newest 3 are kept
# - Negative matches (different tools): "PCF Builder v0.0.1", "Data Migrator v0.0.1"
# - Each tool is handled separately
#
# Features:
# - Scheduled: Runs weekly on Sundays at 00:00 UTC
# - Manual trigger: Can be run manually via workflow_dispatch
# - Dry-run mode: Default mode that shows what would be deleted without actually deleting
# - Delete mode: Set dry_run to 'false' to actually delete old releases
#
# Usage:
# 1. By default (scheduled or manual), runs in DRY RUN mode - safe to test
# 2. To actually delete releases, manually trigger with dry_run='false'
# 3. Review the workflow logs to see what will be deleted before running in delete mode
#
name: Cleanup Old Releases
on:
schedule:
# Run weekly on Sundays at 00:00 UTC
- cron: "0 0 * * 0"
workflow_dispatch:
inputs:
dry_run:
description: "Dry run mode (true to only show what would be deleted, false to actually delete)"
required: false
default: "true"
type: choice
options:
- "true"
- "false"
jobs:
cleanup-releases:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Fetch and cleanup releases
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DRY_RUN: ${{ github.event.inputs.dry_run || 'true' }}
run: |
set -e
echo "==================================="
echo "Release Cleanup Workflow"
echo "==================================="
echo "Dry Run Mode: $DRY_RUN"
echo ""
# Fetch all releases
echo "📥 Fetching all releases..."
gh release list --limit 1000 --json tagName,name,publishedAt,createdAt > releases.json
RELEASE_COUNT=$(jq 'length' releases.json)
echo "Found $RELEASE_COUNT total releases"
echo ""
if [ "$RELEASE_COUNT" -eq 0 ]; then
echo "✅ No releases found. Nothing to clean up."
exit 0
fi
# Parse releases and group by tool name
# Release name format: "Tool Name v0.0.1" or "Tool Name v1.2.3"
# We need to extract the tool name (everything before " v")
echo "🔍 Analyzing releases and grouping by tool..."
# Create a list of tools with their releases
jq -r '.[] | "\(.name)|\(.tagName)|\(.publishedAt // .createdAt // "1970-01-01T00:00:00Z")"' releases.json | while IFS='|' read -r name tag published; do
# Use tag as fallback when the release name is empty/null so grouping still works
display_name="$name"
if [ -z "$display_name" ] || [ "$display_name" = "null" ]; then
display_name="$tag"
fi
if [ -z "$display_name" ]; then
display_name="Unknown Release"
fi
# Extract tool name by removing version pattern (v followed by numbers and dots)
# Match patterns like " v0.0.1", " v1.2.3", etc.
tool_name=$(echo "$display_name" | sed -E 's/ v[0-9]+\.[0-9]+\.[0-9]+.*$//')
# If tool_name is empty or same as name, it might not match our pattern
if [ -z "$tool_name" ] || [ "$tool_name" = "$display_name" ]; then
# Try alternative pattern: remove everything after last space if it starts with v
if echo "$display_name" | grep -qE ' v[0-9]'; then
tool_name=$(echo "$display_name" | sed -E 's/ v[0-9].*$//')
else
tool_name="$display_name"
fi
fi
if [ -z "$tool_name" ]; then
tool_name="Unknown Tool"
fi
echo "$tool_name|$display_name|$tag|$published"
done | sort > releases_with_tools.txt
# Get unique tool names
cut -d'|' -f1 releases_with_tools.txt | sort -u > unique_tools.txt
TOOL_COUNT=$(wc -l < unique_tools.txt)
echo "Identified $TOOL_COUNT unique tools"
echo ""
# For each tool, find releases to delete (all but top 3)
echo "📊 Analysis by Tool:"
echo "===================="
# Clear the delete list file
> releases_to_delete.txt
while read -r tool_name; do
echo ""
echo "Tool: $tool_name"
# Get all releases for this tool, sorted by date (newest first)
# Use grep -F for fixed string matching to avoid regex metacharacter issues
grep -F "$tool_name|" releases_with_tools.txt | sort -t'|' -k4 -r > tool_releases.txt
release_count=$(wc -l < tool_releases.txt)
echo " Total releases: $release_count"
if [ "$release_count" -le 3 ]; then
echo " ✅ Keeping all $release_count releases (≤ 3)"
else
keep_count=3
delete_count=$((release_count - keep_count))
echo " ⚠️ Will delete $delete_count old releases (keeping newest 3)"
# Show what we're keeping
echo " 📌 Keeping:"
head -n 3 tool_releases.txt | while IFS='|' read -r tn rname rtag rpub; do
echo " - $rname (published: $rpub)"
done
# Show what we're deleting
echo " 🗑️ Marking for deletion:"
tail -n +4 tool_releases.txt | while IFS='|' read -r tn rname rtag rpub; do
echo " - $rname (published: $rpub)"
# Append to file instead of variable to avoid subshell issues
echo "$rtag|$rname" >> releases_to_delete.txt
done
fi
done < unique_tools.txt
# Count releases to delete
if [ -s releases_to_delete.txt ]; then
DELETE_COUNT=$(wc -l < releases_to_delete.txt)
else
DELETE_COUNT=0
fi
echo ""
echo "==================================="
echo "Summary"
echo "==================================="
echo "Total releases: $RELEASE_COUNT"
echo "Unique tools: $TOOL_COUNT"
echo "Releases to delete: $DELETE_COUNT"
echo ""
if [ "$DELETE_COUNT" -eq 0 ]; then
echo "✅ Nothing to delete. All tools have ≤ 3 releases."
exit 0
fi
if [ "$DRY_RUN" = "true" ]; then
echo "🔍 DRY RUN MODE - No releases will be deleted"
echo ""
echo "To actually delete these releases, run this workflow again with dry_run=false"
else
echo "⚠️ DELETION MODE - Deleting releases..."
echo ""
# Delete releases
while IFS='|' read -r rtag rname; do
if [ -n "$rtag" ]; then
echo "Deleting: $rname ($rtag)"
if gh release delete "$rtag" --yes --cleanup-tag 2>&1; then
echo " ✅ Deleted successfully"
else
echo " ❌ Failed to delete"
fi
fi
done < releases_to_delete.txt
echo ""
echo "✅ Cleanup complete!"
fi