Skip to content

Commit

Permalink
feat(dev): add hooks setup and pre-push hook with black formatter
Browse files Browse the repository at this point in the history
  • Loading branch information
guidodinello committed Dec 18, 2024
1 parent 0006331 commit a39cabc
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 0 deletions.
4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,17 @@ test = [
"pytest>=8.2.2",
"pytest-cov>=5.0.0",
]
format = [
"black>=24.10.0",
]

[project.urls]
"Homepage" = "https://github.com/jahwag/claudesync"
"Bug Tracker" = "https://github.com/jahwag/claudesync/issues"

[project.scripts]
claudesync = "claudesync.cli.main:cli"
install-hooks = "claudesync.cli.hooks:install_hooks"

[build-system]
requires = ["setuptools>=42", "wheel"]
Expand Down
77 changes: 77 additions & 0 deletions src/claudesync/cli/hook_templates/pre_commit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#!/usr/bin/env python3
import os
import subprocess
import sys


def get_changed_files() -> list[str]:
"""Get list of Python files that are staged for commit."""
try:
cmd = ["git", "diff", "--cached", "--name-only", "--diff-filter=ACMR"]
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
files = result.stdout.strip().split("\n")
return [f for f in files if f.endswith(".py") and os.path.exists(f)]
except subprocess.CalledProcessError:
print("Failed to get changed files")
return []


def format_files(files: list[str]) -> tuple[bool, list[str]]:
"""Format the given files using black."""
if not files:
return True, []

formatted_files = []
try:
subprocess.run(["black", "--version"], capture_output=True, check=True)
except (subprocess.CalledProcessError, FileNotFoundError):
print(
"Error: black is not installed. Please install it with: pip install black"
)
return False, []

for file_path in files:
try:
subprocess.run(
["black", "--quiet", file_path], capture_output=True, check=True
)
formatted_files.append(file_path)
except subprocess.CalledProcessError as e:
print(f"Failed to format {file_path}: {e}")
return False, formatted_files

return True, formatted_files


def main():
files = get_changed_files()
if not files:
print("No Python files to format")
sys.exit(0)

print("Formatting Python files with black...")
success, formatted_files = format_files(files)

if formatted_files:
print("\nFormatted files:")
for file in formatted_files:
print(f" - {file}")

# Re-stage formatted files
try:
subprocess.run(["git", "add"] + formatted_files, check=True)
print("\nFormatted files have been re-staged")
except subprocess.CalledProcessError:
print("Failed to re-stage formatted files")
sys.exit(1)

if not success:
print("\nSome files could not be formatted")
sys.exit(1)

print("\nAll files formatted successfully")
sys.exit(0)


if __name__ == "__main__":
main()
75 changes: 75 additions & 0 deletions src/claudesync/cli/hooks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import os
import shutil
import stat
import subprocess
from pathlib import Path

import click

SUPPORTED_GIT_HOOKS = [
"pre-commit",
]


def copy_hook(hooks_dir, hook_name):
"""
Copy a specific hook from templates to git hooks directory.
Args:
hooks_dir: Path to the .git/hooks directory
hook_name: Name of the hook to install
"""
hook_path = hooks_dir / hook_name
module_dir = Path(__file__).parent
template_name = f"{hook_name.replace('-', '_')}.py"
hook_template = module_dir / "hook_templates" / template_name

if not hook_template.exists():
click.echo(f"Warning: Template for {hook_name} not found at {hook_template}")
return

try:
shutil.copy2(hook_template, hook_path)

st = os.stat(hook_path)
os.chmod(hook_path, st.st_mode | stat.S_IEXEC)

click.echo(f"Successfully installed {hook_name} hook")
except Exception as e:
click.echo(f"Error installing {hook_name} hook: {e}")


def install_hooks():
"""Install all supported Git hooks for the project."""
project_root = find_git_root()
if not project_root:
click.echo("Error: Not a git repository (or any of the parent directories)")
return

hooks_dir = project_root / ".git" / "hooks"
if not hooks_dir.exists():
click.echo(f"Creating hooks directory: {hooks_dir}")
hooks_dir.mkdir(parents=True, exist_ok=True)

for hook in SUPPORTED_GIT_HOOKS:
copy_hook(hooks_dir, hook)

click.echo("\nHook installation complete!")


def find_git_root():
"""Find the root directory of the Git repository"""
try:
result = subprocess.run(
["git", "rev-parse", "--show-toplevel"],
capture_output=True,
text=True,
check=True,
)
return Path(result.stdout.strip())
except subprocess.CalledProcessError:
return None


if __name__ == "__main__":
install_hooks()

0 comments on commit a39cabc

Please sign in to comment.