Skip to content

Commit 45e6e2a

Browse files
yeoldegroveNotTheEvilOne
authored andcommitted
add Github Class
1 parent 9c1798d commit 45e6e2a

File tree

2 files changed

+181
-0
lines changed

2 files changed

+181
-0
lines changed

src/gardenlinux/github/__init__.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# -*- coding: utf-8 -*-
2+
3+
"""
4+
GitHub module
5+
"""
6+
7+
from .github import GitHub
8+
9+
__all__ = ["GitHub"]

src/gardenlinux/github/github.py

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
# -*- coding: utf-8 -*-
2+
3+
import subprocess
4+
import json
5+
import base64
6+
7+
from ..logger import LoggerSetup
8+
9+
10+
class GitHub(object):
11+
"""
12+
GitHub operations handler using GitHub CLI.
13+
14+
:author: Garden Linux Maintainers
15+
:copyright: Copyright 2024 SAP SE
16+
:package: gardenlinux
17+
:subpackage: github
18+
:since: 0.9.0
19+
:license: https://www.apache.org/licenses/LICENSE-2.0
20+
Apache License, Version 2.0
21+
"""
22+
23+
def __init__(self, owner="gardenlinux", repo="gardenlinux", logger=None):
24+
"""
25+
Constructor __init__(GitHub)
26+
27+
:param owner: GitHub repository owner
28+
:param repo: GitHub repository name
29+
:param logger: Logger instance
30+
31+
:since: 0.9.0
32+
"""
33+
34+
if logger is None or not logger.hasHandlers():
35+
logger = LoggerSetup.get_logger("gardenlinux.github")
36+
37+
self._owner = owner
38+
self._repo = repo
39+
self._logger = logger
40+
41+
self._logger.debug(f"GitHub initialized for {owner}/{repo}")
42+
43+
def api(self, endpoint, **kwargs):
44+
"""
45+
Execute a GitHub API call using gh cli.
46+
47+
:param endpoint: GitHub API endpoint (e.g. "/repos/owner/repo/contents/file.yaml")
48+
:param kwargs: Additional parameters for the API call
49+
50+
:return: (dict) Parsed JSON response
51+
:since: 0.9.0
52+
"""
53+
54+
command = ["gh", "api", endpoint]
55+
56+
# Add any additional parameters to the command
57+
for key, value in kwargs.items():
58+
if key.startswith("--"):
59+
command.extend([key, str(value)])
60+
else:
61+
command.extend([f"--{key}", str(value)])
62+
63+
self._logger.debug(f"Executing GitHub API call: {' '.join(command)}")
64+
65+
try:
66+
result = subprocess.run(
67+
command,
68+
stdout=subprocess.PIPE,
69+
stderr=subprocess.PIPE,
70+
text=True,
71+
check=True,
72+
)
73+
74+
return json.loads(result.stdout)
75+
76+
except subprocess.CalledProcessError as e:
77+
self._logger.error(f"GitHub API call failed: {e.stderr}")
78+
raise RuntimeError(
79+
f"GitHub API call failed for endpoint {endpoint}: {e.stderr}"
80+
)
81+
except json.JSONDecodeError as e:
82+
self._logger.error(f"Failed to parse GitHub API response: {e}")
83+
raise RuntimeError(f"Failed to parse GitHub API response: {e}")
84+
85+
def get_file_content(self, file_path, ref=None):
86+
"""
87+
Get file content from GitHub repository.
88+
89+
:param file_path: Path to file in repository (e.g. "flavors.yaml")
90+
:param ref: Git reference (commit, branch, tag). If None, uses default branch
91+
92+
:return: (str) File content
93+
:since: 0.9.0
94+
"""
95+
96+
endpoint = f"/repos/{self._owner}/{self._repo}/contents/{file_path}"
97+
98+
# Add ref parameter if specified
99+
if ref is not None:
100+
endpoint = f"{endpoint}?ref={ref}"
101+
102+
self._logger.debug(
103+
f"Fetching file content: {file_path} (ref: {ref or 'default'})"
104+
)
105+
106+
try:
107+
response = self.api(endpoint)
108+
109+
# Decode base64 content
110+
content = base64.b64decode(response["content"]).decode("utf-8")
111+
112+
self._logger.debug(
113+
f"Successfully fetched {len(content)} characters from {file_path}"
114+
)
115+
116+
return content
117+
118+
except Exception as e:
119+
self._logger.error(f"Failed to fetch file content for {file_path}: {e}")
120+
raise RuntimeError(f"Failed to fetch file content for {file_path}: {e}")
121+
122+
def get_flavors_yaml(self, commit="latest"):
123+
"""
124+
Get flavors.yaml content from the repository.
125+
126+
:param commit: Commit hash or "latest" for default branch
127+
128+
:return: (str) flavors.yaml content
129+
:since: 0.9.0
130+
"""
131+
132+
ref = None if commit == "latest" else commit
133+
commit_short = commit if commit == "latest" else commit[:8]
134+
135+
self._logger.debug(f"Fetching flavors.yaml for commit {commit_short}")
136+
137+
try:
138+
content = self.get_file_content("flavors.yaml", ref=ref)
139+
self._logger.debug(
140+
f"Successfully fetched flavors.yaml for commit {commit_short}"
141+
)
142+
return content
143+
144+
except Exception as e:
145+
self._logger.error(
146+
f"Failed to fetch flavors.yaml for commit {commit_short}: {e}"
147+
)
148+
raise RuntimeError(
149+
f"Failed to fetch flavors.yaml for commit {commit_short}: {e}"
150+
)
151+
152+
@property
153+
def repository_url(self):
154+
"""
155+
Returns the GitHub repository URL.
156+
157+
:return: (str) GitHub repository URL
158+
:since: 0.9.0
159+
"""
160+
161+
return f"https://github.com/{self._owner}/{self._repo}"
162+
163+
@property
164+
def api_url(self):
165+
"""
166+
Returns the GitHub API base URL for this repository.
167+
168+
:return: (str) GitHub API base URL
169+
:since: 0.9.0
170+
"""
171+
172+
return f"https://api.github.com/repos/{self._owner}/{self._repo}"

0 commit comments

Comments
 (0)