Post

Cleaning GitHub Workflow History: The Fast and Async Way

🇬🇧 How to clean thousands of GitHub Action history items in one line and asynchronously? Cleaning in turbo mode with gh CLI and xargs.

Cleaning GitHub Workflow History: The Fast and Async Way

When developing an open source project - like Everything curl which I’m currently working on - CI/CD processes can be very intense. Every commit, every pull request leaves behind dozens of “workflow runs”.

Over time, the GitHub Actions tab turns into a junkyard filled with failed attempts and old logs. It creates visual pollution and makes it hard to find what you’re looking for.

Today, we’ll look at how to clean this history using GitHub CLI (gh) and how to speed it up 10x by making it asynchronous.

🔐 Prerequisite: Login

Before running these commands, you must be logged in to your account via GitHub CLI:

1
gh auth login

🐧 Linux and 🍎 macOS (Bash/Zsh)

On Unix-based systems (Linux, macOS, WSL), we will use the magnificent trio of gh, jq, and xargs to accomplish this.

🛠️ The Command

The following command fetches the last 1000 runs in the repo and starts the deletion process through 10 different channels simultaneously:

1
gh run list --repo fr0stb1rd/her-yonuyle-curl --limit 1000 --json databaseId --jq '.[].databaseId' | xargs -P 10 -I{} gh run delete {} --repo fr0stb1rd/her-yonuyle-curl

(If you are already inside the repo folder, you don’t need to write the --repo ... parts.)

🔬 Anatomy of the Command

Let’s analyze what this line does step by step:

  1. gh run list --limit 1000: Lists the last 1000 workflow runs.
  2. --json databaseId --jq '.[].databaseId': We don’t need date, name, or status. It just fetches “ID” numbers as a pure list (via JSON query).
  3. | (Pipe): Transfers the resulting ID list to the next command.
  4. xargs: Here is our main hero. It takes the incoming list as arguments and gives them to another command.
    • -P 10: This is the most critical point. It opens “Parallel” mode. It performs operations not sequentially, but by running 10 delete commands at the same time.
    • -I{}: Represents each ID with the {} placeholder.
  5. gh run delete {}: The final deletion action.

🪟 Windows (PowerShell Core)

Windows users often feel left behind in such “pipe” operations, but the situation has changed with PowerShell 7 (Core). You can achieve the same speed with the -Parallel parameter of the ForEach-Object command.

🛠️ The Command

1
gh run list --limit 1000 --json databaseId --jq '.[].databaseId' | ForEach-Object -Parallel { gh run delete $_ } -ThrottleLimit 10

🔬 Anatomy of the Command

  1. gh run list ...: Same as the Linux side. Dumps the ID list as JSON.
  2. | (Pipe): Pipes the output to the ForEach-Object command.
  3. ForEach-Object -Parallel { ... }:
    • -Parallel: Runs the code block inside the curly braces on a separate thread for each incoming object.
    • gh run delete $_: $_ represents the current pipeline object (i.e., the current run ID) and executes the delete command.
  4. -ThrottleLimit 10: Determines the maximum number of operations (threads) to run simultaneously. Functions the same as -P 10 in Linux.

This command requires PowerShell 7 or higher. The old PowerShell 5.1 that comes installed with Windows does not support the -Parallel parameter. If you are using an old version, you can run it by removing the -Parallel and -ThrottleLimit parts (albeit slowly).

⚡ Why Async? (Speed Factor)

If you don’t use the -P (Linux/macOS) or -Parallel (Windows) parameter, operations are performed sequentially. For 1000 records, 1000 separate API requests are made and each one is waited on to finish.

In async mode, your computer tells GitHub “Hey, delete these 10”, and while they are being deleted, it prepares the next 10. This theoretically speeds up the process up to 10 times.

Don’t exaggerate the number to 50 or 100. You might get stuck on GitHub API’s “Rate Limit”. 10-20 is a safe limit.

🎉 Conclusion

Keeping a clean workspace is important while developing documentation and tools in the “Everything curl” project. This one-liner command reduces a cleanup that would take minutes down to seconds.

See you on the next git push!

This post is licensed under CC BY 4.0 by the author.