261 lines
8.7 KiB
Python
261 lines
8.7 KiB
Python
"""CLI commands for gitignore-generator."""
|
|
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import List, Optional
|
|
|
|
import click
|
|
|
|
from gitignore_generator import __version__
|
|
from gitignore_generator.config import config
|
|
from gitignore_generator.template_loader import template_loader
|
|
from gitignore_generator.validator import validator
|
|
from gitignore_generator.cli.interactive import run_simple_interactive
|
|
|
|
|
|
def print_version(ctx: click.Context, param: click.Parameter, value: bool) -> None:
|
|
"""Print version and exit."""
|
|
if not value or ctx.resilient_parsing:
|
|
return
|
|
click.echo(f"gitignore-generator v{__version__}")
|
|
ctx.exit()
|
|
|
|
|
|
@click.group()
|
|
@click.option("--version", is_flag=True, callback=print_version, expose_value=False, help="Show version")
|
|
@click.option("--config", "-c", type=click.Path(exists=False), help="Path to config file")
|
|
@click.pass_context
|
|
def main(ctx: click.Context, config: Optional[str] = None) -> None:
|
|
"""Generate optimized .gitignore files for your projects."""
|
|
ctx.ensure_object(dict)
|
|
|
|
|
|
@main.command("generate")
|
|
@click.argument("templates", nargs=-1)
|
|
@click.option("--output", "-o", default=None, help="Output file (default: .gitignore)")
|
|
@click.option("--ide", multiple=True, help="Add IDE-specific templates")
|
|
@click.option("--validate/--no-validate", default=True, help="Validate output syntax")
|
|
@click.option("--overwrite", "-f", is_flag=True, default=False, help="Overwrite existing file")
|
|
def generate(
|
|
templates: tuple,
|
|
output: Optional[str],
|
|
ide: tuple,
|
|
validate: bool,
|
|
overwrite: bool
|
|
) -> None:
|
|
"""Generate .gitignore from templates."""
|
|
output_file = output or config.default_output
|
|
|
|
if os.path.exists(output_file) and not overwrite:
|
|
if not click.confirm(f"File '{output_file}' exists. Overwrite?"):
|
|
return
|
|
|
|
selected_templates = list(templates)
|
|
|
|
for ide_template in ide:
|
|
selected_templates.append(f"ide:{ide_template}")
|
|
|
|
if not selected_templates:
|
|
click.echo("Error: No templates specified. Use 'gitignore generate python' or 'gitignore list' to see templates.")
|
|
sys.exit(1)
|
|
|
|
content_parts = []
|
|
errors = []
|
|
|
|
for template_ref in selected_templates:
|
|
if template_ref.startswith("ide:"):
|
|
template_name = template_ref[4:]
|
|
content = template_loader.get_template_content(template_name)
|
|
category = "ides"
|
|
else:
|
|
template_name = template_ref
|
|
content = template_loader.get_template_content(template_name)
|
|
category = None
|
|
|
|
if content:
|
|
content_parts.append(f"# --- {template_name} ---\n")
|
|
content_parts.append(content.rstrip())
|
|
content_parts.append("\n")
|
|
else:
|
|
errors.append(template_name)
|
|
|
|
if errors:
|
|
click.echo(f"Error: Templates not found: {', '.join(errors)}")
|
|
click.echo("Use 'gitignore list' to see available templates.")
|
|
sys.exit(1)
|
|
|
|
if not content_parts:
|
|
click.echo("Error: No content generated.")
|
|
sys.exit(1)
|
|
|
|
final_content = "\n".join(content_parts).rstrip() + "\n"
|
|
|
|
if validate:
|
|
summary = validator.get_summary(final_content)
|
|
if summary["errors"] > 0:
|
|
click.echo(f"\nValidation errors found:")
|
|
for issue in summary["issues"]:
|
|
if issue.severity == "error":
|
|
click.echo(f" Line {issue.line_number}: {issue.message}")
|
|
if not click.confirm("Generate anyway?"):
|
|
sys.exit(1)
|
|
if summary["warnings"] > 0:
|
|
click.echo(f"\nValidation warnings:")
|
|
for issue in summary["issues"]:
|
|
if issue.severity == "warning":
|
|
click.echo(f" Line {issue.line_number}: {issue.message}")
|
|
|
|
try:
|
|
with open(output_file, "w") as f:
|
|
f.write(final_content)
|
|
click.echo(f"Successfully generated '{output_file}' with {len(selected_templates)} template(s).")
|
|
except IOError as e:
|
|
click.echo(f"Error writing file: {e}")
|
|
sys.exit(1)
|
|
|
|
|
|
@main.command("list")
|
|
@click.option("--category", "-c", type=click.Choice(["languages", "ides", "custom", "all"]), default="all")
|
|
def list_templates(category: str) -> None:
|
|
"""List available templates."""
|
|
templates_by_cat = template_loader.get_templates_by_category()
|
|
|
|
click.echo("Available Templates:\n")
|
|
|
|
if category in ["all", "languages"]:
|
|
langs = templates_by_cat.get("languages", [])
|
|
click.echo(f"Languages ({len(langs)}):")
|
|
for i, template in enumerate(langs, 1):
|
|
click.echo(f" {template}", nl=False)
|
|
if i % 4 == 0:
|
|
click.echo()
|
|
click.echo()
|
|
|
|
if category in ["all", "ides"]:
|
|
ides = templates_by_cat.get("ides", [])
|
|
click.echo(f"IDEs ({len(ides)}):")
|
|
for i, template in enumerate(ides, 1):
|
|
click.echo(f" {template}", nl=False)
|
|
if i % 4 == 0:
|
|
click.echo()
|
|
click.echo()
|
|
|
|
if category in ["all", "custom"]:
|
|
customs = templates_by_cat.get("custom", [])
|
|
click.echo(f"Custom ({len(customs)}):")
|
|
if customs:
|
|
for template in customs:
|
|
click.echo(f" {template}")
|
|
else:
|
|
click.echo(" (no custom templates)")
|
|
|
|
|
|
@main.command("search")
|
|
@click.argument("query")
|
|
def search_templates(query: str) -> None:
|
|
"""Search for templates."""
|
|
results = template_loader.search_templates(query)
|
|
|
|
if results:
|
|
click.echo(f"Found {len(results)} template(s) matching '{query}':")
|
|
for template in results:
|
|
click.echo(f" - {template}")
|
|
else:
|
|
click.echo(f"No templates found matching '{query}'.")
|
|
|
|
|
|
@main.command("interactive")
|
|
@click.option("--output", "-o", default=None, help="Output file (default: .gitignore)")
|
|
def interactive(output: Optional[str]) -> None:
|
|
"""Launch interactive wizard to build .gitignore."""
|
|
run_simple_interactive()
|
|
|
|
|
|
@main.command("validate")
|
|
@click.argument("file", type=click.Path(exists=True))
|
|
def validate_file(file: str) -> None:
|
|
"""Validate a .gitignore file."""
|
|
with open(file, "r") as f:
|
|
content = f.read()
|
|
|
|
summary = validator.get_summary(content)
|
|
|
|
click.echo(f"Validation Results for '{file}':")
|
|
click.echo(f" Status: {'Valid' if summary['valid'] else 'Invalid'}")
|
|
click.echo(f" Errors: {summary['errors']}")
|
|
click.echo(f" Warnings: {summary['warnings']}")
|
|
|
|
if summary["issues"]:
|
|
click.echo("\nIssues:")
|
|
for issue in summary["issues"]:
|
|
severity = "ERROR" if issue.severity == "error" else "WARNING"
|
|
click.echo(f" [{severity}] Line {issue.line_number}: {issue.message}")
|
|
click.echo(f" Pattern: {issue.line}")
|
|
|
|
|
|
@main.command("template-add")
|
|
@click.argument("name")
|
|
@click.argument("file", type=click.Path(exists=True))
|
|
def template_add(name: str, file: str) -> None:
|
|
"""Add a custom template from file."""
|
|
with open(file, "r") as f:
|
|
content = f.read()
|
|
|
|
if template_loader.save_custom_template(name, content):
|
|
click.echo(f"Template '{name}' added successfully.")
|
|
else:
|
|
click.echo(f"Error: Failed to add template '{name}'.")
|
|
sys.exit(1)
|
|
|
|
|
|
@main.command("template-remove")
|
|
@click.argument("name")
|
|
def template_remove(name: str) -> None:
|
|
"""Remove a custom template."""
|
|
if template_loader.delete_custom_template(name):
|
|
click.echo(f"Template '{name}' removed successfully.")
|
|
else:
|
|
click.echo(f"Error: Template '{name}' not found or could not be removed.")
|
|
sys.exit(1)
|
|
|
|
|
|
@main.command("template-export")
|
|
@click.argument("name")
|
|
@click.argument("output", type=click.Path(exists=False))
|
|
def template_export(name: str, output: str) -> None:
|
|
"""Export a template to file."""
|
|
content = template_loader.get_template_content(name)
|
|
|
|
if content is None:
|
|
click.echo(f"Error: Template '{name}' not found.")
|
|
sys.exit(1)
|
|
|
|
try:
|
|
with open(output, "w") as f:
|
|
f.write(content)
|
|
click.echo(f"Template '{name}' exported to '{output}'.")
|
|
except IOError as e:
|
|
click.echo(f"Error writing file: {e}")
|
|
sys.exit(1)
|
|
|
|
|
|
@main.command("info")
|
|
@click.argument("template")
|
|
def template_info(template: str) -> None:
|
|
"""Show information about a template."""
|
|
content = template_loader.get_template_content(template)
|
|
|
|
if content is None:
|
|
click.echo(f"Error: Template '{template}' not found.")
|
|
sys.exit(1)
|
|
|
|
lines = [l for l in content.splitlines() if l.strip() and not l.strip().startswith("#")]
|
|
|
|
click.echo(f"Template: {template}")
|
|
click.echo(f"Lines: {len(content.splitlines())}")
|
|
click.echo(f"Patterns: {len(lines)}")
|
|
click.echo("\nFirst 10 patterns:")
|
|
for line in lines[:10]:
|
|
click.echo(f" {line}")
|