Skip to content

Commit

Permalink
✨ Add GitHubOperations class and improve imports and initaliation (#10)
Browse files Browse the repository at this point in the history
* ✨ Add GitHubOperations class and improve imports and initaliation

Also:
* Optimized Class Initialization: Initialized TerragruntOperations, FileOperations, and GitHubOperations only when their respective menu options are selected. \n
* Added Options to Delete Local and Remote Branches: Implemented methods to delete local and remote branches in GitHubOperations. \n
* Updated README: Reflected the new functions and configuration in the README.md. \n
* Fixed AttributeError: Ensured TerragruntOperations is initialized before calling run_terragrunt in FileOperations \n
* Add file formatting before commits

---------

Co-authored-by: ChristophShyper <45788587+ChristophShyper@users.noreply.github.com>
  • Loading branch information
github-actions[bot] and ChristophShyper authored Feb 27, 2025
1 parent 2fa5b10 commit ddf737b
Show file tree
Hide file tree
Showing 8 changed files with 749 additions and 266 deletions.
57 changes: 32 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,50 @@

DevOps/CloudOps CLI framework for making work with Terragrunt and performing various cloud operations much easier.

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!

<a href="https://gitmoji.dev">
<img
src="https://img.shields.io/badge/gitmoji-%20😜%20😍-FFDD67.svg?style=flat-square"
alt="Gitmoji"
/>
</a>

## 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.


![Velez](img/velez.jpg)


## Features

- Supporting following services/tools and operations on them:
- Terragrunt:
- Walk directory structure containing Terragrunt modules.
- Run Plan on a selected module or a specific target.
- Run Apply on a selected module or a specific target.
- Import a resource to the state.
- Run Destroy on a selected module or a specific target.
- Run Output on a selected module or a specific target.
- Run Validate on a selected module.
- Run Refresh on a selected module.
- Run State operations like:
- List resources.
- Move a resource.
- Remove a resource.
- Show resource.
- Pull and push state.
- Run Module operations on source modules:
- Move a module to a new directory, including moving remote state.
- Destroy resources and backend of the module.
- Destroy backend of the module.
- Taint and Untaint a resource.
- Unlock module and show lock information.
- File operations:
- Formatting all HCL files in the project.
- Cleaning up temporary files in the project or a selected module.
Supporting following services/tools and operations on them:
- Terragrunt:
- 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.
- Unlock module and show lock information.
- Run Validate and Refresh on a selected module.
- Import a resource to the state.
- Run State operations, like list, move, remove, show, pull and push.
- Run Module operations on source modules:
- 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:
- Formatting all HCL files in the project.
- Cleaning up temporary files in the project or a selected module.
- GitHub operations:
- 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.


## Installation
Expand Down Expand Up @@ -138,6 +144,7 @@ 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.
Expand Down
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pick==2.4.0
setuptools==75.8.1
boto3==1.37.1
python-hcl2==6.1.1
python-hcl2==6.1.1
PyGithub==2.6.1
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
install_requires=[
'pick',
'boto3',
'python-hcl2'
'python-hcl2',
'PyGithub'
],
entry_points={
'console_scripts': [
Expand Down
154 changes: 77 additions & 77 deletions velez/file_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,88 +5,17 @@

import hcl2
from pick import pick
from velez.utils import run_command, str_format_files, str_clean_files, str_back, str_exit
from velez.utils import str_back, str_exit, run_command


def load_hcl_file(hcl_file: str) -> dict:
"""
Load HCL file into a dictionary.
:param hcl_file: HCL file to load
:return: dictionary of HCL file
"""
with open(hcl_file, 'r') as fr:
return hcl2.load(fr)


def load_json_file(json_file: str) -> dict:
"""
Load JSON file into a dictionary.
:param json_file: JSON file to load
:return: dictionary of JSON file
"""
with open(json_file, 'r') as fr:
return json.load(fr)


def format_hcl_file(file: str) -> None:
"""
Format HCL file.
:param file: HCL file to format
:return: None
"""
print(f"Formatting HCL file {file}")
run_command(f"terragrunt hclfmt {file}")

def clean_files(clean_path: str='.') -> None:
"""
Clean temporary files from Terraform and Terragrunt.
:param clean_path: root path to start cleaning from
:return: None
"""
print(f"Cleaning temporary files in {clean_path}")
for root, dirs, files in os.walk(clean_path):
if ".terraform" in dirs:
dir_to_remove = os.path.join(root, '.terraform')
try:
shutil.rmtree(dir_to_remove)
print(f"Removed {dir_to_remove}")
except Exception as e:
print(f"Error removing {dir_to_remove}: {e}")
elif "registry.terraform.io" in dirs:
dir_to_remove = os.path.join(root, 'registry.terraform.io')
try:
shutil.rmtree(dir_to_remove)
print(f"Removed {dir_to_remove}")
except Exception as e:
print(f"Error removing {dir_to_remove}: {e}")
# ignore .terragrunt-cache folder in main terragrunt directory if it has .gitkeep file
elif ".terragrunt-cache" in dirs:
for file in files:
if file == '.gitkeep':
print(f"Skipping {os.path.join(root, '.terragrunt-cache')}")
break
# remove the directory if it doesn't have .gitkeep file
dir_to_remove = os.path.join(root, '.terragrunt-cache')
try:
shutil.rmtree(dir_to_remove)
print(f"Removed {dir_to_remove}")
except Exception as e:
print(f"Error removing {dir_to_remove}: {e}")
for file in files:
if file in ['.terraform.lock.hcl', 'terragrunt-debug.tfvars.json', 'tfplan']:
file_path = os.path.join(root, file)
try:
os.remove(file_path)
print(f"Removed {file_path}")
except Exception as e:
print(f"Error removing {file_path}: {e}")
input("Press Enter to return to the file menu...")
str_format_files = "⎆ Format HCL files"
str_clean_files = "⌧ Clean temporary files"


class FileOperations:
"""
Class for file operations.
"""

def __init__(self, velez):
self.velez = velez

Expand All @@ -111,7 +40,7 @@ def file_menu(self) -> None:
elif option == str_format_files:
self.format_hcl_files()
elif option == str_clean_files:
clean_files()
self.clean_files()

self.file_menu()

Expand All @@ -121,4 +50,75 @@ def format_hcl_files(self) -> None:
:return: None
"""
print("Formatting HCL files...")
self.velez.terragrunt_ops.run_terragrunt(arguments=['hclfmt'])
if self.velez.check_terragrunt():
run_command(['terragrunt', 'hclfmt'])
if self.velez.get_tf_ot() == 'terraform':
run_command(['terraform', 'fmt', '-recursive', '.'])
elif self.velez.get_tf_ot() == 'tofu':
run_command(['tofu', 'fmt', '-recursive', '.'])

@staticmethod
def clean_files(clean_path: str = '.') -> None:
"""
Clean temporary files from Terraform and Terragrunt.
:param clean_path: root path to start cleaning from
:return: None
"""
print(f"Cleaning temporary files in {clean_path}")
for root, dirs, files in os.walk(clean_path):
if ".terraform" in dirs:
dir_to_remove = os.path.join(root, '.terraform')
try:
shutil.rmtree(dir_to_remove)
print(f"Removed {dir_to_remove}")
except Exception as e:
print(f"Error removing {dir_to_remove}: {e}")
elif "registry.terraform.io" in dirs:
dir_to_remove = os.path.join(root, 'registry.terraform.io')
try:
shutil.rmtree(dir_to_remove)
print(f"Removed {dir_to_remove}")
except Exception as e:
print(f"Error removing {dir_to_remove}: {e}")
# ignore .terragrunt-cache folder in main terragrunt directory if it has .gitkeep file
elif ".terragrunt-cache" in dirs:
for file in files:
if file == '.gitkeep':
print(f"Skipping {os.path.join(root, '.terragrunt-cache')}")
break
# remove the directory if it doesn't have .gitkeep file
dir_to_remove = os.path.join(root, '.terragrunt-cache')
try:
shutil.rmtree(dir_to_remove)
print(f"Removed {dir_to_remove}")
except Exception as e:
print(f"Error removing {dir_to_remove}: {e}")
for file in files:
if file in ['.terraform.lock.hcl', 'terragrunt-debug.tfvars.json', 'tfplan']:
file_path = os.path.join(root, file)
try:
os.remove(file_path)
print(f"Removed {file_path}")
except Exception as e:
print(f"Error removing {file_path}: {e}")
input("Press Enter to return to the file menu...")

@staticmethod
def load_hcl_file(hcl_file: str) -> dict:
"""
Load HCL file into a dictionary.
:param hcl_file: HCL file to load
:return: dictionary of HCL file
"""
with open(hcl_file, 'r') as fr:
return hcl2.load(fr)

@staticmethod
def load_json_file(json_file: str) -> dict:
"""
Load JSON file into a dictionary.
:param json_file: JSON file to load
:return: dictionary of JSON file
"""
with open(json_file, 'r') as fr:
return json.load(fr)
Loading

0 comments on commit ddf737b

Please sign in to comment.