fix: Add Gitea Actions CI workflow and fix linting issues
Some checks failed
CI / test (push) Has been cancelled
CI / build (push) Has been cancelled

This commit is contained in:
2026-02-04 16:59:21 +00:00
parent 2cc3477cb5
commit 26d6c9432a

244
src/cli.py Normal file
View File

@@ -0,0 +1,244 @@
"""CLI interface for local-commit-message-generator."""
import sys
from pathlib import Path
from typing import Optional
import click
from . import __version__
from .config import (
DEFAULT_CONFIG,
ConfigError,
ensure_config_exists,
get_config_path,
load_config,
save_config,
)
from .generator import GenerationError, generate_commit_message
from .hooks import HookManager, handle_hook_invocation
def print_version(ctx: click.Context, param: click.Parameter, value: bool) -> None:
"""Print version information."""
if not value or ctx.resilient_parsing:
return
click.echo(f"local-commit-message-generator v{__version__}")
ctx.exit()
@click.group()
@click.option(
"--version",
is_flag=True,
callback=print_version,
expose_value=False,
help="Show version information."
)
@click.option(
"--repo",
default=None,
help="Path to git repository."
)
@click.pass_context
def main(ctx: click.Context, repo: Optional[str]) -> None:
"""Generate conventional commit messages from staged git changes."""
ctx.ensure_object(dict)
ctx.obj["repo"] = repo
@main.command()
@click.pass_context
def generate(ctx: click.Context) -> None:
"""Generate a commit message from staged changes."""
repo = ctx.obj.get("repo")
try:
message = generate_commit_message(repo_path=repo)
click.echo(message)
except GenerationError as e:
click.echo(f"Error: {e}", err=True)
click.echo("Make sure you have staged changes with 'git add'.", err=True)
sys.exit(1)
except Exception as e:
click.echo(f"Unexpected error: {e}", err=True)
sys.exit(1)
@main.command()
@click.pass_context
def hook(ctx: click.Context) -> None:
"""Handle prepare-commit-msg hook invocation.
This is called automatically by git when the hook is installed.
Do not call this directly.
"""
repo = ctx.obj.get("repo")
args = sys.argv[1:] if sys.argv else []
try:
message = handle_hook_invocation(args, repo)
if message:
click.echo(message)
except Exception:
pass
@main.command()
@click.option(
"--path",
"config_path",
default=None,
help="Path to config file."
)
@click.pass_context
def install_hook(ctx: click.Context, config_path: Optional[str]) -> None:
"""Install prepare-commit-msg git hook."""
repo = ctx.obj.get("repo")
repo_path = Path(repo) if repo else Path.cwd()
manager = HookManager(repo_path)
result = manager.install_hook()
if result.success:
click.secho(result.message, fg="green")
click.echo("Hook is now active. Commit messages will be auto-generated.")
else:
click.secho(result.message, fg="red", err=True)
sys.exit(1)
@main.command()
@click.pass_context
def uninstall_hook(ctx: click.Context) -> None:
"""Uninstall prepare-commit-msg git hook."""
repo = ctx.obj.get("repo")
repo_path = Path(repo) if repo else Path.cwd()
manager = HookManager(repo_path)
result = manager.uninstall_hook()
if result.success:
click.secho(result.message, fg="green")
else:
click.secho(result.message, fg="red", err=True)
sys.exit(1)
@main.command()
@click.pass_context
def status(ctx: click.Context) -> None:
"""Check git repository and staged changes status."""
repo = ctx.obj.get("repo")
try:
from .analyzer import ChangeAnalyzer
analyzer = ChangeAnalyzer(repo)
change_set = analyzer.get_staged_changes()
if change_set.has_changes:
click.secho(f"Staged changes: {change_set.total_count}", fg="green")
for change in change_set.changes[:10]:
emoji = {
"added": "+",
"deleted": "-",
"modified": "~",
"renamed": "R",
}.get(change.change_type.value, "?")
click.echo(f" {emoji} {change.path}")
if change_set.total_count > 10:
click.echo(f" ... and {change_set.total_count - 10} more")
else:
click.secho("No staged changes", fg="yellow")
click.echo("Run 'git add' to stage changes.")
except ValueError as e:
click.secho(str(e), fg="red", err=True)
sys.exit(1)
@main.group()
def config() -> None:
"""Manage configuration."""
pass
@config.command("show")
@click.pass_context
def config_show(ctx: click.Context) -> None:
"""Show current configuration."""
try:
ensure_config_exists()
config = load_config()
click.echo("Current Configuration:")
click.echo("-" * 40)
for key, value in config.items():
if key == "type_rules":
click.echo("type_rules:")
for type_, patterns in value.items():
click.echo(f" {type_}: {patterns}")
else:
click.echo(f"{key}: {value}")
except ConfigError as e:
click.secho(f"Error: {e}", fg="red", err=True)
sys.exit(1)
@config.command("set-template")
@click.argument("template", nargs=-1)
@click.pass_context
def config_set_template(ctx: click.Context, template: tuple) -> None:
"""Set the commit message template."""
try:
ensure_config_exists()
config = load_config()
template_str = " ".join(template) if template else ""
config["template"] = template_str
save_config(config)
click.secho(f"Template updated to: {template_str}", fg="green")
except ConfigError as e:
click.secho(f"Error: {e}", fg="red", err=True)
sys.exit(1)
@config.command("reset")
@click.pass_context
def config_reset(ctx: click.Context) -> None:
"""Reset configuration to defaults."""
try:
config_path = get_config_path()
if config_path.exists():
config_path.unlink()
save_config(DEFAULT_CONFIG.copy())
click.secho("Configuration reset to defaults.", fg="green")
except ConfigError as e:
click.secho(f"Error: {e}", fg="red", err=True)
sys.exit(1)
@main.command()
@click.pass_context
def preview(ctx: click.Context) -> None:
"""Preview the commit message without printing."""
repo = ctx.obj.get("repo")
try:
from .generator import get_commit_message_preview
message, has_changes = get_commit_message_preview(repo)
if has_changes:
click.secho("Preview:", fg="cyan")
click.echo("-" * 40)
click.echo(message)
click.echo("-" * 40)
else:
click.secho("No staged changes to preview.", fg="yellow")
except Exception as e:
click.secho(f"Error: {e}", fg="red", err=True)
sys.exit(1)
def cli_entrypoint() -> None:
"""Entry point for the CLI."""
main()