feat(hooks): add git hooks management CLI

This commit is contained in:
sankalp committed 2025-08-25 14:17:12 +05:45
1 parent b09b5f676e
commit 0036e47968
24 files changed
+1221 -531

No files matched your search

+73 -50
View File
@@ -1,6 +1,7 @@
"""
Changelog generation CLI.
"""
import os
import click
from rich.console import Console
@@ -11,28 +12,41 @@ from ..shared.config import Config
console = Console()
@click.group()
def cli():
"""Generate changelogs."""
pass
@cli.command()
@click.option("--version", "-v", required=True, help="Version number for the changelog entry")
@click.option(
"--version", "-v", required=True, help="Version number for the changelog entry"
)
@click.option("--from-tag", "-t", help="Generate changelog from this tag")
@click.option("--days", "-d", type=int, help="Generate changelog from the last N days")
@click.option("--commits", "-n", type=int, help="Generate changelog from the last N commits")
@click.option(
"--commits", "-n", type=int, help="Generate changelog from the last N commits"
)
@click.option("--output", "-o", default="CHANGELOG.md", help="Output file path")
@click.option("--temperature", type=float, default=0.7, help="AI temperature (0.0-1.0)")
def generate(version: str, from_tag: str, days: int, commits: int, output: str, temperature: float):
def generate(
version: str,
from_tag: str,
days: int,
commits: int,
output: str,
temperature: float,
):
"""Generate a changelog from git history."""
try:
# Get configuration
config = Config()
# Validate version format
if not version.startswith("v"):
version = f"v{version}"
# Get git history
if from_tag:
# Get commits since tag
@@ -46,16 +60,16 @@ def generate(version: str, from_tag: str, days: int, commits: int, output: str,
else:
# Get all commits
cmd = "git log --pretty=format:%s"
# Execute git command
result = os.popen(cmd).read().strip()
if not result:
console.print("[yellow]No commits found in the specified range")
return
# Split into individual commits
commit_messages = [msg.strip() for msg in result.split("\n") if msg.strip()]
# Group commits by type
groups = {
"✨ Added": [],
@@ -65,9 +79,9 @@ def generate(version: str, from_tag: str, days: int, commits: int, output: str,
"📝 Documentation": [],
"🔧 Maintenance": [],
"🗑️ Removed": [],
"🔒 Security": []
"🔒 Security": [],
}
# Map commit types to groups
type_map = {
"feat": "✨ Added",
@@ -83,21 +97,21 @@ def generate(version: str, from_tag: str, days: int, commits: int, output: str,
"refactor": "🔧 Maintenance",
"remove": "🗑️ Removed",
"delete": "🗑️ Removed",
"security": "🔒 Security"
"security": "🔒 Security",
}
# Categorize commits
for msg in commit_messages:
msg_lower = msg.lower()
categorized = False
# Check for type prefixes
for type_key, group in type_map.items():
if msg_lower.startswith(type_key):
groups[group].append(msg)
categorized = True
break
# If not categorized, check for keywords
if not categorized:
for type_key, group in type_map.items():
@@ -107,10 +121,10 @@ def generate(version: str, from_tag: str, days: int, commits: int, output: str,
else:
# Default to maintenance if no match
groups["🔧 Maintenance"].append(msg)
# Generate changelog
changelog = f"# Changelog\n\n## {version}\n\n"
# Add each group
for group, messages in groups.items():
if messages:
@@ -118,18 +132,21 @@ def generate(version: str, from_tag: str, days: int, commits: int, output: str,
for msg in messages:
changelog += f"- {msg}\n"
changelog += "\n"
# Write to file
with open(output, "w") as f:
f.write(changelog)
console.print(f"[green]✓[/green] Changelog generated successfully at: {output}")
except Exception as e:
console.print(f"[red]Error:[/red] {str(e)}")
@cli.command()
@click.option("--version", "-v", required=True, help="Version number for the changelog entry")
@click.option(
"--version", "-v", required=True, help="Version number for the changelog entry"
)
@click.option("--output", "-o", default="CHANGELOG.md", help="Output file path")
@click.option("--temperature", type=float, default=0.7, help="AI temperature (0.0-1.0)")
def generate_interactive(version: str, output: str, temperature: float):
@@ -137,11 +154,11 @@ def generate_interactive(version: str, output: str, temperature: float):
try:
# Get configuration
config = Config()
# Validate version format
if not version.startswith("v"):
version = f"v{version}"
# Initialize groups
groups = {
"✨ Added": [],
@@ -151,10 +168,12 @@ def generate_interactive(version: str, output: str, temperature: float):
"📝 Documentation": [],
"🔧 Maintenance": [],
"🗑️ Removed": [],
"🔒 Security": []
"🔒 Security": [],
}
console.print("\n[bold cyan]Enter your changes (press Enter twice to finish):[/bold cyan]")
console.print(
"\n[bold cyan]Enter your changes (press Enter twice to finish):[/bold cyan]"
)
console.print("[dim]Start each line with an emoji to categorize it:[/dim]")
console.print("✨ - Added")
console.print("🔄 - Changed")
@@ -165,12 +184,12 @@ def generate_interactive(version: str, output: str, temperature: float):
console.print("🗑️ - Removed")
console.print("🔒 - Security")
console.print("\n[dim]Example: ✨ Added new feature X[/dim]\n")
while True:
line = click.prompt("", default="", show_default=False)
if not line:
break
# Categorize based on emoji
for group in groups:
if line.startswith(group.split()[0]):
@@ -179,10 +198,10 @@ def generate_interactive(version: str, output: str, temperature: float):
else:
# Default to maintenance if no emoji
groups["🔧 Maintenance"].append(line)
# Generate changelog
changelog = f"# Changelog\n\n## {version}\n\n"
# Add each group
for group, messages in groups.items():
if messages:
@@ -190,18 +209,21 @@ def generate_interactive(version: str, output: str, temperature: float):
for msg in messages:
changelog += f"- {msg}\n"
changelog += "\n"
# Write to file
with open(output, "w") as f:
f.write(changelog)
console.print(f"[green]✓[/green] Changelog generated successfully at: {output}")
except Exception as e:
console.print(f"[red]Error:[/red] {str(e)}")
@cli.command()
@click.option("--version", "-v", required=True, help="Version number for the changelog entry")
@click.option(
"--version", "-v", required=True, help="Version number for the changelog entry"
)
@click.option("--input", "-i", required=True, help="Input file with changes")
@click.option("--output", "-o", default="CHANGELOG.md", help="Output file path")
@click.option("--temperature", type=float, default=0.7, help="AI temperature (0.0-1.0)")
@@ -210,22 +232,22 @@ def generate_from_file(version: str, input: str, output: str, temperature: float
try:
# Get configuration
config = Config()
# Validate version format
if not version.startswith("v"):
version = f"v{version}"
# Read input file
with open(input, "r") as f:
changes = f.read().strip()
if not changes:
console.print("[yellow]No changes found in input file")
return
# Split into lines
lines = [line.strip() for line in changes.split("\n") if line.strip()]
# Initialize groups
groups = {
"✨ Added": [],
@@ -235,21 +257,21 @@ def generate_from_file(version: str, input: str, output: str, temperature: float
"📝 Documentation": [],
"🔧 Maintenance": [],
"🗑️ Removed": [],
"🔒 Security": []
"🔒 Security": [],
}
# Categorize changes
for line in lines:
line_lower = line.lower()
categorized = False
# Check for emoji prefixes
for group in groups:
if line.startswith(group.split()[0]):
groups[group].append(line)
categorized = True
break
# If not categorized, check for keywords
if not categorized:
type_map = {
@@ -265,9 +287,9 @@ def generate_from_file(version: str, input: str, output: str, temperature: float
"refactor": "🔧 Maintenance",
"remove": "🗑️ Removed",
"delete": "🗑️ Removed",
"security": "🔒 Security"
"security": "🔒 Security",
}
for type_key, group in type_map.items():
if type_key in line_lower:
groups[group].append(line)
@@ -275,10 +297,10 @@ def generate_from_file(version: str, input: str, output: str, temperature: float
else:
# Default to maintenance if no match
groups["🔧 Maintenance"].append(line)
# Generate changelog
changelog = f"# Changelog\n\n## {version}\n\n"
# Add each group
for group, messages in groups.items():
if messages:
@@ -286,20 +308,21 @@ def generate_from_file(version: str, input: str, output: str, temperature: float
for msg in messages:
changelog += f"- {msg}\n"
changelog += "\n"
# Write to file
with open(output, "w") as f:
f.write(changelog)
console.print(f"[green]✓[/green] Changelog generated successfully at: {output}")
except Exception as e:
console.print(f"[red]Error:[/red] {str(e)}")
# Add command aliases
cli.add_command(generate, name="g")
cli.add_command(generate_interactive, name="i")
cli.add_command(generate_from_file, name="f")
if __name__ == "__main__":
cli()
cli()