Coverage for src/system/git.py: 100.00%
40 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-10-18 15:02 +0000
« prev ^ index » next coverage.py v7.15.4, created at 2026-10-18 15:02 +0000
1#!/usr/bin/env python3
3# Standard libraries
4from os import environ
5from re import sub as regex_sub
6from subprocess import CalledProcessError, check_output, DEVNULL
7from typing import Optional
9# Components
10from ..package.bundle import Bundle
12# Git class
13class Git:
15 # Members
16 __binary: str = 'git'
18 # Constructor
19 def __init__(self) -> None:
21 # Configure binary
22 if environ.get(Bundle.ENV_GIT_BINARY_PATH, ''):
23 self.__binary = environ[Bundle.ENV_GIT_BINARY_PATH]
25 # Default branch
26 def branch_default(
27 self,
28 workdir: Optional[str] = None,
29 ) -> str:
31 # Result
32 try:
33 remote_name: str = check_output(
34 [self.__binary, 'remote'],
35 cwd=workdir,
36 shell=False,
37 stderr=DEVNULL,
38 ).strip().decode().splitlines()[0]
39 return check_output(
40 [
41 self.__binary,
42 'symbolic-ref',
43 '--short',
44 f'refs/remotes/{remote_name}/HEAD',
45 ],
46 shell=False,
47 stderr=DEVNULL,
48 ).strip().decode().rsplit('/', maxsplit=1)[-1]
49 except (CalledProcessError, FileNotFoundError, IndexError):
50 return 'main'
52 # HEAD reference name
53 def head_reference_name(
54 self,
55 workdir: Optional[str] = None,
56 ) -> str:
58 # Result
59 try:
60 return check_output(
61 [self.__binary, 'rev-parse', '--abbrev-ref', 'HEAD'],
62 cwd=workdir,
63 shell=False,
64 stderr=DEVNULL,
65 ).strip().decode()
66 except (CalledProcessError, FileNotFoundError):
67 return ''
69 # HEAD reference slug
70 def head_reference_slug(
71 self,
72 workdir: Optional[str] = None,
73 name: Optional[str] = None,
74 ) -> str:
76 # Get name
77 if name is None: # pragma: no cover
78 name = self.head_reference_name(workdir)
80 # Adapt slug
81 slug = name.lower()[0:63]
82 slug = regex_sub(r'[^a-z0-9]', '-', slug).strip('-')
84 # Result
85 return slug
87 # HEAD revision hash
88 def head_revision_hash(
89 self,
90 workdir: Optional[str] = None,
91 ) -> str:
93 # Result
94 try:
95 return check_output(
96 [self.__binary, 'rev-parse', 'HEAD'],
97 cwd=workdir,
98 shell=False,
99 stderr=DEVNULL,
100 ).strip().decode()
101 except (CalledProcessError, FileNotFoundError):
102 return ''
104 # HEAD revision short hash
105 def head_revision_short_hash(
106 self,
107 workdir: Optional[str] = None,
108 ) -> str:
110 # Result
111 try:
112 return check_output(
113 [self.__binary, 'rev-parse', '--short', 'HEAD'],
114 cwd=workdir,
115 shell=False,
116 stderr=DEVNULL,
117 ).strip().decode()
118 except (CalledProcessError, FileNotFoundError):
119 return ''
121 # HEAD revision timestamp
122 def head_revision_timestamp(
123 self,
124 workdir: Optional[str] = None,
125 ) -> str:
127 # Result
128 try:
129 return check_output(
130 [self.__binary, 'show', '-s', '--format=%cI', 'HEAD'],
131 cwd=workdir,
132 shell=False,
133 stderr=DEVNULL,
134 ).strip().decode()
135 except (CalledProcessError, FileNotFoundError):
136 return ''