@@ -881,6 +881,33 @@ def remove_stubs(distribution: str) -> None:
881881 f .writelines (lines )
882882
883883
884+ async def delete_closed_stubsabot_branches (session : aiohttp .ClientSession ) -> None :
885+ """Delete `stubsabot/*` branch if the PR is already closed."""
886+ fork_owner = get_origin_owner ()
887+
888+ branches = subprocess .run (["git" , "ls-remote" , "--heads" , "origin" ], capture_output = True , text = True , check = True )
889+ for line in branches .stdout .splitlines ():
890+ _ , ref = line .split ()
891+ if not ref .startswith (f"refs/heads/{ BRANCH_PREFIX } /" ):
892+ continue
893+ branch = ref .removeprefix ("refs/heads/" )
894+
895+ # Fetch all open PRs for an existing branch:
896+ async with session .get (
897+ f"{ TYPESHED_API_URL } /pulls" ,
898+ params = {"state" : "open" , "head" : f"{ fork_owner } :{ branch } " , "base" : "main" },
899+ headers = get_github_api_headers (),
900+ ) as response :
901+ response .raise_for_status ()
902+ open_prs = await response .json ()
903+
904+ # If we don't have an open PR for an existing branch,
905+ # it means PR was closed and branch was forgotten to be deleted:
906+ if not open_prs :
907+ print (colored (f"Deleting branch { branch } on origin..." , "red" ))
908+ subprocess .check_call (["git" , "push" , "origin" , "--delete" , branch ])
909+
910+
884911async def suggest_typeshed_update (update : Update , session : aiohttp .ClientSession , action_level : ActionLevel ) -> None :
885912 if action_level <= ActionLevel .nothing :
886913 return
@@ -1002,6 +1029,9 @@ async def main() -> int:
10021029 try :
10031030 conn = aiohttp .TCPConnector (limit_per_host = 10 )
10041031 async with aiohttp .ClientSession (connector = conn ) as session :
1032+ # First we clean the old branches:
1033+ await delete_closed_stubsabot_branches (session )
1034+
10051035 tasks = [
10061036 asyncio .create_task (determine_action (distribution , session ))
10071037 for distribution in dists_to_update
0 commit comments