Skip to content

Commit e1109bb

Browse files
committed
Add dynamic ssh agent or token based auth for git Repository Class
Signed-off-by: Eike Waldt <waldt@b1-systems.de> On-behalf-of: SAP <eike.waldt@sap.com>
1 parent a7de0f0 commit e1109bb

3 files changed

Lines changed: 80 additions & 3 deletions

File tree

src/gardenlinux/git/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
Git module
55
"""
66

7+
from .credentials import Credentials
78
from .repository import Repository
89

9-
__all__ = ["Repository"]
10+
__all__ = ["Credentials", "Repository"]

src/gardenlinux/git/credentials.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
# -*- coding: utf-8 -*-
2+
3+
"""
4+
Git credentials provider
5+
"""
6+
7+
import os
8+
from typing import Optional, Union
9+
10+
from pygit2 import KeypairFromAgent, UserPass
11+
from pygit2.enums import CredentialType
12+
13+
14+
class Credentials:
15+
"""
16+
Git credentials provider for pygit2 remote operations.
17+
18+
Handles authentication dynamically based on the server's allowed credential
19+
types. This accounts for git config `url.<base>.insteadOf` rules that may
20+
rewrite HTTPS URLs to SSH at the libgit2 transport layer.
21+
22+
:author: Garden Linux Maintainers
23+
:copyright: Copyright 2024 SAP SE
24+
:package: gardenlinux
25+
:subpackage: git
26+
:since: 0.10.0
27+
:license: https://www.apache.org/licenses/LICENSE-2.0
28+
Apache License, Version 2.0
29+
"""
30+
31+
def __init__(self, token: Optional[str] = None):
32+
"""
33+
Constructor __init__(Credentials)
34+
35+
:param token: GitHub/Git access token for HTTPS authentication.
36+
Falls back to the GITHUB_TOKEN environment variable if not provided.
37+
38+
:since: 0.10.0
39+
"""
40+
41+
self._token = token
42+
43+
if self._token is None:
44+
self._token = os.environ.get("GITHUB_TOKEN")
45+
46+
def __call__(
47+
self,
48+
url: str,
49+
username_from_url: Optional[str],
50+
allowed_types: CredentialType,
51+
) -> Optional[Union[UserPass, KeypairFromAgent]]:
52+
"""
53+
Pygit2 credentials callback.
54+
55+
Called by libgit2 when authentication is required during remote
56+
operations. Returns the appropriate credential object based on what
57+
the server allows.
58+
59+
:param url: The URL being accessed (after any insteadOf rewrites)
60+
:param username_from_url: Username extracted from the URL, if any
61+
:param allowed_types: Bitmask of credential types the server accepts
62+
63+
:return: A pygit2 credential object, or None if no credentials available
64+
:since: 0.10.0
65+
"""
66+
67+
if allowed_types & CredentialType.USERPASS_PLAINTEXT:
68+
if self._token:
69+
return UserPass(self._token, "x-oauth-basic")
70+
71+
if allowed_types & CredentialType.SSH_KEY:
72+
return KeypairFromAgent(username_from_url or "git")
73+
74+
return None

src/gardenlinux/git/repository.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55
from pathlib import Path
66
from typing import Any, List, Optional
77

8-
from pygit2 import Oid
8+
from pygit2 import Oid, RemoteCallbacks
99
from pygit2 import Repository as _Repository
1010
from pygit2 import init_repository
1111

1212
from ..constants import GL_REPOSITORY_URL
1313
from ..logger import LoggerSetup
14+
from .credentials import Credentials
1415

1516

1617
class Repository(_Repository): # type: ignore[misc]
@@ -135,7 +136,8 @@ def checkout_repo(
135136
)
136137

137138
repo = init_repository(git_directory, origin_url=repo_url)
138-
repo.remotes["origin"].fetch()
139+
callbacks = RemoteCallbacks(credentials=Credentials())
140+
repo.remotes["origin"].fetch(callbacks=callbacks)
139141

140142
if commit is None:
141143
refish = f"origin/{branch}"

0 commit comments

Comments
 (0)