Automatically deleting GitHub workflow runs when branches are deleted

Automatically deleting GitHub workflow runs when branches are deleted

If you use GitHub Actions, you probably faced the obstacle of deleting workflow runs manually that belonged to long gone branches. So what usually happens is you create a feature branch for example, do your magic there and if you use CI/CD pipelines, probably more than one workflow run will be associated to your branch. Now when you merge it, even if you set the branch to be automatically deleted after merge to main, the workflow runs remain. And after several branches, this pollutes your Actions overview potentially with several hundreds of superfluous workflow runs.

Official GitHub documentation only has description of how to manually delete a workflow run here but I wanted an automated way to do it and although I found some actions on GitHub Marketplace, I wanted a simple and lightweight solution.

GitHub API

So why not do it programmatically ourselves? We can use GitHub API for that, but there are multiple versions of it, more specifically the

  • free (free / pro / team version)

and

  • enterprise

versions. We need the API to interact with the workflow runs, which is described here for the free versions. If you are using enterprise, please select appropriate version in the version selector of the header.

curl

We can use the GitHub API with a well-known CLI tool called curl. It is a command-line tool that lets you send and receive data from servers on the internet, like a Swiss Army knife for making web requests. With curl, you can fetch web pages, download files, interact with APIs, and more, all without needing a graphical web browser. It's a versatile tool used by developers, sysadmins, and anyone who needs to work with web services directly from the command line

Thus using this tool we can send an HTTP DELETE request to the GitHub API to delete workflow runs.

The solution

Define a workflow under .github/workflows folder which will run when a branch is deleted. Let’s call it delete_branch.yml.

name: Delete branch
on: delete
jobs:
  delete_workflow_runs:
    runs-on: ubuntu-latest
    steps:
      - name: Delete workflow runs for deleted branch
        id: delete-workflow-runs-for-deleted-branch
        if: github.event.ref_type == 'branch'
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GITHUB_BRANCH: ${{ github.event.ref }}
          GITHUB_REPOSITORY_URL: ${{ github.event.repository.url }}
        run: |
          echo "Deleting workflow runs for deleted branch ${GITHUB_BRANCH}"
          # Here we have to call GitHub API and process its response

We need to call the GitHub API with the branch name that is about to be deleted. It will return a lot of information about it, including the workflow runs.

curl -s -H "Authorization: Bearer ${GITHUB_TOKEN}" ${GITHUB_REPOSITORY_URL}/actions/runs?branch=${GITHUB_BRANCH}

Now all we have to do is to get only that portion of the information and parse it for the workflow run identifiers. A simple tool called jq comes in handy to process the response we get. It is a lightweight command line JSON processor tool. You can filter / transform / slice or map JSON data with it. Now to get the workflow runs, you need to filter for the key workflow_runs:

curl -s -H "Authorization: Bearer ${GITHUB_TOKEN}" ${GITHUB_REPOSITORY_URL}/actions/runs?branch=${GITHUB_BRANCH} | jq '.workflow_runs'

Then, you need to iterate over the result array:

curl -s -H "Authorization: Bearer ${GITHUB_TOKEN}" ${GITHUB_REPOSITORY_URL}/actions/runs?branch=${GITHUB_BRANCH} | jq '.workflow_runs | .[]'

And select the id key from each item in the array:

curl -s -H "Authorization: Bearer ${GITHUB_TOKEN}" ${GITHUB_REPOSITORY_URL}/actions/runs?branch=${GITHUB_BRANCH} | jq '.workflow_runs | .[] |  .id'

To delete an actual workflow run via GitHub, you need to execute this command with curl:

curl -s -X DELETE -H "Authorization: Bearer ${GITHUB_TOKEN}" ${GITHUB_REPOSITORY_URL}/actions/runs/${workflow_run_id}

Now that we have every step, we can chain these and create a for loop to delete all workflow runs:

for id in $(curl -s -H "Authorization: Bearer ${GITHUB_TOKEN}" ${GITHUB_REPOSITORY_URL}/actions/runs?branch=${GITHUB_BRANCH} | jq '.workflow_runs | .[] |  .id'); do
    curl -s -X DELETE -H "Authorization: Bearer ${GITHUB_TOKEN}" ${GITHUB_REPOSITORY_URL}/actions/runs/${id}
    echo "Deleted workflow run ${id}"
done

TL;DR

Using curl to interact with the GitHub API you can query the workflow runs associated to a branch and then delete them one by one if the branch gets deleted.

The final solution looks like this:

name: Delete branch
on: delete
jobs:
  delete_workflow_runs:
    runs-on: ubuntu-latest
    steps:
      - name: Delete workflow runs for deleted branch
        id: delete-workflow-runs-for-deleted-branch
        if: github.event.ref_type == 'branch'
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GITHUB_BRANCH: ${{ github.event.ref }}
          GITHUB_REPOSITORY_URL: ${{ github.event.repository.url }}
        run: |
          echo "Deleting workflow runs for deleted branch ${GITHUB_BRANCH}"
          for id in $(curl -s -H "Authorization: Bearer ${GITHUB_TOKEN}" ${GITHUB_REPOSITORY_URL}/actions/runs?branch=${GITHUB_BRANCH} | jq '.workflow_runs | .[] |  .id'); do
            curl -s -X DELETE -H "Authorization: Bearer ${GITHUB_TOKEN}" ${GITHUB_REPOSITORY_URL}/actions/runs/${id}
            echo "Deleted workflow run ${id}"
          done