@@ -142,8 +142,8 @@ def split_task_id(task_id):
142142# as a Python object.
143143#
144144# repo includes the user, like "postgres/postgres".
145- def get_github_api (repo , action , params = None , none_for_404 = False ):
146- url = "https://api.github.com/repos/" + repo + "/actions/" + action
145+ def get_github_api (repo , action , params = None , none_for_404 = False , prefix = "actions/" ):
146+ url = "https://api.github.com/repos/" + repo + "/" + prefix + action
147147 headers = {
148148 "Accept" : "application/vnd.github+json" ,
149149 "X-GitHub-Api-Version" : "2026-03-10" ,
@@ -768,6 +768,88 @@ def refresh_build_status_statistics(conn):
768768 group by 1, 2""" )
769769
770770
771+ def gc_remote_branches (conn ):
772+ # This horrible big lock prevents branches from being created or deleted
773+ # while we are paginating through them...
774+ #
775+ # XXX Could do better than this... one problem is that we only create
776+ # branch records after pushing, creating a window for deleting a branch
777+ # we'd just pushed...
778+ with cfbot_util .lock ():
779+ # File the branches that still have build records
780+ cursor = conn .cursor ()
781+
782+ # Find all the branches we still have builds for.
783+ live_branches = set ()
784+ cursor .execute (
785+ """select distinct branch_name
786+ from build
787+ where build_id like %s || ':%%'
788+ order by 1""" ,
789+ (cfbot_config .GITHUB_FULL_REPO ,),
790+ )
791+ for (branch ,) in cursor :
792+ live_branches .add (branch )
793+
794+ # There could also be very recently pushed branches that we don't have
795+ # a build for yet.
796+ #
797+ # XXX Maybe branch needs a branch_name column? This is not nice...
798+ cursor .execute ("""select distinct 'cf/' || submission_id
799+ from branch
800+ where created > now() - interval '24 hours'""" )
801+ for (branch ,) in cursor :
802+ live_branches .add (branch )
803+
804+ # We can delete branches in batches (this is limited by command line
805+ # length and by index key length, but 512 has worked...).
806+ BATCH_SIZE = 128
807+ MAX_BATCHES = 4
808+ batches = 0
809+ batch = []
810+
811+ # Since we don't have a stable snapshot due to pagination via the API,
812+ # deduplicate the branch names we see.
813+ seen_branches = set ()
814+
815+ page = 0
816+ while batches < MAX_BATCHES :
817+ remote_branches = get_github_api (
818+ cfbot_config .GITHUB_FULL_REPO ,
819+ "branches" ,
820+ prefix = "" ,
821+ params = {"per_page" : "100" , "page" : str (page )},
822+ )
823+ # print("page %s got %s branches" % (page, len(remote_branches)))
824+ if len (remote_branches ) == 0 :
825+ break
826+ for remote_branch in remote_branches :
827+ branch = remote_branch ["name" ]
828+ if branch in seen_branches :
829+ continue
830+ seen_branches .add (branch )
831+ if re .match (cfbot_config .GITHUB_MIRROR_BRANCH_PATTERN , branch ):
832+ continue
833+ if branch in live_branches :
834+ continue
835+ logging .info ("will drop unreferenced remote branch " + branch )
836+ batch .append (branch )
837+ if len (batch ) == BATCH_SIZE :
838+ cfbot_work_queue .insert_work_queue (
839+ cursor , "push-delete-branch" , " " .join (batch )
840+ )
841+ batch = []
842+ batches += 1
843+ if batches == MAX_BATCHES :
844+ break
845+ page += 1
846+
847+ if len (batch ) > 0 :
848+ cfbot_work_queue .insert_work_queue (
849+ cursor , "push-delete-branch" , " " .join (batch )
850+ )
851+
852+
771853# ======================================================================
772854# Functions called by flask when our webhook is called by Github.
773855# ======================================================================
@@ -849,6 +931,10 @@ def poll_github_run(conn, key):
849931# ======================================================================
850932
851933if __name__ == "__main__" :
934+ with cfbot_util .db () as conn :
935+ gc_remote_branches (conn )
936+ conn .commit ()
937+ exit (0 )
852938 # print(json.dumps(get_builds_for_commit("3c970e3e544bb17a894854c027d3d3bc285fb072"), indent=4))
853939 # print(json.dumps(get_tasks_for_build("26492296536"), indent=4))
854940 # print(json.dumps(get_github_api("runs/" + "264922965360", none_for_404=True), indent=4))
0 commit comments