Skip to content

Instantly share code, notes, and snippets.

@viperadnan-git
Last active March 3, 2026 23:23
Show Gist options
  • Select an option

  • Save viperadnan-git/5968c1dc3756427c83c69a8a71a41a2a to your computer and use it in GitHub Desktop.

Select an option

Save viperadnan-git/5968c1dc3756427c83c69a8a71a41a2a to your computer and use it in GitHub Desktop.
GitHub Actions Cleanup Script
#!/bin/bash
# GitHub Actions Cleanup Script
# Cancels running workflows and deletes ALL runs (including cancelled ones)
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# Check if gh is installed
if ! command -v gh &> /dev/null; then
echo -e "${RED}Error: gh CLI is not installed. Install it from https://cli.github.com/${NC}"
exit 1
fi
# Check if jq is installed
if ! command -v jq &> /dev/null; then
echo -e "${RED}Error: jq is not installed. Install it with 'apt install jq' or 'brew install jq'${NC}"
exit 1
fi
# Check if authenticated
if ! gh auth status &> /dev/null; then
echo -e "${RED}Error: Not authenticated with gh. Run 'gh auth login' first.${NC}"
exit 1
fi
# Get repository (use current repo or specify one)
REPO="${1:-}"
REPO_FLAG=""
if [ -n "$REPO" ]; then
REPO_FLAG="--repo $REPO"
echo -e "${BLUE}Working on repository: $REPO${NC}"
else
echo -e "${BLUE}Working on current repository${NC}"
fi
echo ""
iteration=1
# Process runs in batches until none remain
while true; do
echo -e "${CYAN}=== Iteration $iteration ===${NC}"
# Fetch batch of runs (max 1000 per request)
runs=$(gh run list $REPO_FLAG --limit 1000 --json databaseId,status,conclusion,displayTitle,workflowName 2>/dev/null)
if [ -z "$runs" ] || [ "$runs" == "[]" ]; then
echo -e "${GREEN}No more workflow runs found.${NC}"
break
fi
batch_count=$(echo "$runs" | jq length)
echo -e "${BLUE}Found $batch_count workflow runs in this batch${NC}"
echo "----------------------------------------"
# Process each run
echo "$runs" | jq -c '.[]' | while read -r run; do
run_id=$(echo "$run" | jq -r '.databaseId')
status=$(echo "$run" | jq -r '.status')
conclusion=$(echo "$run" | jq -r '.conclusion')
title=$(echo "$run" | jq -r '.displayTitle')
workflow=$(echo "$run" | jq -r '.workflowName')
# Truncate title if too long
if [ ${#title} -gt 40 ]; then
title="${title:0:37}..."
fi
echo -ne "Run #$run_id [$workflow] \"$title\" - "
# Check if running (in_progress, queued, waiting, pending, requested)
if [[ "$status" == "in_progress" || "$status" == "queued" || "$status" == "waiting" || "$status" == "pending" || "$status" == "requested" ]]; then
echo -ne "${YELLOW}$status${NC} -> "
# Cancel first
if gh run cancel "$run_id" $REPO_FLAG 2>/dev/null; then
echo -ne "${GREEN}CANCELLED${NC} -> "
# Wait a moment for the cancellation to process
sleep 1
# Then delete
if gh run delete "$run_id" $REPO_FLAG 2>/dev/null; then
echo -e "${GREEN}DELETED${NC}"
else
echo -e "${YELLOW}DELETE PENDING (will retry)${NC}"
fi
else
echo -e "${RED}CANCEL FAILED${NC}"
fi
else
# Run is completed, just delete it
echo -ne "${BLUE}$status${NC}"
[ -n "$conclusion" ] && [ "$conclusion" != "null" ] && echo -ne " ($conclusion)"
echo -ne " -> "
if gh run delete "$run_id" $REPO_FLAG 2>/dev/null; then
echo -e "${GREEN}DELETED${NC}"
else
echo -e "${RED}DELETE FAILED${NC}"
fi
fi
done
echo ""
((iteration++))
# Small delay between iterations to avoid rate limiting
sleep 2
done
echo ""
echo "========================================"
echo -e "${GREEN}Cleanup complete!${NC}"
echo "========================================"
@viperadnan-git
Copy link
Author

Or run directly using curl

curl -fsSL "https://gist.githubusercontent.com/viperadnan-git/5968c1dc3756427c83c69a8a71a41a2a/raw/cleanup-gh-actions.sh" | bash -s -- user/repo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment