feat(hooks): add git hooks management CLI
This commit is contained in:
24 files changed
+1221
-531
No files matched your search
+86
-56
@@ -12,11 +12,13 @@ import re
|
||||
|
||||
console = Console()
|
||||
|
||||
|
||||
@click.group()
|
||||
def license():
|
||||
"""License generation commands."""
|
||||
pass
|
||||
|
||||
|
||||
@license.command()
|
||||
@click.option("--type", "-t", help="Type of license to generate")
|
||||
@click.option("--author", "-a", help="Name of the author/copyright holder")
|
||||
@@ -29,65 +31,71 @@ def generate(type: str, author: str, year: int, output: str, multi: bool):
|
||||
# If no author specified, prompt for it
|
||||
if not author:
|
||||
author = click.prompt("Enter author name")
|
||||
|
||||
|
||||
# Fetch available licenses
|
||||
licenses = license_generator.fetch_available_licenses()
|
||||
if not licenses:
|
||||
console.print("[yellow]No licenses available")
|
||||
return
|
||||
|
||||
|
||||
# Create selection table
|
||||
table = Table(show_header=True, header_style="bold magenta")
|
||||
table.add_column("No.", style="dim", width=4)
|
||||
table.add_column("License Key", style="dim", width=15)
|
||||
table.add_column("Name", width=30)
|
||||
table.add_column("Permissions", width=40)
|
||||
|
||||
|
||||
# Add rows with numbers
|
||||
for i, license in enumerate(licenses, 1):
|
||||
table.add_row(
|
||||
str(i),
|
||||
license["key"],
|
||||
license["name"],
|
||||
format_permissions(license.get("permissions", []))
|
||||
format_permissions(license.get("permissions", [])),
|
||||
)
|
||||
|
||||
|
||||
# Display table
|
||||
console.print("\nSelect license(s) to generate:")
|
||||
console.print(table)
|
||||
|
||||
|
||||
if multi:
|
||||
# Get user selection for multiple licenses
|
||||
while True:
|
||||
try:
|
||||
selection = click.prompt("Enter license numbers (e.g., 1,3,5)").split(",")
|
||||
selection = click.prompt(
|
||||
"Enter license numbers (e.g., 1,3,5)"
|
||||
).split(",")
|
||||
selected_indices = [int(s.strip()) for s in selection if s.strip()]
|
||||
|
||||
|
||||
# Validate selection
|
||||
if not selected_indices:
|
||||
console.print("[yellow]No licenses selected")
|
||||
continue
|
||||
|
||||
|
||||
if any(i < 1 or i > len(licenses) for i in selected_indices):
|
||||
console.print("[red]Invalid selection. Please enter valid numbers.")
|
||||
console.print(
|
||||
"[red]Invalid selection. Please enter valid numbers."
|
||||
)
|
||||
continue
|
||||
|
||||
|
||||
# Get selected licenses
|
||||
selected_licenses = [licenses[i-1] for i in selected_indices]
|
||||
|
||||
selected_licenses = [licenses[i - 1] for i in selected_indices]
|
||||
|
||||
# Show confirmation
|
||||
console.print("\nSelected licenses:")
|
||||
for license in selected_licenses:
|
||||
console.print(f"- {license['name']} ({license['key']})")
|
||||
|
||||
|
||||
if Confirm.ask("\nGenerate these licenses?"):
|
||||
break
|
||||
else:
|
||||
console.print("\nPlease select licenses again:")
|
||||
continue
|
||||
|
||||
|
||||
except ValueError:
|
||||
console.print("[red]Invalid input. Please enter numbers separated by commas.")
|
||||
console.print(
|
||||
"[red]Invalid input. Please enter numbers separated by commas."
|
||||
)
|
||||
continue
|
||||
else:
|
||||
# Get user selection for single license
|
||||
@@ -95,29 +103,33 @@ def generate(type: str, author: str, year: int, output: str, multi: bool):
|
||||
try:
|
||||
selection = click.prompt("Enter license number")
|
||||
selected_index = int(selection.strip())
|
||||
|
||||
|
||||
# Validate selection
|
||||
if selected_index < 1 or selected_index > len(licenses):
|
||||
console.print("[red]Invalid selection. Please enter a valid number.")
|
||||
console.print(
|
||||
"[red]Invalid selection. Please enter a valid number."
|
||||
)
|
||||
continue
|
||||
|
||||
|
||||
# Get selected license
|
||||
selected_license = licenses[selected_index - 1]
|
||||
|
||||
|
||||
# Show confirmation
|
||||
console.print(f"\nSelected license: {selected_license['name']} ({selected_license['key']})")
|
||||
|
||||
console.print(
|
||||
f"\nSelected license: {selected_license['name']} ({selected_license['key']})"
|
||||
)
|
||||
|
||||
if Confirm.ask("\nGenerate this license?"):
|
||||
selected_licenses = [selected_license]
|
||||
break
|
||||
else:
|
||||
console.print("\nPlease select a license again:")
|
||||
continue
|
||||
|
||||
|
||||
except ValueError:
|
||||
console.print("[red]Invalid input. Please enter a number.")
|
||||
continue
|
||||
|
||||
|
||||
# Generate each license
|
||||
for license in selected_licenses:
|
||||
license_type = license["key"]
|
||||
@@ -127,19 +139,23 @@ def generate(type: str, author: str, year: int, output: str, multi: bool):
|
||||
else:
|
||||
base, ext = os.path.splitext(output)
|
||||
output_file = f"{base}_{license_type}{ext}"
|
||||
|
||||
|
||||
try:
|
||||
output_path = generate_license(
|
||||
license_type=license_type,
|
||||
author=author,
|
||||
year=year,
|
||||
output_file=output_file
|
||||
output_file=output_file,
|
||||
)
|
||||
console.print(
|
||||
f"[green]✓[/green] Generated {license_type} license at: {output_path}"
|
||||
)
|
||||
console.print(f"[green]✓[/green] Generated {license_type} license at: {output_path}")
|
||||
except ValueError as e:
|
||||
console.print(f"[red]Error generating {license_type} license:[/red] {str(e)}")
|
||||
console.print(
|
||||
f"[red]Error generating {license_type} license:[/red] {str(e)}"
|
||||
)
|
||||
continue
|
||||
|
||||
|
||||
# Create a combined README section
|
||||
if len(selected_licenses) > 1:
|
||||
readme_path = "LICENSE.md"
|
||||
@@ -148,64 +164,73 @@ def generate(type: str, author: str, year: int, output: str, multi: bool):
|
||||
f.write("This project is licensed under multiple licenses:\n\n")
|
||||
for license in selected_licenses:
|
||||
f.write(f"- [{license['name']}](LICENSE_{license['key']})\n")
|
||||
console.print(f"\n[green]✓[/green] Created license summary at: {readme_path}")
|
||||
|
||||
console.print(
|
||||
f"\n[green]✓[/green] Created license summary at: {readme_path}"
|
||||
)
|
||||
|
||||
except ValueError as e:
|
||||
console.print(f"[red]Error:[/red] {str(e)}")
|
||||
except Exception as e:
|
||||
console.print(f"[red]Error:[/red] {str(e)}")
|
||||
|
||||
|
||||
@license.command()
|
||||
def list():
|
||||
"""List available licenses."""
|
||||
show_licenses()
|
||||
|
||||
|
||||
def format_permissions(permissions: list) -> str:
|
||||
"""Format permissions list into a readable string."""
|
||||
if not permissions:
|
||||
return "N/A"
|
||||
|
||||
|
||||
# Map permission keys to readable names
|
||||
permission_map = {
|
||||
"commercial-use": "Commercial Use",
|
||||
"modifications": "Modifications",
|
||||
"distribution": "Distribution",
|
||||
"private-use": "Private Use",
|
||||
"patent-use": "Patent Use"
|
||||
"patent-use": "Patent Use",
|
||||
}
|
||||
|
||||
|
||||
# Convert each permission to its readable name
|
||||
readable_permissions = [
|
||||
permission_map.get(perm, perm.replace("-", " ").title())
|
||||
for perm in permissions
|
||||
permission_map.get(perm, perm.replace("-", " ").title()) for perm in permissions
|
||||
]
|
||||
|
||||
|
||||
return ", ".join(readable_permissions)
|
||||
|
||||
|
||||
def truncate_description(description: str, max_length: int = 60) -> str:
|
||||
"""Truncate description to a maximum length and add ellipsis if needed."""
|
||||
if not description:
|
||||
return "No description available"
|
||||
|
||||
|
||||
# Remove HTML tags
|
||||
description = re.sub(r'<[^>]+>', '', description)
|
||||
|
||||
description = re.sub(r"<[^>]+>", "", description)
|
||||
|
||||
# Remove URLs
|
||||
description = re.sub(r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\\(\\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', '', description)
|
||||
|
||||
description = re.sub(
|
||||
r"http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\\(\\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+",
|
||||
"",
|
||||
description,
|
||||
)
|
||||
|
||||
# Clean up whitespace
|
||||
description = ' '.join(description.split())
|
||||
|
||||
description = " ".join(description.split())
|
||||
|
||||
if len(description) <= max_length:
|
||||
return description
|
||||
|
||||
|
||||
# Find the last space before max_length
|
||||
last_space = description[:max_length].rfind(" ")
|
||||
if last_space == -1:
|
||||
last_space = max_length
|
||||
|
||||
|
||||
return description[:last_space] + "..."
|
||||
|
||||
|
||||
def show_licenses():
|
||||
"""Display available licenses in a table."""
|
||||
try:
|
||||
@@ -214,36 +239,41 @@ def show_licenses():
|
||||
github_token = config.get("github_token")
|
||||
if github_token:
|
||||
os.environ["GITHUB_TOKEN"] = github_token
|
||||
|
||||
|
||||
# Fetch available licenses
|
||||
licenses = license_generator.fetch_available_licenses()
|
||||
|
||||
|
||||
if not licenses:
|
||||
console.print("[yellow]No licenses available")
|
||||
return
|
||||
|
||||
|
||||
# Create table
|
||||
table = Table(show_header=True, header_style="bold magenta")
|
||||
table.add_column("License Key", style="dim", width=15)
|
||||
table.add_column("Name", width=30)
|
||||
table.add_column("Permissions", width=40)
|
||||
|
||||
|
||||
# Add rows
|
||||
for license in licenses:
|
||||
table.add_row(
|
||||
license["key"],
|
||||
license["name"],
|
||||
format_permissions(license.get("permissions", []))
|
||||
format_permissions(license.get("permissions", [])),
|
||||
)
|
||||
|
||||
|
||||
# Display table
|
||||
console.print("\nAvailable Licenses:")
|
||||
console.print(table)
|
||||
console.print("\nTo generate a license, use: [bold]devtools license generate[/bold]")
|
||||
console.print("To generate multiple licenses, use: [bold]devtools license generate --multi[/bold]")
|
||||
|
||||
console.print(
|
||||
"\nTo generate a license, use: [bold]devtools license generate[/bold]"
|
||||
)
|
||||
console.print(
|
||||
"To generate multiple licenses, use: [bold]devtools license generate --multi[/bold]"
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
console.print(f"[red]Error:[/red] {str(e)}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
license()
|
||||
license()
|
||||
Reference in new issue
Block a user