From 9f948a803a4b8e099cf5cac0dbfdc9f80abbb55d Mon Sep 17 00:00:00 2001 From: 7000pctAUTO Date: Thu, 29 Jan 2026 12:00:30 +0000 Subject: [PATCH] Initial upload of auto-changelog-generator --- src/changeloggen/git_hooks.py | 136 ++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) create mode 100644 src/changeloggen/git_hooks.py diff --git a/src/changeloggen/git_hooks.py b/src/changeloggen/git_hooks.py new file mode 100644 index 0000000..7a497fc --- /dev/null +++ b/src/changeloggen/git_hooks.py @@ -0,0 +1,136 @@ +from pathlib import Path +from typing import Optional +import os +import stat + + +class GitHookType: + PREPARE_COMMIT_MSG = "prepare-commit-msg" + COMMIT_MSG = "commit-msg" + + +def get_git_hooks_dir(repo_path: Optional[Path] = None) -> Path: + """Get the git hooks directory for a repository.""" + if repo_path is None: + repo_path = Path.cwd() + + git_dir = repo_path / ".git" + hooks_dir = git_dir / "hooks" + return hooks_dir + + +def get_hook_script_path(hook_type: str, repo_path: Optional[Path] = None) -> Path: + """Get the path for a specific hook script.""" + hooks_dir = get_git_hooks_dir(repo_path) + return hooks_dir / hook_type + + +def install_prepare_hook( + repo_path: Optional[Path] = None, + model: str = "llama3.2", + branches: Optional[list[str]] = None +) -> Path: + """Install prepare-commit-msg hook for automatic changelog generation.""" + hook_path = get_hook_script_path(GitHookType.PREPARE_COMMIT_MSG, repo_path) + + branches_str = ",".join(branches) if branches else "*" + + hook_content = f'''#!/bin/bash +# Auto Changelog Generator - prepare-commit-msg hook +# Generated by changeloggen + +CHANGELOGGEN_MODEL="{model}" +CHANGELOGGEN_BRANCHES="{branches_str}" + +BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD) + +if [ "$CHANGELOGGEN_BRANCHES" != "*" ]; then + if ! echo "$CHANGELOGGEN_BRANCHES" | grep -q "$BRANCH_NAME"; then + exit 0 + fi +fi + +STAGED_FILES=$(git diff --cached --name-only) +if [ -z "$STAGED_FILES" ]; then + echo "No staged files found. Skipping changelog generation." + exit 0 +fi + +echo "Generating changelog for commit on branch: $BRANCH_NAME" +python -m changeloggen generate --model "$CHANGELOGGEN_MODEL" --output commit-message + +exit 0 +''' + + return _write_hook(hook_path, hook_content) + + +def install_commit_msg_hook( + repo_path: Optional[Path] = None, + model: str = "llama3.2", + branches: Optional[list[str]] = None +) -> Path: + """Install commit-msg hook for validating/updating commit messages.""" + hook_path = get_hook_script_path(GitHookType.COMMIT_MSG, repo_path) + + branches_str = ",".join(branches) if branches else "*" + + hook_content = f'''#!/bin/bash +# Auto Changelog Generator - commit-msg hook +# Generated by changeloggen + +CHANGELOGGEN_MODEL="{model}" +CHANGELOGGEN_BRANCHES="{branches_str}" + +BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD) + +if [ "$CHANGELOGGEN_BRANCHES" != "*" ]; then + if ! echo "$CHANGELOGGEN_BRANCHES" | grep -q "$BRANCH_NAME"; then + exit 0 + fi +fi + +exit 0 +''' + + return _write_hook(hook_path, hook_content) + + +def _write_hook(hook_path: Path, content: str) -> Path: + """Write hook script and make it executable.""" + hook_path.parent.mkdir(parents=True, exist_ok=True) + + with open(hook_path, 'w') as f: + f.write(content) + + os.chmod(hook_path, hook_path.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) + + return hook_path + + +def remove_hook(hook_type: str, repo_path: Optional[Path] = None) -> bool: + """Remove a git hook.""" + hook_path = get_hook_script_path(hook_type, repo_path) + + if hook_path.exists(): + hook_path.unlink() + return True + + return False + + +def is_hook_installed(hook_type: str, repo_path: Optional[Path] = None) -> bool: + """Check if a hook is installed.""" + hook_path = get_hook_script_path(hook_type, repo_path) + return hook_path.exists() + + +def list_installed_hooks(repo_path: Optional[Path] = None) -> list[str]: + """List all installed changeloggen hooks.""" + hooks = [] + + for hook_type in [GitHookType.PREPARE_COMMIT_MSG, GitHookType.COMMIT_MSG]: + if is_hook_installed(hook_type, repo_path): + hooks.append(hook_type) + + return hooks