Add utils, templates, config, interactive, and github modules
This commit is contained in:
186
src/auto_readme/utils/git_utils.py
Normal file
186
src/auto_readme/utils/git_utils.py
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
"""Git utilities for the Auto README Generator."""
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from ..models import GitInfo
|
||||||
|
|
||||||
|
|
||||||
|
class GitUtils:
|
||||||
|
"""Utility class for git operations."""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def is_git_repo(cls, path: Path) -> bool:
|
||||||
|
"""Check if the path is a git repository."""
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
["git", "rev-parse", "--git-dir"],
|
||||||
|
cwd=path,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=5,
|
||||||
|
)
|
||||||
|
return result.returncode == 0
|
||||||
|
except (subprocess.SubprocessError, FileNotFoundError):
|
||||||
|
return False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_remote_url(cls, path: Path) -> Optional[str]:
|
||||||
|
"""Get the remote URL for the origin remote."""
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
["git", "remote", "get-url", "origin"],
|
||||||
|
cwd=path,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=5,
|
||||||
|
)
|
||||||
|
if result.returncode == 0:
|
||||||
|
return result.stdout.strip()
|
||||||
|
except (subprocess.SubprocessError, FileNotFoundError):
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_current_branch(cls, path: Path) -> Optional[str]:
|
||||||
|
"""Get the current branch name."""
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
["git", "rev-parse", "--abbrev-ref", "HEAD"],
|
||||||
|
cwd=path,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=5,
|
||||||
|
)
|
||||||
|
if result.returncode == 0:
|
||||||
|
return result.stdout.strip()
|
||||||
|
except (subprocess.SubprocessError, FileNotFoundError):
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_commit_sha(cls, path: Path, short: bool = True) -> Optional[str]:
|
||||||
|
"""Get the current commit SHA."""
|
||||||
|
try:
|
||||||
|
flag = "--short" if short else ""
|
||||||
|
result = subprocess.run(
|
||||||
|
["git", "rev-parse", flag, "HEAD"],
|
||||||
|
cwd=path,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=5,
|
||||||
|
)
|
||||||
|
if result.returncode == 0:
|
||||||
|
return result.stdout.strip()
|
||||||
|
except (subprocess.SubprocessError, FileNotFoundError):
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_repo_info(cls, remote_url: Optional[str]) -> tuple[Optional[str], Optional[str]]:
|
||||||
|
"""Extract owner and repo name from remote URL."""
|
||||||
|
if not remote_url:
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
|
patterns = [
|
||||||
|
r"github\.com[:/](/[^/]+)/([^/]+)\.git",
|
||||||
|
r"github\.com/([^/]+)/([^/]+)",
|
||||||
|
r"git@github\.com:([^/]+)/([^/]+)\.git",
|
||||||
|
r"git@github\.com:([^/]+)/([^/]+)",
|
||||||
|
]
|
||||||
|
|
||||||
|
for pattern in patterns:
|
||||||
|
match = re.search(pattern, remote_url)
|
||||||
|
if match:
|
||||||
|
owner, repo = match.groups()
|
||||||
|
repo = repo.replace(".git", "")
|
||||||
|
return owner, repo
|
||||||
|
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_git_info(cls, path: Path) -> GitInfo:
|
||||||
|
"""Get complete git information for a repository."""
|
||||||
|
is_repo = cls.is_git_repo(path)
|
||||||
|
if not is_repo:
|
||||||
|
return GitInfo()
|
||||||
|
|
||||||
|
remote_url = cls.get_remote_url(path)
|
||||||
|
branch = cls.get_current_branch(path)
|
||||||
|
commit_sha = cls.get_commit_sha(path)
|
||||||
|
|
||||||
|
owner, repo_name = cls.get_repo_info(remote_url)
|
||||||
|
|
||||||
|
return GitInfo(
|
||||||
|
remote_url=remote_url,
|
||||||
|
branch=branch,
|
||||||
|
commit_sha=commit_sha,
|
||||||
|
is_repo=True,
|
||||||
|
repo_name=repo_name,
|
||||||
|
owner=owner,
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_last_commit_message(cls, path: Path) -> Optional[str]:
|
||||||
|
"""Get the last commit message."""
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
["git", "log", "-1", "--format=%s"],
|
||||||
|
cwd=path,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=5,
|
||||||
|
)
|
||||||
|
if result.returncode == 0:
|
||||||
|
return result.stdout.strip()
|
||||||
|
except (subprocess.SubprocessError, FileNotFoundError):
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_commit_count(cls, path: Path) -> Optional[int]:
|
||||||
|
"""Get the total number of commits."""
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
["git", "rev-list", "--count", "HEAD"],
|
||||||
|
cwd=path,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=5,
|
||||||
|
)
|
||||||
|
if result.returncode == 0:
|
||||||
|
return int(result.stdout.strip())
|
||||||
|
except (subprocess.SubprocessError, FileNotFoundError, ValueError):
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_contributors(cls, path: Path) -> list[str]:
|
||||||
|
"""Get list of contributors."""
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
["git", "log", "--format=%an", "--reverse"],
|
||||||
|
cwd=path,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=10,
|
||||||
|
)
|
||||||
|
if result.returncode == 0:
|
||||||
|
contributors = []
|
||||||
|
seen = set()
|
||||||
|
for line in result.stdout.strip().split("\n"):
|
||||||
|
if line and line not in seen:
|
||||||
|
seen.add(line)
|
||||||
|
contributors.append(line)
|
||||||
|
return contributors
|
||||||
|
except (subprocess.SubprocessError, FileNotFoundError):
|
||||||
|
pass
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def get_git_info(path: Path) -> GitInfo:
|
||||||
|
"""Get complete git information for a repository."""
|
||||||
|
return GitUtils.get_git_info(path)
|
||||||
Reference in New Issue
Block a user