Skip to content

feat: daily automated branch cleanup workflow - #11

Merged
tassan merged 2 commits into
mainfrom
copilot/automate-branches-cleaning
Mar 4, 2026
Merged

tassan merged 2 commits into
mainfrom
copilot/automate-branches-cleaning

Conversation

Copilot AI commented Mar 4, 2026 •

Copy link
Copy Markdown
Contributor

Dangling merged branches accumulate over time with no automated cleanup. Adds a scheduled GitHub Actions workflow to delete branches that have been fully merged into the default branch.

Changes

  • .github/workflows/branch-cleanup.yml — new workflow triggered daily at 03:00 UTC (+ workflow_dispatch for on-demand runs)
    • Resolves default branch dynamically via API — no hardcoded main
    • Paginates all branches to handle repos with > 100 branches
    • Skips protected branches: main, master, develop
    • Deletes only branches where ahead_by === 0 (all commits already in default branch)
    • Uses built-in GITHUB_TOKEN with contents: write — no extra secrets required
    • Catches comparison API errors gracefully and skips the branch with a log message
Original prompt

This section details on the original issue you should resolve

<issue_title>Automate branches cleaning</issue_title>
<issue_description>We need a workflow with a schedule to clean dangling branches daily.</issue_description>

Comments on the Issue (you are @copilot in this section)


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Co-authored-by: tassan <7563760+tassan@users.noreply.github.com>
Copilot AI changed the title [WIP] Add workflow to automate branches cleaning feat: daily automated branch cleanup workflow Mar 4, 2026
@tassan
tassan marked this pull request as ready for review March 4, 2026 23:19
@tassan
tassan requested a review from Copilot March 4, 2026 23:19
@tassan
tassan merged commit 318efbd into main Mar 4, 2026
4 checks passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a scheduled GitHub Actions workflow to automatically delete branches whose commits are fully merged into the repository’s default branch, reducing long-lived “dangling” branches over time.

Changes:

  • Introduces a daily (03:00 UTC) + manual (workflow_dispatch) workflow that enumerates all branches via pagination.
  • Resolves the repo default branch via the GitHub API and compares each branch against it before deletion.
  • Skips a small allowlist of branch names and logs/continues on compare API failures.

Comment on lines +57 to +63
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}`,
});

Copilot AI Mar 4, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Branch deletion errors aren’t caught. If git.deleteRef fails (e.g., branch protection rules, insufficient permissions, transient API errors), the whole job will fail mid-loop and leave remaining branches unprocessed. Wrap the delete call in a try/catch (similar to the compare call) and log+continue, and/or skip branches where branch.protected === true instead of relying only on a name allowlist.

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot apply changes based on this feedback

Comment on lines +21 to +41
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;

Copilot AI Mar 4, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The workflow can delete the repository’s default branch if it isn’t named one of main|master|develop. repos.listBranches includes the default branch, and comparing ${defaultBranch}...${branch.name} for the default branch yields ahead_by === 0, triggering deleteRef. Add an explicit guard to always skip branch.name === defaultBranch (or add defaultBranch to the protected list after resolving it).

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot apply changes based on this feedback

@github-actions
github-actions Bot deleted the copilot/automate-branches-cleaning branch March 5, 2026 03:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Automate branches cleaning

3 participants