1#

2# Copyright (C) 2019-2025 Mathieu Parent <math.parent@gmail.com>

3#

4# This program is free software: you can redistribute it and/or modify

5# it under the terms of the GNU Lesser General Public License as published by

6# the Free Software Foundation, either version 3 of the License, or

7# (at your option) any later version.

8#

9# This program is distributed in the hope that it will be useful,

10# but WITHOUT ANY WARRANTY; without even the implied warranty of

11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

12# GNU Lesser General Public License for more details.

13#

14# You should have received a copy of the GNU Lesser General Public License

15# along with this program. If not, see <http://www.gnu.org/licenses/>.

16

17

18 from __future__ import annotations

19

20 from os import getenv

21 from typing import TYPE_CHECKING

22

23 from gitlab import Gitlab

24 from requests.auth import HTTPBasicAuth

25

26 from gitlabracadabra import __version__ as gitlabracadabra_version

27 from gitlabracadabra.auth_info import AuthInfo

28 from gitlabracadabra.gitlab.deploy_key_cache import DeployKeyCache

29 from gitlabracadabra.gitlab.group_cache import GroupCache

30 from gitlabracadabra.gitlab.user_cache import UserCache

31

32 if TYPE_CHECKING: 32 ↛ 33 line 32 didn't jump to line 33 because the condition on line 32 was never true

33 from requests import Session

34

35 from gitlabracadabra.containers.authenticated_session import AuthenticatedSession

36

37

38 class PyGitlab:

39 """Python-Gitlab wrapper."""

40

41 def __init__(

42 self,

43 gitlab_id: str | None,

44 config_files: list[str] | None,

45 *,

46 debug: bool,

47 auth: bool = True,

48 ) -> None:

49 """Initialize a Python-Gitlab wrapper.

50

51 Args:

52 gitlab_id: Section in python-gitlab config files.

53 config_files: None or list of configuration files.

54 debug: True to enable debugging.

55 auth: True to authenticate on creation.

56 """

57 self._gitlab_id = gitlab_id

58 self._config_files = config_files

59 self._debug = debug

60 options: dict[str, str | None] = {

61 "server_url": getenv("GITLAB_URL"),

62 "private_token": getenv("GITLAB_PRIVATE_TOKEN"),

63 "oauth_token": getenv("GITLAB_OAUTH_TOKEN"),

64 }

65 try:

66 self._gl = Gitlab.merge_config(options, self.gitlab_id, self._config_files)

67 except AttributeError:

68 self._gl = Gitlab.from_config(self.gitlab_id, self._config_files)

69 if self.gitlab_tls_verify != "": 69 ↛ 70 line 69 didn't jump to line 70 because the condition on line 69 was never true

70 self._gl.ssl_verify = self.gitlab_tls_verify

71 self._gl.headers["User-Agent"] = f"gitlabracadabra/{gitlabracadabra_version}"

72 if auth and (self.pygitlab.private_token or self.pygitlab.oauth_token):

73 self.pygitlab.auth()

74 if self._debug: 74 ↛ 75 line 74 didn't jump to line 75 because the condition on line 74 was never true

75 self.pygitlab.enable_debug()

76

77 self.group_cache = GroupCache(self)

78 self.user_cache = UserCache(self)

79 self.deploy_key_cache = DeployKeyCache(self)

80

81 @property

82 def gitlab_id(self) -> str | None:

83 """Get Gitlab id (section in python-gitlab config files).

84

85 Returns:

86 A string.

87 """

88 return self._gitlab_id

89

90 @property

91 def pygitlab(self) -> Gitlab:

92 """Get python-gitlab object.

93

94 Returns:

95 A gitlab.Gitlab object.

96 """

97 return self._gl

98

99 @property

100 def registry_auth_info(self) -> AuthInfo:

101 """Get Registry Authentication information.

102

103 Returns:

104 A dict, with 'headers' and 'auth' to pass to requests.

105

106 Raises:

107 ValueError: No auth info.

108 """

109 if self.pygitlab.private_token: 109 ↛ 111 line 109 didn't jump to line 111 because the condition on line 109 was always true

110 return AuthInfo(auth=HTTPBasicAuth("personal-access-token", self.pygitlab.private_token))

111 if self.pygitlab.oauth_token:

112 return AuthInfo(auth=HTTPBasicAuth("oauth2", self.pygitlab.oauth_token))

113 if self.pygitlab.job_token:

114 return AuthInfo(auth=HTTPBasicAuth("gitlab-ci-token", self.pygitlab.job_token))

115 if self.pygitlab.http_username and self.pygitlab.http_password:

116 return AuthInfo(auth=HTTPBasicAuth(self.pygitlab.http_username, self.pygitlab.http_password))

117 msg = "No auth info"

118 raise ValueError(msg)

119

120 def registry_session_callback(self, session: AuthenticatedSession) -> None:

121 """Apply options to registry session.

122

123 Args:

124 session: requests Session

125 """

126 session.verify = self._gl.ssl_verify

127 session.auth_info = self.registry_auth_info

128

129 def session_callback(self, session: Session) -> None:

130 """Apply options to requests session.

131

132 Args:

133 session: requests Session

134 """

135 session.verify = self._gl.ssl_verify

136 registry_auth_info = self.registry_auth_info

137 session.auth = registry_auth_info.auth

138 if registry_auth_info.headers is not None: 138 ↛ 139 line 138 didn't jump to line 139 because the condition on line 138 was never true

139 for header_name, header_value in registry_auth_info.headers.items():

140 session.headers[header_name] = header_value

141

142 @property

143 def api_url(self) -> str:

144 """Get API URL.

145

146 Returns:

147 API URL.

148 """

149 return self.pygitlab.api_url

150

151 @property

152 def gitlab_tls_verify(self) -> bool | str:

153 """Get TLS verify.

154

155 Returns:

156 Boolean to enable or disable, string for CA path, empty string to keep default.

157 """

158 gitlab_tls_verify = getenv("GITLAB_TLS_VERIFY", "")

159 if gitlab_tls_verify in {"true", "false"}: 159 ↛ 160 line 159 didn't jump to line 160 because the condition on line 159 was never true

160 return gitlab_tls_verify == "true"

161 return gitlab_tls_verify