From 46ba646b62dd339959616db2e693d96e4297a553 Mon Sep 17 00:00:00 2001 From: Jeremy Massel <1123407+jkmassel@users.noreply.github.com> Date: Tue, 5 May 2026 17:31:55 -0600 Subject: [PATCH] ci: prune `pr-build/*` branches for closed PRs on trunk pushes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each PR build force-pushes a `pr-build/` snapshot branch. Nothing prunes them, so they accumulate. Add a Buildkite step that runs on trunk pushes, lists `pr-build/*` refs, queries each PR's state via the GitHub API, and deletes the refs whose PR is `closed` (covers both merged and rejected — GitHub collapses them). Skips a branch on any non-200 response so we never delete a ref we couldn't verify. Mirrors the wordpress-rs sweep step. --- .buildkite/cleanup-pr-build-branches.sh | 86 +++++++++++++++++++++++++ .buildkite/pipeline.yml | 9 ++- 2 files changed, 93 insertions(+), 2 deletions(-) create mode 100755 .buildkite/cleanup-pr-build-branches.sh diff --git a/.buildkite/cleanup-pr-build-branches.sh b/.buildkite/cleanup-pr-build-branches.sh new file mode 100755 index 00000000..13db49b6 --- /dev/null +++ b/.buildkite/cleanup-pr-build-branches.sh @@ -0,0 +1,86 @@ +#!/bin/bash + +set -euo pipefail + +# Deletes `pr-build/` branches whose PR is closed (merged or rejected). +# Runs on trunk pushes so the just-merged PR's branch gets cleaned up +# immediately, and any orphans accumulated from prior failures get swept too. + +if [[ "${BUILDKITE_BRANCH:-}" != "trunk" ]]; then + echo "Not a trunk build (branch=${BUILDKITE_BRANCH:-unset}), skipping" + exit 0 +fi + +if [[ -z "${GITHUB_TOKEN:-}" ]]; then + echo "GITHUB_TOKEN not set, cannot query PR state" >&2 + exit 1 +fi + +GITHUB_REPO="wordpress-mobile/GutenbergKit" + +echo '--- :robot_face: Use bot for Git operations' +source use-bot-for-git + +echo "--- :mag: Listing pr-build/* branches on origin" +mapfile -t branches < <( + git ls-remote --heads origin 'refs/heads/pr-build/*' \ + | awk '{print $2}' \ + | sed 's|^refs/heads/||' +) + +echo "Found ${#branches[@]} pr-build branches" + +if [[ ${#branches[@]} -eq 0 ]]; then + exit 0 +fi + +echo "--- :github: Checking PR state for each branch" +to_delete=() +for branch in "${branches[@]}"; do + pr_number="${branch#pr-build/}" + if ! [[ "$pr_number" =~ ^[0-9]+$ ]]; then + echo "Skipping $branch (unexpected suffix)" + continue + fi + + response=$( + curl --silent --show-error \ + --write-out $'\n%{http_code}' \ + --header "Authorization: Bearer ${GITHUB_TOKEN}" \ + --header "Accept: application/vnd.github+json" \ + --header "X-GitHub-Api-Version: 2022-11-28" \ + "https://api.github.com/repos/${GITHUB_REPO}/pulls/${pr_number}" + ) + http_code=$(printf '%s' "$response" | tail -n1) + body=$(printf '%s' "$response" | sed '$d') + + if [[ "$http_code" != "200" ]]; then + echo "Skipping $branch (HTTP $http_code from GitHub)" + continue + fi + + state=$(printf '%s' "$body" | jq -r '.state') + + if [[ "$state" == "closed" ]]; then + echo "Marking $branch for deletion (PR #$pr_number is closed)" + to_delete+=("$branch") + else + echo "Keeping $branch (PR #$pr_number is $state)" + fi +done + +if [[ ${#to_delete[@]} -eq 0 ]]; then + echo "No closed PR branches to delete" + exit 0 +fi + +echo "--- :wastebasket: Deleting ${#to_delete[@]} stale branches" +chunk_size=50 +for ((i=0; i<${#to_delete[@]}; i+=chunk_size)); do + chunk=("${to_delete[@]:i:chunk_size}") + refspecs=() + for branch in "${chunk[@]}"; do + refspecs+=(":refs/heads/${branch}") + done + git push origin "${refspecs[@]}" +done diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index fac81e13..ab029fc1 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -80,8 +80,8 @@ steps: - label: ':xcode: Build XCFramework' key: build-xcframework depends_on: - - build-react - - swift-test-library + - build-react + - swift-test-library command: | buildkite-agent artifact download dist.tar.gz . tar -xzf dist.tar.gz @@ -149,3 +149,8 @@ steps: - 'android/Gutenberg/build/outputs/androidTest-results/connected/**/*' - 'android/Gutenberg/build/outputs/buildkite-logs/**/*' - 'android/Gutenberg/build/outputs/connected_android_test_additional_output/**/*' + + - label: ':wastebasket: Clean up `pr-build/*` branches for closed PRs' + if: build.branch == "trunk" + command: .buildkite/cleanup-pr-build-branches.sh + plugins: *plugins