Skip to content

Commit

Permalink
✨ Add option to remove stale branches in GitHub (#13)
Browse files Browse the repository at this point in the history
* ✨ Add option to remove stale branches in GitHub



---------

Co-authored-by: ChristophShyper <45788587+ChristophShyper@users.noreply.github.com>
  • Loading branch information
github-actions[bot] and ChristophShyper authored Feb 28, 2025
1 parent ddf737b commit 03d851d
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 27 deletions.
78 changes: 53 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ DevOps/CloudOps CLI framework for making work with Terragrunt and performing var

Do you want to automate your daily tasks with Terragrunt, Terraform, GitHub, and other tools? Velez is here to help you!

Do you sometimes forget to add changed files before pushing to GitHub? Velez will do that for you and even format HCL files before committing!
Do you sometimes forget to add changed files before pushing to GitHub? Velez will do that for you and even format HCL
files before committing!

<a href="https://gitmoji.dev">
<img
Expand All @@ -15,18 +16,20 @@ Do you sometimes forget to add changed files before pushing to GitHub? Velez wil

## Disclaimer

This project is in the early development stage and is not ready for production use.

Since it operates on the infrastructure, user is responsible for the consequences of the actions taken by the tool and should review the code before using it.
This project is in the early development stage and is not ready for production use. It is a work in progress and may
contain bugs, incomplete features, incorrect documentation, backward incompatible changes or other issues.
Use it at your own risk.

Since it operates on the infrastructure, user is responsible for the consequences of the actions taken by the tool and
should review the code before using it.

![Velez](img/velez.jpg)


## Features

Supporting following services/tools and operations on them:
- Terragrunt:

- Terragrunt operations `-tg`:
- Walk directory structure containing Terragrunt modules.
- Run Plan, Apply, Destroy and Output on a selected module or a specific target.
- Taint and Untaint a resource.
Expand All @@ -38,15 +41,15 @@ Supporting following services/tools and operations on them:
- Move a module to a new directory, including moving remote state.
- Destroy resources and backend of the module.
- Destroy backend of the module.
- File operations:
- File operations `-f`:
- Formatting all HCL files in the project.
- Cleaning up temporary files in the project or a selected module.
- GitHub operations:
- GitHub operations `-gh`:
- Source operations, like commit, amend, push, pull or rebase.
- Branch operations, like create, change local or remote, delete local or remote.
- Manage pull requests, like create, list in the repository or the whole organization.
- Manage issues, like create, list in the repository or the whole organization.

- Easily remove stale branches.

## Installation

Expand All @@ -64,9 +67,9 @@ Framework is written in Python and can be installed as a package.
* Install Python if not installed yet - required.
* Install Terragrunt, and Terraform or OpenTofu - required for IaaC operations.
* Install `hcledit` - required for updating `.hcl` files.
* Install `direnv` or similar solution - highly suggested for managing environments.
* Install `direnv` or similar solution - highly suggested for managing environments.

It can be installed, e.g. by running:
It can be installed, e.g. by running:
```sh
brew install python
brew install terraform
Expand All @@ -79,7 +82,6 @@ Framework is written in Python and can be installed as a package.
pip install .
```


## Usage

### Help
Expand All @@ -90,8 +92,7 @@ Run the CLI with the `--help` argument to see the available commands:
velez --help
```

To use the Velez CLI, you have two options:

To use the Velez CLI, you have three options:

### Interactive Menu

Expand All @@ -101,11 +102,36 @@ Run the CLI without additional arguments to use the interactive menu:
velez
```

### Interactive Menu for specific operation

### Automation
#### Terragrunt operations (`-tg` or `--terragrunt`)

```sh
velez --terragrunt <operation> <module> <other-arguments>
```

#### File operations (`-f` or `--file`)

Show menu for file operations:

```sh
velez --file
```

#### GitHub operations (`-gh` or `--github`)

Show menu for GitHub operations:

```sh
velez --github
```

### Automation / CLI

Run the CLI with additional arguments for automation/scripting:

#### Terragrunt operations (`-tg` or `--terragrunt`)

```sh
velez --terragrunt <operation> <module> <other-arguments>
```
Expand Down Expand Up @@ -137,22 +163,24 @@ Run the following command to plan the `aws/dev-account` module:
velez -tg plan aws/dev-account
```


## Configuration

Velez expects following environment variables to be set:

* `VELEZ_ROOT_HCL` - relative path to the Terragrunt configuration file (defaults to `root.hcl`, as per the current recommendations).
* `VELEZ_TEMP_CONFIG` - absolute path to a temporary file created to render Terragrunt configuration (defaults to `/tmp/terragrunt.hcl`).
* `GITHUB_TOKEN` - GitHub token for accessing the GitHub API.

For the convenience, these variables can be set in a `.env` file in the project directory and use the `direnv` (mentioned above) to load
them automatically for every project separately.

Veles will read Terragrunt configuration and expand any dynamic config available statically.
For example for each selected Terragrunt module backed configuration will be read to determine exact values of the S3 bucket and DynamoDB table and key used for locking the state.
| Variable | Description | Required for operations | Default |
|-----------------------------------|-------------------------------------------------------------------------------|-------------------------|-----------------------|
| `VELEZ_TG_ROOT_HCL` | Relative path to the Terragrunt configuration file. | Terragrunt | `root.hcl` |
| `VELEZ_TG_TEMP_CONFIG` | Absolute path to a temporary file created to render Terragrunt configuration. | Terragrunt | `/tmp/terragrunt.hcl` |
| `GITHUB_TOKEN` | GitHub token for accessing the GitHub API. | GitHub | `N/A` |
| `VELEZ_GH_STALE_BRANCHES_DAYS` | Number of days after which branches are considered stale. | GitHub | `45` |
| `VELEZ_GH_STALE_BRANCHES_COMMITS` | Number of commits after which branches are considered stale. | GitHub | `30` |

For the convenience, these variables can be set in a `.env` file in the project directory and use the `direnv` (
mentioned above) to load them automatically for every project separately.

Veles will read Terragrunt configuration and expand any dynamic config available statically.
For example for each selected Terragrunt module backed configuration will be read to determine exact values of the S3
bucket and DynamoDB table and key used for locking the state.

## License

Expand Down
53 changes: 53 additions & 0 deletions velez/github_ops.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import os
import re
import sys
from datetime import datetime

import github
from pick import pick
from velez.file_ops import FileOperations
from velez.utils import str_back, str_exit, run_command

STALE_BRANCHES_DAYS = int(os.getenv('VELEZ_GH_STALE_BRANCHES_DAYS', 45))
STALE_BRANCHES_COMMITS = int(os.getenv('VELEZ_GH_STALE_BRANCHES_COMMITS', 30))

str_commit = "→ Commit"
str_amend = "⇢ Amend commit"
str_push = "⇧ Push"
Expand All @@ -28,6 +32,7 @@
str_create_issue = "✶ Create new issue"
str_list_issues_repo = "⎗ List issues in the repository"
str_list_issues_org = "⎘ List issues for the whole organization"
str_delete_stale_branches = "⌦ Delete stale branches"


class GitHubOperations:
Expand Down Expand Up @@ -172,6 +177,7 @@ def branches_menu(self) -> None:
str_select_remote_branch,
str_delete_local_branch,
str_delete_remote_branch,
str_delete_stale_branches,
str_back,
str_exit
]
Expand All @@ -187,6 +193,8 @@ def branches_menu(self) -> None:
self.delete_local_branch()
elif option == str_delete_remote_branch:
self.delete_remote_branch()
elif option == str_delete_stale_branches:
self.delete_stale_branches()
elif option == str_back:
self.github_menu()
elif option == str_exit:
Expand Down Expand Up @@ -398,3 +406,48 @@ def list_open_issues(self, repo_only: bool) -> None:
for issue in issues:
print(f"#{issue.number} - {issue.title}\nURL: {issue.html_url}\n")
input("Press Enter to return to the GitHub menu...")

def get_stale_branches(self) -> list:
"""
Get a list of stale branches based on the set criteria.
:return: list of stale branches
"""
stale_branches = []
branches = self.repo.get_branches()
main_branch = self.repo.get_branch(self.repo.default_branch)
main_branch_commit = main_branch.commit

for branch in branches:
if branch.name == main_branch.name:
continue

branch_commit = branch.commit
commit_date = branch_commit.commit.author.date
days_since_last_commit = (datetime.now(tz=commit_date.tzinfo) - commit_date).days
commits_behind = self.repo.compare(main_branch_commit.sha, branch_commit.sha).behind_by

if days_since_last_commit > STALE_BRANCHES_DAYS or commits_behind > STALE_BRANCHES_COMMITS:
stale_branches.append(branch.name)

return stale_branches

def delete_stale_branches(self) -> None:
"""
Delete stale branches based on the set criteria.
:return: None
"""
stale_branches = self.get_stale_branches()
if not stale_branches:
print("No stale branches found.")
input("Press Enter to return to the branches menu...")
return

title = f"Current branch: {self.branch}. Select a stale branch to delete:"
option, index = pick(stale_branches + [str_back, str_exit], title)
if option == str_back:
self.branches_menu()
elif option == str_exit:
sys.exit()
else:
run_command(['git', 'push', 'origin', '--delete', option])
input("Press Enter to return to the branches menu...")
4 changes: 2 additions & 2 deletions velez/terragrunt_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,12 @@ def __init__(self, velez):
if not self.velez.check_terragrunt():
input("Press Enter to return to the main menu...")
self.velez.main_menu()
self.root_hcl = os.getenv('VELEZ_ROOT_HCL', 'root.hcl') # Root Terragrunt config file
self.root_hcl = os.getenv('VELEZ_TG_ROOT_HCL', 'root.hcl') # Root Terragrunt config file
if not os.path.exists(self.root_hcl):
print(f"Root Terragrunt config file {self.root_hcl} not found.")
input("Press Enter to return to the main menu...")
self.velez.main_menu()
self.temp_config = os.getenv('VELEZ_TEMP_CONFIG',
self.temp_config = os.getenv('VELEZ_TG_TEMP_CONFIG',
'/tmp/terragrunt_rendered.json') # Temporary file to store rendered config
self.use_s3_backend = False # If S3 backend is used, will be updated for each module separately
self.use_dynamodb_locks = False # If DynamoDB locks are used, will be updated for each module separately
Expand Down

0 comments on commit 03d851d

Please sign in to comment.