Coverage for src/gitlabracadabra/gitlab/pygit2.py: 28%
40 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-14 23:10 +0200
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-14 23:10 +0200
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/>.
18from __future__ import annotations
20from typing import TYPE_CHECKING
21from urllib.parse import urlparse
23from pygit2 import Passthrough, RemoteCallbacks, UserPass
25from gitlabracadabra.gitlab.pygitlab import PyGitlab
27if TYPE_CHECKING: 27 ↛ 28line 27 didn't jump to line 28 because the condition on line 27 was never true
28 from pygit2.enums import CredentialType
31class PyGit2(PyGitlab):
32 """PyGit2 wrapper."""
34 def pygit2_certificate_check(
35 self,
36 certificate: None, # noqa: ARG002
37 valid: bool, # noqa: ARG002,FBT001
38 host: str,
39 ) -> bool:
40 """Check certificate.
42 Args:
43 _certificate: Currently always None.
44 valid: Whether the TLS/SSH library thinks the certificate is valid.
45 host: The hostname we want to connect to.
47 Returns:
48 True to connect, False to abort.
50 Raises:
51 Passthrough: Use default behavior.
52 """
53 if self.pygitlab.ssl_verify is True:
54 raise Passthrough
55 if self.pygitlab.ssl_verify is False:
56 allowed_host = urlparse(self.api_url).hostname
57 if allowed_host is not None and host in {allowed_host, allowed_host.encode()}:
58 return True
59 raise Passthrough
60 # self.pygitlab.ssl_verify is a CA path, no way to verify
61 return False
63 def pygit2_credentials(
64 self,
65 url: str,
66 username_from_url: str | None, # noqa: ARG002
67 allowed_types: CredentialType, # noqa: ARG002
68 ) -> object:
69 """Get PyGit2 credentials.
71 Args:
72 url: The url of the remote.
73 _username_from_url: Username extracted from the url, if any.
74 _allowed_types: Combination of bitflags representing the credential types supported by the remote.
76 Returns:
77 A pygit2.UserPass.
79 Raises:
80 ValueError: No credentials found.
81 """
82 target_hostname = urlparse(url).hostname
83 allowed_hostname = urlparse(self.api_url).hostname
84 if allowed_hostname != target_hostname:
85 msg = f"Target hostname {target_hostname} not matching allowed hostname {allowed_hostname}"
86 raise ValueError(
87 msg,
88 )
89 if self.pygitlab.private_token:
90 return UserPass("oauth2", self.pygitlab.private_token) # type: ignore[no-untyped-call]
91 if self.pygitlab.oauth_token:
92 return UserPass("oauth2", self.pygitlab.oauth_token) # type: ignore[no-untyped-call]
93 if self.pygitlab.job_token:
94 return UserPass("gitlab-ci-token", self.pygitlab.job_token) # type: ignore[no-untyped-call]
95 if self.pygitlab.http_username and self.pygitlab.http_password:
96 return UserPass(self.pygitlab.http_username, self.pygitlab.http_password) # type: ignore[no-untyped-call]
97 msg = "No PyGit2 credentials for {target_hostname}"
98 raise ValueError(msg)
100 @property
101 def pygit2_remote_callbacks(self) -> RemoteCallbacks:
102 """Get PyGit2 RemoteCallbacks.
104 Returns:
105 A pygit2.RemoteCallbacks.
106 """
107 cb = RemoteCallbacks() # type: ignore[no-untyped-call]
108 cb.credentials = self.pygit2_credentials # type: ignore[method-assign]
109 if self.pygitlab.ssl_verify is not True: 109 ↛ 110line 109 didn't jump to line 110 because the condition on line 109 was never true
110 cb.certificate_check = self.pygit2_certificate_check # type: ignore[method-assign]
111 return cb