Skip to content

Branch Cleanup

Branch Cleanup #38

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`);
}
}