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

1#!/usr/bin/env python3 

2 

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 

8 

9# Components 

10from ..package.bundle import Bundle 

11 

12# Git class 

13class Git: 

14 

15 # Members 

16 __binary: str = 'git' 

17 

18 # Constructor 

19 def __init__(self) -> None: 

20 

21 # Configure binary 

22 if environ.get(Bundle.ENV_GIT_BINARY_PATH, ''): 

23 self.__binary = environ[Bundle.ENV_GIT_BINARY_PATH] 

24 

25 # Default branch 

26 def branch_default( 

27 self, 

28 workdir: Optional[str] = None, 

29 ) -> str: 

30 

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' 

51 

52 # HEAD reference name 

53 def head_reference_name( 

54 self, 

55 workdir: Optional[str] = None, 

56 ) -> str: 

57 

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 '' 

68 

69 # HEAD reference slug 

70 def head_reference_slug( 

71 self, 

72 workdir: Optional[str] = None, 

73 name: Optional[str] = None, 

74 ) -> str: 

75 

76 # Get name 

77 if name is None: # pragma: no cover 

78 name = self.head_reference_name(workdir) 

79 

80 # Adapt slug 

81 slug = name.lower()[0:63] 

82 slug = regex_sub(r'[^a-z0-9]', '-', slug).strip('-') 

83 

84 # Result 

85 return slug 

86 

87 # HEAD revision hash 

88 def head_revision_hash( 

89 self, 

90 workdir: Optional[str] = None, 

91 ) -> str: 

92 

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 '' 

103 

104 # HEAD revision short hash 

105 def head_revision_short_hash( 

106 self, 

107 workdir: Optional[str] = None, 

108 ) -> str: 

109 

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 '' 

120 

121 # HEAD revision timestamp 

122 def head_revision_timestamp( 

123 self, 

124 workdir: Optional[str] = None, 

125 ) -> str: 

126 

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 ''