Skip to content

Instantly share code, notes, and snippets.

@viperadnan-git
Last active January 7, 2026 22:08
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

GitHub Actions Cleanup Script

A bash script to bulk cancel and delete all GitHub Actions workflow runs from a repository.

What it does

  • Running/queued/pending workflows → Cancels them, then deletes
  • Completed workflows (success, failure, cancelled, skipped) → Deletes immediately
  • Recursive processing → Continues until all workflow runs are removed

Prerequisites

Installation

macOS:

brew install gh jq

Ubuntu/Debian:

sudo apt install gh jq

Authenticate with GitHub:

gh auth login

Usage

# Make executable
chmod +x cleanup-gh-actions.sh

# Run in current git repository
./cleanup-gh-actions.sh

# Run for a specific repository
./cleanup-gh-actions.sh owner/repo-name

Example Output

Working on repository: myorg/my-repo

=== Iteration 1 ===
Found 150 workflow runs in this batch
----------------------------------------
Run #12345678 [CI] "feat: add new feature" - in_progress -> CANCELLED -> DELETED
Run #12345677 [CI] "fix: bug fix" - completed (success) -> DELETED
Run #12345676 [Deploy] "chore: update deps" - completed (cancelled) -> DELETED
...

=== Iteration 2 ===
No more workflow runs found.

========================================
Cleanup complete!
========================================

How it works

  1. Fetches up to 1000 workflow runs per iteration
  2. For each run:
    • If running/queued/pending: cancels first, waits 1 second, then deletes
    • If completed: deletes immediately
  3. Repeats until no runs remain
  4. Includes 2-second delay between iterations to avoid API rate limiting

Notes

  • The script requires write access to the repository's Actions
  • Rate limiting: GitHub API has rate limits; the script includes delays to mitigate this
  • Large repositories with thousands of runs may take several minutes to fully clean

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