Coverage for src / updates2mqtt / integrations / git_utils.py: 81%
59 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-20 02:29 +0000
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-20 02:29 +0000
1import datetime
2import re
3import subprocess
4from pathlib import Path
5from re import Match
7import structlog
9log = structlog.get_logger()
12def git_trust(repo_path: Path, git_path: Path) -> bool:
13 try:
14 subprocess.run(f"{git_path} config --global --add safe.directory {repo_path}", check=True, shell=True, cwd=repo_path)
15 return True
16 except Exception as e:
17 log.warn("GIT Unable to trust repo at %s: %s", repo_path, e, action="git_trust")
18 return False
21def git_iso_timestamp(repo_path: Path, git_path: Path) -> str | None:
22 result = None
23 try:
24 result = subprocess.run(
25 str(git_path) + r" log -1 --format=%cI --no-show-signature",
26 cwd=repo_path,
27 shell=True,
28 text=True,
29 capture_output=True,
30 check=True,
31 )
32 # round-trip the iso format for pythony consistency
33 return datetime.datetime.fromisoformat(result.stdout.strip()).isoformat()
34 except subprocess.CalledProcessError as cpe:
35 log.warn("GIT No result from git log at %s: %s", repo_path, cpe, action="git_iso_timestamp")
36 except Exception as e:
37 log.error(
38 "GIT Unable to parse timestamp at %s - %s: %s",
39 repo_path,
40 result.stdout if result else "<NO RESULT>",
41 e,
42 action="git_iso_timestamp",
43 )
44 return None
47def git_local_version(repo_path: Path, git_path: Path) -> str | None:
48 result = None
49 try:
50 result = subprocess.run(
51 f"{git_path} rev-parse HEAD",
52 cwd=repo_path,
53 shell=True,
54 text=True,
55 capture_output=True,
56 check=True,
57 )
58 if result.returncode == 0: 58 ↛ 71line 58 didn't jump to line 71 because the condition on line 58 was always true
59 log.debug("Local git rev-parse", action="git_local_version", path=repo_path, version=result.stdout.strip())
60 return f"git:{result.stdout.strip()}"[:19]
61 except subprocess.CalledProcessError as cpe:
62 log.warn("GIT No result from git rev-parse at %s: %s", repo_path, cpe, action="git_local_version")
63 except Exception as e:
64 log.error(
65 "GIT Unable to retrieve version at %s - %s: %s",
66 repo_path,
67 result.stdout if result else "<NO RESULT>",
68 e,
69 action="git_local_version",
70 )
71 return None
74def git_check_update_available(repo_path: Path, git_path: Path, timeout: int = 120) -> int:
75 result = None
76 try:
77 # check if remote repo ahead
78 result = subprocess.run(
79 f"{git_path} fetch;{git_path} status -uno",
80 capture_output=True,
81 text=True,
82 shell=True,
83 check=True,
84 cwd=repo_path,
85 timeout=timeout,
86 )
87 if result.returncode == 0: 87 ↛ 103line 87 didn't jump to line 103 because the condition on line 87 was always true
88 count_match: Match[str] | None = re.search(
89 r"Your branch is behind.*by (\d+) commit", result.stdout, flags=re.MULTILINE
90 )
91 if count_match and count_match.groups():
92 log.debug(
93 "Local git repo update available: %s (%s)",
94 count_match.group(1),
95 result.stdout.strip(),
96 action="git_check",
97 path=repo_path,
98 )
99 return int(count_match.group(1))
100 log.debug("Local git repo no update available", action="git_check", path=repo_path, status=result.stdout.strip())
101 return 0
103 log.debug(
104 "No git update available",
105 action="git_check",
106 path=repo_path,
107 returncode=result.returncode,
108 stdout=result.stdout,
109 stderr=result.stderr,
110 )
111 except Exception as e:
112 log.warn("GIT Unable to check status %s: %s", result.stdout if result else "<NO RESULT>", e, action="git_check")
113 return 0
116def git_pull(repo_path: Path, git_path: Path) -> bool:
117 log.info("GIT Pulling git at %s", repo_path, action="git_pull")
118 proc = subprocess.run(f"{git_path} pull", shell=True, check=False, cwd=repo_path, timeout=300)
119 if proc.returncode == 0:
120 log.info("GIT pull at %s successful", repo_path, action="git_pull")
121 return True
122 log.warn("GIT pull at %s failed: %s", repo_path, proc.returncode, action="git_pull", stdout=proc.stdout, stderr=proc.stderr)
123 return False