[llvm] [Github] Add initial workflow to prune unused user branches (PR #175693)
Aiden Grossman via llvm-commits
llvm-commits at lists.llvm.org
Sun Jan 18 12:57:38 PST 2026
https://github.com/boomanaiden154 updated https://github.com/llvm/llvm-project/pull/175693
>From e05438475e1ed95ce063b6e6969354dd77399840 Mon Sep 17 00:00:00 2001
From: Aiden Grossman <aidengrossman at google.com>
Date: Tue, 13 Jan 2026 01:40:45 +0000
Subject: [PATCH 1/3] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20in?=
=?UTF-8?q?itial=20version?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Created using spr 1.3.7
---
.github/workflows/prune-branches.yml | 30 ++++++++
.github/workflows/prune-unused-branches.py | 89 ++++++++++++++++++++++
2 files changed, 119 insertions(+)
create mode 100644 .github/workflows/prune-branches.yml
create mode 100644 .github/workflows/prune-unused-branches.py
diff --git a/.github/workflows/prune-branches.yml b/.github/workflows/prune-branches.yml
new file mode 100644
index 0000000000000..9a21a9826e486
--- /dev/null
+++ b/.github/workflows/prune-branches.yml
@@ -0,0 +1,30 @@
+name: Prune Branches
+
+permissions:
+ contents: read
+
+on:
+ pull_request:
+ paths:
+ - .github/workflows/prune-branches.yml
+ schedule:
+ - cron: '0 8 * * *'
+
+jobs:
+ prune-branches:
+ name: Prune Branches
+ if: github.repository_owner == 'llvm'
+ runs-on: ubuntu-24.04
+ steps:
+ - name: Fetch LLVM sources
+ uses: actions/checkout at 8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
+ with:
+ fetch-depth: 0
+ - name: Install dependencies
+ run: |
+ pip install --require-hashes -r ./llvm/utils/git/requirements.txt
+ - name: Run Script
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ run: |
+ python3 .github/workflows/prune-unused-branches.py
diff --git a/.github/workflows/prune-unused-branches.py b/.github/workflows/prune-unused-branches.py
new file mode 100644
index 0000000000000..ab09faa63acd2
--- /dev/null
+++ b/.github/workflows/prune-unused-branches.py
@@ -0,0 +1,89 @@
+import subprocess
+import os
+
+import github
+
+
+def get_branches() -> list[str]:
+ git_process = subprocess.run(
+ ["git", "branch", "--all"], stdout=subprocess.PIPE, check=True
+ )
+ branches = [
+ branch.strip() for branch in git_process.stdout.decode("utf-8").split("\n")
+ ]
+
+ def branch_filter(branch_name):
+ return "users/" in branch_name or "revert-" in branch_name
+
+ filtered_branches = list(filter(branch_filter, branches))
+ return [branch.replace("remotes/origin/", "") for branch in filtered_branches]
+
+
+def get_branches_from_open_prs(github_token) -> list[str]:
+ gh = github.Github(auth=github.Auth.Token(github_token))
+ query = """
+ query ($after: String) {
+ search(query: "is:pr repo:llvm/llvm-project is:open head:users/", type: ISSUE, first: 100, after: $after) {
+ nodes {
+ ... on PullRequest {
+ baseRefName
+ headRefName
+ isCrossRepository
+ number
+ }
+ }
+ pageInfo {
+ hasNextPage
+ endCursor
+ }
+ }
+ }"""
+ pr_data = []
+ has_next_page = True
+ variables = {"after": None}
+ while has_next_page:
+ _, res_data = gh.requester.graphql_query(query, variables=variables)
+ import json
+
+ with open("/tmp/test.json", "w") as test_file:
+ json.dump(res_data, test_file)
+ page_info = res_data["data"]["search"]["pageInfo"]
+ has_next_page = page_info["hasNextPage"]
+ if has_next_page:
+ variables["after"] = page_info["endCursor"]
+ prs = res_data["data"]["search"]["nodes"]
+ pr_data.extend(prs)
+ print(f"Processed {len(prs)} PRs")
+
+ user_branches = []
+ for pr in pr_data:
+ if not pr["isCrossRepository"]:
+ if pr["baseRefName"] != "main":
+ user_branches.append(pr["baseRefName"])
+ user_branches.append(pr["headRefName"])
+ return user_branches
+
+
+def get_user_branches_to_remove(
+ user_branches: list[str], user_branches_from_prs: list[str]
+) -> list[str]:
+ user_branches_to_remove = set(user_branches)
+ for pr_user_branch in set(user_branches_from_prs):
+ user_branches_to_remove.remove(pr_user_branch)
+ return list(user_branches_to_remove)
+
+
+def main(github_token):
+ user_branches = get_branches()
+ user_branches_from_prs = get_branches_from_open_prs(github_token)
+ print(f"Found {len(user_branches)} user branches in the repository")
+ print(f"Found {len(user_branches_from_prs)} user branches associated with PRs")
+ user_branches_to_remove = get_user_branches_to_remove(
+ user_branches, user_branches_from_prs
+ )
+ print(f"Deleting {len(user_branches_to_remove)} user branches.")
+ print(user_branches_to_remove)
+
+
+if __name__ == "__main__":
+ main(os.environ["GITHUB_TOKEN"])
>From 9642bc5849198ff577457d0d0267fcdb20e57f62 Mon Sep 17 00:00:00 2001
From: Aiden Grossman <aidengrossman at google.com>
Date: Tue, 13 Jan 2026 04:34:11 +0000
Subject: [PATCH 2/3] feedback
Created using spr 1.3.7
---
.github/workflows/prune-unused-branches.py | 4 ----
1 file changed, 4 deletions(-)
diff --git a/.github/workflows/prune-unused-branches.py b/.github/workflows/prune-unused-branches.py
index ab09faa63acd2..a784cb6db777c 100644
--- a/.github/workflows/prune-unused-branches.py
+++ b/.github/workflows/prune-unused-branches.py
@@ -43,10 +43,6 @@ def get_branches_from_open_prs(github_token) -> list[str]:
variables = {"after": None}
while has_next_page:
_, res_data = gh.requester.graphql_query(query, variables=variables)
- import json
-
- with open("/tmp/test.json", "w") as test_file:
- json.dump(res_data, test_file)
page_info = res_data["data"]["search"]["pageInfo"]
has_next_page = page_info["hasNextPage"]
if has_next_page:
>From 1656a0792a07a882b5d817bdbbdc51bb99b27160 Mon Sep 17 00:00:00 2001
From: Aiden Grossman <aidengrossman at google.com>
Date: Sun, 18 Jan 2026 20:57:26 +0000
Subject: [PATCH 3/3] fix
Created using spr 1.3.7
---
.github/workflows/prune-branches.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/prune-branches.yml b/.github/workflows/prune-branches.yml
index 9a21a9826e486..f8dce5e1e54c8 100644
--- a/.github/workflows/prune-branches.yml
+++ b/.github/workflows/prune-branches.yml
@@ -1,4 +1,4 @@
-name: Prune Branches
+name: Prune Unused Branches
permissions:
contents: read
@@ -8,7 +8,7 @@ on:
paths:
- .github/workflows/prune-branches.yml
schedule:
- - cron: '0 8 * * *'
+ - cron: '0 8 * * *' # Runs daily at 08:00 UTC.
jobs:
prune-branches:
More information about the llvm-commits
mailing list