From 16f2722c85f05e06d7132cd6dca0d00be92dc014 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Mar 2026 23:15:03 +0000 Subject: [PATCH 1/2] Initial plan From f00abf0976b4538c76c576d25d2c63fdb2d6d9ad Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Mar 2026 23:17:21 +0000 Subject: [PATCH 2/2] feat: add daily branch cleanup workflow Co-authored-by: tassan <7563760+tassan@users.noreply.github.com> --- .github/workflows/branch-cleanup.yml | 67 ++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 .github/workflows/branch-cleanup.yml diff --git a/.github/workflows/branch-cleanup.yml b/.github/workflows/branch-cleanup.yml new file mode 100644 index 0000000..b1db84a --- /dev/null +++ b/.github/workflows/branch-cleanup.yml @@ -0,0 +1,67 @@ +name: Branch Cleanup + +on: + schedule: + - cron: '0 3 * * *' # daily at 03:00 UTC + workflow_dispatch: # allow manual trigger + +jobs: + delete-merged-branches: + name: Delete merged branches + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - name: Delete merged branches + uses: actions/github-script@v7 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const protectedBranches = ['main', 'master', 'develop']; + + // Resolve the repository's default branch dynamically + const { data: repo } = await github.rest.repos.get({ + owner: context.repo.owner, + repo: context.repo.repo, + }); + const defaultBranch = repo.default_branch; + core.info(`Default branch: ${defaultBranch}`); + + // Paginate through all branches + const branches = await github.paginate(github.rest.repos.listBranches, { + owner: context.repo.owner, + repo: context.repo.repo, + per_page: 100, + }); + + for (const branch of branches) { + if (protectedBranches.includes(branch.name)) { + core.info(`Skipping protected branch: ${branch.name}`); + continue; + } + + // Check whether the branch has been fully merged into the default branch + let compare; + try { + ({ data: compare } = await github.rest.repos.compareCommitsWithBasehead({ + owner: context.repo.owner, + repo: context.repo.repo, + basehead: `${defaultBranch}...${branch.name}`, + })); + } catch (err) { + core.info(`Could not compare ${branch.name} – skipping (${err.message})`); + continue; + } + + if (compare.ahead_by === 0) { + core.info(`Deleting merged branch: ${branch.name}`); + await github.rest.git.deleteRef({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: `heads/${branch.name}`, + }); + } else { + core.info(`Branch ${branch.name} has ${compare.ahead_by} unmerged commit(s) – skipping`); + } + }