Skip to content

Commit

Permalink
feat: add pre-commit hooks for allowign only .jpg in wallpapers and c…
Browse files Browse the repository at this point in the history
…overs
  • Loading branch information
thepushkarp committed Jan 6, 2025
1 parent ebe5f4b commit e393ca1
Show file tree
Hide file tree
Showing 4 changed files with 213 additions and 0 deletions.
129 changes: 129 additions & 0 deletions .hooks/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
#!/usr/bin/env python3

import os
import sys
import subprocess
from typing import List, Set, Tuple
from pathlib import Path
from dataclasses import dataclass
from enum import Enum


class Color:
"""ANSI color codes for terminal output"""
RED = '\033[91m'
GREEN = '\033[92m'
YELLOW = '\033[93m'
RESET = '\033[0m'


class FileStatus(Enum):
"""Enum for file validation status"""
ALLOWED = "allowed"
BLOCKED = "blocked"
IGNORED = "ignored"


@dataclass
class FileValidation:
"""Data class to store file validation results"""
path: str
status: FileStatus
message: str


class ImageHook:
"""Pre-commit hook for managing image file extensions"""

# Remove or comment out old extension sets:
# BLOCKED_EXTENSIONS: Set[str] = set()
# ALLOWED_PATTERNS: List[Tuple[str, List[str]]] = [...]
# RESTRICTED_EXTENSIONS: Set[str] = {...}
# RESTRICTED_DIRS: List[str] = [...]

def __init__(self):
self.violations: List[FileValidation] = []
self.allowed_files: List[FileValidation] = []

def get_staged_files(self) -> List[str]:
"""Get list of staged files from git"""
try:
result = subprocess.run(
['git', 'diff', '--cached', '--name-only', '--diff-filter=ACMR'],
capture_output=True,
text=True,
check=True
)
return [file for file in result.stdout.splitlines() if file.strip()]
except subprocess.CalledProcessError as e:
print(f"{Color.RED}Error getting staged files: {e}{Color.RESET}")
sys.exit(1)

def validate_file(self, file_path: str) -> FileValidation:
extension = Path(file_path).suffix.lower().lstrip('.')
image_exts = {"jpg", "png", "gif", "bmp",
"jpeg", "webp", "svg", "ico", "tiff", "avif"}
# Ignore non-image files
if extension not in image_exts:
return FileValidation(file_path, FileStatus.IGNORED, "Not an image file")

# Allow only .jpg in /wallpapers or /covers
if any(file_path.startswith(d) for d in ["wallpapers/", "covers/"]):
if extension != "jpg":
return FileValidation(file_path, FileStatus.BLOCKED, f"Only .jpg allowed here, found .{extension}")
return FileValidation(file_path, FileStatus.ALLOWED, "Allowed .jpg in restricted folder")

# No restrictions elsewhere
return FileValidation(file_path, FileStatus.ALLOWED, "No restrictions")

def print_results(self):
"""Print validation results with color formatting"""
# Print blocked files first
for violation in self.violations:
print(f"{Color.RED}{violation.message}: {
violation.path}{Color.RESET}")

# Print allowed files
for allowed in self.allowed_files:
print(f"{Color.GREEN}{allowed.message}: {
allowed.path}{Color.RESET}")

# Print summary if there were violations
if self.violations:
print(f"\n{Color.RED}✖ Commit failed: Found {
len(self.violations)} illegal image file(s){Color.RESET}")
print(
f"{Color.YELLOW}Note: Only .jpg files are allowed in /wallpapers and /covers directories{Color.RESET}")

def run(self) -> bool:
"""Run the hook validation"""
staged_files = self.get_staged_files()

for file_path in staged_files:
validation = self.validate_file(file_path)

if validation.status == FileStatus.BLOCKED:
self.violations.append(validation)
elif validation.status == FileStatus.ALLOWED:
self.allowed_files.append(validation)

self.print_results()
return len(self.violations) == 0


def main():
"""Main entry point"""
try:
hook = ImageHook()
success = hook.run()
sys.exit(0 if success else 1)
except KeyboardInterrupt:
print(f"\n{Color.YELLOW}Hook interrupted by user{Color.RESET}")
sys.exit(1)
except Exception as e:
print(f"{Color.RED}Error running hook: {str(e)}{Color.RESET}")
sys.exit(1)


if __name__ == "__main__":
main()
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Setup

1. Ensure Python 3 is installed on your system.
2. Run install-hooks.py to set up the pre-commit hook:
```
./install-hooks.py
```
3. Commit files as usual (.jpg only allowed in /wallpapers or /covers).
4. Run uninstall-hooks.py to remove the hook:
```
./uninstall-hooks.py
```
42 changes: 42 additions & 0 deletions install-hooks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#!/usr/bin/env python3

import os
import sys
import shutil
from pathlib import Path


class Color:
GREEN = '\033[92m'
RED = '\033[91m'
RESET = '\033[0m'


def install_hooks():
"""Install git hooks from .hooks directory"""
script_dir = Path(__file__).parent.absolute()
hooks_dir = script_dir / '.hooks'
git_hooks_dir = script_dir / '.git' / 'hooks'

# Create .git/hooks directory if it doesn't exist
git_hooks_dir.mkdir(parents=True, exist_ok=True)

# Install pre-commit hook
source = hooks_dir / 'pre-commit'
target = git_hooks_dir / 'pre-commit'

try:
# Copy hook file
shutil.copy2(source, target)
# Make executable
target.chmod(0o755)
print(f"{Color.GREEN}✓ Successfully installed pre-commit hook{Color.RESET}")
return True
except Exception as e:
print(f"{Color.RED}Error installing hooks: {str(e)}{Color.RESET}")
return False


if __name__ == "__main__":
success = install_hooks()
sys.exit(0 if success else 1)
30 changes: 30 additions & 0 deletions uninstall-hooks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#!/usr/bin/env python3

import os
import sys
from pathlib import Path


class Color:
GREEN = '\033[92m'
RESET = '\033[0m'


def uninstall_hooks():
"""Remove installed git hooks"""
script_dir = Path(__file__).parent.absolute()
pre_commit = script_dir / '.git' / 'hooks' / 'pre-commit'

try:
if pre_commit.exists():
pre_commit.unlink()
print(f"{Color.GREEN}✓ Successfully uninstalled git hooks{Color.RESET}")
return True
except Exception as e:
print(f"Error uninstalling hooks: {str(e)}")
return False


if __name__ == "__main__":
success = uninstall_hooks()
sys.exit(0 if success else 1)

0 comments on commit e393ca1

Please sign in to comment.