https://github.com/tagdots/delete-branches
Deletes stale branches in GitHub repository
https://github.com/tagdots/delete-branches
action automation cronjob delete-branch devops github github-branch idle-branch python stale-branch utilities
Last synced: 5 months ago
JSON representation
Deletes stale branches in GitHub repository
- Host: GitHub
- URL: https://github.com/tagdots/delete-branches
- Owner: tagdots
- License: mit
- Created: 2025-08-17T13:22:53.000Z (10 months ago)
- Default Branch: main
- Last Pushed: 2026-01-04T13:52:30.000Z (6 months ago)
- Last Synced: 2026-01-09T04:26:45.941Z (6 months ago)
- Topics: action, automation, cronjob, delete-branch, devops, github, github-branch, idle-branch, python, stale-branch, utilities
- Language: Python
- Homepage:
- Size: 223 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
- Codeowners: .github/CODEOWNERS
- Security: SECURITY.md
Awesome Lists containing this project
README
# delete-branches
[](https://www.bestpractices.dev/projects/11071)
[](https://github.com/tagdots/delete-branches/actions/workflows/ci.yaml)
[](https://github.com/marketplace/actions/delete-github-branches)
[](https://github.com/tagdots/delete-branches/actions/workflows/cron-tasks.yaml)
## โ
What does delete-branches do?
**delete-branches** deletes idle branches in GitHub repository.
_**exemptions**_: `default branch`, `protected branches`, `head branches in PR`, and `user-specified exclude branches`
## โญ Why switch to delete-branches?
- we reduce your supply chain risks with [openssf best practices](https://best.openssf.org) in our SDLC and operations.
- we share evidence of code coverage results in action (click _Code Coverage ยป cron-tasks ยป badge-coverage_).
- we can run using [command line](https://github.com/tagdots/delete-branches?tab=readme-ov-file#-running-delete-branches-from-command-line) or GitHub Action [Delete GitHub Branches](https://github.com/marketplace/actions/delete-github-branches).
## ๐ Running _delete-branches_ in GitHub action
Use the workflow examples below to create your own workflow inside `.github/workflows/`.
### Example 1 - Summary (MOCK delete)
| Input | Workflow Spec | Notes
|-------|-------------|----------|
| `scheduled run` | `- cron: '30 17 * * *'` | Run daily at 5:30 pm UTC
| `github token permissions` | `contents: read`
`pull-requests: read` | MOCK delete requires read permission |
| `dry-run` | `true` | MOCK delete |
| `exclude-branches` | `badges` | Branch `badges` is found and excluded |
| `max-idle-days` | `10` | Without new commits longer than 10 days |
### Example 1 - Workflow (MOCK delete)
```yml
name: delete-github-branches
on:
schedule:
- cron: '30 17 * * *'
permissions:
contents: read
pull-requests: read
jobs:
delete-branches:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
- name: Run delete-branches
id: delete-branches
uses: tagdots/delete-branches@1067f6cf756a462d2da56cffae2df37ea15b600e # 1.2.15
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
repo-url: ${{ github.server_url }}/${{ github.repository }}
max-idle-days: 10
exclude-branches: 'badges'
dry-run: true
```
### Example 2 - Summary (Permanent delete)
| Input | Workflow Spec | Notes
|-------|-------------|----------|
| `scheduled run` | `- cron: '30 17 * * *'` | Run daily at 5:30 pm UTC
| `github token permissions` | `contents: write`
`pull-requests: read`| Delete requires write permission in `contents` |
| `dry-run` | `false` | Permanent delete |
| `exclude-branches` | `badges` | Branch `badges` is found and excluded |
| `max-idle-days` | `10` | Without new commits longer than 10 days |
### Example 2 - Workflow (Permanent delete)
```yml
name: delete-github-branches
on:
schedule:
- cron: '30 17 * * *'
permissions:
contents: read
pull-requests: read
jobs:
delete-branches:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: read
- name: Run delete-branches
id: delete-branches
uses: tagdots/delete-branches@1067f6cf756a462d2da56cffae2df37ea15b600e # 1.2.15
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
repo-url: ${{ github.server_url }}/${{ github.repository }}
max-idle-days: 10
exclude-branches: 'badges'
dry-run: false
```
## ๐ฅ Running _delete-branches_ from command line
### Prerequisites
```
* Python (3.12+)
* GitHub fine-grained token (contents: write)
```
### Setup _delete-branches_
```
~/work/test $ workon test
(test) ~/work/test $ export GH_TOKEN=github_pat_xxxxxxxxxxxxx
(test) ~/work/test $ pip install -U delete-branches
```
### Example 1 - Run for help
```
(test) ~/work/test $ delete-branches --help
Usage: delete-branches [OPTIONS]
Options:
--dry-run BOOLEAN default: true
--repo-url TEXT e.g. https://github.com/{owner}/{repo} [required]
--exclude-branches TEXT e.g. 'exclude-branch-1, exclude-branch-2'
--max-idle-days INTEGER Max. no. of idle days (without commits) [required]
--version Show the version and exit.
--help Show this message and exit.
```
### Example 2 - MOCK delete branches without new commits longer than 10 days
| Input | Input Detail | Result
|-------|-------------|----------|
| `repo-url` | `https://github.com/tagdots/test` | process repository `tagdots/test` |
| `max-idle-days` | `10` | Without new commits longer than 10 days |
| `exclude-branches` | `test-1`, `test-2`, `badges` | Only branch `badges` is found and excluded |
| `dry-run` | `true` | a. 3 branches exempted from delete: `main`, `badges`, `pr-branch-01`
b. 6 branches `NOT exempt from delete`
c. MOCK delete 2 of 6 `NOT-exempt-from-delete` branches |
```
(test) ~/work/test $ delete-branches --dry-run true --max-idle-days 10 --exclude-branches "test-1, test-2, badges" --repo-url https://github.com/tagdots/test
๐ Starting Delete GitHub Branches (dry-run: True, exclude-branches: {'test-1', 'test-2', 'badges'}, max-idle-days: 10)
Current Time (UTC): 2025-08-20 17:08:32
Refined User Exclude Branch(es): {'badges'}
Default Branch : main
Protected Branch : main
Pull Request Head Branch : pr-branch-01
Total Number of Branches : 9
Total Number of Branches (Exempt-From-Delete) : 3
Total Number of Branches (Not-Exempt-From-Delete): 6
From 6 Not-Exempt-From-Delete Branch(es), 2 branch is idle more than 10 day(s)
-------------------------------------------------------------------------------------------------
(MOCK) Delete branch - last update UTC 2025-08-09 13:49:34: branch-test-001
(MOCK) Delete branch - last update UTC 2025-08-08 03:29:34: branch-test-005
```
### Example 3 - Delete branches permanently without new commits longer than 10 days
| Input | Input Detail | Result
|-------|-------------|----------|
| `repo-url` | `https://github.com/tagdots/test` | process repository `tagdots/test` |
| `max-idle-days` | `10` | Without new commits longer than 10 days |
| `exclude-branches` | `badges` | Branch `badges` is found and excluded |
| `dry-run` | `false` | a. 3 branches exempted from delete: `main`, `badges`, `pr-branch-01`
b. 6 branches `NOT exempt from delete`
c. Delete 2 of 6 `NOT-exempt-from-delete` branches permanently |
```
(test) ~/work/test $ delete-branches --dry-run false --max-days 10 --exclude-branches "badges" --repo-url https://github.com/tagdots/test
๐ Starting Delete GitHub Branches (dry-run: False, exclude-branches: {'badges'}, max-idle-days: 10)
Current Time (UTC): 2025-08-20 17:26:55
Refined User Exclude Branch(es): {'badges'}
Default Branch : main
Protected Branch : main
Pull Request Head Branch : pr-branch-01
Total Number of Branches : 9
Total Number of Branches (Exempt-From-Delete) : 3
Total Number of Branches (Not-Exempt-From-Delete): 6
From 6 Not-Exempt-From-Delete Branch(es), 2 branch is idle more than 10 day(s)
-------------------------------------------------------------------------------------------------
โ
Delete branch - last update UTC 2025-08-09 13:49:34: branch-test-001
โ
Delete branch - last update UTC 2025-08-08 03:29:34: branch-test-005
```
## ๐ง delete-branches command line options
| Input | Description | Default | Required | Notes |
|-------|-------------|----------|----------|----------|
| `repo-url` | Repository URL | `None` | Yes | e.g. https://github.com/{owner}/{repo} |
| `dry-run` | Dry-Run | `True` | No | - |
| `max-idle-days` | Maximum number of days without new commits | `None` | Yes | enter number of days |
| `exclude-branches` | Branches excluded from deletion | `None` | No | comma seperated branches e.g. "branch1, branch2" |
## ๐ Troubleshooting
Open an [issue][issues]
## ๐ Contributing
For pull requests to be accepted on this project, you should follow [PEP8][pep8] when creating/updating Python codes.
See [Contributing][contributing]
## ๐ Appreciation
If you find this project helpful, please โญ star it. **Thank you**.
## ๐ References
[How to fork a repo](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo)
[contributing]: https://github.com/tagdots/delete-branches/blob/main/CONTRIBUTING.md
[issues]: https://github.com/tagdots/delete-branches/issues
[pep8]: https://google.github.io/styleguide/pyguide.html