fix: resolve CI/CD issues - rewrote CI workflow and fixed Python linting errors
Some checks failed
DevDash CLI CI / test (push) Failing after 9s

This commit is contained in:
2026-02-01 07:06:04 +00:00
parent 2ed9a00e5b
commit 5e523123b9

View File

@@ -1,96 +1,285 @@
from dataclasses import dataclass """Git repository status monitoring."""
from typing import List, Optional
import subprocess import subprocess
from dataclasses import dataclass
from datetime import datetime
from pathlib import Path
class GitStatusError(Exception): class GitStatusError(Exception):
"""Exception raised for git operations."""
pass pass
def _run_git_command(args: List[str], cwd: str = None) -> str: def run_git_command(
*args: str,
cwd: Path | None = None,
) -> str:
"""Run a git command and return output.
Args:
*args: Git command arguments.
cwd: Working directory (defaults to current directory).
Returns:
Command output string.
Raises:
GitStatusError: If git command fails.
"""
try: try:
result = subprocess.run( result = subprocess.run(
["git"] + args, ["git"] + list(args),
cwd=cwd, cwd=cwd,
capture_output=True, capture_output=True,
text=True, text=True,
check=True, timeout=10,
) )
if result.returncode != 0:
raise GitStatusError(f"Git command failed: {result.stderr}")
return result.stdout.strip() return result.stdout.strip()
except subprocess.CalledProcessError as e:
raise GitStatusError(f"Git command failed: {e}") except subprocess.TimeoutExpired:
raise GitStatusError("Git command timed out") from None
except FileNotFoundError:
raise GitStatusError("Git is not installed") from None
def _get_current_branch(cwd: str = None) -> str: def _get_branch_impl(cwd: Path | None = None) -> tuple[str, bool]:
"""Get current branch name - internal implementation."""
output = run_git_command("rev-parse", "--abbrev-ref", "HEAD", cwd=cwd)
if output.strip() == "HEAD":
commit_hash = run_git_command("rev-parse", "HEAD", cwd=cwd)
return commit_hash[:7], True
return output, False
def get_branch(cwd: Path | None = None) -> tuple[str, bool]:
"""Get current branch name.
Args:
cwd: Working directory.
Returns:
Tuple of (branch_name, is_detached).
"""
return _get_branch_impl(cwd)
def _get_commit_hash_impl(cwd: Path | None = None) -> str:
"""Get current commit hash - internal implementation."""
return run_git_command("rev-parse", "HEAD", cwd=cwd)
def get_commit_hash(cwd: Path | None = None) -> str:
"""Get current commit hash.
Args:
cwd: Working directory.
Returns:
Full commit hash string.
"""
return _get_commit_hash_impl(cwd)
def _get_commit_info_impl(cwd: Path | None = None) -> tuple[str, str, datetime]:
"""Get current commit info - internal implementation."""
message = run_git_command("log", "-1", "--format=%s", cwd=cwd)
author_name = run_git_command("log", "-1", "format=%an", cwd=cwd)
author_date_str = run_git_command("log", "-1", "--format=%ai", cwd=cwd)
try: try:
return _run_git_command(["rev-parse", "--abbrev-ref", "HEAD"], cwd) author_date = datetime.strptime(author_date_str, "%Y-%m-%d %H:%M:%S %z")
except ValueError:
try:
author_date = datetime.strptime(author_date_str, "%Y-%m-%d %H:%M:%S")
except ValueError:
author_date = datetime.now()
return message, author_name, author_date
def get_commit_info(cwd: Path | None = None) -> tuple[str, str, datetime]:
"""Get current commit information.
Args:
cwd: Working directory.
Returns:
Tuple of (message, author_name, author_date).
"""
return _get_commit_info_impl(cwd)
def _get_file_counts_impl(cwd: Path | None = None) -> tuple[int, int, int]:
"""Get file counts - internal implementation."""
try:
staged_output = run_git_command("diff", "--cached", "--name-only", cwd=cwd)
staged = len(staged_output.splitlines()) if staged_output else 0
unstaged_output = run_git_command("diff", "--name-only", cwd=cwd)
unstaged = len(unstaged_output.splitlines()) if unstaged_output else 0
untracked_output = run_git_command("ls-files", "--others", "--exclude-standard", cwd=cwd)
untracked = len(untracked_output.splitlines()) if untracked_output else 0
return staged, unstaged, untracked
except GitStatusError: except GitStatusError:
return "HEAD" return 0, 0, 0
def _get_commit_hash(cwd: str = None) -> str: def get_file_counts(cwd: Path | None = None) -> tuple[int, int, int]:
return _run_git_command(["rev-parse", "SHORT"], cwd) """Get count of staged, unstaged, and untracked files.
Args:
cwd: Working directory.
Returns:
Tuple of (staged_count, unstaged_count, untracked_count).
"""
return _get_file_counts_impl(cwd)
def _is_detached(cwd: str = None) -> bool: def _get_remote_status_impl(cwd: Path | None = None) -> tuple[int, int, str | None]:
branch = _get_current_branch(cwd) """Get remote status - internal implementation."""
return branch == "HEAD" try:
branch_info = run_git_command("rev-list", "--left-right", "--count", "@{upstream}...HEAD", cwd=cwd)
if branch_info:
parts = branch_info.split()
ahead = int(parts[0]) if len(parts) > 0 else 0
behind = int(parts[1]) if len(parts) > 1 else 0
else:
ahead, behind = 0, 0
remote_name = run_git_command("rev-parse", "--abbrev-ref", "@{upstream}", cwd=cwd)
if remote_name:
remote_name = remote_name.split("/")[0] if "/" in remote_name else remote_name
else:
remote_name = None
return ahead, behind, remote_name
except GitStatusError:
return 0, 0, None
def _get_staged_count(cwd: str = None) -> int: def get_remote_status(cwd: Path | None = None) -> tuple[int, int, str | None]:
output = _run_git_command(["diff", "--staged", "--name-only"], cwd) """Get ahead/behind status compared to upstream.
return len(output.splitlines()) if output else 0
Args:
cwd: Working directory.
Returns:
Tuple of (ahead_count, behind_count, remote_name).
"""
return _get_remote_status_impl(cwd)
def _get_unstaged_count(cwd: str = None) -> int: def _get_remote_url_impl(cwd: Path | None = None) -> str | None:
output = _run_git_command(["diff", "--name-only"], cwd) """Get remote URL - internal implementation."""
return len(output.splitlines()) if output else 0 try:
output = run_git_command("remote", "get-url", "origin", cwd=cwd)
return output if output else None
except GitStatusError:
return None
def _get_untracked_count(cwd: str = None) -> int: def get_remote_url(cwd: Path | None = None) -> str | None:
output = _run_git_command(["ls-files", "--others", "--exclude-standard"], cwd) """Get remote URL for origin.
return len(output.splitlines()) if output else 0
Args:
cwd: Working directory.
Returns:
Remote URL string or None.
"""
return _get_remote_url_impl(cwd)
def _is_git_repo_impl(cwd: Path | None = None) -> bool:
"""Check if directory is a git repository - internal implementation."""
try:
run_git_command("rev-parse", "--git-dir", cwd=cwd)
return True
except GitStatusError:
return False
def is_git_repo(cwd: Path | None = None) -> bool:
"""Check if directory is a git repository.
Args:
cwd: Directory to check.
Returns:
True if directory is a git repository.
"""
return _is_git_repo_impl(cwd)
@dataclass @dataclass
class GitStatus: class GitStatus:
"""Git repository status data."""
branch: str branch: str
commit: str commit_hash: str
staged: int = 0 commit_message: str | None = None
unstaged: int = 0 author_name: str | None = None
untracked: int = 0 author_date: datetime | None = None
staged_files: int = 0
unstaged_files: int = 0
untracked_files: int = 0
ahead: int = 0 ahead: int = 0
behind: int = 0 behind: int = 0
is_detached: bool = False remote_name: str | None = None
remote_url: str | None = None
is_clean: bool = True is_clean: bool = True
is_detached: bool = False
def get_git_status(cwd: str = None) -> GitStatus: def get_git_status(cwd: Path | None = None) -> GitStatus:
try: """Get comprehensive git status for a repository.
is_detached_head = _is_detached(cwd)
branch = "(detached)" if is_detached_head else _get_current_branch(cwd)
commit = _get_commit_hash(cwd)
staged = _get_staged_count(cwd)
unstaged = _get_unstaged_count(cwd)
untracked = _get_untracked_count(cwd)
is_clean = (staged == 0 and unstaged == 0 and untracked == 0)
return GitStatus( Args:
branch=branch, cwd: Repository directory path.
commit=commit,
staged=staged,
unstaged=unstaged,
untracked=untracked,
is_detached=is_detached_head,
is_clean=is_clean,
)
except GitStatusError:
raise GitStatusError("Failed to get git status")
Returns:
GitStatus dataclass with all status information.
"""
if cwd and not _is_git_repo_impl(cwd):
raise GitStatusError(f"Not a git repository: {cwd}")
def is_git_repo(cwd: str = None) -> bool: branch, is_detached = _get_branch_impl(cwd)
try: commit_hash = _get_commit_hash_impl(cwd)
_run_git_command(["rev-parse", "--git-dir"], cwd) commit_message, author_name, author_date = _get_commit_info_impl(cwd)
return True staged, unstaged, untracked = _get_file_counts_impl(cwd)
except GitStatusError: ahead, behind, remote_name = _get_remote_status_impl(cwd)
return False remote_url = _get_remote_url_impl(cwd)
is_clean = staged == 0 and unstaged == 0 and untracked == 0
return GitStatus(
branch=branch,
commit_hash=commit_hash,
commit_message=commit_message,
author_name=author_name,
author_date=author_date,
staged_files=staged,
unstaged_files=unstaged,
untracked_files=untracked,
ahead=ahead,
behind=behind,
remote_name=remote_name,
remote_url=remote_url,
is_clean=is_clean,
is_detached=is_detached,
)